move code up one directory
[atutor.git] / jscripts / 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',\r
9 \r
10                 releaseDate : '2010-03-10',\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                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
30                         if (win.tinyMCEPreInit) {\r
31                                 t.suffix = tinyMCEPreInit.suffix;\r
32                                 t.baseURL = tinyMCEPreInit.base;\r
33                                 t.query = tinyMCEPreInit.query;\r
34                                 return;\r
35                         }\r
36 \r
37                         // Get suffix and base\r
38                         t.suffix = '';\r
39 \r
40                         // If base element found, add that infront of baseURL\r
41                         nl = d.getElementsByTagName('base');\r
42                         for (i=0; i<nl.length; i++) {\r
43                                 if (v = nl[i].href) {\r
44                                         // Host only value like http://site.com or http://site.com:8008\r
45                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
46                                                 v += '/';\r
47 \r
48                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
49                                 }\r
50                         }\r
51 \r
52                         function getBase(n) {\r
53                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {\r
54                                         if (/_(src|dev)\.js/g.test(n.src))\r
55                                                 t.suffix = '_src';\r
56 \r
57                                         if ((p = n.src.indexOf('?')) != -1)\r
58                                                 t.query = n.src.substring(p + 1);\r
59 \r
60                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
61 \r
62                                         // If path to script is relative and a base href was found add that one infront\r
63                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
64                                         // so this logic will basically only be executed on older IE versions\r
65                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
66                                                 t.baseURL = base + t.baseURL;\r
67 \r
68                                         return t.baseURL;\r
69                                 }\r
70 \r
71                                 return null;\r
72                         };\r
73 \r
74                         // Check document\r
75                         nl = d.getElementsByTagName('script');\r
76                         for (i=0; i<nl.length; i++) {\r
77                                 if (getBase(nl[i]))\r
78                                         return;\r
79                         }\r
80 \r
81                         // Check head\r
82                         n = d.getElementsByTagName('head')[0];\r
83                         if (n) {\r
84                                 nl = n.getElementsByTagName('script');\r
85                                 for (i=0; i<nl.length; i++) {\r
86                                         if (getBase(nl[i]))\r
87                                                 return;\r
88                                 }\r
89                         }\r
90 \r
91                         return;\r
92                 },\r
93 \r
94                 is : function(o, t) {\r
95                         if (!t)\r
96                                 return o !== undefined;\r
97 \r
98                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
99                                 return true;\r
100 \r
101                         return typeof(o) == t;\r
102                 },\r
103 \r
104                 each : function(o, cb, s) {\r
105                         var n, l;\r
106 \r
107                         if (!o)\r
108                                 return 0;\r
109 \r
110                         s = s || o;\r
111 \r
112                         if (o.length !== undefined) {\r
113                                 // Indexed arrays, needed for Safari\r
114                                 for (n=0, l = o.length; n < l; n++) {\r
115                                         if (cb.call(s, o[n], n, o) === false)\r
116                                                 return 0;\r
117                                 }\r
118                         } else {\r
119                                 // Hashtables\r
120                                 for (n in o) {\r
121                                         if (o.hasOwnProperty(n)) {\r
122                                                 if (cb.call(s, o[n], n, o) === false)\r
123                                                         return 0;\r
124                                         }\r
125                                 }\r
126                         }\r
127 \r
128                         return 1;\r
129                 },\r
130 \r
131 \r
132                 map : function(a, f) {\r
133                         var o = [];\r
134 \r
135                         tinymce.each(a, function(v) {\r
136                                 o.push(f(v));\r
137                         });\r
138 \r
139                         return o;\r
140                 },\r
141 \r
142                 grep : function(a, f) {\r
143                         var o = [];\r
144 \r
145                         tinymce.each(a, function(v) {\r
146                                 if (!f || f(v))\r
147                                         o.push(v);\r
148                         });\r
149 \r
150                         return o;\r
151                 },\r
152 \r
153                 inArray : function(a, v) {\r
154                         var i, l;\r
155 \r
156                         if (a) {\r
157                                 for (i = 0, l = a.length; i < l; i++) {\r
158                                         if (a[i] === v)\r
159                                                 return i;\r
160                                 }\r
161                         }\r
162 \r
163                         return -1;\r
164                 },\r
165 \r
166                 extend : function(o, e) {\r
167                         var i, l, a = arguments;\r
168 \r
169                         for (i = 1, l = a.length; i < l; i++) {\r
170                                 e = a[i];\r
171 \r
172                                 tinymce.each(e, function(v, n) {\r
173                                         if (v !== undefined)\r
174                                                 o[n] = v;\r
175                                 });\r
176                         }\r
177 \r
178                         return o;\r
179                 },\r
180 \r
181 \r
182                 trim : function(s) {\r
183                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
184                 },\r
185 \r
186                 create : function(s, p) {\r
187                         var t = this, sp, ns, cn, scn, c, de = 0;\r
188 \r
189                         // Parse : <prefix> <class>:<super class>\r
190                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
191                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
192 \r
193                         // Create namespace for new class\r
194                         ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
195 \r
196                         // Class already exists\r
197                         if (ns[cn])\r
198                                 return;\r
199 \r
200                         // Make pure static class\r
201                         if (s[2] == 'static') {\r
202                                 ns[cn] = p;\r
203 \r
204                                 if (this.onCreate)\r
205                                         this.onCreate(s[2], s[3], ns[cn]);\r
206 \r
207                                 return;\r
208                         }\r
209 \r
210                         // Create default constructor\r
211                         if (!p[cn]) {\r
212                                 p[cn] = function() {};\r
213                                 de = 1;\r
214                         }\r
215 \r
216                         // Add constructor and methods\r
217                         ns[cn] = p[cn];\r
218                         t.extend(ns[cn].prototype, p);\r
219 \r
220                         // Extend\r
221                         if (s[5]) {\r
222                                 sp = t.resolve(s[5]).prototype;\r
223                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
224 \r
225                                 // Extend constructor\r
226                                 c = ns[cn];\r
227                                 if (de) {\r
228                                         // Add passthrough constructor\r
229                                         ns[cn] = function() {\r
230                                                 return sp[scn].apply(this, arguments);\r
231                                         };\r
232                                 } else {\r
233                                         // Add inherit constructor\r
234                                         ns[cn] = function() {\r
235                                                 this.parent = sp[scn];\r
236                                                 return c.apply(this, arguments);\r
237                                         };\r
238                                 }\r
239                                 ns[cn].prototype[cn] = ns[cn];\r
240 \r
241                                 // Add super methods\r
242                                 t.each(sp, function(f, n) {\r
243                                         ns[cn].prototype[n] = sp[n];\r
244                                 });\r
245 \r
246                                 // Add overridden methods\r
247                                 t.each(p, function(f, n) {\r
248                                         // Extend methods if needed\r
249                                         if (sp[n]) {\r
250                                                 ns[cn].prototype[n] = function() {\r
251                                                         this.parent = sp[n];\r
252                                                         return f.apply(this, arguments);\r
253                                                 };\r
254                                         } else {\r
255                                                 if (n != cn)\r
256                                                         ns[cn].prototype[n] = f;\r
257                                         }\r
258                                 });\r
259                         }\r
260 \r
261                         // Add static methods\r
262                         t.each(p['static'], function(f, n) {\r
263                                 ns[cn][n] = f;\r
264                         });\r
265 \r
266                         if (this.onCreate)\r
267                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
268                 },\r
269 \r
270                 walk : function(o, f, n, s) {\r
271                         s = s || this;\r
272 \r
273                         if (o) {\r
274                                 if (n)\r
275                                         o = o[n];\r
276 \r
277                                 tinymce.each(o, function(o, i) {\r
278                                         if (f.call(s, o, i, n) === false)\r
279                                                 return false;\r
280 \r
281                                         tinymce.walk(o, f, n, s);\r
282                                 });\r
283                         }\r
284                 },\r
285 \r
286                 createNS : function(n, o) {\r
287                         var i, v;\r
288 \r
289                         o = o || win;\r
290 \r
291                         n = n.split('.');\r
292                         for (i=0; i<n.length; i++) {\r
293                                 v = n[i];\r
294 \r
295                                 if (!o[v])\r
296                                         o[v] = {};\r
297 \r
298                                 o = o[v];\r
299                         }\r
300 \r
301                         return o;\r
302                 },\r
303 \r
304                 resolve : function(n, o) {\r
305                         var i, l;\r
306 \r
307                         o = o || win;\r
308 \r
309                         n = n.split('.');\r
310                         for (i = 0, l = n.length; i < l; i++) {\r
311                                 o = o[n[i]];\r
312 \r
313                                 if (!o)\r
314                                         break;\r
315                         }\r
316 \r
317                         return o;\r
318                 },\r
319 \r
320                 addUnload : function(f, s) {\r
321                         var t = this;\r
322 \r
323                         f = {func : f, scope : s || this};\r
324 \r
325                         if (!t.unloads) {\r
326                                 function unload() {\r
327                                         var li = t.unloads, o, n;\r
328 \r
329                                         if (li) {\r
330                                                 // Call unload handlers\r
331                                                 for (n in li) {\r
332                                                         o = li[n];\r
333 \r
334                                                         if (o && o.func)\r
335                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
336                                                 }\r
337 \r
338                                                 // Detach unload function\r
339                                                 if (win.detachEvent) {\r
340                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
341                                                         win.detachEvent('onunload', unload);\r
342                                                 } else if (win.removeEventListener)\r
343                                                         win.removeEventListener('unload', unload, false);\r
344 \r
345                                                 // Destroy references\r
346                                                 t.unloads = o = li = w = unload = 0;\r
347 \r
348                                                 // Run garbarge collector on IE\r
349                                                 if (win.CollectGarbage)\r
350                                                         CollectGarbage();\r
351                                         }\r
352                                 };\r
353 \r
354                                 function fakeUnload() {\r
355                                         var d = document;\r
356 \r
357                                         // Is there things still loading, then do some magic\r
358                                         if (d.readyState == 'interactive') {\r
359                                                 function stop() {\r
360                                                         // Prevent memory leak\r
361                                                         d.detachEvent('onstop', stop);\r
362 \r
363                                                         // Call unload handler\r
364                                                         if (unload)\r
365                                                                 unload();\r
366 \r
367                                                         d = 0;\r
368                                                 };\r
369 \r
370                                                 // Fire unload when the currently loading page is stopped\r
371                                                 if (d)\r
372                                                         d.attachEvent('onstop', stop);\r
373 \r
374                                                 // Remove onstop listener after a while to prevent the unload function\r
375                                                 // to execute if the user presses cancel in an onbeforeunload\r
376                                                 // confirm dialog and then presses the browser stop button\r
377                                                 win.setTimeout(function() {\r
378                                                         if (d)\r
379                                                                 d.detachEvent('onstop', stop);\r
380                                                 }, 0);\r
381                                         }\r
382                                 };\r
383 \r
384                                 // Attach unload handler\r
385                                 if (win.attachEvent) {\r
386                                         win.attachEvent('onunload', unload);\r
387                                         win.attachEvent('onbeforeunload', fakeUnload);\r
388                                 } else if (win.addEventListener)\r
389                                         win.addEventListener('unload', unload, false);\r
390 \r
391                                 // Setup initial unload handler array\r
392                                 t.unloads = [f];\r
393                         } else\r
394                                 t.unloads.push(f);\r
395 \r
396                         return f;\r
397                 },\r
398 \r
399                 removeUnload : function(f) {\r
400                         var u = this.unloads, r = null;\r
401 \r
402                         tinymce.each(u, function(o, i) {\r
403                                 if (o && o.func == f) {\r
404                                         u.splice(i, 1);\r
405                                         r = f;\r
406                                         return false;\r
407                                 }\r
408                         });\r
409 \r
410                         return r;\r
411                 },\r
412 \r
413                 explode : function(s, d) {\r
414                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
415                 },\r
416 \r
417                 _addVer : function(u) {\r
418                         var v;\r
419 \r
420                         if (!this.query)\r
421                                 return u;\r
422 \r
423                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
424 \r
425                         if (u.indexOf('#') == -1)\r
426                                 return u + v;\r
427 \r
428                         return u.replace('#', v + '#');\r
429                 }\r
430 \r
431                 };\r
432 \r
433         // Initialize the API\r
434         tinymce._init();\r
435 \r
436         // Expose tinymce namespace to the global namespace (window)\r
437         win.tinymce = win.tinyMCE = tinymce;\r
438 })(window);\r
439 \r
440 \r
441 tinymce.create('tinymce.util.Dispatcher', {\r
442         scope : null,\r
443         listeners : null,\r
444 \r
445         Dispatcher : function(s) {\r
446                 this.scope = s || this;\r
447                 this.listeners = [];\r
448         },\r
449 \r
450         add : function(cb, s) {\r
451                 this.listeners.push({cb : cb, scope : s || this.scope});\r
452 \r
453                 return cb;\r
454         },\r
455 \r
456         addToTop : function(cb, s) {\r
457                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
458 \r
459                 return cb;\r
460         },\r
461 \r
462         remove : function(cb) {\r
463                 var l = this.listeners, o = null;\r
464 \r
465                 tinymce.each(l, function(c, i) {\r
466                         if (cb == c.cb) {\r
467                                 o = cb;\r
468                                 l.splice(i, 1);\r
469                                 return false;\r
470                         }\r
471                 });\r
472 \r
473                 return o;\r
474         },\r
475 \r
476         dispatch : function() {\r
477                 var s, a = arguments, i, li = this.listeners, c;\r
478 \r
479                 // Needs to be a real loop since the listener count might change while looping\r
480                 // And this is also more efficient\r
481                 for (i = 0; i<li.length; i++) {\r
482                         c = li[i];\r
483                         s = c.cb.apply(c.scope, a);\r
484 \r
485                         if (s === false)\r
486                                 break;\r
487                 }\r
488 \r
489                 return s;\r
490         }\r
491 \r
492         });\r
493 \r
494 (function() {\r
495         var each = tinymce.each;\r
496 \r
497         tinymce.create('tinymce.util.URI', {\r
498                 URI : function(u, s) {\r
499                         var t = this, o, a, b;\r
500 \r
501                         // Trim whitespace\r
502                         u = tinymce.trim(u);\r
503 \r
504                         // Default settings\r
505                         s = t.settings = s || {};\r
506 \r
507                         // Strange app protocol or local anchor\r
508                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
509                                 t.source = u;\r
510                                 return;\r
511                         }\r
512 \r
513                         // Absolute path with no host, fake host and protocol\r
514                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
515                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
516 \r
517                         // Relative path http:// or protocol relative //path\r
518                         if (!/^\w*:?\/\//.test(u))\r
519                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
520 \r
521                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
522                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
523                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
524                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
525                                 var s = u[i];\r
526 \r
527                                 // Zope 3 workaround, they use @@something\r
528                                 if (s)\r
529                                         s = s.replace(/\(mce_at\)/g, '@@');\r
530 \r
531                                 t[v] = s;\r
532                         });\r
533 \r
534                         if (b = s.base_uri) {\r
535                                 if (!t.protocol)\r
536                                         t.protocol = b.protocol;\r
537 \r
538                                 if (!t.userInfo)\r
539                                         t.userInfo = b.userInfo;\r
540 \r
541                                 if (!t.port && t.host == 'mce_host')\r
542                                         t.port = b.port;\r
543 \r
544                                 if (!t.host || t.host == 'mce_host')\r
545                                         t.host = b.host;\r
546 \r
547                                 t.source = '';\r
548                         }\r
549 \r
550                         //t.path = t.path || '/';\r
551                 },\r
552 \r
553                 setPath : function(p) {\r
554                         var t = this;\r
555 \r
556                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
557 \r
558                         // Update path parts\r
559                         t.path = p[0];\r
560                         t.directory = p[1];\r
561                         t.file = p[2];\r
562 \r
563                         // Rebuild source\r
564                         t.source = '';\r
565                         t.getURI();\r
566                 },\r
567 \r
568                 toRelative : function(u) {\r
569                         var t = this, o;\r
570 \r
571                         if (u === "./")\r
572                                 return u;\r
573 \r
574                         u = new tinymce.util.URI(u, {base_uri : t});\r
575 \r
576                         // Not on same domain/port or protocol\r
577                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
578                                 return u.getURI();\r
579 \r
580                         o = t.toRelPath(t.path, u.path);\r
581 \r
582                         // Add query\r
583                         if (u.query)\r
584                                 o += '?' + u.query;\r
585 \r
586                         // Add anchor\r
587                         if (u.anchor)\r
588                                 o += '#' + u.anchor;\r
589 \r
590                         return o;\r
591                 },\r
592         \r
593                 toAbsolute : function(u, nh) {\r
594                         var u = new tinymce.util.URI(u, {base_uri : this});\r
595 \r
596                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
597                 },\r
598 \r
599                 toRelPath : function(base, path) {\r
600                         var items, bp = 0, out = '', i, l;\r
601 \r
602                         // Split the paths\r
603                         base = base.substring(0, base.lastIndexOf('/'));\r
604                         base = base.split('/');\r
605                         items = path.split('/');\r
606 \r
607                         if (base.length >= items.length) {\r
608                                 for (i = 0, l = base.length; i < l; i++) {\r
609                                         if (i >= items.length || base[i] != items[i]) {\r
610                                                 bp = i + 1;\r
611                                                 break;\r
612                                         }\r
613                                 }\r
614                         }\r
615 \r
616                         if (base.length < items.length) {\r
617                                 for (i = 0, l = items.length; i < l; i++) {\r
618                                         if (i >= base.length || base[i] != items[i]) {\r
619                                                 bp = i + 1;\r
620                                                 break;\r
621                                         }\r
622                                 }\r
623                         }\r
624 \r
625                         if (bp == 1)\r
626                                 return path;\r
627 \r
628                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
629                                 out += "../";\r
630 \r
631                         for (i = bp - 1, l = items.length; i < l; i++) {\r
632                                 if (i != bp - 1)\r
633                                         out += "/" + items[i];\r
634                                 else\r
635                                         out += items[i];\r
636                         }\r
637 \r
638                         return out;\r
639                 },\r
640 \r
641                 toAbsPath : function(base, path) {\r
642                         var i, nb = 0, o = [], tr, outPath;\r
643 \r
644                         // Split paths\r
645                         tr = /\/$/.test(path) ? '/' : '';\r
646                         base = base.split('/');\r
647                         path = path.split('/');\r
648 \r
649                         // Remove empty chunks\r
650                         each(base, function(k) {\r
651                                 if (k)\r
652                                         o.push(k);\r
653                         });\r
654 \r
655                         base = o;\r
656 \r
657                         // Merge relURLParts chunks\r
658                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
659                                 // Ignore empty or .\r
660                                 if (path[i].length == 0 || path[i] == ".")\r
661                                         continue;\r
662 \r
663                                 // Is parent\r
664                                 if (path[i] == '..') {\r
665                                         nb++;\r
666                                         continue;\r
667                                 }\r
668 \r
669                                 // Move up\r
670                                 if (nb > 0) {\r
671                                         nb--;\r
672                                         continue;\r
673                                 }\r
674 \r
675                                 o.push(path[i]);\r
676                         }\r
677 \r
678                         i = base.length - nb;\r
679 \r
680                         // If /a/b/c or /\r
681                         if (i <= 0)\r
682                                 outPath = o.reverse().join('/');\r
683                         else\r
684                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
685 \r
686                         // Add front / if it's needed\r
687                         if (outPath.indexOf('/') !== 0)\r
688                                 outPath = '/' + outPath;\r
689 \r
690                         // Add traling / if it's needed\r
691                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
692                                 outPath += tr;\r
693 \r
694                         return outPath;\r
695                 },\r
696 \r
697                 getURI : function(nh) {\r
698                         var s, t = this;\r
699 \r
700                         // Rebuild source\r
701                         if (!t.source || nh) {\r
702                                 s = '';\r
703 \r
704                                 if (!nh) {\r
705                                         if (t.protocol)\r
706                                                 s += t.protocol + '://';\r
707 \r
708                                         if (t.userInfo)\r
709                                                 s += t.userInfo + '@';\r
710 \r
711                                         if (t.host)\r
712                                                 s += t.host;\r
713 \r
714                                         if (t.port)\r
715                                                 s += ':' + t.port;\r
716                                 }\r
717 \r
718                                 if (t.path)\r
719                                         s += t.path;\r
720 \r
721                                 if (t.query)\r
722                                         s += '?' + t.query;\r
723 \r
724                                 if (t.anchor)\r
725                                         s += '#' + t.anchor;\r
726 \r
727                                 t.source = s;\r
728                         }\r
729 \r
730                         return t.source;\r
731                 }\r
732         });\r
733 })();\r
734 \r
735 (function() {\r
736         var each = tinymce.each;\r
737 \r
738         tinymce.create('static tinymce.util.Cookie', {\r
739                 getHash : function(n) {\r
740                         var v = this.get(n), h;\r
741 \r
742                         if (v) {\r
743                                 each(v.split('&'), function(v) {\r
744                                         v = v.split('=');\r
745                                         h = h || {};\r
746                                         h[unescape(v[0])] = unescape(v[1]);\r
747                                 });\r
748                         }\r
749 \r
750                         return h;\r
751                 },\r
752 \r
753                 setHash : function(n, v, e, p, d, s) {\r
754                         var o = '';\r
755 \r
756                         each(v, function(v, k) {\r
757                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
758                         });\r
759 \r
760                         this.set(n, o, e, p, d, s);\r
761                 },\r
762 \r
763                 get : function(n) {\r
764                         var c = document.cookie, e, p = n + "=", b;\r
765 \r
766                         // Strict mode\r
767                         if (!c)\r
768                                 return;\r
769 \r
770                         b = c.indexOf("; " + p);\r
771 \r
772                         if (b == -1) {\r
773                                 b = c.indexOf(p);\r
774 \r
775                                 if (b != 0)\r
776                                         return null;\r
777                         } else\r
778                                 b += 2;\r
779 \r
780                         e = c.indexOf(";", b);\r
781 \r
782                         if (e == -1)\r
783                                 e = c.length;\r
784 \r
785                         return unescape(c.substring(b + p.length, e));\r
786                 },\r
787 \r
788                 set : function(n, v, e, p, d, s) {\r
789                         document.cookie = n + "=" + escape(v) +\r
790                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
791                                 ((p) ? "; path=" + escape(p) : "") +\r
792                                 ((d) ? "; domain=" + d : "") +\r
793                                 ((s) ? "; secure" : "");\r
794                 },\r
795 \r
796                 remove : function(n, p) {\r
797                         var d = new Date();\r
798 \r
799                         d.setTime(d.getTime() - 1000);\r
800 \r
801                         this.set(n, '', d, p, d);\r
802                 }\r
803         });\r
804 })();\r
805 \r
806 tinymce.create('static tinymce.util.JSON', {\r
807         serialize : function(o) {\r
808                 var i, v, s = tinymce.util.JSON.serialize, t;\r
809 \r
810                 if (o == null)\r
811                         return 'null';\r
812 \r
813                 t = typeof o;\r
814 \r
815                 if (t == 'string') {\r
816                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
817 \r
818                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
819                                 i = v.indexOf(b);\r
820 \r
821                                 if (i + 1)\r
822                                         return '\\' + v.charAt(i + 1);\r
823 \r
824                                 a = b.charCodeAt().toString(16);\r
825 \r
826                                 return '\\u' + '0000'.substring(a.length) + a;\r
827                         }) + '"';\r
828                 }\r
829 \r
830                 if (t == 'object') {\r
831                         if (o.hasOwnProperty && o instanceof Array) {\r
832                                         for (i=0, v = '['; i<o.length; i++)\r
833                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
834 \r
835                                         return v + ']';\r
836                                 }\r
837 \r
838                                 v = '{';\r
839 \r
840                                 for (i in o)\r
841                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
842 \r
843                                 return v + '}';\r
844                 }\r
845 \r
846                 return '' + o;\r
847         },\r
848 \r
849         parse : function(s) {\r
850                 try {\r
851                         return eval('(' + s + ')');\r
852                 } catch (ex) {\r
853                         // Ignore\r
854                 }\r
855         }\r
856 \r
857         });\r
858 \r
859 tinymce.create('static tinymce.util.XHR', {\r
860         send : function(o) {\r
861                 var x, t, w = window, c = 0;\r
862 \r
863                 // Default settings\r
864                 o.scope = o.scope || this;\r
865                 o.success_scope = o.success_scope || o.scope;\r
866                 o.error_scope = o.error_scope || o.scope;\r
867                 o.async = o.async === false ? false : true;\r
868                 o.data = o.data || '';\r
869 \r
870                 function get(s) {\r
871                         x = 0;\r
872 \r
873                         try {\r
874                                 x = new ActiveXObject(s);\r
875                         } catch (ex) {\r
876                         }\r
877 \r
878                         return x;\r
879                 };\r
880 \r
881                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
882 \r
883                 if (x) {\r
884                         if (x.overrideMimeType)\r
885                                 x.overrideMimeType(o.content_type);\r
886 \r
887                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
888 \r
889                         if (o.content_type)\r
890                                 x.setRequestHeader('Content-Type', o.content_type);\r
891 \r
892                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
893 \r
894                         x.send(o.data);\r
895 \r
896                         function ready() {\r
897                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
898                                         if (o.success && c < 10000 && x.status == 200)\r
899                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
900                                         else if (o.error)\r
901                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
902 \r
903                                         x = null;\r
904                                 } else\r
905                                         w.setTimeout(ready, 10);\r
906                         };\r
907 \r
908                         // Syncronous request\r
909                         if (!o.async)\r
910                                 return ready();\r
911 \r
912                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
913                         t = w.setTimeout(ready, 10);\r
914                 }\r
915         }\r
916 });\r
917 \r
918 (function() {\r
919         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
920 \r
921         tinymce.create('tinymce.util.JSONRequest', {\r
922                 JSONRequest : function(s) {\r
923                         this.settings = extend({\r
924                         }, s);\r
925                         this.count = 0;\r
926                 },\r
927 \r
928                 send : function(o) {\r
929                         var ecb = o.error, scb = o.success;\r
930 \r
931                         o = extend(this.settings, o);\r
932 \r
933                         o.success = function(c, x) {\r
934                                 c = JSON.parse(c);\r
935 \r
936                                 if (typeof(c) == 'undefined') {\r
937                                         c = {\r
938                                                 error : 'JSON Parse error.'\r
939                                         };\r
940                                 }\r
941 \r
942                                 if (c.error)\r
943                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
944                                 else\r
945                                         scb.call(o.success_scope || o.scope, c.result);\r
946                         };\r
947 \r
948                         o.error = function(ty, x) {\r
949                                 ecb.call(o.error_scope || o.scope, ty, x);\r
950                         };\r
951 \r
952                         o.data = JSON.serialize({\r
953                                 id : o.id || 'c' + (this.count++),\r
954                                 method : o.method,\r
955                                 params : o.params\r
956                         });\r
957 \r
958                         // JSON content type for Ruby on rails. Bug: #1883287\r
959                         o.content_type = 'application/json';\r
960 \r
961                         XHR.send(o);\r
962                 },\r
963 \r
964                 'static' : {\r
965                         sendRPC : function(o) {\r
966                                 return new tinymce.util.JSONRequest().send(o);\r
967                         }\r
968                 }\r
969         });\r
970 }());\r
971 (function(tinymce) {\r
972         // Shorten names\r
973         var each = tinymce.each,\r
974                 is = tinymce.is,\r
975                 isWebKit = tinymce.isWebKit,\r
976                 isIE = tinymce.isIE,\r
977                 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
978                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
979                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
980                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
981                 encodeCharsRe = /[<>&\"]/g,\r
982                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
983                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
984                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
985 \r
986         function makeMap(str) {\r
987                 var map = {}, i;\r
988 \r
989                 str = str.split(',');\r
990                 for (i = str.length; i >= 0; i--)\r
991                         map[str[i]] = 1;\r
992 \r
993                 return map;\r
994         };\r
995 \r
996         tinymce.create('tinymce.dom.DOMUtils', {\r
997                 doc : null,\r
998                 root : null,\r
999                 files : null,\r
1000                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1001                 props : {\r
1002                         "for" : "htmlFor",\r
1003                         "class" : "className",\r
1004                         className : "className",\r
1005                         checked : "checked",\r
1006                         disabled : "disabled",\r
1007                         maxlength : "maxLength",\r
1008                         readonly : "readOnly",\r
1009                         selected : "selected",\r
1010                         value : "value",\r
1011                         id : "id",\r
1012                         name : "name",\r
1013                         type : "type"\r
1014                 },\r
1015 \r
1016                 DOMUtils : function(d, s) {\r
1017                         var t = this, globalStyle;\r
1018 \r
1019                         t.doc = d;\r
1020                         t.win = window;\r
1021                         t.files = {};\r
1022                         t.cssFlicker = false;\r
1023                         t.counter = 0;\r
1024                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
1025                         t.stdMode = d.documentMode === 8;\r
1026 \r
1027                         t.settings = s = tinymce.extend({\r
1028                                 keep_values : false,\r
1029                                 hex_colors : 1,\r
1030                                 process_html : 1\r
1031                         }, s);\r
1032 \r
1033                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1034                         if (tinymce.isIE6) {\r
1035                                 try {\r
1036                                         d.execCommand('BackgroundImageCache', false, true);\r
1037                                 } catch (e) {\r
1038                                         t.cssFlicker = true;\r
1039                                 }\r
1040                         }\r
1041 \r
1042                         // Build styles list\r
1043                         if (s.valid_styles) {\r
1044                                 t._styles = {};\r
1045 \r
1046                                 // Convert styles into a rule list\r
1047                                 each(s.valid_styles, function(value, key) {\r
1048                                         t._styles[key] = tinymce.explode(value);\r
1049                                 });\r
1050                         }\r
1051 \r
1052                         tinymce.addUnload(t.destroy, t);\r
1053                 },\r
1054 \r
1055                 getRoot : function() {\r
1056                         var t = this, s = t.settings;\r
1057 \r
1058                         return (s && t.get(s.root_element)) || t.doc.body;\r
1059                 },\r
1060 \r
1061                 getViewPort : function(w) {\r
1062                         var d, b;\r
1063 \r
1064                         w = !w ? this.win : w;\r
1065                         d = w.document;\r
1066                         b = this.boxModel ? d.documentElement : d.body;\r
1067 \r
1068                         // Returns viewport size excluding scrollbars\r
1069                         return {\r
1070                                 x : w.pageXOffset || b.scrollLeft,\r
1071                                 y : w.pageYOffset || b.scrollTop,\r
1072                                 w : w.innerWidth || b.clientWidth,\r
1073                                 h : w.innerHeight || b.clientHeight\r
1074                         };\r
1075                 },\r
1076 \r
1077                 getRect : function(e) {\r
1078                         var p, t = this, sr;\r
1079 \r
1080                         e = t.get(e);\r
1081                         p = t.getPos(e);\r
1082                         sr = t.getSize(e);\r
1083 \r
1084                         return {\r
1085                                 x : p.x,\r
1086                                 y : p.y,\r
1087                                 w : sr.w,\r
1088                                 h : sr.h\r
1089                         };\r
1090                 },\r
1091 \r
1092                 getSize : function(e) {\r
1093                         var t = this, w, h;\r
1094 \r
1095                         e = t.get(e);\r
1096                         w = t.getStyle(e, 'width');\r
1097                         h = t.getStyle(e, 'height');\r
1098 \r
1099                         // Non pixel value, then force offset/clientWidth\r
1100                         if (w.indexOf('px') === -1)\r
1101                                 w = 0;\r
1102 \r
1103                         // Non pixel value, then force offset/clientWidth\r
1104                         if (h.indexOf('px') === -1)\r
1105                                 h = 0;\r
1106 \r
1107                         return {\r
1108                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1109                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1110                         };\r
1111                 },\r
1112 \r
1113                 getParent : function(n, f, r) {\r
1114                         return this.getParents(n, f, r, false);\r
1115                 },\r
1116 \r
1117                 getParents : function(n, f, r, c) {\r
1118                         var t = this, na, se = t.settings, o = [];\r
1119 \r
1120                         n = t.get(n);\r
1121                         c = c === undefined;\r
1122 \r
1123                         if (se.strict_root)\r
1124                                 r = r || t.getRoot();\r
1125 \r
1126                         // Wrap node name as func\r
1127                         if (is(f, 'string')) {\r
1128                                 na = f;\r
1129 \r
1130                                 if (f === '*') {\r
1131                                         f = function(n) {return n.nodeType == 1;};\r
1132                                 } else {\r
1133                                         f = function(n) {\r
1134                                                 return t.is(n, na);\r
1135                                         };\r
1136                                 }\r
1137                         }\r
1138 \r
1139                         while (n) {\r
1140                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1141                                         break;\r
1142 \r
1143                                 if (!f || f(n)) {\r
1144                                         if (c)\r
1145                                                 o.push(n);\r
1146                                         else\r
1147                                                 return n;\r
1148                                 }\r
1149 \r
1150                                 n = n.parentNode;\r
1151                         }\r
1152 \r
1153                         return c ? o : null;\r
1154                 },\r
1155 \r
1156                 get : function(e) {\r
1157                         var n;\r
1158 \r
1159                         if (e && this.doc && typeof(e) == 'string') {\r
1160                                 n = e;\r
1161                                 e = this.doc.getElementById(e);\r
1162 \r
1163                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1164                                 if (e && e.id !== n)\r
1165                                         return this.doc.getElementsByName(n)[1];\r
1166                         }\r
1167 \r
1168                         return e;\r
1169                 },\r
1170 \r
1171                 getNext : function(node, selector) {\r
1172                         return this._findSib(node, selector, 'nextSibling');\r
1173                 },\r
1174 \r
1175                 getPrev : function(node, selector) {\r
1176                         return this._findSib(node, selector, 'previousSibling');\r
1177                 },\r
1178 \r
1179 \r
1180                 select : function(pa, s) {\r
1181                         var t = this;\r
1182 \r
1183                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1184                 },\r
1185 \r
1186                 is : function(n, selector) {\r
1187                         var i;\r
1188 \r
1189                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
1190                         if (n.length === undefined) {\r
1191                                 // Simple all selector\r
1192                                 if (selector === '*')\r
1193                                         return n.nodeType == 1;\r
1194 \r
1195                                 // Simple selector just elements\r
1196                                 if (simpleSelectorRe.test(selector)) {\r
1197                                         selector = selector.toLowerCase().split(/,/);\r
1198                                         n = n.nodeName.toLowerCase();\r
1199 \r
1200                                         for (i = selector.length - 1; i >= 0; i--) {\r
1201                                                 if (selector[i] == n)\r
1202                                                         return true;\r
1203                                         }\r
1204 \r
1205                                         return false;\r
1206                                 }\r
1207                         }\r
1208 \r
1209                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
1210                 },\r
1211 \r
1212 \r
1213                 add : function(p, n, a, h, c) {\r
1214                         var t = this;\r
1215 \r
1216                         return this.run(p, function(p) {\r
1217                                 var e, k;\r
1218 \r
1219                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1220                                 t.setAttribs(e, a);\r
1221 \r
1222                                 if (h) {\r
1223                                         if (h.nodeType)\r
1224                                                 e.appendChild(h);\r
1225                                         else\r
1226                                                 t.setHTML(e, h);\r
1227                                 }\r
1228 \r
1229                                 return !c ? p.appendChild(e) : e;\r
1230                         });\r
1231                 },\r
1232 \r
1233                 create : function(n, a, h) {\r
1234                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1235                 },\r
1236 \r
1237                 createHTML : function(n, a, h) {\r
1238                         var o = '', t = this, k;\r
1239 \r
1240                         o += '<' + n;\r
1241 \r
1242                         for (k in a) {\r
1243                                 if (a.hasOwnProperty(k))\r
1244                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1245                         }\r
1246 \r
1247                         if (tinymce.is(h))\r
1248                                 return o + '>' + h + '</' + n + '>';\r
1249 \r
1250                         return o + ' />';\r
1251                 },\r
1252 \r
1253                 remove : function(node, keep_children) {\r
1254                         return this.run(node, function(node) {\r
1255                                 var parent, child;\r
1256 \r
1257                                 parent = node.parentNode;\r
1258 \r
1259                                 if (!parent)\r
1260                                         return null;\r
1261 \r
1262                                 if (keep_children) {\r
1263                                         while (child = node.firstChild) {\r
1264                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
1265                                                 if (child.nodeType !== 3 || child.nodeValue)\r
1266                                                         parent.insertBefore(child, node);\r
1267                                                 else\r
1268                                                         node.removeChild(child);\r
1269                                         }\r
1270                                 }\r
1271 \r
1272                                 return parent.removeChild(node);\r
1273                         });\r
1274                 },\r
1275 \r
1276                 setStyle : function(n, na, v) {\r
1277                         var t = this;\r
1278 \r
1279                         return t.run(n, function(e) {\r
1280                                 var s, i;\r
1281 \r
1282                                 s = e.style;\r
1283 \r
1284                                 // Camelcase it, if needed\r
1285                                 na = na.replace(/-(\D)/g, function(a, b){\r
1286                                         return b.toUpperCase();\r
1287                                 });\r
1288 \r
1289                                 // Default px suffix on these\r
1290                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1291                                         v += 'px';\r
1292 \r
1293                                 switch (na) {\r
1294                                         case 'opacity':\r
1295                                                 // IE specific opacity\r
1296                                                 if (isIE) {\r
1297                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1298 \r
1299                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1300                                                                 s.display = 'inline-block';\r
1301                                                 }\r
1302 \r
1303                                                 // Fix for older browsers\r
1304                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1305                                                 break;\r
1306 \r
1307                                         case 'float':\r
1308                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1309                                                 break;\r
1310                                         \r
1311                                         default:\r
1312                                                 s[na] = v || '';\r
1313                                 }\r
1314 \r
1315                                 // Force update of the style data\r
1316                                 if (t.settings.update_styles)\r
1317                                         t.setAttrib(e, '_mce_style');\r
1318                         });\r
1319                 },\r
1320 \r
1321                 getStyle : function(n, na, c) {\r
1322                         n = this.get(n);\r
1323 \r
1324                         if (!n)\r
1325                                 return false;\r
1326 \r
1327                         // Gecko\r
1328                         if (this.doc.defaultView && c) {\r
1329                                 // Remove camelcase\r
1330                                 na = na.replace(/[A-Z]/g, function(a){\r
1331                                         return '-' + a;\r
1332                                 });\r
1333 \r
1334                                 try {\r
1335                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1336                                 } catch (ex) {\r
1337                                         // Old safari might fail\r
1338                                         return null;\r
1339                                 }\r
1340                         }\r
1341 \r
1342                         // Camelcase it, if needed\r
1343                         na = na.replace(/-(\D)/g, function(a, b){\r
1344                                 return b.toUpperCase();\r
1345                         });\r
1346 \r
1347                         if (na == 'float')\r
1348                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1349 \r
1350                         // IE & Opera\r
1351                         if (n.currentStyle && c)\r
1352                                 return n.currentStyle[na];\r
1353 \r
1354                         return n.style[na];\r
1355                 },\r
1356 \r
1357                 setStyles : function(e, o) {\r
1358                         var t = this, s = t.settings, ol;\r
1359 \r
1360                         ol = s.update_styles;\r
1361                         s.update_styles = 0;\r
1362 \r
1363                         each(o, function(v, n) {\r
1364                                 t.setStyle(e, n, v);\r
1365                         });\r
1366 \r
1367                         // Update style info\r
1368                         s.update_styles = ol;\r
1369                         if (s.update_styles)\r
1370                                 t.setAttrib(e, s.cssText);\r
1371                 },\r
1372 \r
1373                 setAttrib : function(e, n, v) {\r
1374                         var t = this;\r
1375 \r
1376                         // Whats the point\r
1377                         if (!e || !n)\r
1378                                 return;\r
1379 \r
1380                         // Strict XML mode\r
1381                         if (t.settings.strict)\r
1382                                 n = n.toLowerCase();\r
1383 \r
1384                         return this.run(e, function(e) {\r
1385                                 var s = t.settings;\r
1386 \r
1387                                 switch (n) {\r
1388                                         case "style":\r
1389                                                 if (!is(v, 'string')) {\r
1390                                                         each(v, function(v, n) {\r
1391                                                                 t.setStyle(e, n, v);\r
1392                                                         });\r
1393 \r
1394                                                         return;\r
1395                                                 }\r
1396 \r
1397                                                 // No mce_style for elements with these since they might get resized by the user\r
1398                                                 if (s.keep_values) {\r
1399                                                         if (v && !t._isRes(v))\r
1400                                                                 e.setAttribute('_mce_style', v, 2);\r
1401                                                         else\r
1402                                                                 e.removeAttribute('_mce_style', 2);\r
1403                                                 }\r
1404 \r
1405                                                 e.style.cssText = v;\r
1406                                                 break;\r
1407 \r
1408                                         case "class":\r
1409                                                 e.className = v || ''; // Fix IE null bug\r
1410                                                 break;\r
1411 \r
1412                                         case "src":\r
1413                                         case "href":\r
1414                                                 if (s.keep_values) {\r
1415                                                         if (s.url_converter)\r
1416                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1417 \r
1418                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1419                                                 }\r
1420 \r
1421                                                 break;\r
1422                                         \r
1423                                         case "shape":\r
1424                                                 e.setAttribute('_mce_style', v);\r
1425                                                 break;\r
1426                                 }\r
1427 \r
1428                                 if (is(v) && v !== null && v.length !== 0)\r
1429                                         e.setAttribute(n, '' + v, 2);\r
1430                                 else\r
1431                                         e.removeAttribute(n, 2);\r
1432                         });\r
1433                 },\r
1434 \r
1435                 setAttribs : function(e, o) {\r
1436                         var t = this;\r
1437 \r
1438                         return this.run(e, function(e) {\r
1439                                 each(o, function(v, n) {\r
1440                                         t.setAttrib(e, n, v);\r
1441                                 });\r
1442                         });\r
1443                 },\r
1444 \r
1445                 getAttrib : function(e, n, dv) {\r
1446                         var v, t = this;\r
1447 \r
1448                         e = t.get(e);\r
1449 \r
1450                         if (!e || e.nodeType !== 1)\r
1451                                 return false;\r
1452 \r
1453                         if (!is(dv))\r
1454                                 dv = '';\r
1455 \r
1456                         // Try the mce variant for these\r
1457                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1458                                 v = e.getAttribute("_mce_" + n);\r
1459 \r
1460                                 if (v)\r
1461                                         return v;\r
1462                         }\r
1463 \r
1464                         if (isIE && t.props[n]) {\r
1465                                 v = e[t.props[n]];\r
1466                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1467                         }\r
1468 \r
1469                         if (!v)\r
1470                                 v = e.getAttribute(n, 2);\r
1471 \r
1472                         // Check boolean attribs\r
1473                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1474                                 if (e[t.props[n]] === true && v === '')\r
1475                                         return n;\r
1476 \r
1477                                 return v ? n : '';\r
1478                         }\r
1479 \r
1480                         // Inner input elements will override attributes on form elements\r
1481                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1482                                 return e.getAttributeNode(n).nodeValue;\r
1483 \r
1484                         if (n === 'style') {\r
1485                                 v = v || e.style.cssText;\r
1486 \r
1487                                 if (v) {\r
1488                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1489 \r
1490                                         if (t.settings.keep_values && !t._isRes(v))\r
1491                                                 e.setAttribute('_mce_style', v);\r
1492                                 }\r
1493                         }\r
1494 \r
1495                         // Remove Apple and WebKit stuff\r
1496                         if (isWebKit && n === "class" && v)\r
1497                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1498 \r
1499                         // Handle IE issues\r
1500                         if (isIE) {\r
1501                                 switch (n) {\r
1502                                         case 'rowspan':\r
1503                                         case 'colspan':\r
1504                                                 // IE returns 1 as default value\r
1505                                                 if (v === 1)\r
1506                                                         v = '';\r
1507 \r
1508                                                 break;\r
1509 \r
1510                                         case 'size':\r
1511                                                 // IE returns +0 as default value for size\r
1512                                                 if (v === '+0' || v === 20 || v === 0)\r
1513                                                         v = '';\r
1514 \r
1515                                                 break;\r
1516 \r
1517                                         case 'width':\r
1518                                         case 'height':\r
1519                                         case 'vspace':\r
1520                                         case 'checked':\r
1521                                         case 'disabled':\r
1522                                         case 'readonly':\r
1523                                                 if (v === 0)\r
1524                                                         v = '';\r
1525 \r
1526                                                 break;\r
1527 \r
1528                                         case 'hspace':\r
1529                                                 // IE returns -1 as default value\r
1530                                                 if (v === -1)\r
1531                                                         v = '';\r
1532 \r
1533                                                 break;\r
1534 \r
1535                                         case 'maxlength':\r
1536                                         case 'tabindex':\r
1537                                                 // IE returns default value\r
1538                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1539                                                         v = '';\r
1540 \r
1541                                                 break;\r
1542 \r
1543                                         case 'multiple':\r
1544                                         case 'compact':\r
1545                                         case 'noshade':\r
1546                                         case 'nowrap':\r
1547                                                 if (v === 65535)\r
1548                                                         return n;\r
1549 \r
1550                                                 return dv;\r
1551 \r
1552                                         case 'shape':\r
1553                                                 v = v.toLowerCase();\r
1554                                                 break;\r
1555 \r
1556                                         default:\r
1557                                                 // IE has odd anonymous function for event attributes\r
1558                                                 if (n.indexOf('on') === 0 && v)\r
1559                                                         v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1560                                 }\r
1561                         }\r
1562 \r
1563                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1564                 },\r
1565 \r
1566                 getPos : function(n, ro) {\r
1567                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1568 \r
1569                         n = t.get(n);\r
1570                         ro = ro || d.body;\r
1571 \r
1572                         if (n) {\r
1573                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1574                                 if (isIE && !t.stdMode) {\r
1575                                         n = n.getBoundingClientRect();\r
1576                                         e = t.boxModel ? d.documentElement : d.body;\r
1577                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1578                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1579                                         n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset\r
1580 \r
1581                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1582                                 }\r
1583 \r
1584                                 r = n;\r
1585                                 while (r && r != ro && r.nodeType) {\r
1586                                         x += r.offsetLeft || 0;\r
1587                                         y += r.offsetTop || 0;\r
1588                                         r = r.offsetParent;\r
1589                                 }\r
1590 \r
1591                                 r = n.parentNode;\r
1592                                 while (r && r != ro && r.nodeType) {\r
1593                                         x -= r.scrollLeft || 0;\r
1594                                         y -= r.scrollTop || 0;\r
1595                                         r = r.parentNode;\r
1596                                 }\r
1597                         }\r
1598 \r
1599                         return {x : x, y : y};\r
1600                 },\r
1601 \r
1602                 parseStyle : function(st) {\r
1603                         var t = this, s = t.settings, o = {};\r
1604 \r
1605                         if (!st)\r
1606                                 return o;\r
1607 \r
1608                         function compress(p, s, ot) {\r
1609                                 var t, r, b, l;\r
1610 \r
1611                                 // Get values and check it it needs compressing\r
1612                                 t = o[p + '-top' + s];\r
1613                                 if (!t)\r
1614                                         return;\r
1615 \r
1616                                 r = o[p + '-right' + s];\r
1617                                 if (t != r)\r
1618                                         return;\r
1619 \r
1620                                 b = o[p + '-bottom' + s];\r
1621                                 if (r != b)\r
1622                                         return;\r
1623 \r
1624                                 l = o[p + '-left' + s];\r
1625                                 if (b != l)\r
1626                                         return;\r
1627 \r
1628                                 // Compress\r
1629                                 o[ot] = l;\r
1630                                 delete o[p + '-top' + s];\r
1631                                 delete o[p + '-right' + s];\r
1632                                 delete o[p + '-bottom' + s];\r
1633                                 delete o[p + '-left' + s];\r
1634                         };\r
1635 \r
1636                         function compress2(ta, a, b, c) {\r
1637                                 var t;\r
1638 \r
1639                                 t = o[a];\r
1640                                 if (!t)\r
1641                                         return;\r
1642 \r
1643                                 t = o[b];\r
1644                                 if (!t)\r
1645                                         return;\r
1646 \r
1647                                 t = o[c];\r
1648                                 if (!t)\r
1649                                         return;\r
1650 \r
1651                                 // Compress\r
1652                                 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1653                                 delete o[a];\r
1654                                 delete o[b];\r
1655                                 delete o[c];\r
1656                         };\r
1657 \r
1658                         st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1659 \r
1660                         each(st.split(';'), function(v) {\r
1661                                 var sv, ur = [];\r
1662 \r
1663                                 if (v) {\r
1664                                         v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1665                                         v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1666                                         v = v.split(':');\r
1667                                         sv = tinymce.trim(v[1]);\r
1668                                         sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1669 \r
1670                                         sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1671                                                 return t.toHex(v);\r
1672                                         });\r
1673 \r
1674                                         if (s.url_converter) {\r
1675                                                 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1676                                                         return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1677                                                 });\r
1678                                         }\r
1679 \r
1680                                         o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1681                                 }\r
1682                         });\r
1683 \r
1684                         compress("border", "", "border");\r
1685                         compress("border", "-width", "border-width");\r
1686                         compress("border", "-color", "border-color");\r
1687                         compress("border", "-style", "border-style");\r
1688                         compress("padding", "", "padding");\r
1689                         compress("margin", "", "margin");\r
1690                         compress2('border', 'border-width', 'border-style', 'border-color');\r
1691 \r
1692                         if (isIE) {\r
1693                                 // Remove pointless border\r
1694                                 if (o.border == 'medium none')\r
1695                                         o.border = '';\r
1696                         }\r
1697 \r
1698                         return o;\r
1699                 },\r
1700 \r
1701                 serializeStyle : function(o, name) {\r
1702                         var t = this, s = '';\r
1703 \r
1704                         function add(v, k) {\r
1705                                 if (k && v) {\r
1706                                         // Remove browser specific styles like -moz- or -webkit-\r
1707                                         if (k.indexOf('-') === 0)\r
1708                                                 return;\r
1709 \r
1710                                         switch (k) {\r
1711                                                 case 'font-weight':\r
1712                                                         // Opera will output bold as 700\r
1713                                                         if (v == 700)\r
1714                                                                 v = 'bold';\r
1715 \r
1716                                                         break;\r
1717 \r
1718                                                 case 'color':\r
1719                                                 case 'background-color':\r
1720                                                         v = v.toLowerCase();\r
1721                                                         break;\r
1722                                         }\r
1723 \r
1724                                         s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1725                                 }\r
1726                         };\r
1727 \r
1728                         // Validate style output\r
1729                         if (name && t._styles) {\r
1730                                 each(t._styles['*'], function(name) {\r
1731                                         add(o[name], name);\r
1732                                 });\r
1733 \r
1734                                 each(t._styles[name.toLowerCase()], function(name) {\r
1735                                         add(o[name], name);\r
1736                                 });\r
1737                         } else\r
1738                                 each(o, add);\r
1739 \r
1740                         return s;\r
1741                 },\r
1742 \r
1743                 loadCSS : function(u) {\r
1744                         var t = this, d = t.doc, head;\r
1745 \r
1746                         if (!u)\r
1747                                 u = '';\r
1748 \r
1749                         head = t.select('head')[0];\r
1750 \r
1751                         each(u.split(','), function(u) {\r
1752                                 var link;\r
1753 \r
1754                                 if (t.files[u])\r
1755                                         return;\r
1756 \r
1757                                 t.files[u] = true;\r
1758                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1759 \r
1760                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1761                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1762                                 // It's ugly but it seems to work fine.\r
1763                                 if (isIE && d.documentMode) {\r
1764                                         link.onload = function() {\r
1765                                                 d.recalc();\r
1766                                                 link.onload = null;\r
1767                                         };\r
1768                                 }\r
1769 \r
1770                                 head.appendChild(link);\r
1771                         });\r
1772                 },\r
1773 \r
1774                 addClass : function(e, c) {\r
1775                         return this.run(e, function(e) {\r
1776                                 var o;\r
1777 \r
1778                                 if (!c)\r
1779                                         return 0;\r
1780 \r
1781                                 if (this.hasClass(e, c))\r
1782                                         return e.className;\r
1783 \r
1784                                 o = this.removeClass(e, c);\r
1785 \r
1786                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1787                         });\r
1788                 },\r
1789 \r
1790                 removeClass : function(e, c) {\r
1791                         var t = this, re;\r
1792 \r
1793                         return t.run(e, function(e) {\r
1794                                 var v;\r
1795 \r
1796                                 if (t.hasClass(e, c)) {\r
1797                                         if (!re)\r
1798                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1799 \r
1800                                         v = e.className.replace(re, ' ');\r
1801                                         v = tinymce.trim(v != ' ' ? v : '');\r
1802 \r
1803                                         e.className = v;\r
1804 \r
1805                                         // Empty class attr\r
1806                                         if (!v)\r
1807                                                 e.removeAttribute('class');\r
1808 \r
1809                                         return v;\r
1810                                 }\r
1811 \r
1812                                 return e.className;\r
1813                         });\r
1814                 },\r
1815 \r
1816                 hasClass : function(n, c) {\r
1817                         n = this.get(n);\r
1818 \r
1819                         if (!n || !c)\r
1820                                 return false;\r
1821 \r
1822                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1823                 },\r
1824 \r
1825                 show : function(e) {\r
1826                         return this.setStyle(e, 'display', 'block');\r
1827                 },\r
1828 \r
1829                 hide : function(e) {\r
1830                         return this.setStyle(e, 'display', 'none');\r
1831                 },\r
1832 \r
1833                 isHidden : function(e) {\r
1834                         e = this.get(e);\r
1835 \r
1836                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1837                 },\r
1838 \r
1839                 uniqueId : function(p) {\r
1840                         return (!p ? 'mce_' : p) + (this.counter++);\r
1841                 },\r
1842 \r
1843                 setHTML : function(e, h) {\r
1844                         var t = this;\r
1845 \r
1846                         return this.run(e, function(e) {\r
1847                                 var x, i, nl, n, p, x;\r
1848 \r
1849                                 h = t.processHTML(h);\r
1850 \r
1851                                 if (isIE) {\r
1852                                         function set() {\r
1853                                                 // Remove all child nodes\r
1854                                                 while (e.firstChild)\r
1855                                                         e.firstChild.removeNode();\r
1856 \r
1857                                                 try {\r
1858                                                         // IE will remove comments from the beginning\r
1859                                                         // unless you padd the contents with something\r
1860                                                         e.innerHTML = '<br />' + h;\r
1861                                                         e.removeChild(e.firstChild);\r
1862                                                 } catch (ex) {\r
1863                                                         // 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
1864                                                         // This seems to fix this problem\r
1865 \r
1866                                                         // Create new div with HTML contents and a BR infront to keep comments\r
1867                                                         x = t.create('div');\r
1868                                                         x.innerHTML = '<br />' + h;\r
1869 \r
1870                                                         // Add all children from div to target\r
1871                                                         each (x.childNodes, function(n, i) {\r
1872                                                                 // Skip br element\r
1873                                                                 if (i)\r
1874                                                                         e.appendChild(n);\r
1875                                                         });\r
1876                                                 }\r
1877                                         };\r
1878 \r
1879                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1880                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1881                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1882                                         if (t.settings.fix_ie_paragraphs)\r
1883                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
1884 \r
1885                                         set();\r
1886 \r
1887                                         if (t.settings.fix_ie_paragraphs) {\r
1888                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
1889                                                 nl = e.getElementsByTagName("p");\r
1890                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1891                                                         n = nl[i];\r
1892 \r
1893                                                         if (!n.hasChildNodes()) {\r
1894                                                                 if (!n._mce_keep) {\r
1895                                                                         x = 1; // Is broken\r
1896                                                                         break;\r
1897                                                                 }\r
1898 \r
1899                                                                 n.removeAttribute('_mce_keep');\r
1900                                                         }\r
1901                                                 }\r
1902                                         }\r
1903 \r
1904                                         // Time to fix the madness IE left us\r
1905                                         if (x) {\r
1906                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1907                                                 // after we use innerHTML we can fix the DOM tree\r
1908                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
1909                                                 h = h.replace(/<\/p>/g, '</div>');\r
1910 \r
1911                                                 // Set the new HTML with DIVs\r
1912                                                 set();\r
1913 \r
1914                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
1915                                                 // This is needed since IE has a annoying bug see above for details\r
1916                                                 // This is a slow process but it has to be done. :(\r
1917                                                 if (t.settings.fix_ie_paragraphs) {\r
1918                                                         nl = e.getElementsByTagName("DIV");\r
1919                                                         for (i = nl.length - 1; i >= 0; i--) {\r
1920                                                                 n = nl[i];\r
1921 \r
1922                                                                 // Is it a temp div\r
1923                                                                 if (n._mce_tmp) {\r
1924                                                                         // Create new paragraph\r
1925                                                                         p = t.doc.createElement('p');\r
1926 \r
1927                                                                         // Copy all attributes\r
1928                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1929                                                                                 var v;\r
1930 \r
1931                                                                                 if (b !== '_mce_tmp') {\r
1932                                                                                         v = n.getAttribute(b);\r
1933 \r
1934                                                                                         if (!v && b === 'class')\r
1935                                                                                                 v = n.className;\r
1936 \r
1937                                                                                         p.setAttribute(b, v);\r
1938                                                                                 }\r
1939                                                                         });\r
1940 \r
1941                                                                         // Append all children to new paragraph\r
1942                                                                         for (x = 0; x<n.childNodes.length; x++)\r
1943                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
1944 \r
1945                                                                         // Replace div with new paragraph\r
1946                                                                         n.swapNode(p);\r
1947                                                                 }\r
1948                                                         }\r
1949                                                 }\r
1950                                         }\r
1951                                 } else\r
1952                                         e.innerHTML = h;\r
1953 \r
1954                                 return h;\r
1955                         });\r
1956                 },\r
1957 \r
1958                 processHTML : function(h) {\r
1959                         var t = this, s = t.settings, codeBlocks = [];\r
1960 \r
1961                         if (!s.process_html)\r
1962                                 return h;\r
1963 \r
1964                         if (isIE) {\r
1965                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
1966                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
1967                         }\r
1968 \r
1969                         // Fix some issues\r
1970                         h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
1971 \r
1972                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
1973                         if (s.keep_values) {\r
1974                                 // Wrap scripts and styles in comments for serialization purposes\r
1975                                 if (/<script|noscript|style/i.test(h)) {\r
1976                                         function trim(s) {\r
1977                                                 // Remove prefix and suffix code for element\r
1978                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
1979                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
1980                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
1981                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
1982 \r
1983                                                 return s;\r
1984                                         };\r
1985 \r
1986                                         // Wrap the script contents in CDATA and keep them from executing\r
1987                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
1988                                                 // Force type attribute\r
1989                                                 if (!attribs)\r
1990                                                         attribs = ' type="text/javascript"';\r
1991 \r
1992                                                 // Convert the src attribute of the scripts\r
1993                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
1994                                                         if (s.url_converter)\r
1995                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
1996 \r
1997                                                         return '_mce_src="' + url + '"';\r
1998                                                 });\r
1999 \r
2000                                                 // Wrap text contents\r
2001                                                 if (tinymce.trim(text)) {\r
2002                                                         codeBlocks.push(trim(text));\r
2003                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2004                                                 }\r
2005 \r
2006                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2007                                         });\r
2008 \r
2009                                         // Wrap style elements\r
2010                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2011                                                 // Wrap text contents\r
2012                                                 if (text) {\r
2013                                                         codeBlocks.push(trim(text));\r
2014                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2015                                                 }\r
2016 \r
2017                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2018                                         });\r
2019 \r
2020                                         // Wrap noscript elements\r
2021                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2022                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2023                                         });\r
2024                                 }\r
2025 \r
2026                                 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
2027 \r
2028                                 // This function processes the attributes in the HTML string to force boolean\r
2029                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2030                                 function processTags(html) {\r
2031                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2032                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2033                                                         var mceValue;\r
2034 \r
2035                                                         name = name.toLowerCase();\r
2036                                                         value = value || val2 || val3 || "";\r
2037 \r
2038                                                         // Treat boolean attributes\r
2039                                                         if (boolAttrs[name]) {\r
2040                                                                 // false or 0 is treated as a missing attribute\r
2041                                                                 if (value === 'false' || value === '0')\r
2042                                                                         return;\r
2043 \r
2044                                                                 return name + '="' + name + '"';\r
2045                                                         }\r
2046 \r
2047                                                         // Is attribute one that needs special treatment\r
2048                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2049                                                                 mceValue = t.decode(value);\r
2050 \r
2051                                                                 // Convert URLs to relative/absolute ones\r
2052                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2053                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2054 \r
2055                                                                 // Process styles lowercases them and compresses them\r
2056                                                                 if (name == 'style')\r
2057                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2058 \r
2059                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2060                                                         }\r
2061 \r
2062                                                         return match;\r
2063                                                 }) + end + '>';\r
2064                                         });\r
2065                                 };\r
2066 \r
2067                                 h = processTags(h);\r
2068 \r
2069                                 // Restore script blocks\r
2070                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2071                                         return codeBlocks[idx];\r
2072                                 });\r
2073                         }\r
2074 \r
2075                         return h;\r
2076                 },\r
2077 \r
2078                 getOuterHTML : function(e) {\r
2079                         var d;\r
2080 \r
2081                         e = this.get(e);\r
2082 \r
2083                         if (!e)\r
2084                                 return null;\r
2085 \r
2086                         if (e.outerHTML !== undefined)\r
2087                                 return e.outerHTML;\r
2088 \r
2089                         d = (e.ownerDocument || this.doc).createElement("body");\r
2090                         d.appendChild(e.cloneNode(true));\r
2091 \r
2092                         return d.innerHTML;\r
2093                 },\r
2094 \r
2095                 setOuterHTML : function(e, h, d) {\r
2096                         var t = this;\r
2097 \r
2098                         function setHTML(e, h, d) {\r
2099                                 var n, tp;\r
2100 \r
2101                                 tp = d.createElement("body");\r
2102                                 tp.innerHTML = h;\r
2103 \r
2104                                 n = tp.lastChild;\r
2105                                 while (n) {\r
2106                                         t.insertAfter(n.cloneNode(true), e);\r
2107                                         n = n.previousSibling;\r
2108                                 }\r
2109 \r
2110                                 t.remove(e);\r
2111                         };\r
2112 \r
2113                         return this.run(e, function(e) {\r
2114                                 e = t.get(e);\r
2115 \r
2116                                 // Only set HTML on elements\r
2117                                 if (e.nodeType == 1) {\r
2118                                         d = d || e.ownerDocument || t.doc;\r
2119 \r
2120                                         if (isIE) {\r
2121                                                 try {\r
2122                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2123                                                         if (isIE && e.nodeType == 1)\r
2124                                                                 e.outerHTML = h;\r
2125                                                         else\r
2126                                                                 setHTML(e, h, d);\r
2127                                                 } catch (ex) {\r
2128                                                         // Fix for unknown runtime error\r
2129                                                         setHTML(e, h, d);\r
2130                                                 }\r
2131                                         } else\r
2132                                                 setHTML(e, h, d);\r
2133                                 }\r
2134                         });\r
2135                 },\r
2136 \r
2137                 decode : function(s) {\r
2138                         var e, n, v;\r
2139 \r
2140                         // Look for entities to decode\r
2141                         if (/&[\w#]+;/.test(s)) {\r
2142                                 // Decode the entities using a div element not super efficient but less code\r
2143                                 e = this.doc.createElement("div");\r
2144                                 e.innerHTML = s;\r
2145                                 n = e.firstChild;\r
2146                                 v = '';\r
2147 \r
2148                                 if (n) {\r
2149                                         do {\r
2150                                                 v += n.nodeValue;\r
2151                                         } while (n = n.nextSibling);\r
2152                                 }\r
2153 \r
2154                                 return v || s;\r
2155                         }\r
2156 \r
2157                         return s;\r
2158                 },\r
2159 \r
2160                 encode : function(str) {\r
2161                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2162                                 return encodedChars[chr];\r
2163                         });\r
2164                 },\r
2165 \r
2166                 insertAfter : function(node, reference_node) {\r
2167                         reference_node = this.get(reference_node);\r
2168 \r
2169                         return this.run(node, function(node) {\r
2170                                 var parent, nextSibling;\r
2171 \r
2172                                 parent = reference_node.parentNode;\r
2173                                 nextSibling = reference_node.nextSibling;\r
2174 \r
2175                                 if (nextSibling)\r
2176                                         parent.insertBefore(node, nextSibling);\r
2177                                 else\r
2178                                         parent.appendChild(node);\r
2179 \r
2180                                 return node;\r
2181                         });\r
2182                 },\r
2183 \r
2184                 isBlock : function(n) {\r
2185                         if (n.nodeType && n.nodeType !== 1)\r
2186                                 return false;\r
2187 \r
2188                         n = n.nodeName || n;\r
2189 \r
2190                         return blockRe.test(n);\r
2191                 },\r
2192 \r
2193                 replace : function(n, o, k) {\r
2194                         var t = this;\r
2195 \r
2196                         if (is(o, 'array'))\r
2197                                 n = n.cloneNode(true);\r
2198 \r
2199                         return t.run(o, function(o) {\r
2200                                 if (k) {\r
2201                                         each(tinymce.grep(o.childNodes), function(c) {\r
2202                                                 n.appendChild(c);\r
2203                                         });\r
2204                                 }\r
2205 \r
2206                                 return o.parentNode.replaceChild(n, o);\r
2207                         });\r
2208                 },\r
2209 \r
2210                 rename : function(elm, name) {\r
2211                         var t = this, newElm;\r
2212 \r
2213                         if (elm.nodeName != name.toUpperCase()) {\r
2214                                 // Rename block element\r
2215                                 newElm = t.create(name);\r
2216 \r
2217                                 // Copy attribs to new block\r
2218                                 each(t.getAttribs(elm), function(attr_node) {\r
2219                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2220                                 });\r
2221 \r
2222                                 // Replace block\r
2223                                 t.replace(newElm, elm, 1);\r
2224                         }\r
2225 \r
2226                         return newElm || elm;\r
2227                 },\r
2228 \r
2229                 findCommonAncestor : function(a, b) {\r
2230                         var ps = a, pe;\r
2231 \r
2232                         while (ps) {\r
2233                                 pe = b;\r
2234 \r
2235                                 while (pe && ps != pe)\r
2236                                         pe = pe.parentNode;\r
2237 \r
2238                                 if (ps == pe)\r
2239                                         break;\r
2240 \r
2241                                 ps = ps.parentNode;\r
2242                         }\r
2243 \r
2244                         if (!ps && a.ownerDocument)\r
2245                                 return a.ownerDocument.documentElement;\r
2246 \r
2247                         return ps;\r
2248                 },\r
2249 \r
2250                 toHex : function(s) {\r
2251                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2252 \r
2253                         function hex(s) {\r
2254                                 s = parseInt(s).toString(16);\r
2255 \r
2256                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2257                         };\r
2258 \r
2259                         if (c) {\r
2260                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2261 \r
2262                                 return s;\r
2263                         }\r
2264 \r
2265                         return s;\r
2266                 },\r
2267 \r
2268                 getClasses : function() {\r
2269                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2270 \r
2271                         if (t.classes)\r
2272                                 return t.classes;\r
2273 \r
2274                         function addClasses(s) {\r
2275                                 // IE style imports\r
2276                                 each(s.imports, function(r) {\r
2277                                         addClasses(r);\r
2278                                 });\r
2279 \r
2280                                 each(s.cssRules || s.rules, function(r) {\r
2281                                         // Real type or fake it on IE\r
2282                                         switch (r.type || 1) {\r
2283                                                 // Rule\r
2284                                                 case 1:\r
2285                                                         if (r.selectorText) {\r
2286                                                                 each(r.selectorText.split(','), function(v) {\r
2287                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2288 \r
2289                                                                         // Is internal or it doesn't contain a class\r
2290                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2291                                                                                 return;\r
2292 \r
2293                                                                         // Remove everything but class name\r
2294                                                                         ov = v;\r
2295                                                                         v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2296 \r
2297                                                                         // Filter classes\r
2298                                                                         if (f && !(v = f(v, ov)))\r
2299                                                                                 return;\r
2300 \r
2301                                                                         if (!lo[v]) {\r
2302                                                                                 cl.push({'class' : v});\r
2303                                                                                 lo[v] = 1;\r
2304                                                                         }\r
2305                                                                 });\r
2306                                                         }\r
2307                                                         break;\r
2308 \r
2309                                                 // Import\r
2310                                                 case 3:\r
2311                                                         addClasses(r.styleSheet);\r
2312                                                         break;\r
2313                                         }\r
2314                                 });\r
2315                         };\r
2316 \r
2317                         try {\r
2318                                 each(t.doc.styleSheets, addClasses);\r
2319                         } catch (ex) {\r
2320                                 // Ignore\r
2321                         }\r
2322 \r
2323                         if (cl.length > 0)\r
2324                                 t.classes = cl;\r
2325 \r
2326                         return cl;\r
2327                 },\r
2328 \r
2329                 run : function(e, f, s) {\r
2330                         var t = this, o;\r
2331 \r
2332                         if (t.doc && typeof(e) === 'string')\r
2333                                 e = t.get(e);\r
2334 \r
2335                         if (!e)\r
2336                                 return false;\r
2337 \r
2338                         s = s || this;\r
2339                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2340                                 o = [];\r
2341 \r
2342                                 each(e, function(e, i) {\r
2343                                         if (e) {\r
2344                                                 if (typeof(e) == 'string')\r
2345                                                         e = t.doc.getElementById(e);\r
2346 \r
2347                                                 o.push(f.call(s, e, i));\r
2348                                         }\r
2349                                 });\r
2350 \r
2351                                 return o;\r
2352                         }\r
2353 \r
2354                         return f.call(s, e);\r
2355                 },\r
2356 \r
2357                 getAttribs : function(n) {\r
2358                         var o;\r
2359 \r
2360                         n = this.get(n);\r
2361 \r
2362                         if (!n)\r
2363                                 return [];\r
2364 \r
2365                         if (isIE) {\r
2366                                 o = [];\r
2367 \r
2368                                 // Object will throw exception in IE\r
2369                                 if (n.nodeName == 'OBJECT')\r
2370                                         return n.attributes;\r
2371 \r
2372                                 // IE doesn't keep the selected attribute if you clone option elements\r
2373                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2374                                         o.push({specified : 1, nodeName : 'selected'});\r
2375 \r
2376                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2377                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2378                                         o.push({specified : 1, nodeName : a});\r
2379                                 });\r
2380 \r
2381                                 return o;\r
2382                         }\r
2383 \r
2384                         return n.attributes;\r
2385                 },\r
2386 \r
2387                 destroy : function(s) {\r
2388                         var t = this;\r
2389 \r
2390                         if (t.events)\r
2391                                 t.events.destroy();\r
2392 \r
2393                         t.win = t.doc = t.root = t.events = null;\r
2394 \r
2395                         // Manual destroy then remove unload handler\r
2396                         if (!s)\r
2397                                 tinymce.removeUnload(t.destroy);\r
2398                 },\r
2399 \r
2400                 createRng : function() {\r
2401                         var d = this.doc;\r
2402 \r
2403                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2404                 },\r
2405 \r
2406                 nodeIndex : function(node, normalized) {\r
2407                         var idx = 0, lastNode, nodeType;\r
2408 \r
2409                         if (node) {\r
2410                                 for (node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2411                                         nodeType = node.nodeType;\r
2412 \r
2413                                         // Text nodes needs special treatment if the normalized argument is specified\r
2414                                         if (normalized && nodeType == 3) {\r
2415                                                 // Checks if the current node has contents and that the last node is a non text node or empty\r
2416                                                 if (node.nodeValue.length > 0 && (lastNode.nodeType != nodeType || lastNode.nodeValue.length === 0))\r
2417                                                         idx++;\r
2418                                         } else\r
2419                                                 idx++;\r
2420 \r
2421                                         lastNode = node;\r
2422                                 }\r
2423                         }\r
2424 \r
2425                         return idx;\r
2426                 },\r
2427 \r
2428                 split : function(pe, e, re) {\r
2429                         var t = this, r = t.createRng(), bef, aft, pa;\r
2430 \r
2431                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2432                         // but we don't want that in our code since it serves no purpose for the end user\r
2433                         // For example if this is chopped:\r
2434                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2435                         // would produce:\r
2436                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2437                         // this function will then trim of empty edges and produce:\r
2438                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2439                         function trim(node) {\r
2440                                 var i, children = node.childNodes;\r
2441 \r
2442                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2443                                         return;\r
2444 \r
2445                                 for (i = children.length - 1; i >= 0; i--)\r
2446                                         trim(children[i]);\r
2447 \r
2448                                 if (node.nodeType != 9) {\r
2449                                         // Keep non whitespace text nodes\r
2450                                         if (node.nodeType == 3 && node.nodeValue.length > 0)\r
2451                                                 return;\r
2452 \r
2453                                         if (node.nodeType == 1) {\r
2454                                                 // If the only child is a bookmark then move it up\r
2455                                                 children = node.childNodes;\r
2456                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2457                                                         node.parentNode.insertBefore(children[0], node);\r
2458 \r
2459                                                 // Keep non empty elements or img, hr etc\r
2460                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2461                                                         return;\r
2462                                         }\r
2463 \r
2464                                         t.remove(node);\r
2465                                 }\r
2466 \r
2467                                 return node;\r
2468                         };\r
2469 \r
2470                         if (pe && e) {\r
2471                                 // Get before chunk\r
2472                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2473                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2474                                 bef = r.extractContents();\r
2475 \r
2476                                 // Get after chunk\r
2477                                 r = t.createRng();\r
2478                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2479                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2480                                 aft = r.extractContents();\r
2481 \r
2482                                 // Insert before chunk\r
2483                                 pa = pe.parentNode;\r
2484                                 pa.insertBefore(trim(bef), pe);\r
2485 \r
2486                                 // Insert middle chunk\r
2487                                 if (re)\r
2488                                         pa.replaceChild(re, e);\r
2489                                 else\r
2490                                         pa.insertBefore(e, pe);\r
2491 \r
2492                                 // Insert after chunk\r
2493                                 pa.insertBefore(trim(aft), pe);\r
2494                                 t.remove(pe);\r
2495 \r
2496                                 return re || e;\r
2497                         }\r
2498                 },\r
2499 \r
2500                 bind : function(target, name, func, scope) {\r
2501                         var t = this;\r
2502 \r
2503                         if (!t.events)\r
2504                                 t.events = new tinymce.dom.EventUtils();\r
2505 \r
2506                         return t.events.add(target, name, func, scope || this);\r
2507                 },\r
2508 \r
2509                 unbind : function(target, name, func) {\r
2510                         var t = this;\r
2511 \r
2512                         if (!t.events)\r
2513                                 t.events = new tinymce.dom.EventUtils();\r
2514 \r
2515                         return t.events.remove(target, name, func);\r
2516                 },\r
2517 \r
2518 \r
2519                 _findSib : function(node, selector, name) {\r
2520                         var t = this, f = selector;\r
2521 \r
2522                         if (node) {\r
2523                                 // If expression make a function of it using is\r
2524                                 if (is(f, 'string')) {\r
2525                                         f = function(node) {\r
2526                                                 return t.is(node, selector);\r
2527                                         };\r
2528                                 }\r
2529 \r
2530                                 // Loop all siblings\r
2531                                 for (node = node[name]; node; node = node[name]) {\r
2532                                         if (f(node))\r
2533                                                 return node;\r
2534                                 }\r
2535                         }\r
2536 \r
2537                         return null;\r
2538                 },\r
2539 \r
2540                 _isRes : function(c) {\r
2541                         // Is live resizble element\r
2542                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2543                 }\r
2544 \r
2545                 /*\r
2546                 walk : function(n, f, s) {\r
2547                         var d = this.doc, w;\r
2548 \r
2549                         if (d.createTreeWalker) {\r
2550                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2551 \r
2552                                 while ((n = w.nextNode()) != null)\r
2553                                         f.call(s || this, n);\r
2554                         } else\r
2555                                 tinymce.walk(n, f, 'childNodes', s);\r
2556                 }\r
2557                 */\r
2558 \r
2559                 /*\r
2560                 toRGB : function(s) {\r
2561                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2562 \r
2563                         if (c) {\r
2564                                 // #FFF -> #FFFFFF\r
2565                                 if (!is(c[3]))\r
2566                                         c[3] = c[2] = c[1];\r
2567 \r
2568                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2569                         }\r
2570 \r
2571                         return s;\r
2572                 }\r
2573                 */\r
2574         });\r
2575 \r
2576         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2577 })(tinymce);\r
2578 \r
2579 (function(ns) {\r
2580         // Range constructor\r
2581         function Range(dom) {\r
2582                 var t = this,\r
2583                         doc = dom.doc,\r
2584                         EXTRACT = 0,\r
2585                         CLONE = 1,\r
2586                         DELETE = 2,\r
2587                         TRUE = true,\r
2588                         FALSE = false,\r
2589                         START_OFFSET = 'startOffset',\r
2590                         START_CONTAINER = 'startContainer',\r
2591                         END_CONTAINER = 'endContainer',\r
2592                         END_OFFSET = 'endOffset',\r
2593                         extend = tinymce.extend,\r
2594                         nodeIndex = dom.nodeIndex;\r
2595 \r
2596                 extend(t, {\r
2597                         // Inital states\r
2598                         startContainer : doc,\r
2599                         startOffset : 0,\r
2600                         endContainer : doc,\r
2601                         endOffset : 0,\r
2602                         collapsed : TRUE,\r
2603                         commonAncestorContainer : doc,\r
2604 \r
2605                         // Range constants\r
2606                         START_TO_START : 0,\r
2607                         START_TO_END : 1,\r
2608                         END_TO_END : 2,\r
2609                         END_TO_START : 3,\r
2610 \r
2611                         // Public methods\r
2612                         setStart : setStart,\r
2613                         setEnd : setEnd,\r
2614                         setStartBefore : setStartBefore,\r
2615                         setStartAfter : setStartAfter,\r
2616                         setEndBefore : setEndBefore,\r
2617                         setEndAfter : setEndAfter,\r
2618                         collapse : collapse,\r
2619                         selectNode : selectNode,\r
2620                         selectNodeContents : selectNodeContents,\r
2621                         compareBoundaryPoints : compareBoundaryPoints,\r
2622                         deleteContents : deleteContents,\r
2623                         extractContents : extractContents,\r
2624                         cloneContents : cloneContents,\r
2625                         insertNode : insertNode,\r
2626                         surroundContents : surroundContents,\r
2627                         cloneRange : cloneRange\r
2628                 });\r
2629 \r
2630                 function setStart(n, o) {\r
2631                         _setEndPoint(TRUE, n, o);\r
2632                 };\r
2633 \r
2634                 function setEnd(n, o) {\r
2635                         _setEndPoint(FALSE, n, o);\r
2636                 };\r
2637 \r
2638                 function setStartBefore(n) {\r
2639                         setStart(n.parentNode, nodeIndex(n));\r
2640                 };\r
2641 \r
2642                 function setStartAfter(n) {\r
2643                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2644                 };\r
2645 \r
2646                 function setEndBefore(n) {\r
2647                         setEnd(n.parentNode, nodeIndex(n));\r
2648                 };\r
2649 \r
2650                 function setEndAfter(n) {\r
2651                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2652                 };\r
2653 \r
2654                 function collapse(ts) {\r
2655                         if (ts) {\r
2656                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2657                                 t[END_OFFSET] = t[START_OFFSET];\r
2658                         } else {\r
2659                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2660                                 t[START_OFFSET] = t[END_OFFSET];\r
2661                         }\r
2662 \r
2663                         t.collapsed = TRUE;\r
2664                 };\r
2665 \r
2666                 function selectNode(n) {\r
2667                         setStartBefore(n);\r
2668                         setEndAfter(n);\r
2669                 };\r
2670 \r
2671                 function selectNodeContents(n) {\r
2672                         setStart(n, 0);\r
2673                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2674                 };\r
2675 \r
2676                 function compareBoundaryPoints(h, r) {\r
2677                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2678 \r
2679                         // Check START_TO_START\r
2680                         if (h === 0)\r
2681                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2682 \r
2683                         // Check START_TO_END\r
2684                         if (h === 1)\r
2685                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2686 \r
2687                         // Check END_TO_END\r
2688                         if (h === 2)\r
2689                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2690 \r
2691                         // Check END_TO_START\r
2692                         if (h === 3)\r
2693                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2694                 };\r
2695 \r
2696                 function deleteContents() {\r
2697                         _traverse(DELETE);\r
2698                 };\r
2699 \r
2700                 function extractContents() {\r
2701                         return _traverse(EXTRACT);\r
2702                 };\r
2703 \r
2704                 function cloneContents() {\r
2705                         return _traverse(CLONE);\r
2706                 };\r
2707 \r
2708                 function insertNode(n) {\r
2709                         var startContainer = this[START_CONTAINER],\r
2710                                 startOffset = this[START_OFFSET], nn, o;\r
2711 \r
2712                         // Node is TEXT_NODE or CDATA\r
2713                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2714                                 if (!startOffset) {\r
2715                                         // At the start of text\r
2716                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2717                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2718                                         // At the end of text\r
2719                                         dom.insertAfter(n, startContainer);\r
2720                                 } else {\r
2721                                         // Middle, need to split\r
2722                                         nn = startContainer.splitText(startOffset);\r
2723                                         startContainer.parentNode.insertBefore(n, nn);\r
2724                                 }\r
2725                         } else {\r
2726                                 // Insert element node\r
2727                                 if (startContainer.childNodes.length > 0)\r
2728                                         o = startContainer.childNodes[startOffset];\r
2729 \r
2730                                 if (o)\r
2731                                         startContainer.insertBefore(n, o);\r
2732                                 else\r
2733                                         startContainer.appendChild(n);\r
2734                         }\r
2735                 };\r
2736 \r
2737                 function surroundContents(n) {\r
2738                         var f = t.extractContents();\r
2739 \r
2740                         t.insertNode(n);\r
2741                         n.appendChild(f);\r
2742                         t.selectNode(n);\r
2743                 };\r
2744 \r
2745                 function cloneRange() {\r
2746                         return extend(new Range(dom), {\r
2747                                 startContainer : t[START_CONTAINER],\r
2748                                 startOffset : t[START_OFFSET],\r
2749                                 endContainer : t[END_CONTAINER],\r
2750                                 endOffset : t[END_OFFSET],\r
2751                                 collapsed : t.collapsed,\r
2752                                 commonAncestorContainer : t.commonAncestorContainer\r
2753                         });\r
2754                 };\r
2755 \r
2756                 // Private methods\r
2757 \r
2758                 function _getSelectedNode(container, offset) {\r
2759                         var child;\r
2760 \r
2761                         if (container.nodeType == 3 /* TEXT_NODE */)\r
2762                                 return container;\r
2763 \r
2764                         if (offset < 0)\r
2765                                 return container;\r
2766 \r
2767                         child = container.firstChild;\r
2768                         while (child && offset > 0) {\r
2769                                 --offset;\r
2770                                 child = child.nextSibling;\r
2771                         }\r
2772 \r
2773                         if (child)\r
2774                                 return child;\r
2775 \r
2776                         return container;\r
2777                 };\r
2778 \r
2779                 function _isCollapsed() {\r
2780                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
2781                 };\r
2782 \r
2783                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
2784                         var c, offsetC, n, cmnRoot, childA, childB;\r
2785 \r
2786                         // In the first case the boundary-points have the same container. A is before B\r
2787                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
2788                         // equal to the offset of B, and A is after B if its offset is greater than the\r
2789                         // offset of B.\r
2790                         if (containerA == containerB) {\r
2791                                 if (offsetA == offsetB)\r
2792                                         return 0; // equal\r
2793 \r
2794                                 if (offsetA < offsetB)\r
2795                                         return -1; // before\r
2796 \r
2797                                 return 1; // after\r
2798                         }\r
2799 \r
2800                         // In the second case a child node C of the container of A is an ancestor\r
2801                         // container of B. In this case, A is before B if the offset of A is less than or\r
2802                         // equal to the index of the child node C and A is after B otherwise.\r
2803                         c = containerB;\r
2804                         while (c && c.parentNode != containerA)\r
2805                                 c = c.parentNode;\r
2806 \r
2807                         if (c) {\r
2808                                 offsetC = 0;\r
2809                                 n = containerA.firstChild;\r
2810 \r
2811                                 while (n != c && offsetC < offsetA) {\r
2812                                         offsetC++;\r
2813                                         n = n.nextSibling;\r
2814                                 }\r
2815 \r
2816                                 if (offsetA <= offsetC)\r
2817                                         return -1; // before\r
2818 \r
2819                                 return 1; // after\r
2820                         }\r
2821 \r
2822                         // In the third case a child node C of the container of B is an ancestor container\r
2823                         // of A. In this case, A is before B if the index of the child node C is less than\r
2824                         // the offset of B and A is after B otherwise.\r
2825                         c = containerA;\r
2826                         while (c && c.parentNode != containerB) {\r
2827                                 c = c.parentNode;\r
2828                         }\r
2829 \r
2830                         if (c) {\r
2831                                 offsetC = 0;\r
2832                                 n = containerB.firstChild;\r
2833 \r
2834                                 while (n != c && offsetC < offsetB) {\r
2835                                         offsetC++;\r
2836                                         n = n.nextSibling;\r
2837                                 }\r
2838 \r
2839                                 if (offsetC < offsetB)\r
2840                                         return -1; // before\r
2841 \r
2842                                 return 1; // after\r
2843                         }\r
2844 \r
2845                         // In the fourth case, none of three other cases hold: the containers of A and B\r
2846                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
2847                         // the container of A is before the container of B in a pre-order traversal of the\r
2848                         // Ranges' context tree and A is after B otherwise.\r
2849                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
2850                         childA = containerA;\r
2851 \r
2852                         while (childA && childA.parentNode != cmnRoot)\r
2853                                 childA = childA.parentNode;\r
2854 \r
2855                         if (!childA)\r
2856                                 childA = cmnRoot;\r
2857 \r
2858                         childB = containerB;\r
2859                         while (childB && childB.parentNode != cmnRoot)\r
2860                                 childB = childB.parentNode;\r
2861 \r
2862                         if (!childB)\r
2863                                 childB = cmnRoot;\r
2864 \r
2865                         if (childA == childB)\r
2866                                 return 0; // equal\r
2867 \r
2868                         n = cmnRoot.firstChild;\r
2869                         while (n) {\r
2870                                 if (n == childA)\r
2871                                         return -1; // before\r
2872 \r
2873                                 if (n == childB)\r
2874                                         return 1; // after\r
2875 \r
2876                                 n = n.nextSibling;\r
2877                         }\r
2878                 };\r
2879 \r
2880                 function _setEndPoint(st, n, o) {\r
2881                         var ec, sc;\r
2882 \r
2883                         if (st) {\r
2884                                 t[START_CONTAINER] = n;\r
2885                                 t[START_OFFSET] = o;\r
2886                         } else {\r
2887                                 t[END_CONTAINER] = n;\r
2888                                 t[END_OFFSET] = o;\r
2889                         }\r
2890 \r
2891                         // If one boundary-point of a Range is set to have a root container\r
2892                         // other than the current one for the Range, the Range is collapsed to\r
2893                         // the new position. This enforces the restriction that both boundary-\r
2894                         // points of a Range must have the same root container.\r
2895                         ec = t[END_CONTAINER];\r
2896                         while (ec.parentNode)\r
2897                                 ec = ec.parentNode;\r
2898 \r
2899                         sc = t[START_CONTAINER];\r
2900                         while (sc.parentNode)\r
2901                                 sc = sc.parentNode;\r
2902 \r
2903                         if (sc == ec) {\r
2904                                 // The start position of a Range is guaranteed to never be after the\r
2905                                 // end position. To enforce this restriction, if the start is set to\r
2906                                 // be at a position after the end, the Range is collapsed to that\r
2907                                 // position.\r
2908                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
2909                                         t.collapse(st);\r
2910                         } else\r
2911                                 t.collapse(st);\r
2912 \r
2913                         t.collapsed = _isCollapsed();\r
2914                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
2915                 };\r
2916 \r
2917                 function _traverse(how) {\r
2918                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2919 \r
2920                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
2921                                 return _traverseSameContainer(how);\r
2922 \r
2923                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2924                                 if (p == t[START_CONTAINER])\r
2925                                         return _traverseCommonStartContainer(c, how);\r
2926 \r
2927                                 ++endContainerDepth;\r
2928                         }\r
2929 \r
2930                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2931                                 if (p == t[END_CONTAINER])\r
2932                                         return _traverseCommonEndContainer(c, how);\r
2933 \r
2934                                 ++startContainerDepth;\r
2935                         }\r
2936 \r
2937                         depthDiff = startContainerDepth - endContainerDepth;\r
2938 \r
2939                         startNode = t[START_CONTAINER];\r
2940                         while (depthDiff > 0) {\r
2941                                 startNode = startNode.parentNode;\r
2942                                 depthDiff--;\r
2943                         }\r
2944 \r
2945                         endNode = t[END_CONTAINER];\r
2946                         while (depthDiff < 0) {\r
2947                                 endNode = endNode.parentNode;\r
2948                                 depthDiff++;\r
2949                         }\r
2950 \r
2951                         // ascend the ancestor hierarchy until we have a common parent.\r
2952                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
2953                                 startNode = sp;\r
2954                                 endNode = ep;\r
2955                         }\r
2956 \r
2957                         return _traverseCommonAncestors(startNode, endNode, how);\r
2958                 };\r
2959 \r
2960                  function _traverseSameContainer(how) {\r
2961                         var frag, s, sub, n, cnt, sibling, xferNode;\r
2962 \r
2963                         if (how != DELETE)\r
2964                                 frag = doc.createDocumentFragment();\r
2965 \r
2966                         // If selection is empty, just return the fragment\r
2967                         if (t[START_OFFSET] == t[END_OFFSET])\r
2968                                 return frag;\r
2969 \r
2970                         // Text node needs special case handling\r
2971                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
2972                                 // get the substring\r
2973                                 s = t[START_CONTAINER].nodeValue;\r
2974                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
2975 \r
2976                                 // set the original text node to its new value\r
2977                                 if (how != CLONE) {\r
2978                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
2979 \r
2980                                         // Nothing is partially selected, so collapse to start point\r
2981                                         t.collapse(TRUE);\r
2982                                 }\r
2983 \r
2984                                 if (how == DELETE)\r
2985                                         return;\r
2986 \r
2987                                 frag.appendChild(doc.createTextNode(sub));\r
2988                                 return frag;\r
2989                         }\r
2990 \r
2991                         // Copy nodes between the start/end offsets.\r
2992                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
2993                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
2994 \r
2995                         while (cnt > 0) {\r
2996                                 sibling = n.nextSibling;\r
2997                                 xferNode = _traverseFullySelected(n, how);\r
2998 \r
2999                                 if (frag)\r
3000                                         frag.appendChild( xferNode );\r
3001 \r
3002                                 --cnt;\r
3003                                 n = sibling;\r
3004                         }\r
3005 \r
3006                         // Nothing is partially selected, so collapse to start point\r
3007                         if (how != CLONE)\r
3008                                 t.collapse(TRUE);\r
3009 \r
3010                         return frag;\r
3011                 };\r
3012 \r
3013                 function _traverseCommonStartContainer(endAncestor, how) {\r
3014                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3015 \r
3016                         if (how != DELETE)\r
3017                                 frag = doc.createDocumentFragment();\r
3018 \r
3019                         n = _traverseRightBoundary(endAncestor, how);\r
3020 \r
3021                         if (frag)\r
3022                                 frag.appendChild(n);\r
3023 \r
3024                         endIdx = nodeIndex(endAncestor);\r
3025                         cnt = endIdx - t[START_OFFSET];\r
3026 \r
3027                         if (cnt <= 0) {\r
3028                                 // Collapse to just before the endAncestor, which\r
3029                                 // is partially selected.\r
3030                                 if (how != CLONE) {\r
3031                                         t.setEndBefore(endAncestor);\r
3032                                         t.collapse(FALSE);\r
3033                                 }\r
3034 \r
3035                                 return frag;\r
3036                         }\r
3037 \r
3038                         n = endAncestor.previousSibling;\r
3039                         while (cnt > 0) {\r
3040                                 sibling = n.previousSibling;\r
3041                                 xferNode = _traverseFullySelected(n, how);\r
3042 \r
3043                                 if (frag)\r
3044                                         frag.insertBefore(xferNode, frag.firstChild);\r
3045 \r
3046                                 --cnt;\r
3047                                 n = sibling;\r
3048                         }\r
3049 \r
3050                         // Collapse to just before the endAncestor, which\r
3051                         // is partially selected.\r
3052                         if (how != CLONE) {\r
3053                                 t.setEndBefore(endAncestor);\r
3054                                 t.collapse(FALSE);\r
3055                         }\r
3056 \r
3057                         return frag;\r
3058                 };\r
3059 \r
3060                 function _traverseCommonEndContainer(startAncestor, how) {\r
3061                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3062 \r
3063                         if (how != DELETE)\r
3064                                 frag = doc.createDocumentFragment();\r
3065 \r
3066                         n = _traverseLeftBoundary(startAncestor, how);\r
3067                         if (frag)\r
3068                                 frag.appendChild(n);\r
3069 \r
3070                         startIdx = nodeIndex(startAncestor);\r
3071                         ++startIdx;  // Because we already traversed it....\r
3072 \r
3073                         cnt = t[END_OFFSET] - startIdx;\r
3074                         n = startAncestor.nextSibling;\r
3075                         while (cnt > 0) {\r
3076                                 sibling = n.nextSibling;\r
3077                                 xferNode = _traverseFullySelected(n, how);\r
3078 \r
3079                                 if (frag)\r
3080                                         frag.appendChild(xferNode);\r
3081 \r
3082                                 --cnt;\r
3083                                 n = sibling;\r
3084                         }\r
3085 \r
3086                         if (how != CLONE) {\r
3087                                 t.setStartAfter(startAncestor);\r
3088                                 t.collapse(TRUE);\r
3089                         }\r
3090 \r
3091                         return frag;\r
3092                 };\r
3093 \r
3094                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3095                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3096 \r
3097                         if (how != DELETE)\r
3098                                 frag = doc.createDocumentFragment();\r
3099 \r
3100                         n = _traverseLeftBoundary(startAncestor, how);\r
3101                         if (frag)\r
3102                                 frag.appendChild(n);\r
3103 \r
3104                         commonParent = startAncestor.parentNode;\r
3105                         startOffset = nodeIndex(startAncestor);\r
3106                         endOffset = nodeIndex(endAncestor);\r
3107                         ++startOffset;\r
3108 \r
3109                         cnt = endOffset - startOffset;\r
3110                         sibling = startAncestor.nextSibling;\r
3111 \r
3112                         while (cnt > 0) {\r
3113                                 nextSibling = sibling.nextSibling;\r
3114                                 n = _traverseFullySelected(sibling, how);\r
3115 \r
3116                                 if (frag)\r
3117                                         frag.appendChild(n);\r
3118 \r
3119                                 sibling = nextSibling;\r
3120                                 --cnt;\r
3121                         }\r
3122 \r
3123                         n = _traverseRightBoundary(endAncestor, how);\r
3124 \r
3125                         if (frag)\r
3126                                 frag.appendChild(n);\r
3127 \r
3128                         if (how != CLONE) {\r
3129                                 t.setStartAfter(startAncestor);\r
3130                                 t.collapse(TRUE);\r
3131                         }\r
3132 \r
3133                         return frag;\r
3134                 };\r
3135 \r
3136                 function _traverseRightBoundary(root, how) {\r
3137                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3138 \r
3139                         if (next == root)\r
3140                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3141 \r
3142                         parent = next.parentNode;\r
3143                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3144 \r
3145                         while (parent) {\r
3146                                 while (next) {\r
3147                                         prevSibling = next.previousSibling;\r
3148                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3149 \r
3150                                         if (how != DELETE)\r
3151                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3152 \r
3153                                         isFullySelected = TRUE;\r
3154                                         next = prevSibling;\r
3155                                 }\r
3156 \r
3157                                 if (parent == root)\r
3158                                         return clonedParent;\r
3159 \r
3160                                 next = parent.previousSibling;\r
3161                                 parent = parent.parentNode;\r
3162 \r
3163                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3164 \r
3165                                 if (how != DELETE)\r
3166                                         clonedGrandParent.appendChild(clonedParent);\r
3167 \r
3168                                 clonedParent = clonedGrandParent;\r
3169                         }\r
3170                 };\r
3171 \r
3172                 function _traverseLeftBoundary(root, how) {\r
3173                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3174 \r
3175                         if (next == root)\r
3176                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3177 \r
3178                         parent = next.parentNode;\r
3179                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3180 \r
3181                         while (parent) {\r
3182                                 while (next) {\r
3183                                         nextSibling = next.nextSibling;\r
3184                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3185 \r
3186                                         if (how != DELETE)\r
3187                                                 clonedParent.appendChild(clonedChild);\r
3188 \r
3189                                         isFullySelected = TRUE;\r
3190                                         next = nextSibling;\r
3191                                 }\r
3192 \r
3193                                 if (parent == root)\r
3194                                         return clonedParent;\r
3195 \r
3196                                 next = parent.nextSibling;\r
3197                                 parent = parent.parentNode;\r
3198 \r
3199                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3200 \r
3201                                 if (how != DELETE)\r
3202                                         clonedGrandParent.appendChild(clonedParent);\r
3203 \r
3204                                 clonedParent = clonedGrandParent;\r
3205                         }\r
3206                 };\r
3207 \r
3208                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3209                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3210 \r
3211                         if (isFullySelected)\r
3212                                 return _traverseFullySelected(n, how);\r
3213 \r
3214                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3215                                 txtValue = n.nodeValue;\r
3216 \r
3217                                 if (isLeft) {\r
3218                                         offset = t[START_OFFSET];\r
3219                                         newNodeValue = txtValue.substring(offset);\r
3220                                         oldNodeValue = txtValue.substring(0, offset);\r
3221                                 } else {\r
3222                                         offset = t[END_OFFSET];\r
3223                                         newNodeValue = txtValue.substring(0, offset);\r
3224                                         oldNodeValue = txtValue.substring(offset);\r
3225                                 }\r
3226 \r
3227                                 if (how != CLONE)\r
3228                                         n.nodeValue = oldNodeValue;\r
3229 \r
3230                                 if (how == DELETE)\r
3231                                         return;\r
3232 \r
3233                                 newNode = n.cloneNode(FALSE);\r
3234                                 newNode.nodeValue = newNodeValue;\r
3235 \r
3236                                 return newNode;\r
3237                         }\r
3238 \r
3239                         if (how == DELETE)\r
3240                                 return;\r
3241 \r
3242                         return n.cloneNode(FALSE);\r
3243                 };\r
3244 \r
3245                 function _traverseFullySelected(n, how) {\r
3246                         if (how != DELETE)\r
3247                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3248 \r
3249                         n.parentNode.removeChild(n);\r
3250                 };\r
3251         };\r
3252 \r
3253         ns.Range = Range;\r
3254 })(tinymce.dom);\r
3255 \r
3256 (function() {\r
3257         function Selection(selection) {\r
3258                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3259 \r
3260                 // Compares two IE specific ranges to see if they are different\r
3261                 // this method is useful when invalidating the cached selection range\r
3262                 function compareRanges(rng1, rng2) {\r
3263                         if (rng1 && rng2) {\r
3264                                 // Both are control ranges and the selected element matches\r
3265                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
3266                                         return TRUE;\r
3267 \r
3268                                 // Both are text ranges and the range matches\r
3269                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {\r
3270                                         // IE will say that the range is equal then produce an invalid argument exception\r
3271                                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3272                                         // This hack will invalidate the range cache if the exception occurs\r
3273                                         try {\r
3274                                                 // Try accessing nextSibling will producer an invalid argument some times\r
3275                                                 range.startContainer.nextSibling;\r
3276                                                 return TRUE;\r
3277                                         } catch (ex) {\r
3278                                                 // Ignore\r
3279                                         }\r
3280                                 }\r
3281                         }\r
3282 \r
3283                         return FALSE;\r
3284                 };\r
3285 \r
3286                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3287                 function getRange() {\r
3288                         var ieRange = selection.getRng(), domRange = dom.createRng(), ieRange2, element, collapsed, isMerged;\r
3289 \r
3290                         // If selection is outside the current document just return an empty range\r
3291                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3292                         if (element.ownerDocument != dom.doc)\r
3293                                 return domRange;\r
3294 \r
3295                         // Handle control selection or text selection of a image\r
3296                         if (ieRange.item || !element.hasChildNodes()) {\r
3297                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3298                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3299 \r
3300                                 return domRange;\r
3301                         }\r
3302 \r
3303                         // Duplicare IE selection range and check if the range is collapsed\r
3304                         ieRange2 = ieRange.duplicate();\r
3305                         collapsed = selection.isCollapsed();\r
3306 \r
3307                         // Insert invisible start marker\r
3308                         ieRange.collapse();\r
3309                         ieRange.pasteHTML('<span id="_mce_start" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
3310 \r
3311                         // Insert invisible end marker\r
3312                         if (!collapsed) {\r
3313                                 ieRange2.collapse(FALSE);\r
3314                                 ieRange2.pasteHTML('<span id="_mce_end" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
3315                         }\r
3316 \r
3317                         // Sets the end point of the range by looking for the marker\r
3318                         // This method also merges the text nodes it splits so that\r
3319                         // the DOM doesn't get fragmented.\r
3320                         function setEndPoint(start) {\r
3321                                 var container, offset, marker, sibling;\r
3322 \r
3323                                 // Look for endpoint marker\r
3324                                 marker = dom.get('_mce_' + (start ? 'start' : 'end'));\r
3325                                 sibling = marker.previousSibling;\r
3326 \r
3327                                 // Is marker after a text node\r
3328                                 if (sibling && sibling.nodeType == 3) {\r
3329                                         // Get container node and calc offset\r
3330                                         container = sibling;\r
3331                                         offset = container.nodeValue.length;\r
3332                                         dom.remove(marker);\r
3333 \r
3334                                         // Merge text nodes to reduce DOM fragmentation\r
3335                                         sibling = container.nextSibling;\r
3336                                         if (sibling && sibling.nodeType == 3) {\r
3337                                                 isMerged = TRUE;\r
3338                                                 container.appendData(sibling.nodeValue);\r
3339                                                 dom.remove(sibling);\r
3340                                         }\r
3341                                 } else {\r
3342                                         sibling = marker.nextSibling;\r
3343 \r
3344                                         // Is marker before a text node\r
3345                                         if (sibling && sibling.nodeType == 3) {\r
3346                                                 container = sibling;\r
3347                                                 offset = 0;\r
3348                                         } else {\r
3349                                                 // Is marker before an element\r
3350                                                 if (sibling)\r
3351                                                         offset = dom.nodeIndex(sibling) - 1;\r
3352                                                 else\r
3353                                                         offset = dom.nodeIndex(marker);\r
3354 \r
3355                                                 container = marker.parentNode;\r
3356                                         }\r
3357 \r
3358                                         dom.remove(marker);\r
3359                                 }\r
3360 \r
3361                                 // Set start of range\r
3362                                 if (start)\r
3363                                         domRange.setStart(container, offset);\r
3364 \r
3365                                 // Set end of range or automatically if it's collapsed to increase performance\r
3366                                 if (!start || collapsed)\r
3367                                         domRange.setEnd(container, offset);\r
3368                         };\r
3369 \r
3370                         // Set start of range\r
3371                         setEndPoint(TRUE);\r
3372 \r
3373                         // Set end of range if needed\r
3374                         if (!collapsed)\r
3375                                 setEndPoint(FALSE);\r
3376 \r
3377                         // Restore selection if the range contents was merged\r
3378                         // since the selection was then moved since the text nodes got changed\r
3379                         if (isMerged)\r
3380                                 t.addRange(domRange);\r
3381 \r
3382                         return domRange;\r
3383                 };\r
3384 \r
3385                 this.addRange = function(rng) {\r
3386                         var ieRng, ieRng2, doc = selection.dom.doc, body = doc.body, startPos, endPos, sc, so, ec, eo, marker, lastIndex, skipStart, skipEnd;\r
3387 \r
3388                         this.destroy();\r
3389 \r
3390                         // Setup some shorter versions\r
3391                         sc = rng.startContainer;\r
3392                         so = rng.startOffset;\r
3393                         ec = rng.endContainer;\r
3394                         eo = rng.endOffset;\r
3395                         ieRng = body.createTextRange();\r
3396 \r
3397                         // If document selection move caret to first node in document\r
3398                         if (sc == doc || ec == doc) {\r
3399                                 ieRng = body.createTextRange();\r
3400                                 ieRng.collapse();\r
3401                                 ieRng.select();\r
3402                                 return;\r
3403                         }\r
3404 \r
3405                         // If child index resolve it\r
3406                         if (sc.nodeType == 1 && sc.hasChildNodes()) {\r
3407                                 lastIndex = sc.childNodes.length - 1;\r
3408 \r
3409                                 // Index is higher that the child count then we need to jump over the start container\r
3410                                 if (so > lastIndex) {\r
3411                                         skipStart = 1;\r
3412                                         sc = sc.childNodes[lastIndex];\r
3413                                 } else\r
3414                                         sc = sc.childNodes[so];\r
3415 \r
3416                                 // Child was text node then move offset to start of it\r
3417                                 if (sc.nodeType == 3)\r
3418                                         so = 0;\r
3419                         }\r
3420 \r
3421                         // If child index resolve it\r
3422                         if (ec.nodeType == 1 && ec.hasChildNodes()) {\r
3423                                 lastIndex = ec.childNodes.length - 1;\r
3424 \r
3425                                 if (eo == 0) {\r
3426                                         skipEnd = 1;\r
3427                                         ec = ec.childNodes[0];\r
3428                                 } else {\r
3429                                         ec = ec.childNodes[Math.min(lastIndex, eo - 1)];\r
3430 \r
3431                                         // Child was text node then move offset to end of text node\r
3432                                         if (ec.nodeType == 3)\r
3433                                                 eo = ec.nodeValue.length;\r
3434                                 }\r
3435                         }\r
3436 \r
3437                         // Single element selection\r
3438                         if (sc == ec && sc.nodeType == 1) {\r
3439                                 // Make control selection for some elements\r
3440                                 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {\r
3441                                         ieRng = body.createControlRange();\r
3442                                         ieRng.addElement(sc);\r
3443                                 } else {\r
3444                                         ieRng = body.createTextRange();\r
3445 \r
3446                                         // Padd empty elements with invisible character\r
3447                                         if (!sc.hasChildNodes() && sc.canHaveHTML)\r
3448                                                 sc.innerHTML = invisibleChar;\r
3449 \r
3450                                         // Select element contents\r
3451                                         ieRng.moveToElementText(sc);\r
3452 \r
3453                                         // If it's only containing a padding remove it so the caret remains\r
3454                                         if (sc.innerHTML == invisibleChar) {\r
3455                                                 ieRng.collapse(TRUE);\r
3456                                                 sc.removeChild(sc.firstChild);\r
3457                                         }\r
3458                                 }\r
3459 \r
3460                                 if (so == eo)\r
3461                                         ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);\r
3462 \r
3463                                 ieRng.select();\r
3464                                 ieRng.scrollIntoView();\r
3465                                 return;\r
3466                         }\r
3467 \r
3468                         // Create range and marker\r
3469                         ieRng = body.createTextRange();\r
3470                         marker = doc.createElement('span');\r
3471                         marker.innerHTML = ' ';\r
3472 \r
3473                         // Set start of range to startContainer/startOffset\r
3474                         if (sc.nodeType == 3) {\r
3475                                 // Insert marker after/before startContainer\r
3476                                 if (skipStart)\r
3477                                         dom.insertAfter(marker, sc);\r
3478                                 else\r
3479                                         sc.parentNode.insertBefore(marker, sc);\r
3480 \r
3481                                 // Select marker the caret to offset position\r
3482                                 ieRng.moveToElementText(marker);\r
3483                                 marker.parentNode.removeChild(marker);\r
3484                                 ieRng.move('character', so);\r
3485                         } else {\r
3486                                 ieRng.moveToElementText(sc);\r
3487 \r
3488                                 if (skipStart)\r
3489                                         ieRng.collapse(FALSE);\r
3490                         }\r
3491 \r
3492                         // If same text container then we can do a more simple move\r
3493                         if (sc == ec && sc.nodeType == 3) {\r
3494                                 ieRng.moveEnd('character', eo - so);\r
3495                                 ieRng.select();\r
3496                                 ieRng.scrollIntoView();\r
3497                                 return;\r
3498                         }\r
3499 \r
3500                         // Set end of range to endContainer/endOffset\r
3501                         ieRng2 = body.createTextRange();\r
3502                         if (ec.nodeType == 3) {\r
3503                                 // Insert marker after/before startContainer\r
3504                                 ec.parentNode.insertBefore(marker, ec);\r
3505 \r
3506                                 // Move selection to end marker and move caret to end offset\r
3507                                 ieRng2.moveToElementText(marker);\r
3508                                 marker.parentNode.removeChild(marker);\r
3509                                 ieRng2.move('character', eo);\r
3510                                 ieRng.setEndPoint('EndToStart', ieRng2);\r
3511                         } else {\r
3512                                 ieRng2.moveToElementText(ec);\r
3513                                 ieRng2.collapse(!!skipEnd);\r
3514                                 ieRng.setEndPoint('EndToEnd', ieRng2);\r
3515                         }\r
3516 \r
3517                         ieRng.select();\r
3518                         ieRng.scrollIntoView();\r
3519                 };\r
3520 \r
3521                 this.getRangeAt = function() {\r
3522                         // Setup new range if the cache is empty\r
3523                         if (!range || !compareRanges(lastIERng, selection.getRng())) {\r
3524                                 range = getRange();\r
3525 \r
3526                                 // Store away text range for next call\r
3527                                 lastIERng = selection.getRng();\r
3528                         }\r
3529 \r
3530                         // Return cached range\r
3531                         return range;\r
3532                 };\r
3533 \r
3534                 this.destroy = function() {\r
3535                         // Destroy cached range and last IE range to avoid memory leaks\r
3536                         lastIERng = range = null;\r
3537                 };\r
3538 \r
3539                 // 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
3540                 if (selection.dom.boxModel) {\r
3541                         (function() {\r
3542                                 var doc = dom.doc, body = doc.body, started, startRng;\r
3543 \r
3544                                 // Make HTML element unselectable since we are going to handle selection by hand\r
3545                                 doc.documentElement.unselectable = TRUE;\r
3546 \r
3547                                 // Return range from point or null if it failed\r
3548                                 function rngFromPoint(x, y) {\r
3549                                         var rng = body.createTextRange();\r
3550 \r
3551                                         try {\r
3552                                                 rng.moveToPoint(x, y);\r
3553                                         } catch (ex) {\r
3554                                                 // IE sometimes throws and exception, so lets just ignore it\r
3555                                                 rng = null;\r
3556                                         }\r
3557 \r
3558                                         return rng;\r
3559                                 };\r
3560 \r
3561                                 // Fires while the selection is changing\r
3562                                 function selectionChange(e) {\r
3563                                         var pointRng;\r
3564 \r
3565                                         // Check if the button is down or not\r
3566                                         if (e.button) {\r
3567                                                 // Create range from mouse position\r
3568                                                 pointRng = rngFromPoint(e.x, e.y);\r
3569 \r
3570                                                 if (pointRng) {\r
3571                                                         // Check if pointRange is before/after selection then change the endPoint\r
3572                                                         if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
3573                                                                 pointRng.setEndPoint('StartToStart', startRng);\r
3574                                                         else\r
3575                                                                 pointRng.setEndPoint('EndToEnd', startRng);\r
3576 \r
3577                                                         pointRng.select();\r
3578                                                 }\r
3579                                         } else\r
3580                                                 endSelection();\r
3581                                 }\r
3582 \r
3583                                 // Removes listeners\r
3584                                 function endSelection() {\r
3585                                         dom.unbind(doc, 'mouseup', endSelection);\r
3586                                         dom.unbind(doc, 'mousemove', selectionChange);\r
3587                                         started = 0;\r
3588                                 };\r
3589 \r
3590                                 // Detect when user selects outside BODY\r
3591                                 dom.bind(doc, 'mousedown', function(e) {\r
3592                                         if (e.target.nodeName === 'HTML') {\r
3593                                                 if (started)\r
3594                                                         endSelection();\r
3595 \r
3596                                                 started = 1;\r
3597 \r
3598                                                 // Setup start position\r
3599                                                 startRng = rngFromPoint(e.x, e.y);\r
3600                                                 if (startRng) {\r
3601                                                         // Listen for selection change events\r
3602                                                         dom.bind(doc, 'mouseup', endSelection);\r
3603                                                         dom.bind(doc, 'mousemove', selectionChange);\r
3604 \r
3605                                                         startRng.select();\r
3606                                                 }\r
3607                                         }\r
3608                                 });\r
3609                         })();\r
3610                 }\r
3611         };\r
3612 \r
3613         // Expose the selection object\r
3614         tinymce.dom.TridentSelection = Selection;\r
3615 })();\r
3616 \r
3617 \r
3618 /*\r
3619  * Sizzle CSS Selector Engine - v1.0\r
3620  *  Copyright 2009, The Dojo Foundation\r
3621  *  Released under the MIT, BSD, and GPL Licenses.\r
3622  *  More information: http://sizzlejs.com/\r
3623  */\r
3624 (function(){\r
3625 \r
3626 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,\r
3627         done = 0,\r
3628         toString = Object.prototype.toString,\r
3629         hasDuplicate = false;\r
3630 \r
3631 var Sizzle = function(selector, context, results, seed) {\r
3632         results = results || [];\r
3633         var origContext = context = context || document;\r
3634 \r
3635         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
3636                 return [];\r
3637         }\r
3638         \r
3639         if ( !selector || typeof selector !== "string" ) {\r
3640                 return results;\r
3641         }\r
3642 \r
3643         var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);\r
3644         \r
3645         // Reset the position of the chunker regexp (start from head)\r
3646         chunker.lastIndex = 0;\r
3647         \r
3648         while ( (m = chunker.exec(selector)) !== null ) {\r
3649                 parts.push( m[1] );\r
3650                 \r
3651                 if ( m[2] ) {\r
3652                         extra = RegExp.rightContext;\r
3653                         break;\r
3654                 }\r
3655         }\r
3656 \r
3657         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
3658                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
3659                         set = posProcess( parts[0] + parts[1], context );\r
3660                 } else {\r
3661                         set = Expr.relative[ parts[0] ] ?\r
3662                                 [ context ] :\r
3663                                 Sizzle( parts.shift(), context );\r
3664 \r
3665                         while ( parts.length ) {\r
3666                                 selector = parts.shift();\r
3667 \r
3668                                 if ( Expr.relative[ selector ] )\r
3669                                         selector += parts.shift();\r
3670 \r
3671                                 set = posProcess( selector, set );\r
3672                         }\r
3673                 }\r
3674         } else {\r
3675                 // Take a shortcut and set the context if the root selector is an ID\r
3676                 // (but not if it'll be faster if the inner selector is an ID)\r
3677                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
3678                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
3679                         var ret = Sizzle.find( parts.shift(), context, contextXML );\r
3680                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
3681                 }\r
3682 \r
3683                 if ( context ) {\r
3684                         var ret = seed ?\r
3685                                 { expr: parts.pop(), set: makeArray(seed) } :\r
3686                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
3687                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
3688 \r
3689                         if ( parts.length > 0 ) {\r
3690                                 checkSet = makeArray(set);\r
3691                         } else {\r
3692                                 prune = false;\r
3693                         }\r
3694 \r
3695                         while ( parts.length ) {\r
3696                                 var cur = parts.pop(), pop = cur;\r
3697 \r
3698                                 if ( !Expr.relative[ cur ] ) {\r
3699                                         cur = "";\r
3700                                 } else {\r
3701                                         pop = parts.pop();\r
3702                                 }\r
3703 \r
3704                                 if ( pop == null ) {\r
3705                                         pop = context;\r
3706                                 }\r
3707 \r
3708                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
3709                         }\r
3710                 } else {\r
3711                         checkSet = parts = [];\r
3712                 }\r
3713         }\r
3714 \r
3715         if ( !checkSet ) {\r
3716                 checkSet = set;\r
3717         }\r
3718 \r
3719         if ( !checkSet ) {\r
3720                 throw "Syntax error, unrecognized expression: " + (cur || selector);\r
3721         }\r
3722 \r
3723         if ( toString.call(checkSet) === "[object Array]" ) {\r
3724                 if ( !prune ) {\r
3725                         results.push.apply( results, checkSet );\r
3726                 } else if ( context && context.nodeType === 1 ) {\r
3727                         for ( var i = 0; checkSet[i] != null; i++ ) {\r
3728                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {\r
3729                                         results.push( set[i] );\r
3730                                 }\r
3731                         }\r
3732                 } else {\r
3733                         for ( var i = 0; checkSet[i] != null; i++ ) {\r
3734                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
3735                                         results.push( set[i] );\r
3736                                 }\r
3737                         }\r
3738                 }\r
3739         } else {\r
3740                 makeArray( checkSet, results );\r
3741         }\r
3742 \r
3743         if ( extra ) {\r
3744                 Sizzle( extra, origContext, results, seed );\r
3745                 Sizzle.uniqueSort( results );\r
3746         }\r
3747 \r
3748         return results;\r
3749 };\r
3750 \r
3751 Sizzle.uniqueSort = function(results){\r
3752         if ( sortOrder ) {\r
3753                 hasDuplicate = false;\r
3754                 results.sort(sortOrder);\r
3755 \r
3756                 if ( hasDuplicate ) {\r
3757                         for ( var i = 1; i < results.length; i++ ) {\r
3758                                 if ( results[i] === results[i-1] ) {\r
3759                                         results.splice(i--, 1);\r
3760                                 }\r
3761                         }\r
3762                 }\r
3763         }\r
3764 };\r
3765 \r
3766 Sizzle.matches = function(expr, set){\r
3767         return Sizzle(expr, null, null, set);\r
3768 };\r
3769 \r
3770 Sizzle.find = function(expr, context, isXML){\r
3771         var set, match;\r
3772 \r
3773         if ( !expr ) {\r
3774                 return [];\r
3775         }\r
3776 \r
3777         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
3778                 var type = Expr.order[i], match;\r
3779                 \r
3780                 if ( (match = Expr.match[ type ].exec( expr )) ) {\r
3781                         var left = RegExp.leftContext;\r
3782 \r
3783                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
3784                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
3785                                 set = Expr.find[ type ]( match, context, isXML );\r
3786                                 if ( set != null ) {\r
3787                                         expr = expr.replace( Expr.match[ type ], "" );\r
3788                                         break;\r
3789                                 }\r
3790                         }\r
3791                 }\r
3792         }\r
3793 \r
3794         if ( !set ) {\r
3795                 set = context.getElementsByTagName("*");\r
3796         }\r
3797 \r
3798         return {set: set, expr: expr};\r
3799 };\r
3800 \r
3801 Sizzle.filter = function(expr, set, inplace, not){\r
3802         var old = expr, result = [], curLoop = set, match, anyFound,\r
3803                 isXMLFilter = set && set[0] && isXML(set[0]);\r
3804 \r
3805         while ( expr && set.length ) {\r
3806                 for ( var type in Expr.filter ) {\r
3807                         if ( (match = Expr.match[ type ].exec( expr )) != null ) {\r
3808                                 var filter = Expr.filter[ type ], found, item;\r
3809                                 anyFound = false;\r
3810 \r
3811                                 if ( curLoop == result ) {\r
3812                                         result = [];\r
3813                                 }\r
3814 \r
3815                                 if ( Expr.preFilter[ type ] ) {\r
3816                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
3817 \r
3818                                         if ( !match ) {\r
3819                                                 anyFound = found = true;\r
3820                                         } else if ( match === true ) {\r
3821                                                 continue;\r
3822                                         }\r
3823                                 }\r
3824 \r
3825                                 if ( match ) {\r
3826                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
3827                                                 if ( item ) {\r
3828                                                         found = filter( item, match, i, curLoop );\r
3829                                                         var pass = not ^ !!found;\r
3830 \r
3831                                                         if ( inplace && found != null ) {\r
3832                                                                 if ( pass ) {\r
3833                                                                         anyFound = true;\r
3834                                                                 } else {\r
3835                                                                         curLoop[i] = false;\r
3836                                                                 }\r
3837                                                         } else if ( pass ) {\r
3838                                                                 result.push( item );\r
3839                                                                 anyFound = true;\r
3840                                                         }\r
3841                                                 }\r
3842                                         }\r
3843                                 }\r
3844 \r
3845                                 if ( found !== undefined ) {\r
3846                                         if ( !inplace ) {\r
3847                                                 curLoop = result;\r
3848                                         }\r
3849 \r
3850                                         expr = expr.replace( Expr.match[ type ], "" );\r
3851 \r
3852                                         if ( !anyFound ) {\r
3853                                                 return [];\r
3854                                         }\r
3855 \r
3856                                         break;\r
3857                                 }\r
3858                         }\r
3859                 }\r
3860 \r
3861                 // Improper expression\r
3862                 if ( expr == old ) {\r
3863                         if ( anyFound == null ) {\r
3864                                 throw "Syntax error, unrecognized expression: " + expr;\r
3865                         } else {\r
3866                                 break;\r
3867                         }\r
3868                 }\r
3869 \r
3870                 old = expr;\r
3871         }\r
3872 \r
3873         return curLoop;\r
3874 };\r
3875 \r
3876 var Expr = Sizzle.selectors = {\r
3877         order: [ "ID", "NAME", "TAG" ],\r
3878         match: {\r
3879                 ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,\r
3880                 CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,\r
3881                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,\r
3882                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
3883                 TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,\r
3884                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,\r
3885                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,\r
3886                 PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/\r
3887         },\r
3888         attrMap: {\r
3889                 "class": "className",\r
3890                 "for": "htmlFor"\r
3891         },\r
3892         attrHandle: {\r
3893                 href: function(elem){\r
3894                         return elem.getAttribute("href");\r
3895                 }\r
3896         },\r
3897         relative: {\r
3898                 "+": function(checkSet, part, isXML){\r
3899                         var isPartStr = typeof part === "string",\r
3900                                 isTag = isPartStr && !/\W/.test(part),\r
3901                                 isPartStrNotTag = isPartStr && !isTag;\r
3902 \r
3903                         if ( isTag && !isXML ) {\r
3904                                 part = part.toUpperCase();\r
3905                         }\r
3906 \r
3907                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
3908                                 if ( (elem = checkSet[i]) ) {\r
3909                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
3910 \r
3911                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?\r
3912                                                 elem || false :\r
3913                                                 elem === part;\r
3914                                 }\r
3915                         }\r
3916 \r
3917                         if ( isPartStrNotTag ) {\r
3918                                 Sizzle.filter( part, checkSet, true );\r
3919                         }\r
3920                 },\r
3921                 ">": function(checkSet, part, isXML){\r
3922                         var isPartStr = typeof part === "string";\r
3923 \r
3924                         if ( isPartStr && !/\W/.test(part) ) {\r
3925                                 part = isXML ? part : part.toUpperCase();\r
3926 \r
3927                                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
3928                                         var elem = checkSet[i];\r
3929                                         if ( elem ) {\r
3930                                                 var parent = elem.parentNode;\r
3931                                                 checkSet[i] = parent.nodeName === part ? parent : false;\r
3932                                         }\r
3933                                 }\r
3934                         } else {\r
3935                                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
3936                                         var elem = checkSet[i];\r
3937                                         if ( elem ) {\r
3938                                                 checkSet[i] = isPartStr ?\r
3939                                                         elem.parentNode :\r
3940                                                         elem.parentNode === part;\r
3941                                         }\r
3942                                 }\r
3943 \r
3944                                 if ( isPartStr ) {\r
3945                                         Sizzle.filter( part, checkSet, true );\r
3946                                 }\r
3947                         }\r
3948                 },\r
3949                 "": function(checkSet, part, isXML){\r
3950                         var doneName = done++, checkFn = dirCheck;\r
3951 \r
3952                         if ( !part.match(/\W/) ) {\r
3953                                 var nodeCheck = part = isXML ? part : part.toUpperCase();\r
3954                                 checkFn = dirNodeCheck;\r
3955                         }\r
3956 \r
3957                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
3958                 },\r
3959                 "~": function(checkSet, part, isXML){\r
3960                         var doneName = done++, checkFn = dirCheck;\r
3961 \r
3962                         if ( typeof part === "string" && !part.match(/\W/) ) {\r
3963                                 var nodeCheck = part = isXML ? part : part.toUpperCase();\r
3964                                 checkFn = dirNodeCheck;\r
3965                         }\r
3966 \r
3967                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
3968                 }\r
3969         },\r
3970         find: {\r
3971                 ID: function(match, context, isXML){\r
3972                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
3973                                 var m = context.getElementById(match[1]);\r
3974                                 return m ? [m] : [];\r
3975                         }\r
3976                 },\r
3977                 NAME: function(match, context, isXML){\r
3978                         if ( typeof context.getElementsByName !== "undefined" ) {\r
3979                                 var ret = [], results = context.getElementsByName(match[1]);\r
3980 \r
3981                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
3982                                         if ( results[i].getAttribute("name") === match[1] ) {\r
3983                                                 ret.push( results[i] );\r
3984                                         }\r
3985                                 }\r
3986 \r
3987                                 return ret.length === 0 ? null : ret;\r
3988                         }\r
3989                 },\r
3990                 TAG: function(match, context){\r
3991                         return context.getElementsByTagName(match[1]);\r
3992                 }\r
3993         },\r
3994         preFilter: {\r
3995                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
3996                         match = " " + match[1].replace(/\\/g, "") + " ";\r
3997 \r
3998                         if ( isXML ) {\r
3999                                 return match;\r
4000                         }\r
4001 \r
4002                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
4003                                 if ( elem ) {\r
4004                                         if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {\r
4005                                                 if ( !inplace )\r
4006                                                         result.push( elem );\r
4007                                         } else if ( inplace ) {\r
4008                                                 curLoop[i] = false;\r
4009                                         }\r
4010                                 }\r
4011                         }\r
4012 \r
4013                         return false;\r
4014                 },\r
4015                 ID: function(match){\r
4016                         return match[1].replace(/\\/g, "");\r
4017                 },\r
4018                 TAG: function(match, curLoop){\r
4019                         for ( var i = 0; curLoop[i] === false; i++ ){}\r
4020                         return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();\r
4021                 },\r
4022                 CHILD: function(match){\r
4023                         if ( match[1] == "nth" ) {\r
4024                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
4025                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
4026                                         match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||\r
4027                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
4028 \r
4029                                 // calculate the numbers (first)n+(last) including if they are negative\r
4030                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
4031                                 match[3] = test[3] - 0;\r
4032                         }\r
4033 \r
4034                         // TODO: Move to normal caching system\r
4035                         match[0] = done++;\r
4036 \r
4037                         return match;\r
4038                 },\r
4039                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
4040                         var name = match[1].replace(/\\/g, "");\r
4041                         \r
4042                         if ( !isXML && Expr.attrMap[name] ) {\r
4043                                 match[1] = Expr.attrMap[name];\r
4044                         }\r
4045 \r
4046                         if ( match[2] === "~=" ) {\r
4047                                 match[4] = " " + match[4] + " ";\r
4048                         }\r
4049 \r
4050                         return match;\r
4051                 },\r
4052                 PSEUDO: function(match, curLoop, inplace, result, not){\r
4053                         if ( match[1] === "not" ) {\r
4054                                 // If we're dealing with a complex expression, or a simple one\r
4055                                 if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {\r
4056                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
4057                                 } else {\r
4058                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
4059                                         if ( !inplace ) {\r
4060                                                 result.push.apply( result, ret );\r
4061                                         }\r
4062                                         return false;\r
4063                                 }\r
4064                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
4065                                 return true;\r
4066                         }\r
4067                         \r
4068                         return match;\r
4069                 },\r
4070                 POS: function(match){\r
4071                         match.unshift( true );\r
4072                         return match;\r
4073                 }\r
4074         },\r
4075         filters: {\r
4076                 enabled: function(elem){\r
4077                         return elem.disabled === false && elem.type !== "hidden";\r
4078                 },\r
4079                 disabled: function(elem){\r
4080                         return elem.disabled === true;\r
4081                 },\r
4082                 checked: function(elem){\r
4083                         return elem.checked === true;\r
4084                 },\r
4085                 selected: function(elem){\r
4086                         // Accessing this property makes selected-by-default\r
4087                         // options in Safari work properly\r
4088                         elem.parentNode.selectedIndex;\r
4089                         return elem.selected === true;\r
4090                 },\r
4091                 parent: function(elem){\r
4092                         return !!elem.firstChild;\r
4093                 },\r
4094                 empty: function(elem){\r
4095                         return !elem.firstChild;\r
4096                 },\r
4097                 has: function(elem, i, match){\r
4098                         return !!Sizzle( match[3], elem ).length;\r
4099                 },\r
4100                 header: function(elem){\r
4101                         return /h\d/i.test( elem.nodeName );\r
4102                 },\r
4103                 text: function(elem){\r
4104                         return "text" === elem.type;\r
4105                 },\r
4106                 radio: function(elem){\r
4107                         return "radio" === elem.type;\r
4108                 },\r
4109                 checkbox: function(elem){\r
4110                         return "checkbox" === elem.type;\r
4111                 },\r
4112                 file: function(elem){\r
4113                         return "file" === elem.type;\r
4114                 },\r
4115                 password: function(elem){\r
4116                         return "password" === elem.type;\r
4117                 },\r
4118                 submit: function(elem){\r
4119                         return "submit" === elem.type;\r
4120                 },\r
4121                 image: function(elem){\r
4122                         return "image" === elem.type;\r
4123                 },\r
4124                 reset: function(elem){\r
4125                         return "reset" === elem.type;\r
4126                 },\r
4127                 button: function(elem){\r
4128                         return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";\r
4129                 },\r
4130                 input: function(elem){\r
4131                         return /input|select|textarea|button/i.test(elem.nodeName);\r
4132                 }\r
4133         },\r
4134         setFilters: {\r
4135                 first: function(elem, i){\r
4136                         return i === 0;\r
4137                 },\r
4138                 last: function(elem, i, match, array){\r
4139                         return i === array.length - 1;\r
4140                 },\r
4141                 even: function(elem, i){\r
4142                         return i % 2 === 0;\r
4143                 },\r
4144                 odd: function(elem, i){\r
4145                         return i % 2 === 1;\r
4146                 },\r
4147                 lt: function(elem, i, match){\r
4148                         return i < match[3] - 0;\r
4149                 },\r
4150                 gt: function(elem, i, match){\r
4151                         return i > match[3] - 0;\r
4152                 },\r
4153                 nth: function(elem, i, match){\r
4154                         return match[3] - 0 == i;\r
4155                 },\r
4156                 eq: function(elem, i, match){\r
4157                         return match[3] - 0 == i;\r
4158                 }\r
4159         },\r
4160         filter: {\r
4161                 PSEUDO: function(elem, match, i, array){\r
4162                         var name = match[1], filter = Expr.filters[ name ];\r
4163 \r
4164                         if ( filter ) {\r
4165                                 return filter( elem, i, match, array );\r
4166                         } else if ( name === "contains" ) {\r
4167                                 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;\r
4168                         } else if ( name === "not" ) {\r
4169                                 var not = match[3];\r
4170 \r
4171                                 for ( var i = 0, l = not.length; i < l; i++ ) {\r
4172                                         if ( not[i] === elem ) {\r
4173                                                 return false;\r
4174                                         }\r
4175                                 }\r
4176 \r
4177                                 return true;\r
4178                         }\r
4179                 },\r
4180                 CHILD: function(elem, match){\r
4181                         var type = match[1], node = elem;\r
4182                         switch (type) {\r
4183                                 case 'only':\r
4184                                 case 'first':\r
4185                                         while (node = node.previousSibling)  {\r
4186                                                 if ( node.nodeType === 1 ) return false;\r
4187                                         }\r
4188                                         if ( type == 'first') return true;\r
4189                                         node = elem;\r
4190                                 case 'last':\r
4191                                         while (node = node.nextSibling)  {\r
4192                                                 if ( node.nodeType === 1 ) return false;\r
4193                                         }\r
4194                                         return true;\r
4195                                 case 'nth':\r
4196                                         var first = match[2], last = match[3];\r
4197 \r
4198                                         if ( first == 1 && last == 0 ) {\r
4199                                                 return true;\r
4200                                         }\r
4201                                         \r
4202                                         var doneName = match[0],\r
4203                                                 parent = elem.parentNode;\r
4204         \r
4205                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
4206                                                 var count = 0;\r
4207                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
4208                                                         if ( node.nodeType === 1 ) {\r
4209                                                                 node.nodeIndex = ++count;\r
4210                                                         }\r
4211                                                 } \r
4212                                                 parent.sizcache = doneName;\r
4213                                         }\r
4214                                         \r
4215                                         var diff = elem.nodeIndex - last;\r
4216                                         if ( first == 0 ) {\r
4217                                                 return diff == 0;\r
4218                                         } else {\r
4219                                                 return ( diff % first == 0 && diff / first >= 0 );\r
4220                                         }\r
4221                         }\r
4222                 },\r
4223                 ID: function(elem, match){\r
4224                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
4225                 },\r
4226                 TAG: function(elem, match){\r
4227                         return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;\r
4228                 },\r
4229                 CLASS: function(elem, match){\r
4230                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
4231                                 .indexOf( match ) > -1;\r
4232                 },\r
4233                 ATTR: function(elem, match){\r
4234                         var name = match[1],\r
4235                                 result = Expr.attrHandle[ name ] ?\r
4236                                         Expr.attrHandle[ name ]( elem ) :\r
4237                                         elem[ name ] != null ?\r
4238                                                 elem[ name ] :\r
4239                                                 elem.getAttribute( name ),\r
4240                                 value = result + "",\r
4241                                 type = match[2],\r
4242                                 check = match[4];\r
4243 \r
4244                         return result == null ?\r
4245                                 type === "!=" :\r
4246                                 type === "=" ?\r
4247                                 value === check :\r
4248                                 type === "*=" ?\r
4249                                 value.indexOf(check) >= 0 :\r
4250                                 type === "~=" ?\r
4251                                 (" " + value + " ").indexOf(check) >= 0 :\r
4252                                 !check ?\r
4253                                 value && result !== false :\r
4254                                 type === "!=" ?\r
4255                                 value != check :\r
4256                                 type === "^=" ?\r
4257                                 value.indexOf(check) === 0 :\r
4258                                 type === "$=" ?\r
4259                                 value.substr(value.length - check.length) === check :\r
4260                                 type === "|=" ?\r
4261                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
4262                                 false;\r
4263                 },\r
4264                 POS: function(elem, match, i, array){\r
4265                         var name = match[2], filter = Expr.setFilters[ name ];\r
4266 \r
4267                         if ( filter ) {\r
4268                                 return filter( elem, i, match, array );\r
4269                         }\r
4270                 }\r
4271         }\r
4272 };\r
4273 \r
4274 var origPOS = Expr.match.POS;\r
4275 \r
4276 for ( var type in Expr.match ) {\r
4277         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );\r
4278 }\r
4279 \r
4280 var makeArray = function(array, results) {\r
4281         array = Array.prototype.slice.call( array );\r
4282 \r
4283         if ( results ) {\r
4284                 results.push.apply( results, array );\r
4285                 return results;\r
4286         }\r
4287         \r
4288         return array;\r
4289 };\r
4290 \r
4291 // Perform a simple check to determine if the browser is capable of\r
4292 // converting a NodeList to an array using builtin methods.\r
4293 try {\r
4294         Array.prototype.slice.call( document.documentElement.childNodes );\r
4295 \r
4296 // Provide a fallback method if it does not work\r
4297 } catch(e){\r
4298         makeArray = function(array, results) {\r
4299                 var ret = results || [];\r
4300 \r
4301                 if ( toString.call(array) === "[object Array]" ) {\r
4302                         Array.prototype.push.apply( ret, array );\r
4303                 } else {\r
4304                         if ( typeof array.length === "number" ) {\r
4305                                 for ( var i = 0, l = array.length; i < l; i++ ) {\r
4306                                         ret.push( array[i] );\r
4307                                 }\r
4308                         } else {\r
4309                                 for ( var i = 0; array[i]; i++ ) {\r
4310                                         ret.push( array[i] );\r
4311                                 }\r
4312                         }\r
4313                 }\r
4314 \r
4315                 return ret;\r
4316         };\r
4317 }\r
4318 \r
4319 var sortOrder;\r
4320 \r
4321 if ( document.documentElement.compareDocumentPosition ) {\r
4322         sortOrder = function( a, b ) {\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                 var ret = a.sourceIndex - b.sourceIndex;\r
4332                 if ( ret === 0 ) {\r
4333                         hasDuplicate = true;\r
4334                 }\r
4335                 return ret;\r
4336         };\r
4337 } else if ( document.createRange ) {\r
4338         sortOrder = function( a, b ) {\r
4339                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
4340                 aRange.setStart(a, 0);\r
4341                 aRange.setEnd(a, 0);\r
4342                 bRange.setStart(b, 0);\r
4343                 bRange.setEnd(b, 0);\r
4344                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
4345                 if ( ret === 0 ) {\r
4346                         hasDuplicate = true;\r
4347                 }\r
4348                 return ret;\r
4349         };\r
4350 }\r
4351 \r
4352 // Check to see if the browser returns elements by name when\r
4353 // querying by getElementById (and provide a workaround)\r
4354 (function(){\r
4355         // We're going to inject a fake input element with a specified name\r
4356         var form = document.createElement("div"),\r
4357                 id = "script" + (new Date).getTime();\r
4358         form.innerHTML = "<a name='" + id + "'/>";\r
4359 \r
4360         // Inject it into the root element, check its status, and remove it quickly\r
4361         var root = document.documentElement;\r
4362         root.insertBefore( form, root.firstChild );\r
4363 \r
4364         // The workaround has to do additional checks after a getElementById\r
4365         // Which slows things down for other browsers (hence the branching)\r
4366         if ( !!document.getElementById( id ) ) {\r
4367                 Expr.find.ID = function(match, context, isXML){\r
4368                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4369                                 var m = context.getElementById(match[1]);\r
4370                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
4371                         }\r
4372                 };\r
4373 \r
4374                 Expr.filter.ID = function(elem, match){\r
4375                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
4376                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
4377                 };\r
4378         }\r
4379 \r
4380         root.removeChild( form );\r
4381 })();\r
4382 \r
4383 (function(){\r
4384         // Check to see if the browser returns only elements\r
4385         // when doing getElementsByTagName("*")\r
4386 \r
4387         // Create a fake element\r
4388         var div = document.createElement("div");\r
4389         div.appendChild( document.createComment("") );\r
4390 \r
4391         // Make sure no comments are found\r
4392         if ( div.getElementsByTagName("*").length > 0 ) {\r
4393                 Expr.find.TAG = function(match, context){\r
4394                         var results = context.getElementsByTagName(match[1]);\r
4395 \r
4396                         // Filter out possible comments\r
4397                         if ( match[1] === "*" ) {\r
4398                                 var tmp = [];\r
4399 \r
4400                                 for ( var i = 0; results[i]; i++ ) {\r
4401                                         if ( results[i].nodeType === 1 ) {\r
4402                                                 tmp.push( results[i] );\r
4403                                         }\r
4404                                 }\r
4405 \r
4406                                 results = tmp;\r
4407                         }\r
4408 \r
4409                         return results;\r
4410                 };\r
4411         }\r
4412 \r
4413         // Check to see if an attribute returns normalized href attributes\r
4414         div.innerHTML = "<a href='#'></a>";\r
4415         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
4416                         div.firstChild.getAttribute("href") !== "#" ) {\r
4417                 Expr.attrHandle.href = function(elem){\r
4418                         return elem.getAttribute("href", 2);\r
4419                 };\r
4420         }\r
4421 })();\r
4422 \r
4423 if ( document.querySelectorAll ) (function(){\r
4424         var oldSizzle = Sizzle, div = document.createElement("div");\r
4425         div.innerHTML = "<p class='TEST'></p>";\r
4426 \r
4427         // Safari can't handle uppercase or unicode characters when\r
4428         // in quirks mode.\r
4429         if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
4430                 return;\r
4431         }\r
4432         \r
4433         Sizzle = function(query, context, extra, seed){\r
4434                 context = context || document;\r
4435 \r
4436                 // Only use querySelectorAll on non-XML documents\r
4437                 // (ID selectors don't work in non-HTML documents)\r
4438                 if ( !seed && context.nodeType === 9 && !isXML(context) ) {\r
4439                         try {\r
4440                                 return makeArray( context.querySelectorAll(query), extra );\r
4441                         } catch(e){}\r
4442                 }\r
4443                 \r
4444                 return oldSizzle(query, context, extra, seed);\r
4445         };\r
4446 \r
4447         for ( var prop in oldSizzle ) {\r
4448                 Sizzle[ prop ] = oldSizzle[ prop ];\r
4449         }\r
4450 })();\r
4451 \r
4452 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){\r
4453         var div = document.createElement("div");\r
4454         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
4455 \r
4456         // Opera can't find a second classname (in 9.6)\r
4457         if ( div.getElementsByClassName("e").length === 0 )\r
4458                 return;\r
4459 \r
4460         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
4461         div.lastChild.className = "e";\r
4462 \r
4463         if ( div.getElementsByClassName("e").length === 1 )\r
4464                 return;\r
4465 \r
4466         Expr.order.splice(1, 0, "CLASS");\r
4467         Expr.find.CLASS = function(match, context, isXML) {\r
4468                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
4469                         return context.getElementsByClassName(match[1]);\r
4470                 }\r
4471         };\r
4472 })();\r
4473 \r
4474 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4475         var sibDir = dir == "previousSibling" && !isXML;\r
4476         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4477                 var elem = checkSet[i];\r
4478                 if ( elem ) {\r
4479                         if ( sibDir && elem.nodeType === 1 ){\r
4480                                 elem.sizcache = doneName;\r
4481                                 elem.sizset = i;\r
4482                         }\r
4483                         elem = elem[dir];\r
4484                         var match = false;\r
4485 \r
4486                         while ( elem ) {\r
4487                                 if ( elem.sizcache === doneName ) {\r
4488                                         match = checkSet[elem.sizset];\r
4489                                         break;\r
4490                                 }\r
4491 \r
4492                                 if ( elem.nodeType === 1 && !isXML ){\r
4493                                         elem.sizcache = doneName;\r
4494                                         elem.sizset = i;\r
4495                                 }\r
4496 \r
4497                                 if ( elem.nodeName === cur ) {\r
4498                                         match = elem;\r
4499                                         break;\r
4500                                 }\r
4501 \r
4502                                 elem = elem[dir];\r
4503                         }\r
4504 \r
4505                         checkSet[i] = match;\r
4506                 }\r
4507         }\r
4508 }\r
4509 \r
4510 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4511         var sibDir = dir == "previousSibling" && !isXML;\r
4512         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4513                 var elem = checkSet[i];\r
4514                 if ( elem ) {\r
4515                         if ( sibDir && elem.nodeType === 1 ) {\r
4516                                 elem.sizcache = doneName;\r
4517                                 elem.sizset = i;\r
4518                         }\r
4519                         elem = elem[dir];\r
4520                         var match = false;\r
4521 \r
4522                         while ( elem ) {\r
4523                                 if ( elem.sizcache === doneName ) {\r
4524                                         match = checkSet[elem.sizset];\r
4525                                         break;\r
4526                                 }\r
4527 \r
4528                                 if ( elem.nodeType === 1 ) {\r
4529                                         if ( !isXML ) {\r
4530                                                 elem.sizcache = doneName;\r
4531                                                 elem.sizset = i;\r
4532                                         }\r
4533                                         if ( typeof cur !== "string" ) {\r
4534                                                 if ( elem === cur ) {\r
4535                                                         match = true;\r
4536                                                         break;\r
4537                                                 }\r
4538 \r
4539                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
4540                                                 match = elem;\r
4541                                                 break;\r
4542                                         }\r
4543                                 }\r
4544 \r
4545                                 elem = elem[dir];\r
4546                         }\r
4547 \r
4548                         checkSet[i] = match;\r
4549                 }\r
4550         }\r
4551 }\r
4552 \r
4553 var contains = document.compareDocumentPosition ?  function(a, b){\r
4554         return a.compareDocumentPosition(b) & 16;\r
4555 } : function(a, b){\r
4556         return a !== b && (a.contains ? a.contains(b) : true);\r
4557 };\r
4558 \r
4559 var isXML = function(elem){\r
4560         return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||\r
4561                 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";\r
4562 };\r
4563 \r
4564 var posProcess = function(selector, context){\r
4565         var tmpSet = [], later = "", match,\r
4566                 root = context.nodeType ? [context] : context;\r
4567 \r
4568         // Position selectors must be done after the filter\r
4569         // And so must :not(positional) so we move all PSEUDOs to the end\r
4570         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
4571                 later += match[0];\r
4572                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
4573         }\r
4574 \r
4575         selector = Expr.relative[selector] ? selector + "*" : selector;\r
4576 \r
4577         for ( var i = 0, l = root.length; i < l; i++ ) {\r
4578                 Sizzle( selector, root[i], tmpSet );\r
4579         }\r
4580 \r
4581         return Sizzle.filter( later, tmpSet );\r
4582 };\r
4583 \r
4584 // EXPOSE\r
4585 \r
4586 window.tinymce.dom.Sizzle = Sizzle;\r
4587 \r
4588 })();\r
4589 \r
4590 \r
4591 (function(tinymce) {\r
4592         // Shorten names\r
4593         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
4594 \r
4595         tinymce.create('tinymce.dom.EventUtils', {\r
4596                 EventUtils : function() {\r
4597                         this.inits = [];\r
4598                         this.events = [];\r
4599                 },\r
4600 \r
4601                 add : function(o, n, f, s) {\r
4602                         var cb, t = this, el = t.events, r;\r
4603 \r
4604                         if (n instanceof Array) {\r
4605                                 r = [];\r
4606 \r
4607                                 each(n, function(n) {\r
4608                                         r.push(t.add(o, n, f, s));\r
4609                                 });\r
4610 \r
4611                                 return r;\r
4612                         }\r
4613 \r
4614                         // Handle array\r
4615                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4616                                 r = [];\r
4617 \r
4618                                 each(o, function(o) {\r
4619                                         o = DOM.get(o);\r
4620                                         r.push(t.add(o, n, f, s));\r
4621                                 });\r
4622 \r
4623                                 return r;\r
4624                         }\r
4625 \r
4626                         o = DOM.get(o);\r
4627 \r
4628                         if (!o)\r
4629                                 return;\r
4630 \r
4631                         // Setup event callback\r
4632                         cb = function(e) {\r
4633                                 // Is all events disabled\r
4634                                 if (t.disabled)\r
4635                                         return;\r
4636 \r
4637                                 e = e || window.event;\r
4638 \r
4639                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
4640                                 if (e && isIE) {\r
4641                                         if (!e.target)\r
4642                                                 e.target = e.srcElement;\r
4643 \r
4644                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
4645                                         tinymce.extend(e, t._stoppers);\r
4646                                 }\r
4647 \r
4648                                 if (!s)\r
4649                                         return f(e);\r
4650 \r
4651                                 return f.call(s, e);\r
4652                         };\r
4653 \r
4654                         if (n == 'unload') {\r
4655                                 tinymce.unloads.unshift({func : cb});\r
4656                                 return cb;\r
4657                         }\r
4658 \r
4659                         if (n == 'init') {\r
4660                                 if (t.domLoaded)\r
4661                                         cb();\r
4662                                 else\r
4663                                         t.inits.push(cb);\r
4664 \r
4665                                 return cb;\r
4666                         }\r
4667 \r
4668                         // Store away listener reference\r
4669                         el.push({\r
4670                                 obj : o,\r
4671                                 name : n,\r
4672                                 func : f,\r
4673                                 cfunc : cb,\r
4674                                 scope : s\r
4675                         });\r
4676 \r
4677                         t._add(o, n, cb);\r
4678 \r
4679                         return f;\r
4680                 },\r
4681 \r
4682                 remove : function(o, n, f) {\r
4683                         var t = this, a = t.events, s = false, r;\r
4684 \r
4685                         // Handle array\r
4686                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4687                                 r = [];\r
4688 \r
4689                                 each(o, function(o) {\r
4690                                         o = DOM.get(o);\r
4691                                         r.push(t.remove(o, n, f));\r
4692                                 });\r
4693 \r
4694                                 return r;\r
4695                         }\r
4696 \r
4697                         o = DOM.get(o);\r
4698 \r
4699                         each(a, function(e, i) {\r
4700                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
4701                                         a.splice(i, 1);\r
4702                                         t._remove(o, n, e.cfunc);\r
4703                                         s = true;\r
4704                                         return false;\r
4705                                 }\r
4706                         });\r
4707 \r
4708                         return s;\r
4709                 },\r
4710 \r
4711                 clear : function(o) {\r
4712                         var t = this, a = t.events, i, e;\r
4713 \r
4714                         if (o) {\r
4715                                 o = DOM.get(o);\r
4716 \r
4717                                 for (i = a.length - 1; i >= 0; i--) {\r
4718                                         e = a[i];\r
4719 \r
4720                                         if (e.obj === o) {\r
4721                                                 t._remove(e.obj, e.name, e.cfunc);\r
4722                                                 e.obj = e.cfunc = null;\r
4723                                                 a.splice(i, 1);\r
4724                                         }\r
4725                                 }\r
4726                         }\r
4727                 },\r
4728 \r
4729                 cancel : function(e) {\r
4730                         if (!e)\r
4731                                 return false;\r
4732 \r
4733                         this.stop(e);\r
4734 \r
4735                         return this.prevent(e);\r
4736                 },\r
4737 \r
4738                 stop : function(e) {\r
4739                         if (e.stopPropagation)\r
4740                                 e.stopPropagation();\r
4741                         else\r
4742                                 e.cancelBubble = true;\r
4743 \r
4744                         return false;\r
4745                 },\r
4746 \r
4747                 prevent : function(e) {\r
4748                         if (e.preventDefault)\r
4749                                 e.preventDefault();\r
4750                         else\r
4751                                 e.returnValue = false;\r
4752 \r
4753                         return false;\r
4754                 },\r
4755 \r
4756                 destroy : function() {\r
4757                         var t = this;\r
4758 \r
4759                         each(t.events, function(e, i) {\r
4760                                 t._remove(e.obj, e.name, e.cfunc);\r
4761                                 e.obj = e.cfunc = null;\r
4762                         });\r
4763 \r
4764                         t.events = [];\r
4765                         t = null;\r
4766                 },\r
4767 \r
4768                 _add : function(o, n, f) {\r
4769                         if (o.attachEvent)\r
4770                                 o.attachEvent('on' + n, f);\r
4771                         else if (o.addEventListener)\r
4772                                 o.addEventListener(n, f, false);\r
4773                         else\r
4774                                 o['on' + n] = f;\r
4775                 },\r
4776 \r
4777                 _remove : function(o, n, f) {\r
4778                         if (o) {\r
4779                                 try {\r
4780                                         if (o.detachEvent)\r
4781                                                 o.detachEvent('on' + n, f);\r
4782                                         else if (o.removeEventListener)\r
4783                                                 o.removeEventListener(n, f, false);\r
4784                                         else\r
4785                                                 o['on' + n] = null;\r
4786                                 } catch (ex) {\r
4787                                         // Might fail with permission denined on IE so we just ignore that\r
4788                                 }\r
4789                         }\r
4790                 },\r
4791 \r
4792                 _pageInit : function(win) {\r
4793                         var t = this;\r
4794 \r
4795                         // Keep it from running more than once\r
4796                         if (t.domLoaded)\r
4797                                 return;\r
4798 \r
4799                         t.domLoaded = true;\r
4800 \r
4801                         each(t.inits, function(c) {\r
4802                                 c();\r
4803                         });\r
4804 \r
4805                         t.inits = [];\r
4806                 },\r
4807 \r
4808                 _wait : function(win) {\r
4809                         var t = this, doc = win.document;\r
4810 \r
4811                         // No need since the document is already loaded\r
4812                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4813                                 t.domLoaded = 1;\r
4814                                 return;\r
4815                         }\r
4816 \r
4817                         // Use IE method\r
4818                         if (doc.attachEvent) {\r
4819                                 doc.attachEvent("onreadystatechange", function() {\r
4820                                         if (doc.readyState === "complete") {\r
4821                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4822                                                 t._pageInit(win);\r
4823                                         }\r
4824                                 });\r
4825 \r
4826                                 if (doc.documentElement.doScroll && win == win.top) {\r
4827                                         (function() {\r
4828                                                 if (t.domLoaded)\r
4829                                                         return;\r
4830 \r
4831                                                 try {\r
4832                                                         // If IE is used, use the trick by Diego Perini\r
4833                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4834                                                         doc.documentElement.doScroll("left");\r
4835                                                 } catch (ex) {\r
4836                                                         setTimeout(arguments.callee, 0);\r
4837                                                         return;\r
4838                                                 }\r
4839 \r
4840                                                 t._pageInit(win);\r
4841                                         })();\r
4842                                 }\r
4843                         } else if (doc.addEventListener) {\r
4844                                 t._add(win, 'DOMContentLoaded', function() {\r
4845                                         t._pageInit(win);\r
4846                                 });\r
4847                         }\r
4848 \r
4849                         t._add(win, 'load', function() {\r
4850                                 t._pageInit(win);\r
4851                         });\r
4852                 },\r
4853 \r
4854                 _stoppers : {\r
4855                         preventDefault :  function() {\r
4856                                 this.returnValue = false;\r
4857                         },\r
4858 \r
4859                         stopPropagation : function() {\r
4860                                 this.cancelBubble = true;\r
4861                         }\r
4862                 }\r
4863         });\r
4864 \r
4865         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4866 \r
4867         // Dispatch DOM content loaded event for IE and Safari\r
4868         Event._wait(window);\r
4869 \r
4870         tinymce.addUnload(function() {\r
4871                 Event.destroy();\r
4872         });\r
4873 })(tinymce);\r
4874 \r
4875 (function(tinymce) {\r
4876         tinymce.dom.Element = function(id, settings) {\r
4877                 var t = this, dom, el;\r
4878 \r
4879                 t.settings = settings = settings || {};\r
4880                 t.id = id;\r
4881                 t.dom = dom = settings.dom || tinymce.DOM;\r
4882 \r
4883                 // Only IE leaks DOM references, this is a lot faster\r
4884                 if (!tinymce.isIE)\r
4885                         el = dom.get(t.id);\r
4886 \r
4887                 tinymce.each(\r
4888                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4889                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4890                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4891                                 'isHidden,setHTML,get').split(/,/)\r
4892                         , function(k) {\r
4893                                 t[k] = function() {\r
4894                                         var a = [id], i;\r
4895 \r
4896                                         for (i = 0; i < arguments.length; i++)\r
4897                                                 a.push(arguments[i]);\r
4898 \r
4899                                         a = dom[k].apply(dom, a);\r
4900                                         t.update(k);\r
4901 \r
4902                                         return a;\r
4903                                 };\r
4904                 });\r
4905 \r
4906                 tinymce.extend(t, {\r
4907                         on : function(n, f, s) {\r
4908                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
4909                         },\r
4910 \r
4911                         getXY : function() {\r
4912                                 return {\r
4913                                         x : parseInt(t.getStyle('left')),\r
4914                                         y : parseInt(t.getStyle('top'))\r
4915                                 };\r
4916                         },\r
4917 \r
4918                         getSize : function() {\r
4919                                 var n = dom.get(t.id);\r
4920 \r
4921                                 return {\r
4922                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
4923                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
4924                                 };\r
4925                         },\r
4926 \r
4927                         moveTo : function(x, y) {\r
4928                                 t.setStyles({left : x, top : y});\r
4929                         },\r
4930 \r
4931                         moveBy : function(x, y) {\r
4932                                 var p = t.getXY();\r
4933 \r
4934                                 t.moveTo(p.x + x, p.y + y);\r
4935                         },\r
4936 \r
4937                         resizeTo : function(w, h) {\r
4938                                 t.setStyles({width : w, height : h});\r
4939                         },\r
4940 \r
4941                         resizeBy : function(w, h) {\r
4942                                 var s = t.getSize();\r
4943 \r
4944                                 t.resizeTo(s.w + w, s.h + h);\r
4945                         },\r
4946 \r
4947                         update : function(k) {\r
4948                                 var b;\r
4949 \r
4950                                 if (tinymce.isIE6 && settings.blocker) {\r
4951                                         k = k || '';\r
4952 \r
4953                                         // Ignore getters\r
4954                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4955                                                 return;\r
4956 \r
4957                                         // Remove blocker on remove\r
4958                                         if (k == 'remove') {\r
4959                                                 dom.remove(t.blocker);\r
4960                                                 return;\r
4961                                         }\r
4962 \r
4963                                         if (!t.blocker) {\r
4964                                                 t.blocker = dom.uniqueId();\r
4965                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
4966                                                 dom.setStyle(b, 'opacity', 0);\r
4967                                         } else\r
4968                                                 b = dom.get(t.blocker);\r
4969 \r
4970                                         dom.setStyles(b, {\r
4971                                                 left : t.getStyle('left', 1),\r
4972                                                 top : t.getStyle('top', 1),\r
4973                                                 width : t.getStyle('width', 1),\r
4974                                                 height : t.getStyle('height', 1),\r
4975                                                 display : t.getStyle('display', 1),\r
4976                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
4977                                         });\r
4978                                 }\r
4979                         }\r
4980                 });\r
4981         };\r
4982 })(tinymce);\r
4983 \r
4984 (function(tinymce) {\r
4985         function trimNl(s) {\r
4986                 return s.replace(/[\n\r]+/g, '');\r
4987         };\r
4988 \r
4989         // Shorten names\r
4990         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
4991 \r
4992         tinymce.create('tinymce.dom.Selection', {\r
4993                 Selection : function(dom, win, serializer) {\r
4994                         var t = this;\r
4995 \r
4996                         t.dom = dom;\r
4997                         t.win = win;\r
4998                         t.serializer = serializer;\r
4999 \r
5000                         // Add events\r
5001                         each([\r
5002                                 'onBeforeSetContent',\r
5003                                 'onBeforeGetContent',\r
5004                                 'onSetContent',\r
5005                                 'onGetContent'\r
5006                         ], function(e) {\r
5007                                 t[e] = new tinymce.util.Dispatcher(t);\r
5008                         });\r
5009 \r
5010                         // No W3C Range support\r
5011                         if (!t.win.getSelection)\r
5012                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
5013 \r
5014                         // Prevent leaks\r
5015                         tinymce.addUnload(t.destroy, t);\r
5016                 },\r
5017 \r
5018                 getContent : function(s) {\r
5019                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
5020 \r
5021                         s = s || {};\r
5022                         wb = wa = '';\r
5023                         s.get = true;\r
5024                         s.format = s.format || 'html';\r
5025                         t.onBeforeGetContent.dispatch(t, s);\r
5026 \r
5027                         if (s.format == 'text')\r
5028                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
5029 \r
5030                         if (r.cloneContents) {\r
5031                                 n = r.cloneContents();\r
5032 \r
5033                                 if (n)\r
5034                                         e.appendChild(n);\r
5035                         } else if (is(r.item) || is(r.htmlText))\r
5036                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
5037                         else\r
5038                                 e.innerHTML = r.toString();\r
5039 \r
5040                         // Keep whitespace before and after\r
5041                         if (/^\s/.test(e.innerHTML))\r
5042                                 wb = ' ';\r
5043 \r
5044                         if (/\s+$/.test(e.innerHTML))\r
5045                                 wa = ' ';\r
5046 \r
5047                         s.getInner = true;\r
5048 \r
5049                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
5050                         t.onGetContent.dispatch(t, s);\r
5051 \r
5052                         return s.content;\r
5053                 },\r
5054 \r
5055                 setContent : function(h, s) {\r
5056                         var t = this, r = t.getRng(), c, d = t.win.document;\r
5057 \r
5058                         s = s || {format : 'html'};\r
5059                         s.set = true;\r
5060                         h = s.content = t.dom.processHTML(h);\r
5061 \r
5062                         // Dispatch before set content event\r
5063                         t.onBeforeSetContent.dispatch(t, s);\r
5064                         h = s.content;\r
5065 \r
5066                         if (r.insertNode) {\r
5067                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
5068                                 h += '<span id="__caret">_</span>';\r
5069 \r
5070                                 // Delete and insert new node\r
5071                                 if (r.startContainer == d && r.endContainer == d) {\r
5072                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
5073                                         d.body.innerHTML = h;\r
5074                                 } else {\r
5075                                         r.deleteContents();\r
5076                                         r.insertNode(t.getRng().createContextualFragment(h));\r
5077                                 }\r
5078 \r
5079                                 // Move to caret marker\r
5080                                 c = t.dom.get('__caret');\r
5081 \r
5082                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
5083                                 r = d.createRange();\r
5084                                 r.setStartBefore(c);\r
5085                                 r.setEndBefore(c);\r
5086                                 t.setRng(r);\r
5087 \r
5088                                 // Remove the caret position\r
5089                                 t.dom.remove('__caret');\r
5090                         } else {\r
5091                                 if (r.item) {\r
5092                                         // Delete content and get caret text selection\r
5093                                         d.execCommand('Delete', false, null);\r
5094                                         r = t.getRng();\r
5095                                 }\r
5096 \r
5097                                 r.pasteHTML(h);\r
5098                         }\r
5099 \r
5100                         // Dispatch set content event\r
5101                         t.onSetContent.dispatch(t, s);\r
5102                 },\r
5103 \r
5104                 getStart : function() {\r
5105                         var t = this, r = t.getRng(), e;\r
5106 \r
5107                         if (isIE) {\r
5108                                 if (r.item)\r
5109                                         return r.item(0);\r
5110 \r
5111                                 r = r.duplicate();\r
5112                                 r.collapse(1);\r
5113                                 e = r.parentElement();\r
5114 \r
5115                                 if (e && e.nodeName == 'BODY')\r
5116                                         return e.firstChild || e;\r
5117 \r
5118                                 return e;\r
5119                         } else {\r
5120                                 e = r.startContainer;\r
5121 \r
5122                                 if (e.nodeType == 1 && e.hasChildNodes())\r
5123                                         e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)];\r
5124 \r
5125                                 if (e && e.nodeType == 3)\r
5126                                         return e.parentNode;\r
5127 \r
5128                                 return e;\r
5129                         }\r
5130                 },\r
5131 \r
5132                 getEnd : function() {\r
5133                         var t = this, r = t.getRng(), e, eo;\r
5134 \r
5135                         if (isIE) {\r
5136                                 if (r.item)\r
5137                                         return r.item(0);\r
5138 \r
5139                                 r = r.duplicate();\r
5140                                 r.collapse(0);\r
5141                                 e = r.parentElement();\r
5142 \r
5143                                 if (e && e.nodeName == 'BODY')\r
5144                                         return e.lastChild || e;\r
5145 \r
5146                                 return e;\r
5147                         } else {\r
5148                                 e = r.endContainer;\r
5149                                 eo = r.endOffset;\r
5150 \r
5151                                 if (e.nodeType == 1 && e.hasChildNodes())\r
5152                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
5153 \r
5154                                 if (e && e.nodeType == 3)\r
5155                                         return e.parentNode;\r
5156 \r
5157                                 return e;\r
5158                         }\r
5159                 },\r
5160 \r
5161                 getBookmark : function(type, normalized) {\r
5162                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
5163 \r
5164                         function findIndex(name, element) {\r
5165                                 var index = 0;\r
5166 \r
5167                                 each(dom.select(name), function(node, i) {\r
5168                                         if (node == element)\r
5169                                                 index = i;\r
5170                                 });\r
5171 \r
5172                                 return index;\r
5173                         };\r
5174 \r
5175                         if (type == 2) {\r
5176                                 function getLocation() {\r
5177                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
5178 \r
5179                                         function getPoint(rng, start) {\r
5180                                                 var indexes = [], node, lastIdx,\r
5181                                                         container = rng[start ? 'startContainer' : 'endContainer'],\r
5182                                                         offset = rng[start ? 'startOffset' : 'endOffset'], exclude, point = {};\r
5183 \r
5184                                                 // Resolve element index\r
5185                                                 if (container.nodeType == 1 && container.hasChildNodes()) {\r
5186                                                         lastIdx = container.childNodes.length - 1;\r
5187                                                         point.exclude = (start && offset > lastIdx) || (!start && offset == 0);\r
5188 \r
5189                                                         if (!start && offset)\r
5190                                                                 offset--;\r
5191 \r
5192                                                         container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
5193 \r
5194                                                         if (container.nodeType == 3)\r
5195                                                                 offset = start ? 0 : container.nodeValue.length;\r
5196                                                 }\r
5197 \r
5198                                                 if (container.nodeType == 3) {\r
5199                                                         if (normalized) {\r
5200                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
5201                                                                         offset += node.nodeValue.length;\r
5202                                                         }\r
5203 \r
5204                                                         point.offset = offset;\r
5205                                                 }\r
5206 \r
5207                                                 for (; container && container != root; container = container.parentNode)\r
5208                                                         indexes.push(t.dom.nodeIndex(container, normalized));\r
5209 \r
5210                                                 point.indexes = indexes;\r
5211 \r
5212                                                 return point;\r
5213                                         };\r
5214 \r
5215                                         bookmark.start = getPoint(rng, true);\r
5216 \r
5217                                         if (!t.isCollapsed())\r
5218                                                 bookmark.end = getPoint(rng);\r
5219 \r
5220                                         return bookmark;\r
5221                                 };\r
5222 \r
5223                                 return getLocation();\r
5224                         }\r
5225 \r
5226                         // Handle simple range\r
5227                         if (type)\r
5228                                 return {rng : t.getRng()};\r
5229 \r
5230                         rng = t.getRng();\r
5231                         id = dom.uniqueId();\r
5232                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
5233                         styles = 'overflow:hidden;line-height:0px';\r
5234 \r
5235                         // Explorer method\r
5236                         if (rng.duplicate || rng.item) {\r
5237                                 // Text selection\r
5238                                 if (!rng.item) {\r
5239                                         rng2 = rng.duplicate();\r
5240 \r
5241                                         // Insert start marker\r
5242                                         rng.collapse();\r
5243                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
5244 \r
5245                                         // Insert end marker\r
5246                                         if (!collapsed) {\r
5247                                                 rng2.collapse(false);\r
5248                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
5249                                         }\r
5250                                 } else {\r
5251                                         // Control selection\r
5252                                         element = rng.item(0);\r
5253                                         name = element.nodeName;\r
5254 \r
5255                                         return {name : name, index : findIndex(name, element)};\r
5256                                 }\r
5257                         } else {\r
5258                                 element = t.getNode();\r
5259                                 name = element.nodeName;\r
5260                                 if (name == 'IMG')\r
5261                                         return {name : name, index : findIndex(name, element)};\r
5262 \r
5263                                 // W3C method\r
5264                                 rng2 = rng.cloneRange();\r
5265 \r
5266                                 // Insert end marker\r
5267                                 if (!collapsed) {\r
5268                                         rng2.collapse(false);\r
5269                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
5270                                 }\r
5271 \r
5272                                 rng.collapse(true);\r
5273                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
5274                         }\r
5275 \r
5276                         t.moveToBookmark({id : id, keep : 1});\r
5277 \r
5278                         return {id : id};\r
5279                 },\r
5280 \r
5281                 moveToBookmark : function(bookmark) {\r
5282                         var t = this, dom = t.dom, marker1, marker2, rng, root;\r
5283 \r
5284                         // Clear selection cache\r
5285                         if (t.tridentSel)\r
5286                                 t.tridentSel.destroy();\r
5287 \r
5288                         if (bookmark) {\r
5289                                 if (bookmark.start) {\r
5290                                         rng = dom.createRng();\r
5291                                         root = dom.getRoot();\r
5292 \r
5293                                         function setEndPoint(start) {\r
5294                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset;\r
5295 \r
5296                                                 if (point) {\r
5297                                                         for (node = root, i = point.indexes.length - 1; i >= 0; i--)\r
5298                                                                 node = node.childNodes[point.indexes[i]] || node;\r
5299 \r
5300                                                         if (start) {\r
5301                                                                 if (node.nodeType == 3 && point.offset)\r
5302                                                                         rng.setStart(node, point.offset);\r
5303                                                                 else {\r
5304                                                                         if (point.exclude)\r
5305                                                                                 rng.setStartAfter(node);\r
5306                                                                         else\r
5307                                                                                 rng.setStartBefore(node);\r
5308                                                                 }\r
5309                                                         } else {\r
5310                                                                 if (node.nodeType == 3 && point.offset)\r
5311                                                                         rng.setEnd(node, point.offset);\r
5312                                                                 else {\r
5313                                                                         if (point.exclude)\r
5314                                                                                 rng.setEndBefore(node);\r
5315                                                                         else\r
5316                                                                                 rng.setEndAfter(node);\r
5317                                                                 }\r
5318                                                         }\r
5319                                                 }\r
5320                                         };\r
5321 \r
5322                                         setEndPoint(true);\r
5323                                         setEndPoint();\r
5324 \r
5325                                         t.setRng(rng);\r
5326                                 } else if (bookmark.id) {\r
5327                                         rng = dom.createRng();\r
5328 \r
5329                                         function restoreEndPoint(suffix) {\r
5330                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
5331 \r
5332                                                 if (marker) {\r
5333                                                         node = marker.parentNode;\r
5334 \r
5335                                                         if (suffix == 'start') {\r
5336                                                                 if (!keep) {\r
5337                                                                         idx = dom.nodeIndex(marker);\r
5338                                                                 } else {\r
5339                                                                         node = marker;\r
5340                                                                         idx = 1;\r
5341                                                                 }\r
5342 \r
5343                                                                 rng.setStart(node, idx);\r
5344                                                                 rng.setEnd(node, idx);\r
5345                                                         } else {\r
5346                                                                 if (!keep) {\r
5347                                                                         idx = dom.nodeIndex(marker);\r
5348                                                                 } else {\r
5349                                                                         node = marker;\r
5350                                                                         idx = 1;\r
5351                                                                 }\r
5352 \r
5353                                                                 rng.setEnd(node, idx);\r
5354                                                         }\r
5355 \r
5356                                                         if (!keep) {\r
5357                                                                 prev = marker.previousSibling;\r
5358                                                                 next = marker.nextSibling;\r
5359 \r
5360                                                                 // Remove all marker text nodes\r
5361                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
5362                                                                         if (node.nodeType == 3)\r
5363                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
5364                                                                 });\r
5365 \r
5366                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
5367                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
5368                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
5369                                                                         dom.remove(marker, 1);\r
5370 \r
5371                                                                 // If siblings are text nodes then merge them\r
5372                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) {\r
5373                                                                         idx = prev.nodeValue.length;\r
5374                                                                         prev.appendData(next.nodeValue);\r
5375                                                                         dom.remove(next);\r
5376 \r
5377                                                                         if (suffix == 'start') {\r
5378                                                                                 rng.setStart(prev, idx);\r
5379                                                                                 rng.setEnd(prev, idx);\r
5380                                                                         } else\r
5381                                                                                 rng.setEnd(prev, idx);\r
5382                                                                 }\r
5383                                                         }\r
5384                                                 }\r
5385                                         };\r
5386 \r
5387                                         // Restore start/end points\r
5388                                         restoreEndPoint('start');\r
5389                                         restoreEndPoint('end');\r
5390 \r
5391                                         t.setRng(rng);\r
5392                                 } else if (bookmark.name) {\r
5393                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
5394                                 } else if (bookmark.rng)\r
5395                                         t.setRng(bookmark.rng);\r
5396                         }\r
5397                 },\r
5398 \r
5399                 select : function(node, content) {\r
5400                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
5401 \r
5402                         idx = dom.nodeIndex(node);\r
5403                         rng.setStart(node.parentNode, idx);\r
5404                         rng.setEnd(node.parentNode, idx + 1);\r
5405 \r
5406                         // Find first/last text node or BR element\r
5407                         if (content) {\r
5408                                 function setPoint(node, start) {\r
5409                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
5410 \r
5411                                         do {\r
5412                                                 // Text node\r
5413                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
5414                                                         if (start)\r
5415                                                                 rng.setStart(node, 0);\r
5416                                                         else\r
5417                                                                 rng.setEnd(node, node.nodeValue.length);\r
5418 \r
5419                                                         return;\r
5420                                                 }\r
5421 \r
5422                                                 // BR element\r
5423                                                 if (node.nodeName == 'BR') {\r
5424                                                         if (start)\r
5425                                                                 rng.setStartBefore(node);\r
5426                                                         else\r
5427                                                                 rng.setEndBefore(node);\r
5428 \r
5429                                                         return;\r
5430                                                 }\r
5431                                         } while (node = (start ? walker.next() : walker.prev()));\r
5432                                 };\r
5433 \r
5434                                 setPoint(node, 1);\r
5435                                 setPoint(node);\r
5436                         }\r
5437 \r
5438                         t.setRng(rng);\r
5439 \r
5440                         return node;\r
5441                 },\r
5442 \r
5443                 isCollapsed : function() {\r
5444                         var t = this, r = t.getRng(), s = t.getSel();\r
5445 \r
5446                         if (!r || r.item)\r
5447                                 return false;\r
5448 \r
5449                         if (r.compareEndPoints)\r
5450                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
5451 \r
5452                         return !s || r.collapsed;\r
5453                 },\r
5454 \r
5455                 collapse : function(b) {\r
5456                         var t = this, r = t.getRng(), n;\r
5457 \r
5458                         // Control range on IE\r
5459                         if (r.item) {\r
5460                                 n = r.item(0);\r
5461                                 r = this.win.document.body.createTextRange();\r
5462                                 r.moveToElementText(n);\r
5463                         }\r
5464 \r
5465                         r.collapse(!!b);\r
5466                         t.setRng(r);\r
5467                 },\r
5468 \r
5469                 getSel : function() {\r
5470                         var t = this, w = this.win;\r
5471 \r
5472                         return w.getSelection ? w.getSelection() : w.document.selection;\r
5473                 },\r
5474 \r
5475                 getRng : function(w3c) {\r
5476                         var t = this, s, r;\r
5477 \r
5478                         // Found tridentSel object then we need to use that one\r
5479                         if (w3c && t.tridentSel)\r
5480                                 return t.tridentSel.getRangeAt(0);\r
5481 \r
5482                         try {\r
5483                                 if (s = t.getSel())\r
5484                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
5485                         } catch (ex) {\r
5486                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
5487                         }\r
5488 \r
5489                         // No range found then create an empty one\r
5490                         // This can occur when the editor is placed in a hidden container element on Gecko\r
5491                         // Or on IE when there was an exception\r
5492                         if (!r)\r
5493                                 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();\r
5494 \r
5495                         return r;\r
5496                 },\r
5497 \r
5498                 setRng : function(r) {\r
5499                         var s, t = this;\r
5500 \r
5501                         if (!t.tridentSel) {\r
5502                                 s = t.getSel();\r
5503 \r
5504                                 if (s) {\r
5505                                         s.removeAllRanges();\r
5506                                         s.addRange(r);\r
5507                                 }\r
5508                         } else {\r
5509                                 // Is W3C Range\r
5510                                 if (r.cloneRange) {\r
5511                                         t.tridentSel.addRange(r);\r
5512                                         return;\r
5513                                 }\r
5514 \r
5515                                 // Is IE specific range\r
5516                                 try {\r
5517                                         r.select();\r
5518                                 } catch (ex) {\r
5519                                         // Needed for some odd IE bug #1843306\r
5520                                 }\r
5521                         }\r
5522                 },\r
5523 \r
5524                 setNode : function(n) {\r
5525                         var t = this;\r
5526 \r
5527                         t.setContent(t.dom.getOuterHTML(n));\r
5528 \r
5529                         return n;\r
5530                 },\r
5531 \r
5532                 getNode : function() {\r
5533                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
5534 \r
5535                         if (!isIE) {\r
5536                                 // Range maybe lost after the editor is made visible again\r
5537                                 if (!rng)\r
5538                                         return t.dom.getRoot();\r
5539 \r
5540                                 elm = rng.commonAncestorContainer;\r
5541 \r
5542                                 // Handle selection a image or other control like element such as anchors\r
5543                                 if (!rng.collapsed) {\r
5544                                         if (rng.startContainer == rng.endContainer) {\r
5545                                                 if (rng.startOffset - rng.endOffset < 2) {\r
5546                                                         if (rng.startContainer.hasChildNodes())\r
5547                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
5548                                                 }\r
5549                                         }\r
5550 \r
5551                                         // If the anchor node is a element instead of a text node then return this element\r
5552                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
5553                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
5554                                 }\r
5555 \r
5556                                 if (elm && elm.nodeType == 3)\r
5557                                         return elm.parentNode;\r
5558 \r
5559                                 return elm;\r
5560                         }\r
5561 \r
5562                         return rng.item ? rng.item(0) : rng.parentElement();\r
5563                 },\r
5564 \r
5565                 getSelectedBlocks : function(st, en) {\r
5566                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
5567 \r
5568                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
5569                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
5570 \r
5571                         if (sb)\r
5572                                 bl.push(sb);\r
5573 \r
5574                         if (sb && eb && sb != eb) {\r
5575                                 n = sb;\r
5576 \r
5577                                 while ((n = n.nextSibling) && n != eb) {\r
5578                                         if (dom.isBlock(n))\r
5579                                                 bl.push(n);\r
5580                                 }\r
5581                         }\r
5582 \r
5583                         if (eb && sb != eb)\r
5584                                 bl.push(eb);\r
5585 \r
5586                         return bl;\r
5587                 },\r
5588 \r
5589                 destroy : function(s) {\r
5590                         var t = this;\r
5591 \r
5592                         t.win = null;\r
5593 \r
5594                         if (t.tridentSel)\r
5595                                 t.tridentSel.destroy();\r
5596 \r
5597                         // Manual destroy then remove unload handler\r
5598                         if (!s)\r
5599                                 tinymce.removeUnload(t.destroy);\r
5600                 }\r
5601         });\r
5602 })(tinymce);\r
5603 \r
5604 (function(tinymce) {\r
5605         tinymce.create('tinymce.dom.XMLWriter', {\r
5606                 node : null,\r
5607 \r
5608                 XMLWriter : function(s) {\r
5609                         // Get XML document\r
5610                         function getXML() {\r
5611                                 var i = document.implementation;\r
5612 \r
5613                                 if (!i || !i.createDocument) {\r
5614                                         // Try IE objects\r
5615                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
5616                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
5617                                 } else\r
5618                                         return i.createDocument('', '', null);\r
5619                         };\r
5620 \r
5621                         this.doc = getXML();\r
5622                         \r
5623                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
5624                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
5625 \r
5626                         this.reset();\r
5627                 },\r
5628 \r
5629                 reset : function() {\r
5630                         var t = this, d = t.doc;\r
5631 \r
5632                         if (d.firstChild)\r
5633                                 d.removeChild(d.firstChild);\r
5634 \r
5635                         t.node = d.appendChild(d.createElement("html"));\r
5636                 },\r
5637 \r
5638                 writeStartElement : function(n) {\r
5639                         var t = this;\r
5640 \r
5641                         t.node = t.node.appendChild(t.doc.createElement(n));\r
5642                 },\r
5643 \r
5644                 writeAttribute : function(n, v) {\r
5645                         if (this.valid)\r
5646                                 v = v.replace(/>/g, '%MCGT%');\r
5647 \r
5648                         this.node.setAttribute(n, v);\r
5649                 },\r
5650 \r
5651                 writeEndElement : function() {\r
5652                         this.node = this.node.parentNode;\r
5653                 },\r
5654 \r
5655                 writeFullEndElement : function() {\r
5656                         var t = this, n = t.node;\r
5657 \r
5658                         n.appendChild(t.doc.createTextNode(""));\r
5659                         t.node = n.parentNode;\r
5660                 },\r
5661 \r
5662                 writeText : function(v) {\r
5663                         if (this.valid)\r
5664                                 v = v.replace(/>/g, '%MCGT%');\r
5665 \r
5666                         this.node.appendChild(this.doc.createTextNode(v));\r
5667                 },\r
5668 \r
5669                 writeCDATA : function(v) {\r
5670                         this.node.appendChild(this.doc.createCDATASection(v));\r
5671                 },\r
5672 \r
5673                 writeComment : function(v) {\r
5674                         // Fix for bug #2035694\r
5675                         if (tinymce.isIE)\r
5676                                 v = v.replace(/^\-|\-$/g, ' ');\r
5677 \r
5678                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
5679                 },\r
5680 \r
5681                 getContent : function() {\r
5682                         var h;\r
5683 \r
5684                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
5685                         h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
5686                         h = h.replace(/ ?\/>/g, ' />');\r
5687 \r
5688                         if (this.valid)\r
5689                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
5690 \r
5691                         return h;\r
5692                 }\r
5693         });\r
5694 })(tinymce);\r
5695 \r
5696 (function(tinymce) {\r
5697         tinymce.create('tinymce.dom.StringWriter', {\r
5698                 str : null,\r
5699                 tags : null,\r
5700                 count : 0,\r
5701                 settings : null,\r
5702                 indent : null,\r
5703 \r
5704                 StringWriter : function(s) {\r
5705                         this.settings = tinymce.extend({\r
5706                                 indent_char : ' ',\r
5707                                 indentation : 0\r
5708                         }, s);\r
5709 \r
5710                         this.reset();\r
5711                 },\r
5712 \r
5713                 reset : function() {\r
5714                         this.indent = '';\r
5715                         this.str = "";\r
5716                         this.tags = [];\r
5717                         this.count = 0;\r
5718                 },\r
5719 \r
5720                 writeStartElement : function(n) {\r
5721                         this._writeAttributesEnd();\r
5722                         this.writeRaw('<' + n);\r
5723                         this.tags.push(n);\r
5724                         this.inAttr = true;\r
5725                         this.count++;\r
5726                         this.elementCount = this.count;\r
5727                 },\r
5728 \r
5729                 writeAttribute : function(n, v) {\r
5730                         var t = this;\r
5731 \r
5732                         t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
5733                 },\r
5734 \r
5735                 writeEndElement : function() {\r
5736                         var n;\r
5737 \r
5738                         if (this.tags.length > 0) {\r
5739                                 n = this.tags.pop();\r
5740 \r
5741                                 if (this._writeAttributesEnd(1))\r
5742                                         this.writeRaw('</' + n + '>');\r
5743 \r
5744                                 if (this.settings.indentation > 0)\r
5745                                         this.writeRaw('\n');\r
5746                         }\r
5747                 },\r
5748 \r
5749                 writeFullEndElement : function() {\r
5750                         if (this.tags.length > 0) {\r
5751                                 this._writeAttributesEnd();\r
5752                                 this.writeRaw('</' + this.tags.pop() + '>');\r
5753 \r
5754                                 if (this.settings.indentation > 0)\r
5755                                         this.writeRaw('\n');\r
5756                         }\r
5757                 },\r
5758 \r
5759                 writeText : function(v) {\r
5760                         this._writeAttributesEnd();\r
5761                         this.writeRaw(this.encode(v));\r
5762                         this.count++;\r
5763                 },\r
5764 \r
5765                 writeCDATA : function(v) {\r
5766                         this._writeAttributesEnd();\r
5767                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5768                         this.count++;\r
5769                 },\r
5770 \r
5771                 writeComment : function(v) {\r
5772                         this._writeAttributesEnd();\r
5773                         this.writeRaw('<!-- ' + v + '-->');\r
5774                         this.count++;\r
5775                 },\r
5776 \r
5777                 writeRaw : function(v) {\r
5778                         this.str += v;\r
5779                 },\r
5780 \r
5781                 encode : function(s) {\r
5782                         return s.replace(/[<>&"]/g, function(v) {\r
5783                                 switch (v) {\r
5784                                         case '<':\r
5785                                                 return '&lt;';\r
5786 \r
5787                                         case '>':\r
5788                                                 return '&gt;';\r
5789 \r
5790                                         case '&':\r
5791                                                 return '&amp;';\r
5792 \r
5793                                         case '"':\r
5794                                                 return '&quot;';\r
5795                                 }\r
5796 \r
5797                                 return v;\r
5798                         });\r
5799                 },\r
5800 \r
5801                 getContent : function() {\r
5802                         return this.str;\r
5803                 },\r
5804 \r
5805                 _writeAttributesEnd : function(s) {\r
5806                         if (!this.inAttr)\r
5807                                 return;\r
5808 \r
5809                         this.inAttr = false;\r
5810 \r
5811                         if (s && this.elementCount == this.count) {\r
5812                                 this.writeRaw(' />');\r
5813                                 return false;\r
5814                         }\r
5815 \r
5816                         this.writeRaw('>');\r
5817 \r
5818                         return true;\r
5819                 }\r
5820         });\r
5821 })(tinymce);\r
5822 \r
5823 (function(tinymce) {\r
5824         // Shorten names\r
5825         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5826 \r
5827         function wildcardToRE(s) {\r
5828                 return s.replace(/([?+*])/g, '.$1');\r
5829         };\r
5830 \r
5831         tinymce.create('tinymce.dom.Serializer', {\r
5832                 Serializer : function(s) {\r
5833                         var t = this;\r
5834 \r
5835                         t.key = 0;\r
5836                         t.onPreProcess = new Dispatcher(t);\r
5837                         t.onPostProcess = new Dispatcher(t);\r
5838 \r
5839                         try {\r
5840                                 t.writer = new tinymce.dom.XMLWriter();\r
5841                         } catch (ex) {\r
5842                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5843                                 t.writer = new tinymce.dom.StringWriter();\r
5844                         }\r
5845 \r
5846                         // Default settings\r
5847                         t.settings = s = extend({\r
5848                                 dom : tinymce.DOM,\r
5849                                 valid_nodes : 0,\r
5850                                 node_filter : 0,\r
5851                                 attr_filter : 0,\r
5852                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5853                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5854                                 entity_encoding : 'named',\r
5855                                 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
5856                                 valid_elements : '*[*]',\r
5857                                 extended_valid_elements : 0,\r
5858                                 invalid_elements : 0,\r
5859                                 fix_table_elements : 1,\r
5860                                 fix_list_elements : true,\r
5861                                 fix_content_duplication : true,\r
5862                                 convert_fonts_to_spans : false,\r
5863                                 font_size_classes : 0,\r
5864                                 apply_source_formatting : 0,\r
5865                                 indent_mode : 'simple',\r
5866                                 indent_char : '\t',\r
5867                                 indent_levels : 1,\r
5868                                 remove_linebreaks : 1,\r
5869                                 remove_redundant_brs : 1,\r
5870                                 element_format : 'xhtml'\r
5871                         }, s);\r
5872 \r
5873                         t.dom = s.dom;\r
5874                         t.schema = s.schema;\r
5875 \r
5876                         // Use raw entities if no entities are defined\r
5877                         if (s.entity_encoding == 'named' && !s.entities)\r
5878                                 s.entity_encoding = 'raw';\r
5879 \r
5880                         if (s.remove_redundant_brs) {\r
5881                                 t.onPostProcess.add(function(se, o) {\r
5882                                         // Remove single BR at end of block elements since they get rendered\r
5883                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5884                                                 // Check if it's a single element\r
5885                                                 if (/^<br \/>\s*<\//.test(a))\r
5886                                                         return '</' + c + '>';\r
5887 \r
5888                                                 return a;\r
5889                                         });\r
5890                                 });\r
5891                         }\r
5892 \r
5893                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
5894                         if (s.element_format == 'html') {\r
5895                                 t.onPostProcess.add(function(se, o) {\r
5896                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
5897                                 });\r
5898                         }\r
5899 \r
5900                         if (s.fix_list_elements) {\r
5901                                 t.onPreProcess.add(function(se, o) {\r
5902                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
5903 \r
5904                                         function prevNode(e, n) {\r
5905                                                 var a = n.split(','), i;\r
5906 \r
5907                                                 while ((e = e.previousSibling) != null) {\r
5908                                                         for (i=0; i<a.length; i++) {\r
5909                                                                 if (e.nodeName == a[i])\r
5910                                                                         return e;\r
5911                                                         }\r
5912                                                 }\r
5913 \r
5914                                                 return null;\r
5915                                         };\r
5916 \r
5917                                         for (x=0; x<a.length; x++) {\r
5918                                                 nl = t.dom.select(a[x], o.node);\r
5919 \r
5920                                                 for (i=0; i<nl.length; i++) {\r
5921                                                         n = nl[i];\r
5922                                                         p = n.parentNode;\r
5923 \r
5924                                                         if (r.test(p.nodeName)) {\r
5925                                                                 np = prevNode(n, 'LI');\r
5926 \r
5927                                                                 if (!np) {\r
5928                                                                         np = t.dom.create('li');\r
5929                                                                         np.innerHTML = '&nbsp;';\r
5930                                                                         np.appendChild(n);\r
5931                                                                         p.insertBefore(np, p.firstChild);\r
5932                                                                 } else\r
5933                                                                         np.appendChild(n);\r
5934                                                         }\r
5935                                                 }\r
5936                                         }\r
5937                                 });\r
5938                         }\r
5939 \r
5940                         if (s.fix_table_elements) {\r
5941                                 t.onPreProcess.add(function(se, o) {\r
5942                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
5943                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
5944                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
5945                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
5946                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
5947 \r
5948                                                         if (parent.nodeName != 'TABLE') {\r
5949                                                                 try {\r
5950                                                                         t.dom.split(parent, n);\r
5951                                                                 } catch (ex) {\r
5952                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
5953                                                                 }\r
5954                                                         }\r
5955                                                 });\r
5956                                         }\r
5957                                 });\r
5958                         }\r
5959                 },\r
5960 \r
5961                 setEntities : function(s) {\r
5962                         var t = this, a, i, l = {}, v;\r
5963 \r
5964                         // No need to setup more than once\r
5965                         if (t.entityLookup)\r
5966                                 return;\r
5967 \r
5968                         // Build regex and lookup array\r
5969                         a = s.split(',');\r
5970                         for (i = 0; i < a.length; i += 2) {\r
5971                                 v = a[i];\r
5972 \r
5973                                 // Don't add default &amp; &quot; etc.\r
5974                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
5975                                         continue;\r
5976 \r
5977                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
5978 \r
5979                                 v = parseInt(a[i]).toString(16);\r
5980                         }\r
5981 \r
5982                         t.entityLookup = l;\r
5983                 },\r
5984 \r
5985                 setRules : function(s) {\r
5986                         var t = this;\r
5987 \r
5988                         t._setup();\r
5989                         t.rules = {};\r
5990                         t.wildRules = [];\r
5991                         t.validElements = {};\r
5992 \r
5993                         return t.addRules(s);\r
5994                 },\r
5995 \r
5996                 addRules : function(s) {\r
5997                         var t = this, dr;\r
5998 \r
5999                         if (!s)\r
6000                                 return;\r
6001 \r
6002                         t._setup();\r
6003 \r
6004                         each(s.split(','), function(s) {\r
6005                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
6006 \r
6007                                 // Extend with default rules\r
6008                                 if (dr)\r
6009                                         at = tinymce.extend([], dr.attribs);\r
6010 \r
6011                                 // Parse attributes\r
6012                                 if (p.length > 1) {\r
6013                                         each(p[1].split('|'), function(s) {\r
6014                                                 var ar = {}, i;\r
6015 \r
6016                                                 at = at || [];\r
6017 \r
6018                                                 // Parse attribute rule\r
6019                                                 s = s.replace(/::/g, '~');\r
6020                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
6021                                                 s[2] = s[2].replace(/~/g, ':');\r
6022 \r
6023                                                 // Add required attributes\r
6024                                                 if (s[1] == '!') {\r
6025                                                         ra = ra || [];\r
6026                                                         ra.push(s[2]);\r
6027                                                 }\r
6028 \r
6029                                                 // Remove inherited attributes\r
6030                                                 if (s[1] == '-') {\r
6031                                                         for (i = 0; i <at.length; i++) {\r
6032                                                                 if (at[i].name == s[2]) {\r
6033                                                                         at.splice(i, 1);\r
6034                                                                         return;\r
6035                                                                 }\r
6036                                                         }\r
6037                                                 }\r
6038 \r
6039                                                 switch (s[3]) {\r
6040                                                         // Add default attrib values\r
6041                                                         case '=':\r
6042                                                                 ar.defaultVal = s[4] || '';\r
6043                                                                 break;\r
6044 \r
6045                                                         // Add forced attrib values\r
6046                                                         case ':':\r
6047                                                                 ar.forcedVal = s[4];\r
6048                                                                 break;\r
6049 \r
6050                                                         // Add validation values\r
6051                                                         case '<':\r
6052                                                                 ar.validVals = s[4].split('?');\r
6053                                                                 break;\r
6054                                                 }\r
6055 \r
6056                                                 if (/[*.?]/.test(s[2])) {\r
6057                                                         wat = wat || [];\r
6058                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
6059                                                         wat.push(ar);\r
6060                                                 } else {\r
6061                                                         ar.name = s[2];\r
6062                                                         at.push(ar);\r
6063                                                 }\r
6064 \r
6065                                                 va.push(s[2]);\r
6066                                         });\r
6067                                 }\r
6068 \r
6069                                 // Handle element names\r
6070                                 each(tn, function(s, i) {\r
6071                                         var pr = s.charAt(0), x = 1, ru = {};\r
6072 \r
6073                                         // Extend with default rule data\r
6074                                         if (dr) {\r
6075                                                 if (dr.noEmpty)\r
6076                                                         ru.noEmpty = dr.noEmpty;\r
6077 \r
6078                                                 if (dr.fullEnd)\r
6079                                                         ru.fullEnd = dr.fullEnd;\r
6080 \r
6081                                                 if (dr.padd)\r
6082                                                         ru.padd = dr.padd;\r
6083                                         }\r
6084 \r
6085                                         // Handle prefixes\r
6086                                         switch (pr) {\r
6087                                                 case '-':\r
6088                                                         ru.noEmpty = true;\r
6089                                                         break;\r
6090 \r
6091                                                 case '+':\r
6092                                                         ru.fullEnd = true;\r
6093                                                         break;\r
6094 \r
6095                                                 case '#':\r
6096                                                         ru.padd = true;\r
6097                                                         break;\r
6098 \r
6099                                                 default:\r
6100                                                         x = 0;\r
6101                                         }\r
6102 \r
6103                                         tn[i] = s = s.substring(x);\r
6104                                         t.validElements[s] = 1;\r
6105 \r
6106                                         // Add element name or element regex\r
6107                                         if (/[*.?]/.test(tn[0])) {\r
6108                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
6109                                                 t.wildRules = t.wildRules || {};\r
6110                                                 t.wildRules.push(ru);\r
6111                                         } else {\r
6112                                                 ru.name = tn[0];\r
6113 \r
6114                                                 // Store away default rule\r
6115                                                 if (tn[0] == '@')\r
6116                                                         dr = ru;\r
6117 \r
6118                                                 t.rules[s] = ru;\r
6119                                         }\r
6120 \r
6121                                         ru.attribs = at;\r
6122 \r
6123                                         if (ra)\r
6124                                                 ru.requiredAttribs = ra;\r
6125 \r
6126                                         if (wat) {\r
6127                                                 // Build valid attributes regexp\r
6128                                                 s = '';\r
6129                                                 each(va, function(v) {\r
6130                                                         if (s)\r
6131                                                                 s += '|';\r
6132 \r
6133                                                         s += '(' + wildcardToRE(v) + ')';\r
6134                                                 });\r
6135                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
6136                                                 ru.wildAttribs = wat;\r
6137                                         }\r
6138                                 });\r
6139                         });\r
6140 \r
6141                         // Build valid elements regexp\r
6142                         s = '';\r
6143                         each(t.validElements, function(v, k) {\r
6144                                 if (s)\r
6145                                         s += '|';\r
6146 \r
6147                                 if (k != '@')\r
6148                                         s += k;\r
6149                         });\r
6150                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
6151 \r
6152                         //console.debug(t.validElementsRE.toString());\r
6153                         //console.dir(t.rules);\r
6154                         //console.dir(t.wildRules);\r
6155                 },\r
6156 \r
6157                 findRule : function(n) {\r
6158                         var t = this, rl = t.rules, i, r;\r
6159 \r
6160                         t._setup();\r
6161 \r
6162                         // Exact match\r
6163                         r = rl[n];\r
6164                         if (r)\r
6165                                 return r;\r
6166 \r
6167                         // Try wildcards\r
6168                         rl = t.wildRules;\r
6169                         for (i = 0; i < rl.length; i++) {\r
6170                                 if (rl[i].nameRE.test(n))\r
6171                                         return rl[i];\r
6172                         }\r
6173 \r
6174                         return null;\r
6175                 },\r
6176 \r
6177                 findAttribRule : function(ru, n) {\r
6178                         var i, wa = ru.wildAttribs;\r
6179 \r
6180                         for (i = 0; i < wa.length; i++) {\r
6181                                 if (wa[i].nameRE.test(n))\r
6182                                         return wa[i];\r
6183                         }\r
6184 \r
6185                         return null;\r
6186                 },\r
6187 \r
6188                 serialize : function(n, o) {\r
6189                         var h, t = this, doc, oldDoc, impl, selected;\r
6190 \r
6191                         t._setup();\r
6192                         o = o || {};\r
6193                         o.format = o.format || 'html';\r
6194                         t.processObj = o;\r
6195 \r
6196                         // IE looses the selected attribute on option elements so we need to store it\r
6197                         // See: http://support.microsoft.com/kb/829907\r
6198                         if (isIE) {\r
6199                                 selected = [];\r
6200                                 each(n.getElementsByTagName('option'), function(n) {\r
6201                                         var v = t.dom.getAttrib(n, 'selected');\r
6202 \r
6203                                         selected.push(v ? v : null);\r
6204                                 });\r
6205                         }\r
6206 \r
6207                         n = n.cloneNode(true);\r
6208 \r
6209                         // IE looses the selected attribute on option elements so we need to restore it\r
6210                         if (isIE) {\r
6211                                 each(n.getElementsByTagName('option'), function(n, i) {\r
6212                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
6213                                 });\r
6214                         }\r
6215 \r
6216                         // Nodes needs to be attached to something in WebKit/Opera\r
6217                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
6218                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
6219                         // This fix will make DOM ranges and make Sizzle happy!\r
6220                         impl = n.ownerDocument.implementation;\r
6221                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
6222                                 // Create an empty HTML document\r
6223                                 doc = impl.createHTMLDocument("");\r
6224 \r
6225                                 // Add the element or it's children if it's a body element to the new document\r
6226                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
6227                                         doc.body.appendChild(doc.importNode(node, true));\r
6228                                 });\r
6229 \r
6230                                 // Grab first child or body element for serialization\r
6231                                 if (n.nodeName != 'BODY')\r
6232                                         n = doc.body.firstChild;\r
6233                                 else\r
6234                                         n = doc.body;\r
6235 \r
6236                                 // set the new document in DOMUtils so createElement etc works\r
6237                                 oldDoc = t.dom.doc;\r
6238                                 t.dom.doc = doc;\r
6239                         }\r
6240 \r
6241                         t.key = '' + (parseInt(t.key) + 1);\r
6242 \r
6243                         // Pre process\r
6244                         if (!o.no_events) {\r
6245                                 o.node = n;\r
6246                                 t.onPreProcess.dispatch(t, o);\r
6247                         }\r
6248 \r
6249                         // Serialize HTML DOM into a string\r
6250                         t.writer.reset();\r
6251                         t._info = o;\r
6252                         t._serializeNode(n, o.getInner);\r
6253 \r
6254                         // Post process\r
6255                         o.content = t.writer.getContent();\r
6256 \r
6257                         // Restore the old document if it was changed\r
6258                         if (oldDoc)\r
6259                                 t.dom.doc = oldDoc;\r
6260 \r
6261                         if (!o.no_events)\r
6262                                 t.onPostProcess.dispatch(t, o);\r
6263 \r
6264                         t._postProcess(o);\r
6265                         o.node = null;\r
6266 \r
6267                         return tinymce.trim(o.content);\r
6268                 },\r
6269 \r
6270                 // Internal functions\r
6271 \r
6272                 _postProcess : function(o) {\r
6273                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
6274 \r
6275                         if (o.format == 'html') {\r
6276                                 // Protect some elements\r
6277                                 p = t._protect({\r
6278                                         content : h,\r
6279                                         patterns : [\r
6280                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
6281                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
6282                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
6283                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
6284                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
6285                                         ]\r
6286                                 });\r
6287 \r
6288                                 h = p.content;\r
6289 \r
6290                                 // Entity encode\r
6291                                 if (s.entity_encoding !== 'raw')\r
6292                                         h = t._encode(h);\r
6293 \r
6294                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
6295 /*                              if (o.set)\r
6296                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
6297                                 else\r
6298                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
6299 \r
6300                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
6301                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
6302                                 // This process is only done when getting contents out from the editor.\r
6303                                 if (!o.set) {\r
6304                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
6305                                         h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
6306 \r
6307                                         if (s.remove_linebreaks) {\r
6308                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
6309                                                 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
6310                                                 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
6311                                                 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
6312                                                 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
6313                                                 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
6314                                         }\r
6315 \r
6316                                         // Simple indentation\r
6317                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
6318                                                 // Add line breaks before and after block elements\r
6319                                                 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
6320                                                 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
6321                                                 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
6322                                                 h = h.replace(/\n\n/g, '\n');\r
6323                                         }\r
6324                                 }\r
6325 \r
6326                                 h = t._unprotect(h, p);\r
6327 \r
6328                                 // Restore CDATA sections\r
6329                                 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
6330 \r
6331                                 // Restore the \u00a0 character if raw mode is enabled\r
6332                                 if (s.entity_encoding == 'raw')\r
6333                                         h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
6334 \r
6335                                 // Restore noscript elements\r
6336                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
6337                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
6338                                 });\r
6339                         }\r
6340 \r
6341                         o.content = h;\r
6342                 },\r
6343 \r
6344                 _serializeNode : function(n, inner) {\r
6345                         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;\r
6346 \r
6347                         if (!s.node_filter || s.node_filter(n)) {\r
6348                                 switch (n.nodeType) {\r
6349                                         case 1: // Element\r
6350                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
6351                                                         return;\r
6352 \r
6353                                                 iv = keep = false;\r
6354                                                 hc = n.hasChildNodes();\r
6355                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
6356 \r
6357                                                 // Get internal type\r
6358                                                 type = n.getAttribute('_mce_type');\r
6359                                                 if (type) {\r
6360                                                         if (!t._info.cleanup) {\r
6361                                                                 iv = true;\r
6362                                                                 return;\r
6363                                                         } else\r
6364                                                                 keep = 1;\r
6365                                                 }\r
6366 \r
6367                                                 // Add correct prefix on IE\r
6368                                                 if (isIE) {\r
6369                                                         if (n.scopeName !== 'HTML' && n.scopeName !== 'html')\r
6370                                                                 nn = n.scopeName + ':' + nn;\r
6371                                                 }\r
6372 \r
6373                                                 // Remove mce prefix on IE needed for the abbr element\r
6374                                                 if (nn.indexOf('mce:') === 0)\r
6375                                                         nn = nn.substring(4);\r
6376 \r
6377                                                 // Check if valid\r
6378                                                 if (!keep) {\r
6379                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
6380                                                                 iv = true;\r
6381                                                                 break;\r
6382                                                         }\r
6383                                                 }\r
6384 \r
6385                                                 if (isIE) {\r
6386                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
6387                                                         if (s.fix_content_duplication) {\r
6388                                                                 if (n._mce_serialized == t.key)\r
6389                                                                         return;\r
6390 \r
6391                                                                 n._mce_serialized = t.key;\r
6392                                                         }\r
6393 \r
6394                                                         // IE sometimes adds a / infront of the node name\r
6395                                                         if (nn.charAt(0) == '/')\r
6396                                                                 nn = nn.substring(1);\r
6397                                                 } else if (isGecko) {\r
6398                                                         // Ignore br elements\r
6399                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
6400                                                                 return;\r
6401                                                 }\r
6402 \r
6403                                                 // Check if valid child\r
6404                                                 if (s.validate_children) {\r
6405                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
6406                                                                 iv = true;\r
6407                                                                 break;\r
6408                                                         }\r
6409 \r
6410                                                         t.elementName = nn;\r
6411                                                 }\r
6412 \r
6413                                                 ru = t.findRule(nn);\r
6414                                                 nn = ru.name || nn;\r
6415                                                 closed = s.closed.test(nn);\r
6416 \r
6417                                                 // Skip empty nodes or empty node name in IE\r
6418                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
6419                                                         iv = true;\r
6420                                                         break;\r
6421                                                 }\r
6422 \r
6423                                                 // Check required\r
6424                                                 if (ru.requiredAttribs) {\r
6425                                                         a = ru.requiredAttribs;\r
6426 \r
6427                                                         for (i = a.length - 1; i >= 0; i--) {\r
6428                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
6429                                                                         break;\r
6430                                                         }\r
6431 \r
6432                                                         // None of the required was there\r
6433                                                         if (i == -1) {\r
6434                                                                 iv = true;\r
6435                                                                 break;\r
6436                                                         }\r
6437                                                 }\r
6438 \r
6439                                                 w.writeStartElement(nn);\r
6440 \r
6441                                                 // Add ordered attributes\r
6442                                                 if (ru.attribs) {\r
6443                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
6444                                                                 a = at[i];\r
6445                                                                 v = t._getAttrib(n, a);\r
6446 \r
6447                                                                 if (v !== null)\r
6448                                                                         w.writeAttribute(a.name, v);\r
6449                                                         }\r
6450                                                 }\r
6451 \r
6452                                                 // Add wild attributes\r
6453                                                 if (ru.validAttribsRE) {\r
6454                                                         at = t.dom.getAttribs(n);\r
6455                                                         for (i=at.length-1; i>-1; i--) {\r
6456                                                                 no = at[i];\r
6457 \r
6458                                                                 if (no.specified) {\r
6459                                                                         a = no.nodeName.toLowerCase();\r
6460 \r
6461                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
6462                                                                                 continue;\r
6463 \r
6464                                                                         ar = t.findAttribRule(ru, a);\r
6465                                                                         v = t._getAttrib(n, ar, a);\r
6466 \r
6467                                                                         if (v !== null)\r
6468                                                                                 w.writeAttribute(a, v);\r
6469                                                                 }\r
6470                                                         }\r
6471                                                 }\r
6472 \r
6473                                                 // Keep type attribute\r
6474                                                 if (type && keep)\r
6475                                                         w.writeAttribute('_mce_type', type);\r
6476 \r
6477                                                 // Write text from script\r
6478                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
6479                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
6480                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
6481                                                         hc = false;\r
6482                                                         break;\r
6483                                                 }\r
6484 \r
6485                                                 // Padd empty nodes with a &nbsp;\r
6486                                                 if (ru.padd) {\r
6487                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
6488                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
6489                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
6490                                                                         w.writeText('\u00a0');\r
6491                                                         } else if (!hc)\r
6492                                                                 w.writeText('\u00a0'); // No children then padd it\r
6493                                                 }\r
6494 \r
6495                                                 break;\r
6496 \r
6497                                         case 3: // Text\r
6498                                                 // Check if valid child\r
6499                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
6500                                                         return;\r
6501 \r
6502                                                 return w.writeText(n.nodeValue);\r
6503 \r
6504                                         case 4: // CDATA\r
6505                                                 return w.writeCDATA(n.nodeValue);\r
6506 \r
6507                                         case 8: // Comment\r
6508                                                 return w.writeComment(n.nodeValue);\r
6509                                 }\r
6510                         } else if (n.nodeType == 1)\r
6511                                 hc = n.hasChildNodes();\r
6512 \r
6513                         if (hc && !closed) {\r
6514                                 cn = n.firstChild;\r
6515 \r
6516                                 while (cn) {\r
6517                                         t._serializeNode(cn);\r
6518                                         t.elementName = nn;\r
6519                                         cn = cn.nextSibling;\r
6520                                 }\r
6521                         }\r
6522 \r
6523                         // Write element end\r
6524                         if (!iv) {\r
6525                                 if (!closed)\r
6526                                         w.writeFullEndElement();\r
6527                                 else\r
6528                                         w.writeEndElement();\r
6529                         }\r
6530                 },\r
6531 \r
6532                 _protect : function(o) {\r
6533                         var t = this;\r
6534 \r
6535                         o.items = o.items || [];\r
6536 \r
6537                         function enc(s) {\r
6538                                 return s.replace(/[\r\n\\]/g, function(c) {\r
6539                                         if (c === '\n')\r
6540                                                 return '\\n';\r
6541                                         else if (c === '\\')\r
6542                                                 return '\\\\';\r
6543 \r
6544                                         return '\\r';\r
6545                                 });\r
6546                         };\r
6547 \r
6548                         function dec(s) {\r
6549                                 return s.replace(/\\[\\rn]/g, function(c) {\r
6550                                         if (c === '\\n')\r
6551                                                 return '\n';\r
6552                                         else if (c === '\\\\')\r
6553                                                 return '\\';\r
6554 \r
6555                                         return '\r';\r
6556                                 });\r
6557                         };\r
6558 \r
6559                         each(o.patterns, function(p) {\r
6560                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
6561                                         b = dec(b);\r
6562 \r
6563                                         if (p.encode)\r
6564                                                 b = t._encode(b);\r
6565 \r
6566                                         o.items.push(b);\r
6567                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
6568                                 }));\r
6569                         });\r
6570 \r
6571                         return o;\r
6572                 },\r
6573 \r
6574                 _unprotect : function(h, o) {\r
6575                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
6576                                 return o.items[parseInt(b)];\r
6577                         });\r
6578 \r
6579                         o.items = [];\r
6580 \r
6581                         return h;\r
6582                 },\r
6583 \r
6584                 _encode : function(h) {\r
6585                         var t = this, s = t.settings, l;\r
6586 \r
6587                         // Entity encode\r
6588                         if (s.entity_encoding !== 'raw') {\r
6589                                 if (s.entity_encoding.indexOf('named') != -1) {\r
6590                                         t.setEntities(s.entities);\r
6591                                         l = t.entityLookup;\r
6592 \r
6593                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6594                                                 var v;\r
6595 \r
6596                                                 if (v = l[a])\r
6597                                                         a = '&' + v + ';';\r
6598 \r
6599                                                 return a;\r
6600                                         });\r
6601                                 }\r
6602 \r
6603                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
6604                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6605                                                 return '&#' + a.charCodeAt(0) + ';';\r
6606                                         });\r
6607                                 }\r
6608                         }\r
6609 \r
6610                         return h;\r
6611                 },\r
6612 \r
6613                 _setup : function() {\r
6614                         var t = this, s = this.settings;\r
6615 \r
6616                         if (t.done)\r
6617                                 return;\r
6618 \r
6619                         t.done = 1;\r
6620 \r
6621                         t.setRules(s.valid_elements);\r
6622                         t.addRules(s.extended_valid_elements);\r
6623 \r
6624                         if (s.invalid_elements)\r
6625                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
6626 \r
6627                         if (s.attrib_value_filter)\r
6628                                 t.attribValueFilter = s.attribValueFilter;\r
6629                 },\r
6630 \r
6631                 _getAttrib : function(n, a, na) {\r
6632                         var i, v;\r
6633 \r
6634                         na = na || a.name;\r
6635 \r
6636                         if (a.forcedVal && (v = a.forcedVal)) {\r
6637                                 if (v === '{$uid}')\r
6638                                         return this.dom.uniqueId();\r
6639 \r
6640                                 return v;\r
6641                         }\r
6642 \r
6643                         v = this.dom.getAttrib(n, na);\r
6644 \r
6645                         switch (na) {\r
6646                                 case 'rowspan':\r
6647                                 case 'colspan':\r
6648                                         // Whats the point? Remove usless attribute value\r
6649                                         if (v == '1')\r
6650                                                 v = '';\r
6651 \r
6652                                         break;\r
6653                         }\r
6654 \r
6655                         if (this.attribValueFilter)\r
6656                                 v = this.attribValueFilter(na, v, n);\r
6657 \r
6658                         if (a.validVals) {\r
6659                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
6660                                         if (v == a.validVals[i])\r
6661                                                 break;\r
6662                                 }\r
6663 \r
6664                                 if (i == -1)\r
6665                                         return null;\r
6666                         }\r
6667 \r
6668                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
6669                                 v = a.defaultVal;\r
6670 \r
6671                                 if (v === '{$uid}')\r
6672                                         return this.dom.uniqueId();\r
6673 \r
6674                                 return v;\r
6675                         } else {\r
6676                                 // Remove internal mceItemXX classes when content is extracted from editor\r
6677                                 if (na == 'class' && this.processObj.get)\r
6678                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
6679                         }\r
6680 \r
6681                         if (v === '')\r
6682                                 return null;\r
6683 \r
6684 \r
6685                         return v;\r
6686                 }\r
6687         });\r
6688 })(tinymce);\r
6689 \r
6690 (function(tinymce) {\r
6691         tinymce.dom.ScriptLoader = function(settings) {\r
6692                 var QUEUED = 0,\r
6693                         LOADING = 1,\r
6694                         LOADED = 2,\r
6695                         states = {},\r
6696                         queue = [],\r
6697                         scriptLoadedCallbacks = {},\r
6698                         queueLoadedCallbacks = [],\r
6699                         loading = 0,\r
6700                         undefined;\r
6701 \r
6702                 function loadScript(url, callback) {\r
6703                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
6704 \r
6705                         // Execute callback when script is loaded\r
6706                         function done() {\r
6707                                 dom.remove(id);\r
6708 \r
6709                                 if (elm)\r
6710                                         elm.onreadystatechange = elm.onload = elm = null;\r
6711 \r
6712                                 callback();\r
6713                         };\r
6714 \r
6715                         id = dom.uniqueId();\r
6716 \r
6717                         if (tinymce.isIE6) {\r
6718                                 uri = new tinymce.util.URI(url);\r
6719                                 loc = location;\r
6720 \r
6721                                 // If script is from same domain and we\r
6722                                 // use IE 6 then use XHR since it's more reliable\r
6723                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
6724                                         tinymce.util.XHR.send({\r
6725                                                 url : tinymce._addVer(uri.getURI()),\r
6726                                                 success : function(content) {\r
6727                                                         // Create new temp script element\r
6728                                                         var script = dom.create('script', {\r
6729                                                                 type : 'text/javascript'\r
6730                                                         });\r
6731 \r
6732                                                         // Evaluate script in global scope\r
6733                                                         script.text = content;\r
6734                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
6735                                                         dom.remove(script);\r
6736 \r
6737                                                         done();\r
6738                                                 }\r
6739                                         });\r
6740 \r
6741                                         return;\r
6742                                 }\r
6743                         }\r
6744 \r
6745                         // Create new script element\r
6746                         elm = dom.create('script', {\r
6747                                 id : id,\r
6748                                 type : 'text/javascript',\r
6749                                 src : tinymce._addVer(url)\r
6750                         });\r
6751 \r
6752                         // Add onload and readystate listeners\r
6753                         elm.onload = done;\r
6754                         elm.onreadystatechange = function() {\r
6755                                 var state = elm.readyState;\r
6756 \r
6757                                 // Loaded state is passed on IE 6 however there\r
6758                                 // are known issues with this method but we can't use\r
6759                                 // XHR in a cross domain loading\r
6760                                 if (state == 'complete' || state == 'loaded')\r
6761                                         done();\r
6762                         };\r
6763 \r
6764                         // Most browsers support this feature so we report errors\r
6765                         // for those at least to help users track their missing plugins etc\r
6766                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6767                         /*elm.onerror = function() {\r
6768                                 alert('Failed to load: ' + url);\r
6769                         };*/\r
6770 \r
6771                         // Add script to document\r
6772                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6773                 };\r
6774 \r
6775                 this.isDone = function(url) {\r
6776                         return states[url] == LOADED;\r
6777                 };\r
6778 \r
6779                 this.markDone = function(url) {\r
6780                         states[url] = LOADED;\r
6781                 };\r
6782 \r
6783                 this.add = this.load = function(url, callback, scope) {\r
6784                         var item, state = states[url];\r
6785 \r
6786                         // Add url to load queue\r
6787                         if (state == undefined) {\r
6788                                 queue.push(url);\r
6789                                 states[url] = QUEUED;\r
6790                         }\r
6791 \r
6792                         if (callback) {\r
6793                                 // Store away callback for later execution\r
6794                                 if (!scriptLoadedCallbacks[url])\r
6795                                         scriptLoadedCallbacks[url] = [];\r
6796 \r
6797                                 scriptLoadedCallbacks[url].push({\r
6798                                         func : callback,\r
6799                                         scope : scope || this\r
6800                                 });\r
6801                         }\r
6802                 };\r
6803 \r
6804                 this.loadQueue = function(callback, scope) {\r
6805                         this.loadScripts(queue, callback, scope);\r
6806                 };\r
6807 \r
6808                 this.loadScripts = function(scripts, callback, scope) {\r
6809                         var loadScripts;\r
6810 \r
6811                         function execScriptLoadedCallbacks(url) {\r
6812                                 // Execute URL callback functions\r
6813                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6814                                         callback.func.call(callback.scope);\r
6815                                 });\r
6816 \r
6817                                 scriptLoadedCallbacks[url] = undefined;\r
6818                         };\r
6819 \r
6820                         queueLoadedCallbacks.push({\r
6821                                 func : callback,\r
6822                                 scope : scope || this\r
6823                         });\r
6824 \r
6825                         loadScripts = function() {\r
6826                                 var loadingScripts = tinymce.grep(scripts);\r
6827 \r
6828                                 // Current scripts has been handled\r
6829                                 scripts.length = 0;\r
6830 \r
6831                                 // Load scripts that needs to be loaded\r
6832                                 tinymce.each(loadingScripts, function(url) {\r
6833                                         // Script is already loaded then execute script callbacks directly\r
6834                                         if (states[url] == LOADED) {\r
6835                                                 execScriptLoadedCallbacks(url);\r
6836                                                 return;\r
6837                                         }\r
6838 \r
6839                                         // Is script not loading then start loading it\r
6840                                         if (states[url] != LOADING) {\r
6841                                                 states[url] = LOADING;\r
6842                                                 loading++;\r
6843 \r
6844                                                 loadScript(url, function() {\r
6845                                                         states[url] = LOADED;\r
6846                                                         loading--;\r
6847 \r
6848                                                         execScriptLoadedCallbacks(url);\r
6849 \r
6850                                                         // Load more scripts if they where added by the recently loaded script\r
6851                                                         loadScripts();\r
6852                                                 });\r
6853                                         }\r
6854                                 });\r
6855 \r
6856                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6857                                 if (!loading) {\r
6858                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6859                                                 callback.func.call(callback.scope);\r
6860                                         });\r
6861 \r
6862                                         queueLoadedCallbacks.length = 0;\r
6863                                 }\r
6864                         };\r
6865 \r
6866                         loadScripts();\r
6867                 };\r
6868         };\r
6869 \r
6870         // Global script loader\r
6871         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6872 })(tinymce);\r
6873 \r
6874 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
6875         var node = start_node;\r
6876 \r
6877         function findSibling(node, start_name, sibling_name, shallow) {\r
6878                 var sibling, parent;\r
6879 \r
6880                 if (node) {\r
6881                         // Walk into nodes if it has a start\r
6882                         if (!shallow && node[start_name])\r
6883                                 return node[start_name];\r
6884 \r
6885                         // Return the sibling if it has one\r
6886                         if (node != root_node) {\r
6887                                 sibling = node[sibling_name];\r
6888                                 if (sibling)\r
6889                                         return sibling;\r
6890 \r
6891                                 // Walk up the parents to look for siblings\r
6892                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
6893                                         sibling = parent[sibling_name];\r
6894                                         if (sibling)\r
6895                                                 return sibling;\r
6896                                 }\r
6897                         }\r
6898                 }\r
6899         };\r
6900 \r
6901         this.current = function() {\r
6902                 return node;\r
6903         };\r
6904 \r
6905         this.next = function(shallow) {\r
6906                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
6907         };\r
6908 \r
6909         this.prev = function(shallow) {\r
6910                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
6911         };\r
6912 };\r
6913 \r
6914 (function() {\r
6915         var transitional = {};\r
6916 \r
6917         function unpack(lookup, data) {\r
6918                 var key;\r
6919 \r
6920                 function replace(value) {\r
6921                         return value.replace(/[A-Z]+/g, function(key) {\r
6922                                 return replace(lookup[key]);\r
6923                         });\r
6924                 };\r
6925 \r
6926                 // Unpack lookup\r
6927                 for (key in lookup) {\r
6928                         if (lookup.hasOwnProperty(key))\r
6929                                 lookup[key] = replace(lookup[key]);\r
6930                 }\r
6931 \r
6932                 // Unpack and parse data into object map\r
6933                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
6934                         var i, map = {};\r
6935 \r
6936                         children = children.split(/\|/);\r
6937 \r
6938                         for (i = children.length - 1; i >= 0; i--)\r
6939                                 map[children[i]] = 1;\r
6940 \r
6941                         transitional[name] = map;\r
6942                 });\r
6943         };\r
6944 \r
6945         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
6946         // we will later include the attributes here and use it as a default for valid elements but it\r
6947         // requires us to rewrite the serializer engine\r
6948         unpack({\r
6949                 Z : '#|H|K|N|O|P',\r
6950                 Y : '#|X|form|R|Q',\r
6951                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
6952                 W : 'pre|hr|blockquote|address|center|noframes',\r
6953                 U : 'ul|ol|dl|menu|dir',\r
6954                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
6955                 T : 'h1|h2|h3|h4|h5|h6',\r
6956                 ZB : '#|X|S|Q',\r
6957                 S : 'R|P',\r
6958                 ZA : '#|a|G|J|M|O|P',\r
6959                 R : '#|a|H|K|N|O',\r
6960                 Q : 'noscript|P',\r
6961                 P : 'ins|del|script',\r
6962                 O : 'input|select|textarea|label|button',\r
6963                 N : 'M|L',\r
6964                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
6965                 L : 'sub|sup',\r
6966                 K : 'J|I',\r
6967                 J : 'tt|i|b|u|s|strike',\r
6968                 I : 'big|small|font|basefont',\r
6969                 H : 'G|F',\r
6970                 G : 'br|span|bdo',\r
6971                 F : 'object|applet|img|map|iframe'\r
6972         }, 'script[]' + \r
6973                 'style[]' + \r
6974                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
6975                 'param[]' + \r
6976                 'p[S]' + \r
6977                 'a[Z]' + \r
6978                 'br[]' + \r
6979                 'span[S]' + \r
6980                 'bdo[S]' + \r
6981                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
6982                 'h1[S]' + \r
6983                 'img[]' + \r
6984                 'map[X|form|Q|area]' + \r
6985                 'h2[S]' + \r
6986                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
6987                 'h3[S]' + \r
6988                 'tt[S]' + \r
6989                 'i[S]' + \r
6990                 'b[S]' + \r
6991                 'u[S]' + \r
6992                 's[S]' + \r
6993                 'strike[S]' + \r
6994                 'big[S]' + \r
6995                 'small[S]' + \r
6996                 'font[S]' + \r
6997                 'basefont[]' + \r
6998                 'em[S]' + \r
6999                 'strong[S]' + \r
7000                 'dfn[S]' + \r
7001                 'code[S]' + \r
7002                 'q[S]' + \r
7003                 'samp[S]' + \r
7004                 'kbd[S]' + \r
7005                 'var[S]' + \r
7006                 'cite[S]' + \r
7007                 'abbr[S]' + \r
7008                 'acronym[S]' + \r
7009                 'sub[S]' + \r
7010                 'sup[S]' + \r
7011                 'input[]' + \r
7012                 'select[optgroup|option]' + \r
7013                 'optgroup[option]' + \r
7014                 'option[]' + \r
7015                 'textarea[]' + \r
7016                 'label[S]' + \r
7017                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
7018                 'h4[S]' + \r
7019                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
7020                 'h5[S]' + \r
7021                 'del[#|X|form|a|H|K|N|O|Q]' + \r
7022                 'h6[S]' + \r
7023                 'div[#|X|form|a|H|K|N|O|Q]' + \r
7024                 'ul[li]' + \r
7025                 'li[#|X|form|a|H|K|N|O|Q]' + \r
7026                 'ol[li]' + \r
7027                 'dl[dt|dd]' + \r
7028                 'dt[S]' + \r
7029                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
7030                 'menu[li]' + \r
7031                 'dir[li]' + \r
7032                 'pre[ZA]' + \r
7033                 'hr[]' + \r
7034                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
7035                 'address[S|p]' + \r
7036                 'center[#|X|form|a|H|K|N|O|Q]' + \r
7037                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
7038                 'isindex[]' + \r
7039                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
7040                 'legend[S]' + \r
7041                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
7042                 'caption[S]' + \r
7043                 'col[]' + \r
7044                 'colgroup[col]' + \r
7045                 'thead[tr]' + \r
7046                 'tr[th|td]' + \r
7047                 'th[#|X|form|a|H|K|N|O|Q]' + \r
7048                 'form[#|X|a|H|K|N|O|Q]' + \r
7049                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
7050                 'td[#|X|form|a|H|K|N|O|Q]' + \r
7051                 'tfoot[tr]' + \r
7052                 'tbody[tr]' + \r
7053                 'area[]' + \r
7054                 'base[]' + \r
7055                 'body[#|X|form|a|H|K|N|O|Q]'\r
7056         );\r
7057 \r
7058         tinymce.dom.Schema = function() {\r
7059                 var t = this, elements = transitional;\r
7060 \r
7061                 t.isValid = function(name, child_name) {\r
7062                         var element = elements[name];\r
7063 \r
7064                         return !!(element && (!child_name || element[child_name]));\r
7065                 };\r
7066         };\r
7067 })();\r
7068 (function(tinymce) {\r
7069         tinymce.dom.RangeUtils = function(dom) {\r
7070                 var INVISIBLE_CHAR = '\uFEFF';\r
7071 \r
7072                 this.walk = function(rng, callback) {\r
7073                         var startContainer = rng.startContainer,\r
7074                                 startOffset = rng.startOffset,\r
7075                                 endContainer = rng.endContainer,\r
7076                                 endOffset = rng.endOffset,\r
7077                                 ancestor, startPoint,\r
7078                                 endPoint, node, parent, siblings, nodes;\r
7079 \r
7080                         // Handle table cell selection the table plugin enables\r
7081                         // you to fake select table cells and perform formatting actions on them\r
7082                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
7083                         if (nodes.length > 0) {\r
7084                                 tinymce.each(nodes, function(node) {\r
7085                                         callback([node]);\r
7086                                 });\r
7087 \r
7088                                 return;\r
7089                         }\r
7090 \r
7091                         function collectSiblings(node, name, end_node) {\r
7092                                 var siblings = [];\r
7093 \r
7094                                 for (; node && node != end_node; node = node[name])\r
7095                                         siblings.push(node);\r
7096 \r
7097                                 return siblings;\r
7098                         };\r
7099 \r
7100                         function findEndPoint(node, root) {\r
7101                                 do {\r
7102                                         if (node.parentNode == root)\r
7103                                                 return node;\r
7104 \r
7105                                         node = node.parentNode;\r
7106                                 } while(node);\r
7107                         };\r
7108 \r
7109                         function walkBoundary(start_node, end_node, next) {\r
7110                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
7111 \r
7112                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
7113                                         parent = node.parentNode;\r
7114                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
7115 \r
7116                                         if (siblings.length) {\r
7117                                                 if (!next)\r
7118                                                         siblings.reverse();\r
7119 \r
7120                                                 callback(siblings);\r
7121                                         }\r
7122                                 }\r
7123                         };\r
7124 \r
7125                         // If index based start position then resolve it\r
7126                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
7127                                 startContainer = startContainer.childNodes[startOffset];\r
7128 \r
7129                         // If index based end position then resolve it\r
7130                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
7131                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
7132 \r
7133                         // Find common ancestor and end points\r
7134                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
7135 \r
7136                         // Same container\r
7137                         if (startContainer == endContainer)\r
7138                                 return callback([startContainer]);\r
7139 \r
7140                         // Process left side\r
7141                         for (node = startContainer; node; node = node.parentNode) {\r
7142                                 if (node == endContainer)\r
7143                                         return walkBoundary(startContainer, ancestor, true);\r
7144 \r
7145                                 if (node == ancestor)\r
7146                                         break;\r
7147                         }\r
7148 \r
7149                         // Process right side\r
7150                         for (node = endContainer; node; node = node.parentNode) {\r
7151                                 if (node == startContainer)\r
7152                                         return walkBoundary(endContainer, ancestor);\r
7153 \r
7154                                 if (node == ancestor)\r
7155                                         break;\r
7156                         }\r
7157 \r
7158                         // Find start/end point\r
7159                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
7160                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
7161 \r
7162                         // Walk left leaf\r
7163                         walkBoundary(startContainer, startPoint, true);\r
7164 \r
7165                         // Walk the middle from start to end point\r
7166                         siblings = collectSiblings(\r
7167                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
7168                                 'nextSibling',\r
7169                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
7170                         );\r
7171 \r
7172                         if (siblings.length)\r
7173                                 callback(siblings);\r
7174 \r
7175                         // Walk right leaf\r
7176                         walkBoundary(endContainer, endPoint);\r
7177                 };\r
7178 \r
7179                 /*              this.split = function(rng) {\r
7180                         var startContainer = rng.startContainer,\r
7181                                 startOffset = rng.startOffset,\r
7182                                 endContainer = rng.endContainer,\r
7183                                 endOffset = rng.endOffset;\r
7184 \r
7185                         function splitText(node, offset) {\r
7186                                 if (offset == node.nodeValue.length)\r
7187                                         node.appendData(INVISIBLE_CHAR);\r
7188 \r
7189                                 node = node.splitText(offset);\r
7190 \r
7191                                 if (node.nodeValue === INVISIBLE_CHAR)\r
7192                                         node.nodeValue = '';\r
7193 \r
7194                                 return node;\r
7195                         };\r
7196 \r
7197                         // Handle single text node\r
7198                         if (startContainer == endContainer) {\r
7199                                 if (startContainer.nodeType == 3) {\r
7200                                         if (startOffset != 0)\r
7201                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
7202 \r
7203                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
7204                                                 splitText(startContainer, endOffset - startOffset);\r
7205                                 }\r
7206                         } else {\r
7207                                 // Split startContainer text node if needed\r
7208                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
7209                                         startContainer = splitText(startContainer, startOffset);\r
7210                                         startOffset = 0;\r
7211                                 }\r
7212 \r
7213                                 // Split endContainer text node if needed\r
7214                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
7215                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
7216                                         endOffset = endContainer.nodeValue.length;\r
7217                                 }\r
7218                         }\r
7219 \r
7220                         return {\r
7221                                 startContainer : startContainer,\r
7222                                 startOffset : startOffset,\r
7223                                 endContainer : endContainer,\r
7224                                 endOffset : endOffset\r
7225                         };\r
7226                 };\r
7227 */\r
7228         };\r
7229 })(tinymce);\r
7230 \r
7231 (function(tinymce) {\r
7232         // Shorten class names\r
7233         var DOM = tinymce.DOM, is = tinymce.is;\r
7234 \r
7235         tinymce.create('tinymce.ui.Control', {\r
7236                 Control : function(id, s) {\r
7237                         this.id = id;\r
7238                         this.settings = s = s || {};\r
7239                         this.rendered = false;\r
7240                         this.onRender = new tinymce.util.Dispatcher(this);\r
7241                         this.classPrefix = '';\r
7242                         this.scope = s.scope || this;\r
7243                         this.disabled = 0;\r
7244                         this.active = 0;\r
7245                 },\r
7246 \r
7247                 setDisabled : function(s) {\r
7248                         var e;\r
7249 \r
7250                         if (s != this.disabled) {\r
7251                                 e = DOM.get(this.id);\r
7252 \r
7253                                 // Add accessibility title for unavailable actions\r
7254                                 if (e && this.settings.unavailable_prefix) {\r
7255                                         if (s) {\r
7256                                                 this.prevTitle = e.title;\r
7257                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
7258                                         } else\r
7259                                                 e.title = this.prevTitle;\r
7260                                 }\r
7261 \r
7262                                 this.setState('Disabled', s);\r
7263                                 this.setState('Enabled', !s);\r
7264                                 this.disabled = s;\r
7265                         }\r
7266                 },\r
7267 \r
7268                 isDisabled : function() {\r
7269                         return this.disabled;\r
7270                 },\r
7271 \r
7272                 setActive : function(s) {\r
7273                         if (s != this.active) {\r
7274                                 this.setState('Active', s);\r
7275                                 this.active = s;\r
7276                         }\r
7277                 },\r
7278 \r
7279                 isActive : function() {\r
7280                         return this.active;\r
7281                 },\r
7282 \r
7283                 setState : function(c, s) {\r
7284                         var n = DOM.get(this.id);\r
7285 \r
7286                         c = this.classPrefix + c;\r
7287 \r
7288                         if (s)\r
7289                                 DOM.addClass(n, c);\r
7290                         else\r
7291                                 DOM.removeClass(n, c);\r
7292                 },\r
7293 \r
7294                 isRendered : function() {\r
7295                         return this.rendered;\r
7296                 },\r
7297 \r
7298                 renderHTML : function() {\r
7299                 },\r
7300 \r
7301                 renderTo : function(n) {\r
7302                         DOM.setHTML(n, this.renderHTML());\r
7303                 },\r
7304 \r
7305                 postRender : function() {\r
7306                         var t = this, b;\r
7307 \r
7308                         // Set pending states\r
7309                         if (is(t.disabled)) {\r
7310                                 b = t.disabled;\r
7311                                 t.disabled = -1;\r
7312                                 t.setDisabled(b);\r
7313                         }\r
7314 \r
7315                         if (is(t.active)) {\r
7316                                 b = t.active;\r
7317                                 t.active = -1;\r
7318                                 t.setActive(b);\r
7319                         }\r
7320                 },\r
7321 \r
7322                 remove : function() {\r
7323                         DOM.remove(this.id);\r
7324                         this.destroy();\r
7325                 },\r
7326 \r
7327                 destroy : function() {\r
7328                         tinymce.dom.Event.clear(this.id);\r
7329                 }\r
7330         });\r
7331 })(tinymce);\r
7332 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
7333         Container : function(id, s) {\r
7334                 this.parent(id, s);\r
7335 \r
7336                 this.controls = [];\r
7337 \r
7338                 this.lookup = {};\r
7339         },\r
7340 \r
7341         add : function(c) {\r
7342                 this.lookup[c.id] = c;\r
7343                 this.controls.push(c);\r
7344 \r
7345                 return c;\r
7346         },\r
7347 \r
7348         get : function(n) {\r
7349                 return this.lookup[n];\r
7350         }\r
7351 });\r
7352 \r
7353 \r
7354 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
7355         Separator : function(id, s) {\r
7356                 this.parent(id, s);\r
7357                 this.classPrefix = 'mceSeparator';\r
7358         },\r
7359 \r
7360         renderHTML : function() {\r
7361                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
7362         }\r
7363 });\r
7364 \r
7365 (function(tinymce) {\r
7366         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7367 \r
7368         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
7369                 MenuItem : function(id, s) {\r
7370                         this.parent(id, s);\r
7371                         this.classPrefix = 'mceMenuItem';\r
7372                 },\r
7373 \r
7374                 setSelected : function(s) {\r
7375                         this.setState('Selected', s);\r
7376                         this.selected = s;\r
7377                 },\r
7378 \r
7379                 isSelected : function() {\r
7380                         return this.selected;\r
7381                 },\r
7382 \r
7383                 postRender : function() {\r
7384                         var t = this;\r
7385                         \r
7386                         t.parent();\r
7387 \r
7388                         // Set pending state\r
7389                         if (is(t.selected))\r
7390                                 t.setSelected(t.selected);\r
7391                 }\r
7392         });\r
7393 })(tinymce);\r
7394 \r
7395 (function(tinymce) {\r
7396         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7397 \r
7398         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
7399                 Menu : function(id, s) {\r
7400                         var t = this;\r
7401 \r
7402                         t.parent(id, s);\r
7403                         t.items = {};\r
7404                         t.collapsed = false;\r
7405                         t.menuCount = 0;\r
7406                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
7407                 },\r
7408 \r
7409                 expand : function(d) {\r
7410                         var t = this;\r
7411 \r
7412                         if (d) {\r
7413                                 walk(t, function(o) {\r
7414                                         if (o.expand)\r
7415                                                 o.expand();\r
7416                                 }, 'items', t);\r
7417                         }\r
7418 \r
7419                         t.collapsed = false;\r
7420                 },\r
7421 \r
7422                 collapse : function(d) {\r
7423                         var t = this;\r
7424 \r
7425                         if (d) {\r
7426                                 walk(t, function(o) {\r
7427                                         if (o.collapse)\r
7428                                                 o.collapse();\r
7429                                 }, 'items', t);\r
7430                         }\r
7431 \r
7432                         t.collapsed = true;\r
7433                 },\r
7434 \r
7435                 isCollapsed : function() {\r
7436                         return this.collapsed;\r
7437                 },\r
7438 \r
7439                 add : function(o) {\r
7440                         if (!o.settings)\r
7441                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
7442 \r
7443                         this.onAddItem.dispatch(this, o);\r
7444 \r
7445                         return this.items[o.id] = o;\r
7446                 },\r
7447 \r
7448                 addSeparator : function() {\r
7449                         return this.add({separator : true});\r
7450                 },\r
7451 \r
7452                 addMenu : function(o) {\r
7453                         if (!o.collapse)\r
7454                                 o = this.createMenu(o);\r
7455 \r
7456                         this.menuCount++;\r
7457 \r
7458                         return this.add(o);\r
7459                 },\r
7460 \r
7461                 hasMenus : function() {\r
7462                         return this.menuCount !== 0;\r
7463                 },\r
7464 \r
7465                 remove : function(o) {\r
7466                         delete this.items[o.id];\r
7467                 },\r
7468 \r
7469                 removeAll : function() {\r
7470                         var t = this;\r
7471 \r
7472                         walk(t, function(o) {\r
7473                                 if (o.removeAll)\r
7474                                         o.removeAll();\r
7475                                 else\r
7476                                         o.remove();\r
7477 \r
7478                                 o.destroy();\r
7479                         }, 'items', t);\r
7480 \r
7481                         t.items = {};\r
7482                 },\r
7483 \r
7484                 createMenu : function(o) {\r
7485                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
7486 \r
7487                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
7488 \r
7489                         return m;\r
7490                 }\r
7491         });\r
7492 })(tinymce);\r
7493 (function(tinymce) {\r
7494         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
7495 \r
7496         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
7497                 DropMenu : function(id, s) {\r
7498                         s = s || {};\r
7499                         s.container = s.container || DOM.doc.body;\r
7500                         s.offset_x = s.offset_x || 0;\r
7501                         s.offset_y = s.offset_y || 0;\r
7502                         s.vp_offset_x = s.vp_offset_x || 0;\r
7503                         s.vp_offset_y = s.vp_offset_y || 0;\r
7504 \r
7505                         if (is(s.icons) && !s.icons)\r
7506                                 s['class'] += ' mceNoIcons';\r
7507 \r
7508                         this.parent(id, s);\r
7509                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
7510                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
7511                         this.classPrefix = 'mceMenu';\r
7512                 },\r
7513 \r
7514                 createMenu : function(s) {\r
7515                         var t = this, cs = t.settings, m;\r
7516 \r
7517                         s.container = s.container || cs.container;\r
7518                         s.parent = t;\r
7519                         s.constrain = s.constrain || cs.constrain;\r
7520                         s['class'] = s['class'] || cs['class'];\r
7521                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
7522                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
7523                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
7524 \r
7525                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
7526 \r
7527                         return m;\r
7528                 },\r
7529 \r
7530                 update : function() {\r
7531                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
7532 \r
7533                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
7534                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
7535 \r
7536                         if (!DOM.boxModel)\r
7537                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
7538                         else\r
7539                                 t.element.setStyles({width : tw, height : th});\r
7540 \r
7541                         if (s.max_width)\r
7542                                 DOM.setStyle(co, 'width', tw);\r
7543 \r
7544                         if (s.max_height) {\r
7545                                 DOM.setStyle(co, 'height', th);\r
7546 \r
7547                                 if (tb.clientHeight < s.max_height)\r
7548                                         DOM.setStyle(co, 'overflow', 'hidden');\r
7549                         }\r
7550                 },\r
7551 \r
7552                 showMenu : function(x, y, px) {\r
7553                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
7554 \r
7555                         t.collapse(1);\r
7556 \r
7557                         if (t.isMenuVisible)\r
7558                                 return;\r
7559 \r
7560                         if (!t.rendered) {\r
7561                                 co = DOM.add(t.settings.container, t.renderNode());\r
7562 \r
7563                                 each(t.items, function(o) {\r
7564                                         o.postRender();\r
7565                                 });\r
7566 \r
7567                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7568                         } else\r
7569                                 co = DOM.get('menu_' + t.id);\r
7570 \r
7571                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
7572                         if (!tinymce.isOpera)\r
7573                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
7574 \r
7575                         DOM.show(co);\r
7576                         t.update();\r
7577 \r
7578                         x += s.offset_x || 0;\r
7579                         y += s.offset_y || 0;\r
7580                         vp.w -= 4;\r
7581                         vp.h -= 4;\r
7582 \r
7583                         // Move inside viewport if not submenu\r
7584                         if (s.constrain) {\r
7585                                 w = co.clientWidth - ot;\r
7586                                 h = co.clientHeight - ot;\r
7587                                 mx = vp.x + vp.w;\r
7588                                 my = vp.y + vp.h;\r
7589 \r
7590                                 if ((x + s.vp_offset_x + w) > mx)\r
7591                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
7592 \r
7593                                 if ((y + s.vp_offset_y + h) > my)\r
7594                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
7595                         }\r
7596 \r
7597                         DOM.setStyles(co, {left : x , top : y});\r
7598                         t.element.update();\r
7599 \r
7600                         t.isMenuVisible = 1;\r
7601                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
7602                                 var m;\r
7603 \r
7604                                 e = e.target;\r
7605 \r
7606                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
7607                                         m = t.items[e.id];\r
7608 \r
7609                                         if (m.isDisabled())\r
7610                                                 return;\r
7611 \r
7612                                         dm = t;\r
7613 \r
7614                                         while (dm) {\r
7615                                                 if (dm.hideMenu)\r
7616                                                         dm.hideMenu();\r
7617 \r
7618                                                 dm = dm.settings.parent;\r
7619                                         }\r
7620 \r
7621                                         if (m.settings.onclick)\r
7622                                                 m.settings.onclick(e);\r
7623 \r
7624                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7625                                 }\r
7626                         });\r
7627 \r
7628                         if (t.hasMenus()) {\r
7629                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
7630                                         var m, r, mi;\r
7631 \r
7632                                         e = e.target;\r
7633                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
7634                                                 m = t.items[e.id];\r
7635 \r
7636                                                 if (t.lastMenu)\r
7637                                                         t.lastMenu.collapse(1);\r
7638 \r
7639                                                 if (m.isDisabled())\r
7640                                                         return;\r
7641 \r
7642                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
7643                                                         //p = DOM.getPos(s.container);\r
7644                                                         r = DOM.getRect(e);\r
7645                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
7646                                                         t.lastMenu = m;\r
7647                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
7648                                                 }\r
7649                                         }\r
7650                                 });\r
7651                         }\r
7652 \r
7653                         t.onShowMenu.dispatch(t);\r
7654 \r
7655                         if (s.keyboard_focus) {\r
7656                                 Event.add(co, 'keydown', t._keyHandler, t);\r
7657                                 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
7658                                 t._focusIdx = 0;\r
7659                         }\r
7660                 },\r
7661 \r
7662                 hideMenu : function(c) {\r
7663                         var t = this, co = DOM.get('menu_' + t.id), e;\r
7664 \r
7665                         if (!t.isMenuVisible)\r
7666                                 return;\r
7667 \r
7668                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7669                         Event.remove(co, 'click', t.mouseClickFunc);\r
7670                         Event.remove(co, 'keydown', t._keyHandler);\r
7671                         DOM.hide(co);\r
7672                         t.isMenuVisible = 0;\r
7673 \r
7674                         if (!c)\r
7675                                 t.collapse(1);\r
7676 \r
7677                         if (t.element)\r
7678                                 t.element.hide();\r
7679 \r
7680                         if (e = DOM.get(t.id))\r
7681                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
7682 \r
7683                         t.onHideMenu.dispatch(t);\r
7684                 },\r
7685 \r
7686                 add : function(o) {\r
7687                         var t = this, co;\r
7688 \r
7689                         o = t.parent(o);\r
7690 \r
7691                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
7692                                 t._add(DOM.select('tbody', co)[0], o);\r
7693 \r
7694                         return o;\r
7695                 },\r
7696 \r
7697                 collapse : function(d) {\r
7698                         this.parent(d);\r
7699                         this.hideMenu(1);\r
7700                 },\r
7701 \r
7702                 remove : function(o) {\r
7703                         DOM.remove(o.id);\r
7704                         this.destroy();\r
7705 \r
7706                         return this.parent(o);\r
7707                 },\r
7708 \r
7709                 destroy : function() {\r
7710                         var t = this, co = DOM.get('menu_' + t.id);\r
7711 \r
7712                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7713                         Event.remove(co, 'click', t.mouseClickFunc);\r
7714 \r
7715                         if (t.element)\r
7716                                 t.element.remove();\r
7717 \r
7718                         DOM.remove(co);\r
7719                 },\r
7720 \r
7721                 renderNode : function() {\r
7722                         var t = this, s = t.settings, n, tb, co, w;\r
7723 \r
7724                         w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
7725                         co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
7726                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7727 \r
7728                         if (s.menu_line)\r
7729                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
7730 \r
7731 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
7732                         n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
7733                         tb = DOM.add(n, 'tbody');\r
7734 \r
7735                         each(t.items, function(o) {\r
7736                                 t._add(tb, o);\r
7737                         });\r
7738 \r
7739                         t.rendered = true;\r
7740 \r
7741                         return w;\r
7742                 },\r
7743 \r
7744                 // Internal functions\r
7745 \r
7746                 _keyHandler : function(e) {\r
7747                         var t = this, kc = e.keyCode;\r
7748 \r
7749                         function focus(d) {\r
7750                                 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7751 \r
7752                                 if (e) {\r
7753                                         t._focusIdx = i;\r
7754                                         e.focus();\r
7755                                 }\r
7756                         };\r
7757 \r
7758                         switch (kc) {\r
7759                                 case 38:\r
7760                                         focus(-1); // Select first link\r
7761                                         return;\r
7762 \r
7763                                 case 40:\r
7764                                         focus(1);\r
7765                                         return;\r
7766 \r
7767                                 case 13:\r
7768                                         return;\r
7769 \r
7770                                 case 27:\r
7771                                         return this.hideMenu();\r
7772                         }\r
7773                 },\r
7774 \r
7775                 _add : function(tb, o) {\r
7776                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7777 \r
7778                         if (s.separator) {\r
7779                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7780                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7781 \r
7782                                 if (n = ro.previousSibling)\r
7783                                         DOM.addClass(n, 'mceLast');\r
7784 \r
7785                                 return;\r
7786                         }\r
7787 \r
7788                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7789                         n = it = DOM.add(n, 'td');\r
7790                         n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7791 \r
7792                         DOM.addClass(it, s['class']);\r
7793 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
7794 \r
7795                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7796 \r
7797                         if (s.icon_src)\r
7798                                 DOM.add(ic, 'img', {src : s.icon_src});\r
7799 \r
7800                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7801 \r
7802                         if (o.settings.style)\r
7803                                 DOM.setAttrib(n, 'style', o.settings.style);\r
7804 \r
7805                         if (tb.childNodes.length == 1)\r
7806                                 DOM.addClass(ro, 'mceFirst');\r
7807 \r
7808                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7809                                 DOM.addClass(ro, 'mceFirst');\r
7810 \r
7811                         if (o.collapse)\r
7812                                 DOM.addClass(ro, cp + 'ItemSub');\r
7813 \r
7814                         if (n = ro.previousSibling)\r
7815                                 DOM.removeClass(n, 'mceLast');\r
7816 \r
7817                         DOM.addClass(ro, 'mceLast');\r
7818                 }\r
7819         });\r
7820 })(tinymce);\r
7821 (function(tinymce) {\r
7822         var DOM = tinymce.DOM;\r
7823 \r
7824         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7825                 Button : function(id, s) {\r
7826                         this.parent(id, s);\r
7827                         this.classPrefix = 'mceButton';\r
7828                 },\r
7829 \r
7830                 renderHTML : function() {\r
7831                         var cp = this.classPrefix, s = this.settings, h, l;\r
7832 \r
7833                         l = DOM.encode(s.label || '');\r
7834                         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
7835 \r
7836                         if (s.image)\r
7837                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7838                         else\r
7839                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7840 \r
7841                         return h;\r
7842                 },\r
7843 \r
7844                 postRender : function() {\r
7845                         var t = this, s = t.settings;\r
7846 \r
7847                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7848                                 if (!t.isDisabled())\r
7849                                         return s.onclick.call(s.scope, e);\r
7850                         });\r
7851                 }\r
7852         });\r
7853 })(tinymce);\r
7854 \r
7855 (function(tinymce) {\r
7856         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7857 \r
7858         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
7859                 ListBox : function(id, s) {\r
7860                         var t = this;\r
7861 \r
7862                         t.parent(id, s);\r
7863 \r
7864                         t.items = [];\r
7865 \r
7866                         t.onChange = new Dispatcher(t);\r
7867 \r
7868                         t.onPostRender = new Dispatcher(t);\r
7869 \r
7870                         t.onAdd = new Dispatcher(t);\r
7871 \r
7872                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7873 \r
7874                         t.classPrefix = 'mceListBox';\r
7875                 },\r
7876 \r
7877                 select : function(va) {\r
7878                         var t = this, fv, f;\r
7879 \r
7880                         if (va == undefined)\r
7881                                 return t.selectByIndex(-1);\r
7882 \r
7883                         // Is string or number make function selector\r
7884                         if (va && va.call)\r
7885                                 f = va;\r
7886                         else {\r
7887                                 f = function(v) {\r
7888                                         return v == va;\r
7889                                 };\r
7890                         }\r
7891 \r
7892                         // Do we need to do something?\r
7893                         if (va != t.selectedValue) {\r
7894                                 // Find item\r
7895                                 each(t.items, function(o, i) {\r
7896                                         if (f(o.value)) {\r
7897                                                 fv = 1;\r
7898                                                 t.selectByIndex(i);\r
7899                                                 return false;\r
7900                                         }\r
7901                                 });\r
7902 \r
7903                                 if (!fv)\r
7904                                         t.selectByIndex(-1);\r
7905                         }\r
7906                 },\r
7907 \r
7908                 selectByIndex : function(idx) {\r
7909                         var t = this, e, o;\r
7910 \r
7911                         if (idx != t.selectedIndex) {\r
7912                                 e = DOM.get(t.id + '_text');\r
7913                                 o = t.items[idx];\r
7914 \r
7915                                 if (o) {\r
7916                                         t.selectedValue = o.value;\r
7917                                         t.selectedIndex = idx;\r
7918                                         DOM.setHTML(e, DOM.encode(o.title));\r
7919                                         DOM.removeClass(e, 'mceTitle');\r
7920                                 } else {\r
7921                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
7922                                         DOM.addClass(e, 'mceTitle');\r
7923                                         t.selectedValue = t.selectedIndex = null;\r
7924                                 }\r
7925 \r
7926                                 e = 0;\r
7927                         }\r
7928                 },\r
7929 \r
7930                 add : function(n, v, o) {\r
7931                         var t = this;\r
7932 \r
7933                         o = o || {};\r
7934                         o = tinymce.extend(o, {\r
7935                                 title : n,\r
7936                                 value : v\r
7937                         });\r
7938 \r
7939                         t.items.push(o);\r
7940                         t.onAdd.dispatch(t, o);\r
7941                 },\r
7942 \r
7943                 getLength : function() {\r
7944                         return this.items.length;\r
7945                 },\r
7946 \r
7947                 renderHTML : function() {\r
7948                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
7949 \r
7950                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
7951                         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
7952                         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
7953                         h += '</tr></tbody></table>';\r
7954 \r
7955                         return h;\r
7956                 },\r
7957 \r
7958                 showMenu : function() {\r
7959                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
7960 \r
7961                         if (t.isDisabled() || t.items.length == 0)\r
7962                                 return;\r
7963 \r
7964                         if (t.menu && t.menu.isMenuVisible)\r
7965                                 return t.hideMenu();\r
7966 \r
7967                         if (!t.isMenuRendered) {\r
7968                                 t.renderMenu();\r
7969                                 t.isMenuRendered = true;\r
7970                         }\r
7971 \r
7972                         p1 = DOM.getPos(this.settings.menu_container);\r
7973                         p2 = DOM.getPos(e);\r
7974 \r
7975                         m = t.menu;\r
7976                         m.settings.offset_x = p2.x;\r
7977                         m.settings.offset_y = p2.y;\r
7978                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
7979 \r
7980                         // Select in menu\r
7981                         if (t.oldID)\r
7982                                 m.items[t.oldID].setSelected(0);\r
7983 \r
7984                         each(t.items, function(o) {\r
7985                                 if (o.value === t.selectedValue) {\r
7986                                         m.items[o.id].setSelected(1);\r
7987                                         t.oldID = o.id;\r
7988                                 }\r
7989                         });\r
7990 \r
7991                         m.showMenu(0, e.clientHeight);\r
7992 \r
7993                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7994                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
7995 \r
7996                         //DOM.get(t.id + '_text').focus();\r
7997                 },\r
7998 \r
7999                 hideMenu : function(e) {\r
8000                         var t = this;\r
8001 \r
8002                         if (t.menu && t.menu.isMenuVisible) {\r
8003                                 // Prevent double toogles by canceling the mouse click event to the button\r
8004                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
8005                                         return;\r
8006 \r
8007                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8008                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
8009                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8010                                         t.menu.hideMenu();\r
8011                                 }\r
8012                         }\r
8013                 },\r
8014 \r
8015                 renderMenu : function() {\r
8016                         var t = this, m;\r
8017 \r
8018                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8019                                 menu_line : 1,\r
8020                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
8021                                 max_width : 150,\r
8022                                 max_height : 150\r
8023                         });\r
8024 \r
8025                         m.onHideMenu.add(t.hideMenu, t);\r
8026 \r
8027                         m.add({\r
8028                                 title : t.settings.title,\r
8029                                 'class' : 'mceMenuItemTitle',\r
8030                                 onclick : function() {\r
8031                                         if (t.settings.onselect('') !== false)\r
8032                                                 t.select(''); // Must be runned after\r
8033                                 }\r
8034                         });\r
8035 \r
8036                         each(t.items, function(o) {\r
8037                                 // No value then treat it as a title\r
8038                                 if (o.value === undefined) {\r
8039                                         m.add({\r
8040                                                 title : o.title,\r
8041                                                 'class' : 'mceMenuItemTitle',\r
8042                                                 onclick : function() {\r
8043                                                         if (t.settings.onselect('') !== false)\r
8044                                                                 t.select(''); // Must be runned after\r
8045                                                 }\r
8046                                         });\r
8047                                 } else {\r
8048                                         o.id = DOM.uniqueId();\r
8049                                         o.onclick = function() {\r
8050                                                 if (t.settings.onselect(o.value) !== false)\r
8051                                                         t.select(o.value); // Must be runned after\r
8052                                         };\r
8053 \r
8054                                         m.add(o);\r
8055                                 }\r
8056                         });\r
8057 \r
8058                         t.onRenderMenu.dispatch(t, m);\r
8059                         t.menu = m;\r
8060                 },\r
8061 \r
8062                 postRender : function() {\r
8063                         var t = this, cp = t.classPrefix;\r
8064 \r
8065                         Event.add(t.id, 'click', t.showMenu, t);\r
8066                         Event.add(t.id + '_text', 'focus', function(e) {\r
8067                                 if (!t._focused) {\r
8068                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
8069                                                 var idx = -1, v, kc = e.keyCode;\r
8070 \r
8071                                                 // Find current index\r
8072                                                 each(t.items, function(v, i) {\r
8073                                                         if (t.selectedValue == v.value)\r
8074                                                                 idx = i;\r
8075                                                 });\r
8076 \r
8077                                                 // Move up/down\r
8078                                                 if (kc == 38)\r
8079                                                         v = t.items[idx - 1];\r
8080                                                 else if (kc == 40)\r
8081                                                         v = t.items[idx + 1];\r
8082                                                 else if (kc == 13) {\r
8083                                                         // Fake select on enter\r
8084                                                         v = t.selectedValue;\r
8085                                                         t.selectedValue = null; // Needs to be null to fake change\r
8086                                                         t.settings.onselect(v);\r
8087                                                         return Event.cancel(e);\r
8088                                                 }\r
8089 \r
8090                                                 if (v) {\r
8091                                                         t.hideMenu();\r
8092                                                         t.select(v.value);\r
8093                                                 }\r
8094                                         });\r
8095                                 }\r
8096 \r
8097                                 t._focused = 1;\r
8098                         });\r
8099                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
8100 \r
8101                         // Old IE doesn't have hover on all elements\r
8102                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8103                                 Event.add(t.id, 'mouseover', function() {\r
8104                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8105                                                 DOM.addClass(t.id, cp + 'Hover');\r
8106                                 });\r
8107 \r
8108                                 Event.add(t.id, 'mouseout', function() {\r
8109                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8110                                                 DOM.removeClass(t.id, cp + 'Hover');\r
8111                                 });\r
8112                         }\r
8113 \r
8114                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8115                 },\r
8116 \r
8117                 destroy : function() {\r
8118                         this.parent();\r
8119 \r
8120                         Event.clear(this.id + '_text');\r
8121                         Event.clear(this.id + '_open');\r
8122                 }\r
8123         });\r
8124 })(tinymce);\r
8125 (function(tinymce) {\r
8126         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8127 \r
8128         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
8129                 NativeListBox : function(id, s) {\r
8130                         this.parent(id, s);\r
8131                         this.classPrefix = 'mceNativeListBox';\r
8132                 },\r
8133 \r
8134                 setDisabled : function(s) {\r
8135                         DOM.get(this.id).disabled = s;\r
8136                 },\r
8137 \r
8138                 isDisabled : function() {\r
8139                         return DOM.get(this.id).disabled;\r
8140                 },\r
8141 \r
8142                 select : function(va) {\r
8143                         var t = this, fv, f;\r
8144 \r
8145                         if (va == undefined)\r
8146                                 return t.selectByIndex(-1);\r
8147 \r
8148                         // Is string or number make function selector\r
8149                         if (va && va.call)\r
8150                                 f = va;\r
8151                         else {\r
8152                                 f = function(v) {\r
8153                                         return v == va;\r
8154                                 };\r
8155                         }\r
8156 \r
8157                         // Do we need to do something?\r
8158                         if (va != t.selectedValue) {\r
8159                                 // Find item\r
8160                                 each(t.items, function(o, i) {\r
8161                                         if (f(o.value)) {\r
8162                                                 fv = 1;\r
8163                                                 t.selectByIndex(i);\r
8164                                                 return false;\r
8165                                         }\r
8166                                 });\r
8167 \r
8168                                 if (!fv)\r
8169                                         t.selectByIndex(-1);\r
8170                         }\r
8171                 },\r
8172 \r
8173                 selectByIndex : function(idx) {\r
8174                         DOM.get(this.id).selectedIndex = idx + 1;\r
8175                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
8176                 },\r
8177 \r
8178                 add : function(n, v, a) {\r
8179                         var o, t = this;\r
8180 \r
8181                         a = a || {};\r
8182                         a.value = v;\r
8183 \r
8184                         if (t.isRendered())\r
8185                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
8186 \r
8187                         o = {\r
8188                                 title : n,\r
8189                                 value : v,\r
8190                                 attribs : a\r
8191                         };\r
8192 \r
8193                         t.items.push(o);\r
8194                         t.onAdd.dispatch(t, o);\r
8195                 },\r
8196 \r
8197                 getLength : function() {\r
8198                         return DOM.get(this.id).options.length - 1;\r
8199                 },\r
8200 \r
8201                 renderHTML : function() {\r
8202                         var h, t = this;\r
8203 \r
8204                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
8205 \r
8206                         each(t.items, function(it) {\r
8207                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
8208                         });\r
8209 \r
8210                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
8211 \r
8212                         return h;\r
8213                 },\r
8214 \r
8215                 postRender : function() {\r
8216                         var t = this, ch;\r
8217 \r
8218                         t.rendered = true;\r
8219 \r
8220                         function onChange(e) {\r
8221                                 var v = t.items[e.target.selectedIndex - 1];\r
8222 \r
8223                                 if (v && (v = v.value)) {\r
8224                                         t.onChange.dispatch(t, v);\r
8225 \r
8226                                         if (t.settings.onselect)\r
8227                                                 t.settings.onselect(v);\r
8228                                 }\r
8229                         };\r
8230 \r
8231                         Event.add(t.id, 'change', onChange);\r
8232 \r
8233                         // Accessibility keyhandler\r
8234                         Event.add(t.id, 'keydown', function(e) {\r
8235                                 var bf;\r
8236 \r
8237                                 Event.remove(t.id, 'change', ch);\r
8238 \r
8239                                 bf = Event.add(t.id, 'blur', function() {\r
8240                                         Event.add(t.id, 'change', onChange);\r
8241                                         Event.remove(t.id, 'blur', bf);\r
8242                                 });\r
8243 \r
8244                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
8245                                         onChange(e);\r
8246                                         return Event.cancel(e);\r
8247                                 }\r
8248                         });\r
8249 \r
8250                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8251                 }\r
8252         });\r
8253 })(tinymce);\r
8254 (function(tinymce) {\r
8255         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8256 \r
8257         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
8258                 MenuButton : function(id, s) {\r
8259                         this.parent(id, s);\r
8260 \r
8261                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8262 \r
8263                         s.menu_container = s.menu_container || DOM.doc.body;\r
8264                 },\r
8265 \r
8266                 showMenu : function() {\r
8267                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
8268 \r
8269                         if (t.isDisabled())\r
8270                                 return;\r
8271 \r
8272                         if (!t.isMenuRendered) {\r
8273                                 t.renderMenu();\r
8274                                 t.isMenuRendered = true;\r
8275                         }\r
8276 \r
8277                         if (t.isMenuVisible)\r
8278                                 return t.hideMenu();\r
8279 \r
8280                         p1 = DOM.getPos(t.settings.menu_container);\r
8281                         p2 = DOM.getPos(e);\r
8282 \r
8283                         m = t.menu;\r
8284                         m.settings.offset_x = p2.x;\r
8285                         m.settings.offset_y = p2.y;\r
8286                         m.settings.vp_offset_x = p2.x;\r
8287                         m.settings.vp_offset_y = p2.y;\r
8288                         m.settings.keyboard_focus = t._focused;\r
8289                         m.showMenu(0, e.clientHeight);\r
8290 \r
8291                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8292                         t.setState('Selected', 1);\r
8293 \r
8294                         t.isMenuVisible = 1;\r
8295                 },\r
8296 \r
8297                 renderMenu : function() {\r
8298                         var t = this, m;\r
8299 \r
8300                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8301                                 menu_line : 1,\r
8302                                 'class' : this.classPrefix + 'Menu',\r
8303                                 icons : t.settings.icons\r
8304                         });\r
8305 \r
8306                         m.onHideMenu.add(t.hideMenu, t);\r
8307 \r
8308                         t.onRenderMenu.dispatch(t, m);\r
8309                         t.menu = m;\r
8310                 },\r
8311 \r
8312                 hideMenu : function(e) {\r
8313                         var t = this;\r
8314 \r
8315                         // Prevent double toogles by canceling the mouse click event to the button\r
8316                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
8317                                 return;\r
8318 \r
8319                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8320                                 t.setState('Selected', 0);\r
8321                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8322                                 if (t.menu)\r
8323                                         t.menu.hideMenu();\r
8324                         }\r
8325 \r
8326                         t.isMenuVisible = 0;\r
8327                 },\r
8328 \r
8329                 postRender : function() {\r
8330                         var t = this, s = t.settings;\r
8331 \r
8332                         Event.add(t.id, 'click', function() {\r
8333                                 if (!t.isDisabled()) {\r
8334                                         if (s.onclick)\r
8335                                                 s.onclick(t.value);\r
8336 \r
8337                                         t.showMenu();\r
8338                                 }\r
8339                         });\r
8340                 }\r
8341         });\r
8342 })(tinymce);\r
8343 \r
8344 (function(tinymce) {\r
8345         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8346 \r
8347         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
8348                 SplitButton : function(id, s) {\r
8349                         this.parent(id, s);\r
8350                         this.classPrefix = 'mceSplitButton';\r
8351                 },\r
8352 \r
8353                 renderHTML : function() {\r
8354                         var h, t = this, s = t.settings, h1;\r
8355 \r
8356                         h = '<tbody><tr>';\r
8357 \r
8358                         if (s.image)\r
8359                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
8360                         else\r
8361                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
8362 \r
8363                         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
8364         \r
8365                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
8366                         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
8367 \r
8368                         h += '</tr></tbody>';\r
8369 \r
8370                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
8371                 },\r
8372 \r
8373                 postRender : function() {\r
8374                         var t = this, s = t.settings;\r
8375 \r
8376                         if (s.onclick) {\r
8377                                 Event.add(t.id + '_action', 'click', function() {\r
8378                                         if (!t.isDisabled())\r
8379                                                 s.onclick(t.value);\r
8380                                 });\r
8381                         }\r
8382 \r
8383                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
8384                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
8385                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
8386 \r
8387                         // Old IE doesn't have hover on all elements\r
8388                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8389                                 Event.add(t.id, 'mouseover', function() {\r
8390                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8391                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
8392                                 });\r
8393 \r
8394                                 Event.add(t.id, 'mouseout', function() {\r
8395                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8396                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
8397                                 });\r
8398                         }\r
8399                 },\r
8400 \r
8401                 destroy : function() {\r
8402                         this.parent();\r
8403 \r
8404                         Event.clear(this.id + '_action');\r
8405                         Event.clear(this.id + '_open');\r
8406                 }\r
8407         });\r
8408 })(tinymce);\r
8409 \r
8410 (function(tinymce) {\r
8411         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
8412 \r
8413         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
8414                 ColorSplitButton : function(id, s) {\r
8415                         var t = this;\r
8416 \r
8417                         t.parent(id, s);\r
8418 \r
8419                         t.settings = s = tinymce.extend({\r
8420                                 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
8421                                 grid_width : 8,\r
8422                                 default_color : '#888888'\r
8423                         }, t.settings);\r
8424 \r
8425                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
8426 \r
8427                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
8428 \r
8429                         t.value = s.default_color;\r
8430                 },\r
8431 \r
8432                 showMenu : function() {\r
8433                         var t = this, r, p, e, p2;\r
8434 \r
8435                         if (t.isDisabled())\r
8436                                 return;\r
8437 \r
8438                         if (!t.isMenuRendered) {\r
8439                                 t.renderMenu();\r
8440                                 t.isMenuRendered = true;\r
8441                         }\r
8442 \r
8443                         if (t.isMenuVisible)\r
8444                                 return t.hideMenu();\r
8445 \r
8446                         e = DOM.get(t.id);\r
8447                         DOM.show(t.id + '_menu');\r
8448                         DOM.addClass(e, 'mceSplitButtonSelected');\r
8449                         p2 = DOM.getPos(e);\r
8450                         DOM.setStyles(t.id + '_menu', {\r
8451                                 left : p2.x,\r
8452                                 top : p2.y + e.clientHeight,\r
8453                                 zIndex : 200000\r
8454                         });\r
8455                         e = 0;\r
8456 \r
8457                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8458                         t.onShowMenu.dispatch(t);\r
8459 \r
8460                         if (t._focused) {\r
8461                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
8462                                         if (e.keyCode == 27)\r
8463                                                 t.hideMenu();\r
8464                                 });\r
8465 \r
8466                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
8467                         }\r
8468 \r
8469                         t.isMenuVisible = 1;\r
8470                 },\r
8471 \r
8472                 hideMenu : function(e) {\r
8473                         var t = this;\r
8474 \r
8475                         // Prevent double toogles by canceling the mouse click event to the button\r
8476                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
8477                                 return;\r
8478 \r
8479                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
8480                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
8481                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8482                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
8483                                 DOM.hide(t.id + '_menu');\r
8484                         }\r
8485 \r
8486                         t.onHideMenu.dispatch(t);\r
8487 \r
8488                         t.isMenuVisible = 0;\r
8489                 },\r
8490 \r
8491                 renderMenu : function() {\r
8492                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
8493 \r
8494                         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
8495                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
8496                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
8497 \r
8498                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
8499                         tb = DOM.add(n, 'tbody');\r
8500 \r
8501                         // Generate color grid\r
8502                         i = 0;\r
8503                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
8504                                 c = c.replace(/^#/, '');\r
8505 \r
8506                                 if (!i--) {\r
8507                                         tr = DOM.add(tb, 'tr');\r
8508                                         i = s.grid_width - 1;\r
8509                                 }\r
8510 \r
8511                                 n = DOM.add(tr, 'td');\r
8512 \r
8513                                 n = DOM.add(n, 'a', {\r
8514                                         href : 'javascript:;',\r
8515                                         style : {\r
8516                                                 backgroundColor : '#' + c\r
8517                                         },\r
8518                                         _mce_color : '#' + c\r
8519                                 });\r
8520                         });\r
8521 \r
8522                         if (s.more_colors_func) {\r
8523                                 n = DOM.add(tb, 'tr');\r
8524                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
8525                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
8526 \r
8527                                 Event.add(n, 'click', function(e) {\r
8528                                         s.more_colors_func.call(s.more_colors_scope || this);\r
8529                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8530                                 });\r
8531                         }\r
8532 \r
8533                         DOM.addClass(m, 'mceColorSplitMenu');\r
8534 \r
8535                         Event.add(t.id + '_menu', 'click', function(e) {\r
8536                                 var c;\r
8537 \r
8538                                 e = e.target;\r
8539 \r
8540                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
8541                                         t.setColor(c);\r
8542 \r
8543                                 return Event.cancel(e); // Prevent IE auto save warning\r
8544                         });\r
8545 \r
8546                         return w;\r
8547                 },\r
8548 \r
8549                 setColor : function(c) {\r
8550                         var t = this;\r
8551 \r
8552                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
8553 \r
8554                         t.value = c;\r
8555                         t.hideMenu();\r
8556                         t.settings.onselect(c);\r
8557                 },\r
8558 \r
8559                 postRender : function() {\r
8560                         var t = this, id = t.id;\r
8561 \r
8562                         t.parent();\r
8563                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
8564                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
8565                 },\r
8566 \r
8567                 destroy : function() {\r
8568                         this.parent();\r
8569 \r
8570                         Event.clear(this.id + '_menu');\r
8571                         Event.clear(this.id + '_more');\r
8572                         DOM.remove(this.id + '_menu');\r
8573                 }\r
8574         });\r
8575 })(tinymce);\r
8576 \r
8577 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
8578         renderHTML : function() {\r
8579                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
8580 \r
8581                 cl = t.controls;\r
8582                 for (i=0; i<cl.length; i++) {\r
8583                         // Get current control, prev control, next control and if the control is a list box or not\r
8584                         co = cl[i];\r
8585                         pr = cl[i - 1];\r
8586                         nx = cl[i + 1];\r
8587 \r
8588                         // Add toolbar start\r
8589                         if (i === 0) {\r
8590                                 c = 'mceToolbarStart';\r
8591 \r
8592                                 if (co.Button)\r
8593                                         c += ' mceToolbarStartButton';\r
8594                                 else if (co.SplitButton)\r
8595                                         c += ' mceToolbarStartSplitButton';\r
8596                                 else if (co.ListBox)\r
8597                                         c += ' mceToolbarStartListBox';\r
8598 \r
8599                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8600                         }\r
8601 \r
8602                         // Add toolbar end before list box and after the previous button\r
8603                         // This is to fix the o2k7 editor skins\r
8604                         if (pr && co.ListBox) {\r
8605                                 if (pr.Button || pr.SplitButton)\r
8606                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8607                         }\r
8608 \r
8609                         // Render control HTML\r
8610 \r
8611                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
8612                         if (dom.stdMode)\r
8613                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
8614                         else\r
8615                                 h += '<td>' + co.renderHTML() + '</td>';\r
8616 \r
8617                         // Add toolbar start after list box and before the next button\r
8618                         // This is to fix the o2k7 editor skins\r
8619                         if (nx && co.ListBox) {\r
8620                                 if (nx.Button || nx.SplitButton)\r
8621                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8622                         }\r
8623                 }\r
8624 \r
8625                 c = 'mceToolbarEnd';\r
8626 \r
8627                 if (co.Button)\r
8628                         c += ' mceToolbarEndButton';\r
8629                 else if (co.SplitButton)\r
8630                         c += ' mceToolbarEndSplitButton';\r
8631                 else if (co.ListBox)\r
8632                         c += ' mceToolbarEndListBox';\r
8633 \r
8634                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8635 \r
8636                 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
8637         }\r
8638 });\r
8639 \r
8640 (function(tinymce) {\r
8641         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
8642 \r
8643         tinymce.create('tinymce.AddOnManager', {\r
8644                 items : [],\r
8645                 urls : {},\r
8646                 lookup : {},\r
8647 \r
8648                 onAdd : new Dispatcher(this),\r
8649 \r
8650                 get : function(n) {\r
8651                         return this.lookup[n];\r
8652                 },\r
8653 \r
8654                 requireLangPack : function(n) {\r
8655                         var s = tinymce.settings;\r
8656 \r
8657                         if (s && s.language)\r
8658                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
8659                 },\r
8660 \r
8661                 add : function(id, o) {\r
8662                         this.items.push(o);\r
8663                         this.lookup[id] = o;\r
8664                         this.onAdd.dispatch(this, id, o);\r
8665 \r
8666                         return o;\r
8667                 },\r
8668 \r
8669                 load : function(n, u, cb, s) {\r
8670                         var t = this;\r
8671 \r
8672                         if (t.urls[n])\r
8673                                 return;\r
8674 \r
8675                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
8676                                 u = tinymce.baseURL + '/' +  u;\r
8677 \r
8678                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
8679                         tinymce.ScriptLoader.add(u, cb, s);\r
8680                 }\r
8681         });\r
8682 \r
8683         // Create plugin and theme managers\r
8684         tinymce.PluginManager = new tinymce.AddOnManager();\r
8685         tinymce.ThemeManager = new tinymce.AddOnManager();\r
8686 }(tinymce));\r
8687 \r
8688 (function(tinymce) {\r
8689         // Shorten names\r
8690         var each = tinymce.each, extend = tinymce.extend,\r
8691                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
8692                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8693                 explode = tinymce.explode,\r
8694                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
8695 \r
8696         // Setup some URLs where the editor API is located and where the document is\r
8697         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
8698         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
8699                 tinymce.documentBaseURL += '/';\r
8700 \r
8701         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
8702 \r
8703         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
8704 \r
8705         // Add before unload listener\r
8706         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
8707         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
8708         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
8709 \r
8710         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
8711         Event.add(window, 'beforeunload', function(e) {\r
8712                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
8713         });\r
8714 \r
8715         tinymce.onAddEditor = new Dispatcher(tinymce);\r
8716 \r
8717         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
8718 \r
8719         tinymce.EditorManager = extend(tinymce, {\r
8720                 editors : [],\r
8721 \r
8722                 i18n : {},\r
8723 \r
8724                 activeEditor : null,\r
8725 \r
8726                 init : function(s) {\r
8727                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
8728 \r
8729                         function execCallback(se, n, s) {\r
8730                                 var f = se[n];\r
8731 \r
8732                                 if (!f)\r
8733                                         return;\r
8734 \r
8735                                 if (tinymce.is(f, 'string')) {\r
8736                                         s = f.replace(/\.\w+$/, '');\r
8737                                         s = s ? tinymce.resolve(s) : 0;\r
8738                                         f = tinymce.resolve(f);\r
8739                                 }\r
8740 \r
8741                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8742                         };\r
8743 \r
8744                         s = extend({\r
8745                                 theme : "simple",\r
8746                                 language : "en"\r
8747                         }, s);\r
8748 \r
8749                         t.settings = s;\r
8750 \r
8751                         // Legacy call\r
8752                         Event.add(document, 'init', function() {\r
8753                                 var l, co;\r
8754 \r
8755                                 execCallback(s, 'onpageload');\r
8756 \r
8757                                 switch (s.mode) {\r
8758                                         case "exact":\r
8759                                                 l = s.elements || '';\r
8760 \r
8761                                                 if(l.length > 0) {\r
8762                                                         each(explode(l), function(v) {\r
8763                                                                 if (DOM.get(v)) {\r
8764                                                                         ed = new tinymce.Editor(v, s);\r
8765                                                                         el.push(ed);\r
8766                                                                         ed.render(1);\r
8767                                                                 } else {\r
8768                                                                         each(document.forms, function(f) {\r
8769                                                                                 each(f.elements, function(e) {\r
8770                                                                                         if (e.name === v) {\r
8771                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8772                                                                                                 DOM.setAttrib(e, 'id', v);\r
8773 \r
8774                                                                                                 ed = new tinymce.Editor(v, s);\r
8775                                                                                                 el.push(ed);\r
8776                                                                                                 ed.render(1);\r
8777                                                                                         }\r
8778                                                                                 });\r
8779                                                                         });\r
8780                                                                 }\r
8781                                                         });\r
8782                                                 }\r
8783                                                 break;\r
8784 \r
8785                                         case "textareas":\r
8786                                         case "specific_textareas":\r
8787                                                 function hasClass(n, c) {\r
8788                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8789                                                 };\r
8790 \r
8791                                                 each(DOM.select('textarea'), function(v) {\r
8792                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8793                                                                 return;\r
8794 \r
8795                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8796                                                                 // Can we use the name\r
8797                                                                 e = DOM.get(v.name);\r
8798                                                                 if (!v.id && !e)\r
8799                                                                         v.id = v.name;\r
8800 \r
8801                                                                 // Generate unique name if missing or already exists\r
8802                                                                 if (!v.id || t.get(v.id))\r
8803                                                                         v.id = DOM.uniqueId();\r
8804 \r
8805                                                                 ed = new tinymce.Editor(v.id, s);\r
8806                                                                 el.push(ed);\r
8807                                                                 ed.render(1);\r
8808                                                         }\r
8809                                                 });\r
8810                                                 break;\r
8811                                 }\r
8812 \r
8813                                 // Call onInit when all editors are initialized\r
8814                                 if (s.oninit) {\r
8815                                         l = co = 0;\r
8816 \r
8817                                         each(el, function(ed) {\r
8818                                                 co++;\r
8819 \r
8820                                                 if (!ed.initialized) {\r
8821                                                         // Wait for it\r
8822                                                         ed.onInit.add(function() {\r
8823                                                                 l++;\r
8824 \r
8825                                                                 // All done\r
8826                                                                 if (l == co)\r
8827                                                                         execCallback(s, 'oninit');\r
8828                                                         });\r
8829                                                 } else\r
8830                                                         l++;\r
8831 \r
8832                                                 // All done\r
8833                                                 if (l == co)\r
8834                                                         execCallback(s, 'oninit');                                      \r
8835                                         });\r
8836                                 }\r
8837                         });\r
8838                 },\r
8839 \r
8840                 get : function(id) {\r
8841                         if (id === undefined)\r
8842                                 return this.editors;\r
8843 \r
8844                         return this.editors[id];\r
8845                 },\r
8846 \r
8847                 getInstanceById : function(id) {\r
8848                         return this.get(id);\r
8849                 },\r
8850 \r
8851                 add : function(editor) {\r
8852                         var self = this, editors = self.editors;\r
8853 \r
8854                         // Add named and index editor instance\r
8855                         editors[editor.id] = editor;\r
8856                         editors.push(editor);\r
8857 \r
8858                         self._setActive(editor);\r
8859                         self.onAddEditor.dispatch(self, editor);\r
8860 \r
8861 \r
8862                         return editor;\r
8863                 },\r
8864 \r
8865                 remove : function(editor) {\r
8866                         var t = this, i, editors = t.editors;\r
8867 \r
8868                         // Not in the collection\r
8869                         if (!editors[editor.id])\r
8870                                 return null;\r
8871 \r
8872                         delete editors[editor.id];\r
8873 \r
8874                         for (i = 0; i < editors.length; i++) {\r
8875                                 if (editors[i] == editor) {\r
8876                                         editors.splice(i, 1);\r
8877                                         break;\r
8878                                 }\r
8879                         }\r
8880 \r
8881                         // Select another editor since the active one was removed\r
8882                         if (t.activeEditor == editor)\r
8883                                 t._setActive(editors[0]);\r
8884 \r
8885                         editor.destroy();\r
8886                         t.onRemoveEditor.dispatch(t, editor);\r
8887 \r
8888                         return editor;\r
8889                 },\r
8890 \r
8891                 execCommand : function(c, u, v) {\r
8892                         var t = this, ed = t.get(v), w;\r
8893 \r
8894                         // Manager commands\r
8895                         switch (c) {\r
8896                                 case "mceFocus":\r
8897                                         ed.focus();\r
8898                                         return true;\r
8899 \r
8900                                 case "mceAddEditor":\r
8901                                 case "mceAddControl":\r
8902                                         if (!t.get(v))\r
8903                                                 new tinymce.Editor(v, t.settings).render();\r
8904 \r
8905                                         return true;\r
8906 \r
8907                                 case "mceAddFrameControl":\r
8908                                         w = v.window;\r
8909 \r
8910                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
8911                                         w.tinyMCE = tinyMCE;\r
8912                                         w.tinymce = tinymce;\r
8913 \r
8914                                         tinymce.DOM.doc = w.document;\r
8915                                         tinymce.DOM.win = w;\r
8916 \r
8917                                         ed = new tinymce.Editor(v.element_id, v);\r
8918                                         ed.render();\r
8919 \r
8920                                         // Fix IE memory leaks\r
8921                                         if (tinymce.isIE) {\r
8922                                                 function clr() {\r
8923                                                         ed.destroy();\r
8924                                                         w.detachEvent('onunload', clr);\r
8925                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
8926                                                 };\r
8927 \r
8928                                                 w.attachEvent('onunload', clr);\r
8929                                         }\r
8930 \r
8931                                         v.page_window = null;\r
8932 \r
8933                                         return true;\r
8934 \r
8935                                 case "mceRemoveEditor":\r
8936                                 case "mceRemoveControl":\r
8937                                         if (ed)\r
8938                                                 ed.remove();\r
8939 \r
8940                                         return true;\r
8941 \r
8942                                 case 'mceToggleEditor':\r
8943                                         if (!ed) {\r
8944                                                 t.execCommand('mceAddControl', 0, v);\r
8945                                                 return true;\r
8946                                         }\r
8947 \r
8948                                         if (ed.isHidden())\r
8949                                                 ed.show();\r
8950                                         else\r
8951                                                 ed.hide();\r
8952 \r
8953                                         return true;\r
8954                         }\r
8955 \r
8956                         // Run command on active editor\r
8957                         if (t.activeEditor)\r
8958                                 return t.activeEditor.execCommand(c, u, v);\r
8959 \r
8960                         return false;\r
8961                 },\r
8962 \r
8963                 execInstanceCommand : function(id, c, u, v) {\r
8964                         var ed = this.get(id);\r
8965 \r
8966                         if (ed)\r
8967                                 return ed.execCommand(c, u, v);\r
8968 \r
8969                         return false;\r
8970                 },\r
8971 \r
8972                 triggerSave : function() {\r
8973                         each(this.editors, function(e) {\r
8974                                 e.save();\r
8975                         });\r
8976                 },\r
8977 \r
8978                 addI18n : function(p, o) {\r
8979                         var lo, i18n = this.i18n;\r
8980 \r
8981                         if (!tinymce.is(p, 'string')) {\r
8982                                 each(p, function(o, lc) {\r
8983                                         each(o, function(o, g) {\r
8984                                                 each(o, function(o, k) {\r
8985                                                         if (g === 'common')\r
8986                                                                 i18n[lc + '.' + k] = o;\r
8987                                                         else\r
8988                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
8989                                                 });\r
8990                                         });\r
8991                                 });\r
8992                         } else {\r
8993                                 each(o, function(o, k) {\r
8994                                         i18n[p + '.' + k] = o;\r
8995                                 });\r
8996                         }\r
8997                 },\r
8998 \r
8999                 // Private methods\r
9000 \r
9001                 _setActive : function(editor) {\r
9002                         this.selectedInstance = this.activeEditor = editor;\r
9003                 }\r
9004         });\r
9005 })(tinymce);\r
9006 \r
9007 (function(tinymce) {\r
9008         // Shorten these names\r
9009         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
9010                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
9011                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
9012                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
9013                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
9014 \r
9015         tinymce.create('tinymce.Editor', {\r
9016                 Editor : function(id, s) {\r
9017                         var t = this;\r
9018 \r
9019                         t.id = t.editorId = id;\r
9020 \r
9021                         t.execCommands = {};\r
9022                         t.queryStateCommands = {};\r
9023                         t.queryValueCommands = {};\r
9024 \r
9025                         t.isNotDirty = false;\r
9026 \r
9027                         t.plugins = {};\r
9028 \r
9029                         // Add events to the editor\r
9030                         each([\r
9031                                 'onPreInit',\r
9032 \r
9033                                 'onBeforeRenderUI',\r
9034 \r
9035                                 'onPostRender',\r
9036 \r
9037                                 'onInit',\r
9038 \r
9039                                 'onRemove',\r
9040 \r
9041                                 'onActivate',\r
9042 \r
9043                                 'onDeactivate',\r
9044 \r
9045                                 'onClick',\r
9046 \r
9047                                 'onEvent',\r
9048 \r
9049                                 'onMouseUp',\r
9050 \r
9051                                 'onMouseDown',\r
9052 \r
9053                                 'onDblClick',\r
9054 \r
9055                                 'onKeyDown',\r
9056 \r
9057                                 'onKeyUp',\r
9058 \r
9059                                 'onKeyPress',\r
9060 \r
9061                                 'onContextMenu',\r
9062 \r
9063                                 'onSubmit',\r
9064 \r
9065                                 'onReset',\r
9066 \r
9067                                 'onPaste',\r
9068 \r
9069                                 'onPreProcess',\r
9070 \r
9071                                 'onPostProcess',\r
9072 \r
9073                                 'onBeforeSetContent',\r
9074 \r
9075                                 'onBeforeGetContent',\r
9076 \r
9077                                 'onSetContent',\r
9078 \r
9079                                 'onGetContent',\r
9080 \r
9081                                 'onLoadContent',\r
9082 \r
9083                                 'onSaveContent',\r
9084 \r
9085                                 'onNodeChange',\r
9086 \r
9087                                 'onChange',\r
9088 \r
9089                                 'onBeforeExecCommand',\r
9090 \r
9091                                 'onExecCommand',\r
9092 \r
9093                                 'onUndo',\r
9094 \r
9095                                 'onRedo',\r
9096 \r
9097                                 'onVisualAid',\r
9098 \r
9099                                 'onSetProgressState'\r
9100                         ], function(e) {\r
9101                                 t[e] = new Dispatcher(t);\r
9102                         });\r
9103 \r
9104                         t.settings = s = extend({\r
9105                                 id : id,\r
9106                                 language : 'en',\r
9107                                 docs_language : 'en',\r
9108                                 theme : 'simple',\r
9109                                 skin : 'default',\r
9110                                 delta_width : 0,\r
9111                                 delta_height : 0,\r
9112                                 popup_css : '',\r
9113                                 plugins : '',\r
9114                                 document_base_url : tinymce.documentBaseURL,\r
9115                                 add_form_submit_trigger : 1,\r
9116                                 submit_patch : 1,\r
9117                                 add_unload_trigger : 1,\r
9118                                 convert_urls : 1,\r
9119                                 relative_urls : 1,\r
9120                                 remove_script_host : 1,\r
9121                                 table_inline_editing : 0,\r
9122                                 object_resizing : 1,\r
9123                                 cleanup : 1,\r
9124                                 accessibility_focus : 1,\r
9125                                 custom_shortcuts : 1,\r
9126                                 custom_undo_redo_keyboard_shortcuts : 1,\r
9127                                 custom_undo_redo_restore_selection : 1,\r
9128                                 custom_undo_redo : 1,\r
9129                                 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
9130                                 visual_table_class : 'mceItemTable',\r
9131                                 visual : 1,\r
9132                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
9133                                 apply_source_formatting : 1,\r
9134                                 directionality : 'ltr',\r
9135                                 forced_root_block : 'p',\r
9136                                 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
9137                                 hidden_input : 1,\r
9138                                 padd_empty_editor : 1,\r
9139                                 render_ui : 1,\r
9140                                 init_theme : 1,\r
9141                                 force_p_newlines : 1,\r
9142                                 indentation : '30px',\r
9143                                 keep_styles : 1,\r
9144                                 fix_table_elements : 1,\r
9145                                 inline_styles : 1,\r
9146                                 convert_fonts_to_spans : true\r
9147                         }, s);\r
9148 \r
9149                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
9150                                 base_uri : tinyMCE.baseURI\r
9151                         });\r
9152 \r
9153                         t.baseURI = tinymce.baseURI;\r
9154 \r
9155                         // Call setup\r
9156                         t.execCallback('setup', t);\r
9157                 },\r
9158 \r
9159                 render : function(nst) {\r
9160                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
9161 \r
9162                         // Page is not loaded yet, wait for it\r
9163                         if (!Event.domLoaded) {\r
9164                                 Event.add(document, 'init', function() {\r
9165                                         t.render();\r
9166                                 });\r
9167                                 return;\r
9168                         }\r
9169 \r
9170                         tinyMCE.settings = s;\r
9171 \r
9172                         // Element not found, then skip initialization\r
9173                         if (!t.getElement())\r
9174                                 return;\r
9175 \r
9176                         // Add hidden input for non input elements inside form elements\r
9177                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
9178                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
9179 \r
9180                         if (tinymce.WindowManager)\r
9181                                 t.windowManager = new tinymce.WindowManager(t);\r
9182 \r
9183                         if (s.encoding == 'xml') {\r
9184                                 t.onGetContent.add(function(ed, o) {\r
9185                                         if (o.save)\r
9186                                                 o.content = DOM.encode(o.content);\r
9187                                 });\r
9188                         }\r
9189 \r
9190                         if (s.add_form_submit_trigger) {\r
9191                                 t.onSubmit.addToTop(function() {\r
9192                                         if (t.initialized) {\r
9193                                                 t.save();\r
9194                                                 t.isNotDirty = 1;\r
9195                                         }\r
9196                                 });\r
9197                         }\r
9198 \r
9199                         if (s.add_unload_trigger) {\r
9200                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
9201                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
9202                                                 t.save({format : 'raw', no_events : true});\r
9203                                 });\r
9204                         }\r
9205 \r
9206                         tinymce.addUnload(t.destroy, t);\r
9207 \r
9208                         if (s.submit_patch) {\r
9209                                 t.onBeforeRenderUI.add(function() {\r
9210                                         var n = t.getElement().form;\r
9211 \r
9212                                         if (!n)\r
9213                                                 return;\r
9214 \r
9215                                         // Already patched\r
9216                                         if (n._mceOldSubmit)\r
9217                                                 return;\r
9218 \r
9219                                         // Check page uses id="submit" or name="submit" for it's submit button\r
9220                                         if (!n.submit.nodeType && !n.submit.length) {\r
9221                                                 t.formElement = n;\r
9222                                                 n._mceOldSubmit = n.submit;\r
9223                                                 n.submit = function() {\r
9224                                                         // Save all instances\r
9225                                                         tinymce.triggerSave();\r
9226                                                         t.isNotDirty = 1;\r
9227 \r
9228                                                         return t.formElement._mceOldSubmit(t.formElement);\r
9229                                                 };\r
9230                                         }\r
9231 \r
9232                                         n = null;\r
9233                                 });\r
9234                         }\r
9235 \r
9236                         // Load scripts\r
9237                         function loadScripts() {\r
9238                                 if (s.language)\r
9239                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
9240 \r
9241                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
9242                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
9243 \r
9244                                 each(explode(s.plugins), function(p) {\r
9245                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
9246                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
9247                                                 if (p == 'safari')\r
9248                                                         return;\r
9249 \r
9250                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
9251                                         }\r
9252                                 });\r
9253 \r
9254                                 // Init when que is loaded\r
9255                                 sl.loadQueue(function() {\r
9256                                         if (!t.removed)\r
9257                                                 t.init();\r
9258                                 });\r
9259                         };\r
9260 \r
9261                         loadScripts();\r
9262                 },\r
9263 \r
9264                 init : function() {\r
9265                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
9266 \r
9267                         tinymce.add(t);\r
9268 \r
9269                         if (s.theme) {\r
9270                                 s.theme = s.theme.replace(/-/, '');\r
9271                                 o = ThemeManager.get(s.theme);\r
9272                                 t.theme = new o();\r
9273 \r
9274                                 if (t.theme.init && s.init_theme)\r
9275                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
9276                         }\r
9277 \r
9278                         // Create all plugins\r
9279                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
9280                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
9281 \r
9282                                 if (c) {\r
9283                                         po = new c(t, u);\r
9284 \r
9285                                         t.plugins[p] = po;\r
9286 \r
9287                                         if (po.init)\r
9288                                                 po.init(t, u);\r
9289                                 }\r
9290                         });\r
9291 \r
9292                         // Setup popup CSS path(s)\r
9293                         if (s.popup_css !== false) {\r
9294                                 if (s.popup_css)\r
9295                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
9296                                 else\r
9297                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
9298                         }\r
9299 \r
9300                         if (s.popup_css_add)\r
9301                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
9302 \r
9303                         t.controlManager = new tinymce.ControlManager(t);\r
9304 \r
9305                         if (s.custom_undo_redo) {\r
9306                                 // Add initial undo level\r
9307                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
9308                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
9309                                                 if (!t.undoManager.hasUndo())\r
9310                                                         t.undoManager.add();\r
9311                                         }\r
9312                                 });\r
9313 \r
9314                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
9315                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
9316                                                 t.undoManager.add();\r
9317                                 });\r
9318                         }\r
9319 \r
9320                         t.onExecCommand.add(function(ed, c) {\r
9321                                 // Don't refresh the select lists until caret move\r
9322                                 if (!/^(FontName|FontSize)$/.test(c))\r
9323                                         t.nodeChanged();\r
9324                         });\r
9325 \r
9326                         // Remove ghost selections on images and tables in Gecko\r
9327                         if (isGecko) {\r
9328                                 function repaint(a, o) {\r
9329                                         if (!o || !o.initial)\r
9330                                                 t.execCommand('mceRepaint');\r
9331                                 };\r
9332 \r
9333                                 t.onUndo.add(repaint);\r
9334                                 t.onRedo.add(repaint);\r
9335                                 t.onSetContent.add(repaint);\r
9336                         }\r
9337 \r
9338                         // Enables users to override the control factory\r
9339                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
9340 \r
9341                         // Measure box\r
9342                         if (s.render_ui) {\r
9343                                 w = s.width || e.style.width || e.offsetWidth;\r
9344                                 h = s.height || e.style.height || e.offsetHeight;\r
9345                                 t.orgDisplay = e.style.display;\r
9346                                 re = /^[0-9\.]+(|px)$/i;\r
9347 \r
9348                                 if (re.test('' + w))\r
9349                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
9350 \r
9351                                 if (re.test('' + h))\r
9352                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
9353 \r
9354                                 // Render UI\r
9355                                 o = t.theme.renderUI({\r
9356                                         targetNode : e,\r
9357                                         width : w,\r
9358                                         height : h,\r
9359                                         deltaWidth : s.delta_width,\r
9360                                         deltaHeight : s.delta_height\r
9361                                 });\r
9362 \r
9363                                 t.editorContainer = o.editorContainer;\r
9364                         }\r
9365 \r
9366 \r
9367                         // User specified a document.domain value\r
9368                         if (document.domain && location.hostname != document.domain)\r
9369                                 tinymce.relaxedDomain = document.domain;\r
9370 \r
9371                         // Resize editor\r
9372                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
9373                                 width : w,\r
9374                                 height : h\r
9375                         });\r
9376 \r
9377                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
9378                         if (h < 100)\r
9379                                 h = 100;\r
9380 \r
9381                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
9382 \r
9383                         // We only need to override paths if we have to\r
9384                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
9385                         if (s.document_base_url != tinymce.documentBaseURL)\r
9386                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
9387 \r
9388                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
9389 \r
9390                         if (tinymce.relaxedDomain)\r
9391                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
9392 \r
9393                         bi = s.body_id || 'tinymce';\r
9394                         if (bi.indexOf('=') != -1) {\r
9395                                 bi = t.getParam('body_id', '', 'hash');\r
9396                                 bi = bi[t.id] || bi;\r
9397                         }\r
9398 \r
9399                         bc = s.body_class || '';\r
9400                         if (bc.indexOf('=') != -1) {\r
9401                                 bc = t.getParam('body_class', '', 'hash');\r
9402                                 bc = bc[t.id] || '';\r
9403                         }\r
9404 \r
9405                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
9406 \r
9407                         // Domain relaxing enabled, then set document domain\r
9408                         if (tinymce.relaxedDomain) {\r
9409                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
9410                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
9411                                         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
9412                                 else if (tinymce.isOpera)\r
9413                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
9414                         }\r
9415 \r
9416                         // Create iframe\r
9417                         n = DOM.add(o.iframeContainer, 'iframe', {\r
9418                                 id : t.id + "_ifr",\r
9419                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
9420                                 frameBorder : '0',\r
9421                                 style : {\r
9422                                         width : '100%',\r
9423                                         height : h\r
9424                                 }\r
9425                         });\r
9426 \r
9427                         t.contentAreaContainer = o.iframeContainer;\r
9428                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
9429                         DOM.get(t.id).style.display = 'none';\r
9430 \r
9431                         if (!isIE || !tinymce.relaxedDomain)\r
9432                                 t.setupIframe();\r
9433 \r
9434                         e = n = o = null; // Cleanup\r
9435                 },\r
9436 \r
9437                 setupIframe : function() {\r
9438                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
9439 \r
9440                         // Setup iframe body\r
9441                         if (!isIE || !tinymce.relaxedDomain) {\r
9442                                 d.open();\r
9443                                 d.write(t.iframeHTML);\r
9444                                 d.close();\r
9445                         }\r
9446 \r
9447                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
9448                         if (!isIE) {\r
9449                                 try {\r
9450                                         if (!s.readonly)\r
9451                                                 d.designMode = 'On';\r
9452                                 } catch (ex) {\r
9453                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
9454                                         // The design mode will be set ones the editor is focused\r
9455                                 }\r
9456                         }\r
9457 \r
9458                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
9459                         if (isIE) {\r
9460                                 // It will not steal focus if we hide it while setting contentEditable\r
9461                                 b = t.getBody();\r
9462                                 DOM.hide(b);\r
9463 \r
9464                                 if (!s.readonly)\r
9465                                         b.contentEditable = true;\r
9466 \r
9467                                 DOM.show(b);\r
9468                         }\r
9469 \r
9470                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
9471                                 keep_values : true,\r
9472                                 url_converter : t.convertURL,\r
9473                                 url_converter_scope : t,\r
9474                                 hex_colors : s.force_hex_style_colors,\r
9475                                 class_filter : s.class_filter,\r
9476                                 update_styles : 1,\r
9477                                 fix_ie_paragraphs : 1,\r
9478                                 valid_styles : s.valid_styles\r
9479                         });\r
9480 \r
9481                         t.schema = new tinymce.dom.Schema();\r
9482 \r
9483                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
9484                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
9485                                 dom : t.dom,\r
9486                                 schema : t.schema\r
9487                         }));\r
9488 \r
9489                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
9490 \r
9491                         t.formatter = new tinymce.Formatter(this);\r
9492 \r
9493                         // Register default formats\r
9494                         t.formatter.register({\r
9495                                 alignleft : [\r
9496                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
9497                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
9498                                 ],\r
9499 \r
9500                                 aligncenter : [\r
9501                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
9502                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
9503                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
9504                                 ],\r
9505 \r
9506                                 alignright : [\r
9507                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
9508                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
9509                                 ],\r
9510 \r
9511                                 alignfull : [\r
9512                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
9513                                 ],\r
9514 \r
9515                                 bold : [\r
9516                                         {inline : 'strong'},\r
9517                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
9518                                         {inline : 'b'}\r
9519                                 ],\r
9520 \r
9521                                 italic : [\r
9522                                         {inline : 'em'},\r
9523                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
9524                                         {inline : 'i'}\r
9525                                 ],\r
9526 \r
9527                                 underline : [\r
9528                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
9529                                         {inline : 'u'}\r
9530                                 ],\r
9531 \r
9532                                 strikethrough : [\r
9533                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
9534                                         {inline : 'u'}\r
9535                                 ],\r
9536 \r
9537                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
9538                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
9539                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
9540                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
9541                                 blockquote : {block : 'blockquote', wrapper : 1},\r
9542 \r
9543                                 removeformat : [\r
9544                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
9545                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
9546                                         {selector : '*', attributes : ['style', 'class'], expand : false, deep : true}\r
9547                                 ]\r
9548                         });\r
9549 \r
9550                         // Register default block formats\r
9551                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
9552                                 t.formatter.register(name, {block : name});\r
9553                         });\r
9554 \r
9555                         // Register user defined formats\r
9556                         t.formatter.register(t.settings.formats);\r
9557 \r
9558                         t.undoManager = new tinymce.UndoManager(t);\r
9559 \r
9560                         // Pass through\r
9561                         t.undoManager.onAdd.add(function(um, l) {\r
9562                                 if (!l.initial)\r
9563                                         return t.onChange.dispatch(t, l, um);\r
9564                         });\r
9565 \r
9566                         t.undoManager.onUndo.add(function(um, l) {\r
9567                                 return t.onUndo.dispatch(t, l, um);\r
9568                         });\r
9569 \r
9570                         t.undoManager.onRedo.add(function(um, l) {\r
9571                                 return t.onRedo.dispatch(t, l, um);\r
9572                         });\r
9573 \r
9574                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
9575                                 forced_root_block : s.forced_root_block\r
9576                         });\r
9577 \r
9578                         t.editorCommands = new tinymce.EditorCommands(t);\r
9579 \r
9580                         // Pass through\r
9581                         t.serializer.onPreProcess.add(function(se, o) {\r
9582                                 return t.onPreProcess.dispatch(t, o, se);\r
9583                         });\r
9584 \r
9585                         t.serializer.onPostProcess.add(function(se, o) {\r
9586                                 return t.onPostProcess.dispatch(t, o, se);\r
9587                         });\r
9588 \r
9589                         t.onPreInit.dispatch(t);\r
9590 \r
9591                         if (!s.gecko_spellcheck)\r
9592                                 t.getBody().spellcheck = 0;\r
9593 \r
9594                         if (!s.readonly)\r
9595                                 t._addEvents();\r
9596 \r
9597                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
9598                         t.onPostRender.dispatch(t);\r
9599 \r
9600                         if (s.directionality)\r
9601                                 t.getBody().dir = s.directionality;\r
9602 \r
9603                         if (s.nowrap)\r
9604                                 t.getBody().style.whiteSpace = "nowrap";\r
9605 \r
9606                         if (s.custom_elements) {\r
9607                                 function handleCustom(ed, o) {\r
9608                                         each(explode(s.custom_elements), function(v) {\r
9609                                                 var n;\r
9610 \r
9611                                                 if (v.indexOf('~') === 0) {\r
9612                                                         v = v.substring(1);\r
9613                                                         n = 'span';\r
9614                                                 } else\r
9615                                                         n = 'div';\r
9616 \r
9617                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
9618                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
9619                                         });\r
9620                                 };\r
9621 \r
9622                                 t.onBeforeSetContent.add(handleCustom);\r
9623                                 t.onPostProcess.add(function(ed, o) {\r
9624                                         if (o.set)\r
9625                                                 handleCustom(ed, o);\r
9626                                 });\r
9627                         }\r
9628 \r
9629                         if (s.handle_node_change_callback) {\r
9630                                 t.onNodeChange.add(function(ed, cm, n) {\r
9631                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
9632                                 });\r
9633                         }\r
9634 \r
9635                         if (s.save_callback) {\r
9636                                 t.onSaveContent.add(function(ed, o) {\r
9637                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9638 \r
9639                                         if (h)\r
9640                                                 o.content = h;\r
9641                                 });\r
9642                         }\r
9643 \r
9644                         if (s.onchange_callback) {\r
9645                                 t.onChange.add(function(ed, l) {\r
9646                                         t.execCallback('onchange_callback', t, l);\r
9647                                 });\r
9648                         }\r
9649 \r
9650                         if (s.convert_newlines_to_brs) {\r
9651                                 t.onBeforeSetContent.add(function(ed, o) {\r
9652                                         if (o.initial)\r
9653                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
9654                                 });\r
9655                         }\r
9656 \r
9657                         if (s.fix_nesting && isIE) {\r
9658                                 t.onBeforeSetContent.add(function(ed, o) {\r
9659                                         o.content = t._fixNesting(o.content);\r
9660                                 });\r
9661                         }\r
9662 \r
9663                         if (s.preformatted) {\r
9664                                 t.onPostProcess.add(function(ed, o) {\r
9665                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
9666                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
9667 \r
9668                                         if (o.set)\r
9669                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
9670                                 });\r
9671                         }\r
9672 \r
9673                         if (s.verify_css_classes) {\r
9674                                 t.serializer.attribValueFilter = function(n, v) {\r
9675                                         var s, cl;\r
9676 \r
9677                                         if (n == 'class') {\r
9678                                                 // Build regexp for classes\r
9679                                                 if (!t.classesRE) {\r
9680                                                         cl = t.dom.getClasses();\r
9681 \r
9682                                                         if (cl.length > 0) {\r
9683                                                                 s = '';\r
9684 \r
9685                                                                 each (cl, function(o) {\r
9686                                                                         s += (s ? '|' : '') + o['class'];\r
9687                                                                 });\r
9688 \r
9689                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
9690                                                         }\r
9691                                                 }\r
9692 \r
9693                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
9694                                         }\r
9695 \r
9696                                         return v;\r
9697                                 };\r
9698                         }\r
9699 \r
9700                         if (s.cleanup_callback) {\r
9701                                 t.onBeforeSetContent.add(function(ed, o) {\r
9702                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9703                                 });\r
9704 \r
9705                                 t.onPreProcess.add(function(ed, o) {\r
9706                                         if (o.set)\r
9707                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
9708 \r
9709                                         if (o.get)\r
9710                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
9711                                 });\r
9712 \r
9713                                 t.onPostProcess.add(function(ed, o) {\r
9714                                         if (o.set)\r
9715                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9716 \r
9717                                         if (o.get)                                              \r
9718                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
9719                                 });\r
9720                         }\r
9721 \r
9722                         if (s.save_callback) {\r
9723                                 t.onGetContent.add(function(ed, o) {\r
9724                                         if (o.save)\r
9725                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9726                                 });\r
9727                         }\r
9728 \r
9729                         if (s.handle_event_callback) {\r
9730                                 t.onEvent.add(function(ed, e, o) {\r
9731                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9732                                                 Event.cancel(e);\r
9733                                 });\r
9734                         }\r
9735 \r
9736                         // Add visual aids when new contents is added\r
9737                         t.onSetContent.add(function() {\r
9738                                 t.addVisual(t.getBody());\r
9739                         });\r
9740 \r
9741                         // Remove empty contents\r
9742                         if (s.padd_empty_editor) {\r
9743                                 t.onPostProcess.add(function(ed, o) {\r
9744                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9745                                 });\r
9746                         }\r
9747 \r
9748                         if (isGecko) {\r
9749                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9750                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9751                                 function fixLinks(ed, o) {\r
9752                                         each(ed.dom.select('a'), function(n) {\r
9753                                                 var pn = n.parentNode;\r
9754 \r
9755                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9756                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9757                                         });\r
9758                                 };\r
9759 \r
9760                                 t.onExecCommand.add(function(ed, cmd) {\r
9761                                         if (cmd === 'CreateLink')\r
9762                                                 fixLinks(ed);\r
9763                                 });\r
9764 \r
9765                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9766 \r
9767                                 if (!s.readonly) {\r
9768                                         try {\r
9769                                                 // Design mode must be set here once again to fix a bug where\r
9770                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9771                                                 d.designMode = 'Off';\r
9772                                                 d.designMode = 'On';\r
9773                                         } catch (ex) {\r
9774                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9775                                                 // The design mode will be set ones the editor is focused\r
9776                                         }\r
9777                                 }\r
9778                         }\r
9779 \r
9780                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9781                         setTimeout(function () {\r
9782                                 if (t.removed)\r
9783                                         return;\r
9784 \r
9785                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9786                                 t.startContent = t.getContent({format : 'raw'});\r
9787                                 t.initialized = true;\r
9788 \r
9789                                 t.onInit.dispatch(t);\r
9790                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9791                                 t.execCallback('init_instance_callback', t);\r
9792                                 t.focus(true);\r
9793                                 t.nodeChanged({initial : 1});\r
9794 \r
9795                                 // Load specified content CSS last\r
9796                                 if (s.content_css) {\r
9797                                         tinymce.each(explode(s.content_css), function(u) {\r
9798                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9799                                         });\r
9800                                 }\r
9801 \r
9802                                 // Handle auto focus\r
9803                                 if (s.auto_focus) {\r
9804                                         setTimeout(function () {\r
9805                                                 var ed = tinymce.get(s.auto_focus);\r
9806 \r
9807                                                 ed.selection.select(ed.getBody(), 1);\r
9808                                                 ed.selection.collapse(1);\r
9809                                                 ed.getWin().focus();\r
9810                                         }, 100);\r
9811                                 }\r
9812                         }, 1);\r
9813         \r
9814                         e = null;\r
9815                 },\r
9816 \r
9817 \r
9818                 focus : function(sf) {\r
9819                         var oed, t = this, ce = t.settings.content_editable;\r
9820 \r
9821                         if (!sf) {\r
9822                                 // Is not content editable or the selection is outside the area in IE\r
9823                                 // the IE statement is needed to avoid bluring if element selections inside layers since\r
9824                                 // the layer is like it's own document in IE\r
9825                                 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))\r
9826                                         t.getWin().focus();\r
9827 \r
9828                         }\r
9829 \r
9830                         if (tinymce.activeEditor != t) {\r
9831                                 if ((oed = tinymce.activeEditor) != null)\r
9832                                         oed.onDeactivate.dispatch(oed, t);\r
9833 \r
9834                                 t.onActivate.dispatch(t, oed);\r
9835                         }\r
9836 \r
9837                         tinymce._setActive(t);\r
9838                 },\r
9839 \r
9840                 execCallback : function(n) {\r
9841                         var t = this, f = t.settings[n], s;\r
9842 \r
9843                         if (!f)\r
9844                                 return;\r
9845 \r
9846                         // Look through lookup\r
9847                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
9848                                 f = s.func;\r
9849                                 s = s.scope;\r
9850                         }\r
9851 \r
9852                         if (is(f, 'string')) {\r
9853                                 s = f.replace(/\.\w+$/, '');\r
9854                                 s = s ? tinymce.resolve(s) : 0;\r
9855                                 f = tinymce.resolve(f);\r
9856                                 t.callbackLookup = t.callbackLookup || {};\r
9857                                 t.callbackLookup[n] = {func : f, scope : s};\r
9858                         }\r
9859 \r
9860                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
9861                 },\r
9862 \r
9863                 translate : function(s) {\r
9864                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
9865 \r
9866                         if (!s)\r
9867                                 return '';\r
9868 \r
9869                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
9870                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
9871                         });\r
9872                 },\r
9873 \r
9874                 getLang : function(n, dv) {\r
9875                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
9876                 },\r
9877 \r
9878                 getParam : function(n, dv, ty) {\r
9879                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
9880 \r
9881                         if (ty === 'hash') {\r
9882                                 o = {};\r
9883 \r
9884                                 if (is(v, 'string')) {\r
9885                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
9886                                                 v = v.split('=');\r
9887 \r
9888                                                 if (v.length > 1)\r
9889                                                         o[tr(v[0])] = tr(v[1]);\r
9890                                                 else\r
9891                                                         o[tr(v[0])] = tr(v);\r
9892                                         });\r
9893                                 } else\r
9894                                         o = v;\r
9895 \r
9896                                 return o;\r
9897                         }\r
9898 \r
9899                         return v;\r
9900                 },\r
9901 \r
9902                 nodeChanged : function(o) {\r
9903                         var t = this, s = t.selection, n = s.getNode() || t.getBody();\r
9904 \r
9905                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
9906                         if (t.initialized) {\r
9907                                 o = o || {};\r
9908                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
9909 \r
9910                                 // Get parents and add them to object\r
9911                                 o.parents = [];\r
9912                                 t.dom.getParent(n, function(node) {\r
9913                                         if (node.nodeName == 'BODY')\r
9914                                                 return true;\r
9915 \r
9916                                         o.parents.push(node);\r
9917                                 });\r
9918 \r
9919                                 t.onNodeChange.dispatch(\r
9920                                         t,\r
9921                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
9922                                         n,\r
9923                                         s.isCollapsed(),\r
9924                                         o\r
9925                                 );\r
9926                         }\r
9927                 },\r
9928 \r
9929                 addButton : function(n, s) {\r
9930                         var t = this;\r
9931 \r
9932                         t.buttons = t.buttons || {};\r
9933                         t.buttons[n] = s;\r
9934                 },\r
9935 \r
9936                 addCommand : function(n, f, s) {\r
9937                         this.execCommands[n] = {func : f, scope : s || this};\r
9938                 },\r
9939 \r
9940                 addQueryStateHandler : function(n, f, s) {\r
9941                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
9942                 },\r
9943 \r
9944                 addQueryValueHandler : function(n, f, s) {\r
9945                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
9946                 },\r
9947 \r
9948                 addShortcut : function(pa, desc, cmd_func, sc) {\r
9949                         var t = this, c;\r
9950 \r
9951                         if (!t.settings.custom_shortcuts)\r
9952                                 return false;\r
9953 \r
9954                         t.shortcuts = t.shortcuts || {};\r
9955 \r
9956                         if (is(cmd_func, 'string')) {\r
9957                                 c = cmd_func;\r
9958 \r
9959                                 cmd_func = function() {\r
9960                                         t.execCommand(c, false, null);\r
9961                                 };\r
9962                         }\r
9963 \r
9964                         if (is(cmd_func, 'object')) {\r
9965                                 c = cmd_func;\r
9966 \r
9967                                 cmd_func = function() {\r
9968                                         t.execCommand(c[0], c[1], c[2]);\r
9969                                 };\r
9970                         }\r
9971 \r
9972                         each(explode(pa), function(pa) {\r
9973                                 var o = {\r
9974                                         func : cmd_func,\r
9975                                         scope : sc || this,\r
9976                                         desc : desc,\r
9977                                         alt : false,\r
9978                                         ctrl : false,\r
9979                                         shift : false\r
9980                                 };\r
9981 \r
9982                                 each(explode(pa, '+'), function(v) {\r
9983                                         switch (v) {\r
9984                                                 case 'alt':\r
9985                                                 case 'ctrl':\r
9986                                                 case 'shift':\r
9987                                                         o[v] = true;\r
9988                                                         break;\r
9989 \r
9990                                                 default:\r
9991                                                         o.charCode = v.charCodeAt(0);\r
9992                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
9993                                         }\r
9994                                 });\r
9995 \r
9996                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
9997                         });\r
9998 \r
9999                         return true;\r
10000                 },\r
10001 \r
10002                 execCommand : function(cmd, ui, val, a) {\r
10003                         var t = this, s = 0, o, st;\r
10004 \r
10005                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
10006                                 t.focus();\r
10007 \r
10008                         o = {};\r
10009                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
10010                         if (o.terminate)\r
10011                                 return false;\r
10012 \r
10013                         // Command callback\r
10014                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
10015                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10016                                 return true;\r
10017                         }\r
10018 \r
10019                         // Registred commands\r
10020                         if (o = t.execCommands[cmd]) {\r
10021                                 st = o.func.call(o.scope, ui, val);\r
10022 \r
10023                                 // Fall through on true\r
10024                                 if (st !== true) {\r
10025                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10026                                         return st;\r
10027                                 }\r
10028                         }\r
10029 \r
10030                         // Plugin commands\r
10031                         each(t.plugins, function(p) {\r
10032                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
10033                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10034                                         s = 1;\r
10035                                         return false;\r
10036                                 }\r
10037                         });\r
10038 \r
10039                         if (s)\r
10040                                 return true;\r
10041 \r
10042                         // Theme commands\r
10043                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
10044                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10045                                 return true;\r
10046                         }\r
10047 \r
10048                         // Execute global commands\r
10049                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
10050                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10051                                 return true;\r
10052                         }\r
10053 \r
10054                         // Editor commands\r
10055                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
10056                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10057                                 return true;\r
10058                         }\r
10059 \r
10060                         // Browser commands\r
10061                         t.getDoc().execCommand(cmd, ui, val);\r
10062                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10063                 },\r
10064 \r
10065                 queryCommandState : function(cmd) {\r
10066                         var t = this, o, s;\r
10067 \r
10068                         // Is hidden then return undefined\r
10069                         if (t._isHidden())\r
10070                                 return;\r
10071 \r
10072                         // Registred commands\r
10073                         if (o = t.queryStateCommands[cmd]) {\r
10074                                 s = o.func.call(o.scope);\r
10075 \r
10076                                 // Fall though on true\r
10077                                 if (s !== true)\r
10078                                         return s;\r
10079                         }\r
10080 \r
10081                         // Registred commands\r
10082                         o = t.editorCommands.queryCommandState(cmd);\r
10083                         if (o !== -1)\r
10084                                 return o;\r
10085 \r
10086                         // Browser commands\r
10087                         try {\r
10088                                 return this.getDoc().queryCommandState(cmd);\r
10089                         } catch (ex) {\r
10090                                 // Fails sometimes see bug: 1896577\r
10091                         }\r
10092                 },\r
10093 \r
10094                 queryCommandValue : function(c) {\r
10095                         var t = this, o, s;\r
10096 \r
10097                         // Is hidden then return undefined\r
10098                         if (t._isHidden())\r
10099                                 return;\r
10100 \r
10101                         // Registred commands\r
10102                         if (o = t.queryValueCommands[c]) {\r
10103                                 s = o.func.call(o.scope);\r
10104 \r
10105                                 // Fall though on true\r
10106                                 if (s !== true)\r
10107                                         return s;\r
10108                         }\r
10109 \r
10110                         // Registred commands\r
10111                         o = t.editorCommands.queryCommandValue(c);\r
10112                         if (is(o))\r
10113                                 return o;\r
10114 \r
10115                         // Browser commands\r
10116                         try {\r
10117                                 return this.getDoc().queryCommandValue(c);\r
10118                         } catch (ex) {\r
10119                                 // Fails sometimes see bug: 1896577\r
10120                         }\r
10121                 },\r
10122 \r
10123                 show : function() {\r
10124                         var t = this;\r
10125 \r
10126                         DOM.show(t.getContainer());\r
10127                         DOM.hide(t.id);\r
10128                         t.load();\r
10129                 },\r
10130 \r
10131                 hide : function() {\r
10132                         var t = this, d = t.getDoc();\r
10133 \r
10134                         // Fixed bug where IE has a blinking cursor left from the editor\r
10135                         if (isIE && d)\r
10136                                 d.execCommand('SelectAll');\r
10137 \r
10138                         // We must save before we hide so Safari doesn't crash\r
10139                         t.save();\r
10140                         DOM.hide(t.getContainer());\r
10141                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
10142                 },\r
10143 \r
10144                 isHidden : function() {\r
10145                         return !DOM.isHidden(this.id);\r
10146                 },\r
10147 \r
10148                 setProgressState : function(b, ti, o) {\r
10149                         this.onSetProgressState.dispatch(this, b, ti, o);\r
10150 \r
10151                         return b;\r
10152                 },\r
10153 \r
10154                 load : function(o) {\r
10155                         var t = this, e = t.getElement(), h;\r
10156 \r
10157                         if (e) {\r
10158                                 o = o || {};\r
10159                                 o.load = true;\r
10160 \r
10161                                 // Double encode existing entities in the value\r
10162                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
10163                                 o.element = e;\r
10164 \r
10165                                 if (!o.no_events)\r
10166                                         t.onLoadContent.dispatch(t, o);\r
10167 \r
10168                                 o.element = e = null;\r
10169 \r
10170                                 return h;\r
10171                         }\r
10172                 },\r
10173 \r
10174                 save : function(o) {\r
10175                         var t = this, e = t.getElement(), h, f;\r
10176 \r
10177                         if (!e || !t.initialized)\r
10178                                 return;\r
10179 \r
10180                         o = o || {};\r
10181                         o.save = true;\r
10182 \r
10183                         // Add undo level will trigger onchange event\r
10184                         if (!o.no_events) {\r
10185                                 t.undoManager.typing = 0;\r
10186                                 t.undoManager.add();\r
10187                         }\r
10188 \r
10189                         o.element = e;\r
10190                         h = o.content = t.getContent(o);\r
10191 \r
10192                         if (!o.no_events)\r
10193                                 t.onSaveContent.dispatch(t, o);\r
10194 \r
10195                         h = o.content;\r
10196 \r
10197                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
10198                                 e.innerHTML = h;\r
10199 \r
10200                                 // Update hidden form element\r
10201                                 if (f = DOM.getParent(t.id, 'form')) {\r
10202                                         each(f.elements, function(e) {\r
10203                                                 if (e.name == t.id) {\r
10204                                                         e.value = h;\r
10205                                                         return false;\r
10206                                                 }\r
10207                                         });\r
10208                                 }\r
10209                         } else\r
10210                                 e.value = h;\r
10211 \r
10212                         o.element = e = null;\r
10213 \r
10214                         return h;\r
10215                 },\r
10216 \r
10217                 setContent : function(h, o) {\r
10218                         var t = this;\r
10219 \r
10220                         o = o || {};\r
10221                         o.format = o.format || 'html';\r
10222                         o.set = true;\r
10223                         o.content = h;\r
10224 \r
10225                         if (!o.no_events)\r
10226                                 t.onBeforeSetContent.dispatch(t, o);\r
10227 \r
10228                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
10229                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
10230                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
10231                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
10232                                 o.format = 'raw';\r
10233                         }\r
10234 \r
10235                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
10236 \r
10237                         if (o.format != 'raw' && t.settings.cleanup) {\r
10238                                 o.getInner = true;\r
10239                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
10240                         }\r
10241 \r
10242                         if (!o.no_events)\r
10243                                 t.onSetContent.dispatch(t, o);\r
10244 \r
10245                         return o.content;\r
10246                 },\r
10247 \r
10248                 getContent : function(o) {\r
10249                         var t = this, h;\r
10250 \r
10251                         o = o || {};\r
10252                         o.format = o.format || 'html';\r
10253                         o.get = true;\r
10254 \r
10255                         if (!o.no_events)\r
10256                                 t.onBeforeGetContent.dispatch(t, o);\r
10257 \r
10258                         if (o.format != 'raw' && t.settings.cleanup) {\r
10259                                 o.getInner = true;\r
10260                                 h = t.serializer.serialize(t.getBody(), o);\r
10261                         } else\r
10262                                 h = t.getBody().innerHTML;\r
10263 \r
10264                         h = h.replace(/^\s*|\s*$/g, '');\r
10265                         o.content = h;\r
10266 \r
10267                         if (!o.no_events)\r
10268                                 t.onGetContent.dispatch(t, o);\r
10269 \r
10270                         return o.content;\r
10271                 },\r
10272 \r
10273                 isDirty : function() {\r
10274                         var t = this;\r
10275 \r
10276                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
10277                 },\r
10278 \r
10279                 getContainer : function() {\r
10280                         var t = this;\r
10281 \r
10282                         if (!t.container)\r
10283                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
10284 \r
10285                         return t.container;\r
10286                 },\r
10287 \r
10288                 getContentAreaContainer : function() {\r
10289                         return this.contentAreaContainer;\r
10290                 },\r
10291 \r
10292                 getElement : function() {\r
10293                         return DOM.get(this.settings.content_element || this.id);\r
10294                 },\r
10295 \r
10296                 getWin : function() {\r
10297                         var t = this, e;\r
10298 \r
10299                         if (!t.contentWindow) {\r
10300                                 e = DOM.get(t.id + "_ifr");\r
10301 \r
10302                                 if (e)\r
10303                                         t.contentWindow = e.contentWindow;\r
10304                         }\r
10305 \r
10306                         return t.contentWindow;\r
10307                 },\r
10308 \r
10309                 getDoc : function() {\r
10310                         var t = this, w;\r
10311 \r
10312                         if (!t.contentDocument) {\r
10313                                 w = t.getWin();\r
10314 \r
10315                                 if (w)\r
10316                                         t.contentDocument = w.document;\r
10317                         }\r
10318 \r
10319                         return t.contentDocument;\r
10320                 },\r
10321 \r
10322                 getBody : function() {\r
10323                         return this.bodyElement || this.getDoc().body;\r
10324                 },\r
10325 \r
10326                 convertURL : function(u, n, e) {\r
10327                         var t = this, s = t.settings;\r
10328 \r
10329                         // Use callback instead\r
10330                         if (s.urlconverter_callback)\r
10331                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
10332 \r
10333                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
10334                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
10335                                 return u;\r
10336 \r
10337                         // Convert to relative\r
10338                         if (s.relative_urls)\r
10339                                 return t.documentBaseURI.toRelative(u);\r
10340 \r
10341                         // Convert to absolute\r
10342                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
10343 \r
10344                         return u;\r
10345                 },\r
10346 \r
10347                 addVisual : function(e) {\r
10348                         var t = this, s = t.settings;\r
10349 \r
10350                         e = e || t.getBody();\r
10351 \r
10352                         if (!is(t.hasVisual))\r
10353                                 t.hasVisual = s.visual;\r
10354 \r
10355                         each(t.dom.select('table,a', e), function(e) {\r
10356                                 var v;\r
10357 \r
10358                                 switch (e.nodeName) {\r
10359                                         case 'TABLE':\r
10360                                                 v = t.dom.getAttrib(e, 'border');\r
10361 \r
10362                                                 if (!v || v == '0') {\r
10363                                                         if (t.hasVisual)\r
10364                                                                 t.dom.addClass(e, s.visual_table_class);\r
10365                                                         else\r
10366                                                                 t.dom.removeClass(e, s.visual_table_class);\r
10367                                                 }\r
10368 \r
10369                                                 return;\r
10370 \r
10371                                         case 'A':\r
10372                                                 v = t.dom.getAttrib(e, 'name');\r
10373 \r
10374                                                 if (v) {\r
10375                                                         if (t.hasVisual)\r
10376                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
10377                                                         else\r
10378                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
10379                                                 }\r
10380 \r
10381                                                 return;\r
10382                                 }\r
10383                         });\r
10384 \r
10385                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
10386                 },\r
10387 \r
10388                 remove : function() {\r
10389                         var t = this, e = t.getContainer();\r
10390 \r
10391                         t.removed = 1; // Cancels post remove event execution\r
10392                         t.hide();\r
10393 \r
10394                         t.execCallback('remove_instance_callback', t);\r
10395                         t.onRemove.dispatch(t);\r
10396 \r
10397                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
10398                         t.onExecCommand.listeners = [];\r
10399 \r
10400                         tinymce.remove(t);\r
10401                         DOM.remove(e);\r
10402                 },\r
10403 \r
10404                 destroy : function(s) {\r
10405                         var t = this;\r
10406 \r
10407                         // One time is enough\r
10408                         if (t.destroyed)\r
10409                                 return;\r
10410 \r
10411                         if (!s) {\r
10412                                 tinymce.removeUnload(t.destroy);\r
10413                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
10414 \r
10415                                 // Manual destroy\r
10416                                 if (t.theme && t.theme.destroy)\r
10417                                         t.theme.destroy();\r
10418 \r
10419                                 // Destroy controls, selection and dom\r
10420                                 t.controlManager.destroy();\r
10421                                 t.selection.destroy();\r
10422                                 t.dom.destroy();\r
10423 \r
10424                                 // Remove all events\r
10425 \r
10426                                 // Don't clear the window or document if content editable\r
10427                                 // is enabled since other instances might still be present\r
10428                                 if (!t.settings.content_editable) {\r
10429                                         Event.clear(t.getWin());\r
10430                                         Event.clear(t.getDoc());\r
10431                                 }\r
10432 \r
10433                                 Event.clear(t.getBody());\r
10434                                 Event.clear(t.formElement);\r
10435                         }\r
10436 \r
10437                         if (t.formElement) {\r
10438                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
10439                                 t.formElement._mceOldSubmit = null;\r
10440                         }\r
10441 \r
10442                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
10443 \r
10444                         if (t.selection)\r
10445                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
10446 \r
10447                         t.destroyed = 1;\r
10448                 },\r
10449 \r
10450                 // Internal functions\r
10451 \r
10452                 _addEvents : function() {\r
10453                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
10454                         var t = this, i, s = t.settings, lo = {\r
10455                                 mouseup : 'onMouseUp',\r
10456                                 mousedown : 'onMouseDown',\r
10457                                 click : 'onClick',\r
10458                                 keyup : 'onKeyUp',\r
10459                                 keydown : 'onKeyDown',\r
10460                                 keypress : 'onKeyPress',\r
10461                                 submit : 'onSubmit',\r
10462                                 reset : 'onReset',\r
10463                                 contextmenu : 'onContextMenu',\r
10464                                 dblclick : 'onDblClick',\r
10465                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
10466                         };\r
10467 \r
10468                         function eventHandler(e, o) {\r
10469                                 var ty = e.type;\r
10470 \r
10471                                 // Don't fire events when it's removed\r
10472                                 if (t.removed)\r
10473                                         return;\r
10474 \r
10475                                 // Generic event handler\r
10476                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
10477                                         // Specific event handler\r
10478                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
10479                                 }\r
10480                         };\r
10481 \r
10482                         // Add DOM events\r
10483                         each(lo, function(v, k) {\r
10484                                 switch (k) {\r
10485                                         case 'contextmenu':\r
10486                                                 if (tinymce.isOpera) {\r
10487                                                         // Fake contextmenu on Opera\r
10488                                                         t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
10489                                                                 if (e.ctrlKey) {\r
10490                                                                         e.fakeType = 'contextmenu';\r
10491                                                                         eventHandler(e);\r
10492                                                                 }\r
10493                                                         });\r
10494                                                 } else\r
10495                                                         t.dom.bind(t.getBody(), k, eventHandler);\r
10496                                                 break;\r
10497 \r
10498                                         case 'paste':\r
10499                                                 t.dom.bind(t.getBody(), k, function(e) {\r
10500                                                         eventHandler(e);\r
10501                                                 });\r
10502                                                 break;\r
10503 \r
10504                                         case 'submit':\r
10505                                         case 'reset':\r
10506                                                 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
10507                                                 break;\r
10508 \r
10509                                         default:\r
10510                                                 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
10511                                 }\r
10512                         });\r
10513 \r
10514                         t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
10515                                 t.focus(true);\r
10516                         });\r
10517 \r
10518 \r
10519                         // Fixes bug where a specified document_base_uri could result in broken images\r
10520                         // This will also fix drag drop of images in Gecko\r
10521                         if (tinymce.isGecko) {\r
10522                                 // Convert all images to absolute URLs\r
10523 /*                              t.onSetContent.add(function(ed, o) {\r
10524                                         each(ed.dom.select('img'), function(e) {\r
10525                                                 var v;\r
10526 \r
10527                                                 if (v = e.getAttribute('_mce_src'))\r
10528                                                         e.src = t.documentBaseURI.toAbsolute(v);\r
10529                                         })\r
10530                                 });*/\r
10531 \r
10532                                 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
10533                                         var v;\r
10534 \r
10535                                         e = e.target;\r
10536 \r
10537                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
10538                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
10539                                 });\r
10540                         }\r
10541 \r
10542                         // Set various midas options in Gecko\r
10543                         if (isGecko) {\r
10544                                 function setOpts() {\r
10545                                         var t = this, d = t.getDoc(), s = t.settings;\r
10546 \r
10547                                         if (isGecko && !s.readonly) {\r
10548                                                 if (t._isHidden()) {\r
10549                                                         try {\r
10550                                                                 if (!s.content_editable)\r
10551                                                                         d.designMode = 'On';\r
10552                                                         } catch (ex) {\r
10553                                                                 // Fails if it's hidden\r
10554                                                         }\r
10555                                                 }\r
10556 \r
10557                                                 try {\r
10558                                                         // Try new Gecko method\r
10559                                                         d.execCommand("styleWithCSS", 0, false);\r
10560                                                 } catch (ex) {\r
10561                                                         // Use old method\r
10562                                                         if (!t._isHidden())\r
10563                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
10564                                                 }\r
10565 \r
10566                                                 if (!s.table_inline_editing)\r
10567                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
10568 \r
10569                                                 if (!s.object_resizing)\r
10570                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
10571                                         }\r
10572                                 };\r
10573 \r
10574                                 t.onBeforeExecCommand.add(setOpts);\r
10575                                 t.onMouseDown.add(setOpts);\r
10576                         }\r
10577 \r
10578                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
10579                         // WebKit can't even do simple things like selecting an image\r
10580                         // This also fixes so it's possible to select mceItemAnchors\r
10581                         if (tinymce.isWebKit) {\r
10582                                 t.onClick.add(function(ed, e) {\r
10583                                         e = e.target;\r
10584 \r
10585                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
10586                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor')))\r
10587                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
10588                                 });\r
10589                         }\r
10590 \r
10591                         // Add node change handlers\r
10592                         t.onMouseUp.add(t.nodeChanged);\r
10593                         t.onClick.add(t.nodeChanged);\r
10594                         t.onKeyUp.add(function(ed, e) {\r
10595                                 var c = e.keyCode;\r
10596 \r
10597                                 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
10598                                         t.nodeChanged();\r
10599                         });\r
10600 \r
10601                         // Add reset handler\r
10602                         t.onReset.add(function() {\r
10603                                 t.setContent(t.startContent, {format : 'raw'});\r
10604                         });\r
10605 \r
10606                         // Add shortcuts\r
10607                         if (s.custom_shortcuts) {\r
10608                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
10609                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
10610                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
10611                                 }\r
10612 \r
10613                                 // Add default shortcuts for gecko\r
10614                                 if (isGecko) {\r
10615                                         t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
10616                                         t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
10617                                         t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
10618                                 }\r
10619 \r
10620                                 // BlockFormat shortcuts keys\r
10621                                 for (i=1; i<=6; i++)\r
10622                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
10623 \r
10624                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
10625                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
10626                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
10627 \r
10628                                 function find(e) {\r
10629                                         var v = null;\r
10630 \r
10631                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
10632                                                 return v;\r
10633 \r
10634                                         each(t.shortcuts, function(o) {\r
10635                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
10636                                                         return;\r
10637                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
10638                                                         return;\r
10639 \r
10640                                                 if (o.alt != e.altKey)\r
10641                                                         return;\r
10642 \r
10643                                                 if (o.shift != e.shiftKey)\r
10644                                                         return;\r
10645 \r
10646                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
10647                                                         v = o;\r
10648                                                         return false;\r
10649                                                 }\r
10650                                         });\r
10651 \r
10652                                         return v;\r
10653                                 };\r
10654 \r
10655                                 t.onKeyUp.add(function(ed, e) {\r
10656                                         var o = find(e);\r
10657 \r
10658                                         if (o)\r
10659                                                 return Event.cancel(e);\r
10660                                 });\r
10661 \r
10662                                 t.onKeyPress.add(function(ed, e) {\r
10663                                         var o = find(e);\r
10664 \r
10665                                         if (o)\r
10666                                                 return Event.cancel(e);\r
10667                                 });\r
10668 \r
10669                                 t.onKeyDown.add(function(ed, e) {\r
10670                                         var o = find(e);\r
10671 \r
10672                                         if (o) {\r
10673                                                 o.func.call(o.scope);\r
10674                                                 return Event.cancel(e);\r
10675                                         }\r
10676                                 });\r
10677                         }\r
10678 \r
10679                         if (tinymce.isIE) {\r
10680                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
10681                                 // It will also block mceItemNoResize items\r
10682                                 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
10683                                         var re = t.resizeInfo, cb;\r
10684 \r
10685                                         e = e.target;\r
10686 \r
10687                                         // Don't do this action for non image elements\r
10688                                         if (e.nodeName !== 'IMG')\r
10689                                                 return;\r
10690 \r
10691                                         if (re)\r
10692                                                 t.dom.unbind(re.node, re.ev, re.cb);\r
10693 \r
10694                                         if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
10695                                                 ev = 'resizeend';\r
10696                                                 cb = t.dom.bind(e, ev, function(e) {\r
10697                                                         var v;\r
10698 \r
10699                                                         e = e.target;\r
10700 \r
10701                                                         if (v = t.dom.getStyle(e, 'width')) {\r
10702                                                                 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
10703                                                                 t.dom.setStyle(e, 'width', '');\r
10704                                                         }\r
10705 \r
10706                                                         if (v = t.dom.getStyle(e, 'height')) {\r
10707                                                                 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
10708                                                                 t.dom.setStyle(e, 'height', '');\r
10709                                                         }\r
10710                                                 });\r
10711                                         } else {\r
10712                                                 ev = 'resizestart';\r
10713                                                 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
10714                                         }\r
10715 \r
10716                                         re = t.resizeInfo = {\r
10717                                                 node : e,\r
10718                                                 ev : ev,\r
10719                                                 cb : cb\r
10720                                         };\r
10721                                 });\r
10722 \r
10723                                 t.onKeyDown.add(function(ed, e) {\r
10724                                         switch (e.keyCode) {\r
10725                                                 case 8:\r
10726                                                         // Fix IE control + backspace browser bug\r
10727                                                         if (t.selection.getRng().item) {\r
10728                                                                 ed.dom.remove(t.selection.getRng().item(0));\r
10729                                                                 return Event.cancel(e);\r
10730                                                         }\r
10731                                         }\r
10732                                 });\r
10733 \r
10734                                 /*if (t.dom.boxModel) {\r
10735                                         t.getBody().style.height = '100%';\r
10736 \r
10737                                         Event.add(t.getWin(), 'resize', function(e) {\r
10738                                                 var docElm = t.getDoc().documentElement;\r
10739 \r
10740                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10741                                         });\r
10742                                 }*/\r
10743                         }\r
10744 \r
10745                         if (tinymce.isOpera) {\r
10746                                 t.onClick.add(function(ed, e) {\r
10747                                         Event.prevent(e);\r
10748                                 });\r
10749                         }\r
10750 \r
10751                         // Add custom undo/redo handlers\r
10752                         if (s.custom_undo_redo) {\r
10753                                 function addUndo() {\r
10754                                         t.undoManager.typing = 0;\r
10755                                         t.undoManager.add();\r
10756                                 };\r
10757 \r
10758                                 t.dom.bind(t.getDoc(), 'focusout', function(e) {\r
10759                                         if (!t.removed && t.undoManager.typing)\r
10760                                                 addUndo();\r
10761                                 });\r
10762 \r
10763                                 t.onKeyUp.add(function(ed, e) {\r
10764                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10765                                                 addUndo();\r
10766                                 });\r
10767 \r
10768                                 t.onKeyDown.add(function(ed, e) {\r
10769                                         // Is caracter positon keys\r
10770                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10771                                                 if (t.undoManager.typing)\r
10772                                                         addUndo();\r
10773 \r
10774                                                 return;\r
10775                                         }\r
10776 \r
10777                                         if (!t.undoManager.typing) {\r
10778                                                 t.undoManager.add();\r
10779                                                 t.undoManager.typing = 1;\r
10780                                         }\r
10781                                 });\r
10782 \r
10783                                 t.onMouseDown.add(function() {\r
10784                                         if (t.undoManager.typing)\r
10785                                                 addUndo();\r
10786                                 });\r
10787                         }\r
10788                 },\r
10789 \r
10790                 _isHidden : function() {\r
10791                         var s;\r
10792 \r
10793                         if (!isGecko)\r
10794                                 return 0;\r
10795 \r
10796                         // Weird, wheres that cursor selection?\r
10797                         s = this.selection.getSel();\r
10798                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
10799                 },\r
10800 \r
10801                 // Fix for bug #1867292\r
10802                 _fixNesting : function(s) {\r
10803                         var d = [], i;\r
10804 \r
10805                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
10806                                 var e;\r
10807 \r
10808                                 // Handle end element\r
10809                                 if (b === '/') {\r
10810                                         if (!d.length)\r
10811                                                 return '';\r
10812 \r
10813                                         if (c !== d[d.length - 1].tag) {\r
10814                                                 for (i=d.length - 1; i>=0; i--) {\r
10815                                                         if (d[i].tag === c) {\r
10816                                                                 d[i].close = 1;\r
10817                                                                 break;\r
10818                                                         }\r
10819                                                 }\r
10820 \r
10821                                                 return '';\r
10822                                         } else {\r
10823                                                 d.pop();\r
10824 \r
10825                                                 if (d.length && d[d.length - 1].close) {\r
10826                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
10827                                                         d.pop();\r
10828                                                 }\r
10829                                         }\r
10830                                 } else {\r
10831                                         // Ignore these\r
10832                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
10833                                                 return a;\r
10834 \r
10835                                         // Ignore closed ones\r
10836                                         if (/\/>$/.test(a))\r
10837                                                 return a;\r
10838 \r
10839                                         d.push({tag : c}); // Push start element\r
10840                                 }\r
10841 \r
10842                                 return a;\r
10843                         });\r
10844 \r
10845                         // End all open tags\r
10846                         for (i=d.length - 1; i>=0; i--)\r
10847                                 s += '</' + d[i].tag + '>';\r
10848 \r
10849                         return s;\r
10850                 }\r
10851         });\r
10852 })(tinymce);\r
10853 \r
10854 (function(tinymce) {\r
10855         // Added for compression purposes\r
10856         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
10857 \r
10858         tinymce.EditorCommands = function(editor) {\r
10859                 var dom = editor.dom,\r
10860                         selection = editor.selection,\r
10861                         commands = {state: {}, exec : {}, value : {}},\r
10862                         settings = editor.settings,\r
10863                         bookmark;\r
10864 \r
10865                 function execCommand(command, ui, value) {\r
10866                         var func;\r
10867 \r
10868                         command = command.toLowerCase();\r
10869                         if (func = commands.exec[command]) {\r
10870                                 func(command, ui, value);\r
10871                                 return TRUE;\r
10872                         }\r
10873 \r
10874                         return FALSE;\r
10875                 };\r
10876 \r
10877                 function queryCommandState(command) {\r
10878                         var func;\r
10879 \r
10880                         command = command.toLowerCase();\r
10881                         if (func = commands.state[command])\r
10882                                 return func(command);\r
10883 \r
10884                         return -1;\r
10885                 };\r
10886 \r
10887                 function queryCommandValue(command) {\r
10888                         var func;\r
10889 \r
10890                         command = command.toLowerCase();\r
10891                         if (func = commands.value[command])\r
10892                                 return func(command);\r
10893 \r
10894                         return FALSE;\r
10895                 };\r
10896 \r
10897                 function addCommands(command_list, type) {\r
10898                         type = type || 'exec';\r
10899 \r
10900                         each(command_list, function(callback, command) {\r
10901                                 each(command.toLowerCase().split(','), function(command) {\r
10902                                         commands[type][command] = callback;\r
10903                                 });\r
10904                         });\r
10905                 };\r
10906 \r
10907                 // Expose public methods\r
10908                 tinymce.extend(this, {\r
10909                         execCommand : execCommand,\r
10910                         queryCommandState : queryCommandState,\r
10911                         queryCommandValue : queryCommandValue,\r
10912                         addCommands : addCommands\r
10913                 });\r
10914 \r
10915                 // Private methods\r
10916 \r
10917                 function execNativeCommand(command, ui, value) {\r
10918                         if (ui === undefined)\r
10919                                 ui = FALSE;\r
10920 \r
10921                         if (value === undefined)\r
10922                                 value = null;\r
10923 \r
10924                         return editor.getDoc().execCommand(command, ui, value);\r
10925                 };\r
10926 \r
10927                 function isFormatMatch(name) {\r
10928                         return editor.formatter.match(name);\r
10929                 };\r
10930 \r
10931                 function toggleFormat(name, value) {\r
10932                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
10933                 };\r
10934 \r
10935                 function storeSelection(type) {\r
10936                         bookmark = selection.getBookmark(type);\r
10937                 };\r
10938 \r
10939                 function restoreSelection() {\r
10940                         selection.moveToBookmark(bookmark);\r
10941                 };\r
10942 \r
10943                 // Add execCommand overrides\r
10944                 addCommands({\r
10945                         // Ignore these, added for compatibility\r
10946                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
10947 \r
10948                         // Add undo manager logic\r
10949                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
10950                                 editor.undoManager.add();\r
10951                         },\r
10952 \r
10953                         'Cut,Copy,Paste' : function(command) {\r
10954                                 var doc = editor.getDoc(), failed;\r
10955 \r
10956                                 // Try executing the native command\r
10957                                 try {\r
10958                                         execNativeCommand(command);\r
10959                                 } catch (ex) {\r
10960                                         // Command failed\r
10961                                         failed = TRUE;\r
10962                                 }\r
10963 \r
10964                                 // Present alert message about clipboard access not being available\r
10965                                 if (failed || !doc.queryCommandEnabled(command)) {\r
10966                                         if (tinymce.isGecko) {\r
10967                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
10968                                                         if (state)\r
10969                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
10970                                                 });\r
10971                                         } else\r
10972                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
10973                                 }\r
10974                         },\r
10975 \r
10976                         // Override unlink command\r
10977                         unlink : function(command) {\r
10978                                 if (selection.isCollapsed())\r
10979                                         selection.select(selection.getNode());\r
10980 \r
10981                                 execNativeCommand(command);\r
10982                                 selection.collapse(FALSE);\r
10983                         },\r
10984 \r
10985                         // Override justify commands to use the text formatter engine\r
10986                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
10987                                 var align = command.substring(7);\r
10988 \r
10989                                 // Remove all other alignments first\r
10990                                 each('left,center,right,full'.split(','), function(name) {\r
10991                                         if (align != name)\r
10992                                                 editor.formatter.remove('align' + name);\r
10993                                 });\r
10994 \r
10995                                 toggleFormat('align' + align);\r
10996                         },\r
10997 \r
10998                         // Override list commands to fix WebKit bug\r
10999                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11000                                 var listElm, listParent;\r
11001 \r
11002                                 execNativeCommand(command);\r
11003 \r
11004                                 // WebKit produces lists within block elements so we need to split them\r
11005                                 // we will replace the native list creation logic to custom logic later on\r
11006                                 // TODO: Remove this when the list creation logic is removed\r
11007                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
11008                                 if (listElm) {\r
11009                                         listParent = listElm.parentNode;\r
11010 \r
11011                                         // If list is within a text block then split that block\r
11012                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
11013                                                 storeSelection();\r
11014                                                 dom.split(listParent, listElm);\r
11015                                                 restoreSelection();\r
11016                                         }\r
11017                                 }\r
11018                         },\r
11019 \r
11020                         // Override commands to use the text formatter engine\r
11021                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11022                                 toggleFormat(command);\r
11023                         },\r
11024 \r
11025                         // Override commands to use the text formatter engine\r
11026                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
11027                                 toggleFormat(command, value);\r
11028                         },\r
11029 \r
11030                         FontSize : function(command, ui, value) {\r
11031                                 var fontClasses, fontSizes;\r
11032 \r
11033                                 // Convert font size 1-7 to styles\r
11034                                 if (value >= 1 && value <= 7) {\r
11035                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
11036                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
11037 \r
11038                                         if (fontClasses)\r
11039                                                 value = fontClasses[value - 1] || value;\r
11040                                         else\r
11041                                                 value = fontSizes[value - 1] || value;\r
11042                                 }\r
11043 \r
11044                                 toggleFormat(command, value);\r
11045                         },\r
11046 \r
11047                         RemoveFormat : function(command) {\r
11048                                 editor.formatter.remove(command);\r
11049                         },\r
11050 \r
11051                         mceBlockQuote : function(command) {\r
11052                                 toggleFormat('blockquote');\r
11053                         },\r
11054 \r
11055                         FormatBlock : function(command, ui, value) {\r
11056                                 return toggleFormat(value);\r
11057                         },\r
11058 \r
11059                         mceCleanup : function() {\r
11060                                 storeSelection();\r
11061                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
11062                                 restoreSelection();\r
11063                         },\r
11064 \r
11065                         mceRemoveNode : function(command, ui, value) {\r
11066                                 var node = value || selection.getNode();\r
11067 \r
11068                                 // Make sure that the body node isn't removed\r
11069                                 if (node != ed.getBody()) {\r
11070                                         storeSelection();\r
11071                                         editor.dom.remove(node, TRUE);\r
11072                                         restoreSelection();\r
11073                                 }\r
11074                         },\r
11075 \r
11076                         mceSelectNodeDepth : function(command, ui, value) {\r
11077                                 var counter = 0;\r
11078 \r
11079                                 dom.getParent(selection.getNode(), function(node) {\r
11080                                         if (node.nodeType == 1 && counter++ == value) {\r
11081                                                 selection.select(node);\r
11082                                                 return FALSE;\r
11083                                         }\r
11084                                 }, editor.getBody());\r
11085                         },\r
11086 \r
11087                         mceSelectNode : function(command, ui, value) {\r
11088                                 selection.select(value);\r
11089                         },\r
11090 \r
11091                         mceInsertContent : function(command, ui, value) {\r
11092                                 selection.setContent(value);\r
11093                         },\r
11094 \r
11095                         mceInsertRawHTML : function(command, ui, value) {\r
11096                                 selection.setContent('tiny_mce_marker');\r
11097                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value));\r
11098                         },\r
11099 \r
11100                         mceSetContent : function(command, ui, value) {\r
11101                                 editor.setContent(value);\r
11102                         },\r
11103 \r
11104                         'Indent,Outdent' : function(command) {\r
11105                                 var intentValue, indentUnit, value;\r
11106 \r
11107                                 // Setup indent level\r
11108                                 intentValue = settings.indentation;\r
11109                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
11110                                 intentValue = parseInt(intentValue);\r
11111 \r
11112                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
11113                                         each(selection.getSelectedBlocks(), function(element) {\r
11114                                                 if (command == 'outdent') {\r
11115                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
11116                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
11117                                                 } else\r
11118                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
11119                                         });\r
11120                                 } else\r
11121                                         execNativeCommand(command);\r
11122                         },\r
11123 \r
11124                         mceRepaint : function() {\r
11125                                 var bookmark;\r
11126 \r
11127                                 if (tinymce.isGecko) {\r
11128                                         try {\r
11129                                                 storeSelection(TRUE);\r
11130 \r
11131                                                 if (selection.getSel())\r
11132                                                         selection.getSel().selectAllChildren(editor.getBody());\r
11133 \r
11134                                                 selection.collapse(TRUE);\r
11135                                                 restoreSelection();\r
11136                                         } catch (ex) {\r
11137                                                 // Ignore\r
11138                                         }\r
11139                                 }\r
11140                         },\r
11141 \r
11142                         InsertHorizontalRule : function() {\r
11143                                 selection.setContent('<hr />');\r
11144                         },\r
11145 \r
11146                         mceToggleVisualAid : function() {\r
11147                                 editor.hasVisual = !editor.hasVisual;\r
11148                                 editor.addVisual();\r
11149                         },\r
11150 \r
11151                         mceReplaceContent : function(command, ui, value) {\r
11152                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
11153                         },\r
11154 \r
11155                         mceInsertLink : function(command, ui, value) {\r
11156                                 var link = dom.getParent(selection.getNode(), 'a');\r
11157 \r
11158                                 if (tinymce.is(value, 'string'))\r
11159                                         value = {href : value};\r
11160 \r
11161                                 if (!link) {\r
11162                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
11163                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
11164                                                 dom.setAttribs(link, value);\r
11165                                         });\r
11166                                 } else {\r
11167                                         if (value.href)\r
11168                                                 dom.setAttribs(link, value);\r
11169                                         else\r
11170                                                 ed.dom.remove(link, TRUE);\r
11171                                 }\r
11172                         }\r
11173                 });\r
11174 \r
11175                 // Add queryCommandState overrides\r
11176                 addCommands({\r
11177                         // Override justify commands\r
11178                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11179                                 return isFormatMatch('align' + command.substring(7));\r
11180                         },\r
11181 \r
11182                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11183                                 return isFormatMatch(command);\r
11184                         },\r
11185 \r
11186                         mceBlockQuote : function() {\r
11187                                 return isFormatMatch('blockquote');\r
11188                         },\r
11189 \r
11190                         Outdent : function() {\r
11191                                 var node;\r
11192 \r
11193                                 if (settings.inline_styles) {\r
11194                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11195                                                 return TRUE;\r
11196 \r
11197                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11198                                                 return TRUE;\r
11199                                 }\r
11200 \r
11201                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
11202                         },\r
11203 \r
11204                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11205                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
11206                         }\r
11207                 }, 'state');\r
11208 \r
11209                 // Add queryCommandValue overrides\r
11210                 addCommands({\r
11211                         'FontSize,FontName' : function(command) {\r
11212                                 var value = 0, parent;\r
11213 \r
11214                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
11215                                         if (command == 'fontsize')\r
11216                                                 value = parent.style.fontSize;\r
11217                                         else\r
11218                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
11219                                 }\r
11220 \r
11221                                 return value;\r
11222                         }\r
11223                 }, 'value');\r
11224 \r
11225                 // Add undo manager logic\r
11226                 if (settings.custom_undo_redo) {\r
11227                         addCommands({\r
11228                                 Undo : function() {\r
11229                                         editor.undoManager.undo();\r
11230                                 },\r
11231 \r
11232                                 Redo : function() {\r
11233                                         editor.undoManager.redo();\r
11234                                 }\r
11235                         });\r
11236                 }\r
11237         };\r
11238 })(tinymce);\r
11239 (function(tinymce) {\r
11240         tinymce.create('tinymce.UndoManager', {\r
11241                 index : 0,\r
11242                 data : null,\r
11243                 typing : 0,\r
11244 \r
11245                 UndoManager : function(ed) {\r
11246                         var t = this, Dispatcher = tinymce.util.Dispatcher;\r
11247 \r
11248                         t.editor = ed;\r
11249                         t.data = [];\r
11250                         t.onAdd = new Dispatcher(this);\r
11251                         t.onUndo = new Dispatcher(this);\r
11252                         t.onRedo = new Dispatcher(this);\r
11253                 },\r
11254 \r
11255                 add : function(l) {\r
11256                         var t = this, i, ed = t.editor, b, s = ed.settings, la;\r
11257 \r
11258                         l = l || {};\r
11259                         l.content = l.content || ed.getContent({format : 'raw', no_events : 1});\r
11260                         l.content = l.content.replace(/^\s*|\s*$/g, '');\r
11261 \r
11262                         // Add undo level if needed\r
11263                         la = t.data[t.index];\r
11264                         if (la && la.content == l.content) {\r
11265                                 if (t.index > 0 || t.data.length == 1)\r
11266                                         return null;\r
11267                         }\r
11268 \r
11269                         // Time to compress\r
11270                         if (s.custom_undo_redo_levels) {\r
11271                                 if (t.data.length > s.custom_undo_redo_levels) {\r
11272                                         for (i = 0; i < t.data.length - 1; i++)\r
11273                                                 t.data[i] = t.data[i + 1];\r
11274 \r
11275                                         t.data.length--;\r
11276                                         t.index = t.data.length;\r
11277                                 }\r
11278                         }\r
11279 \r
11280                         if (s.custom_undo_redo_restore_selection)\r
11281                                 l.bookmark = b = l.bookmark || ed.selection.getBookmark(2, true);\r
11282 \r
11283                         // Crop array if needed\r
11284                         if (t.index < t.data.length - 1) {\r
11285                                 // Treat first level as initial\r
11286                                 if (t.index == 0)\r
11287                                         t.data = [];\r
11288                                 else\r
11289                                         t.data.length = t.index + 1;\r
11290                         }\r
11291 \r
11292                         t.data.push(l);\r
11293                         t.index = t.data.length - 1;\r
11294 \r
11295                         t.onAdd.dispatch(t, l);\r
11296                         ed.isNotDirty = 0;\r
11297 \r
11298                         //console.log(t.index);\r
11299                         //console.dir(t.data);\r
11300 \r
11301                         return l;\r
11302                 },\r
11303 \r
11304                 undo : function() {\r
11305                         var t = this, ed = t.editor, l = l, i;\r
11306 \r
11307                         if (t.typing) {\r
11308                                 t.add();\r
11309                                 t.typing = 0;\r
11310                         }\r
11311 \r
11312                         if (t.index > 0) {\r
11313                                 l = t.data[--t.index];\r
11314 \r
11315                                 ed.setContent(l.content, {format : 'raw'});\r
11316                                 ed.selection.moveToBookmark(l.bookmark);\r
11317 \r
11318                                 t.onUndo.dispatch(t, l);\r
11319                         }\r
11320 \r
11321                         return l;\r
11322                 },\r
11323 \r
11324                 redo : function() {\r
11325                         var t = this, ed = t.editor, l = null;\r
11326 \r
11327                         if (t.index < t.data.length - 1) {\r
11328                                 l = t.data[++t.index];\r
11329                                 ed.setContent(l.content, {format : 'raw'});\r
11330                                 ed.selection.moveToBookmark(l.bookmark);\r
11331 \r
11332                                 t.onRedo.dispatch(t, l);\r
11333                         }\r
11334 \r
11335                         return l;\r
11336                 },\r
11337 \r
11338                 clear : function() {\r
11339                         var t = this;\r
11340 \r
11341                         t.data = [];\r
11342                         t.index = 0;\r
11343                         t.typing = 0;\r
11344                 },\r
11345 \r
11346                 hasUndo : function() {\r
11347                         return this.index > 0 || this.typing;\r
11348                 },\r
11349 \r
11350                 hasRedo : function() {\r
11351                         return this.index < this.data.length - 1;\r
11352                 }\r
11353         });\r
11354 })(tinymce);\r
11355 \r
11356 (function(tinymce) {\r
11357         // Shorten names\r
11358         var Event = tinymce.dom.Event,\r
11359                 isIE = tinymce.isIE,\r
11360                 isGecko = tinymce.isGecko,\r
11361                 isOpera = tinymce.isOpera,\r
11362                 each = tinymce.each,\r
11363                 extend = tinymce.extend,\r
11364                 TRUE = true,\r
11365                 FALSE = false;\r
11366 \r
11367         // Checks if the selection/caret is at the end of the specified block element\r
11368         function isAtEnd(rng, par) {\r
11369                 var rng2 = par.ownerDocument.createRange();\r
11370 \r
11371                 rng2.setStart(rng.endContainer, rng.endOffset);\r
11372                 rng2.setEndAfter(par);\r
11373 \r
11374                 // 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
11375                 return rng2.cloneContents().textContent.length == 0;\r
11376         };\r
11377 \r
11378         function isEmpty(n) {\r
11379                 n = n.innerHTML;\r
11380 \r
11381                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
11382                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
11383 \r
11384                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
11385         };\r
11386 \r
11387         function splitList(selection, dom, li) {\r
11388                 var listBlock, block;\r
11389 \r
11390                 if (isEmpty(li)) {\r
11391                         listBlock = dom.getParent(li, 'ul,ol');\r
11392 \r
11393                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
11394                                 dom.split(listBlock, li);\r
11395                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
11396                                 dom.replace(block, li);\r
11397                                 selection.select(block, 1);\r
11398                         }\r
11399 \r
11400                         return FALSE;\r
11401                 }\r
11402 \r
11403                 return TRUE;\r
11404         };\r
11405 \r
11406         tinymce.create('tinymce.ForceBlocks', {\r
11407                 ForceBlocks : function(ed) {\r
11408                         var t = this, s = ed.settings, elm;\r
11409 \r
11410                         t.editor = ed;\r
11411                         t.dom = ed.dom;\r
11412                         elm = (s.forced_root_block || 'p').toLowerCase();\r
11413                         s.element = elm.toUpperCase();\r
11414 \r
11415                         ed.onPreInit.add(t.setup, t);\r
11416 \r
11417                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
11418                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11419                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11420                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
11421                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
11422 \r
11423                         function padd(ed, o) {\r
11424                                 if (isOpera)\r
11425                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
11426 \r
11427                                 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
11428 \r
11429                                 if (!isIE && !isOpera && o.set) {\r
11430                                         // Use &nbsp; instead of BR in padded paragraphs\r
11431                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
11432                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
11433                                 } else\r
11434                                         o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
11435                         };\r
11436 \r
11437                         ed.onBeforeSetContent.add(padd);\r
11438                         ed.onPostProcess.add(padd);\r
11439 \r
11440                         if (s.forced_root_block) {\r
11441                                 ed.onInit.add(t.forceRoots, t);\r
11442                                 ed.onSetContent.add(t.forceRoots, t);\r
11443                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
11444                         }\r
11445                 },\r
11446 \r
11447                 setup : function() {\r
11448                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
11449 \r
11450                         // Force root blocks when typing and when getting output\r
11451                         if (s.forced_root_block) {\r
11452                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
11453                                 ed.onKeyUp.add(t.forceRoots, t);\r
11454                                 ed.onPreProcess.add(t.forceRoots, t);\r
11455                         }\r
11456 \r
11457                         if (s.force_br_newlines) {\r
11458                                 // Force IE to produce BRs on enter\r
11459                                 if (isIE) {\r
11460                                         ed.onKeyPress.add(function(ed, e) {\r
11461                                                 var n;\r
11462 \r
11463                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
11464                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
11465                                                         n = dom.get('__');\r
11466                                                         n.removeAttribute('id');\r
11467                                                         selection.select(n);\r
11468                                                         selection.collapse();\r
11469                                                         return Event.cancel(e);\r
11470                                                 }\r
11471                                         });\r
11472                                 }\r
11473                         }\r
11474 \r
11475                         if (!isIE && s.force_p_newlines) {\r
11476                                 ed.onKeyPress.add(function(ed, e) {\r
11477                                         if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
11478                                                 Event.cancel(e);\r
11479                                 });\r
11480 \r
11481                                 if (isGecko) {\r
11482                                         ed.onKeyDown.add(function(ed, e) {\r
11483                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
11484                                                         t.backspaceDelete(e, e.keyCode == 8);\r
11485                                         });\r
11486                                 }\r
11487                         }\r
11488 \r
11489                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
11490                         if (tinymce.isWebKit) {\r
11491                                 function insertBr(ed) {\r
11492                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
11493 \r
11494                                         // Insert BR element\r
11495                                         rng.insertNode(br = dom.create('br'));\r
11496 \r
11497                                         // Place caret after BR\r
11498                                         rng.setStartAfter(br);\r
11499                                         rng.setEndAfter(br);\r
11500                                         selection.setRng(rng);\r
11501 \r
11502                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
11503                                         if (selection.getSel().focusNode == br.previousSibling) {\r
11504                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
11505                                                 selection.collapse(TRUE);\r
11506                                         }\r
11507 \r
11508                                         // Create a temporary DIV after the BR and get the position as it\r
11509                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
11510                                         dom.insertAfter(div, br);\r
11511                                         divYPos = dom.getPos(div).y;\r
11512                                         dom.remove(div);\r
11513 \r
11514                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
11515                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
11516                                                 ed.getWin().scrollTo(0, divYPos);\r
11517                                 };\r
11518 \r
11519                                 ed.onKeyPress.add(function(ed, e) {\r
11520                                         if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) {\r
11521                                                 insertBr(ed);\r
11522                                                 Event.cancel(e);\r
11523                                         }\r
11524                                 });\r
11525                         }\r
11526 \r
11527                         // Padd empty inline elements within block elements\r
11528                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
11529                         ed.onPreProcess.add(function(ed, o) {\r
11530                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
11531                                         if (isEmpty(p)) {\r
11532                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
11533                                                         if (!n.hasChildNodes()) {\r
11534                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
11535                                                                 return FALSE; // Break the loop one padding is enough\r
11536                                                         }\r
11537                                                 });\r
11538                                         }\r
11539                                 });\r
11540                         });\r
11541 \r
11542                         // IE specific fixes\r
11543                         if (isIE) {\r
11544                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
11545                                 if (s.element != 'P') {\r
11546                                         ed.onKeyPress.add(function(ed, e) {\r
11547                                                 t.lastElm = selection.getNode().nodeName;\r
11548                                         });\r
11549 \r
11550                                         ed.onKeyUp.add(function(ed, e) {\r
11551                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
11552 \r
11553                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
11554                                                         n = dom.rename(n, s.element);\r
11555                                                         selection.select(n);\r
11556                                                         selection.collapse();\r
11557                                                         ed.nodeChanged();\r
11558                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
11559                                                         bl = dom.getParent(n, 'p');\r
11560 \r
11561                                                         if (bl) {\r
11562                                                                 dom.rename(bl, s.element);\r
11563                                                                 ed.nodeChanged();\r
11564                                                         }\r
11565                                                 }\r
11566                                         });\r
11567                                 }\r
11568                         }\r
11569                 },\r
11570 \r
11571                 find : function(n, t, s) {\r
11572                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
11573 \r
11574                         while (n = w.nextNode()) {\r
11575                                 c++;\r
11576 \r
11577                                 // Index by node\r
11578                                 if (t == 0 && n == s)\r
11579                                         return c;\r
11580 \r
11581                                 // Node by index\r
11582                                 if (t == 1 && c == s)\r
11583                                         return n;\r
11584                         }\r
11585 \r
11586                         return -1;\r
11587                 },\r
11588 \r
11589                 forceRoots : function(ed, e) {\r
11590                         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
11591                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
11592 \r
11593                         // Fix for bug #1863847\r
11594                         //if (e && e.keyCode == 13)\r
11595                         //      return TRUE;\r
11596 \r
11597                         // Wrap non blocks into blocks\r
11598                         for (i = nl.length - 1; i >= 0; i--) {\r
11599                                 nx = nl[i];\r
11600 \r
11601                                 // Ignore internal elements\r
11602                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
11603                                         bl = null;\r
11604                                         continue;\r
11605                                 }\r
11606 \r
11607                                 // Is text or non block element\r
11608                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11609                                         if (!bl) {\r
11610                                                 // Create new block but ignore whitespace\r
11611                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11612                                                         // Store selection\r
11613                                                         if (si == -2 && r) {\r
11614                                                                 if (!isIE) {\r
11615                                                                         // If selection is element then mark it\r
11616                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11617                                                                                 // Save the id of the selected element\r
11618                                                                                 eid = n.getAttribute("id");\r
11619                                                                                 n.setAttribute("id", "__mce");\r
11620                                                                         } else {\r
11621                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
11622                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11623                                                                                         so = r.startOffset;\r
11624                                                                                         eo = r.endOffset;\r
11625                                                                                         si = t.find(b, 0, r.startContainer);\r
11626                                                                                         ei = t.find(b, 0, r.endContainer);\r
11627                                                                                 }\r
11628                                                                         }\r
11629                                                                 } else {\r
11630                                                                         tr = d.body.createTextRange();\r
11631                                                                         tr.moveToElementText(b);\r
11632                                                                         tr.collapse(1);\r
11633                                                                         bp = tr.move('character', c) * -1;\r
11634 \r
11635                                                                         tr = r.duplicate();\r
11636                                                                         tr.collapse(1);\r
11637                                                                         sp = tr.move('character', c) * -1;\r
11638 \r
11639                                                                         tr = r.duplicate();\r
11640                                                                         tr.collapse(0);\r
11641                                                                         le = (tr.move('character', c) * -1) - sp;\r
11642 \r
11643                                                                         si = sp - bp;\r
11644                                                                         ei = le;\r
11645                                                                 }\r
11646                                                         }\r
11647 \r
11648                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11649                                                         // See: http://support.microsoft.com/kb/829907\r
11650                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
11651                                                         nx.parentNode.replaceChild(bl, nx);\r
11652                                                         bl.appendChild(nx);\r
11653                                                 }\r
11654                                         } else {\r
11655                                                 if (bl.hasChildNodes())\r
11656                                                         bl.insertBefore(nx, bl.firstChild);\r
11657                                                 else\r
11658                                                         bl.appendChild(nx);\r
11659                                         }\r
11660                                 } else\r
11661                                         bl = null; // Time to create new block\r
11662                         }\r
11663 \r
11664                         // Restore selection\r
11665                         if (si != -2) {\r
11666                                 if (!isIE) {\r
11667                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
11668                                         r = d.createRange();\r
11669 \r
11670                                         // Select last location or generated block\r
11671                                         if (si != -1)\r
11672                                                 r.setStart(t.find(b, 1, si), so);\r
11673                                         else\r
11674                                                 r.setStart(bl, 0);\r
11675 \r
11676                                         // Select last location or generated block\r
11677                                         if (ei != -1)\r
11678                                                 r.setEnd(t.find(b, 1, ei), eo);\r
11679                                         else\r
11680                                                 r.setEnd(bl, 0);\r
11681 \r
11682                                         if (s) {\r
11683                                                 s.removeAllRanges();\r
11684                                                 s.addRange(r);\r
11685                                         }\r
11686                                 } else {\r
11687                                         try {\r
11688                                                 r = s.createRange();\r
11689                                                 r.moveToElementText(b);\r
11690                                                 r.collapse(1);\r
11691                                                 r.moveStart('character', si);\r
11692                                                 r.moveEnd('character', ei);\r
11693                                                 r.select();\r
11694                                         } catch (ex) {\r
11695                                                 // Ignore\r
11696                                         }\r
11697                                 }\r
11698                         } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
11699                                 // Restore the id of the selected element\r
11700                                 if (eid)\r
11701                                         n.setAttribute('id', eid);\r
11702                                 else\r
11703                                         n.removeAttribute('id');\r
11704 \r
11705                                 // Move caret before selected element\r
11706                                 r = d.createRange();\r
11707                                 r.setStartBefore(n);\r
11708                                 r.setEndBefore(n);\r
11709                                 se.setRng(r);\r
11710                         }\r
11711                 },\r
11712 \r
11713                 getParentBlock : function(n) {\r
11714                         var d = this.dom;\r
11715 \r
11716                         return d.getParent(n, d.isBlock);\r
11717                 },\r
11718 \r
11719                 insertPara : function(e) {\r
11720                         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
11721                         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
11722 \r
11723                         // If root blocks are forced then use Operas default behavior since it's really good\r
11724 // Removed due to bug: #1853816\r
11725 //                      if (se.forced_root_block && isOpera)\r
11726 //                              return TRUE;\r
11727 \r
11728                         // Setup before range\r
11729                         rb = d.createRange();\r
11730 \r
11731                         // If is before the first block element and in body, then move it into first block element\r
11732                         rb.setStart(s.anchorNode, s.anchorOffset);\r
11733                         rb.collapse(TRUE);\r
11734 \r
11735                         // Setup after range\r
11736                         ra = d.createRange();\r
11737 \r
11738                         // If is before the first block element and in body, then move it into first block element\r
11739                         ra.setStart(s.focusNode, s.focusOffset);\r
11740                         ra.collapse(TRUE);\r
11741 \r
11742                         // Setup start/end points\r
11743                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
11744                         sn = dir ? s.anchorNode : s.focusNode;\r
11745                         so = dir ? s.anchorOffset : s.focusOffset;\r
11746                         en = dir ? s.focusNode : s.anchorNode;\r
11747                         eo = dir ? s.focusOffset : s.anchorOffset;\r
11748 \r
11749                         // If selection is in empty table cell\r
11750                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
11751                                 if (sn.firstChild.nodeName == 'BR')\r
11752                                         dom.remove(sn.firstChild); // Remove BR\r
11753 \r
11754                                 // Create two new block elements\r
11755                                 if (sn.childNodes.length == 0) {\r
11756                                         ed.dom.add(sn, se.element, null, '<br />');\r
11757                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11758                                 } else {\r
11759                                         n = sn.innerHTML;\r
11760                                         sn.innerHTML = '';\r
11761                                         ed.dom.add(sn, se.element, null, n);\r
11762                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11763                                 }\r
11764 \r
11765                                 // Move caret into the last one\r
11766                                 r = d.createRange();\r
11767                                 r.selectNodeContents(aft);\r
11768                                 r.collapse(1);\r
11769                                 ed.selection.setRng(r);\r
11770 \r
11771                                 return FALSE;\r
11772                         }\r
11773 \r
11774                         // If the caret is in an invalid location in FF we need to move it into the first block\r
11775                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
11776                                 sn = en = sn.firstChild;\r
11777                                 so = eo = 0;\r
11778                                 rb = d.createRange();\r
11779                                 rb.setStart(sn, 0);\r
11780                                 ra = d.createRange();\r
11781                                 ra.setStart(en, 0);\r
11782                         }\r
11783 \r
11784                         // Never use body as start or end node\r
11785                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11786                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
11787                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11788                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
11789 \r
11790                         // Get start and end blocks\r
11791                         sb = t.getParentBlock(sn);\r
11792                         eb = t.getParentBlock(en);\r
11793                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
11794 \r
11795                         // Return inside list use default browser behavior\r
11796                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
11797                                 if (n.nodeName == 'LI')\r
11798                                         return splitList(ed.selection, t.dom, n);\r
11799 \r
11800                                 return TRUE;\r
11801                         }\r
11802 \r
11803                         // If caption or absolute layers then always generate new blocks within\r
11804                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11805                                 bn = se.element;\r
11806                                 sb = null;\r
11807                         }\r
11808 \r
11809                         // If caption or absolute layers then always generate new blocks within\r
11810                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11811                                 bn = se.element;\r
11812                                 eb = null;\r
11813                         }\r
11814 \r
11815                         // Use P instead\r
11816                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
11817                                 bn = se.element;\r
11818                                 sb = eb = null;\r
11819                         }\r
11820 \r
11821                         // Setup new before and after blocks\r
11822                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
11823                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
11824 \r
11825                         // Remove id from after clone\r
11826                         aft.removeAttribute('id');\r
11827 \r
11828                         // Is header and cursor is at the end, then force paragraph under\r
11829                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
11830                                 aft = ed.dom.create(se.element);\r
11831 \r
11832                         // Find start chop node\r
11833                         n = sc = sn;\r
11834                         do {\r
11835                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11836                                         break;\r
11837 \r
11838                                 sc = n;\r
11839                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
11840 \r
11841                         // Find end chop node\r
11842                         n = ec = en;\r
11843                         do {\r
11844                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11845                                         break;\r
11846 \r
11847                                 ec = n;\r
11848                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
11849 \r
11850                         // Place first chop part into before block element\r
11851                         if (sc.nodeName == bn)\r
11852                                 rb.setStart(sc, 0);\r
11853                         else\r
11854                                 rb.setStartBefore(sc);\r
11855 \r
11856                         rb.setEnd(sn, so);\r
11857                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11858 \r
11859                         // Place secnd chop part within new block element\r
11860                         try {\r
11861                                 ra.setEndAfter(ec);\r
11862                         } catch(ex) {\r
11863                                 //console.debug(s.focusNode, s.focusOffset);\r
11864                         }\r
11865 \r
11866                         ra.setStart(en, eo);\r
11867                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11868 \r
11869                         // Create range around everything\r
11870                         r = d.createRange();\r
11871                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
11872                                 r.setStartBefore(sc.parentNode);\r
11873                         } else {\r
11874                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
11875                                         r.setStartBefore(rb.startContainer);\r
11876                                 else\r
11877                                         r.setStart(rb.startContainer, rb.startOffset);\r
11878                         }\r
11879 \r
11880                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
11881                                 r.setEndAfter(ec.parentNode);\r
11882                         else\r
11883                                 r.setEnd(ra.endContainer, ra.endOffset);\r
11884 \r
11885                         // Delete and replace it with new block elements\r
11886                         r.deleteContents();\r
11887 \r
11888                         if (isOpera)\r
11889                                 ed.getWin().scrollTo(0, vp.y);\r
11890 \r
11891                         // Never wrap blocks in blocks\r
11892                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
11893                                 bef.innerHTML = bef.firstChild.innerHTML;\r
11894 \r
11895                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
11896                                 aft.innerHTML = aft.firstChild.innerHTML;\r
11897 \r
11898                         // Padd empty blocks\r
11899                         if (isEmpty(bef))\r
11900                                 bef.innerHTML = '<br />';\r
11901 \r
11902                         function appendStyles(e, en) {\r
11903                                 var nl = [], nn, n, i;\r
11904 \r
11905                                 e.innerHTML = '';\r
11906 \r
11907                                 // Make clones of style elements\r
11908                                 if (se.keep_styles) {\r
11909                                         n = en;\r
11910                                         do {\r
11911                                                 // We only want style specific elements\r
11912                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
11913                                                         nn = n.cloneNode(FALSE);\r
11914                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
11915                                                         nl.push(nn);\r
11916                                                 }\r
11917                                         } while (n = n.parentNode);\r
11918                                 }\r
11919 \r
11920                                 // Append style elements to aft\r
11921                                 if (nl.length > 0) {\r
11922                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
11923                                                 nn = nn.appendChild(nl[i]);\r
11924 \r
11925                                         // Padd most inner style element\r
11926                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11927                                         return nl[0]; // Move caret to most inner element\r
11928                                 } else\r
11929                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11930                         };\r
11931 \r
11932                         // Fill empty afterblook with current style\r
11933                         if (isEmpty(aft))\r
11934                                 car = appendStyles(aft, en);\r
11935 \r
11936                         // Opera needs this one backwards for older versions\r
11937                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
11938                                 r.insertNode(bef);\r
11939                                 r.insertNode(aft);\r
11940                         } else {\r
11941                                 r.insertNode(aft);\r
11942                                 r.insertNode(bef);\r
11943                         }\r
11944 \r
11945                         // Normalize\r
11946                         aft.normalize();\r
11947                         bef.normalize();\r
11948 \r
11949                         function first(n) {\r
11950                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
11951                         };\r
11952 \r
11953                         // Move cursor and scroll into view\r
11954                         r = d.createRange();\r
11955                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
11956                         r.collapse(1);\r
11957                         s.removeAllRanges();\r
11958                         s.addRange(r);\r
11959 \r
11960                         // 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
11961                         y = ed.dom.getPos(aft).y;\r
11962                         ch = aft.clientHeight;\r
11963 \r
11964                         // Is element within viewport\r
11965                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
11966                                 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
11967                                 //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
11968                         }\r
11969 \r
11970                         return FALSE;\r
11971                 },\r
11972 \r
11973                 backspaceDelete : function(e, bs) {\r
11974                         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;\r
11975 \r
11976                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
11977                         // This workaround removes the element by hand and moves the caret to the previous element\r
11978                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
11979                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
11980                                         // Find previous block element\r
11981                                         n = sc;\r
11982                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
11983 \r
11984                                         if (n) {\r
11985                                                 if (sc != b.firstChild) {\r
11986                                                         // Find last text node\r
11987                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
11988                                                         while (tn = w.nextNode())\r
11989                                                                 n = tn;\r
11990 \r
11991                                                         // Place caret at the end of last text node\r
11992                                                         r = ed.getDoc().createRange();\r
11993                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
11994                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
11995                                                         se.setRng(r);\r
11996 \r
11997                                                         // Remove the target container\r
11998                                                         ed.dom.remove(sc);\r
11999                                                 }\r
12000 \r
12001                                                 return Event.cancel(e);\r
12002                                         }\r
12003                                 }\r
12004                         }\r
12005 \r
12006                         // Gecko generates BR elements here and there, we don't like those so lets remove them\r
12007                         function handler(e) {\r
12008                                 var pr;\r
12009 \r
12010                                 e = e.target;\r
12011 \r
12012                                 // A new BR was created in a block element, remove it\r
12013                                 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {\r
12014                                         pr = e.previousSibling;\r
12015 \r
12016                                         Event.remove(b, 'DOMNodeInserted', handler);\r
12017 \r
12018                                         // Is there whitespace at the end of the node before then we might need the pesky BR\r
12019                                         // to place the caret at a correct location see bug: #2013943\r
12020                                         if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))\r
12021                                                 return;\r
12022 \r
12023                                         // Only remove BR elements that got inserted in the middle of the text\r
12024                                         if (e.previousSibling || e.nextSibling)\r
12025                                                 ed.dom.remove(e);\r
12026                                 }\r
12027                         };\r
12028 \r
12029                         // Listen for new nodes\r
12030                         Event._add(b, 'DOMNodeInserted', handler);\r
12031 \r
12032                         // Remove listener\r
12033                         window.setTimeout(function() {\r
12034                                 Event._remove(b, 'DOMNodeInserted', handler);\r
12035                         }, 1);\r
12036                 }\r
12037         });\r
12038 })(tinymce);\r
12039 \r
12040 (function(tinymce) {\r
12041         // Shorten names\r
12042         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
12043 \r
12044         tinymce.create('tinymce.ControlManager', {\r
12045                 ControlManager : function(ed, s) {\r
12046                         var t = this, i;\r
12047 \r
12048                         s = s || {};\r
12049                         t.editor = ed;\r
12050                         t.controls = {};\r
12051                         t.onAdd = new tinymce.util.Dispatcher(t);\r
12052                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
12053                         t.prefix = s.prefix || ed.id + '_';\r
12054                         t._cls = {};\r
12055 \r
12056                         t.onPostRender.add(function() {\r
12057                                 each(t.controls, function(c) {\r
12058                                         c.postRender();\r
12059                                 });\r
12060                         });\r
12061                 },\r
12062 \r
12063                 get : function(id) {\r
12064                         return this.controls[this.prefix + id] || this.controls[id];\r
12065                 },\r
12066 \r
12067                 setActive : function(id, s) {\r
12068                         var c = null;\r
12069 \r
12070                         if (c = this.get(id))\r
12071                                 c.setActive(s);\r
12072 \r
12073                         return c;\r
12074                 },\r
12075 \r
12076                 setDisabled : function(id, s) {\r
12077                         var c = null;\r
12078 \r
12079                         if (c = this.get(id))\r
12080                                 c.setDisabled(s);\r
12081 \r
12082                         return c;\r
12083                 },\r
12084 \r
12085                 add : function(c) {\r
12086                         var t = this;\r
12087 \r
12088                         if (c) {\r
12089                                 t.controls[c.id] = c;\r
12090                                 t.onAdd.dispatch(c, t);\r
12091                         }\r
12092 \r
12093                         return c;\r
12094                 },\r
12095 \r
12096                 createControl : function(n) {\r
12097                         var c, t = this, ed = t.editor;\r
12098 \r
12099                         each(ed.plugins, function(p) {\r
12100                                 if (p.createControl) {\r
12101                                         c = p.createControl(n, t);\r
12102 \r
12103                                         if (c)\r
12104                                                 return false;\r
12105                                 }\r
12106                         });\r
12107 \r
12108                         switch (n) {\r
12109                                 case "|":\r
12110                                 case "separator":\r
12111                                         return t.createSeparator();\r
12112                         }\r
12113 \r
12114                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
12115                                 return t.createButton(n, c);\r
12116 \r
12117                         return t.add(c);\r
12118                 },\r
12119 \r
12120                 createDropMenu : function(id, s, cc) {\r
12121                         var t = this, ed = t.editor, c, bm, v, cls;\r
12122 \r
12123                         s = extend({\r
12124                                 'class' : 'mceDropDown',\r
12125                                 constrain : ed.settings.constrain_menus\r
12126                         }, s);\r
12127 \r
12128                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
12129                         if (v = ed.getParam('skin_variant'))\r
12130                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
12131 \r
12132                         id = t.prefix + id;\r
12133                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
12134                         c = t.controls[id] = new cls(id, s);\r
12135                         c.onAddItem.add(function(c, o) {\r
12136                                 var s = o.settings;\r
12137 \r
12138                                 s.title = ed.getLang(s.title, s.title);\r
12139 \r
12140                                 if (!s.onclick) {\r
12141                                         s.onclick = function(v) {\r
12142                                                 if (s.cmd)\r
12143                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12144                                         };\r
12145                                 }\r
12146                         });\r
12147 \r
12148                         ed.onRemove.add(function() {\r
12149                                 c.destroy();\r
12150                         });\r
12151 \r
12152                         // Fix for bug #1897785, #1898007\r
12153                         if (tinymce.isIE) {\r
12154                                 c.onShowMenu.add(function() {\r
12155                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12156                                         ed.focus();\r
12157 \r
12158                                         bm = ed.selection.getBookmark(1);\r
12159                                 });\r
12160 \r
12161                                 c.onHideMenu.add(function() {\r
12162                                         if (bm) {\r
12163                                                 ed.selection.moveToBookmark(bm);\r
12164                                                 bm = 0;\r
12165                                         }\r
12166                                 });\r
12167                         }\r
12168 \r
12169                         return t.add(c);\r
12170                 },\r
12171 \r
12172                 createListBox : function(id, s, cc) {\r
12173                         var t = this, ed = t.editor, cmd, c, cls;\r
12174 \r
12175                         if (t.get(id))\r
12176                                 return null;\r
12177 \r
12178                         s.title = ed.translate(s.title);\r
12179                         s.scope = s.scope || ed;\r
12180 \r
12181                         if (!s.onselect) {\r
12182                                 s.onselect = function(v) {\r
12183                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12184                                 };\r
12185                         }\r
12186 \r
12187                         s = extend({\r
12188                                 title : s.title,\r
12189                                 'class' : 'mce_' + id,\r
12190                                 scope : s.scope,\r
12191                                 control_manager : t\r
12192                         }, s);\r
12193 \r
12194                         id = t.prefix + id;\r
12195 \r
12196                         if (ed.settings.use_native_selects)\r
12197                                 c = new tinymce.ui.NativeListBox(id, s);\r
12198                         else {\r
12199                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
12200                                 c = new cls(id, s);\r
12201                         }\r
12202 \r
12203                         t.controls[id] = c;\r
12204 \r
12205                         // Fix focus problem in Safari\r
12206                         if (tinymce.isWebKit) {\r
12207                                 c.onPostRender.add(function(c, n) {\r
12208                                         // Store bookmark on mousedown\r
12209                                         Event.add(n, 'mousedown', function() {\r
12210                                                 ed.bookmark = ed.selection.getBookmark(1);\r
12211                                         });\r
12212 \r
12213                                         // Restore on focus, since it might be lost\r
12214                                         Event.add(n, 'focus', function() {\r
12215                                                 ed.selection.moveToBookmark(ed.bookmark);\r
12216                                                 ed.bookmark = null;\r
12217                                         });\r
12218                                 });\r
12219                         }\r
12220 \r
12221                         if (c.hideMenu)\r
12222                                 ed.onMouseDown.add(c.hideMenu, c);\r
12223 \r
12224                         return t.add(c);\r
12225                 },\r
12226 \r
12227                 createButton : function(id, s, cc) {\r
12228                         var t = this, ed = t.editor, o, c, cls;\r
12229 \r
12230                         if (t.get(id))\r
12231                                 return null;\r
12232 \r
12233                         s.title = ed.translate(s.title);\r
12234                         s.label = ed.translate(s.label);\r
12235                         s.scope = s.scope || ed;\r
12236 \r
12237                         if (!s.onclick && !s.menu_button) {\r
12238                                 s.onclick = function() {\r
12239                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12240                                 };\r
12241                         }\r
12242 \r
12243                         s = extend({\r
12244                                 title : s.title,\r
12245                                 'class' : 'mce_' + id,\r
12246                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
12247                                 scope : s.scope,\r
12248                                 control_manager : t\r
12249                         }, s);\r
12250 \r
12251                         id = t.prefix + id;\r
12252 \r
12253                         if (s.menu_button) {\r
12254                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
12255                                 c = new cls(id, s);\r
12256                                 ed.onMouseDown.add(c.hideMenu, c);\r
12257                         } else {\r
12258                                 cls = t._cls.button || tinymce.ui.Button;\r
12259                                 c = new cls(id, s);\r
12260                         }\r
12261 \r
12262                         return t.add(c);\r
12263                 },\r
12264 \r
12265                 createMenuButton : function(id, s, cc) {\r
12266                         s = s || {};\r
12267                         s.menu_button = 1;\r
12268 \r
12269                         return this.createButton(id, s, cc);\r
12270                 },\r
12271 \r
12272                 createSplitButton : function(id, s, cc) {\r
12273                         var t = this, ed = t.editor, cmd, c, cls;\r
12274 \r
12275                         if (t.get(id))\r
12276                                 return null;\r
12277 \r
12278                         s.title = ed.translate(s.title);\r
12279                         s.scope = s.scope || ed;\r
12280 \r
12281                         if (!s.onclick) {\r
12282                                 s.onclick = function(v) {\r
12283                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12284                                 };\r
12285                         }\r
12286 \r
12287                         if (!s.onselect) {\r
12288                                 s.onselect = function(v) {\r
12289                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12290                                 };\r
12291                         }\r
12292 \r
12293                         s = extend({\r
12294                                 title : s.title,\r
12295                                 'class' : 'mce_' + id,\r
12296                                 scope : s.scope,\r
12297                                 control_manager : t\r
12298                         }, s);\r
12299 \r
12300                         id = t.prefix + id;\r
12301                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
12302                         c = t.add(new cls(id, s));\r
12303                         ed.onMouseDown.add(c.hideMenu, c);\r
12304 \r
12305                         return c;\r
12306                 },\r
12307 \r
12308                 createColorSplitButton : function(id, s, cc) {\r
12309                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
12310 \r
12311                         if (t.get(id))\r
12312                                 return null;\r
12313 \r
12314                         s.title = ed.translate(s.title);\r
12315                         s.scope = s.scope || ed;\r
12316 \r
12317                         if (!s.onclick) {\r
12318                                 s.onclick = function(v) {\r
12319                                         if (tinymce.isIE)\r
12320                                                 bm = ed.selection.getBookmark(1);\r
12321 \r
12322                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12323                                 };\r
12324                         }\r
12325 \r
12326                         if (!s.onselect) {\r
12327                                 s.onselect = function(v) {\r
12328                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12329                                 };\r
12330                         }\r
12331 \r
12332                         s = extend({\r
12333                                 title : s.title,\r
12334                                 'class' : 'mce_' + id,\r
12335                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
12336                                 scope : s.scope,\r
12337                                 more_colors_title : ed.getLang('more_colors')\r
12338                         }, s);\r
12339 \r
12340                         id = t.prefix + id;\r
12341                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
12342                         c = new cls(id, s);\r
12343                         ed.onMouseDown.add(c.hideMenu, c);\r
12344 \r
12345                         // Remove the menu element when the editor is removed\r
12346                         ed.onRemove.add(function() {\r
12347                                 c.destroy();\r
12348                         });\r
12349 \r
12350                         // Fix for bug #1897785, #1898007\r
12351                         if (tinymce.isIE) {\r
12352                                 c.onShowMenu.add(function() {\r
12353                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12354                                         ed.focus();\r
12355                                         bm = ed.selection.getBookmark(1);\r
12356                                 });\r
12357 \r
12358                                 c.onHideMenu.add(function() {\r
12359                                         if (bm) {\r
12360                                                 ed.selection.moveToBookmark(bm);\r
12361                                                 bm = 0;\r
12362                                         }\r
12363                                 });\r
12364                         }\r
12365 \r
12366                         return t.add(c);\r
12367                 },\r
12368 \r
12369                 createToolbar : function(id, s, cc) {\r
12370                         var c, t = this, cls;\r
12371 \r
12372                         id = t.prefix + id;\r
12373                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
12374                         c = new cls(id, s);\r
12375 \r
12376                         if (t.get(id))\r
12377                                 return null;\r
12378 \r
12379                         return t.add(c);\r
12380                 },\r
12381 \r
12382                 createSeparator : function(cc) {\r
12383                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
12384 \r
12385                         return new cls();\r
12386                 },\r
12387 \r
12388                 setControlType : function(n, c) {\r
12389                         return this._cls[n.toLowerCase()] = c;\r
12390                 },\r
12391         \r
12392                 destroy : function() {\r
12393                         each(this.controls, function(c) {\r
12394                                 c.destroy();\r
12395                         });\r
12396 \r
12397                         this.controls = null;\r
12398                 }\r
12399         });\r
12400 })(tinymce);\r
12401 \r
12402 (function(tinymce) {\r
12403         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
12404 \r
12405         tinymce.create('tinymce.WindowManager', {\r
12406                 WindowManager : function(ed) {\r
12407                         var t = this;\r
12408 \r
12409                         t.editor = ed;\r
12410                         t.onOpen = new Dispatcher(t);\r
12411                         t.onClose = new Dispatcher(t);\r
12412                         t.params = {};\r
12413                         t.features = {};\r
12414                 },\r
12415 \r
12416                 open : function(s, p) {\r
12417                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
12418 \r
12419                         // Default some options\r
12420                         s = s || {};\r
12421                         p = p || {};\r
12422                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
12423                         sh = isOpera ? vp.h : screen.height;\r
12424                         s.name = s.name || 'mc_' + new Date().getTime();\r
12425                         s.width = parseInt(s.width || 320);\r
12426                         s.height = parseInt(s.height || 240);\r
12427                         s.resizable = true;\r
12428                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
12429                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
12430                         p.inline = false;\r
12431                         p.mce_width = s.width;\r
12432                         p.mce_height = s.height;\r
12433                         p.mce_auto_focus = s.auto_focus;\r
12434 \r
12435                         if (mo) {\r
12436                                 if (isIE) {\r
12437                                         s.center = true;\r
12438                                         s.help = false;\r
12439                                         s.dialogWidth = s.width + 'px';\r
12440                                         s.dialogHeight = s.height + 'px';\r
12441                                         s.scroll = s.scrollbars || false;\r
12442                                 }\r
12443                         }\r
12444 \r
12445                         // Build features string\r
12446                         each(s, function(v, k) {\r
12447                                 if (tinymce.is(v, 'boolean'))\r
12448                                         v = v ? 'yes' : 'no';\r
12449 \r
12450                                 if (!/^(name|url)$/.test(k)) {\r
12451                                         if (isIE && mo)\r
12452                                                 f += (f ? ';' : '') + k + ':' + v;\r
12453                                         else\r
12454                                                 f += (f ? ',' : '') + k + '=' + v;\r
12455                                 }\r
12456                         });\r
12457 \r
12458                         t.features = s;\r
12459                         t.params = p;\r
12460                         t.onOpen.dispatch(t, s, p);\r
12461 \r
12462                         u = s.url || s.file;\r
12463                         u = tinymce._addVer(u);\r
12464 \r
12465                         try {\r
12466                                 if (isIE && mo) {\r
12467                                         w = 1;\r
12468                                         window.showModalDialog(u, window, f);\r
12469                                 } else\r
12470                                         w = window.open(u, s.name, f);\r
12471                         } catch (ex) {\r
12472                                 // Ignore\r
12473                         }\r
12474 \r
12475                         if (!w)\r
12476                                 alert(t.editor.getLang('popup_blocked'));\r
12477                 },\r
12478 \r
12479                 close : function(w) {\r
12480                         w.close();\r
12481                         this.onClose.dispatch(this);\r
12482                 },\r
12483 \r
12484                 createInstance : function(cl, a, b, c, d, e) {\r
12485                         var f = tinymce.resolve(cl);\r
12486 \r
12487                         return new f(a, b, c, d, e);\r
12488                 },\r
12489 \r
12490                 confirm : function(t, cb, s, w) {\r
12491                         w = w || window;\r
12492 \r
12493                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
12494                 },\r
12495 \r
12496                 alert : function(tx, cb, s, w) {\r
12497                         var t = this;\r
12498 \r
12499                         w = w || window;\r
12500                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
12501 \r
12502                         if (cb)\r
12503                                 cb.call(s || t);\r
12504                 },\r
12505 \r
12506                 resizeBy : function(dw, dh, win) {\r
12507                         win.resizeBy(dw, dh);\r
12508                 },\r
12509 \r
12510                 // Internal functions\r
12511 \r
12512                 _decode : function(s) {\r
12513                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
12514                 }\r
12515         });\r
12516 }(tinymce));\r
12517 (function(tinymce) {\r
12518         function CommandManager() {\r
12519                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
12520 \r
12521                 function add(collection, cmd, func, scope) {\r
12522                         if (typeof(cmd) == 'string')\r
12523                                 cmd = [cmd];\r
12524 \r
12525                         tinymce.each(cmd, function(cmd) {\r
12526                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
12527                         });\r
12528                 };\r
12529 \r
12530                 tinymce.extend(this, {\r
12531                         add : function(cmd, func, scope) {\r
12532                                 add(execCommands, cmd, func, scope);\r
12533                         },\r
12534 \r
12535                         addQueryStateHandler : function(cmd, func, scope) {\r
12536                                 add(queryStateCommands, cmd, func, scope);\r
12537                         },\r
12538 \r
12539                         addQueryValueHandler : function(cmd, func, scope) {\r
12540                                 add(queryValueCommands, cmd, func, scope);\r
12541                         },\r
12542 \r
12543                         execCommand : function(scope, cmd, ui, value, args) {\r
12544                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
12545                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
12546                                                 return true;\r
12547                                 }\r
12548                         },\r
12549 \r
12550                         queryCommandValue : function() {\r
12551                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
12552                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12553                         },\r
12554 \r
12555                         queryCommandState : function() {\r
12556                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
12557                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12558                         }\r
12559                 });\r
12560         };\r
12561 \r
12562         tinymce.GlobalCommands = new CommandManager();\r
12563 })(tinymce);\r
12564 (function(tinymce) {\r
12565         tinymce.Formatter = function(ed) {\r
12566                 var formats = {},\r
12567                         each = tinymce.each,\r
12568                         dom = ed.dom,\r
12569                         selection = ed.selection,\r
12570                         TreeWalker = tinymce.dom.TreeWalker,\r
12571                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
12572                         isValid = ed.schema.isValid,\r
12573                         isBlock = dom.isBlock,\r
12574                         forcedRootBlock = ed.settings.forced_root_block,\r
12575                         nodeIndex = dom.nodeIndex,\r
12576                         INVISIBLE_CHAR = '\uFEFF',\r
12577                         MCE_ATTR_RE = /^(src|href|style)$/,\r
12578                         FALSE = false,\r
12579                         TRUE = true,\r
12580                         undefined,\r
12581                         caretHandler,\r
12582                         pendingFormats;\r
12583 \r
12584                 function getParents(node, selector) {\r
12585                         return dom.getParents(node, selector, dom.getRoot());\r
12586                 };\r
12587 \r
12588                 function resetPending() {\r
12589                         // Needs reset\r
12590                         if (!pendingFormats || pendingFormats.apply.length || pendingFormats.remove.length)\r
12591                                 pendingFormats = {apply : [], remove : []};\r
12592                 };\r
12593 \r
12594                 ed.onMouseUp.add(resetPending);\r
12595                 resetPending();\r
12596 \r
12597                 // Public functions\r
12598 \r
12599                 function get(name) {\r
12600                         return name ? formats[name] : formats;\r
12601                 };\r
12602 \r
12603                 function register(name, format) {\r
12604                         if (name) {\r
12605                                 if (typeof(name) !== 'string') {\r
12606                                         each(name, function(format, name) {\r
12607                                                 register(name, format);\r
12608                                         });\r
12609                                 } else {\r
12610                                         // Force format into array and add it to internal collection\r
12611                                         format = format.length ? format : [format];\r
12612 \r
12613                                         each(format, function(format) {\r
12614                                                 // Set deep to false by default on selector formats this to avoid removing\r
12615                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
12616                                                 if (format.deep === undefined)\r
12617                                                         format.deep = !format.selector;\r
12618 \r
12619                                                 // Default to true\r
12620                                                 if (format.split === undefined)\r
12621                                                         format.split = !format.selector;\r
12622 \r
12623                                                 // Default to true\r
12624                                                 if (format.remove === undefined && format.selector)\r
12625                                                         format.remove = 'none';\r
12626 \r
12627                                                 // Split classes if needed\r
12628                                                 if (typeof(format.classes) === 'string')\r
12629                                                         format.classes = format.classes.split(/\s+/);\r
12630                                         });\r
12631 \r
12632                                         formats[name] = format;\r
12633                                 }\r
12634                         }\r
12635                 };\r
12636 \r
12637                 function apply(name, vars, node) {\r
12638                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
12639 \r
12640                         function moveStart(rng) {\r
12641                                 var container = rng.startContainer,\r
12642                                         offset = rng.startOffset,\r
12643                                         walker, node;\r
12644 \r
12645                                 // Move startContainer/startOffset in to a suitable node\r
12646                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
12647                                         walker = new TreeWalker(container.childNodes[offset]);\r
12648                                         for (node = walker.current(); node; node = walker.next()) {\r
12649                                                 if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) {\r
12650                                                         rng.setStart(node, 0);\r
12651                                                         break;\r
12652                                                 }\r
12653                                         }\r
12654                                 }\r
12655 \r
12656                                 return rng;\r
12657                         };\r
12658 \r
12659                         function setElementFormat(elm, fmt) {\r
12660                                 fmt = fmt || format;\r
12661 \r
12662                                 if (elm) {\r
12663                                         each(fmt.styles, function(value, name) {\r
12664                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
12665                                         });\r
12666 \r
12667                                         each(fmt.attributes, function(value, name) {\r
12668                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
12669                                         });\r
12670 \r
12671                                         each(fmt.classes, function(value) {\r
12672                                                 value = replaceVars(value, vars);\r
12673 \r
12674                                                 if (!dom.hasClass(elm, value))\r
12675                                                         dom.addClass(elm, value);\r
12676                                         });\r
12677                                 }\r
12678                         };\r
12679 \r
12680                         function applyRngStyle(rng) {\r
12681                                 var newWrappers = [], wrapName, wrapElm;\r
12682 \r
12683                                 // Setup wrapper element\r
12684                                 wrapName = format.inline || format.block;\r
12685                                 wrapElm = dom.create(wrapName);\r
12686                                 setElementFormat(wrapElm);\r
12687 \r
12688                                 rangeUtils.walk(rng, function(nodes) {\r
12689                                         var currentWrapElm;\r
12690 \r
12691                                         function process(node) {\r
12692                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase();\r
12693 \r
12694                                                 // Stop wrapping on br elements\r
12695                                                 if (isEq(nodeName, 'br')) {\r
12696                                                         currentWrapElm = 0;\r
12697 \r
12698                                                         // Remove any br elements when we wrap things\r
12699                                                         if (format.block)\r
12700                                                                 dom.remove(node);\r
12701 \r
12702                                                         return;\r
12703                                                 }\r
12704 \r
12705                                                 // If node is wrapper type\r
12706                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
12707                                                         currentWrapElm = 0;\r
12708                                                         return;\r
12709                                                 }\r
12710 \r
12711                                                 // Can we rename the block\r
12712                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
12713                                                         node = dom.rename(node, wrapName);\r
12714                                                         setElementFormat(node);\r
12715                                                         newWrappers.push(node);\r
12716                                                         currentWrapElm = 0;\r
12717                                                         return;\r
12718                                                 }\r
12719 \r
12720                                                 // Handle selector patterns\r
12721                                                 if (format.selector) {\r
12722                                                         // Look for matching formats\r
12723                                                         each(formatList, function(format) {\r
12724                                                                 if (dom.is(node, format.selector))\r
12725                                                                         setElementFormat(node, format);\r
12726                                                         });\r
12727 \r
12728                                                         return;\r
12729                                                 }\r
12730 \r
12731                                                 // Is it valid to wrap this item\r
12732                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
12733                                                         // Start wrapping\r
12734                                                         if (!currentWrapElm) {\r
12735                                                                 // Wrap the node\r
12736                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
12737                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
12738                                                                 newWrappers.push(currentWrapElm);\r
12739                                                         }\r
12740 \r
12741                                                         currentWrapElm.appendChild(node);\r
12742                                                 } else {\r
12743                                                         // Start a new wrapper for possible children\r
12744                                                         currentWrapElm = 0;\r
12745 \r
12746                                                         each(tinymce.grep(node.childNodes), process);\r
12747 \r
12748                                                         // End the last wrapper\r
12749                                                         currentWrapElm = 0;\r
12750                                                 }\r
12751                                         };\r
12752 \r
12753                                         // Process siblings from range\r
12754                                         each(nodes, process);\r
12755                                 });\r
12756 \r
12757                                 // Cleanup\r
12758                                 each(newWrappers, function(node) {\r
12759                                         var childCount;\r
12760 \r
12761                                         function getChildCount(node) {\r
12762                                                 var count = 0;\r
12763 \r
12764                                                 each(node.childNodes, function(node) {\r
12765                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
12766                                                                 count++;\r
12767                                                 });\r
12768 \r
12769                                                 return count;\r
12770                                         };\r
12771 \r
12772                                         function mergeStyles(node) {\r
12773                                                 var child, clone;\r
12774 \r
12775                                                 each(node.childNodes, function(node) {\r
12776                                                         if (node.nodeType == 1 && !isBookmarkNode(node)) {\r
12777                                                                 child = node;\r
12778                                                                 return FALSE; // break loop\r
12779                                                         }\r
12780                                                 });\r
12781 \r
12782                                                 // If child was found and of the same type as the current node\r
12783                                                 if (child && matchName(child, format)) {\r
12784                                                         clone = child.cloneNode(FALSE);\r
12785                                                         setElementFormat(clone);\r
12786 \r
12787                                                         dom.replace(clone, node, TRUE);\r
12788                                                         dom.remove(child, 1);\r
12789                                                 }\r
12790 \r
12791                                                 return clone || node;\r
12792                                         };\r
12793 \r
12794                                         childCount = getChildCount(node);\r
12795 \r
12796                                         // Remove empty nodes\r
12797                                         if (childCount === 0) {\r
12798                                                 dom.remove(node, 1);\r
12799                                                 return;\r
12800                                         }\r
12801 \r
12802                                         if (format.inline || format.wrapper) {\r
12803                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
12804                                                 if (!format.exact && childCount === 1)\r
12805                                                         node = mergeStyles(node);\r
12806 \r
12807                                                 // Remove/merge children\r
12808                                                 each(formatList, function(format) {\r
12809                                                         // Merge all children of similar type will move styles from child to parent\r
12810                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
12811                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
12812                                                         each(dom.select(format.inline, node), function(child) {\r
12813                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
12814                                                         });\r
12815                                                 });\r
12816 \r
12817                                                 // Look for parent with similar style format\r
12818                                                 dom.getParent(node.parentNode, function(parent) {\r
12819                                                         if (matchNode(parent, name, vars)) {\r
12820                                                                 dom.remove(node, 1);\r
12821                                                                 node = 0;\r
12822                                                                 return TRUE;\r
12823                                                         }\r
12824                                                 });\r
12825 \r
12826                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
12827                                                 if (node) {\r
12828                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
12829                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
12830                                                 }\r
12831                                         }\r
12832                                 });\r
12833                         };\r
12834 \r
12835                         if (format) {\r
12836                                 if (node) {\r
12837                                         rng = dom.createRng();\r
12838 \r
12839                                         rng.setStartBefore(node);\r
12840                                         rng.setEndAfter(node);\r
12841 \r
12842                                         applyRngStyle(rng);\r
12843                                 } else {\r
12844                                         if (!selection.isCollapsed() || !format.inline) {\r
12845                                                 // Apply formatting to selection\r
12846                                                 bookmark = selection.getBookmark();\r
12847                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
12848 \r
12849                                                 selection.moveToBookmark(bookmark);\r
12850                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
12851                                                 ed.nodeChanged();\r
12852                                         } else\r
12853                                                 performCaretAction('apply', name, vars);\r
12854                                 }\r
12855                         }\r
12856                 };\r
12857 \r
12858                 function remove(name, vars, node) {\r
12859                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
12860 \r
12861                         // Merges the styles for each node\r
12862                         function process(node) {\r
12863                                 var children, i, l;\r
12864 \r
12865                                 // Grab the children first since the nodelist might be changed\r
12866                                 children = tinymce.grep(node.childNodes);\r
12867 \r
12868                                 // Process current node\r
12869                                 for (i = 0, l = formatList.length; i < l; i++) {\r
12870                                         if (removeFormat(formatList[i], vars, node, node))\r
12871                                                 break;\r
12872                                 }\r
12873 \r
12874                                 // Process the children\r
12875                                 if (format.deep) {\r
12876                                         for (i = 0, l = children.length; i < l; i++)\r
12877                                                 process(children[i]);\r
12878                                 }\r
12879                         };\r
12880 \r
12881                         function findFormatRoot(container) {\r
12882                                 var formatRoot;\r
12883 \r
12884                                 // Find format root\r
12885                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
12886                                         // Find format root element\r
12887                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
12888                                                 // If the matched format has a remove none flag we shouldn't split it\r
12889                                                 if (!isBlock(parent) && matchNode(parent, name, vars))\r
12890                                                         formatRoot = parent;\r
12891                                         }\r
12892                                 });\r
12893 \r
12894                                 return formatRoot;\r
12895                         };\r
12896 \r
12897                         function wrapAndSplit(format_root, container, target, split) {\r
12898                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
12899 \r
12900                                 // Format root found then clone formats and split it\r
12901                                 if (format_root) {\r
12902                                         formatRootParent = format_root.parentNode;\r
12903 \r
12904                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
12905                                                 clone = parent.cloneNode(FALSE);\r
12906 \r
12907                                                 for (i = 0; i < formatList.length; i++) {\r
12908                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
12909                                                                 clone = 0;\r
12910                                                                 break;\r
12911                                                         }\r
12912                                                 }\r
12913 \r
12914                                                 // Build wrapper node\r
12915                                                 if (clone) {\r
12916                                                         if (lastClone)\r
12917                                                                 clone.appendChild(lastClone);\r
12918 \r
12919                                                         if (!firstClone)\r
12920                                                                 firstClone = clone;\r
12921 \r
12922                                                         lastClone = clone;\r
12923                                                 }\r
12924                                         }\r
12925 \r
12926                                         if (split)\r
12927                                                 container = dom.split(format_root, container);\r
12928 \r
12929                                         // Wrap container in cloned formats\r
12930                                         if (lastClone) {\r
12931                                                 target.parentNode.insertBefore(lastClone, target);\r
12932                                                 firstClone.appendChild(target);\r
12933                                         }\r
12934                                 }\r
12935 \r
12936                                 return container;\r
12937                         };\r
12938 \r
12939                         function splitToFormatRoot(container) {\r
12940                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
12941                         };\r
12942 \r
12943                         function unwrap(start) {\r
12944                                 var node = dom.get(start ? '_start' : '_end'),\r
12945                                         out = node[start ? 'firstChild' : 'lastChild'];\r
12946 \r
12947                                 dom.remove(node, 1);\r
12948 \r
12949                                 return out;\r
12950                         };\r
12951 \r
12952                         function removeRngStyle(rng) {\r
12953                                 var startContainer, endContainer;\r
12954 \r
12955                                 rng = expandRng(rng, formatList, TRUE);\r
12956 \r
12957                                 if (format.split) {\r
12958                                         startContainer = getContainer(rng, TRUE);\r
12959                                         endContainer = getContainer(rng);\r
12960 \r
12961                                         if (startContainer != endContainer) {\r
12962                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
12963                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
12964                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
12965 \r
12966                                                 // Split start/end\r
12967                                                 splitToFormatRoot(startContainer);\r
12968                                                 splitToFormatRoot(endContainer);\r
12969 \r
12970                                                 // Unwrap start/end to get real elements again\r
12971                                                 startContainer = unwrap(TRUE);\r
12972                                                 endContainer = unwrap();\r
12973                                         } else\r
12974                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
12975 \r
12976                                         // Update range positions since they might have changed after the split operations\r
12977                                         rng.startContainer = startContainer.parentNode;\r
12978                                         rng.startOffset = nodeIndex(startContainer);\r
12979                                         rng.endContainer = endContainer.parentNode;\r
12980                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
12981                                 }\r
12982 \r
12983                                 // Remove items between start/end\r
12984                                 rangeUtils.walk(rng, function(nodes) {\r
12985                                         each(nodes, function(node) {\r
12986                                                 process(node);\r
12987                                         });\r
12988                                 });\r
12989                         };\r
12990 \r
12991                         // Handle node\r
12992                         if (node) {\r
12993                                 rng = dom.createRng();\r
12994                                 rng.setStartBefore(node);\r
12995                                 rng.setEndAfter(node);\r
12996                                 removeRngStyle(rng);\r
12997                                 return;\r
12998                         }\r
12999 \r
13000                         if (!selection.isCollapsed() || !format.inline) {\r
13001                                 bookmark = selection.getBookmark();\r
13002                                 removeRngStyle(selection.getRng(TRUE));\r
13003                                 selection.moveToBookmark(bookmark);\r
13004                                 ed.nodeChanged();\r
13005                         } else\r
13006                                 performCaretAction('remove', name, vars);\r
13007                 };\r
13008 \r
13009                 function toggle(name, vars, node) {\r
13010                         if (match(name, vars, node))\r
13011                                 remove(name, vars, node);\r
13012                         else\r
13013                                 apply(name, vars, node);\r
13014                 };\r
13015 \r
13016                 function matchNode(node, name, vars) {\r
13017                         var formatList = get(name), format, i, classes;\r
13018 \r
13019                         function matchItems(node, format, item_name) {\r
13020                                 var key, value, items = format[item_name], i;\r
13021 \r
13022                                 // Check all items\r
13023                                 if (items) {\r
13024                                         // Non indexed object\r
13025                                         if (items.length === undefined) {\r
13026                                                 for (key in items) {\r
13027                                                         if (items.hasOwnProperty(key)) {\r
13028                                                                 if (item_name === 'attributes')\r
13029                                                                         value = dom.getAttrib(node, key);\r
13030                                                                 else\r
13031                                                                         value = getStyle(node, key);\r
13032 \r
13033                                                                 if (!isEq(value, replaceVars(items[key], vars)))\r
13034                                                                         return;\r
13035                                                         }\r
13036                                                 }\r
13037                                         } else {\r
13038                                                 // Only one match needed for indexed arrays\r
13039                                                 for (i = 0; i < items.length; i++) {\r
13040                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
13041                                                                 return TRUE;\r
13042                                                 }\r
13043                                         }\r
13044                                 }\r
13045 \r
13046                                 return TRUE;\r
13047                         };\r
13048 \r
13049                         if (formatList && node) {\r
13050                                 // Check each format in list\r
13051                                 for (i = 0; i < formatList.length; i++) {\r
13052                                         format = formatList[i];\r
13053 \r
13054                                         // Name name, attributes, styles and classes\r
13055                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
13056                                                 // Match classes\r
13057                                                 if (classes = format.classes) {\r
13058                                                         for (i = 0; i < classes.length; i++) {\r
13059                                                                 if (!dom.hasClass(node, classes[i]))\r
13060                                                                         return;\r
13061                                                         }\r
13062                                                 }\r
13063 \r
13064                                                 return TRUE;\r
13065                                         }\r
13066                                 }\r
13067                         }\r
13068                 };\r
13069 \r
13070                 function match(name, vars, node) {\r
13071                         var startNode, i;\r
13072 \r
13073                         function matchParents(node) {\r
13074                                 // Find first node with similar format settings\r
13075                                 node = dom.getParent(node, function(node) {\r
13076                                         return !!matchNode(node, name, vars);\r
13077                                 });\r
13078 \r
13079                                 // Do an exact check on the similar format element\r
13080                                 return matchNode(node, name, vars);\r
13081                         };\r
13082 \r
13083                         // Check specified node\r
13084                         if (node)\r
13085                                 return matchParents(node);\r
13086 \r
13087                         // Check pending formats\r
13088                         if (selection.isCollapsed()) {\r
13089                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13090                                         if (pendingFormats.apply[i].name == name)\r
13091                                                 return true;\r
13092                                 }\r
13093 \r
13094                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13095                                         if (pendingFormats.remove[i].name == name)\r
13096                                                 return false;\r
13097                                 }\r
13098 \r
13099                                 return matchParents(selection.getNode());\r
13100                         }\r
13101 \r
13102                         // Check selected node\r
13103                         node = selection.getNode();\r
13104                         if (matchParents(node))\r
13105                                 return TRUE;\r
13106 \r
13107                         // Check start node if it's different\r
13108                         startNode = selection.getStart();\r
13109                         if (startNode != node) {\r
13110                                 if (matchParents(startNode))\r
13111                                         return TRUE;\r
13112                         }\r
13113 \r
13114                         return FALSE;\r
13115                 };\r
13116 \r
13117                 function canApply(name) {\r
13118                         var formatList = get(name), startNode, parents, i, x, selector;\r
13119 \r
13120                         if (formatList) {\r
13121                                 startNode = selection.getStart();\r
13122                                 parents = getParents(startNode);\r
13123 \r
13124                                 for (x = formatList.length - 1; x >= 0; x--) {\r
13125                                         selector = formatList[x].selector;\r
13126 \r
13127                                         // Format is not selector based, then always return TRUE\r
13128                                         if (!selector)\r
13129                                                 return TRUE;\r
13130 \r
13131                                         for (i = parents.length - 1; i >= 0; i--) {\r
13132                                                 if (dom.is(parents[i], selector))\r
13133                                                         return TRUE;\r
13134                                         }\r
13135                                 }\r
13136                         }\r
13137 \r
13138                         return FALSE;\r
13139                 };\r
13140 \r
13141                 // Expose to public\r
13142                 tinymce.extend(this, {\r
13143                         get : get,\r
13144                         register : register,\r
13145                         apply : apply,\r
13146                         remove : remove,\r
13147                         toggle : toggle,\r
13148                         match : match,\r
13149                         matchNode : matchNode,\r
13150                         canApply : canApply\r
13151                 });\r
13152 \r
13153                 // Private functions\r
13154 \r
13155                 function matchName(node, format) {\r
13156                         // Check for inline match\r
13157                         if (isEq(node, format.inline))\r
13158                                 return TRUE;\r
13159 \r
13160                         // Check for block match\r
13161                         if (isEq(node, format.block))\r
13162                                 return TRUE;\r
13163 \r
13164                         // Check for selector match\r
13165                         if (format.selector)\r
13166                                 return dom.is(node, format.selector);\r
13167                 };\r
13168 \r
13169                 function isEq(str1, str2) {\r
13170                         str1 = str1 || '';\r
13171                         str2 = str2 || '';\r
13172 \r
13173                         str1 = str1.nodeName || str1;\r
13174                         str2 = str2.nodeName || str2;\r
13175 \r
13176                         return str1.toLowerCase() == str2.toLowerCase();\r
13177                 };\r
13178 \r
13179                 function getStyle(node, name) {\r
13180                         var styleVal = dom.getStyle(node, name);\r
13181 \r
13182                         // Force the format to hex\r
13183                         if (name == 'color' || name == 'backgroundColor')\r
13184                                 styleVal = dom.toHex(styleVal);\r
13185 \r
13186                         // Opera will return bold as 700\r
13187                         if (name == 'fontWeight' && styleVal == 700)\r
13188                                 styleVal = 'bold';\r
13189 \r
13190                         return '' + styleVal;\r
13191                 };\r
13192 \r
13193                 function replaceVars(value, vars) {\r
13194                         if (typeof(value) != "string")\r
13195                                 value = value(vars);\r
13196                         else if (vars) {\r
13197                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
13198                                         return vars[name] || str;\r
13199                                 });\r
13200                         }\r
13201 \r
13202                         return value;\r
13203                 };\r
13204 \r
13205                 function isWhiteSpaceNode(node) {\r
13206                         return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue);\r
13207                 };\r
13208 \r
13209                 function wrap(node, name, attrs) {\r
13210                         var wrapper = dom.create(name, attrs);\r
13211 \r
13212                         node.parentNode.insertBefore(wrapper, node);\r
13213                         wrapper.appendChild(node);\r
13214 \r
13215                         return wrapper;\r
13216                 };\r
13217 \r
13218                 function expandRng(rng, format, remove) {\r
13219                         var startContainer = rng.startContainer,\r
13220                                 startOffset = rng.startOffset,\r
13221                                 endContainer = rng.endContainer,\r
13222                                 endOffset = rng.endOffset, sibling, lastIdx;\r
13223 \r
13224                         // This function walks up the tree if there is no siblings before/after the node\r
13225                         function findParentContainer(container, child_name, sibling_name, root) {\r
13226                                 var parent, child;\r
13227 \r
13228                                 root = root || dom.getRoot();\r
13229 \r
13230                                 for (;;) {\r
13231                                         // Check if we can move up are we at root level or body level\r
13232                                         parent = container.parentNode;\r
13233 \r
13234                                         // Stop expanding on block elements or root depending on format\r
13235                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
13236                                                 return container;\r
13237 \r
13238                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
13239                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13240                                                         return container;\r
13241 \r
13242                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13243                                                         return container;\r
13244                                         }\r
13245 \r
13246                                         container = container.parentNode;\r
13247                                 }\r
13248 \r
13249                                 return container;\r
13250                         };\r
13251 \r
13252                         // If index based start position then resolve it\r
13253                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
13254                                 lastIdx = startContainer.childNodes.length - 1;\r
13255                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
13256 \r
13257                                 if (startContainer.nodeType == 3)\r
13258                                         startOffset = 0;\r
13259                         }\r
13260 \r
13261                         // If index based end position then resolve it\r
13262                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
13263                                 lastIdx = endContainer.childNodes.length - 1;\r
13264                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
13265 \r
13266                                 if (endContainer.nodeType == 3)\r
13267                                         endOffset = endContainer.nodeValue.length;\r
13268                         }\r
13269 \r
13270                         // Exclude bookmark nodes if possible\r
13271                         if (isBookmarkNode(startContainer.parentNode))\r
13272                                 startContainer = startContainer.parentNode;\r
13273 \r
13274                         if (isBookmarkNode(startContainer))\r
13275                                 startContainer = startContainer.nextSibling || startContainer;\r
13276 \r
13277                         if (isBookmarkNode(endContainer.parentNode))\r
13278                                 endContainer = endContainer.parentNode;\r
13279 \r
13280                         if (isBookmarkNode(endContainer))\r
13281                                 endContainer = endContainer.previousSibling || endContainer;\r
13282 \r
13283                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
13284                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
13285                         // This will reduce the number of wrapper elements that needs to be created\r
13286                         // Move start point up the tree\r
13287                         if (format[0].inline || format[0].block_expand) {\r
13288                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13289                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13290                         }\r
13291 \r
13292                         // Expand start/end container to matching selector\r
13293                         if (format[0].selector && format[0].expand !== FALSE) {\r
13294                                 function findSelectorEndPoint(container, sibling_name) {\r
13295                                         var parents, i, y;\r
13296 \r
13297                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
13298                                                 container = container[sibling_name];\r
13299 \r
13300                                         parents = getParents(container);\r
13301                                         for (i = 0; i < parents.length; i++) {\r
13302                                                 for (y = 0; y < format.length; y++) {\r
13303                                                         if (dom.is(parents[i], format[y].selector))\r
13304                                                                 return parents[i];\r
13305                                                 }\r
13306                                         }\r
13307 \r
13308                                         return container;\r
13309                                 };\r
13310 \r
13311                                 // Find new startContainer/endContainer if there is better one\r
13312                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
13313                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
13314                         }\r
13315 \r
13316                         // Expand start/end container to matching block element or text node\r
13317                         if (format[0].block || format[0].selector) {\r
13318                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
13319                                         var node;\r
13320 \r
13321                                         // Expand to block of similar type\r
13322                                         if (!format[0].wrapper)\r
13323                                                 node = dom.getParent(container, format[0].block);\r
13324 \r
13325                                         // Expand to first wrappable block element or any block element\r
13326                                         if (!node)\r
13327                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
13328 \r
13329                                         // Exclude inner lists from wrapping\r
13330                                         if (node && format[0].wrapper)\r
13331                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
13332 \r
13333                                         // Didn't find a block element look for first/last wrappable element\r
13334                                         if (!node) {\r
13335                                                 node = container;\r
13336 \r
13337                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
13338                                                         node = node[sibling_name];\r
13339 \r
13340                                                         // Break on BR but include it will be removed later on\r
13341                                                         // we can't remove it now since we need to check if it can be wrapped\r
13342                                                         if (isEq(node, 'br'))\r
13343                                                                 break;\r
13344                                                 }\r
13345                                         }\r
13346 \r
13347                                         return node || container;\r
13348                                 };\r
13349 \r
13350                                 // Find new startContainer/endContainer if there is better one\r
13351                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
13352                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
13353 \r
13354                                 // Non block element then try to expand up the leaf\r
13355                                 if (format[0].block) {\r
13356                                         if (!isBlock(startContainer))\r
13357                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13358 \r
13359                                         if (!isBlock(endContainer))\r
13360                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13361                                 }\r
13362                         }\r
13363 \r
13364                         // Setup index for startContainer\r
13365                         if (startContainer.nodeType == 1) {\r
13366                                 startOffset = nodeIndex(startContainer);\r
13367                                 startContainer = startContainer.parentNode;\r
13368                         }\r
13369 \r
13370                         // Setup index for endContainer\r
13371                         if (endContainer.nodeType == 1) {\r
13372                                 endOffset = nodeIndex(endContainer) + 1;\r
13373                                 endContainer = endContainer.parentNode;\r
13374                         }\r
13375 \r
13376                         // Return new range like object\r
13377                         return {\r
13378                                 startContainer : startContainer,\r
13379                                 startOffset : startOffset,\r
13380                                 endContainer : endContainer,\r
13381                                 endOffset : endOffset\r
13382                         };\r
13383                 }\r
13384 \r
13385                 function removeFormat(format, vars, node, compare_node) {\r
13386                         var i, attrs, stylesModified;\r
13387 \r
13388                         // Check if node matches format\r
13389                         if (!matchName(node, format))\r
13390                                 return FALSE;\r
13391 \r
13392                         // Should we compare with format attribs and styles\r
13393                         if (format.remove != 'all') {\r
13394                                 // Remove styles\r
13395                                 each(format.styles, function(value, name) {\r
13396                                         value = replaceVars(value, vars);\r
13397 \r
13398                                         // Indexed array\r
13399                                         if (typeof(name) === 'number') {\r
13400                                                 name = value;\r
13401                                                 compare_node = 0;\r
13402                                         }\r
13403 \r
13404                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
13405                                                 dom.setStyle(node, name, '');\r
13406 \r
13407                                         stylesModified = 1;\r
13408                                 });\r
13409 \r
13410                                 // Remove style attribute if it's empty\r
13411                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
13412                                         node.removeAttribute('style');\r
13413                                         node.removeAttribute('_mce_style');\r
13414                                 }\r
13415 \r
13416                                 // Remove attributes\r
13417                                 each(format.attributes, function(value, name) {\r
13418                                         var valueOut;\r
13419 \r
13420                                         value = replaceVars(value, vars);\r
13421 \r
13422                                         // Indexed array\r
13423                                         if (typeof(name) === 'number') {\r
13424                                                 name = value;\r
13425                                                 compare_node = 0;\r
13426                                         }\r
13427 \r
13428                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
13429                                                 // Keep internal classes\r
13430                                                 if (name == 'class') {\r
13431                                                         value = dom.getAttrib(node, name);\r
13432                                                         if (value) {\r
13433                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
13434                                                                 valueOut = '';\r
13435                                                                 each(value.split(/\s+/), function(cls) {\r
13436                                                                         if (/mce\w+/.test(cls))\r
13437                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
13438                                                                 });\r
13439 \r
13440                                                                 // We got some internal classes left\r
13441                                                                 if (valueOut) {\r
13442                                                                         dom.setAttrib(node, name, valueOut);\r
13443                                                                         return;\r
13444                                                                 }\r
13445                                                         }\r
13446                                                 }\r
13447 \r
13448                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
13449                                                 if (name == "class")\r
13450                                                         node.removeAttribute('className');\r
13451 \r
13452                                                 // Remove mce prefixed attributes\r
13453                                                 if (MCE_ATTR_RE.test(name))\r
13454                                                         node.removeAttribute('_mce_' + name);\r
13455 \r
13456                                                 node.removeAttribute(name);\r
13457                                         }\r
13458                                 });\r
13459 \r
13460                                 // Remove classes\r
13461                                 each(format.classes, function(value) {\r
13462                                         value = replaceVars(value, vars);\r
13463 \r
13464                                         if (!compare_node || dom.hasClass(compare_node, value))\r
13465                                                 dom.removeClass(node, value);\r
13466                                 });\r
13467 \r
13468                                 // Check for non internal attributes\r
13469                                 attrs = dom.getAttribs(node);\r
13470                                 for (i = 0; i < attrs.length; i++) {\r
13471                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
13472                                                 return FALSE;\r
13473                                 }\r
13474                         }\r
13475 \r
13476                         // Remove the inline child if it's empty for example <b> or <span>\r
13477                         if (format.remove != 'none') {\r
13478                                 removeNode(node, format);\r
13479                                 return TRUE;\r
13480                         }\r
13481                 };\r
13482 \r
13483                 function removeNode(node, format) {\r
13484                         var parentNode = node.parentNode, rootBlockElm;\r
13485 \r
13486                         if (format.block) {\r
13487                                 if (!forcedRootBlock) {\r
13488                                         function find(node, next, inc) {\r
13489                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
13490 \r
13491                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
13492                                         };\r
13493 \r
13494                                         // Append BR elements if needed before we remove the block\r
13495                                         if (isBlock(node) && !isBlock(parentNode)) {\r
13496                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
13497                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
13498 \r
13499                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
13500                                                         node.appendChild(dom.create('br'));\r
13501                                         }\r
13502                                 } else {\r
13503                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
13504                                         if (parentNode == dom.getRoot()) {\r
13505                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
13506                                                         each(tinymce.grep(node.childNodes), function(node) {\r
13507                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
13508                                                                         if (!rootBlockElm)\r
13509                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
13510                                                                         else\r
13511                                                                                 rootBlockElm.appendChild(node);\r
13512                                                                 } else\r
13513                                                                         rootBlockElm = 0;\r
13514                                                         });\r
13515                                                 }\r
13516                                         }\r
13517                                 }\r
13518                         }\r
13519 \r
13520                         dom.remove(node, 1);\r
13521                 };\r
13522 \r
13523                 function getNonWhiteSpaceSibling(node, next, inc) {\r
13524                         if (node) {\r
13525                                 next = next ? 'nextSibling' : 'previousSibling';\r
13526 \r
13527                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
13528                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
13529                                                 return node;\r
13530                                 }\r
13531                         }\r
13532                 };\r
13533 \r
13534                 function isBookmarkNode(node) {\r
13535                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
13536                 };\r
13537 \r
13538                 function mergeSiblings(prev, next) {\r
13539                         var marker, sibling, tmpSibling;\r
13540 \r
13541                         function compareElements(node1, node2) {\r
13542                                 // Not the same name\r
13543                                 if (node1.nodeName != node2.nodeName)\r
13544                                         return FALSE;\r
13545 \r
13546                                 function getAttribs(node) {\r
13547                                         var attribs = {};\r
13548 \r
13549                                         each(dom.getAttribs(node), function(attr) {\r
13550                                                 var name = attr.nodeName.toLowerCase();\r
13551 \r
13552                                                 // Don't compare internal attributes or style\r
13553                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
13554                                                         attribs[name] = dom.getAttrib(node, name);\r
13555                                         });\r
13556 \r
13557                                         return attribs;\r
13558                                 };\r
13559 \r
13560                                 function compareObjects(obj1, obj2) {\r
13561                                         var value, name;\r
13562 \r
13563                                         for (name in obj1) {\r
13564                                                 // Obj1 has item obj2 doesn't have\r
13565                                                 if (obj1.hasOwnProperty(name)) {\r
13566                                                         value = obj2[name];\r
13567 \r
13568                                                         // Obj2 doesn't have obj1 item\r
13569                                                         if (value === undefined)\r
13570                                                                 return FALSE;\r
13571 \r
13572                                                         // Obj2 item has a different value\r
13573                                                         if (obj1[name] != value)\r
13574                                                                 return FALSE;\r
13575 \r
13576                                                         // Delete similar value\r
13577                                                         delete obj2[name];\r
13578                                                 }\r
13579                                         }\r
13580 \r
13581                                         // Check if obj 2 has something obj 1 doesn't have\r
13582                                         for (name in obj2) {\r
13583                                                 // Obj2 has item obj1 doesn't have\r
13584                                                 if (obj2.hasOwnProperty(name))\r
13585                                                         return FALSE;\r
13586                                         }\r
13587 \r
13588                                         return TRUE;\r
13589                                 };\r
13590 \r
13591                                 // Attribs are not the same\r
13592                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
13593                                         return FALSE;\r
13594 \r
13595                                 // Styles are not the same\r
13596                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
13597                                         return FALSE;\r
13598 \r
13599                                 return TRUE;\r
13600                         };\r
13601 \r
13602                         // Check if next/prev exists and that they are elements\r
13603                         if (prev && next) {\r
13604                                 function findElementSibling(node, sibling_name) {\r
13605                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
13606                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13607                                                         return node;\r
13608 \r
13609                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13610                                                         return sibling;\r
13611                                         }\r
13612 \r
13613                                         return node;\r
13614                                 };\r
13615 \r
13616                                 // If previous sibling is empty then jump over it\r
13617                                 prev = findElementSibling(prev, 'previousSibling');\r
13618                                 next = findElementSibling(next, 'nextSibling');\r
13619 \r
13620                                 // Compare next and previous nodes\r
13621                                 if (compareElements(prev, next)) {\r
13622                                         // Append nodes between\r
13623                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
13624                                                 tmpSibling = sibling;\r
13625                                                 sibling = sibling.nextSibling;\r
13626                                                 prev.appendChild(tmpSibling);\r
13627                                         }\r
13628 \r
13629                                         // Remove next node\r
13630                                         dom.remove(next);\r
13631 \r
13632                                         // Move children into prev node\r
13633                                         each(tinymce.grep(next.childNodes), function(node) {\r
13634                                                 prev.appendChild(node);\r
13635                                         });\r
13636 \r
13637                                         return prev;\r
13638                                 }\r
13639                         }\r
13640 \r
13641                         return next;\r
13642                 };\r
13643 \r
13644                 function isTextBlock(name) {\r
13645                         return /^(h[1-6]|p|div|pre|address)$/.test(name);\r
13646                 };\r
13647 \r
13648                 function getContainer(rng, start) {\r
13649                         var container, offset, lastIdx;\r
13650 \r
13651                         container = rng[start ? 'startContainer' : 'endContainer'];\r
13652                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
13653 \r
13654                         if (container.nodeType == 1) {\r
13655                                 lastIdx = container.childNodes.length - 1;\r
13656 \r
13657                                 if (!start && offset)\r
13658                                         offset--;\r
13659 \r
13660                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
13661                         }\r
13662 \r
13663                         return container;\r
13664                 };\r
13665 \r
13666                 function performCaretAction(type, name, vars) {\r
13667                         var i, rng, selectedNode = selection.getNode().parentNode,\r
13668                                 doc = ed.getDoc(), marker = 'mceinline',\r
13669                                 events = ['onKeyDown', 'onKeyUp', 'onKeyPress'],\r
13670                                 currentPendingFormats = pendingFormats[type],\r
13671                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
13672 \r
13673                         // Check if it already exists\r
13674                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
13675                                 if (currentPendingFormats[i].name == name)\r
13676                                         return;\r
13677                         }\r
13678 \r
13679                         currentPendingFormats.push({name : name, vars : vars});\r
13680 \r
13681                         // Check if it's in the oter type\r
13682                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
13683                                 if (otherPendingFormats[i].name == name)\r
13684                                         otherPendingFormats.splice(i, 1);\r
13685                         }\r
13686 \r
13687                         function unbind() {\r
13688                                 if (caretHandler) {\r
13689                                         each(events, function(event) {\r
13690                                                 ed[event].remove(caretHandler);\r
13691                                         });\r
13692 \r
13693                                         caretHandler = 0;\r
13694                                 }\r
13695                         };\r
13696 \r
13697                         function perform(caret_node) {\r
13698                                 // Apply pending formats\r
13699                                 each(pendingFormats.apply.reverse(), function(item) {\r
13700                                         apply(item.name, item.vars, caret_node);\r
13701                                 });\r
13702 \r
13703                                 // Remove pending formats\r
13704                                 each(pendingFormats.remove.reverse(), function(item) {\r
13705                                         remove(item.name, item.vars, caret_node);\r
13706                                 });\r
13707 \r
13708                                 dom.remove(caret_node, 1);\r
13709                                 resetPending();\r
13710                         };\r
13711 \r
13712                         function isMarker(node) {\r
13713                                 return node.face == marker || node.style.fontFamily == marker;\r
13714                         };\r
13715 \r
13716                         unbind();\r
13717 \r
13718                         doc.execCommand('FontName', false, marker);\r
13719 \r
13720                         // IE will convert the current word\r
13721                         each(dom.select('font,span', selectedNode), function(node) {\r
13722                                 var bookmark;\r
13723 \r
13724                                 if (isMarker(node)) {\r
13725                                         bookmark = selection.getBookmark();\r
13726                                         perform(node);\r
13727                                         selection.moveToBookmark(bookmark);\r
13728                                         ed.nodeChanged();\r
13729                                         selectedNode = 0;\r
13730                                 }\r
13731                         });\r
13732 \r
13733                         if (selectedNode) {\r
13734                                 caretHandler = function(ed, e) {\r
13735                                         each(dom.select('font,span', selectedNode), function(node) {\r
13736                                                 var bookmark, textNode;\r
13737 \r
13738                                                 // Look for marker\r
13739                                                 if (node.face == marker || node.style.fontFamily == marker) {\r
13740                                                         textNode = node.firstChild;\r
13741 \r
13742                                                         perform(node);\r
13743 \r
13744                                                         rng = dom.createRng();\r
13745                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
13746                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
13747                                                         selection.setRng(rng);\r
13748                                                         ed.nodeChanged();\r
13749 \r
13750                                                         unbind();\r
13751                                                 }\r
13752                                         });\r
13753 \r
13754                                         // Always unbind and clear pending styles on keyup\r
13755                                         if (e.type == 'keyup') {\r
13756                                                 unbind();\r
13757                                                 resetPending();\r
13758                                         }\r
13759                                 };\r
13760 \r
13761                                 each(events, function(event) {\r
13762                                         ed[event].addToTop(caretHandler);\r
13763                                 });\r
13764                         }\r
13765                 }\r
13766         };\r
13767 })(tinymce);\r
13768 \r
13769 tinymce.onAddEditor.add(function(tinymce, ed) {\r
13770         var filters, fontSizes, dom, settings = ed.settings;\r
13771 \r
13772         if (settings.inline_styles) {\r
13773                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
13774 \r
13775                 function replaceWithSpan(node, styles) {\r
13776                         dom.replace(dom.create('span', {\r
13777                                 style : styles\r
13778                         }), node, 1);\r
13779                 };\r
13780 \r
13781                 filters = {\r
13782                         font : function(dom, node) {\r
13783                                 replaceWithSpan(node, {\r
13784                                         backgroundColor : node.style.backgroundColor,\r
13785                                         color : node.color,\r
13786                                         fontFamily : node.face,\r
13787                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
13788                                 });\r
13789                         },\r
13790 \r
13791                         u : function(dom, node) {\r
13792                                 replaceWithSpan(node, {\r
13793                                         textDecoration : 'underline'\r
13794                                 });\r
13795                         },\r
13796 \r
13797                         strike : function(dom, node) {\r
13798                                 replaceWithSpan(node, {\r
13799                                         textDecoration : 'line-through'\r
13800                                 });\r
13801                         }\r
13802                 };\r
13803 \r
13804                 function convert(editor, params) {\r
13805                         dom = editor.dom;\r
13806 \r
13807                         if (settings.convert_fonts_to_spans) {\r
13808                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
13809                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
13810                                 });\r
13811                         }\r
13812                 };\r
13813 \r
13814                 ed.onPreProcess.add(convert);\r
13815 \r
13816                 ed.onInit.add(function() {\r
13817                         ed.selection.onSetContent.add(convert);\r
13818                 });\r
13819         }\r
13820 });\r
13821 \r