]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/TinyMCE/js/tiny_mce_src.js
a26e4ea0047fc8ed431fadb917ac2d6f54ad6625
[quix0rs-gnu-social.git] / plugins / TinyMCE / js / tiny_mce_src.js
1 (function(win) {\r
2         var whiteSpaceRe = /^\s*|\s*$/g,\r
3                 undefined;\r
4 \r
5         win.tinymce = win.tinyMCE = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '3rc1',\r
9 \r
10                 releaseDate : '2010-02-23',\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                 trim : function(s) {\r
133                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
134                 },\r
135 \r
136                 create : function(s, p) {\r
137                         var t = this, sp, ns, cn, scn, c, de = 0;\r
138 \r
139                         // Parse : <prefix> <class>:<super class>\r
140                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
141                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
142 \r
143                         // Create namespace for new class\r
144                         ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
145 \r
146                         // Class already exists\r
147                         if (ns[cn])\r
148                                 return;\r
149 \r
150                         // Make pure static class\r
151                         if (s[2] == 'static') {\r
152                                 ns[cn] = p;\r
153 \r
154                                 if (this.onCreate)\r
155                                         this.onCreate(s[2], s[3], ns[cn]);\r
156 \r
157                                 return;\r
158                         }\r
159 \r
160                         // Create default constructor\r
161                         if (!p[cn]) {\r
162                                 p[cn] = function() {};\r
163                                 de = 1;\r
164                         }\r
165 \r
166                         // Add constructor and methods\r
167                         ns[cn] = p[cn];\r
168                         t.extend(ns[cn].prototype, p);\r
169 \r
170                         // Extend\r
171                         if (s[5]) {\r
172                                 sp = t.resolve(s[5]).prototype;\r
173                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
174 \r
175                                 // Extend constructor\r
176                                 c = ns[cn];\r
177                                 if (de) {\r
178                                         // Add passthrough constructor\r
179                                         ns[cn] = function() {\r
180                                                 return sp[scn].apply(this, arguments);\r
181                                         };\r
182                                 } else {\r
183                                         // Add inherit constructor\r
184                                         ns[cn] = function() {\r
185                                                 this.parent = sp[scn];\r
186                                                 return c.apply(this, arguments);\r
187                                         };\r
188                                 }\r
189                                 ns[cn].prototype[cn] = ns[cn];\r
190 \r
191                                 // Add super methods\r
192                                 t.each(sp, function(f, n) {\r
193                                         ns[cn].prototype[n] = sp[n];\r
194                                 });\r
195 \r
196                                 // Add overridden methods\r
197                                 t.each(p, function(f, n) {\r
198                                         // Extend methods if needed\r
199                                         if (sp[n]) {\r
200                                                 ns[cn].prototype[n] = function() {\r
201                                                         this.parent = sp[n];\r
202                                                         return f.apply(this, arguments);\r
203                                                 };\r
204                                         } else {\r
205                                                 if (n != cn)\r
206                                                         ns[cn].prototype[n] = f;\r
207                                         }\r
208                                 });\r
209                         }\r
210 \r
211                         // Add static methods\r
212                         t.each(p['static'], function(f, n) {\r
213                                 ns[cn][n] = f;\r
214                         });\r
215 \r
216                         if (this.onCreate)\r
217                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
218                 },\r
219 \r
220                 walk : function(o, f, n, s) {\r
221                         s = s || this;\r
222 \r
223                         if (o) {\r
224                                 if (n)\r
225                                         o = o[n];\r
226 \r
227                                 tinymce.each(o, function(o, i) {\r
228                                         if (f.call(s, o, i, n) === false)\r
229                                                 return false;\r
230 \r
231                                         tinymce.walk(o, f, n, s);\r
232                                 });\r
233                         }\r
234                 },\r
235 \r
236                 createNS : function(n, o) {\r
237                         var i, v;\r
238 \r
239                         o = o || window;\r
240 \r
241                         n = n.split('.');\r
242                         for (i=0; i<n.length; i++) {\r
243                                 v = n[i];\r
244 \r
245                                 if (!o[v])\r
246                                         o[v] = {};\r
247 \r
248                                 o = o[v];\r
249                         }\r
250 \r
251                         return o;\r
252                 },\r
253 \r
254                 resolve : function(n, o) {\r
255                         var i, l;\r
256 \r
257                         o = o || win;\r
258 \r
259                         n = n.split('.');\r
260                         for (i = 0, l = n.length; i < l; i++) {\r
261                                 o = o[n[i]];\r
262 \r
263                                 if (!o)\r
264                                         break;\r
265                         }\r
266 \r
267                         return o;\r
268                 },\r
269 \r
270                 addUnload : function(f, s) {\r
271                         var t = this;\r
272 \r
273                         f = {func : f, scope : s || this};\r
274 \r
275                         if (!t.unloads) {\r
276                                 function unload() {\r
277                                         var li = t.unloads, o, n;\r
278 \r
279                                         if (li) {\r
280                                                 // Call unload handlers\r
281                                                 for (n in li) {\r
282                                                         o = li[n];\r
283 \r
284                                                         if (o && o.func)\r
285                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
286                                                 }\r
287 \r
288                                                 // Detach unload function\r
289                                                 if (win.detachEvent) {\r
290                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
291                                                         win.detachEvent('onunload', unload);\r
292                                                 } else if (win.removeEventListener)\r
293                                                         win.removeEventListener('unload', unload, false);\r
294 \r
295                                                 // Destroy references\r
296                                                 t.unloads = o = li = w = unload = 0;\r
297 \r
298                                                 // Run garbarge collector on IE\r
299                                                 if (win.CollectGarbage)\r
300                                                         CollectGarbage();\r
301                                         }\r
302                                 };\r
303 \r
304                                 function fakeUnload() {\r
305                                         var d = document;\r
306 \r
307                                         // Is there things still loading, then do some magic\r
308                                         if (d.readyState == 'interactive') {\r
309                                                 function stop() {\r
310                                                         // Prevent memory leak\r
311                                                         d.detachEvent('onstop', stop);\r
312 \r
313                                                         // Call unload handler\r
314                                                         if (unload)\r
315                                                                 unload();\r
316 \r
317                                                         d = 0;\r
318                                                 };\r
319 \r
320                                                 // Fire unload when the currently loading page is stopped\r
321                                                 if (d)\r
322                                                         d.attachEvent('onstop', stop);\r
323 \r
324                                                 // Remove onstop listener after a while to prevent the unload function\r
325                                                 // to execute if the user presses cancel in an onbeforeunload\r
326                                                 // confirm dialog and then presses the browser stop button\r
327                                                 win.setTimeout(function() {\r
328                                                         if (d)\r
329                                                                 d.detachEvent('onstop', stop);\r
330                                                 }, 0);\r
331                                         }\r
332                                 };\r
333 \r
334                                 // Attach unload handler\r
335                                 if (win.attachEvent) {\r
336                                         win.attachEvent('onunload', unload);\r
337                                         win.attachEvent('onbeforeunload', fakeUnload);\r
338                                 } else if (win.addEventListener)\r
339                                         win.addEventListener('unload', unload, false);\r
340 \r
341                                 // Setup initial unload handler array\r
342                                 t.unloads = [f];\r
343                         } else\r
344                                 t.unloads.push(f);\r
345 \r
346                         return f;\r
347                 },\r
348 \r
349                 removeUnload : function(f) {\r
350                         var u = this.unloads, r = null;\r
351 \r
352                         tinymce.each(u, function(o, i) {\r
353                                 if (o && o.func == f) {\r
354                                         u.splice(i, 1);\r
355                                         r = f;\r
356                                         return false;\r
357                                 }\r
358                         });\r
359 \r
360                         return r;\r
361                 },\r
362 \r
363                 explode : function(s, d) {\r
364                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
365                 },\r
366 \r
367                 _addVer : function(u) {\r
368                         var v;\r
369 \r
370                         if (!this.query)\r
371                                 return u;\r
372 \r
373                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
374 \r
375                         if (u.indexOf('#') == -1)\r
376                                 return u + v;\r
377 \r
378                         return u.replace('#', v + '#');\r
379                 }\r
380 \r
381                 };\r
382 \r
383         // Initialize the API\r
384         tinymce._init();\r
385 })(window);\r
386 \r
387 (function($, tinymce) {\r
388         var is = tinymce.is, attrRegExp = /^(href|src|style)$/i, undefined;\r
389 \r
390         // jQuery is undefined\r
391         if (!$)\r
392                 return alert("Load jQuery first!");\r
393 \r
394         // Stick jQuery into the tinymce namespace\r
395         tinymce.$ = $;\r
396 \r
397         // Setup adapter\r
398         tinymce.adapter = {\r
399                 patchEditor : function(editor) {\r
400                         var fn = $.fn;\r
401 \r
402                         // Adapt the css function to make sure that the _mce_style\r
403                         // attribute gets updated with the new style information\r
404                         function css(name, value) {\r
405                                 var self = this;\r
406 \r
407                                 // Remove _mce_style when set operation occurs\r
408                                 if (value)\r
409                                         self.removeAttr('_mce_style');\r
410 \r
411                                 return fn.css.apply(self, arguments);\r
412                         };\r
413 \r
414                         // Apapt the attr function to make sure that it uses the _mce_ prefixed variants\r
415                         function attr(name, value) {\r
416                                 var self = this;\r
417 \r
418                                 // Update/retrive _mce_ attribute variants\r
419                                 if (attrRegExp.test(name)) {\r
420                                         if (value !== undefined) {\r
421                                                 // Use TinyMCE behavior when setting the specifc attributes\r
422                                                 self.each(function(i, node) {\r
423                                                         editor.dom.setAttrib(node, name, value);\r
424                                                 });\r
425 \r
426                                                 return self;\r
427                                         } else\r
428                                                 return self.attr('_mce_' + name);\r
429                                 }\r
430 \r
431                                 // Default behavior\r
432                                 return fn.attr.apply(self, arguments);\r
433                         };\r
434 \r
435                         function htmlPatchFunc(func) {\r
436                                 // Returns a modified function that processes\r
437                                 // the HTML before executing the action this makes sure\r
438                                 // that href/src etc gets moved into the _mce_ variants\r
439                                 return function(content) {\r
440                                         if (content)\r
441                                                 content = editor.dom.processHTML(content);\r
442 \r
443                                         return func.call(this, content);\r
444                                 };\r
445                         };\r
446 \r
447                         // Patch various jQuery functions to handle tinymce specific attribute and content behavior\r
448                         // we don't patch the jQuery.fn directly since it will most likely break compatibility\r
449                         // with other jQuery logic on the page. Only instances created by TinyMCE should be patched.\r
450                         function patch(jq) {\r
451                                 // Patch some functions, only patch the object once\r
452                                 if (jq.css !== css) {\r
453                                         // Patch css/attr to use the _mce_ prefixed attribute variants\r
454                                         jq.css = css;\r
455                                         jq.attr = attr;\r
456 \r
457                                         // Patch HTML functions to use the DOMUtils.processHTML filter logic\r
458                                         jq.html = htmlPatchFunc(fn.html);\r
459                                         jq.append = htmlPatchFunc(fn.append);\r
460                                         jq.prepend = htmlPatchFunc(fn.prepend);\r
461                                         jq.after = htmlPatchFunc(fn.after);\r
462                                         jq.before = htmlPatchFunc(fn.before);\r
463                                         jq.replaceWith = htmlPatchFunc(fn.replaceWith);\r
464                                         jq.tinymce = editor;\r
465 \r
466                                         // Each pushed jQuery instance needs to be patched\r
467                                         // as well for example when traversing the DOM\r
468                                         jq.pushStack = function() {\r
469                                                 return patch(fn.pushStack.apply(this, arguments));\r
470                                         };\r
471                                 }\r
472 \r
473                                 return jq;\r
474                         };\r
475 \r
476                         // Add a $ function on each editor instance this one is scoped for the editor document object\r
477                         // this way you can do chaining like this tinymce.get(0).$('p').append('text').css('color', 'red');\r
478                         editor.$ = function(selector, scope) {\r
479                                 var doc = editor.getDoc();\r
480 \r
481                                 return patch($(selector || doc, doc || scope));\r
482                         };\r
483                 }\r
484         };\r
485 \r
486         // Patch in core NS functions\r
487         tinymce.extend = $.extend;\r
488         tinymce.extend(tinymce, {\r
489                 map : $.map,\r
490                 grep : function(a, f) {return $.grep(a, f || function(){return 1;});},\r
491                 inArray : function(a, v) {return $.inArray(v, a || []);}\r
492 \r
493                 /* Didn't iterate stylesheets\r
494                 each : function(o, cb, s) {\r
495                         if (!o)\r
496                                 return 0;\r
497 \r
498                         var r = 1;\r
499 \r
500                         $.each(o, function(nr, el){\r
501                                 if (cb.call(s, el, nr, o) === false) {\r
502                                         r = 0;\r
503                                         return false;\r
504                                 }\r
505                         });\r
506 \r
507                         return r;\r
508                 }*/\r
509         });\r
510 \r
511         // Patch in functions in various clases\r
512         // Add a "#ifndefjquery" statement around each core API function you add below\r
513         var patches = {\r
514                 'tinymce.dom.DOMUtils' : {\r
515                         /*\r
516                         addClass : function(e, c) {\r
517                                 if (is(e, 'array') && is(e[0], 'string'))\r
518                                         e = e.join(',#');\r
519                                 return (e && $(is(e, 'string') ? '#' + e : e)\r
520                                         .addClass(c)\r
521                                         .attr('class')) || false;\r
522                         },\r
523 \r
524                         hasClass : function(n, c) {\r
525                                 return $(is(n, 'string') ? '#' + n : n).hasClass(c);\r
526                         },\r
527 \r
528                         removeClass : function(e, c) {\r
529                                 if (!e)\r
530                                         return false;\r
531 \r
532                                 var r = [];\r
533 \r
534                                 $(is(e, 'string') ? '#' + e : e)\r
535                                         .removeClass(c)\r
536                                         .each(function(){\r
537                                                 r.push(this.className);\r
538                                         });\r
539 \r
540                                 return r.length == 1 ? r[0] : r;\r
541                         },\r
542                         */\r
543 \r
544                         select : function(pattern, scope) {\r
545                                 var t = this;\r
546 \r
547                                 return $.find(pattern, t.get(scope) || t.get(t.settings.root_element) || t.doc, []);\r
548                         },\r
549 \r
550                         is : function(n, patt) {\r
551                                 return $(this.get(n)).is(patt);\r
552                         }\r
553 \r
554                         /*\r
555                         show : function(e) {\r
556                                 if (is(e, 'array') && is(e[0], 'string'))\r
557                                         e = e.join(',#');\r
558 \r
559                                 $(is(e, 'string') ? '#' + e : e).css('display', 'block');\r
560                         },\r
561 \r
562                         hide : function(e) {\r
563                                 if (is(e, 'array') && is(e[0], 'string'))\r
564                                         e = e.join(',#');\r
565 \r
566                                 $(is(e, 'string') ? '#' + e : e).css('display', 'none');\r
567                         },\r
568 \r
569                         isHidden : function(e) {\r
570                                 return $(is(e, 'string') ? '#' + e : e).is(':hidden');\r
571                         },\r
572 \r
573                         insertAfter : function(n, e) {\r
574                                 return $(is(e, 'string') ? '#' + e : e).after(n);\r
575                         },\r
576 \r
577                         replace : function(o, n, k) {\r
578                                 n = $(is(n, 'string') ? '#' + n : n);\r
579 \r
580                                 if (k)\r
581                                         n.children().appendTo(o);\r
582 \r
583                                 n.replaceWith(o);\r
584                         },\r
585 \r
586                         setStyle : function(n, na, v) {\r
587                                 if (is(n, 'array') && is(n[0], 'string'))\r
588                                         n = n.join(',#');\r
589 \r
590                                 $(is(n, 'string') ? '#' + n : n).css(na, v);\r
591                         },\r
592 \r
593                         getStyle : function(n, na, c) {\r
594                                 return $(is(n, 'string') ? '#' + n : n).css(na);\r
595                         },\r
596 \r
597                         setStyles : function(e, o) {\r
598                                 if (is(e, 'array') && is(e[0], 'string'))\r
599                                         e = e.join(',#');\r
600                                 $(is(e, 'string') ? '#' + e : e).css(o);\r
601                         },\r
602 \r
603                         setAttrib : function(e, n, v) {\r
604                                 var t = this, s = t.settings;\r
605 \r
606                                 if (is(e, 'array') && is(e[0], 'string'))\r
607                                         e = e.join(',#');\r
608 \r
609                                 e = $(is(e, 'string') ? '#' + e : e);\r
610 \r
611                                 switch (n) {\r
612                                         case "style":\r
613                                                 e.each(function(i, v){\r
614                                                         if (s.keep_values)\r
615                                                                 $(v).attr('_mce_style', v);\r
616 \r
617                                                         v.style.cssText = v;\r
618                                                 });\r
619                                                 break;\r
620 \r
621                                         case "class":\r
622                                                 e.each(function(){\r
623                                                         this.className = v;\r
624                                                 });\r
625                                                 break;\r
626 \r
627                                         case "src":\r
628                                         case "href":\r
629                                                 e.each(function(i, v){\r
630                                                         if (s.keep_values) {\r
631                                                                 if (s.url_converter)\r
632                                                                         v = s.url_converter.call(s.url_converter_scope || t, v, n, v);\r
633 \r
634                                                                 t.setAttrib(v, '_mce_' + n, v);\r
635                                                         }\r
636                                                 });\r
637 \r
638                                                 break;\r
639                                 }\r
640 \r
641                                 if (v !== null && v.length !== 0)\r
642                                         e.attr(n, '' + v);\r
643                                 else\r
644                                         e.removeAttr(n);\r
645                         },\r
646 \r
647                         setAttribs : function(e, o) {\r
648                                 var t = this;\r
649 \r
650                                 $.each(o, function(n, v){\r
651                                         t.setAttrib(e,n,v);\r
652                                 });\r
653                         }\r
654                         */\r
655                 }\r
656 \r
657 /*\r
658                 'tinymce.dom.Event' : {\r
659                         add : function (o, n, f, s) {\r
660                                 var lo, cb;\r
661 \r
662                                 cb = function(e) {\r
663                                         e.target = e.target || this;\r
664                                         f.call(s || this, e);\r
665                                 };\r
666 \r
667                                 if (is(o, 'array') && is(o[0], 'string'))\r
668                                         o = o.join(',#');\r
669                                 o = $(is(o, 'string') ? '#' + o : o);\r
670                                 if (n == 'init') {\r
671                                         o.ready(cb, s);\r
672                                 } else {\r
673                                         if (s) {\r
674                                                 o.bind(n, s, cb);\r
675                                         } else {\r
676                                                 o.bind(n, cb);\r
677                                         }\r
678                                 }\r
679 \r
680                                 lo = this._jqLookup || (this._jqLookup = []);\r
681                                 lo.push({func : f, cfunc : cb});\r
682 \r
683                                 return cb;\r
684                         },\r
685 \r
686                         remove : function(o, n, f) {\r
687                                 // Find cfunc\r
688                                 $(this._jqLookup).each(function() {\r
689                                         if (this.func === f)\r
690                                                 f = this.cfunc;\r
691                                 });\r
692 \r
693                                 if (is(o, 'array') && is(o[0], 'string'))\r
694                                         o = o.join(',#');\r
695 \r
696                                 $(is(o, 'string') ? '#' + o : o).unbind(n,f);\r
697 \r
698                                 return true;\r
699                         }\r
700                 }\r
701 */\r
702         };\r
703 \r
704         // Patch functions after a class is created\r
705         tinymce.onCreate = function(ty, c, p) {\r
706                 tinymce.extend(p, patches[c]);\r
707         };\r
708 })(jQuery, tinymce);\r
709 \r
710 tinymce.create('tinymce.util.Dispatcher', {\r
711         scope : null,\r
712         listeners : null,\r
713 \r
714         Dispatcher : function(s) {\r
715                 this.scope = s || this;\r
716                 this.listeners = [];\r
717         },\r
718 \r
719         add : function(cb, s) {\r
720                 this.listeners.push({cb : cb, scope : s || this.scope});\r
721 \r
722                 return cb;\r
723         },\r
724 \r
725         addToTop : function(cb, s) {\r
726                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
727 \r
728                 return cb;\r
729         },\r
730 \r
731         remove : function(cb) {\r
732                 var l = this.listeners, o = null;\r
733 \r
734                 tinymce.each(l, function(c, i) {\r
735                         if (cb == c.cb) {\r
736                                 o = cb;\r
737                                 l.splice(i, 1);\r
738                                 return false;\r
739                         }\r
740                 });\r
741 \r
742                 return o;\r
743         },\r
744 \r
745         dispatch : function() {\r
746                 var s, a = arguments, i, li = this.listeners, c;\r
747 \r
748                 // Needs to be a real loop since the listener count might change while looping\r
749                 // And this is also more efficient\r
750                 for (i = 0; i<li.length; i++) {\r
751                         c = li[i];\r
752                         s = c.cb.apply(c.scope, a);\r
753 \r
754                         if (s === false)\r
755                                 break;\r
756                 }\r
757 \r
758                 return s;\r
759         }\r
760 \r
761         });\r
762 (function() {\r
763         var each = tinymce.each;\r
764 \r
765         tinymce.create('tinymce.util.URI', {\r
766                 URI : function(u, s) {\r
767                         var t = this, o, a, b;\r
768 \r
769                         // Trim whitespace\r
770                         u = tinymce.trim(u);\r
771 \r
772                         // Default settings\r
773                         s = t.settings = s || {};\r
774 \r
775                         // Strange app protocol or local anchor\r
776                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
777                                 t.source = u;\r
778                                 return;\r
779                         }\r
780 \r
781                         // Absolute path with no host, fake host and protocol\r
782                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
783                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
784 \r
785                         // Relative path http:// or protocol relative //path\r
786                         if (!/^\w*:?\/\//.test(u))\r
787                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
788 \r
789                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
790                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
791                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
792                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
793                                 var s = u[i];\r
794 \r
795                                 // Zope 3 workaround, they use @@something\r
796                                 if (s)\r
797                                         s = s.replace(/\(mce_at\)/g, '@@');\r
798 \r
799                                 t[v] = s;\r
800                         });\r
801 \r
802                         if (b = s.base_uri) {\r
803                                 if (!t.protocol)\r
804                                         t.protocol = b.protocol;\r
805 \r
806                                 if (!t.userInfo)\r
807                                         t.userInfo = b.userInfo;\r
808 \r
809                                 if (!t.port && t.host == 'mce_host')\r
810                                         t.port = b.port;\r
811 \r
812                                 if (!t.host || t.host == 'mce_host')\r
813                                         t.host = b.host;\r
814 \r
815                                 t.source = '';\r
816                         }\r
817 \r
818                         //t.path = t.path || '/';\r
819                 },\r
820 \r
821                 setPath : function(p) {\r
822                         var t = this;\r
823 \r
824                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
825 \r
826                         // Update path parts\r
827                         t.path = p[0];\r
828                         t.directory = p[1];\r
829                         t.file = p[2];\r
830 \r
831                         // Rebuild source\r
832                         t.source = '';\r
833                         t.getURI();\r
834                 },\r
835 \r
836                 toRelative : function(u) {\r
837                         var t = this, o;\r
838 \r
839                         if (u === "./")\r
840                                 return u;\r
841 \r
842                         u = new tinymce.util.URI(u, {base_uri : t});\r
843 \r
844                         // Not on same domain/port or protocol\r
845                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
846                                 return u.getURI();\r
847 \r
848                         o = t.toRelPath(t.path, u.path);\r
849 \r
850                         // Add query\r
851                         if (u.query)\r
852                                 o += '?' + u.query;\r
853 \r
854                         // Add anchor\r
855                         if (u.anchor)\r
856                                 o += '#' + u.anchor;\r
857 \r
858                         return o;\r
859                 },\r
860         \r
861                 toAbsolute : function(u, nh) {\r
862                         var u = new tinymce.util.URI(u, {base_uri : this});\r
863 \r
864                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
865                 },\r
866 \r
867                 toRelPath : function(base, path) {\r
868                         var items, bp = 0, out = '', i, l;\r
869 \r
870                         // Split the paths\r
871                         base = base.substring(0, base.lastIndexOf('/'));\r
872                         base = base.split('/');\r
873                         items = path.split('/');\r
874 \r
875                         if (base.length >= items.length) {\r
876                                 for (i = 0, l = base.length; i < l; i++) {\r
877                                         if (i >= items.length || base[i] != items[i]) {\r
878                                                 bp = i + 1;\r
879                                                 break;\r
880                                         }\r
881                                 }\r
882                         }\r
883 \r
884                         if (base.length < items.length) {\r
885                                 for (i = 0, l = items.length; i < l; i++) {\r
886                                         if (i >= base.length || base[i] != items[i]) {\r
887                                                 bp = i + 1;\r
888                                                 break;\r
889                                         }\r
890                                 }\r
891                         }\r
892 \r
893                         if (bp == 1)\r
894                                 return path;\r
895 \r
896                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
897                                 out += "../";\r
898 \r
899                         for (i = bp - 1, l = items.length; i < l; i++) {\r
900                                 if (i != bp - 1)\r
901                                         out += "/" + items[i];\r
902                                 else\r
903                                         out += items[i];\r
904                         }\r
905 \r
906                         return out;\r
907                 },\r
908 \r
909                 toAbsPath : function(base, path) {\r
910                         var i, nb = 0, o = [], tr, outPath;\r
911 \r
912                         // Split paths\r
913                         tr = /\/$/.test(path) ? '/' : '';\r
914                         base = base.split('/');\r
915                         path = path.split('/');\r
916 \r
917                         // Remove empty chunks\r
918                         each(base, function(k) {\r
919                                 if (k)\r
920                                         o.push(k);\r
921                         });\r
922 \r
923                         base = o;\r
924 \r
925                         // Merge relURLParts chunks\r
926                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
927                                 // Ignore empty or .\r
928                                 if (path[i].length == 0 || path[i] == ".")\r
929                                         continue;\r
930 \r
931                                 // Is parent\r
932                                 if (path[i] == '..') {\r
933                                         nb++;\r
934                                         continue;\r
935                                 }\r
936 \r
937                                 // Move up\r
938                                 if (nb > 0) {\r
939                                         nb--;\r
940                                         continue;\r
941                                 }\r
942 \r
943                                 o.push(path[i]);\r
944                         }\r
945 \r
946                         i = base.length - nb;\r
947 \r
948                         // If /a/b/c or /\r
949                         if (i <= 0)\r
950                                 outPath = o.reverse().join('/');\r
951                         else\r
952                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
953 \r
954                         // Add front / if it's needed\r
955                         if (outPath.indexOf('/') !== 0)\r
956                                 outPath = '/' + outPath;\r
957 \r
958                         // Add traling / if it's needed\r
959                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
960                                 outPath += tr;\r
961 \r
962                         return outPath;\r
963                 },\r
964 \r
965                 getURI : function(nh) {\r
966                         var s, t = this;\r
967 \r
968                         // Rebuild source\r
969                         if (!t.source || nh) {\r
970                                 s = '';\r
971 \r
972                                 if (!nh) {\r
973                                         if (t.protocol)\r
974                                                 s += t.protocol + '://';\r
975 \r
976                                         if (t.userInfo)\r
977                                                 s += t.userInfo + '@';\r
978 \r
979                                         if (t.host)\r
980                                                 s += t.host;\r
981 \r
982                                         if (t.port)\r
983                                                 s += ':' + t.port;\r
984                                 }\r
985 \r
986                                 if (t.path)\r
987                                         s += t.path;\r
988 \r
989                                 if (t.query)\r
990                                         s += '?' + t.query;\r
991 \r
992                                 if (t.anchor)\r
993                                         s += '#' + t.anchor;\r
994 \r
995                                 t.source = s;\r
996                         }\r
997 \r
998                         return t.source;\r
999                 }\r
1000         });\r
1001 })();\r
1002 (function() {\r
1003         var each = tinymce.each;\r
1004 \r
1005         tinymce.create('static tinymce.util.Cookie', {\r
1006                 getHash : function(n) {\r
1007                         var v = this.get(n), h;\r
1008 \r
1009                         if (v) {\r
1010                                 each(v.split('&'), function(v) {\r
1011                                         v = v.split('=');\r
1012                                         h = h || {};\r
1013                                         h[unescape(v[0])] = unescape(v[1]);\r
1014                                 });\r
1015                         }\r
1016 \r
1017                         return h;\r
1018                 },\r
1019 \r
1020                 setHash : function(n, v, e, p, d, s) {\r
1021                         var o = '';\r
1022 \r
1023                         each(v, function(v, k) {\r
1024                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
1025                         });\r
1026 \r
1027                         this.set(n, o, e, p, d, s);\r
1028                 },\r
1029 \r
1030                 get : function(n) {\r
1031                         var c = document.cookie, e, p = n + "=", b;\r
1032 \r
1033                         // Strict mode\r
1034                         if (!c)\r
1035                                 return;\r
1036 \r
1037                         b = c.indexOf("; " + p);\r
1038 \r
1039                         if (b == -1) {\r
1040                                 b = c.indexOf(p);\r
1041 \r
1042                                 if (b != 0)\r
1043                                         return null;\r
1044                         } else\r
1045                                 b += 2;\r
1046 \r
1047                         e = c.indexOf(";", b);\r
1048 \r
1049                         if (e == -1)\r
1050                                 e = c.length;\r
1051 \r
1052                         return unescape(c.substring(b + p.length, e));\r
1053                 },\r
1054 \r
1055                 set : function(n, v, e, p, d, s) {\r
1056                         document.cookie = n + "=" + escape(v) +\r
1057                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
1058                                 ((p) ? "; path=" + escape(p) : "") +\r
1059                                 ((d) ? "; domain=" + d : "") +\r
1060                                 ((s) ? "; secure" : "");\r
1061                 },\r
1062 \r
1063                 remove : function(n, p) {\r
1064                         var d = new Date();\r
1065 \r
1066                         d.setTime(d.getTime() - 1000);\r
1067 \r
1068                         this.set(n, '', d, p, d);\r
1069                 }\r
1070         });\r
1071 })();\r
1072 tinymce.create('static tinymce.util.JSON', {\r
1073         serialize : function(o) {\r
1074                 var i, v, s = tinymce.util.JSON.serialize, t;\r
1075 \r
1076                 if (o == null)\r
1077                         return 'null';\r
1078 \r
1079                 t = typeof o;\r
1080 \r
1081                 if (t == 'string') {\r
1082                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
1083 \r
1084                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
1085                                 i = v.indexOf(b);\r
1086 \r
1087                                 if (i + 1)\r
1088                                         return '\\' + v.charAt(i + 1);\r
1089 \r
1090                                 a = b.charCodeAt().toString(16);\r
1091 \r
1092                                 return '\\u' + '0000'.substring(a.length) + a;\r
1093                         }) + '"';\r
1094                 }\r
1095 \r
1096                 if (t == 'object') {\r
1097                         if (o.hasOwnProperty && o instanceof Array) {\r
1098                                         for (i=0, v = '['; i<o.length; i++)\r
1099                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
1100 \r
1101                                         return v + ']';\r
1102                                 }\r
1103 \r
1104                                 v = '{';\r
1105 \r
1106                                 for (i in o)\r
1107                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
1108 \r
1109                                 return v + '}';\r
1110                 }\r
1111 \r
1112                 return '' + o;\r
1113         },\r
1114 \r
1115         parse : function(s) {\r
1116                 try {\r
1117                         return eval('(' + s + ')');\r
1118                 } catch (ex) {\r
1119                         // Ignore\r
1120                 }\r
1121         }\r
1122 \r
1123         });\r
1124 tinymce.create('static tinymce.util.XHR', {\r
1125         send : function(o) {\r
1126                 var x, t, w = window, c = 0;\r
1127 \r
1128                 // Default settings\r
1129                 o.scope = o.scope || this;\r
1130                 o.success_scope = o.success_scope || o.scope;\r
1131                 o.error_scope = o.error_scope || o.scope;\r
1132                 o.async = o.async === false ? false : true;\r
1133                 o.data = o.data || '';\r
1134 \r
1135                 function get(s) {\r
1136                         x = 0;\r
1137 \r
1138                         try {\r
1139                                 x = new ActiveXObject(s);\r
1140                         } catch (ex) {\r
1141                         }\r
1142 \r
1143                         return x;\r
1144                 };\r
1145 \r
1146                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
1147 \r
1148                 if (x) {\r
1149                         if (x.overrideMimeType)\r
1150                                 x.overrideMimeType(o.content_type);\r
1151 \r
1152                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
1153 \r
1154                         if (o.content_type)\r
1155                                 x.setRequestHeader('Content-Type', o.content_type);\r
1156 \r
1157                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
1158 \r
1159                         x.send(o.data);\r
1160 \r
1161                         function ready() {\r
1162                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
1163                                         if (o.success && c < 10000 && x.status == 200)\r
1164                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
1165                                         else if (o.error)\r
1166                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
1167 \r
1168                                         x = null;\r
1169                                 } else\r
1170                                         w.setTimeout(ready, 10);\r
1171                         };\r
1172 \r
1173                         // Syncronous request\r
1174                         if (!o.async)\r
1175                                 return ready();\r
1176 \r
1177                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
1178                         t = w.setTimeout(ready, 10);\r
1179                 }\r
1180         }\r
1181 });\r
1182 (function() {\r
1183         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
1184 \r
1185         tinymce.create('tinymce.util.JSONRequest', {\r
1186                 JSONRequest : function(s) {\r
1187                         this.settings = extend({\r
1188                         }, s);\r
1189                         this.count = 0;\r
1190                 },\r
1191 \r
1192                 send : function(o) {\r
1193                         var ecb = o.error, scb = o.success;\r
1194 \r
1195                         o = extend(this.settings, o);\r
1196 \r
1197                         o.success = function(c, x) {\r
1198                                 c = JSON.parse(c);\r
1199 \r
1200                                 if (typeof(c) == 'undefined') {\r
1201                                         c = {\r
1202                                                 error : 'JSON Parse error.'\r
1203                                         };\r
1204                                 }\r
1205 \r
1206                                 if (c.error)\r
1207                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
1208                                 else\r
1209                                         scb.call(o.success_scope || o.scope, c.result);\r
1210                         };\r
1211 \r
1212                         o.error = function(ty, x) {\r
1213                                 ecb.call(o.error_scope || o.scope, ty, x);\r
1214                         };\r
1215 \r
1216                         o.data = JSON.serialize({\r
1217                                 id : o.id || 'c' + (this.count++),\r
1218                                 method : o.method,\r
1219                                 params : o.params\r
1220                         });\r
1221 \r
1222                         // JSON content type for Ruby on rails. Bug: #1883287\r
1223                         o.content_type = 'application/json';\r
1224 \r
1225                         XHR.send(o);\r
1226                 },\r
1227 \r
1228                 'static' : {\r
1229                         sendRPC : function(o) {\r
1230                                 return new tinymce.util.JSONRequest().send(o);\r
1231                         }\r
1232                 }\r
1233         });\r
1234 }());(function(tinymce) {\r
1235         // Shorten names\r
1236         var each = tinymce.each,\r
1237                 is = tinymce.is,\r
1238                 isWebKit = tinymce.isWebKit,\r
1239                 isIE = tinymce.isIE,\r
1240                 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
1241                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
1242                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
1243                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
1244                 encodeCharsRe = /[<>&\"]/g,\r
1245                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
1246                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
1247                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
1248 \r
1249         function makeMap(str) {\r
1250                 var map = {}, i;\r
1251 \r
1252                 str = str.split(',');\r
1253                 for (i = str.length; i >= 0; i--)\r
1254                         map[str[i]] = 1;\r
1255 \r
1256                 return map;\r
1257         };\r
1258 \r
1259         tinymce.create('tinymce.dom.DOMUtils', {\r
1260                 doc : null,\r
1261                 root : null,\r
1262                 files : null,\r
1263                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1264                 props : {\r
1265                         "for" : "htmlFor",\r
1266                         "class" : "className",\r
1267                         className : "className",\r
1268                         checked : "checked",\r
1269                         disabled : "disabled",\r
1270                         maxlength : "maxLength",\r
1271                         readonly : "readOnly",\r
1272                         selected : "selected",\r
1273                         value : "value",\r
1274                         id : "id",\r
1275                         name : "name",\r
1276                         type : "type"\r
1277                 },\r
1278 \r
1279                 DOMUtils : function(d, s) {\r
1280                         var t = this, globalStyle;\r
1281 \r
1282                         t.doc = d;\r
1283                         t.win = window;\r
1284                         t.files = {};\r
1285                         t.cssFlicker = false;\r
1286                         t.counter = 0;\r
1287                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
1288                         t.stdMode = d.documentMode === 8;\r
1289 \r
1290                         t.settings = s = tinymce.extend({\r
1291                                 keep_values : false,\r
1292                                 hex_colors : 1,\r
1293                                 process_html : 1\r
1294                         }, s);\r
1295 \r
1296                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1297                         if (tinymce.isIE6) {\r
1298                                 try {\r
1299                                         d.execCommand('BackgroundImageCache', false, true);\r
1300                                 } catch (e) {\r
1301                                         t.cssFlicker = true;\r
1302                                 }\r
1303                         }\r
1304 \r
1305                         // Build styles list\r
1306                         if (s.valid_styles) {\r
1307                                 t._styles = {};\r
1308 \r
1309                                 // Convert styles into a rule list\r
1310                                 each(s.valid_styles, function(value, key) {\r
1311                                         t._styles[key] = tinymce.explode(value);\r
1312                                 });\r
1313                         }\r
1314 \r
1315                         tinymce.addUnload(t.destroy, t);\r
1316                 },\r
1317 \r
1318                 getRoot : function() {\r
1319                         var t = this, s = t.settings;\r
1320 \r
1321                         return (s && t.get(s.root_element)) || t.doc.body;\r
1322                 },\r
1323 \r
1324                 getViewPort : function(w) {\r
1325                         var d, b;\r
1326 \r
1327                         w = !w ? this.win : w;\r
1328                         d = w.document;\r
1329                         b = this.boxModel ? d.documentElement : d.body;\r
1330 \r
1331                         // Returns viewport size excluding scrollbars\r
1332                         return {\r
1333                                 x : w.pageXOffset || b.scrollLeft,\r
1334                                 y : w.pageYOffset || b.scrollTop,\r
1335                                 w : w.innerWidth || b.clientWidth,\r
1336                                 h : w.innerHeight || b.clientHeight\r
1337                         };\r
1338                 },\r
1339 \r
1340                 getRect : function(e) {\r
1341                         var p, t = this, sr;\r
1342 \r
1343                         e = t.get(e);\r
1344                         p = t.getPos(e);\r
1345                         sr = t.getSize(e);\r
1346 \r
1347                         return {\r
1348                                 x : p.x,\r
1349                                 y : p.y,\r
1350                                 w : sr.w,\r
1351                                 h : sr.h\r
1352                         };\r
1353                 },\r
1354 \r
1355                 getSize : function(e) {\r
1356                         var t = this, w, h;\r
1357 \r
1358                         e = t.get(e);\r
1359                         w = t.getStyle(e, 'width');\r
1360                         h = t.getStyle(e, 'height');\r
1361 \r
1362                         // Non pixel value, then force offset/clientWidth\r
1363                         if (w.indexOf('px') === -1)\r
1364                                 w = 0;\r
1365 \r
1366                         // Non pixel value, then force offset/clientWidth\r
1367                         if (h.indexOf('px') === -1)\r
1368                                 h = 0;\r
1369 \r
1370                         return {\r
1371                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1372                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1373                         };\r
1374                 },\r
1375 \r
1376                 getParent : function(n, f, r) {\r
1377                         return this.getParents(n, f, r, false);\r
1378                 },\r
1379 \r
1380                 getParents : function(n, f, r, c) {\r
1381                         var t = this, na, se = t.settings, o = [];\r
1382 \r
1383                         n = t.get(n);\r
1384                         c = c === undefined;\r
1385 \r
1386                         if (se.strict_root)\r
1387                                 r = r || t.getRoot();\r
1388 \r
1389                         // Wrap node name as func\r
1390                         if (is(f, 'string')) {\r
1391                                 na = f;\r
1392 \r
1393                                 if (f === '*') {\r
1394                                         f = function(n) {return n.nodeType == 1;};\r
1395                                 } else {\r
1396                                         f = function(n) {\r
1397                                                 return t.is(n, na);\r
1398                                         };\r
1399                                 }\r
1400                         }\r
1401 \r
1402                         while (n) {\r
1403                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1404                                         break;\r
1405 \r
1406                                 if (!f || f(n)) {\r
1407                                         if (c)\r
1408                                                 o.push(n);\r
1409                                         else\r
1410                                                 return n;\r
1411                                 }\r
1412 \r
1413                                 n = n.parentNode;\r
1414                         }\r
1415 \r
1416                         return c ? o : null;\r
1417                 },\r
1418 \r
1419                 get : function(e) {\r
1420                         var n;\r
1421 \r
1422                         if (e && this.doc && typeof(e) == 'string') {\r
1423                                 n = e;\r
1424                                 e = this.doc.getElementById(e);\r
1425 \r
1426                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1427                                 if (e && e.id !== n)\r
1428                                         return this.doc.getElementsByName(n)[1];\r
1429                         }\r
1430 \r
1431                         return e;\r
1432                 },\r
1433 \r
1434                 getNext : function(node, selector) {\r
1435                         return this._findSib(node, selector, 'nextSibling');\r
1436                 },\r
1437 \r
1438                 getPrev : function(node, selector) {\r
1439                         return this._findSib(node, selector, 'previousSibling');\r
1440                 },\r
1441 \r
1442 \r
1443                 add : function(p, n, a, h, c) {\r
1444                         var t = this;\r
1445 \r
1446                         return this.run(p, function(p) {\r
1447                                 var e, k;\r
1448 \r
1449                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1450                                 t.setAttribs(e, a);\r
1451 \r
1452                                 if (h) {\r
1453                                         if (h.nodeType)\r
1454                                                 e.appendChild(h);\r
1455                                         else\r
1456                                                 t.setHTML(e, h);\r
1457                                 }\r
1458 \r
1459                                 return !c ? p.appendChild(e) : e;\r
1460                         });\r
1461                 },\r
1462 \r
1463                 create : function(n, a, h) {\r
1464                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1465                 },\r
1466 \r
1467                 createHTML : function(n, a, h) {\r
1468                         var o = '', t = this, k;\r
1469 \r
1470                         o += '<' + n;\r
1471 \r
1472                         for (k in a) {\r
1473                                 if (a.hasOwnProperty(k))\r
1474                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1475                         }\r
1476 \r
1477                         if (tinymce.is(h))\r
1478                                 return o + '>' + h + '</' + n + '>';\r
1479 \r
1480                         return o + ' />';\r
1481                 },\r
1482 \r
1483                 remove : function(n, k) {\r
1484                         var t = this;\r
1485 \r
1486                         return this.run(n, function(n) {\r
1487                                 var p, g, i;\r
1488 \r
1489                                 p = n.parentNode;\r
1490 \r
1491                                 if (!p)\r
1492                                         return null;\r
1493 \r
1494                                 if (k) {\r
1495                                         for (i = n.childNodes.length - 1; i >= 0; i--)\r
1496                                                 t.insertAfter(n.childNodes[i], n);\r
1497 \r
1498                                         //each(n.childNodes, function(c) {\r
1499                                         //      p.insertBefore(c.cloneNode(true), n);\r
1500                                         //});\r
1501                                 }\r
1502 \r
1503                                 // Fix IE psuedo leak\r
1504                                 if (t.fixPsuedoLeaks) {\r
1505                                         p = n.cloneNode(true);\r
1506                                         k = 'IELeakGarbageBin';\r
1507                                         g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});\r
1508                                         g.appendChild(n);\r
1509                                         g.innerHTML = '';\r
1510 \r
1511                                         return p;\r
1512                                 }\r
1513 \r
1514                                 return p.removeChild(n);\r
1515                         });\r
1516                 },\r
1517 \r
1518                 setStyle : function(n, na, v) {\r
1519                         var t = this;\r
1520 \r
1521                         return t.run(n, function(e) {\r
1522                                 var s, i;\r
1523 \r
1524                                 s = e.style;\r
1525 \r
1526                                 // Camelcase it, if needed\r
1527                                 na = na.replace(/-(\D)/g, function(a, b){\r
1528                                         return b.toUpperCase();\r
1529                                 });\r
1530 \r
1531                                 // Default px suffix on these\r
1532                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1533                                         v += 'px';\r
1534 \r
1535                                 switch (na) {\r
1536                                         case 'opacity':\r
1537                                                 // IE specific opacity\r
1538                                                 if (isIE) {\r
1539                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1540 \r
1541                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1542                                                                 s.display = 'inline-block';\r
1543                                                 }\r
1544 \r
1545                                                 // Fix for older browsers\r
1546                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1547                                                 break;\r
1548 \r
1549                                         case 'float':\r
1550                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1551                                                 break;\r
1552                                         \r
1553                                         default:\r
1554                                                 s[na] = v || '';\r
1555                                 }\r
1556 \r
1557                                 // Force update of the style data\r
1558                                 if (t.settings.update_styles)\r
1559                                         t.setAttrib(e, '_mce_style');\r
1560                         });\r
1561                 },\r
1562 \r
1563                 getStyle : function(n, na, c) {\r
1564                         n = this.get(n);\r
1565 \r
1566                         if (!n)\r
1567                                 return false;\r
1568 \r
1569                         // Gecko\r
1570                         if (this.doc.defaultView && c) {\r
1571                                 // Remove camelcase\r
1572                                 na = na.replace(/[A-Z]/g, function(a){\r
1573                                         return '-' + a;\r
1574                                 });\r
1575 \r
1576                                 try {\r
1577                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1578                                 } catch (ex) {\r
1579                                         // Old safari might fail\r
1580                                         return null;\r
1581                                 }\r
1582                         }\r
1583 \r
1584                         // Camelcase it, if needed\r
1585                         na = na.replace(/-(\D)/g, function(a, b){\r
1586                                 return b.toUpperCase();\r
1587                         });\r
1588 \r
1589                         if (na == 'float')\r
1590                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1591 \r
1592                         // IE & Opera\r
1593                         if (n.currentStyle && c)\r
1594                                 return n.currentStyle[na];\r
1595 \r
1596                         return n.style[na];\r
1597                 },\r
1598 \r
1599                 setStyles : function(e, o) {\r
1600                         var t = this, s = t.settings, ol;\r
1601 \r
1602                         ol = s.update_styles;\r
1603                         s.update_styles = 0;\r
1604 \r
1605                         each(o, function(v, n) {\r
1606                                 t.setStyle(e, n, v);\r
1607                         });\r
1608 \r
1609                         // Update style info\r
1610                         s.update_styles = ol;\r
1611                         if (s.update_styles)\r
1612                                 t.setAttrib(e, s.cssText);\r
1613                 },\r
1614 \r
1615                 setAttrib : function(e, n, v) {\r
1616                         var t = this;\r
1617 \r
1618                         // Whats the point\r
1619                         if (!e || !n)\r
1620                                 return;\r
1621 \r
1622                         // Strict XML mode\r
1623                         if (t.settings.strict)\r
1624                                 n = n.toLowerCase();\r
1625 \r
1626                         return this.run(e, function(e) {\r
1627                                 var s = t.settings;\r
1628 \r
1629                                 switch (n) {\r
1630                                         case "style":\r
1631                                                 if (!is(v, 'string')) {\r
1632                                                         each(v, function(v, n) {\r
1633                                                                 t.setStyle(e, n, v);\r
1634                                                         });\r
1635 \r
1636                                                         return;\r
1637                                                 }\r
1638 \r
1639                                                 // No mce_style for elements with these since they might get resized by the user\r
1640                                                 if (s.keep_values) {\r
1641                                                         if (v && !t._isRes(v))\r
1642                                                                 e.setAttribute('_mce_style', v, 2);\r
1643                                                         else\r
1644                                                                 e.removeAttribute('_mce_style', 2);\r
1645                                                 }\r
1646 \r
1647                                                 e.style.cssText = v;\r
1648                                                 break;\r
1649 \r
1650                                         case "class":\r
1651                                                 e.className = v || ''; // Fix IE null bug\r
1652                                                 break;\r
1653 \r
1654                                         case "src":\r
1655                                         case "href":\r
1656                                                 if (s.keep_values) {\r
1657                                                         if (s.url_converter)\r
1658                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1659 \r
1660                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1661                                                 }\r
1662 \r
1663                                                 break;\r
1664                                         \r
1665                                         case "shape":\r
1666                                                 e.setAttribute('_mce_style', v);\r
1667                                                 break;\r
1668                                 }\r
1669 \r
1670                                 if (is(v) && v !== null && v.length !== 0)\r
1671                                         e.setAttribute(n, '' + v, 2);\r
1672                                 else\r
1673                                         e.removeAttribute(n, 2);\r
1674                         });\r
1675                 },\r
1676 \r
1677                 setAttribs : function(e, o) {\r
1678                         var t = this;\r
1679 \r
1680                         return this.run(e, function(e) {\r
1681                                 each(o, function(v, n) {\r
1682                                         t.setAttrib(e, n, v);\r
1683                                 });\r
1684                         });\r
1685                 },\r
1686 \r
1687                 getAttrib : function(e, n, dv) {\r
1688                         var v, t = this;\r
1689 \r
1690                         e = t.get(e);\r
1691 \r
1692                         if (!e || e.nodeType !== 1)\r
1693                                 return false;\r
1694 \r
1695                         if (!is(dv))\r
1696                                 dv = '';\r
1697 \r
1698                         // Try the mce variant for these\r
1699                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1700                                 v = e.getAttribute("_mce_" + n);\r
1701 \r
1702                                 if (v)\r
1703                                         return v;\r
1704                         }\r
1705 \r
1706                         if (isIE && t.props[n]) {\r
1707                                 v = e[t.props[n]];\r
1708                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1709                         }\r
1710 \r
1711                         if (!v)\r
1712                                 v = e.getAttribute(n, 2);\r
1713 \r
1714                         // Check boolean attribs\r
1715                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1716                                 if (e[t.props[n]] === true && v === '')\r
1717                                         return n;\r
1718 \r
1719                                 return v ? n : '';\r
1720                         }\r
1721 \r
1722                         // Inner input elements will override attributes on form elements\r
1723                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1724                                 return e.getAttributeNode(n).nodeValue;\r
1725 \r
1726                         if (n === 'style') {\r
1727                                 v = v || e.style.cssText;\r
1728 \r
1729                                 if (v) {\r
1730                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1731 \r
1732                                         if (t.settings.keep_values && !t._isRes(v))\r
1733                                                 e.setAttribute('_mce_style', v);\r
1734                                 }\r
1735                         }\r
1736 \r
1737                         // Remove Apple and WebKit stuff\r
1738                         if (isWebKit && n === "class" && v)\r
1739                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1740 \r
1741                         // Handle IE issues\r
1742                         if (isIE) {\r
1743                                 switch (n) {\r
1744                                         case 'rowspan':\r
1745                                         case 'colspan':\r
1746                                                 // IE returns 1 as default value\r
1747                                                 if (v === 1)\r
1748                                                         v = '';\r
1749 \r
1750                                                 break;\r
1751 \r
1752                                         case 'size':\r
1753                                                 // IE returns +0 as default value for size\r
1754                                                 if (v === '+0' || v === 20 || v === 0)\r
1755                                                         v = '';\r
1756 \r
1757                                                 break;\r
1758 \r
1759                                         case 'width':\r
1760                                         case 'height':\r
1761                                         case 'vspace':\r
1762                                         case 'checked':\r
1763                                         case 'disabled':\r
1764                                         case 'readonly':\r
1765                                                 if (v === 0)\r
1766                                                         v = '';\r
1767 \r
1768                                                 break;\r
1769 \r
1770                                         case 'hspace':\r
1771                                                 // IE returns -1 as default value\r
1772                                                 if (v === -1)\r
1773                                                         v = '';\r
1774 \r
1775                                                 break;\r
1776 \r
1777                                         case 'maxlength':\r
1778                                         case 'tabindex':\r
1779                                                 // IE returns default value\r
1780                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1781                                                         v = '';\r
1782 \r
1783                                                 break;\r
1784 \r
1785                                         case 'multiple':\r
1786                                         case 'compact':\r
1787                                         case 'noshade':\r
1788                                         case 'nowrap':\r
1789                                                 if (v === 65535)\r
1790                                                         return n;\r
1791 \r
1792                                                 return dv;\r
1793 \r
1794                                         case 'shape':\r
1795                                                 v = v.toLowerCase();\r
1796                                                 break;\r
1797 \r
1798                                         default:\r
1799                                                 // IE has odd anonymous function for event attributes\r
1800                                                 if (n.indexOf('on') === 0 && v)\r
1801                                                         v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1802                                 }\r
1803                         }\r
1804 \r
1805                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1806                 },\r
1807 \r
1808                 getPos : function(n, ro) {\r
1809                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1810 \r
1811                         n = t.get(n);\r
1812                         ro = ro || d.body;\r
1813 \r
1814                         if (n) {\r
1815                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1816                                 if (isIE && !t.stdMode) {\r
1817                                         n = n.getBoundingClientRect();\r
1818                                         e = t.boxModel ? d.documentElement : d.body;\r
1819                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1820                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1821                                         n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset\r
1822 \r
1823                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1824                                 }\r
1825 \r
1826                                 r = n;\r
1827                                 while (r && r != ro && r.nodeType) {\r
1828                                         x += r.offsetLeft || 0;\r
1829                                         y += r.offsetTop || 0;\r
1830                                         r = r.offsetParent;\r
1831                                 }\r
1832 \r
1833                                 r = n.parentNode;\r
1834                                 while (r && r != ro && r.nodeType) {\r
1835                                         x -= r.scrollLeft || 0;\r
1836                                         y -= r.scrollTop || 0;\r
1837                                         r = r.parentNode;\r
1838                                 }\r
1839                         }\r
1840 \r
1841                         return {x : x, y : y};\r
1842                 },\r
1843 \r
1844                 parseStyle : function(st) {\r
1845                         var t = this, s = t.settings, o = {};\r
1846 \r
1847                         if (!st)\r
1848                                 return o;\r
1849 \r
1850                         function compress(p, s, ot) {\r
1851                                 var t, r, b, l;\r
1852 \r
1853                                 // Get values and check it it needs compressing\r
1854                                 t = o[p + '-top' + s];\r
1855                                 if (!t)\r
1856                                         return;\r
1857 \r
1858                                 r = o[p + '-right' + s];\r
1859                                 if (t != r)\r
1860                                         return;\r
1861 \r
1862                                 b = o[p + '-bottom' + s];\r
1863                                 if (r != b)\r
1864                                         return;\r
1865 \r
1866                                 l = o[p + '-left' + s];\r
1867                                 if (b != l)\r
1868                                         return;\r
1869 \r
1870                                 // Compress\r
1871                                 o[ot] = l;\r
1872                                 delete o[p + '-top' + s];\r
1873                                 delete o[p + '-right' + s];\r
1874                                 delete o[p + '-bottom' + s];\r
1875                                 delete o[p + '-left' + s];\r
1876                         };\r
1877 \r
1878                         function compress2(ta, a, b, c) {\r
1879                                 var t;\r
1880 \r
1881                                 t = o[a];\r
1882                                 if (!t)\r
1883                                         return;\r
1884 \r
1885                                 t = o[b];\r
1886                                 if (!t)\r
1887                                         return;\r
1888 \r
1889                                 t = o[c];\r
1890                                 if (!t)\r
1891                                         return;\r
1892 \r
1893                                 // Compress\r
1894                                 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1895                                 delete o[a];\r
1896                                 delete o[b];\r
1897                                 delete o[c];\r
1898                         };\r
1899 \r
1900                         st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1901 \r
1902                         each(st.split(';'), function(v) {\r
1903                                 var sv, ur = [];\r
1904 \r
1905                                 if (v) {\r
1906                                         v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1907                                         v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1908                                         v = v.split(':');\r
1909                                         sv = tinymce.trim(v[1]);\r
1910                                         sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1911 \r
1912                                         sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1913                                                 return t.toHex(v);\r
1914                                         });\r
1915 \r
1916                                         if (s.url_converter) {\r
1917                                                 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1918                                                         return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1919                                                 });\r
1920                                         }\r
1921 \r
1922                                         o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1923                                 }\r
1924                         });\r
1925 \r
1926                         compress("border", "", "border");\r
1927                         compress("border", "-width", "border-width");\r
1928                         compress("border", "-color", "border-color");\r
1929                         compress("border", "-style", "border-style");\r
1930                         compress("padding", "", "padding");\r
1931                         compress("margin", "", "margin");\r
1932                         compress2('border', 'border-width', 'border-style', 'border-color');\r
1933 \r
1934                         if (isIE) {\r
1935                                 // Remove pointless border\r
1936                                 if (o.border == 'medium none')\r
1937                                         o.border = '';\r
1938                         }\r
1939 \r
1940                         return o;\r
1941                 },\r
1942 \r
1943                 serializeStyle : function(o, name) {\r
1944                         var t = this, s = '';\r
1945 \r
1946                         function add(v, k) {\r
1947                                 if (k && v) {\r
1948                                         // Remove browser specific styles like -moz- or -webkit-\r
1949                                         if (k.indexOf('-') === 0)\r
1950                                                 return;\r
1951 \r
1952                                         switch (k) {\r
1953                                                 case 'font-weight':\r
1954                                                         // Opera will output bold as 700\r
1955                                                         if (v == 700)\r
1956                                                                 v = 'bold';\r
1957 \r
1958                                                         break;\r
1959 \r
1960                                                 case 'color':\r
1961                                                 case 'background-color':\r
1962                                                         v = v.toLowerCase();\r
1963                                                         break;\r
1964                                         }\r
1965 \r
1966                                         s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1967                                 }\r
1968                         };\r
1969 \r
1970                         // Validate style output\r
1971                         if (name && t._styles) {\r
1972                                 each(t._styles['*'], function(name) {\r
1973                                         add(o[name], name);\r
1974                                 });\r
1975 \r
1976                                 each(t._styles[name.toLowerCase()], function(name) {\r
1977                                         add(o[name], name);\r
1978                                 });\r
1979                         } else\r
1980                                 each(o, add);\r
1981 \r
1982                         return s;\r
1983                 },\r
1984 \r
1985                 loadCSS : function(u) {\r
1986                         var t = this, d = t.doc, head;\r
1987 \r
1988                         if (!u)\r
1989                                 u = '';\r
1990 \r
1991                         head = t.select('head')[0];\r
1992 \r
1993                         each(u.split(','), function(u) {\r
1994                                 var link;\r
1995 \r
1996                                 if (t.files[u])\r
1997                                         return;\r
1998 \r
1999                                 t.files[u] = true;\r
2000                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
2001 \r
2002                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
2003                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
2004                                 // It's ugly but it seems to work fine.\r
2005                                 if (isIE && d.documentMode) {\r
2006                                         link.onload = function() {\r
2007                                                 d.recalc();\r
2008                                                 link.onload = null;\r
2009                                         };\r
2010                                 }\r
2011 \r
2012                                 head.appendChild(link);\r
2013                         });\r
2014                 },\r
2015 \r
2016                 addClass : function(e, c) {\r
2017                         return this.run(e, function(e) {\r
2018                                 var o;\r
2019 \r
2020                                 if (!c)\r
2021                                         return 0;\r
2022 \r
2023                                 if (this.hasClass(e, c))\r
2024                                         return e.className;\r
2025 \r
2026                                 o = this.removeClass(e, c);\r
2027 \r
2028                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
2029                         });\r
2030                 },\r
2031 \r
2032                 removeClass : function(e, c) {\r
2033                         var t = this, re;\r
2034 \r
2035                         return t.run(e, function(e) {\r
2036                                 var v;\r
2037 \r
2038                                 if (t.hasClass(e, c)) {\r
2039                                         if (!re)\r
2040                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
2041 \r
2042                                         v = e.className.replace(re, ' ');\r
2043                                         v = tinymce.trim(v != ' ' ? v : '');\r
2044 \r
2045                                         e.className = v;\r
2046 \r
2047                                         // Empty class attr\r
2048                                         if (!v)\r
2049                                                 e.removeAttribute('class');\r
2050 \r
2051                                         return v;\r
2052                                 }\r
2053 \r
2054                                 return e.className;\r
2055                         });\r
2056                 },\r
2057 \r
2058                 hasClass : function(n, c) {\r
2059                         n = this.get(n);\r
2060 \r
2061                         if (!n || !c)\r
2062                                 return false;\r
2063 \r
2064                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
2065                 },\r
2066 \r
2067                 show : function(e) {\r
2068                         return this.setStyle(e, 'display', 'block');\r
2069                 },\r
2070 \r
2071                 hide : function(e) {\r
2072                         return this.setStyle(e, 'display', 'none');\r
2073                 },\r
2074 \r
2075                 isHidden : function(e) {\r
2076                         e = this.get(e);\r
2077 \r
2078                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
2079                 },\r
2080 \r
2081                 uniqueId : function(p) {\r
2082                         return (!p ? 'mce_' : p) + (this.counter++);\r
2083                 },\r
2084 \r
2085                 setHTML : function(e, h) {\r
2086                         var t = this;\r
2087 \r
2088                         return this.run(e, function(e) {\r
2089                                 var x, i, nl, n, p, x;\r
2090 \r
2091                                 h = t.processHTML(h);\r
2092 \r
2093                                 if (isIE) {\r
2094                                         function set() {\r
2095                                                 // Remove all child nodes\r
2096                                                 while (e.firstChild)\r
2097                                                         e.firstChild.removeNode();\r
2098 \r
2099                                                 try {\r
2100                                                         // IE will remove comments from the beginning\r
2101                                                         // unless you padd the contents with something\r
2102                                                         e.innerHTML = '<br />' + h;\r
2103                                                         e.removeChild(e.firstChild);\r
2104                                                 } catch (ex) {\r
2105                                                         // 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
2106                                                         // This seems to fix this problem\r
2107 \r
2108                                                         // Create new div with HTML contents and a BR infront to keep comments\r
2109                                                         x = t.create('div');\r
2110                                                         x.innerHTML = '<br />' + h;\r
2111 \r
2112                                                         // Add all children from div to target\r
2113                                                         each (x.childNodes, function(n, i) {\r
2114                                                                 // Skip br element\r
2115                                                                 if (i)\r
2116                                                                         e.appendChild(n);\r
2117                                                         });\r
2118                                                 }\r
2119                                         };\r
2120 \r
2121                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
2122                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
2123                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
2124                                         if (t.settings.fix_ie_paragraphs)\r
2125                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
2126 \r
2127                                         set();\r
2128 \r
2129                                         if (t.settings.fix_ie_paragraphs) {\r
2130                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
2131                                                 nl = e.getElementsByTagName("p");\r
2132                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
2133                                                         n = nl[i];\r
2134 \r
2135                                                         if (!n.hasChildNodes()) {\r
2136                                                                 if (!n._mce_keep) {\r
2137                                                                         x = 1; // Is broken\r
2138                                                                         break;\r
2139                                                                 }\r
2140 \r
2141                                                                 n.removeAttribute('_mce_keep');\r
2142                                                         }\r
2143                                                 }\r
2144                                         }\r
2145 \r
2146                                         // Time to fix the madness IE left us\r
2147                                         if (x) {\r
2148                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
2149                                                 // after we use innerHTML we can fix the DOM tree\r
2150                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
2151                                                 h = h.replace(/<\/p>/g, '</div>');\r
2152 \r
2153                                                 // Set the new HTML with DIVs\r
2154                                                 set();\r
2155 \r
2156                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
2157                                                 // This is needed since IE has a annoying bug see above for details\r
2158                                                 // This is a slow process but it has to be done. :(\r
2159                                                 if (t.settings.fix_ie_paragraphs) {\r
2160                                                         nl = e.getElementsByTagName("DIV");\r
2161                                                         for (i = nl.length - 1; i >= 0; i--) {\r
2162                                                                 n = nl[i];\r
2163 \r
2164                                                                 // Is it a temp div\r
2165                                                                 if (n._mce_tmp) {\r
2166                                                                         // Create new paragraph\r
2167                                                                         p = t.doc.createElement('p');\r
2168 \r
2169                                                                         // Copy all attributes\r
2170                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
2171                                                                                 var v;\r
2172 \r
2173                                                                                 if (b !== '_mce_tmp') {\r
2174                                                                                         v = n.getAttribute(b);\r
2175 \r
2176                                                                                         if (!v && b === 'class')\r
2177                                                                                                 v = n.className;\r
2178 \r
2179                                                                                         p.setAttribute(b, v);\r
2180                                                                                 }\r
2181                                                                         });\r
2182 \r
2183                                                                         // Append all children to new paragraph\r
2184                                                                         for (x = 0; x<n.childNodes.length; x++)\r
2185                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
2186 \r
2187                                                                         // Replace div with new paragraph\r
2188                                                                         n.swapNode(p);\r
2189                                                                 }\r
2190                                                         }\r
2191                                                 }\r
2192                                         }\r
2193                                 } else\r
2194                                         e.innerHTML = h;\r
2195 \r
2196                                 return h;\r
2197                         });\r
2198                 },\r
2199 \r
2200                 processHTML : function(h) {\r
2201                         var t = this, s = t.settings, codeBlocks = [];\r
2202 \r
2203                         if (!s.process_html)\r
2204                                 return h;\r
2205 \r
2206                         if (isIE) {\r
2207                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
2208                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
2209                         }\r
2210 \r
2211                         // Fix some issues\r
2212                         h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
2213 \r
2214                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
2215                         if (s.keep_values) {\r
2216                                 // Wrap scripts and styles in comments for serialization purposes\r
2217                                 if (/<script|noscript|style/i.test(h)) {\r
2218                                         function trim(s) {\r
2219                                                 // Remove prefix and suffix code for element\r
2220                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
2221                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
2222                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
2223                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
2224 \r
2225                                                 return s;\r
2226                                         };\r
2227 \r
2228                                         // Wrap the script contents in CDATA and keep them from executing\r
2229                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
2230                                                 // Force type attribute\r
2231                                                 if (!attribs)\r
2232                                                         attribs = ' type="text/javascript"';\r
2233 \r
2234                                                 // Convert the src attribute of the scripts\r
2235                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
2236                                                         if (s.url_converter)\r
2237                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
2238 \r
2239                                                         return '_mce_src="' + url + '"';\r
2240                                                 });\r
2241 \r
2242                                                 // Wrap text contents\r
2243                                                 if (tinymce.trim(text)) {\r
2244                                                         codeBlocks.push(trim(text));\r
2245                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2246                                                 }\r
2247 \r
2248                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2249                                         });\r
2250 \r
2251                                         // Wrap style elements\r
2252                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2253                                                 // Wrap text contents\r
2254                                                 if (text) {\r
2255                                                         codeBlocks.push(trim(text));\r
2256                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2257                                                 }\r
2258 \r
2259                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2260                                         });\r
2261 \r
2262                                         // Wrap noscript elements\r
2263                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2264                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2265                                         });\r
2266                                 }\r
2267 \r
2268                                 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
2269 \r
2270                                 // This function processes the attributes in the HTML string to force boolean\r
2271                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2272                                 function processTags(html) {\r
2273                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2274                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2275                                                         var mceValue;\r
2276 \r
2277                                                         name = name.toLowerCase();\r
2278                                                         value = value || val2 || val3 || "";\r
2279 \r
2280                                                         // Treat boolean attributes\r
2281                                                         if (boolAttrs[name]) {\r
2282                                                                 // false or 0 is treated as a missing attribute\r
2283                                                                 if (value === 'false' || value === '0')\r
2284                                                                         return;\r
2285 \r
2286                                                                 return name + '="' + name + '"';\r
2287                                                         }\r
2288 \r
2289                                                         // Is attribute one that needs special treatment\r
2290                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2291                                                                 mceValue = t.decode(value);\r
2292 \r
2293                                                                 // Convert URLs to relative/absolute ones\r
2294                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2295                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2296 \r
2297                                                                 // Process styles lowercases them and compresses them\r
2298                                                                 if (name == 'style')\r
2299                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2300 \r
2301                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2302                                                         }\r
2303 \r
2304                                                         return match;\r
2305                                                 }) + end + '>';\r
2306                                         });\r
2307                                 };\r
2308 \r
2309                                 h = processTags(h);\r
2310 \r
2311                                 // Restore script blocks\r
2312                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2313                                         return codeBlocks[idx];\r
2314                                 });\r
2315                         }\r
2316 \r
2317                         return h;\r
2318                 },\r
2319 \r
2320                 getOuterHTML : function(e) {\r
2321                         var d;\r
2322 \r
2323                         e = this.get(e);\r
2324 \r
2325                         if (!e)\r
2326                                 return null;\r
2327 \r
2328                         if (e.outerHTML !== undefined)\r
2329                                 return e.outerHTML;\r
2330 \r
2331                         d = (e.ownerDocument || this.doc).createElement("body");\r
2332                         d.appendChild(e.cloneNode(true));\r
2333 \r
2334                         return d.innerHTML;\r
2335                 },\r
2336 \r
2337                 setOuterHTML : function(e, h, d) {\r
2338                         var t = this;\r
2339 \r
2340                         function setHTML(e, h, d) {\r
2341                                 var n, tp;\r
2342 \r
2343                                 tp = d.createElement("body");\r
2344                                 tp.innerHTML = h;\r
2345 \r
2346                                 n = tp.lastChild;\r
2347                                 while (n) {\r
2348                                         t.insertAfter(n.cloneNode(true), e);\r
2349                                         n = n.previousSibling;\r
2350                                 }\r
2351 \r
2352                                 t.remove(e);\r
2353                         };\r
2354 \r
2355                         return this.run(e, function(e) {\r
2356                                 e = t.get(e);\r
2357 \r
2358                                 // Only set HTML on elements\r
2359                                 if (e.nodeType == 1) {\r
2360                                         d = d || e.ownerDocument || t.doc;\r
2361 \r
2362                                         if (isIE) {\r
2363                                                 try {\r
2364                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2365                                                         if (isIE && e.nodeType == 1)\r
2366                                                                 e.outerHTML = h;\r
2367                                                         else\r
2368                                                                 setHTML(e, h, d);\r
2369                                                 } catch (ex) {\r
2370                                                         // Fix for unknown runtime error\r
2371                                                         setHTML(e, h, d);\r
2372                                                 }\r
2373                                         } else\r
2374                                                 setHTML(e, h, d);\r
2375                                 }\r
2376                         });\r
2377                 },\r
2378 \r
2379                 decode : function(s) {\r
2380                         var e, n, v;\r
2381 \r
2382                         // Look for entities to decode\r
2383                         if (/&[\w#]+;/.test(s)) {\r
2384                                 // Decode the entities using a div element not super efficient but less code\r
2385                                 e = this.doc.createElement("div");\r
2386                                 e.innerHTML = s;\r
2387                                 n = e.firstChild;\r
2388                                 v = '';\r
2389 \r
2390                                 if (n) {\r
2391                                         do {\r
2392                                                 v += n.nodeValue;\r
2393                                         } while (n = n.nextSibling);\r
2394                                 }\r
2395 \r
2396                                 return v || s;\r
2397                         }\r
2398 \r
2399                         return s;\r
2400                 },\r
2401 \r
2402                 encode : function(str) {\r
2403                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2404                                 return encodedChars[chr];\r
2405                         });\r
2406                 },\r
2407 \r
2408                 insertAfter : function(n, r) {\r
2409                         var t = this;\r
2410 \r
2411                         r = t.get(r);\r
2412 \r
2413                         return this.run(n, function(n) {\r
2414                                 var p, ns;\r
2415 \r
2416                                 p = r.parentNode;\r
2417                                 ns = r.nextSibling;\r
2418 \r
2419                                 if (ns)\r
2420                                         p.insertBefore(n, ns);\r
2421                                 else\r
2422                                         p.appendChild(n);\r
2423 \r
2424                                 return n;\r
2425                         });\r
2426                 },\r
2427 \r
2428                 isBlock : function(n) {\r
2429                         if (n.nodeType && n.nodeType !== 1)\r
2430                                 return false;\r
2431 \r
2432                         n = n.nodeName || n;\r
2433 \r
2434                         return blockRe.test(n);\r
2435                 },\r
2436 \r
2437                 replace : function(n, o, k) {\r
2438                         var t = this;\r
2439 \r
2440                         if (is(o, 'array'))\r
2441                                 n = n.cloneNode(true);\r
2442 \r
2443                         return t.run(o, function(o) {\r
2444                                 if (k) {\r
2445                                         each(tinymce.grep(o.childNodes), function(c) {\r
2446                                                 n.appendChild(c);\r
2447                                         });\r
2448                                 }\r
2449 \r
2450                                 // Fix IE psuedo leak for elements since replacing elements if fairly common\r
2451                                 // Will break parentNode for some unknown reason\r
2452                                 if (t.fixPsuedoLeaks && o.nodeType === 1) {\r
2453                                         o.parentNode.insertBefore(n, o);\r
2454                                         t.remove(o);\r
2455                                         return n;\r
2456                                 }\r
2457 \r
2458                                 return o.parentNode.replaceChild(n, o);\r
2459                         });\r
2460                 },\r
2461 \r
2462                 rename : function(elm, name) {\r
2463                         var t = this, newElm;\r
2464 \r
2465                         if (elm.nodeName != name.toUpperCase()) {\r
2466                                 // Rename block element\r
2467                                 newElm = t.create(name);\r
2468 \r
2469                                 // Copy attribs to new block\r
2470                                 each(t.getAttribs(elm), function(attr_node) {\r
2471                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2472                                 });\r
2473 \r
2474                                 // Replace block\r
2475                                 t.replace(newElm, elm, 1);\r
2476                         }\r
2477 \r
2478                         return newElm || elm;\r
2479                 },\r
2480 \r
2481                 findCommonAncestor : function(a, b) {\r
2482                         var ps = a, pe;\r
2483 \r
2484                         while (ps) {\r
2485                                 pe = b;\r
2486 \r
2487                                 while (pe && ps != pe)\r
2488                                         pe = pe.parentNode;\r
2489 \r
2490                                 if (ps == pe)\r
2491                                         break;\r
2492 \r
2493                                 ps = ps.parentNode;\r
2494                         }\r
2495 \r
2496                         if (!ps && a.ownerDocument)\r
2497                                 return a.ownerDocument.documentElement;\r
2498 \r
2499                         return ps;\r
2500                 },\r
2501 \r
2502                 toHex : function(s) {\r
2503                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2504 \r
2505                         function hex(s) {\r
2506                                 s = parseInt(s).toString(16);\r
2507 \r
2508                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2509                         };\r
2510 \r
2511                         if (c) {\r
2512                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2513 \r
2514                                 return s;\r
2515                         }\r
2516 \r
2517                         return s;\r
2518                 },\r
2519 \r
2520                 getClasses : function() {\r
2521                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2522 \r
2523                         if (t.classes)\r
2524                                 return t.classes;\r
2525 \r
2526                         function addClasses(s) {\r
2527                                 // IE style imports\r
2528                                 each(s.imports, function(r) {\r
2529                                         addClasses(r);\r
2530                                 });\r
2531 \r
2532                                 each(s.cssRules || s.rules, function(r) {\r
2533                                         // Real type or fake it on IE\r
2534                                         switch (r.type || 1) {\r
2535                                                 // Rule\r
2536                                                 case 1:\r
2537                                                         if (r.selectorText) {\r
2538                                                                 each(r.selectorText.split(','), function(v) {\r
2539                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2540 \r
2541                                                                         // Is internal or it doesn't contain a class\r
2542                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2543                                                                                 return;\r
2544 \r
2545                                                                         // Remove everything but class name\r
2546                                                                         ov = v;\r
2547                                                                         v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2548 \r
2549                                                                         // Filter classes\r
2550                                                                         if (f && !(v = f(v, ov)))\r
2551                                                                                 return;\r
2552 \r
2553                                                                         if (!lo[v]) {\r
2554                                                                                 cl.push({'class' : v});\r
2555                                                                                 lo[v] = 1;\r
2556                                                                         }\r
2557                                                                 });\r
2558                                                         }\r
2559                                                         break;\r
2560 \r
2561                                                 // Import\r
2562                                                 case 3:\r
2563                                                         addClasses(r.styleSheet);\r
2564                                                         break;\r
2565                                         }\r
2566                                 });\r
2567                         };\r
2568 \r
2569                         try {\r
2570                                 each(t.doc.styleSheets, addClasses);\r
2571                         } catch (ex) {\r
2572                                 // Ignore\r
2573                         }\r
2574 \r
2575                         if (cl.length > 0)\r
2576                                 t.classes = cl;\r
2577 \r
2578                         return cl;\r
2579                 },\r
2580 \r
2581                 run : function(e, f, s) {\r
2582                         var t = this, o;\r
2583 \r
2584                         if (t.doc && typeof(e) === 'string')\r
2585                                 e = t.get(e);\r
2586 \r
2587                         if (!e)\r
2588                                 return false;\r
2589 \r
2590                         s = s || this;\r
2591                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2592                                 o = [];\r
2593 \r
2594                                 each(e, function(e, i) {\r
2595                                         if (e) {\r
2596                                                 if (typeof(e) == 'string')\r
2597                                                         e = t.doc.getElementById(e);\r
2598 \r
2599                                                 o.push(f.call(s, e, i));\r
2600                                         }\r
2601                                 });\r
2602 \r
2603                                 return o;\r
2604                         }\r
2605 \r
2606                         return f.call(s, e);\r
2607                 },\r
2608 \r
2609                 getAttribs : function(n) {\r
2610                         var o;\r
2611 \r
2612                         n = this.get(n);\r
2613 \r
2614                         if (!n)\r
2615                                 return [];\r
2616 \r
2617                         if (isIE) {\r
2618                                 o = [];\r
2619 \r
2620                                 // Object will throw exception in IE\r
2621                                 if (n.nodeName == 'OBJECT')\r
2622                                         return n.attributes;\r
2623 \r
2624                                 // IE doesn't keep the selected attribute if you clone option elements\r
2625                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2626                                         o.push({specified : 1, nodeName : 'selected'});\r
2627 \r
2628                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2629                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2630                                         o.push({specified : 1, nodeName : a});\r
2631                                 });\r
2632 \r
2633                                 return o;\r
2634                         }\r
2635 \r
2636                         return n.attributes;\r
2637                 },\r
2638 \r
2639                 destroy : function(s) {\r
2640                         var t = this;\r
2641 \r
2642                         if (t.events)\r
2643                                 t.events.destroy();\r
2644 \r
2645                         t.win = t.doc = t.root = t.events = null;\r
2646 \r
2647                         // Manual destroy then remove unload handler\r
2648                         if (!s)\r
2649                                 tinymce.removeUnload(t.destroy);\r
2650                 },\r
2651 \r
2652                 createRng : function() {\r
2653                         var d = this.doc;\r
2654 \r
2655                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2656                 },\r
2657 \r
2658                 nodeIndex : function(node, normalized) {\r
2659                         var idx = 0, lastNode, nodeType;\r
2660 \r
2661                         if (node) {\r
2662                                 for (node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2663                                         nodeType = node.nodeType;\r
2664 \r
2665                                         // Text nodes needs special treatment if the normalized argument is specified\r
2666                                         if (normalized && nodeType == 3) {\r
2667                                                 // Checks if the current node has contents and that the last node is a non text node or empty\r
2668                                                 if (node.nodeValue.length > 0 && (lastNode.nodeType != nodeType || lastNode.nodeValue.length === 0))\r
2669                                                         idx++;\r
2670                                         } else\r
2671                                                 idx++;\r
2672 \r
2673                                         lastNode = node;\r
2674                                 }\r
2675                         }\r
2676 \r
2677                         return idx;\r
2678                 },\r
2679 \r
2680                 split : function(pe, e, re) {\r
2681                         var t = this, r = t.createRng(), bef, aft, pa;\r
2682 \r
2683                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2684                         // but we don't want that in our code since it serves no purpose for the end user\r
2685                         // For example if this is chopped:\r
2686                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2687                         // would produce:\r
2688                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2689                         // this function will then trim of empty edges and produce:\r
2690                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2691                         function trim(node) {\r
2692                                 var i, children = node.childNodes;\r
2693 \r
2694                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2695                                         return;\r
2696 \r
2697                                 for (i = children.length - 1; i >= 0; i--)\r
2698                                         trim(children[i]);\r
2699 \r
2700                                 if (node.nodeType != 9) {\r
2701                                         // Keep non whitespace text nodes\r
2702                                         if (node.nodeType == 3 && node.nodeValue.length > 0)\r
2703                                                 return;\r
2704 \r
2705                                         if (node.nodeType == 1) {\r
2706                                                 // If the only child is a bookmark then move it up\r
2707                                                 children = node.childNodes;\r
2708                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2709                                                         node.parentNode.insertBefore(children[0], node);\r
2710 \r
2711                                                 // Keep non empty elements or img, hr etc\r
2712                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2713                                                         return;\r
2714                                         }\r
2715 \r
2716                                         t.remove(node);\r
2717                                 }\r
2718 \r
2719                                 return node;\r
2720                         };\r
2721 \r
2722                         if (pe && e) {\r
2723                                 // Get before chunk\r
2724                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2725                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2726                                 bef = r.extractContents();\r
2727 \r
2728                                 // Get after chunk\r
2729                                 r = t.createRng();\r
2730                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2731                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2732                                 aft = r.extractContents();\r
2733 \r
2734                                 // Insert before chunk\r
2735                                 pa = pe.parentNode;\r
2736                                 pa.insertBefore(trim(bef), pe);\r
2737 \r
2738                                 // Insert middle chunk\r
2739                                 if (re)\r
2740                                         pa.replaceChild(re, e);\r
2741                                 else\r
2742                                         pa.insertBefore(e, pe);\r
2743 \r
2744                                 // Insert after chunk\r
2745                                 pa.insertBefore(trim(aft), pe);\r
2746                                 t.remove(pe);\r
2747 \r
2748                                 return re || e;\r
2749                         }\r
2750                 },\r
2751 \r
2752                 bind : function(target, name, func, scope) {\r
2753                         var t = this;\r
2754 \r
2755                         if (!t.events)\r
2756                                 t.events = new tinymce.dom.EventUtils();\r
2757 \r
2758                         return t.events.add(target, name, func, scope || this);\r
2759                 },\r
2760 \r
2761                 unbind : function(target, name, func) {\r
2762                         var t = this;\r
2763 \r
2764                         if (!t.events)\r
2765                                 t.events = new tinymce.dom.EventUtils();\r
2766 \r
2767                         return t.events.remove(target, name, func);\r
2768                 },\r
2769 \r
2770 \r
2771                 _findSib : function(node, selector, name) {\r
2772                         var t = this, f = selector;\r
2773 \r
2774                         if (node) {\r
2775                                 // If expression make a function of it using is\r
2776                                 if (is(f, 'string')) {\r
2777                                         f = function(node) {\r
2778                                                 return t.is(node, selector);\r
2779                                         };\r
2780                                 }\r
2781 \r
2782                                 // Loop all siblings\r
2783                                 for (node = node[name]; node; node = node[name]) {\r
2784                                         if (f(node))\r
2785                                                 return node;\r
2786                                 }\r
2787                         }\r
2788 \r
2789                         return null;\r
2790                 },\r
2791 \r
2792                 _isRes : function(c) {\r
2793                         // Is live resizble element\r
2794                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2795                 }\r
2796 \r
2797                 /*\r
2798                 walk : function(n, f, s) {\r
2799                         var d = this.doc, w;\r
2800 \r
2801                         if (d.createTreeWalker) {\r
2802                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2803 \r
2804                                 while ((n = w.nextNode()) != null)\r
2805                                         f.call(s || this, n);\r
2806                         } else\r
2807                                 tinymce.walk(n, f, 'childNodes', s);\r
2808                 }\r
2809                 */\r
2810 \r
2811                 /*\r
2812                 toRGB : function(s) {\r
2813                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2814 \r
2815                         if (c) {\r
2816                                 // #FFF -> #FFFFFF\r
2817                                 if (!is(c[3]))\r
2818                                         c[3] = c[2] = c[1];\r
2819 \r
2820                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2821                         }\r
2822 \r
2823                         return s;\r
2824                 }\r
2825                 */\r
2826         });\r
2827 \r
2828         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2829 })(tinymce);\r
2830 (function(ns) {\r
2831         // Range constructor\r
2832         function Range(dom) {\r
2833                 var t = this,\r
2834                         doc = dom.doc,\r
2835                         EXTRACT = 0,\r
2836                         CLONE = 1,\r
2837                         DELETE = 2,\r
2838                         TRUE = true,\r
2839                         FALSE = false,\r
2840                         START_OFFSET = 'startOffset',\r
2841                         START_CONTAINER = 'startContainer',\r
2842                         END_CONTAINER = 'endContainer',\r
2843                         END_OFFSET = 'endOffset',\r
2844                         extend = tinymce.extend,\r
2845                         nodeIndex = dom.nodeIndex;\r
2846 \r
2847                 extend(t, {\r
2848                         // Inital states\r
2849                         startContainer : doc,\r
2850                         startOffset : 0,\r
2851                         endContainer : doc,\r
2852                         endOffset : 0,\r
2853                         collapsed : TRUE,\r
2854                         commonAncestorContainer : doc,\r
2855 \r
2856                         // Range constants\r
2857                         START_TO_START : 0,\r
2858                         START_TO_END : 1,\r
2859                         END_TO_END : 2,\r
2860                         END_TO_START : 3,\r
2861 \r
2862                         // Public methods\r
2863                         setStart : setStart,\r
2864                         setEnd : setEnd,\r
2865                         setStartBefore : setStartBefore,\r
2866                         setStartAfter : setStartAfter,\r
2867                         setEndBefore : setEndBefore,\r
2868                         setEndAfter : setEndAfter,\r
2869                         collapse : collapse,\r
2870                         selectNode : selectNode,\r
2871                         selectNodeContents : selectNodeContents,\r
2872                         compareBoundaryPoints : compareBoundaryPoints,\r
2873                         deleteContents : deleteContents,\r
2874                         extractContents : extractContents,\r
2875                         cloneContents : cloneContents,\r
2876                         insertNode : insertNode,\r
2877                         surroundContents : surroundContents,\r
2878                         cloneRange : cloneRange\r
2879                 });\r
2880 \r
2881                 function setStart(n, o) {\r
2882                         _setEndPoint(TRUE, n, o);\r
2883                 };\r
2884 \r
2885                 function setEnd(n, o) {\r
2886                         _setEndPoint(FALSE, n, o);\r
2887                 };\r
2888 \r
2889                 function setStartBefore(n) {\r
2890                         setStart(n.parentNode, nodeIndex(n));\r
2891                 };\r
2892 \r
2893                 function setStartAfter(n) {\r
2894                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2895                 };\r
2896 \r
2897                 function setEndBefore(n) {\r
2898                         setEnd(n.parentNode, nodeIndex(n));\r
2899                 };\r
2900 \r
2901                 function setEndAfter(n) {\r
2902                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2903                 };\r
2904 \r
2905                 function collapse(ts) {\r
2906                         if (ts) {\r
2907                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2908                                 t[END_OFFSET] = t[START_OFFSET];\r
2909                         } else {\r
2910                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2911                                 t[START_OFFSET] = t[END_OFFSET];\r
2912                         }\r
2913 \r
2914                         t.collapsed = TRUE;\r
2915                 };\r
2916 \r
2917                 function selectNode(n) {\r
2918                         setStartBefore(n);\r
2919                         setEndAfter(n);\r
2920                 };\r
2921 \r
2922                 function selectNodeContents(n) {\r
2923                         setStart(n, 0);\r
2924                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2925                 };\r
2926 \r
2927                 function compareBoundaryPoints(h, r) {\r
2928                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2929 \r
2930                         // Check START_TO_START\r
2931                         if (h === 0)\r
2932                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2933 \r
2934                         // Check START_TO_END\r
2935                         if (h === 1)\r
2936                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2937 \r
2938                         // Check END_TO_END\r
2939                         if (h === 2)\r
2940                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2941 \r
2942                         // Check END_TO_START\r
2943                         if (h === 3)\r
2944                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2945                 };\r
2946 \r
2947                 function deleteContents() {\r
2948                         _traverse(DELETE);\r
2949                 };\r
2950 \r
2951                 function extractContents() {\r
2952                         return _traverse(EXTRACT);\r
2953                 };\r
2954 \r
2955                 function cloneContents() {\r
2956                         return _traverse(CLONE);\r
2957                 };\r
2958 \r
2959                 function insertNode(n) {\r
2960                         var startContainer = this[START_CONTAINER],\r
2961                                 startOffset = this[START_OFFSET], nn, o;\r
2962 \r
2963                         // Node is TEXT_NODE or CDATA\r
2964                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2965                                 if (!startOffset) {\r
2966                                         // At the start of text\r
2967                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2968                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2969                                         // At the end of text\r
2970                                         dom.insertAfter(n, startContainer);\r
2971                                 } else {\r
2972                                         // Middle, need to split\r
2973                                         nn = startContainer.splitText(startOffset);\r
2974                                         startContainer.parentNode.insertBefore(n, nn);\r
2975                                 }\r
2976                         } else {\r
2977                                 // Insert element node\r
2978                                 if (startContainer.childNodes.length > 0)\r
2979                                         o = startContainer.childNodes[startOffset];\r
2980 \r
2981                                 if (o)\r
2982                                         startContainer.insertBefore(n, o);\r
2983                                 else\r
2984                                         startContainer.appendChild(n);\r
2985                         }\r
2986                 };\r
2987 \r
2988                 function surroundContents(n) {\r
2989                         var f = t.extractContents();\r
2990 \r
2991                         t.insertNode(n);\r
2992                         n.appendChild(f);\r
2993                         t.selectNode(n);\r
2994                 };\r
2995 \r
2996                 function cloneRange() {\r
2997                         return extend(new Range(dom), {\r
2998                                 startContainer : t[START_CONTAINER],\r
2999                                 startOffset : t[START_OFFSET],\r
3000                                 endContainer : t[END_CONTAINER],\r
3001                                 endOffset : t[END_OFFSET],\r
3002                                 collapsed : t.collapsed,\r
3003                                 commonAncestorContainer : t.commonAncestorContainer\r
3004                         });\r
3005                 };\r
3006 \r
3007                 // Private methods\r
3008 \r
3009                 function _getSelectedNode(container, offset) {\r
3010                         var child;\r
3011 \r
3012                         if (container.nodeType == 3 /* TEXT_NODE */)\r
3013                                 return container;\r
3014 \r
3015                         if (offset < 0)\r
3016                                 return container;\r
3017 \r
3018                         child = container.firstChild;\r
3019                         while (child && offset > 0) {\r
3020                                 --offset;\r
3021                                 child = child.nextSibling;\r
3022                         }\r
3023 \r
3024                         if (child)\r
3025                                 return child;\r
3026 \r
3027                         return container;\r
3028                 };\r
3029 \r
3030                 function _isCollapsed() {\r
3031                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
3032                 };\r
3033 \r
3034                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
3035                         var c, offsetC, n, cmnRoot, childA, childB;\r
3036 \r
3037                         // In the first case the boundary-points have the same container. A is before B\r
3038                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
3039                         // equal to the offset of B, and A is after B if its offset is greater than the\r
3040                         // offset of B.\r
3041                         if (containerA == containerB) {\r
3042                                 if (offsetA == offsetB)\r
3043                                         return 0; // equal\r
3044 \r
3045                                 if (offsetA < offsetB)\r
3046                                         return -1; // before\r
3047 \r
3048                                 return 1; // after\r
3049                         }\r
3050 \r
3051                         // In the second case a child node C of the container of A is an ancestor\r
3052                         // container of B. In this case, A is before B if the offset of A is less than or\r
3053                         // equal to the index of the child node C and A is after B otherwise.\r
3054                         c = containerB;\r
3055                         while (c && c.parentNode != containerA)\r
3056                                 c = c.parentNode;\r
3057 \r
3058                         if (c) {\r
3059                                 offsetC = 0;\r
3060                                 n = containerA.firstChild;\r
3061 \r
3062                                 while (n != c && offsetC < offsetA) {\r
3063                                         offsetC++;\r
3064                                         n = n.nextSibling;\r
3065                                 }\r
3066 \r
3067                                 if (offsetA <= offsetC)\r
3068                                         return -1; // before\r
3069 \r
3070                                 return 1; // after\r
3071                         }\r
3072 \r
3073                         // In the third case a child node C of the container of B is an ancestor container\r
3074                         // of A. In this case, A is before B if the index of the child node C is less than\r
3075                         // the offset of B and A is after B otherwise.\r
3076                         c = containerA;\r
3077                         while (c && c.parentNode != containerB) {\r
3078                                 c = c.parentNode;\r
3079                         }\r
3080 \r
3081                         if (c) {\r
3082                                 offsetC = 0;\r
3083                                 n = containerB.firstChild;\r
3084 \r
3085                                 while (n != c && offsetC < offsetB) {\r
3086                                         offsetC++;\r
3087                                         n = n.nextSibling;\r
3088                                 }\r
3089 \r
3090                                 if (offsetC < offsetB)\r
3091                                         return -1; // before\r
3092 \r
3093                                 return 1; // after\r
3094                         }\r
3095 \r
3096                         // In the fourth case, none of three other cases hold: the containers of A and B\r
3097                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
3098                         // the container of A is before the container of B in a pre-order traversal of the\r
3099                         // Ranges' context tree and A is after B otherwise.\r
3100                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
3101                         childA = containerA;\r
3102 \r
3103                         while (childA && childA.parentNode != cmnRoot)\r
3104                                 childA = childA.parentNode;\r
3105 \r
3106                         if (!childA)\r
3107                                 childA = cmnRoot;\r
3108 \r
3109                         childB = containerB;\r
3110                         while (childB && childB.parentNode != cmnRoot)\r
3111                                 childB = childB.parentNode;\r
3112 \r
3113                         if (!childB)\r
3114                                 childB = cmnRoot;\r
3115 \r
3116                         if (childA == childB)\r
3117                                 return 0; // equal\r
3118 \r
3119                         n = cmnRoot.firstChild;\r
3120                         while (n) {\r
3121                                 if (n == childA)\r
3122                                         return -1; // before\r
3123 \r
3124                                 if (n == childB)\r
3125                                         return 1; // after\r
3126 \r
3127                                 n = n.nextSibling;\r
3128                         }\r
3129                 };\r
3130 \r
3131                 function _setEndPoint(st, n, o) {\r
3132                         var ec, sc;\r
3133 \r
3134                         if (st) {\r
3135                                 t[START_CONTAINER] = n;\r
3136                                 t[START_OFFSET] = o;\r
3137                         } else {\r
3138                                 t[END_CONTAINER] = n;\r
3139                                 t[END_OFFSET] = o;\r
3140                         }\r
3141 \r
3142                         // If one boundary-point of a Range is set to have a root container\r
3143                         // other than the current one for the Range, the Range is collapsed to\r
3144                         // the new position. This enforces the restriction that both boundary-\r
3145                         // points of a Range must have the same root container.\r
3146                         ec = t[END_CONTAINER];\r
3147                         while (ec.parentNode)\r
3148                                 ec = ec.parentNode;\r
3149 \r
3150                         sc = t[START_CONTAINER];\r
3151                         while (sc.parentNode)\r
3152                                 sc = sc.parentNode;\r
3153 \r
3154                         if (sc == ec) {\r
3155                                 // The start position of a Range is guaranteed to never be after the\r
3156                                 // end position. To enforce this restriction, if the start is set to\r
3157                                 // be at a position after the end, the Range is collapsed to that\r
3158                                 // position.\r
3159                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
3160                                         t.collapse(st);\r
3161                         } else\r
3162                                 t.collapse(st);\r
3163 \r
3164                         t.collapsed = _isCollapsed();\r
3165                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
3166                 };\r
3167 \r
3168                 function _traverse(how) {\r
3169                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
3170 \r
3171                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
3172                                 return _traverseSameContainer(how);\r
3173 \r
3174                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
3175                                 if (p == t[START_CONTAINER])\r
3176                                         return _traverseCommonStartContainer(c, how);\r
3177 \r
3178                                 ++endContainerDepth;\r
3179                         }\r
3180 \r
3181                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
3182                                 if (p == t[END_CONTAINER])\r
3183                                         return _traverseCommonEndContainer(c, how);\r
3184 \r
3185                                 ++startContainerDepth;\r
3186                         }\r
3187 \r
3188                         depthDiff = startContainerDepth - endContainerDepth;\r
3189 \r
3190                         startNode = t[START_CONTAINER];\r
3191                         while (depthDiff > 0) {\r
3192                                 startNode = startNode.parentNode;\r
3193                                 depthDiff--;\r
3194                         }\r
3195 \r
3196                         endNode = t[END_CONTAINER];\r
3197                         while (depthDiff < 0) {\r
3198                                 endNode = endNode.parentNode;\r
3199                                 depthDiff++;\r
3200                         }\r
3201 \r
3202                         // ascend the ancestor hierarchy until we have a common parent.\r
3203                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
3204                                 startNode = sp;\r
3205                                 endNode = ep;\r
3206                         }\r
3207 \r
3208                         return _traverseCommonAncestors(startNode, endNode, how);\r
3209                 };\r
3210 \r
3211                  function _traverseSameContainer(how) {\r
3212                         var frag, s, sub, n, cnt, sibling, xferNode;\r
3213 \r
3214                         if (how != DELETE)\r
3215                                 frag = doc.createDocumentFragment();\r
3216 \r
3217                         // If selection is empty, just return the fragment\r
3218                         if (t[START_OFFSET] == t[END_OFFSET])\r
3219                                 return frag;\r
3220 \r
3221                         // Text node needs special case handling\r
3222                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
3223                                 // get the substring\r
3224                                 s = t[START_CONTAINER].nodeValue;\r
3225                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
3226 \r
3227                                 // set the original text node to its new value\r
3228                                 if (how != CLONE) {\r
3229                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
3230 \r
3231                                         // Nothing is partially selected, so collapse to start point\r
3232                                         t.collapse(TRUE);\r
3233                                 }\r
3234 \r
3235                                 if (how == DELETE)\r
3236                                         return;\r
3237 \r
3238                                 frag.appendChild(doc.createTextNode(sub));\r
3239                                 return frag;\r
3240                         }\r
3241 \r
3242                         // Copy nodes between the start/end offsets.\r
3243                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
3244                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
3245 \r
3246                         while (cnt > 0) {\r
3247                                 sibling = n.nextSibling;\r
3248                                 xferNode = _traverseFullySelected(n, how);\r
3249 \r
3250                                 if (frag)\r
3251                                         frag.appendChild( xferNode );\r
3252 \r
3253                                 --cnt;\r
3254                                 n = sibling;\r
3255                         }\r
3256 \r
3257                         // Nothing is partially selected, so collapse to start point\r
3258                         if (how != CLONE)\r
3259                                 t.collapse(TRUE);\r
3260 \r
3261                         return frag;\r
3262                 };\r
3263 \r
3264                 function _traverseCommonStartContainer(endAncestor, how) {\r
3265                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3266 \r
3267                         if (how != DELETE)\r
3268                                 frag = doc.createDocumentFragment();\r
3269 \r
3270                         n = _traverseRightBoundary(endAncestor, how);\r
3271 \r
3272                         if (frag)\r
3273                                 frag.appendChild(n);\r
3274 \r
3275                         endIdx = nodeIndex(endAncestor);\r
3276                         cnt = endIdx - t[START_OFFSET];\r
3277 \r
3278                         if (cnt <= 0) {\r
3279                                 // Collapse to just before the endAncestor, which\r
3280                                 // is partially selected.\r
3281                                 if (how != CLONE) {\r
3282                                         t.setEndBefore(endAncestor);\r
3283                                         t.collapse(FALSE);\r
3284                                 }\r
3285 \r
3286                                 return frag;\r
3287                         }\r
3288 \r
3289                         n = endAncestor.previousSibling;\r
3290                         while (cnt > 0) {\r
3291                                 sibling = n.previousSibling;\r
3292                                 xferNode = _traverseFullySelected(n, how);\r
3293 \r
3294                                 if (frag)\r
3295                                         frag.insertBefore(xferNode, frag.firstChild);\r
3296 \r
3297                                 --cnt;\r
3298                                 n = sibling;\r
3299                         }\r
3300 \r
3301                         // Collapse to just before the endAncestor, which\r
3302                         // is partially selected.\r
3303                         if (how != CLONE) {\r
3304                                 t.setEndBefore(endAncestor);\r
3305                                 t.collapse(FALSE);\r
3306                         }\r
3307 \r
3308                         return frag;\r
3309                 };\r
3310 \r
3311                 function _traverseCommonEndContainer(startAncestor, how) {\r
3312                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3313 \r
3314                         if (how != DELETE)\r
3315                                 frag = doc.createDocumentFragment();\r
3316 \r
3317                         n = _traverseLeftBoundary(startAncestor, how);\r
3318                         if (frag)\r
3319                                 frag.appendChild(n);\r
3320 \r
3321                         startIdx = nodeIndex(startAncestor);\r
3322                         ++startIdx;  // Because we already traversed it....\r
3323 \r
3324                         cnt = t[END_OFFSET] - startIdx;\r
3325                         n = startAncestor.nextSibling;\r
3326                         while (cnt > 0) {\r
3327                                 sibling = n.nextSibling;\r
3328                                 xferNode = _traverseFullySelected(n, how);\r
3329 \r
3330                                 if (frag)\r
3331                                         frag.appendChild(xferNode);\r
3332 \r
3333                                 --cnt;\r
3334                                 n = sibling;\r
3335                         }\r
3336 \r
3337                         if (how != CLONE) {\r
3338                                 t.setStartAfter(startAncestor);\r
3339                                 t.collapse(TRUE);\r
3340                         }\r
3341 \r
3342                         return frag;\r
3343                 };\r
3344 \r
3345                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3346                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3347 \r
3348                         if (how != DELETE)\r
3349                                 frag = doc.createDocumentFragment();\r
3350 \r
3351                         n = _traverseLeftBoundary(startAncestor, how);\r
3352                         if (frag)\r
3353                                 frag.appendChild(n);\r
3354 \r
3355                         commonParent = startAncestor.parentNode;\r
3356                         startOffset = nodeIndex(startAncestor);\r
3357                         endOffset = nodeIndex(endAncestor);\r
3358                         ++startOffset;\r
3359 \r
3360                         cnt = endOffset - startOffset;\r
3361                         sibling = startAncestor.nextSibling;\r
3362 \r
3363                         while (cnt > 0) {\r
3364                                 nextSibling = sibling.nextSibling;\r
3365                                 n = _traverseFullySelected(sibling, how);\r
3366 \r
3367                                 if (frag)\r
3368                                         frag.appendChild(n);\r
3369 \r
3370                                 sibling = nextSibling;\r
3371                                 --cnt;\r
3372                         }\r
3373 \r
3374                         n = _traverseRightBoundary(endAncestor, how);\r
3375 \r
3376                         if (frag)\r
3377                                 frag.appendChild(n);\r
3378 \r
3379                         if (how != CLONE) {\r
3380                                 t.setStartAfter(startAncestor);\r
3381                                 t.collapse(TRUE);\r
3382                         }\r
3383 \r
3384                         return frag;\r
3385                 };\r
3386 \r
3387                 function _traverseRightBoundary(root, how) {\r
3388                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3389 \r
3390                         if (next == root)\r
3391                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3392 \r
3393                         parent = next.parentNode;\r
3394                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3395 \r
3396                         while (parent) {\r
3397                                 while (next) {\r
3398                                         prevSibling = next.previousSibling;\r
3399                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3400 \r
3401                                         if (how != DELETE)\r
3402                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3403 \r
3404                                         isFullySelected = TRUE;\r
3405                                         next = prevSibling;\r
3406                                 }\r
3407 \r
3408                                 if (parent == root)\r
3409                                         return clonedParent;\r
3410 \r
3411                                 next = parent.previousSibling;\r
3412                                 parent = parent.parentNode;\r
3413 \r
3414                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3415 \r
3416                                 if (how != DELETE)\r
3417                                         clonedGrandParent.appendChild(clonedParent);\r
3418 \r
3419                                 clonedParent = clonedGrandParent;\r
3420                         }\r
3421                 };\r
3422 \r
3423                 function _traverseLeftBoundary(root, how) {\r
3424                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3425 \r
3426                         if (next == root)\r
3427                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3428 \r
3429                         parent = next.parentNode;\r
3430                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3431 \r
3432                         while (parent) {\r
3433                                 while (next) {\r
3434                                         nextSibling = next.nextSibling;\r
3435                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3436 \r
3437                                         if (how != DELETE)\r
3438                                                 clonedParent.appendChild(clonedChild);\r
3439 \r
3440                                         isFullySelected = TRUE;\r
3441                                         next = nextSibling;\r
3442                                 }\r
3443 \r
3444                                 if (parent == root)\r
3445                                         return clonedParent;\r
3446 \r
3447                                 next = parent.nextSibling;\r
3448                                 parent = parent.parentNode;\r
3449 \r
3450                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3451 \r
3452                                 if (how != DELETE)\r
3453                                         clonedGrandParent.appendChild(clonedParent);\r
3454 \r
3455                                 clonedParent = clonedGrandParent;\r
3456                         }\r
3457                 };\r
3458 \r
3459                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3460                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3461 \r
3462                         if (isFullySelected)\r
3463                                 return _traverseFullySelected(n, how);\r
3464 \r
3465                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3466                                 txtValue = n.nodeValue;\r
3467 \r
3468                                 if (isLeft) {\r
3469                                         offset = t[START_OFFSET];\r
3470                                         newNodeValue = txtValue.substring(offset);\r
3471                                         oldNodeValue = txtValue.substring(0, offset);\r
3472                                 } else {\r
3473                                         offset = t[END_OFFSET];\r
3474                                         newNodeValue = txtValue.substring(0, offset);\r
3475                                         oldNodeValue = txtValue.substring(offset);\r
3476                                 }\r
3477 \r
3478                                 if (how != CLONE)\r
3479                                         n.nodeValue = oldNodeValue;\r
3480 \r
3481                                 if (how == DELETE)\r
3482                                         return;\r
3483 \r
3484                                 newNode = n.cloneNode(FALSE);\r
3485                                 newNode.nodeValue = newNodeValue;\r
3486 \r
3487                                 return newNode;\r
3488                         }\r
3489 \r
3490                         if (how == DELETE)\r
3491                                 return;\r
3492 \r
3493                         return n.cloneNode(FALSE);\r
3494                 };\r
3495 \r
3496                 function _traverseFullySelected(n, how) {\r
3497                         if (how != DELETE)\r
3498                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3499 \r
3500                         n.parentNode.removeChild(n);\r
3501                 };\r
3502         };\r
3503 \r
3504         ns.Range = Range;\r
3505 })(tinymce.dom);\r
3506 (function() {\r
3507         function Selection(selection) {\r
3508                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3509 \r
3510                 // Compares two IE specific ranges to see if they are different\r
3511                 // this method is useful when invalidating the cached selection range\r
3512                 function compareRanges(rng1, rng2) {\r
3513                         if (rng1 && rng2) {\r
3514                                 // Both are control ranges and the selected element matches\r
3515                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
3516                                         return TRUE;\r
3517 \r
3518                                 // Both are text ranges and the range matches\r
3519                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {\r
3520                                         // IE will say that the range is equal then produce an invalid argument exception\r
3521                                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3522                                         // This hack will invalidate the range cache if the exception occurs\r
3523                                         try {\r
3524                                                 // Try accessing nextSibling will producer an invalid argument some times\r
3525                                                 range.startContainer.nextSibling;\r
3526                                                 return TRUE;\r
3527                                         } catch (ex) {\r
3528                                                 // Ignore\r
3529                                         }\r
3530                                 }\r
3531                         }\r
3532 \r
3533                         return FALSE;\r
3534                 };\r
3535 \r
3536                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3537                 function getRange() {\r
3538                         var ieRange = selection.getRng(), domRange = dom.createRng(), ieRange2, element, collapsed, isMerged;\r
3539 \r
3540                         // If selection is outside the current document just return an empty range\r
3541                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3542                         if (element.ownerDocument != dom.doc)\r
3543                                 return domRange;\r
3544 \r
3545                         // Handle control selection or text selection of a image\r
3546                         if (ieRange.item || !element.hasChildNodes()) {\r
3547                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3548                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3549 \r
3550                                 return domRange;\r
3551                         }\r
3552 \r
3553                         // Duplicare IE selection range and check if the range is collapsed\r
3554                         ieRange2 = ieRange.duplicate();\r
3555                         collapsed = selection.isCollapsed();\r
3556 \r
3557                         // Insert invisible start marker\r
3558                         ieRange.collapse();\r
3559                         ieRange.pasteHTML('<span id="_mce_start" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
3560 \r
3561                         // Insert invisible end marker\r
3562                         if (!collapsed) {\r
3563                                 ieRange2.collapse(FALSE);\r
3564                                 ieRange2.pasteHTML('<span id="_mce_end" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
3565                         }\r
3566 \r
3567                         // Sets the end point of the range by looking for the marker\r
3568                         // This method also merges the text nodes it splits so that\r
3569                         // the DOM doesn't get fragmented.\r
3570                         function setEndPoint(start) {\r
3571                                 var container, offset, marker, sibling;\r
3572 \r
3573                                 // Look for endpoint marker\r
3574                                 marker = dom.get('_mce_' + (start ? 'start' : 'end'));\r
3575                                 sibling = marker.previousSibling;\r
3576 \r
3577                                 // Is marker after a text node\r
3578                                 if (sibling && sibling.nodeType == 3) {\r
3579                                         // Get container node and calc offset\r
3580                                         container = sibling;\r
3581                                         offset = container.nodeValue.length;\r
3582                                         dom.remove(marker);\r
3583 \r
3584                                         // Merge text nodes to reduce DOM fragmentation\r
3585                                         sibling = container.nextSibling;\r
3586                                         if (sibling && sibling.nodeType == 3) {\r
3587                                                 isMerged = TRUE;\r
3588                                                 container.appendData(sibling.nodeValue);\r
3589                                                 dom.remove(sibling);\r
3590                                         }\r
3591                                 } else {\r
3592                                         sibling = marker.nextSibling;\r
3593 \r
3594                                         // Is marker before a text node\r
3595                                         if (sibling && sibling.nodeType == 3) {\r
3596                                                 container = sibling;\r
3597                                                 offset = 0;\r
3598                                         } else {\r
3599                                                 // Is marker before an element\r
3600                                                 if (sibling)\r
3601                                                         offset = dom.nodeIndex(sibling) - 1;\r
3602                                                 else\r
3603                                                         offset = dom.nodeIndex(marker);\r
3604 \r
3605                                                 container = marker.parentNode;\r
3606                                         }\r
3607 \r
3608                                         dom.remove(marker);\r
3609                                 }\r
3610 \r
3611                                 // Set start of range\r
3612                                 if (start)\r
3613                                         domRange.setStart(container, offset);\r
3614 \r
3615                                 // Set end of range or automatically if it's collapsed to increase performance\r
3616                                 if (!start || collapsed)\r
3617                                         domRange.setEnd(container, offset);\r
3618                         };\r
3619 \r
3620                         // Set start of range\r
3621                         setEndPoint(TRUE);\r
3622 \r
3623                         // Set end of range if needed\r
3624                         if (!collapsed)\r
3625                                 setEndPoint(FALSE);\r
3626 \r
3627                         // Restore selection if the range contents was merged\r
3628                         // since the selection was then moved since the text nodes got changed\r
3629                         if (isMerged)\r
3630                                 t.addRange(domRange);\r
3631 \r
3632                         return domRange;\r
3633                 };\r
3634 \r
3635                 this.addRange = function(rng) {\r
3636                         var ieRng, ieRng2, doc = selection.dom.doc, body = doc.body, startPos, endPos, sc, so, ec, eo, marker, lastIndex, skipStart, skipEnd;\r
3637 \r
3638                         this.destroy();\r
3639 \r
3640                         // Setup some shorter versions\r
3641                         sc = rng.startContainer;\r
3642                         so = rng.startOffset;\r
3643                         ec = rng.endContainer;\r
3644                         eo = rng.endOffset;\r
3645                         ieRng = body.createTextRange();\r
3646 \r
3647                         // If document selection move caret to first node in document\r
3648                         if (sc == doc || ec == doc) {\r
3649                                 ieRng = body.createTextRange();\r
3650                                 ieRng.collapse();\r
3651                                 ieRng.select();\r
3652                                 return;\r
3653                         }\r
3654 \r
3655                         // If child index resolve it\r
3656                         if (sc.nodeType == 1 && sc.hasChildNodes()) {\r
3657                                 lastIndex = sc.childNodes.length - 1;\r
3658 \r
3659                                 // Index is higher that the child count then we need to jump over the start container\r
3660                                 if (so > lastIndex) {\r
3661                                         skipStart = 1;\r
3662                                         sc = sc.childNodes[lastIndex];\r
3663                                 } else\r
3664                                         sc = sc.childNodes[so];\r
3665 \r
3666                                 // Child was text node then move offset to start of it\r
3667                                 if (sc.nodeType == 3)\r
3668                                         so = 0;\r
3669                         }\r
3670 \r
3671                         // If child index resolve it\r
3672                         if (ec.nodeType == 1 && ec.hasChildNodes()) {\r
3673                                 lastIndex = ec.childNodes.length - 1;\r
3674 \r
3675                                 if (eo == 0) {\r
3676                                         skipEnd = 1;\r
3677                                         ec = ec.childNodes[0];\r
3678                                 } else {\r
3679                                         ec = ec.childNodes[Math.min(lastIndex, eo - 1)];\r
3680 \r
3681                                         // Child was text node then move offset to end of text node\r
3682                                         if (ec.nodeType == 3)\r
3683                                                 eo = ec.nodeValue.length;\r
3684                                 }\r
3685                         }\r
3686 \r
3687                         // Single element selection\r
3688                         if (sc == ec && sc.nodeType == 1) {\r
3689                                 // Make control selection for some elements\r
3690                                 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {\r
3691                                         ieRng = body.createControlRange();\r
3692                                         ieRng.addElement(sc);\r
3693                                 } else {\r
3694                                         ieRng = body.createTextRange();\r
3695 \r
3696                                         // Padd empty elements with invisible character\r
3697                                         if (!sc.hasChildNodes() && sc.canHaveHTML)\r
3698                                                 sc.innerHTML = invisibleChar;\r
3699 \r
3700                                         // Select element contents\r
3701                                         ieRng.moveToElementText(sc);\r
3702 \r
3703                                         // If it's only containing a padding remove it so the caret remains\r
3704                                         if (sc.innerHTML == invisibleChar) {\r
3705                                                 ieRng.collapse(TRUE);\r
3706                                                 sc.removeChild(sc.firstChild);\r
3707                                         }\r
3708                                 }\r
3709 \r
3710                                 if (so == eo)\r
3711                                         ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);\r
3712 \r
3713                                 ieRng.select();\r
3714                                 ieRng.scrollIntoView();\r
3715                                 return;\r
3716                         }\r
3717 \r
3718                         // Create range and marker\r
3719                         ieRng = body.createTextRange();\r
3720                         marker = doc.createElement('span');\r
3721                         marker.innerHTML = ' ';\r
3722 \r
3723                         // Set start of range to startContainer/startOffset\r
3724                         if (sc.nodeType == 3) {\r
3725                                 // Insert marker after/before startContainer\r
3726                                 if (skipStart)\r
3727                                         dom.insertAfter(marker, sc);\r
3728                                 else\r
3729                                         sc.parentNode.insertBefore(marker, sc);\r
3730 \r
3731                                 // Select marker the caret to offset position\r
3732                                 ieRng.moveToElementText(marker);\r
3733                                 marker.parentNode.removeChild(marker);\r
3734                                 ieRng.move('character', so);\r
3735                         } else {\r
3736                                 ieRng.moveToElementText(sc);\r
3737 \r
3738                                 if (skipStart)\r
3739                                         ieRng.collapse(FALSE);\r
3740                         }\r
3741 \r
3742                         // If same text container then we can do a more simple move\r
3743                         if (sc == ec && sc.nodeType == 3) {\r
3744                                 ieRng.moveEnd('character', eo - so);\r
3745                                 ieRng.select();\r
3746                                 ieRng.scrollIntoView();\r
3747                                 return;\r
3748                         }\r
3749 \r
3750                         // Set end of range to endContainer/endOffset\r
3751                         ieRng2 = body.createTextRange();\r
3752                         if (ec.nodeType == 3) {\r
3753                                 // Insert marker after/before startContainer\r
3754                                 ec.parentNode.insertBefore(marker, ec);\r
3755 \r
3756                                 // Move selection to end marker and move caret to end offset\r
3757                                 ieRng2.moveToElementText(marker);\r
3758                                 marker.parentNode.removeChild(marker);\r
3759                                 ieRng2.move('character', eo);\r
3760                                 ieRng.setEndPoint('EndToStart', ieRng2);\r
3761                         } else {\r
3762                                 ieRng2.moveToElementText(ec);\r
3763                                 ieRng2.collapse(!!skipEnd);\r
3764                                 ieRng.setEndPoint('EndToEnd', ieRng2);\r
3765                         }\r
3766 \r
3767                         ieRng.select();\r
3768                         ieRng.scrollIntoView();\r
3769                 };\r
3770 \r
3771                 this.getRangeAt = function() {\r
3772                         // Setup new range if the cache is empty\r
3773                         if (!range || !compareRanges(lastIERng, selection.getRng())) {\r
3774                                 range = getRange();\r
3775 \r
3776                                 // Store away text range for next call\r
3777                                 lastIERng = selection.getRng();\r
3778                         }\r
3779 \r
3780                         // Return cached range\r
3781                         return range;\r
3782                 };\r
3783 \r
3784                 this.destroy = function() {\r
3785                         // Destroy cached range and last IE range to avoid memory leaks\r
3786                         lastIERng = range = null;\r
3787                 };\r
3788 \r
3789                 // 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
3790                 if (selection.dom.boxModel) {\r
3791                         (function() {\r
3792                                 var doc = dom.doc, body = doc.body, started, startRng;\r
3793 \r
3794                                 // Make HTML element unselectable since we are going to handle selection by hand\r
3795                                 doc.documentElement.unselectable = TRUE;\r
3796 \r
3797                                 // Return range from point or null if it failed\r
3798                                 function rngFromPoint(x, y) {\r
3799                                         var rng = body.createTextRange();\r
3800 \r
3801                                         try {\r
3802                                                 rng.moveToPoint(x, y);\r
3803                                         } catch (ex) {\r
3804                                                 // IE sometimes throws and exception, so lets just ignore it\r
3805                                                 rng = null;\r
3806                                         }\r
3807 \r
3808                                         return rng;\r
3809                                 };\r
3810 \r
3811                                 // Fires while the selection is changing\r
3812                                 function selectionChange(e) {\r
3813                                         var pointRng;\r
3814 \r
3815                                         // Check if the button is down or not\r
3816                                         if (e.button) {\r
3817                                                 // Create range from mouse position\r
3818                                                 pointRng = rngFromPoint(e.x, e.y);\r
3819 \r
3820                                                 if (pointRng) {\r
3821                                                         // Check if pointRange is before/after selection then change the endPoint\r
3822                                                         if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
3823                                                                 pointRng.setEndPoint('StartToStart', startRng);\r
3824                                                         else\r
3825                                                                 pointRng.setEndPoint('EndToEnd', startRng);\r
3826 \r
3827                                                         pointRng.select();\r
3828                                                 }\r
3829                                         } else\r
3830                                                 endSelection();\r
3831                                 }\r
3832 \r
3833                                 // Removes listeners\r
3834                                 function endSelection() {\r
3835                                         dom.unbind(doc, 'mouseup', endSelection);\r
3836                                         dom.unbind(doc, 'mousemove', selectionChange);\r
3837                                         started = 0;\r
3838                                 };\r
3839 \r
3840                                 // Detect when user selects outside BODY\r
3841                                 dom.bind(doc, 'mousedown', function(e) {\r
3842                                         if (e.target.nodeName === 'HTML') {\r
3843                                                 if (started)\r
3844                                                         endSelection();\r
3845 \r
3846                                                 started = 1;\r
3847 \r
3848                                                 // Setup start position\r
3849                                                 startRng = rngFromPoint(e.x, e.y);\r
3850                                                 if (startRng) {\r
3851                                                         // Listen for selection change events\r
3852                                                         dom.bind(doc, 'mouseup', endSelection);\r
3853                                                         dom.bind(doc, 'mousemove', selectionChange);\r
3854 \r
3855                                                         startRng.select();\r
3856                                                 }\r
3857                                         }\r
3858                                 });\r
3859                         })();\r
3860                 }\r
3861         };\r
3862 \r
3863         // Expose the selection object\r
3864         tinymce.dom.TridentSelection = Selection;\r
3865 })();\r
3866 (function(tinymce) {\r
3867         // Shorten names\r
3868         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
3869 \r
3870         tinymce.create('tinymce.dom.EventUtils', {\r
3871                 EventUtils : function() {\r
3872                         this.inits = [];\r
3873                         this.events = [];\r
3874                 },\r
3875 \r
3876                 add : function(o, n, f, s) {\r
3877                         var cb, t = this, el = t.events, r;\r
3878 \r
3879                         if (n instanceof Array) {\r
3880                                 r = [];\r
3881 \r
3882                                 each(n, function(n) {\r
3883                                         r.push(t.add(o, n, f, s));\r
3884                                 });\r
3885 \r
3886                                 return r;\r
3887                         }\r
3888 \r
3889                         // Handle array\r
3890                         if (o && o.hasOwnProperty && o instanceof Array) {\r
3891                                 r = [];\r
3892 \r
3893                                 each(o, function(o) {\r
3894                                         o = DOM.get(o);\r
3895                                         r.push(t.add(o, n, f, s));\r
3896                                 });\r
3897 \r
3898                                 return r;\r
3899                         }\r
3900 \r
3901                         o = DOM.get(o);\r
3902 \r
3903                         if (!o)\r
3904                                 return;\r
3905 \r
3906                         // Setup event callback\r
3907                         cb = function(e) {\r
3908                                 // Is all events disabled\r
3909                                 if (t.disabled)\r
3910                                         return;\r
3911 \r
3912                                 e = e || window.event;\r
3913 \r
3914                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
3915                                 if (e && isIE) {\r
3916                                         if (!e.target)\r
3917                                                 e.target = e.srcElement;\r
3918 \r
3919                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
3920                                         tinymce.extend(e, t._stoppers);\r
3921                                 }\r
3922 \r
3923                                 if (!s)\r
3924                                         return f(e);\r
3925 \r
3926                                 return f.call(s, e);\r
3927                         };\r
3928 \r
3929                         if (n == 'unload') {\r
3930                                 tinymce.unloads.unshift({func : cb});\r
3931                                 return cb;\r
3932                         }\r
3933 \r
3934                         if (n == 'init') {\r
3935                                 if (t.domLoaded)\r
3936                                         cb();\r
3937                                 else\r
3938                                         t.inits.push(cb);\r
3939 \r
3940                                 return cb;\r
3941                         }\r
3942 \r
3943                         // Store away listener reference\r
3944                         el.push({\r
3945                                 obj : o,\r
3946                                 name : n,\r
3947                                 func : f,\r
3948                                 cfunc : cb,\r
3949                                 scope : s\r
3950                         });\r
3951 \r
3952                         t._add(o, n, cb);\r
3953 \r
3954                         return f;\r
3955                 },\r
3956 \r
3957                 remove : function(o, n, f) {\r
3958                         var t = this, a = t.events, s = false, r;\r
3959 \r
3960                         // Handle array\r
3961                         if (o && o.hasOwnProperty && o instanceof Array) {\r
3962                                 r = [];\r
3963 \r
3964                                 each(o, function(o) {\r
3965                                         o = DOM.get(o);\r
3966                                         r.push(t.remove(o, n, f));\r
3967                                 });\r
3968 \r
3969                                 return r;\r
3970                         }\r
3971 \r
3972                         o = DOM.get(o);\r
3973 \r
3974                         each(a, function(e, i) {\r
3975                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
3976                                         a.splice(i, 1);\r
3977                                         t._remove(o, n, e.cfunc);\r
3978                                         s = true;\r
3979                                         return false;\r
3980                                 }\r
3981                         });\r
3982 \r
3983                         return s;\r
3984                 },\r
3985 \r
3986                 clear : function(o) {\r
3987                         var t = this, a = t.events, i, e;\r
3988 \r
3989                         if (o) {\r
3990                                 o = DOM.get(o);\r
3991 \r
3992                                 for (i = a.length - 1; i >= 0; i--) {\r
3993                                         e = a[i];\r
3994 \r
3995                                         if (e.obj === o) {\r
3996                                                 t._remove(e.obj, e.name, e.cfunc);\r
3997                                                 e.obj = e.cfunc = null;\r
3998                                                 a.splice(i, 1);\r
3999                                         }\r
4000                                 }\r
4001                         }\r
4002                 },\r
4003 \r
4004                 cancel : function(e) {\r
4005                         if (!e)\r
4006                                 return false;\r
4007 \r
4008                         this.stop(e);\r
4009 \r
4010                         return this.prevent(e);\r
4011                 },\r
4012 \r
4013                 stop : function(e) {\r
4014                         if (e.stopPropagation)\r
4015                                 e.stopPropagation();\r
4016                         else\r
4017                                 e.cancelBubble = true;\r
4018 \r
4019                         return false;\r
4020                 },\r
4021 \r
4022                 prevent : function(e) {\r
4023                         if (e.preventDefault)\r
4024                                 e.preventDefault();\r
4025                         else\r
4026                                 e.returnValue = false;\r
4027 \r
4028                         return false;\r
4029                 },\r
4030 \r
4031                 destroy : function() {\r
4032                         var t = this;\r
4033 \r
4034                         each(t.events, function(e, i) {\r
4035                                 t._remove(e.obj, e.name, e.cfunc);\r
4036                                 e.obj = e.cfunc = null;\r
4037                         });\r
4038 \r
4039                         t.events = [];\r
4040                         t = null;\r
4041                 },\r
4042 \r
4043                 _add : function(o, n, f) {\r
4044                         if (o.attachEvent)\r
4045                                 o.attachEvent('on' + n, f);\r
4046                         else if (o.addEventListener)\r
4047                                 o.addEventListener(n, f, false);\r
4048                         else\r
4049                                 o['on' + n] = f;\r
4050                 },\r
4051 \r
4052                 _remove : function(o, n, f) {\r
4053                         if (o) {\r
4054                                 try {\r
4055                                         if (o.detachEvent)\r
4056                                                 o.detachEvent('on' + n, f);\r
4057                                         else if (o.removeEventListener)\r
4058                                                 o.removeEventListener(n, f, false);\r
4059                                         else\r
4060                                                 o['on' + n] = null;\r
4061                                 } catch (ex) {\r
4062                                         // Might fail with permission denined on IE so we just ignore that\r
4063                                 }\r
4064                         }\r
4065                 },\r
4066 \r
4067                 _pageInit : function(win) {\r
4068                         var t = this;\r
4069 \r
4070                         // Keep it from running more than once\r
4071                         if (t.domLoaded)\r
4072                                 return;\r
4073 \r
4074                         t.domLoaded = true;\r
4075 \r
4076                         each(t.inits, function(c) {\r
4077                                 c();\r
4078                         });\r
4079 \r
4080                         t.inits = [];\r
4081                 },\r
4082 \r
4083                 _wait : function(win) {\r
4084                         var t = this, doc = win.document;\r
4085 \r
4086                         // No need since the document is already loaded\r
4087                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4088                                 t.domLoaded = 1;\r
4089                                 return;\r
4090                         }\r
4091 \r
4092                         // Use IE method\r
4093                         if (doc.attachEvent) {\r
4094                                 doc.attachEvent("onreadystatechange", function() {\r
4095                                         if (doc.readyState === "complete") {\r
4096                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4097                                                 t._pageInit(win);\r
4098                                         }\r
4099                                 });\r
4100 \r
4101                                 if (doc.documentElement.doScroll && win == win.top) {\r
4102                                         (function() {\r
4103                                                 if (t.domLoaded)\r
4104                                                         return;\r
4105 \r
4106                                                 try {\r
4107                                                         // If IE is used, use the trick by Diego Perini\r
4108                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4109                                                         doc.documentElement.doScroll("left");\r
4110                                                 } catch (ex) {\r
4111                                                         setTimeout(arguments.callee, 0);\r
4112                                                         return;\r
4113                                                 }\r
4114 \r
4115                                                 t._pageInit(win);\r
4116                                         })();\r
4117                                 }\r
4118                         } else if (doc.addEventListener) {\r
4119                                 t._add(win, 'DOMContentLoaded', function() {\r
4120                                         t._pageInit(win);\r
4121                                 });\r
4122                         }\r
4123 \r
4124                         t._add(win, 'load', function() {\r
4125                                 t._pageInit(win);\r
4126                         });\r
4127                 },\r
4128 \r
4129                 _stoppers : {\r
4130                         preventDefault :  function() {\r
4131                                 this.returnValue = false;\r
4132                         },\r
4133 \r
4134                         stopPropagation : function() {\r
4135                                 this.cancelBubble = true;\r
4136                         }\r
4137                 }\r
4138         });\r
4139 \r
4140         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4141 \r
4142         // Dispatch DOM content loaded event for IE and Safari\r
4143         Event._wait(window);\r
4144 \r
4145         tinymce.addUnload(function() {\r
4146                 Event.destroy();\r
4147         });\r
4148 })(tinymce);\r
4149 (function(tinymce) {\r
4150         tinymce.dom.Element = function(id, settings) {\r
4151                 var t = this, dom, el;\r
4152 \r
4153                 t.settings = settings = settings || {};\r
4154                 t.id = id;\r
4155                 t.dom = dom = settings.dom || tinymce.DOM;\r
4156 \r
4157                 // Only IE leaks DOM references, this is a lot faster\r
4158                 if (!tinymce.isIE)\r
4159                         el = dom.get(t.id);\r
4160 \r
4161                 tinymce.each(\r
4162                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4163                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4164                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4165                                 'isHidden,setHTML,get').split(/,/)\r
4166                         , function(k) {\r
4167                                 t[k] = function() {\r
4168                                         var a = [id], i;\r
4169 \r
4170                                         for (i = 0; i < arguments.length; i++)\r
4171                                                 a.push(arguments[i]);\r
4172 \r
4173                                         a = dom[k].apply(dom, a);\r
4174                                         t.update(k);\r
4175 \r
4176                                         return a;\r
4177                                 };\r
4178                 });\r
4179 \r
4180                 tinymce.extend(t, {\r
4181                         on : function(n, f, s) {\r
4182                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
4183                         },\r
4184 \r
4185                         getXY : function() {\r
4186                                 return {\r
4187                                         x : parseInt(t.getStyle('left')),\r
4188                                         y : parseInt(t.getStyle('top'))\r
4189                                 };\r
4190                         },\r
4191 \r
4192                         getSize : function() {\r
4193                                 var n = dom.get(t.id);\r
4194 \r
4195                                 return {\r
4196                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
4197                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
4198                                 };\r
4199                         },\r
4200 \r
4201                         moveTo : function(x, y) {\r
4202                                 t.setStyles({left : x, top : y});\r
4203                         },\r
4204 \r
4205                         moveBy : function(x, y) {\r
4206                                 var p = t.getXY();\r
4207 \r
4208                                 t.moveTo(p.x + x, p.y + y);\r
4209                         },\r
4210 \r
4211                         resizeTo : function(w, h) {\r
4212                                 t.setStyles({width : w, height : h});\r
4213                         },\r
4214 \r
4215                         resizeBy : function(w, h) {\r
4216                                 var s = t.getSize();\r
4217 \r
4218                                 t.resizeTo(s.w + w, s.h + h);\r
4219                         },\r
4220 \r
4221                         update : function(k) {\r
4222                                 var b;\r
4223 \r
4224                                 if (tinymce.isIE6 && settings.blocker) {\r
4225                                         k = k || '';\r
4226 \r
4227                                         // Ignore getters\r
4228                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4229                                                 return;\r
4230 \r
4231                                         // Remove blocker on remove\r
4232                                         if (k == 'remove') {\r
4233                                                 dom.remove(t.blocker);\r
4234                                                 return;\r
4235                                         }\r
4236 \r
4237                                         if (!t.blocker) {\r
4238                                                 t.blocker = dom.uniqueId();\r
4239                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
4240                                                 dom.setStyle(b, 'opacity', 0);\r
4241                                         } else\r
4242                                                 b = dom.get(t.blocker);\r
4243 \r
4244                                         dom.setStyles(b, {\r
4245                                                 left : t.getStyle('left', 1),\r
4246                                                 top : t.getStyle('top', 1),\r
4247                                                 width : t.getStyle('width', 1),\r
4248                                                 height : t.getStyle('height', 1),\r
4249                                                 display : t.getStyle('display', 1),\r
4250                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
4251                                         });\r
4252                                 }\r
4253                         }\r
4254                 });\r
4255         };\r
4256 })(tinymce);\r
4257 (function(tinymce) {\r
4258         function trimNl(s) {\r
4259                 return s.replace(/[\n\r]+/g, '');\r
4260         };\r
4261 \r
4262         // Shorten names\r
4263         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
4264 \r
4265         tinymce.create('tinymce.dom.Selection', {\r
4266                 Selection : function(dom, win, serializer) {\r
4267                         var t = this;\r
4268 \r
4269                         t.dom = dom;\r
4270                         t.win = win;\r
4271                         t.serializer = serializer;\r
4272 \r
4273                         // Add events\r
4274                         each([\r
4275                                 'onBeforeSetContent',\r
4276                                 'onBeforeGetContent',\r
4277                                 'onSetContent',\r
4278                                 'onGetContent'\r
4279                         ], function(e) {\r
4280                                 t[e] = new tinymce.util.Dispatcher(t);\r
4281                         });\r
4282 \r
4283                         // No W3C Range support\r
4284                         if (!t.win.getSelection)\r
4285                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
4286 \r
4287                         // Prevent leaks\r
4288                         tinymce.addUnload(t.destroy, t);\r
4289                 },\r
4290 \r
4291                 getContent : function(s) {\r
4292                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
4293 \r
4294                         s = s || {};\r
4295                         wb = wa = '';\r
4296                         s.get = true;\r
4297                         s.format = s.format || 'html';\r
4298                         t.onBeforeGetContent.dispatch(t, s);\r
4299 \r
4300                         if (s.format == 'text')\r
4301                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
4302 \r
4303                         if (r.cloneContents) {\r
4304                                 n = r.cloneContents();\r
4305 \r
4306                                 if (n)\r
4307                                         e.appendChild(n);\r
4308                         } else if (is(r.item) || is(r.htmlText))\r
4309                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
4310                         else\r
4311                                 e.innerHTML = r.toString();\r
4312 \r
4313                         // Keep whitespace before and after\r
4314                         if (/^\s/.test(e.innerHTML))\r
4315                                 wb = ' ';\r
4316 \r
4317                         if (/\s+$/.test(e.innerHTML))\r
4318                                 wa = ' ';\r
4319 \r
4320                         s.getInner = true;\r
4321 \r
4322                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
4323                         t.onGetContent.dispatch(t, s);\r
4324 \r
4325                         return s.content;\r
4326                 },\r
4327 \r
4328                 setContent : function(h, s) {\r
4329                         var t = this, r = t.getRng(), c, d = t.win.document;\r
4330 \r
4331                         s = s || {format : 'html'};\r
4332                         s.set = true;\r
4333                         h = s.content = t.dom.processHTML(h);\r
4334 \r
4335                         // Dispatch before set content event\r
4336                         t.onBeforeSetContent.dispatch(t, s);\r
4337                         h = s.content;\r
4338 \r
4339                         if (r.insertNode) {\r
4340                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
4341                                 h += '<span id="__caret">_</span>';\r
4342 \r
4343                                 // Delete and insert new node\r
4344                                 if (r.startContainer == d && r.endContainer == d) {\r
4345                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
4346                                         d.body.innerHTML = h;\r
4347                                 } else {\r
4348                                         r.deleteContents();\r
4349                                         r.insertNode(t.getRng().createContextualFragment(h));\r
4350                                 }\r
4351 \r
4352                                 // Move to caret marker\r
4353                                 c = t.dom.get('__caret');\r
4354 \r
4355                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
4356                                 r = d.createRange();\r
4357                                 r.setStartBefore(c);\r
4358                                 r.setEndBefore(c);\r
4359                                 t.setRng(r);\r
4360 \r
4361                                 // Remove the caret position\r
4362                                 t.dom.remove('__caret');\r
4363                         } else {\r
4364                                 if (r.item) {\r
4365                                         // Delete content and get caret text selection\r
4366                                         d.execCommand('Delete', false, null);\r
4367                                         r = t.getRng();\r
4368                                 }\r
4369 \r
4370                                 r.pasteHTML(h);\r
4371                         }\r
4372 \r
4373                         // Dispatch set content event\r
4374                         t.onSetContent.dispatch(t, s);\r
4375                 },\r
4376 \r
4377                 getStart : function() {\r
4378                         var t = this, r = t.getRng(), e;\r
4379 \r
4380                         if (isIE) {\r
4381                                 if (r.item)\r
4382                                         return r.item(0);\r
4383 \r
4384                                 r = r.duplicate();\r
4385                                 r.collapse(1);\r
4386                                 e = r.parentElement();\r
4387 \r
4388                                 if (e && e.nodeName == 'BODY')\r
4389                                         return e.firstChild || e;\r
4390 \r
4391                                 return e;\r
4392                         } else {\r
4393                                 e = r.startContainer;\r
4394 \r
4395                                 if (e.nodeType == 1 && e.hasChildNodes())\r
4396                                         e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)];\r
4397 \r
4398                                 if (e && e.nodeType == 3)\r
4399                                         return e.parentNode;\r
4400 \r
4401                                 return e;\r
4402                         }\r
4403                 },\r
4404 \r
4405                 getEnd : function() {\r
4406                         var t = this, r = t.getRng(), e, eo;\r
4407 \r
4408                         if (isIE) {\r
4409                                 if (r.item)\r
4410                                         return r.item(0);\r
4411 \r
4412                                 r = r.duplicate();\r
4413                                 r.collapse(0);\r
4414                                 e = r.parentElement();\r
4415 \r
4416                                 if (e && e.nodeName == 'BODY')\r
4417                                         return e.lastChild || e;\r
4418 \r
4419                                 return e;\r
4420                         } else {\r
4421                                 e = r.endContainer;\r
4422                                 eo = r.endOffset;\r
4423 \r
4424                                 if (e.nodeType == 1 && e.hasChildNodes())\r
4425                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
4426 \r
4427                                 if (e && e.nodeType == 3)\r
4428                                         return e.parentNode;\r
4429 \r
4430                                 return e;\r
4431                         }\r
4432                 },\r
4433 \r
4434                 getBookmark : function(type, normalized) {\r
4435                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
4436 \r
4437                         function findIndex(name, element) {\r
4438                                 var index = 0;\r
4439 \r
4440                                 each(dom.select(name), function(node, i) {\r
4441                                         if (node == element)\r
4442                                                 index = i;\r
4443                                 });\r
4444 \r
4445                                 return index;\r
4446                         };\r
4447 \r
4448                         if (type == 2) {\r
4449                                 function getLocation() {\r
4450                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
4451 \r
4452                                         function getPoint(rng, start) {\r
4453                                                 var indexes = [], node, lastIdx,\r
4454                                                         container = rng[start ? 'startContainer' : 'endContainer'],\r
4455                                                         offset = rng[start ? 'startOffset' : 'endOffset'], exclude, point = {};\r
4456 \r
4457                                                 // Resolve element index\r
4458                                                 if (container.nodeType == 1 && container.hasChildNodes()) {\r
4459                                                         lastIdx = container.childNodes.length - 1;\r
4460                                                         point.exclude = (start && offset > lastIdx) || (!start && offset == 0);\r
4461 \r
4462                                                         if (!start && offset)\r
4463                                                                 offset--;\r
4464 \r
4465                                                         container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
4466 \r
4467                                                         if (container.nodeType == 3)\r
4468                                                                 offset = start ? 0 : container.nodeValue.length;\r
4469                                                 }\r
4470 \r
4471                                                 if (container.nodeType == 3) {\r
4472                                                         if (normalized) {\r
4473                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
4474                                                                         offset += node.nodeValue.length;\r
4475                                                         }\r
4476 \r
4477                                                         point.offset = offset;\r
4478                                                 }\r
4479 \r
4480                                                 for (; container && container != root; container = container.parentNode)\r
4481                                                         indexes.push(t.dom.nodeIndex(container, normalized));\r
4482 \r
4483                                                 point.indexes = indexes;\r
4484 \r
4485                                                 return point;\r
4486                                         };\r
4487 \r
4488                                         bookmark.start = getPoint(rng, true);\r
4489 \r
4490                                         if (!t.isCollapsed())\r
4491                                                 bookmark.end = getPoint(rng);\r
4492 \r
4493                                         return bookmark;\r
4494                                 };\r
4495 \r
4496                                 return getLocation();\r
4497                         }\r
4498 \r
4499                         // Handle simple range\r
4500                         if (type)\r
4501                                 return {rng : t.getRng()};\r
4502 \r
4503                         rng = t.getRng();\r
4504                         id = dom.uniqueId();\r
4505                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
4506                         styles = 'overflow:hidden;line-height:0px';\r
4507 \r
4508                         // Explorer method\r
4509                         if (rng.duplicate || rng.item) {\r
4510                                 // Text selection\r
4511                                 if (!rng.item) {\r
4512                                         rng2 = rng.duplicate();\r
4513 \r
4514                                         // Insert start marker\r
4515                                         rng.collapse();\r
4516                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
4517 \r
4518                                         // Insert end marker\r
4519                                         if (!collapsed) {\r
4520                                                 rng2.collapse(false);\r
4521                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
4522                                         }\r
4523                                 } else {\r
4524                                         // Control selection\r
4525                                         element = rng.item(0);\r
4526                                         name = element.nodeName;\r
4527 \r
4528                                         return {name : name, index : findIndex(name, element)};\r
4529                                 }\r
4530                         } else {\r
4531                                 element = t.getNode();\r
4532                                 name = element.nodeName;\r
4533                                 if (name == 'IMG')\r
4534                                         return {name : name, index : findIndex(name, element)};\r
4535 \r
4536                                 // W3C method\r
4537                                 rng2 = rng.cloneRange();\r
4538 \r
4539                                 // Insert end marker\r
4540                                 if (!collapsed) {\r
4541                                         rng2.collapse(false);\r
4542                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
4543                                 }\r
4544 \r
4545                                 rng.collapse(true);\r
4546                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
4547                         }\r
4548 \r
4549                         t.moveToBookmark({id : id, keep : 1});\r
4550 \r
4551                         return {id : id};\r
4552                 },\r
4553 \r
4554                 moveToBookmark : function(bookmark) {\r
4555                         var t = this, dom = t.dom, marker1, marker2, rng, root;\r
4556 \r
4557                         // Clear selection cache\r
4558                         if (t.tridentSel)\r
4559                                 t.tridentSel.destroy();\r
4560 \r
4561                         if (bookmark) {\r
4562                                 if (bookmark.start) {\r
4563                                         rng = dom.createRng();\r
4564                                         root = dom.getRoot();\r
4565 \r
4566                                         function setEndPoint(start) {\r
4567                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset;\r
4568 \r
4569                                                 if (point) {\r
4570                                                         for (node = root, i = point.indexes.length - 1; i >= 0; i--)\r
4571                                                                 node = node.childNodes[point.indexes[i]] || node;\r
4572 \r
4573                                                         if (start) {\r
4574                                                                 if (node.nodeType == 3 && point.offset)\r
4575                                                                         rng.setStart(node, point.offset);\r
4576                                                                 else {\r
4577                                                                         if (point.exclude)\r
4578                                                                                 rng.setStartAfter(node);\r
4579                                                                         else\r
4580                                                                                 rng.setStartBefore(node);\r
4581                                                                 }\r
4582                                                         } else {\r
4583                                                                 if (node.nodeType == 3 && point.offset)\r
4584                                                                         rng.setEnd(node, point.offset);\r
4585                                                                 else {\r
4586                                                                         if (point.exclude)\r
4587                                                                                 rng.setEndBefore(node);\r
4588                                                                         else\r
4589                                                                                 rng.setEndAfter(node);\r
4590                                                                 }\r
4591                                                         }\r
4592                                                 }\r
4593                                         };\r
4594 \r
4595                                         setEndPoint(true);\r
4596                                         setEndPoint();\r
4597 \r
4598                                         t.setRng(rng);\r
4599                                 } else if (bookmark.id) {\r
4600                                         rng = dom.createRng();\r
4601 \r
4602                                         function restoreEndPoint(suffix) {\r
4603                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
4604 \r
4605                                                 if (marker) {\r
4606                                                         node = marker.parentNode;\r
4607 \r
4608                                                         if (suffix == 'start') {\r
4609                                                                 if (!keep) {\r
4610                                                                         idx = dom.nodeIndex(marker);\r
4611 \r
4612                                                                         if (idx > 0)\r
4613                                                                                 idx++;\r
4614                                                                 } else {\r
4615                                                                         node = marker;\r
4616                                                                         idx = 1;\r
4617                                                                 }\r
4618 \r
4619                                                                 rng.setStart(node, idx);\r
4620                                                                 rng.setEnd(node, idx);\r
4621                                                         } else {\r
4622                                                                 if (!keep) {\r
4623                                                                         idx = dom.nodeIndex(marker);\r
4624                                                                 } else {\r
4625                                                                         node = marker;\r
4626                                                                         idx = 1;\r
4627                                                                 }\r
4628 \r
4629                                                                 rng.setEnd(node, idx);\r
4630                                                         }\r
4631 \r
4632                                                         if (!keep) {\r
4633                                                                 prev = marker.previousSibling;\r
4634                                                                 next = marker.nextSibling;\r
4635 \r
4636                                                                 // Remove all marker text nodes\r
4637                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
4638                                                                         if (node.nodeType == 3)\r
4639                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
4640                                                                 });\r
4641 \r
4642                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
4643                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
4644                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
4645                                                                         dom.remove(marker, 1);\r
4646 \r
4647                                                                 // If siblings are text nodes then merge them\r
4648                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) {\r
4649                                                                         idx = prev.nodeValue.length;\r
4650                                                                         prev.appendData(next.nodeValue);\r
4651                                                                         dom.remove(next);\r
4652 \r
4653                                                                         if (suffix == 'start') {\r
4654                                                                                 rng.setStart(prev, idx);\r
4655                                                                                 rng.setEnd(prev, idx);\r
4656                                                                         } else\r
4657                                                                                 rng.setEnd(prev, idx);\r
4658                                                                 }\r
4659                                                         }\r
4660                                                 }\r
4661                                         };\r
4662 \r
4663                                         // Restore start/end points\r
4664                                         restoreEndPoint('start');\r
4665                                         restoreEndPoint('end');\r
4666 \r
4667                                         t.setRng(rng);\r
4668                                 } else if (bookmark.name) {\r
4669                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
4670                                 } else if (bookmark.rng)\r
4671                                         t.setRng(bookmark.rng);\r
4672                         }\r
4673                 },\r
4674 \r
4675                 select : function(node, content) {\r
4676                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
4677 \r
4678                         idx = dom.nodeIndex(node);\r
4679                         rng.setStart(node.parentNode, idx);\r
4680                         rng.setEnd(node.parentNode, idx + 1);\r
4681 \r
4682                         // Find first/last text node or BR element\r
4683                         if (content) {\r
4684                                 function setPoint(node, start) {\r
4685                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
4686 \r
4687                                         do {\r
4688                                                 // Text node\r
4689                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
4690                                                         if (start)\r
4691                                                                 rng.setStart(node, 0);\r
4692                                                         else\r
4693                                                                 rng.setEnd(node, node.nodeValue.length);\r
4694 \r
4695                                                         return;\r
4696                                                 }\r
4697 \r
4698                                                 // BR element\r
4699                                                 if (node.nodeName == 'BR') {\r
4700                                                         if (start)\r
4701                                                                 rng.setStartBefore(node);\r
4702                                                         else\r
4703                                                                 rng.setEndBefore(node);\r
4704 \r
4705                                                         return;\r
4706                                                 }\r
4707                                         } while (node = (start ? walker.next() : walker.prev()));\r
4708                                 };\r
4709 \r
4710                                 setPoint(node, 1);\r
4711                                 setPoint(node);\r
4712                         }\r
4713 \r
4714                         t.setRng(rng);\r
4715 \r
4716                         return node;\r
4717                 },\r
4718 \r
4719                 isCollapsed : function() {\r
4720                         var t = this, r = t.getRng(), s = t.getSel();\r
4721 \r
4722                         if (!r || r.item)\r
4723                                 return false;\r
4724 \r
4725                         if (r.compareEndPoints)\r
4726                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
4727 \r
4728                         return !s || r.collapsed;\r
4729                 },\r
4730 \r
4731                 collapse : function(b) {\r
4732                         var t = this, r = t.getRng(), n;\r
4733 \r
4734                         // Control range on IE\r
4735                         if (r.item) {\r
4736                                 n = r.item(0);\r
4737                                 r = this.win.document.body.createTextRange();\r
4738                                 r.moveToElementText(n);\r
4739                         }\r
4740 \r
4741                         r.collapse(!!b);\r
4742                         t.setRng(r);\r
4743                 },\r
4744 \r
4745                 getSel : function() {\r
4746                         var t = this, w = this.win;\r
4747 \r
4748                         return w.getSelection ? w.getSelection() : w.document.selection;\r
4749                 },\r
4750 \r
4751                 getRng : function(w3c) {\r
4752                         var t = this, s, r;\r
4753 \r
4754                         // Found tridentSel object then we need to use that one\r
4755                         if (w3c && t.tridentSel)\r
4756                                 return t.tridentSel.getRangeAt(0);\r
4757 \r
4758                         try {\r
4759                                 if (s = t.getSel())\r
4760                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
4761                         } catch (ex) {\r
4762                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
4763                         }\r
4764 \r
4765                         // No range found then create an empty one\r
4766                         // This can occur when the editor is placed in a hidden container element on Gecko\r
4767                         // Or on IE when there was an exception\r
4768                         if (!r)\r
4769                                 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();\r
4770 \r
4771                         return r;\r
4772                 },\r
4773 \r
4774                 setRng : function(r) {\r
4775                         var s, t = this;\r
4776 \r
4777                         if (!t.tridentSel) {\r
4778                                 s = t.getSel();\r
4779 \r
4780                                 if (s) {\r
4781                                         s.removeAllRanges();\r
4782                                         s.addRange(r);\r
4783                                 }\r
4784                         } else {\r
4785                                 // Is W3C Range\r
4786                                 if (r.cloneRange) {\r
4787                                         t.tridentSel.addRange(r);\r
4788                                         return;\r
4789                                 }\r
4790 \r
4791                                 // Is IE specific range\r
4792                                 try {\r
4793                                         r.select();\r
4794                                 } catch (ex) {\r
4795                                         // Needed for some odd IE bug #1843306\r
4796                                 }\r
4797                         }\r
4798                 },\r
4799 \r
4800                 setNode : function(n) {\r
4801                         var t = this;\r
4802 \r
4803                         t.setContent(t.dom.getOuterHTML(n));\r
4804 \r
4805                         return n;\r
4806                 },\r
4807 \r
4808                 getNode : function() {\r
4809                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
4810 \r
4811                         if (!isIE) {\r
4812                                 // Range maybe lost after the editor is made visible again\r
4813                                 if (!rng)\r
4814                                         return t.dom.getRoot();\r
4815 \r
4816                                 elm = rng.commonAncestorContainer;\r
4817 \r
4818                                 // Handle selection a image or other control like element such as anchors\r
4819                                 if (!rng.collapsed) {\r
4820                                         if (rng.startContainer == rng.endContainer) {\r
4821                                                 if (rng.startOffset - rng.endOffset < 2) {\r
4822                                                         if (rng.startContainer.hasChildNodes())\r
4823                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
4824                                                 }\r
4825                                         }\r
4826 \r
4827                                         // If the anchor node is a element instead of a text node then return this element\r
4828                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
4829                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
4830                                 }\r
4831 \r
4832                                 if (elm && elm.nodeType == 3)\r
4833                                         return elm.parentNode;\r
4834 \r
4835                                 return elm;\r
4836                         }\r
4837 \r
4838                         return rng.item ? rng.item(0) : rng.parentElement();\r
4839                 },\r
4840 \r
4841                 getSelectedBlocks : function(st, en) {\r
4842                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
4843 \r
4844                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
4845                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
4846 \r
4847                         if (sb)\r
4848                                 bl.push(sb);\r
4849 \r
4850                         if (sb && eb && sb != eb) {\r
4851                                 n = sb;\r
4852 \r
4853                                 while ((n = n.nextSibling) && n != eb) {\r
4854                                         if (dom.isBlock(n))\r
4855                                                 bl.push(n);\r
4856                                 }\r
4857                         }\r
4858 \r
4859                         if (eb && sb != eb)\r
4860                                 bl.push(eb);\r
4861 \r
4862                         return bl;\r
4863                 },\r
4864 \r
4865                 destroy : function(s) {\r
4866                         var t = this;\r
4867 \r
4868                         t.win = null;\r
4869 \r
4870                         if (t.tridentSel)\r
4871                                 t.tridentSel.destroy();\r
4872 \r
4873                         // Manual destroy then remove unload handler\r
4874                         if (!s)\r
4875                                 tinymce.removeUnload(t.destroy);\r
4876                 }\r
4877         });\r
4878 })(tinymce);\r
4879 (function(tinymce) {\r
4880         tinymce.create('tinymce.dom.XMLWriter', {\r
4881                 node : null,\r
4882 \r
4883                 XMLWriter : function(s) {\r
4884                         // Get XML document\r
4885                         function getXML() {\r
4886                                 var i = document.implementation;\r
4887 \r
4888                                 if (!i || !i.createDocument) {\r
4889                                         // Try IE objects\r
4890                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
4891                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
4892                                 } else\r
4893                                         return i.createDocument('', '', null);\r
4894                         };\r
4895 \r
4896                         this.doc = getXML();\r
4897                         \r
4898                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
4899                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
4900 \r
4901                         this.reset();\r
4902                 },\r
4903 \r
4904                 reset : function() {\r
4905                         var t = this, d = t.doc;\r
4906 \r
4907                         if (d.firstChild)\r
4908                                 d.removeChild(d.firstChild);\r
4909 \r
4910                         t.node = d.appendChild(d.createElement("html"));\r
4911                 },\r
4912 \r
4913                 writeStartElement : function(n) {\r
4914                         var t = this;\r
4915 \r
4916                         t.node = t.node.appendChild(t.doc.createElement(n));\r
4917                 },\r
4918 \r
4919                 writeAttribute : function(n, v) {\r
4920                         if (this.valid)\r
4921                                 v = v.replace(/>/g, '%MCGT%');\r
4922 \r
4923                         this.node.setAttribute(n, v);\r
4924                 },\r
4925 \r
4926                 writeEndElement : function() {\r
4927                         this.node = this.node.parentNode;\r
4928                 },\r
4929 \r
4930                 writeFullEndElement : function() {\r
4931                         var t = this, n = t.node;\r
4932 \r
4933                         n.appendChild(t.doc.createTextNode(""));\r
4934                         t.node = n.parentNode;\r
4935                 },\r
4936 \r
4937                 writeText : function(v) {\r
4938                         if (this.valid)\r
4939                                 v = v.replace(/>/g, '%MCGT%');\r
4940 \r
4941                         this.node.appendChild(this.doc.createTextNode(v));\r
4942                 },\r
4943 \r
4944                 writeCDATA : function(v) {\r
4945                         this.node.appendChild(this.doc.createCDATASection(v));\r
4946                 },\r
4947 \r
4948                 writeComment : function(v) {\r
4949                         // Fix for bug #2035694\r
4950                         if (tinymce.isIE)\r
4951                                 v = v.replace(/^\-|\-$/g, ' ');\r
4952 \r
4953                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
4954                 },\r
4955 \r
4956                 getContent : function() {\r
4957                         var h;\r
4958 \r
4959                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
4960                         h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
4961                         h = h.replace(/ ?\/>/g, ' />');\r
4962 \r
4963                         if (this.valid)\r
4964                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
4965 \r
4966                         return h;\r
4967                 }\r
4968         });\r
4969 })(tinymce);\r
4970 (function(tinymce) {\r
4971         tinymce.create('tinymce.dom.StringWriter', {\r
4972                 str : null,\r
4973                 tags : null,\r
4974                 count : 0,\r
4975                 settings : null,\r
4976                 indent : null,\r
4977 \r
4978                 StringWriter : function(s) {\r
4979                         this.settings = tinymce.extend({\r
4980                                 indent_char : ' ',\r
4981                                 indentation : 0\r
4982                         }, s);\r
4983 \r
4984                         this.reset();\r
4985                 },\r
4986 \r
4987                 reset : function() {\r
4988                         this.indent = '';\r
4989                         this.str = "";\r
4990                         this.tags = [];\r
4991                         this.count = 0;\r
4992                 },\r
4993 \r
4994                 writeStartElement : function(n) {\r
4995                         this._writeAttributesEnd();\r
4996                         this.writeRaw('<' + n);\r
4997                         this.tags.push(n);\r
4998                         this.inAttr = true;\r
4999                         this.count++;\r
5000                         this.elementCount = this.count;\r
5001                 },\r
5002 \r
5003                 writeAttribute : function(n, v) {\r
5004                         var t = this;\r
5005 \r
5006                         t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
5007                 },\r
5008 \r
5009                 writeEndElement : function() {\r
5010                         var n;\r
5011 \r
5012                         if (this.tags.length > 0) {\r
5013                                 n = this.tags.pop();\r
5014 \r
5015                                 if (this._writeAttributesEnd(1))\r
5016                                         this.writeRaw('</' + n + '>');\r
5017 \r
5018                                 if (this.settings.indentation > 0)\r
5019                                         this.writeRaw('\n');\r
5020                         }\r
5021                 },\r
5022 \r
5023                 writeFullEndElement : function() {\r
5024                         if (this.tags.length > 0) {\r
5025                                 this._writeAttributesEnd();\r
5026                                 this.writeRaw('</' + this.tags.pop() + '>');\r
5027 \r
5028                                 if (this.settings.indentation > 0)\r
5029                                         this.writeRaw('\n');\r
5030                         }\r
5031                 },\r
5032 \r
5033                 writeText : function(v) {\r
5034                         this._writeAttributesEnd();\r
5035                         this.writeRaw(this.encode(v));\r
5036                         this.count++;\r
5037                 },\r
5038 \r
5039                 writeCDATA : function(v) {\r
5040                         this._writeAttributesEnd();\r
5041                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5042                         this.count++;\r
5043                 },\r
5044 \r
5045                 writeComment : function(v) {\r
5046                         this._writeAttributesEnd();\r
5047                         this.writeRaw('<!-- ' + v + '-->');\r
5048                         this.count++;\r
5049                 },\r
5050 \r
5051                 writeRaw : function(v) {\r
5052                         this.str += v;\r
5053                 },\r
5054 \r
5055                 encode : function(s) {\r
5056                         return s.replace(/[<>&"]/g, function(v) {\r
5057                                 switch (v) {\r
5058                                         case '<':\r
5059                                                 return '&lt;';\r
5060 \r
5061                                         case '>':\r
5062                                                 return '&gt;';\r
5063 \r
5064                                         case '&':\r
5065                                                 return '&amp;';\r
5066 \r
5067                                         case '"':\r
5068                                                 return '&quot;';\r
5069                                 }\r
5070 \r
5071                                 return v;\r
5072                         });\r
5073                 },\r
5074 \r
5075                 getContent : function() {\r
5076                         return this.str;\r
5077                 },\r
5078 \r
5079                 _writeAttributesEnd : function(s) {\r
5080                         if (!this.inAttr)\r
5081                                 return;\r
5082 \r
5083                         this.inAttr = false;\r
5084 \r
5085                         if (s && this.elementCount == this.count) {\r
5086                                 this.writeRaw(' />');\r
5087                                 return false;\r
5088                         }\r
5089 \r
5090                         this.writeRaw('>');\r
5091 \r
5092                         return true;\r
5093                 }\r
5094         });\r
5095 })(tinymce);\r
5096 (function(tinymce) {\r
5097         // Shorten names\r
5098         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5099 \r
5100         function wildcardToRE(s) {\r
5101                 return s.replace(/([?+*])/g, '.$1');\r
5102         };\r
5103 \r
5104         tinymce.create('tinymce.dom.Serializer', {\r
5105                 Serializer : function(s) {\r
5106                         var t = this;\r
5107 \r
5108                         t.key = 0;\r
5109                         t.onPreProcess = new Dispatcher(t);\r
5110                         t.onPostProcess = new Dispatcher(t);\r
5111 \r
5112                         try {\r
5113                                 t.writer = new tinymce.dom.XMLWriter();\r
5114                         } catch (ex) {\r
5115                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5116                                 t.writer = new tinymce.dom.StringWriter();\r
5117                         }\r
5118 \r
5119                         // Default settings\r
5120                         t.settings = s = extend({\r
5121                                 dom : tinymce.DOM,\r
5122                                 valid_nodes : 0,\r
5123                                 node_filter : 0,\r
5124                                 attr_filter : 0,\r
5125                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5126                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5127                                 entity_encoding : 'named',\r
5128                                 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
5129                                 valid_elements : '*[*]',\r
5130                                 extended_valid_elements : 0,\r
5131                                 invalid_elements : 0,\r
5132                                 fix_table_elements : 1,\r
5133                                 fix_list_elements : true,\r
5134                                 fix_content_duplication : true,\r
5135                                 convert_fonts_to_spans : false,\r
5136                                 font_size_classes : 0,\r
5137                                 apply_source_formatting : 0,\r
5138                                 indent_mode : 'simple',\r
5139                                 indent_char : '\t',\r
5140                                 indent_levels : 1,\r
5141                                 remove_linebreaks : 1,\r
5142                                 remove_redundant_brs : 1,\r
5143                                 element_format : 'xhtml'\r
5144                         }, s);\r
5145 \r
5146                         t.dom = s.dom;\r
5147                         t.schema = s.schema;\r
5148 \r
5149                         // Use raw entities if no entities are defined\r
5150                         if (s.entity_encoding == 'named' && !s.entities)\r
5151                                 s.entity_encoding = 'raw';\r
5152 \r
5153                         if (s.remove_redundant_brs) {\r
5154                                 t.onPostProcess.add(function(se, o) {\r
5155                                         // Remove single BR at end of block elements since they get rendered\r
5156                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5157                                                 // Check if it's a single element\r
5158                                                 if (/^<br \/>\s*<\//.test(a))\r
5159                                                         return '</' + c + '>';\r
5160 \r
5161                                                 return a;\r
5162                                         });\r
5163                                 });\r
5164                         }\r
5165 \r
5166                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
5167                         if (s.element_format == 'html') {\r
5168                                 t.onPostProcess.add(function(se, o) {\r
5169                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
5170                                 });\r
5171                         }\r
5172 \r
5173                         if (s.fix_list_elements) {\r
5174                                 t.onPreProcess.add(function(se, o) {\r
5175                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
5176 \r
5177                                         function prevNode(e, n) {\r
5178                                                 var a = n.split(','), i;\r
5179 \r
5180                                                 while ((e = e.previousSibling) != null) {\r
5181                                                         for (i=0; i<a.length; i++) {\r
5182                                                                 if (e.nodeName == a[i])\r
5183                                                                         return e;\r
5184                                                         }\r
5185                                                 }\r
5186 \r
5187                                                 return null;\r
5188                                         };\r
5189 \r
5190                                         for (x=0; x<a.length; x++) {\r
5191                                                 nl = t.dom.select(a[x], o.node);\r
5192 \r
5193                                                 for (i=0; i<nl.length; i++) {\r
5194                                                         n = nl[i];\r
5195                                                         p = n.parentNode;\r
5196 \r
5197                                                         if (r.test(p.nodeName)) {\r
5198                                                                 np = prevNode(n, 'LI');\r
5199 \r
5200                                                                 if (!np) {\r
5201                                                                         np = t.dom.create('li');\r
5202                                                                         np.innerHTML = '&nbsp;';\r
5203                                                                         np.appendChild(n);\r
5204                                                                         p.insertBefore(np, p.firstChild);\r
5205                                                                 } else\r
5206                                                                         np.appendChild(n);\r
5207                                                         }\r
5208                                                 }\r
5209                                         }\r
5210                                 });\r
5211                         }\r
5212 \r
5213                         if (s.fix_table_elements) {\r
5214                                 t.onPreProcess.add(function(se, o) {\r
5215                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
5216                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
5217                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
5218                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
5219                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
5220 \r
5221                                                         if (parent.nodeName != 'TABLE') {\r
5222                                                                 try {\r
5223                                                                         t.dom.split(parent, n);\r
5224                                                                 } catch (ex) {\r
5225                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
5226                                                                 }\r
5227                                                         }\r
5228                                                 });\r
5229                                         }\r
5230                                 });\r
5231                         }\r
5232                 },\r
5233 \r
5234                 setEntities : function(s) {\r
5235                         var t = this, a, i, l = {}, v;\r
5236 \r
5237                         // No need to setup more than once\r
5238                         if (t.entityLookup)\r
5239                                 return;\r
5240 \r
5241                         // Build regex and lookup array\r
5242                         a = s.split(',');\r
5243                         for (i = 0; i < a.length; i += 2) {\r
5244                                 v = a[i];\r
5245 \r
5246                                 // Don't add default &amp; &quot; etc.\r
5247                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
5248                                         continue;\r
5249 \r
5250                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
5251 \r
5252                                 v = parseInt(a[i]).toString(16);\r
5253                         }\r
5254 \r
5255                         t.entityLookup = l;\r
5256                 },\r
5257 \r
5258                 setRules : function(s) {\r
5259                         var t = this;\r
5260 \r
5261                         t._setup();\r
5262                         t.rules = {};\r
5263                         t.wildRules = [];\r
5264                         t.validElements = {};\r
5265 \r
5266                         return t.addRules(s);\r
5267                 },\r
5268 \r
5269                 addRules : function(s) {\r
5270                         var t = this, dr;\r
5271 \r
5272                         if (!s)\r
5273                                 return;\r
5274 \r
5275                         t._setup();\r
5276 \r
5277                         each(s.split(','), function(s) {\r
5278                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
5279 \r
5280                                 // Extend with default rules\r
5281                                 if (dr)\r
5282                                         at = tinymce.extend([], dr.attribs);\r
5283 \r
5284                                 // Parse attributes\r
5285                                 if (p.length > 1) {\r
5286                                         each(p[1].split('|'), function(s) {\r
5287                                                 var ar = {}, i;\r
5288 \r
5289                                                 at = at || [];\r
5290 \r
5291                                                 // Parse attribute rule\r
5292                                                 s = s.replace(/::/g, '~');\r
5293                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
5294                                                 s[2] = s[2].replace(/~/g, ':');\r
5295 \r
5296                                                 // Add required attributes\r
5297                                                 if (s[1] == '!') {\r
5298                                                         ra = ra || [];\r
5299                                                         ra.push(s[2]);\r
5300                                                 }\r
5301 \r
5302                                                 // Remove inherited attributes\r
5303                                                 if (s[1] == '-') {\r
5304                                                         for (i = 0; i <at.length; i++) {\r
5305                                                                 if (at[i].name == s[2]) {\r
5306                                                                         at.splice(i, 1);\r
5307                                                                         return;\r
5308                                                                 }\r
5309                                                         }\r
5310                                                 }\r
5311 \r
5312                                                 switch (s[3]) {\r
5313                                                         // Add default attrib values\r
5314                                                         case '=':\r
5315                                                                 ar.defaultVal = s[4] || '';\r
5316                                                                 break;\r
5317 \r
5318                                                         // Add forced attrib values\r
5319                                                         case ':':\r
5320                                                                 ar.forcedVal = s[4];\r
5321                                                                 break;\r
5322 \r
5323                                                         // Add validation values\r
5324                                                         case '<':\r
5325                                                                 ar.validVals = s[4].split('?');\r
5326                                                                 break;\r
5327                                                 }\r
5328 \r
5329                                                 if (/[*.?]/.test(s[2])) {\r
5330                                                         wat = wat || [];\r
5331                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
5332                                                         wat.push(ar);\r
5333                                                 } else {\r
5334                                                         ar.name = s[2];\r
5335                                                         at.push(ar);\r
5336                                                 }\r
5337 \r
5338                                                 va.push(s[2]);\r
5339                                         });\r
5340                                 }\r
5341 \r
5342                                 // Handle element names\r
5343                                 each(tn, function(s, i) {\r
5344                                         var pr = s.charAt(0), x = 1, ru = {};\r
5345 \r
5346                                         // Extend with default rule data\r
5347                                         if (dr) {\r
5348                                                 if (dr.noEmpty)\r
5349                                                         ru.noEmpty = dr.noEmpty;\r
5350 \r
5351                                                 if (dr.fullEnd)\r
5352                                                         ru.fullEnd = dr.fullEnd;\r
5353 \r
5354                                                 if (dr.padd)\r
5355                                                         ru.padd = dr.padd;\r
5356                                         }\r
5357 \r
5358                                         // Handle prefixes\r
5359                                         switch (pr) {\r
5360                                                 case '-':\r
5361                                                         ru.noEmpty = true;\r
5362                                                         break;\r
5363 \r
5364                                                 case '+':\r
5365                                                         ru.fullEnd = true;\r
5366                                                         break;\r
5367 \r
5368                                                 case '#':\r
5369                                                         ru.padd = true;\r
5370                                                         break;\r
5371 \r
5372                                                 default:\r
5373                                                         x = 0;\r
5374                                         }\r
5375 \r
5376                                         tn[i] = s = s.substring(x);\r
5377                                         t.validElements[s] = 1;\r
5378 \r
5379                                         // Add element name or element regex\r
5380                                         if (/[*.?]/.test(tn[0])) {\r
5381                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
5382                                                 t.wildRules = t.wildRules || {};\r
5383                                                 t.wildRules.push(ru);\r
5384                                         } else {\r
5385                                                 ru.name = tn[0];\r
5386 \r
5387                                                 // Store away default rule\r
5388                                                 if (tn[0] == '@')\r
5389                                                         dr = ru;\r
5390 \r
5391                                                 t.rules[s] = ru;\r
5392                                         }\r
5393 \r
5394                                         ru.attribs = at;\r
5395 \r
5396                                         if (ra)\r
5397                                                 ru.requiredAttribs = ra;\r
5398 \r
5399                                         if (wat) {\r
5400                                                 // Build valid attributes regexp\r
5401                                                 s = '';\r
5402                                                 each(va, function(v) {\r
5403                                                         if (s)\r
5404                                                                 s += '|';\r
5405 \r
5406                                                         s += '(' + wildcardToRE(v) + ')';\r
5407                                                 });\r
5408                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
5409                                                 ru.wildAttribs = wat;\r
5410                                         }\r
5411                                 });\r
5412                         });\r
5413 \r
5414                         // Build valid elements regexp\r
5415                         s = '';\r
5416                         each(t.validElements, function(v, k) {\r
5417                                 if (s)\r
5418                                         s += '|';\r
5419 \r
5420                                 if (k != '@')\r
5421                                         s += k;\r
5422                         });\r
5423                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
5424 \r
5425                         //console.debug(t.validElementsRE.toString());\r
5426                         //console.dir(t.rules);\r
5427                         //console.dir(t.wildRules);\r
5428                 },\r
5429 \r
5430                 findRule : function(n) {\r
5431                         var t = this, rl = t.rules, i, r;\r
5432 \r
5433                         t._setup();\r
5434 \r
5435                         // Exact match\r
5436                         r = rl[n];\r
5437                         if (r)\r
5438                                 return r;\r
5439 \r
5440                         // Try wildcards\r
5441                         rl = t.wildRules;\r
5442                         for (i = 0; i < rl.length; i++) {\r
5443                                 if (rl[i].nameRE.test(n))\r
5444                                         return rl[i];\r
5445                         }\r
5446 \r
5447                         return null;\r
5448                 },\r
5449 \r
5450                 findAttribRule : function(ru, n) {\r
5451                         var i, wa = ru.wildAttribs;\r
5452 \r
5453                         for (i = 0; i < wa.length; i++) {\r
5454                                 if (wa[i].nameRE.test(n))\r
5455                                         return wa[i];\r
5456                         }\r
5457 \r
5458                         return null;\r
5459                 },\r
5460 \r
5461                 serialize : function(n, o) {\r
5462                         var h, t = this, doc, oldDoc, impl, selected;\r
5463 \r
5464                         t._setup();\r
5465                         o = o || {};\r
5466                         o.format = o.format || 'html';\r
5467                         t.processObj = o;\r
5468 \r
5469                         // IE looses the selected attribute on option elements so we need to store it\r
5470                         // See: http://support.microsoft.com/kb/829907\r
5471                         if (isIE) {\r
5472                                 selected = [];\r
5473                                 each(n.getElementsByTagName('option'), function(n) {\r
5474                                         var v = t.dom.getAttrib(n, 'selected');\r
5475 \r
5476                                         selected.push(v ? v : null);\r
5477                                 });\r
5478                         }\r
5479 \r
5480                         n = n.cloneNode(true);\r
5481 \r
5482                         // IE looses the selected attribute on option elements so we need to restore it\r
5483                         if (isIE) {\r
5484                                 each(n.getElementsByTagName('option'), function(n, i) {\r
5485                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
5486                                 });\r
5487                         }\r
5488 \r
5489                         // Nodes needs to be attached to something in WebKit/Opera\r
5490                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
5491                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
5492                         // This fix will make DOM ranges and make Sizzle happy!\r
5493                         impl = n.ownerDocument.implementation;\r
5494                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
5495                                 // Create an empty HTML document\r
5496                                 doc = impl.createHTMLDocument("");\r
5497 \r
5498                                 // Add the element or it's children if it's a body element to the new document\r
5499                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
5500                                         doc.body.appendChild(doc.importNode(node, true));\r
5501                                 });\r
5502 \r
5503                                 // Grab first child or body element for serialization\r
5504                                 if (n.nodeName != 'BODY')\r
5505                                         n = doc.body.firstChild;\r
5506                                 else\r
5507                                         n = doc.body;\r
5508 \r
5509                                 // set the new document in DOMUtils so createElement etc works\r
5510                                 oldDoc = t.dom.doc;\r
5511                                 t.dom.doc = doc;\r
5512                         }\r
5513 \r
5514                         t.key = '' + (parseInt(t.key) + 1);\r
5515 \r
5516                         // Pre process\r
5517                         if (!o.no_events) {\r
5518                                 o.node = n;\r
5519                                 t.onPreProcess.dispatch(t, o);\r
5520                         }\r
5521 \r
5522                         // Serialize HTML DOM into a string\r
5523                         t.writer.reset();\r
5524                         t._info = o;\r
5525                         t._serializeNode(n, o.getInner);\r
5526 \r
5527                         // Post process\r
5528                         o.content = t.writer.getContent();\r
5529 \r
5530                         // Restore the old document if it was changed\r
5531                         if (oldDoc)\r
5532                                 t.dom.doc = oldDoc;\r
5533 \r
5534                         if (!o.no_events)\r
5535                                 t.onPostProcess.dispatch(t, o);\r
5536 \r
5537                         t._postProcess(o);\r
5538                         o.node = null;\r
5539 \r
5540                         return tinymce.trim(o.content);\r
5541                 },\r
5542 \r
5543                 // Internal functions\r
5544 \r
5545                 _postProcess : function(o) {\r
5546                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
5547 \r
5548                         if (o.format == 'html') {\r
5549                                 // Protect some elements\r
5550                                 p = t._protect({\r
5551                                         content : h,\r
5552                                         patterns : [\r
5553                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
5554                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
5555                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
5556                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
5557                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
5558                                         ]\r
5559                                 });\r
5560 \r
5561                                 h = p.content;\r
5562 \r
5563                                 // Entity encode\r
5564                                 if (s.entity_encoding !== 'raw')\r
5565                                         h = t._encode(h);\r
5566 \r
5567                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
5568 /*                              if (o.set)\r
5569                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
5570                                 else\r
5571                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
5572 \r
5573                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
5574                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
5575                                 // This process is only done when getting contents out from the editor.\r
5576                                 if (!o.set) {\r
5577                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
5578                                         h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
5579 \r
5580                                         if (s.remove_linebreaks) {\r
5581                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
5582                                                 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
5583                                                 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
5584                                                 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
5585                                                 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
5586                                                 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
5587                                         }\r
5588 \r
5589                                         // Simple indentation\r
5590                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
5591                                                 // Add line breaks before and after block elements\r
5592                                                 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
5593                                                 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
5594                                                 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
5595                                                 h = h.replace(/\n\n/g, '\n');\r
5596                                         }\r
5597                                 }\r
5598 \r
5599                                 h = t._unprotect(h, p);\r
5600 \r
5601                                 // Restore CDATA sections\r
5602                                 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
5603 \r
5604                                 // Restore the \u00a0 character if raw mode is enabled\r
5605                                 if (s.entity_encoding == 'raw')\r
5606                                         h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
5607 \r
5608                                 // Restore noscript elements\r
5609                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
5610                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
5611                                 });\r
5612                         }\r
5613 \r
5614                         o.content = h;\r
5615                 },\r
5616 \r
5617                 _serializeNode : function(n, inner) {\r
5618                         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
5619 \r
5620                         if (!s.node_filter || s.node_filter(n)) {\r
5621                                 switch (n.nodeType) {\r
5622                                         case 1: // Element\r
5623                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
5624                                                         return;\r
5625 \r
5626                                                 iv = keep = false;\r
5627                                                 hc = n.hasChildNodes();\r
5628                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
5629 \r
5630                                                 // Get internal type\r
5631                                                 type = n.getAttribute('_mce_type');\r
5632                                                 if (type) {\r
5633                                                         if (!t._info.cleanup) {\r
5634                                                                 iv = true;\r
5635                                                                 return;\r
5636                                                         } else\r
5637                                                                 keep = 1;\r
5638                                                 }\r
5639 \r
5640                                                 // Add correct prefix on IE\r
5641                                                 if (isIE) {\r
5642                                                         if (n.scopeName !== 'HTML' && n.scopeName !== 'html')\r
5643                                                                 nn = n.scopeName + ':' + nn;\r
5644                                                 }\r
5645 \r
5646                                                 // Remove mce prefix on IE needed for the abbr element\r
5647                                                 if (nn.indexOf('mce:') === 0)\r
5648                                                         nn = nn.substring(4);\r
5649 \r
5650                                                 // Check if valid\r
5651                                                 if (!keep) {\r
5652                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
5653                                                                 iv = true;\r
5654                                                                 break;\r
5655                                                         }\r
5656                                                 }\r
5657 \r
5658                                                 if (isIE) {\r
5659                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
5660                                                         if (s.fix_content_duplication) {\r
5661                                                                 if (n._mce_serialized == t.key)\r
5662                                                                         return;\r
5663 \r
5664                                                                 n._mce_serialized = t.key;\r
5665                                                         }\r
5666 \r
5667                                                         // IE sometimes adds a / infront of the node name\r
5668                                                         if (nn.charAt(0) == '/')\r
5669                                                                 nn = nn.substring(1);\r
5670                                                 } else if (isGecko) {\r
5671                                                         // Ignore br elements\r
5672                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
5673                                                                 return;\r
5674                                                 }\r
5675 \r
5676                                                 // Check if valid child\r
5677                                                 if (s.validate_children) {\r
5678                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
5679                                                                 iv = true;\r
5680                                                                 break;\r
5681                                                         }\r
5682 \r
5683                                                         t.elementName = nn;\r
5684                                                 }\r
5685 \r
5686                                                 ru = t.findRule(nn);\r
5687                                                 nn = ru.name || nn;\r
5688                                                 closed = s.closed.test(nn);\r
5689 \r
5690                                                 // Skip empty nodes or empty node name in IE\r
5691                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
5692                                                         iv = true;\r
5693                                                         break;\r
5694                                                 }\r
5695 \r
5696                                                 // Check required\r
5697                                                 if (ru.requiredAttribs) {\r
5698                                                         a = ru.requiredAttribs;\r
5699 \r
5700                                                         for (i = a.length - 1; i >= 0; i--) {\r
5701                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
5702                                                                         break;\r
5703                                                         }\r
5704 \r
5705                                                         // None of the required was there\r
5706                                                         if (i == -1) {\r
5707                                                                 iv = true;\r
5708                                                                 break;\r
5709                                                         }\r
5710                                                 }\r
5711 \r
5712                                                 w.writeStartElement(nn);\r
5713 \r
5714                                                 // Add ordered attributes\r
5715                                                 if (ru.attribs) {\r
5716                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
5717                                                                 a = at[i];\r
5718                                                                 v = t._getAttrib(n, a);\r
5719 \r
5720                                                                 if (v !== null)\r
5721                                                                         w.writeAttribute(a.name, v);\r
5722                                                         }\r
5723                                                 }\r
5724 \r
5725                                                 // Add wild attributes\r
5726                                                 if (ru.validAttribsRE) {\r
5727                                                         at = t.dom.getAttribs(n);\r
5728                                                         for (i=at.length-1; i>-1; i--) {\r
5729                                                                 no = at[i];\r
5730 \r
5731                                                                 if (no.specified) {\r
5732                                                                         a = no.nodeName.toLowerCase();\r
5733 \r
5734                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
5735                                                                                 continue;\r
5736 \r
5737                                                                         ar = t.findAttribRule(ru, a);\r
5738                                                                         v = t._getAttrib(n, ar, a);\r
5739 \r
5740                                                                         if (v !== null)\r
5741                                                                                 w.writeAttribute(a, v);\r
5742                                                                 }\r
5743                                                         }\r
5744                                                 }\r
5745 \r
5746                                                 // Keep type attribute\r
5747                                                 if (type && keep)\r
5748                                                         w.writeAttribute('_mce_type', type);\r
5749 \r
5750                                                 // Write text from script\r
5751                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
5752                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
5753                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
5754                                                         hc = false;\r
5755                                                         break;\r
5756                                                 }\r
5757 \r
5758                                                 // Padd empty nodes with a &nbsp;\r
5759                                                 if (ru.padd) {\r
5760                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
5761                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
5762                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
5763                                                                         w.writeText('\u00a0');\r
5764                                                         } else if (!hc)\r
5765                                                                 w.writeText('\u00a0'); // No children then padd it\r
5766                                                 }\r
5767 \r
5768                                                 break;\r
5769 \r
5770                                         case 3: // Text\r
5771                                                 // Check if valid child\r
5772                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
5773                                                         return;\r
5774 \r
5775                                                 return w.writeText(n.nodeValue);\r
5776 \r
5777                                         case 4: // CDATA\r
5778                                                 return w.writeCDATA(n.nodeValue);\r
5779 \r
5780                                         case 8: // Comment\r
5781                                                 return w.writeComment(n.nodeValue);\r
5782                                 }\r
5783                         } else if (n.nodeType == 1)\r
5784                                 hc = n.hasChildNodes();\r
5785 \r
5786                         if (hc && !closed) {\r
5787                                 cn = n.firstChild;\r
5788 \r
5789                                 while (cn) {\r
5790                                         t._serializeNode(cn);\r
5791                                         t.elementName = nn;\r
5792                                         cn = cn.nextSibling;\r
5793                                 }\r
5794                         }\r
5795 \r
5796                         // Write element end\r
5797                         if (!iv) {\r
5798                                 if (!closed)\r
5799                                         w.writeFullEndElement();\r
5800                                 else\r
5801                                         w.writeEndElement();\r
5802                         }\r
5803                 },\r
5804 \r
5805                 _protect : function(o) {\r
5806                         var t = this;\r
5807 \r
5808                         o.items = o.items || [];\r
5809 \r
5810                         function enc(s) {\r
5811                                 return s.replace(/[\r\n\\]/g, function(c) {\r
5812                                         if (c === '\n')\r
5813                                                 return '\\n';\r
5814                                         else if (c === '\\')\r
5815                                                 return '\\\\';\r
5816 \r
5817                                         return '\\r';\r
5818                                 });\r
5819                         };\r
5820 \r
5821                         function dec(s) {\r
5822                                 return s.replace(/\\[\\rn]/g, function(c) {\r
5823                                         if (c === '\\n')\r
5824                                                 return '\n';\r
5825                                         else if (c === '\\\\')\r
5826                                                 return '\\';\r
5827 \r
5828                                         return '\r';\r
5829                                 });\r
5830                         };\r
5831 \r
5832                         each(o.patterns, function(p) {\r
5833                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
5834                                         b = dec(b);\r
5835 \r
5836                                         if (p.encode)\r
5837                                                 b = t._encode(b);\r
5838 \r
5839                                         o.items.push(b);\r
5840                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
5841                                 }));\r
5842                         });\r
5843 \r
5844                         return o;\r
5845                 },\r
5846 \r
5847                 _unprotect : function(h, o) {\r
5848                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
5849                                 return o.items[parseInt(b)];\r
5850                         });\r
5851 \r
5852                         o.items = [];\r
5853 \r
5854                         return h;\r
5855                 },\r
5856 \r
5857                 _encode : function(h) {\r
5858                         var t = this, s = t.settings, l;\r
5859 \r
5860                         // Entity encode\r
5861                         if (s.entity_encoding !== 'raw') {\r
5862                                 if (s.entity_encoding.indexOf('named') != -1) {\r
5863                                         t.setEntities(s.entities);\r
5864                                         l = t.entityLookup;\r
5865 \r
5866                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
5867                                                 var v;\r
5868 \r
5869                                                 if (v = l[a])\r
5870                                                         a = '&' + v + ';';\r
5871 \r
5872                                                 return a;\r
5873                                         });\r
5874                                 }\r
5875 \r
5876                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
5877                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
5878                                                 return '&#' + a.charCodeAt(0) + ';';\r
5879                                         });\r
5880                                 }\r
5881                         }\r
5882 \r
5883                         return h;\r
5884                 },\r
5885 \r
5886                 _setup : function() {\r
5887                         var t = this, s = this.settings;\r
5888 \r
5889                         if (t.done)\r
5890                                 return;\r
5891 \r
5892                         t.done = 1;\r
5893 \r
5894                         t.setRules(s.valid_elements);\r
5895                         t.addRules(s.extended_valid_elements);\r
5896 \r
5897                         if (s.invalid_elements)\r
5898                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
5899 \r
5900                         if (s.attrib_value_filter)\r
5901                                 t.attribValueFilter = s.attribValueFilter;\r
5902                 },\r
5903 \r
5904                 _getAttrib : function(n, a, na) {\r
5905                         var i, v;\r
5906 \r
5907                         na = na || a.name;\r
5908 \r
5909                         if (a.forcedVal && (v = a.forcedVal)) {\r
5910                                 if (v === '{$uid}')\r
5911                                         return this.dom.uniqueId();\r
5912 \r
5913                                 return v;\r
5914                         }\r
5915 \r
5916                         v = this.dom.getAttrib(n, na);\r
5917 \r
5918                         switch (na) {\r
5919                                 case 'rowspan':\r
5920                                 case 'colspan':\r
5921                                         // Whats the point? Remove usless attribute value\r
5922                                         if (v == '1')\r
5923                                                 v = '';\r
5924 \r
5925                                         break;\r
5926                         }\r
5927 \r
5928                         if (this.attribValueFilter)\r
5929                                 v = this.attribValueFilter(na, v, n);\r
5930 \r
5931                         if (a.validVals) {\r
5932                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
5933                                         if (v == a.validVals[i])\r
5934                                                 break;\r
5935                                 }\r
5936 \r
5937                                 if (i == -1)\r
5938                                         return null;\r
5939                         }\r
5940 \r
5941                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
5942                                 v = a.defaultVal;\r
5943 \r
5944                                 if (v === '{$uid}')\r
5945                                         return this.dom.uniqueId();\r
5946 \r
5947                                 return v;\r
5948                         } else {\r
5949                                 // Remove internal mceItemXX classes when content is extracted from editor\r
5950                                 if (na == 'class' && this.processObj.get)\r
5951                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
5952                         }\r
5953 \r
5954                         if (v === '')\r
5955                                 return null;\r
5956 \r
5957 \r
5958                         return v;\r
5959                 }\r
5960         });\r
5961 })(tinymce);\r
5962 (function(tinymce) {\r
5963         tinymce.dom.ScriptLoader = function(settings) {\r
5964                 var QUEUED = 0,\r
5965                         LOADING = 1,\r
5966                         LOADED = 2,\r
5967                         states = {},\r
5968                         queue = [],\r
5969                         scriptLoadedCallbacks = {},\r
5970                         queueLoadedCallbacks = [],\r
5971                         loading = 0,\r
5972                         undefined;\r
5973 \r
5974                 function loadScript(url, callback) {\r
5975                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
5976 \r
5977                         // Execute callback when script is loaded\r
5978                         function done() {\r
5979                                 dom.remove(id);\r
5980 \r
5981                                 if (elm)\r
5982                                         elm.onreadystatechange = elm.onload = elm = null;\r
5983 \r
5984                                 callback();\r
5985                         };\r
5986 \r
5987                         id = dom.uniqueId();\r
5988 \r
5989                         if (tinymce.isIE6) {\r
5990                                 uri = new tinymce.util.URI(url);\r
5991                                 loc = location;\r
5992 \r
5993                                 // If script is from same domain and we\r
5994                                 // use IE 6 then use XHR since it's more reliable\r
5995                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
5996                                         tinymce.util.XHR.send({\r
5997                                                 url : tinymce._addVer(uri.getURI()),\r
5998                                                 success : function(content) {\r
5999                                                         // Create new temp script element\r
6000                                                         var script = dom.create('script', {\r
6001                                                                 type : 'text/javascript'\r
6002                                                         });\r
6003 \r
6004                                                         // Evaluate script in global scope\r
6005                                                         script.text = content;\r
6006                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
6007                                                         dom.remove(script);\r
6008 \r
6009                                                         done();\r
6010                                                 }\r
6011                                         });\r
6012 \r
6013                                         return;\r
6014                                 }\r
6015                         }\r
6016 \r
6017                         // Create new script element\r
6018                         elm = dom.create('script', {\r
6019                                 id : id,\r
6020                                 type : 'text/javascript',\r
6021                                 src : tinymce._addVer(url)\r
6022                         });\r
6023 \r
6024                         // Add onload and readystate listeners\r
6025                         elm.onload = done;\r
6026                         elm.onreadystatechange = function() {\r
6027                                 var state = elm.readyState;\r
6028 \r
6029                                 // Loaded state is passed on IE 6 however there\r
6030                                 // are known issues with this method but we can't use\r
6031                                 // XHR in a cross domain loading\r
6032                                 if (state == 'complete' || state == 'loaded')\r
6033                                         done();\r
6034                         };\r
6035 \r
6036                         // Most browsers support this feature so we report errors\r
6037                         // for those at least to help users track their missing plugins etc\r
6038                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6039                         /*elm.onerror = function() {\r
6040                                 alert('Failed to load: ' + url);\r
6041                         };*/\r
6042 \r
6043                         // Add script to document\r
6044                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6045                 };\r
6046 \r
6047                 this.isDone = function(url) {\r
6048                         return states[url] == LOADED;\r
6049                 };\r
6050 \r
6051                 this.markDone = function(url) {\r
6052                         states[url] = LOADED;\r
6053                 };\r
6054 \r
6055                 this.add = this.load = function(url, callback, scope) {\r
6056                         var item, state = states[url];\r
6057 \r
6058                         // Add url to load queue\r
6059                         if (state == undefined) {\r
6060                                 queue.push(url);\r
6061                                 states[url] = QUEUED;\r
6062                         }\r
6063 \r
6064                         if (callback) {\r
6065                                 // Store away callback for later execution\r
6066                                 if (!scriptLoadedCallbacks[url])\r
6067                                         scriptLoadedCallbacks[url] = [];\r
6068 \r
6069                                 scriptLoadedCallbacks[url].push({\r
6070                                         func : callback,\r
6071                                         scope : scope || this\r
6072                                 });\r
6073                         }\r
6074                 };\r
6075 \r
6076                 this.loadQueue = function(callback, scope) {\r
6077                         this.loadScripts(queue, callback, scope);\r
6078                 };\r
6079 \r
6080                 this.loadScripts = function(scripts, callback, scope) {\r
6081                         var loadScripts;\r
6082 \r
6083                         function execScriptLoadedCallbacks(url) {\r
6084                                 // Execute URL callback functions\r
6085                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6086                                         callback.func.call(callback.scope);\r
6087                                 });\r
6088 \r
6089                                 scriptLoadedCallbacks[url] = undefined;\r
6090                         };\r
6091 \r
6092                         queueLoadedCallbacks.push({\r
6093                                 func : callback,\r
6094                                 scope : scope || this\r
6095                         });\r
6096 \r
6097                         loadScripts = function() {\r
6098                                 var loadingScripts = tinymce.grep(scripts);\r
6099 \r
6100                                 // Current scripts has been handled\r
6101                                 scripts.length = 0;\r
6102 \r
6103                                 // Load scripts that needs to be loaded\r
6104                                 tinymce.each(loadingScripts, function(url) {\r
6105                                         // Script is already loaded then execute script callbacks directly\r
6106                                         if (states[url] == LOADED) {\r
6107                                                 execScriptLoadedCallbacks(url);\r
6108                                                 return;\r
6109                                         }\r
6110 \r
6111                                         // Is script not loading then start loading it\r
6112                                         if (states[url] != LOADING) {\r
6113                                                 states[url] = LOADING;\r
6114                                                 loading++;\r
6115 \r
6116                                                 loadScript(url, function() {\r
6117                                                         states[url] = LOADED;\r
6118                                                         loading--;\r
6119 \r
6120                                                         execScriptLoadedCallbacks(url);\r
6121 \r
6122                                                         // Load more scripts if they where added by the recently loaded script\r
6123                                                         loadScripts();\r
6124                                                 });\r
6125                                         }\r
6126                                 });\r
6127 \r
6128                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6129                                 if (!loading) {\r
6130                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6131                                                 callback.func.call(callback.scope);\r
6132                                         });\r
6133 \r
6134                                         queueLoadedCallbacks.length = 0;\r
6135                                 }\r
6136                         };\r
6137 \r
6138                         loadScripts();\r
6139                 };\r
6140         };\r
6141 \r
6142         // Global script loader\r
6143         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6144 })(tinymce);\r
6145 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
6146         var node = start_node;\r
6147 \r
6148         function findSibling(node, start_name, sibling_name, shallow) {\r
6149                 var sibling, parent;\r
6150 \r
6151                 if (node) {\r
6152                         // Walk into nodes if it has a start\r
6153                         if (!shallow && node[start_name])\r
6154                                 return node[start_name];\r
6155 \r
6156                         // Return the sibling if it has one\r
6157                         if (node != root_node) {\r
6158                                 sibling = node[sibling_name];\r
6159                                 if (sibling)\r
6160                                         return sibling;\r
6161 \r
6162                                 // Walk up the parents to look for siblings\r
6163                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
6164                                         sibling = parent[sibling_name];\r
6165                                         if (sibling)\r
6166                                                 return sibling;\r
6167                                 }\r
6168                         }\r
6169                 }\r
6170         };\r
6171 \r
6172         this.current = function() {\r
6173                 return node;\r
6174         };\r
6175 \r
6176         this.next = function(shallow) {\r
6177                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
6178         };\r
6179 \r
6180         this.prev = function(shallow) {\r
6181                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
6182         };\r
6183 };\r
6184 (function() {\r
6185         var transitional = {};\r
6186 \r
6187         function unpack(lookup, data) {\r
6188                 var key;\r
6189 \r
6190                 function replace(value) {\r
6191                         return value.replace(/[A-Z]+/g, function(key) {\r
6192                                 return replace(lookup[key]);\r
6193                         });\r
6194                 };\r
6195 \r
6196                 // Unpack lookup\r
6197                 for (key in lookup) {\r
6198                         if (lookup.hasOwnProperty(key))\r
6199                                 lookup[key] = replace(lookup[key]);\r
6200                 }\r
6201 \r
6202                 // Unpack and parse data into object map\r
6203                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
6204                         var i, map = {};\r
6205 \r
6206                         children = children.split(/\|/);\r
6207 \r
6208                         for (i = children.length - 1; i >= 0; i--)\r
6209                                 map[children[i]] = 1;\r
6210 \r
6211                         transitional[name] = map;\r
6212                 });\r
6213         };\r
6214 \r
6215         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
6216         // we will later include the attributes here and use it as a default for valid elements but it\r
6217         // requires us to rewrite the serializer engine\r
6218         unpack({\r
6219                 Z : '#|H|K|N|O|P',\r
6220                 Y : '#|X|form|R|Q',\r
6221                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
6222                 W : 'pre|hr|blockquote|address|center|noframes',\r
6223                 U : 'ul|ol|dl|menu|dir',\r
6224                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
6225                 T : 'h1|h2|h3|h4|h5|h6',\r
6226                 ZB : '#|X|S|Q',\r
6227                 S : 'R|P',\r
6228                 ZA : '#|a|G|J|M|O|P',\r
6229                 R : '#|a|H|K|N|O',\r
6230                 Q : 'noscript|P',\r
6231                 P : 'ins|del|script',\r
6232                 O : 'input|select|textarea|label|button',\r
6233                 N : 'M|L',\r
6234                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
6235                 L : 'sub|sup',\r
6236                 K : 'J|I',\r
6237                 J : 'tt|i|b|u|s|strike',\r
6238                 I : 'big|small|font|basefont',\r
6239                 H : 'G|F',\r
6240                 G : 'br|span|bdo',\r
6241                 F : 'object|applet|img|map|iframe'\r
6242         }, 'script[]' + \r
6243                 'style[]' + \r
6244                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
6245                 'param[]' + \r
6246                 'p[S]' + \r
6247                 'a[Z]' + \r
6248                 'br[]' + \r
6249                 'span[S]' + \r
6250                 'bdo[S]' + \r
6251                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
6252                 'h1[S]' + \r
6253                 'img[]' + \r
6254                 'map[X|form|Q|area]' + \r
6255                 'h2[S]' + \r
6256                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
6257                 'h3[S]' + \r
6258                 'tt[S]' + \r
6259                 'i[S]' + \r
6260                 'b[S]' + \r
6261                 'u[S]' + \r
6262                 's[S]' + \r
6263                 'strike[S]' + \r
6264                 'big[S]' + \r
6265                 'small[S]' + \r
6266                 'font[S]' + \r
6267                 'basefont[]' + \r
6268                 'em[S]' + \r
6269                 'strong[S]' + \r
6270                 'dfn[S]' + \r
6271                 'code[S]' + \r
6272                 'q[S]' + \r
6273                 'samp[S]' + \r
6274                 'kbd[S]' + \r
6275                 'var[S]' + \r
6276                 'cite[S]' + \r
6277                 'abbr[S]' + \r
6278                 'acronym[S]' + \r
6279                 'sub[S]' + \r
6280                 'sup[S]' + \r
6281                 'input[]' + \r
6282                 'select[optgroup|option]' + \r
6283                 'optgroup[option]' + \r
6284                 'option[]' + \r
6285                 'textarea[]' + \r
6286                 'label[S]' + \r
6287                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
6288                 'h4[S]' + \r
6289                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
6290                 'h5[S]' + \r
6291                 'del[#|X|form|a|H|K|N|O|Q]' + \r
6292                 'h6[S]' + \r
6293                 'div[#|X|form|a|H|K|N|O|Q]' + \r
6294                 'ul[li]' + \r
6295                 'li[#|X|form|a|H|K|N|O|Q]' + \r
6296                 'ol[li]' + \r
6297                 'dl[dt|dd]' + \r
6298                 'dt[S]' + \r
6299                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
6300                 'menu[li]' + \r
6301                 'dir[li]' + \r
6302                 'pre[ZA]' + \r
6303                 'hr[]' + \r
6304                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
6305                 'address[S|p]' + \r
6306                 'center[#|X|form|a|H|K|N|O|Q]' + \r
6307                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
6308                 'isindex[]' + \r
6309                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
6310                 'legend[S]' + \r
6311                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
6312                 'caption[S]' + \r
6313                 'col[]' + \r
6314                 'colgroup[col]' + \r
6315                 'thead[tr]' + \r
6316                 'tr[th|td]' + \r
6317                 'th[#|X|form|a|H|K|N|O|Q]' + \r
6318                 'form[#|X|a|H|K|N|O|Q]' + \r
6319                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
6320                 'td[#|X|form|a|H|K|N|O|Q]' + \r
6321                 'tfoot[tr]' + \r
6322                 'tbody[tr]' + \r
6323                 'area[]' + \r
6324                 'base[]' + \r
6325                 'body[#|X|form|a|H|K|N|O|Q]'\r
6326         );\r
6327 \r
6328         tinymce.dom.Schema = function() {\r
6329                 var t = this, elements = transitional;\r
6330 \r
6331                 t.isValid = function(name, child_name) {\r
6332                         var element = elements[name];\r
6333 \r
6334                         return !!(element && (!child_name || element[child_name]));\r
6335                 };\r
6336         };\r
6337 })();(function(tinymce) {\r
6338         tinymce.dom.RangeUtils = function(dom) {\r
6339                 var INVISIBLE_CHAR = '\uFEFF';\r
6340 \r
6341                 this.walk = function(rng, callback) {\r
6342                         var startContainer = rng.startContainer,\r
6343                                 startOffset = rng.startOffset,\r
6344                                 endContainer = rng.endContainer,\r
6345                                 endOffset = rng.endOffset,\r
6346                                 ancestor, startPoint,\r
6347                                 endPoint, node, parent, siblings, nodes;\r
6348 \r
6349                         // Handle table cell selection the table plugin enables\r
6350                         // you to fake select table cells and perform formatting actions on them\r
6351                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
6352                         if (nodes.length > 0) {\r
6353                                 tinymce.each(nodes, function(node) {\r
6354                                         callback([node]);\r
6355                                 });\r
6356 \r
6357                                 return;\r
6358                         }\r
6359 \r
6360                         function collectSiblings(node, name, end_node) {\r
6361                                 var siblings = [];\r
6362 \r
6363                                 for (; node && node != end_node; node = node[name])\r
6364                                         siblings.push(node);\r
6365 \r
6366                                 return siblings;\r
6367                         };\r
6368 \r
6369                         function findEndPoint(node, root) {\r
6370                                 do {\r
6371                                         if (node.parentNode == root)\r
6372                                                 return node;\r
6373 \r
6374                                         node = node.parentNode;\r
6375                                 } while(node);\r
6376                         };\r
6377 \r
6378                         function walkBoundary(start_node, end_node, next) {\r
6379                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
6380 \r
6381                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
6382                                         parent = node.parentNode;\r
6383                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
6384 \r
6385                                         if (siblings.length) {\r
6386                                                 if (!next)\r
6387                                                         siblings.reverse();\r
6388 \r
6389                                                 callback(siblings);\r
6390                                         }\r
6391                                 }\r
6392                         };\r
6393 \r
6394                         // If index based start position then resolve it\r
6395                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
6396                                 startContainer = startContainer.childNodes[startOffset];\r
6397 \r
6398                         // If index based end position then resolve it\r
6399                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
6400                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
6401 \r
6402                         // Find common ancestor and end points\r
6403                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
6404 \r
6405                         // Same container\r
6406                         if (startContainer == endContainer)\r
6407                                 return callback([startContainer]);\r
6408 \r
6409                         // Process left side\r
6410                         for (node = startContainer; node; node = node.parentNode) {\r
6411                                 if (node == endContainer)\r
6412                                         return walkBoundary(startContainer, ancestor, true);\r
6413 \r
6414                                 if (node == ancestor)\r
6415                                         break;\r
6416                         }\r
6417 \r
6418                         // Process right side\r
6419                         for (node = endContainer; node; node = node.parentNode) {\r
6420                                 if (node == startContainer)\r
6421                                         return walkBoundary(endContainer, ancestor);\r
6422 \r
6423                                 if (node == ancestor)\r
6424                                         break;\r
6425                         }\r
6426 \r
6427                         // Find start/end point\r
6428                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
6429                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
6430 \r
6431                         // Walk left leaf\r
6432                         walkBoundary(startContainer, startPoint, true);\r
6433 \r
6434                         // Walk the middle from start to end point\r
6435                         siblings = collectSiblings(\r
6436                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
6437                                 'nextSibling',\r
6438                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
6439                         );\r
6440 \r
6441                         if (siblings.length)\r
6442                                 callback(siblings);\r
6443 \r
6444                         // Walk right leaf\r
6445                         walkBoundary(endContainer, endPoint);\r
6446                 };\r
6447 \r
6448                 /*              this.split = function(rng) {\r
6449                         var startContainer = rng.startContainer,\r
6450                                 startOffset = rng.startOffset,\r
6451                                 endContainer = rng.endContainer,\r
6452                                 endOffset = rng.endOffset;\r
6453 \r
6454                         function splitText(node, offset) {\r
6455                                 if (offset == node.nodeValue.length)\r
6456                                         node.appendData(INVISIBLE_CHAR);\r
6457 \r
6458                                 node = node.splitText(offset);\r
6459 \r
6460                                 if (node.nodeValue === INVISIBLE_CHAR)\r
6461                                         node.nodeValue = '';\r
6462 \r
6463                                 return node;\r
6464                         };\r
6465 \r
6466                         // Handle single text node\r
6467                         if (startContainer == endContainer) {\r
6468                                 if (startContainer.nodeType == 3) {\r
6469                                         if (startOffset != 0)\r
6470                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
6471 \r
6472                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
6473                                                 splitText(startContainer, endOffset - startOffset);\r
6474                                 }\r
6475                         } else {\r
6476                                 // Split startContainer text node if needed\r
6477                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
6478                                         startContainer = splitText(startContainer, startOffset);\r
6479                                         startOffset = 0;\r
6480                                 }\r
6481 \r
6482                                 // Split endContainer text node if needed\r
6483                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
6484                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
6485                                         endOffset = endContainer.nodeValue.length;\r
6486                                 }\r
6487                         }\r
6488 \r
6489                         return {\r
6490                                 startContainer : startContainer,\r
6491                                 startOffset : startOffset,\r
6492                                 endContainer : endContainer,\r
6493                                 endOffset : endOffset\r
6494                         };\r
6495                 };\r
6496 */\r
6497         };\r
6498 })(tinymce);\r
6499 (function(tinymce) {\r
6500         // Shorten class names\r
6501         var DOM = tinymce.DOM, is = tinymce.is;\r
6502 \r
6503         tinymce.create('tinymce.ui.Control', {\r
6504                 Control : function(id, s) {\r
6505                         this.id = id;\r
6506                         this.settings = s = s || {};\r
6507                         this.rendered = false;\r
6508                         this.onRender = new tinymce.util.Dispatcher(this);\r
6509                         this.classPrefix = '';\r
6510                         this.scope = s.scope || this;\r
6511                         this.disabled = 0;\r
6512                         this.active = 0;\r
6513                 },\r
6514 \r
6515                 setDisabled : function(s) {\r
6516                         var e;\r
6517 \r
6518                         if (s != this.disabled) {\r
6519                                 e = DOM.get(this.id);\r
6520 \r
6521                                 // Add accessibility title for unavailable actions\r
6522                                 if (e && this.settings.unavailable_prefix) {\r
6523                                         if (s) {\r
6524                                                 this.prevTitle = e.title;\r
6525                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
6526                                         } else\r
6527                                                 e.title = this.prevTitle;\r
6528                                 }\r
6529 \r
6530                                 this.setState('Disabled', s);\r
6531                                 this.setState('Enabled', !s);\r
6532                                 this.disabled = s;\r
6533                         }\r
6534                 },\r
6535 \r
6536                 isDisabled : function() {\r
6537                         return this.disabled;\r
6538                 },\r
6539 \r
6540                 setActive : function(s) {\r
6541                         if (s != this.active) {\r
6542                                 this.setState('Active', s);\r
6543                                 this.active = s;\r
6544                         }\r
6545                 },\r
6546 \r
6547                 isActive : function() {\r
6548                         return this.active;\r
6549                 },\r
6550 \r
6551                 setState : function(c, s) {\r
6552                         var n = DOM.get(this.id);\r
6553 \r
6554                         c = this.classPrefix + c;\r
6555 \r
6556                         if (s)\r
6557                                 DOM.addClass(n, c);\r
6558                         else\r
6559                                 DOM.removeClass(n, c);\r
6560                 },\r
6561 \r
6562                 isRendered : function() {\r
6563                         return this.rendered;\r
6564                 },\r
6565 \r
6566                 renderHTML : function() {\r
6567                 },\r
6568 \r
6569                 renderTo : function(n) {\r
6570                         DOM.setHTML(n, this.renderHTML());\r
6571                 },\r
6572 \r
6573                 postRender : function() {\r
6574                         var t = this, b;\r
6575 \r
6576                         // Set pending states\r
6577                         if (is(t.disabled)) {\r
6578                                 b = t.disabled;\r
6579                                 t.disabled = -1;\r
6580                                 t.setDisabled(b);\r
6581                         }\r
6582 \r
6583                         if (is(t.active)) {\r
6584                                 b = t.active;\r
6585                                 t.active = -1;\r
6586                                 t.setActive(b);\r
6587                         }\r
6588                 },\r
6589 \r
6590                 remove : function() {\r
6591                         DOM.remove(this.id);\r
6592                         this.destroy();\r
6593                 },\r
6594 \r
6595                 destroy : function() {\r
6596                         tinymce.dom.Event.clear(this.id);\r
6597                 }\r
6598         });\r
6599 })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
6600         Container : function(id, s) {\r
6601                 this.parent(id, s);\r
6602 \r
6603                 this.controls = [];\r
6604 \r
6605                 this.lookup = {};\r
6606         },\r
6607 \r
6608         add : function(c) {\r
6609                 this.lookup[c.id] = c;\r
6610                 this.controls.push(c);\r
6611 \r
6612                 return c;\r
6613         },\r
6614 \r
6615         get : function(n) {\r
6616                 return this.lookup[n];\r
6617         }\r
6618 });\r
6619 \r
6620 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
6621         Separator : function(id, s) {\r
6622                 this.parent(id, s);\r
6623                 this.classPrefix = 'mceSeparator';\r
6624         },\r
6625 \r
6626         renderHTML : function() {\r
6627                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
6628         }\r
6629 });\r
6630 (function(tinymce) {\r
6631         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
6632 \r
6633         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
6634                 MenuItem : function(id, s) {\r
6635                         this.parent(id, s);\r
6636                         this.classPrefix = 'mceMenuItem';\r
6637                 },\r
6638 \r
6639                 setSelected : function(s) {\r
6640                         this.setState('Selected', s);\r
6641                         this.selected = s;\r
6642                 },\r
6643 \r
6644                 isSelected : function() {\r
6645                         return this.selected;\r
6646                 },\r
6647 \r
6648                 postRender : function() {\r
6649                         var t = this;\r
6650                         \r
6651                         t.parent();\r
6652 \r
6653                         // Set pending state\r
6654                         if (is(t.selected))\r
6655                                 t.setSelected(t.selected);\r
6656                 }\r
6657         });\r
6658 })(tinymce);\r
6659 (function(tinymce) {\r
6660         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
6661 \r
6662         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
6663                 Menu : function(id, s) {\r
6664                         var t = this;\r
6665 \r
6666                         t.parent(id, s);\r
6667                         t.items = {};\r
6668                         t.collapsed = false;\r
6669                         t.menuCount = 0;\r
6670                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
6671                 },\r
6672 \r
6673                 expand : function(d) {\r
6674                         var t = this;\r
6675 \r
6676                         if (d) {\r
6677                                 walk(t, function(o) {\r
6678                                         if (o.expand)\r
6679                                                 o.expand();\r
6680                                 }, 'items', t);\r
6681                         }\r
6682 \r
6683                         t.collapsed = false;\r
6684                 },\r
6685 \r
6686                 collapse : function(d) {\r
6687                         var t = this;\r
6688 \r
6689                         if (d) {\r
6690                                 walk(t, function(o) {\r
6691                                         if (o.collapse)\r
6692                                                 o.collapse();\r
6693                                 }, 'items', t);\r
6694                         }\r
6695 \r
6696                         t.collapsed = true;\r
6697                 },\r
6698 \r
6699                 isCollapsed : function() {\r
6700                         return this.collapsed;\r
6701                 },\r
6702 \r
6703                 add : function(o) {\r
6704                         if (!o.settings)\r
6705                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
6706 \r
6707                         this.onAddItem.dispatch(this, o);\r
6708 \r
6709                         return this.items[o.id] = o;\r
6710                 },\r
6711 \r
6712                 addSeparator : function() {\r
6713                         return this.add({separator : true});\r
6714                 },\r
6715 \r
6716                 addMenu : function(o) {\r
6717                         if (!o.collapse)\r
6718                                 o = this.createMenu(o);\r
6719 \r
6720                         this.menuCount++;\r
6721 \r
6722                         return this.add(o);\r
6723                 },\r
6724 \r
6725                 hasMenus : function() {\r
6726                         return this.menuCount !== 0;\r
6727                 },\r
6728 \r
6729                 remove : function(o) {\r
6730                         delete this.items[o.id];\r
6731                 },\r
6732 \r
6733                 removeAll : function() {\r
6734                         var t = this;\r
6735 \r
6736                         walk(t, function(o) {\r
6737                                 if (o.removeAll)\r
6738                                         o.removeAll();\r
6739                                 else\r
6740                                         o.remove();\r
6741 \r
6742                                 o.destroy();\r
6743                         }, 'items', t);\r
6744 \r
6745                         t.items = {};\r
6746                 },\r
6747 \r
6748                 createMenu : function(o) {\r
6749                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
6750 \r
6751                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
6752 \r
6753                         return m;\r
6754                 }\r
6755         });\r
6756 })(tinymce);(function(tinymce) {\r
6757         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
6758 \r
6759         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
6760                 DropMenu : function(id, s) {\r
6761                         s = s || {};\r
6762                         s.container = s.container || DOM.doc.body;\r
6763                         s.offset_x = s.offset_x || 0;\r
6764                         s.offset_y = s.offset_y || 0;\r
6765                         s.vp_offset_x = s.vp_offset_x || 0;\r
6766                         s.vp_offset_y = s.vp_offset_y || 0;\r
6767 \r
6768                         if (is(s.icons) && !s.icons)\r
6769                                 s['class'] += ' mceNoIcons';\r
6770 \r
6771                         this.parent(id, s);\r
6772                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
6773                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
6774                         this.classPrefix = 'mceMenu';\r
6775                 },\r
6776 \r
6777                 createMenu : function(s) {\r
6778                         var t = this, cs = t.settings, m;\r
6779 \r
6780                         s.container = s.container || cs.container;\r
6781                         s.parent = t;\r
6782                         s.constrain = s.constrain || cs.constrain;\r
6783                         s['class'] = s['class'] || cs['class'];\r
6784                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
6785                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
6786                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
6787 \r
6788                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
6789 \r
6790                         return m;\r
6791                 },\r
6792 \r
6793                 update : function() {\r
6794                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
6795 \r
6796                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
6797                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
6798 \r
6799                         if (!DOM.boxModel)\r
6800                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
6801                         else\r
6802                                 t.element.setStyles({width : tw, height : th});\r
6803 \r
6804                         if (s.max_width)\r
6805                                 DOM.setStyle(co, 'width', tw);\r
6806 \r
6807                         if (s.max_height) {\r
6808                                 DOM.setStyle(co, 'height', th);\r
6809 \r
6810                                 if (tb.clientHeight < s.max_height)\r
6811                                         DOM.setStyle(co, 'overflow', 'hidden');\r
6812                         }\r
6813                 },\r
6814 \r
6815                 showMenu : function(x, y, px) {\r
6816                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
6817 \r
6818                         t.collapse(1);\r
6819 \r
6820                         if (t.isMenuVisible)\r
6821                                 return;\r
6822 \r
6823                         if (!t.rendered) {\r
6824                                 co = DOM.add(t.settings.container, t.renderNode());\r
6825 \r
6826                                 each(t.items, function(o) {\r
6827                                         o.postRender();\r
6828                                 });\r
6829 \r
6830                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
6831                         } else\r
6832                                 co = DOM.get('menu_' + t.id);\r
6833 \r
6834                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
6835                         if (!tinymce.isOpera)\r
6836                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
6837 \r
6838                         DOM.show(co);\r
6839                         t.update();\r
6840 \r
6841                         x += s.offset_x || 0;\r
6842                         y += s.offset_y || 0;\r
6843                         vp.w -= 4;\r
6844                         vp.h -= 4;\r
6845 \r
6846                         // Move inside viewport if not submenu\r
6847                         if (s.constrain) {\r
6848                                 w = co.clientWidth - ot;\r
6849                                 h = co.clientHeight - ot;\r
6850                                 mx = vp.x + vp.w;\r
6851                                 my = vp.y + vp.h;\r
6852 \r
6853                                 if ((x + s.vp_offset_x + w) > mx)\r
6854                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
6855 \r
6856                                 if ((y + s.vp_offset_y + h) > my)\r
6857                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
6858                         }\r
6859 \r
6860                         DOM.setStyles(co, {left : x , top : y});\r
6861                         t.element.update();\r
6862 \r
6863                         t.isMenuVisible = 1;\r
6864                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
6865                                 var m;\r
6866 \r
6867                                 e = e.target;\r
6868 \r
6869                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
6870                                         m = t.items[e.id];\r
6871 \r
6872                                         if (m.isDisabled())\r
6873                                                 return;\r
6874 \r
6875                                         dm = t;\r
6876 \r
6877                                         while (dm) {\r
6878                                                 if (dm.hideMenu)\r
6879                                                         dm.hideMenu();\r
6880 \r
6881                                                 dm = dm.settings.parent;\r
6882                                         }\r
6883 \r
6884                                         if (m.settings.onclick)\r
6885                                                 m.settings.onclick(e);\r
6886 \r
6887                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
6888                                 }\r
6889                         });\r
6890 \r
6891                         if (t.hasMenus()) {\r
6892                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
6893                                         var m, r, mi;\r
6894 \r
6895                                         e = e.target;\r
6896                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
6897                                                 m = t.items[e.id];\r
6898 \r
6899                                                 if (t.lastMenu)\r
6900                                                         t.lastMenu.collapse(1);\r
6901 \r
6902                                                 if (m.isDisabled())\r
6903                                                         return;\r
6904 \r
6905                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
6906                                                         //p = DOM.getPos(s.container);\r
6907                                                         r = DOM.getRect(e);\r
6908                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
6909                                                         t.lastMenu = m;\r
6910                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
6911                                                 }\r
6912                                         }\r
6913                                 });\r
6914                         }\r
6915 \r
6916                         t.onShowMenu.dispatch(t);\r
6917 \r
6918                         if (s.keyboard_focus) {\r
6919                                 Event.add(co, 'keydown', t._keyHandler, t);\r
6920                                 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
6921                                 t._focusIdx = 0;\r
6922                         }\r
6923                 },\r
6924 \r
6925                 hideMenu : function(c) {\r
6926                         var t = this, co = DOM.get('menu_' + t.id), e;\r
6927 \r
6928                         if (!t.isMenuVisible)\r
6929                                 return;\r
6930 \r
6931                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
6932                         Event.remove(co, 'click', t.mouseClickFunc);\r
6933                         Event.remove(co, 'keydown', t._keyHandler);\r
6934                         DOM.hide(co);\r
6935                         t.isMenuVisible = 0;\r
6936 \r
6937                         if (!c)\r
6938                                 t.collapse(1);\r
6939 \r
6940                         if (t.element)\r
6941                                 t.element.hide();\r
6942 \r
6943                         if (e = DOM.get(t.id))\r
6944                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
6945 \r
6946                         t.onHideMenu.dispatch(t);\r
6947                 },\r
6948 \r
6949                 add : function(o) {\r
6950                         var t = this, co;\r
6951 \r
6952                         o = t.parent(o);\r
6953 \r
6954                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
6955                                 t._add(DOM.select('tbody', co)[0], o);\r
6956 \r
6957                         return o;\r
6958                 },\r
6959 \r
6960                 collapse : function(d) {\r
6961                         this.parent(d);\r
6962                         this.hideMenu(1);\r
6963                 },\r
6964 \r
6965                 remove : function(o) {\r
6966                         DOM.remove(o.id);\r
6967                         this.destroy();\r
6968 \r
6969                         return this.parent(o);\r
6970                 },\r
6971 \r
6972                 destroy : function() {\r
6973                         var t = this, co = DOM.get('menu_' + t.id);\r
6974 \r
6975                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
6976                         Event.remove(co, 'click', t.mouseClickFunc);\r
6977 \r
6978                         if (t.element)\r
6979                                 t.element.remove();\r
6980 \r
6981                         DOM.remove(co);\r
6982                 },\r
6983 \r
6984                 renderNode : function() {\r
6985                         var t = this, s = t.settings, n, tb, co, w;\r
6986 \r
6987                         w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
6988                         co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
6989                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
6990 \r
6991                         if (s.menu_line)\r
6992                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
6993 \r
6994 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
6995                         n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
6996                         tb = DOM.add(n, 'tbody');\r
6997 \r
6998                         each(t.items, function(o) {\r
6999                                 t._add(tb, o);\r
7000                         });\r
7001 \r
7002                         t.rendered = true;\r
7003 \r
7004                         return w;\r
7005                 },\r
7006 \r
7007                 // Internal functions\r
7008 \r
7009                 _keyHandler : function(e) {\r
7010                         var t = this, kc = e.keyCode;\r
7011 \r
7012                         function focus(d) {\r
7013                                 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7014 \r
7015                                 if (e) {\r
7016                                         t._focusIdx = i;\r
7017                                         e.focus();\r
7018                                 }\r
7019                         };\r
7020 \r
7021                         switch (kc) {\r
7022                                 case 38:\r
7023                                         focus(-1); // Select first link\r
7024                                         return;\r
7025 \r
7026                                 case 40:\r
7027                                         focus(1);\r
7028                                         return;\r
7029 \r
7030                                 case 13:\r
7031                                         return;\r
7032 \r
7033                                 case 27:\r
7034                                         return this.hideMenu();\r
7035                         }\r
7036                 },\r
7037 \r
7038                 _add : function(tb, o) {\r
7039                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7040 \r
7041                         if (s.separator) {\r
7042                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7043                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7044 \r
7045                                 if (n = ro.previousSibling)\r
7046                                         DOM.addClass(n, 'mceLast');\r
7047 \r
7048                                 return;\r
7049                         }\r
7050 \r
7051                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7052                         n = it = DOM.add(n, 'td');\r
7053                         n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7054 \r
7055                         DOM.addClass(it, s['class']);\r
7056 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
7057 \r
7058                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7059 \r
7060                         if (s.icon_src)\r
7061                                 DOM.add(ic, 'img', {src : s.icon_src});\r
7062 \r
7063                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7064 \r
7065                         if (o.settings.style)\r
7066                                 DOM.setAttrib(n, 'style', o.settings.style);\r
7067 \r
7068                         if (tb.childNodes.length == 1)\r
7069                                 DOM.addClass(ro, 'mceFirst');\r
7070 \r
7071                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7072                                 DOM.addClass(ro, 'mceFirst');\r
7073 \r
7074                         if (o.collapse)\r
7075                                 DOM.addClass(ro, cp + 'ItemSub');\r
7076 \r
7077                         if (n = ro.previousSibling)\r
7078                                 DOM.removeClass(n, 'mceLast');\r
7079 \r
7080                         DOM.addClass(ro, 'mceLast');\r
7081                 }\r
7082         });\r
7083 })(tinymce);(function(tinymce) {\r
7084         var DOM = tinymce.DOM;\r
7085 \r
7086         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7087                 Button : function(id, s) {\r
7088                         this.parent(id, s);\r
7089                         this.classPrefix = 'mceButton';\r
7090                 },\r
7091 \r
7092                 renderHTML : function() {\r
7093                         var cp = this.classPrefix, s = this.settings, h, l;\r
7094 \r
7095                         l = DOM.encode(s.label || '');\r
7096                         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
7097 \r
7098                         if (s.image)\r
7099                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7100                         else\r
7101                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7102 \r
7103                         return h;\r
7104                 },\r
7105 \r
7106                 postRender : function() {\r
7107                         var t = this, s = t.settings;\r
7108 \r
7109                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7110                                 if (!t.isDisabled())\r
7111                                         return s.onclick.call(s.scope, e);\r
7112                         });\r
7113                 }\r
7114         });\r
7115 })(tinymce);\r
7116 (function(tinymce) {\r
7117         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7118 \r
7119         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
7120                 ListBox : function(id, s) {\r
7121                         var t = this;\r
7122 \r
7123                         t.parent(id, s);\r
7124 \r
7125                         t.items = [];\r
7126 \r
7127                         t.onChange = new Dispatcher(t);\r
7128 \r
7129                         t.onPostRender = new Dispatcher(t);\r
7130 \r
7131                         t.onAdd = new Dispatcher(t);\r
7132 \r
7133                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7134 \r
7135                         t.classPrefix = 'mceListBox';\r
7136                 },\r
7137 \r
7138                 select : function(va) {\r
7139                         var t = this, fv, f;\r
7140 \r
7141                         if (va == undefined)\r
7142                                 return t.selectByIndex(-1);\r
7143 \r
7144                         // Is string or number make function selector\r
7145                         if (va && va.call)\r
7146                                 f = va;\r
7147                         else {\r
7148                                 f = function(v) {\r
7149                                         return v == va;\r
7150                                 };\r
7151                         }\r
7152 \r
7153                         // Do we need to do something?\r
7154                         if (va != t.selectedValue) {\r
7155                                 // Find item\r
7156                                 each(t.items, function(o, i) {\r
7157                                         if (f(o.value)) {\r
7158                                                 fv = 1;\r
7159                                                 t.selectByIndex(i);\r
7160                                                 return false;\r
7161                                         }\r
7162                                 });\r
7163 \r
7164                                 if (!fv)\r
7165                                         t.selectByIndex(-1);\r
7166                         }\r
7167                 },\r
7168 \r
7169                 selectByIndex : function(idx) {\r
7170                         var t = this, e, o;\r
7171 \r
7172                         if (idx != t.selectedIndex) {\r
7173                                 e = DOM.get(t.id + '_text');\r
7174                                 o = t.items[idx];\r
7175 \r
7176                                 if (o) {\r
7177                                         t.selectedValue = o.value;\r
7178                                         t.selectedIndex = idx;\r
7179                                         DOM.setHTML(e, DOM.encode(o.title));\r
7180                                         DOM.removeClass(e, 'mceTitle');\r
7181                                 } else {\r
7182                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
7183                                         DOM.addClass(e, 'mceTitle');\r
7184                                         t.selectedValue = t.selectedIndex = null;\r
7185                                 }\r
7186 \r
7187                                 e = 0;\r
7188                         }\r
7189                 },\r
7190 \r
7191                 add : function(n, v, o) {\r
7192                         var t = this;\r
7193 \r
7194                         o = o || {};\r
7195                         o = tinymce.extend(o, {\r
7196                                 title : n,\r
7197                                 value : v\r
7198                         });\r
7199 \r
7200                         t.items.push(o);\r
7201                         t.onAdd.dispatch(t, o);\r
7202                 },\r
7203 \r
7204                 getLength : function() {\r
7205                         return this.items.length;\r
7206                 },\r
7207 \r
7208                 renderHTML : function() {\r
7209                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
7210 \r
7211                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
7212                         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
7213                         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
7214                         h += '</tr></tbody></table>';\r
7215 \r
7216                         return h;\r
7217                 },\r
7218 \r
7219                 showMenu : function() {\r
7220                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
7221 \r
7222                         if (t.isDisabled() || t.items.length == 0)\r
7223                                 return;\r
7224 \r
7225                         if (t.menu && t.menu.isMenuVisible)\r
7226                                 return t.hideMenu();\r
7227 \r
7228                         if (!t.isMenuRendered) {\r
7229                                 t.renderMenu();\r
7230                                 t.isMenuRendered = true;\r
7231                         }\r
7232 \r
7233                         p1 = DOM.getPos(this.settings.menu_container);\r
7234                         p2 = DOM.getPos(e);\r
7235 \r
7236                         m = t.menu;\r
7237                         m.settings.offset_x = p2.x;\r
7238                         m.settings.offset_y = p2.y;\r
7239                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
7240 \r
7241                         // Select in menu\r
7242                         if (t.oldID)\r
7243                                 m.items[t.oldID].setSelected(0);\r
7244 \r
7245                         each(t.items, function(o) {\r
7246                                 if (o.value === t.selectedValue) {\r
7247                                         m.items[o.id].setSelected(1);\r
7248                                         t.oldID = o.id;\r
7249                                 }\r
7250                         });\r
7251 \r
7252                         m.showMenu(0, e.clientHeight);\r
7253 \r
7254                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7255                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
7256 \r
7257                         //DOM.get(t.id + '_text').focus();\r
7258                 },\r
7259 \r
7260                 hideMenu : function(e) {\r
7261                         var t = this;\r
7262 \r
7263                         if (t.menu && t.menu.isMenuVisible) {\r
7264                                 // Prevent double toogles by canceling the mouse click event to the button\r
7265                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
7266                                         return;\r
7267 \r
7268                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7269                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
7270                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7271                                         t.menu.hideMenu();\r
7272                                 }\r
7273                         }\r
7274                 },\r
7275 \r
7276                 renderMenu : function() {\r
7277                         var t = this, m;\r
7278 \r
7279                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7280                                 menu_line : 1,\r
7281                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
7282                                 max_width : 150,\r
7283                                 max_height : 150\r
7284                         });\r
7285 \r
7286                         m.onHideMenu.add(t.hideMenu, t);\r
7287 \r
7288                         m.add({\r
7289                                 title : t.settings.title,\r
7290                                 'class' : 'mceMenuItemTitle',\r
7291                                 onclick : function() {\r
7292                                         if (t.settings.onselect('') !== false)\r
7293                                                 t.select(''); // Must be runned after\r
7294                                 }\r
7295                         });\r
7296 \r
7297                         each(t.items, function(o) {\r
7298                                 // No value then treat it as a title\r
7299                                 if (o.value === undefined) {\r
7300                                         m.add({\r
7301                                                 title : o.title,\r
7302                                                 'class' : 'mceMenuItemTitle',\r
7303                                                 onclick : function() {\r
7304                                                         if (t.settings.onselect('') !== false)\r
7305                                                                 t.select(''); // Must be runned after\r
7306                                                 }\r
7307                                         });\r
7308                                 } else {\r
7309                                         o.id = DOM.uniqueId();\r
7310                                         o.onclick = function() {\r
7311                                                 if (t.settings.onselect(o.value) !== false)\r
7312                                                         t.select(o.value); // Must be runned after\r
7313                                         };\r
7314 \r
7315                                         m.add(o);\r
7316                                 }\r
7317                         });\r
7318 \r
7319                         t.onRenderMenu.dispatch(t, m);\r
7320                         t.menu = m;\r
7321                 },\r
7322 \r
7323                 postRender : function() {\r
7324                         var t = this, cp = t.classPrefix;\r
7325 \r
7326                         Event.add(t.id, 'click', t.showMenu, t);\r
7327                         Event.add(t.id + '_text', 'focus', function(e) {\r
7328                                 if (!t._focused) {\r
7329                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
7330                                                 var idx = -1, v, kc = e.keyCode;\r
7331 \r
7332                                                 // Find current index\r
7333                                                 each(t.items, function(v, i) {\r
7334                                                         if (t.selectedValue == v.value)\r
7335                                                                 idx = i;\r
7336                                                 });\r
7337 \r
7338                                                 // Move up/down\r
7339                                                 if (kc == 38)\r
7340                                                         v = t.items[idx - 1];\r
7341                                                 else if (kc == 40)\r
7342                                                         v = t.items[idx + 1];\r
7343                                                 else if (kc == 13) {\r
7344                                                         // Fake select on enter\r
7345                                                         v = t.selectedValue;\r
7346                                                         t.selectedValue = null; // Needs to be null to fake change\r
7347                                                         t.settings.onselect(v);\r
7348                                                         return Event.cancel(e);\r
7349                                                 }\r
7350 \r
7351                                                 if (v) {\r
7352                                                         t.hideMenu();\r
7353                                                         t.select(v.value);\r
7354                                                 }\r
7355                                         });\r
7356                                 }\r
7357 \r
7358                                 t._focused = 1;\r
7359                         });\r
7360                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
7361 \r
7362                         // Old IE doesn't have hover on all elements\r
7363                         if (tinymce.isIE6 || !DOM.boxModel) {\r
7364                                 Event.add(t.id, 'mouseover', function() {\r
7365                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7366                                                 DOM.addClass(t.id, cp + 'Hover');\r
7367                                 });\r
7368 \r
7369                                 Event.add(t.id, 'mouseout', function() {\r
7370                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7371                                                 DOM.removeClass(t.id, cp + 'Hover');\r
7372                                 });\r
7373                         }\r
7374 \r
7375                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
7376                 },\r
7377 \r
7378                 destroy : function() {\r
7379                         this.parent();\r
7380 \r
7381                         Event.clear(this.id + '_text');\r
7382                         Event.clear(this.id + '_open');\r
7383                 }\r
7384         });\r
7385 })(tinymce);(function(tinymce) {\r
7386         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7387 \r
7388         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
7389                 NativeListBox : function(id, s) {\r
7390                         this.parent(id, s);\r
7391                         this.classPrefix = 'mceNativeListBox';\r
7392                 },\r
7393 \r
7394                 setDisabled : function(s) {\r
7395                         DOM.get(this.id).disabled = s;\r
7396                 },\r
7397 \r
7398                 isDisabled : function() {\r
7399                         return DOM.get(this.id).disabled;\r
7400                 },\r
7401 \r
7402                 select : function(va) {\r
7403                         var t = this, fv, f;\r
7404 \r
7405                         if (va == undefined)\r
7406                                 return t.selectByIndex(-1);\r
7407 \r
7408                         // Is string or number make function selector\r
7409                         if (va && va.call)\r
7410                                 f = va;\r
7411                         else {\r
7412                                 f = function(v) {\r
7413                                         return v == va;\r
7414                                 };\r
7415                         }\r
7416 \r
7417                         // Do we need to do something?\r
7418                         if (va != t.selectedValue) {\r
7419                                 // Find item\r
7420                                 each(t.items, function(o, i) {\r
7421                                         if (f(o.value)) {\r
7422                                                 fv = 1;\r
7423                                                 t.selectByIndex(i);\r
7424                                                 return false;\r
7425                                         }\r
7426                                 });\r
7427 \r
7428                                 if (!fv)\r
7429                                         t.selectByIndex(-1);\r
7430                         }\r
7431                 },\r
7432 \r
7433                 selectByIndex : function(idx) {\r
7434                         DOM.get(this.id).selectedIndex = idx + 1;\r
7435                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
7436                 },\r
7437 \r
7438                 add : function(n, v, a) {\r
7439                         var o, t = this;\r
7440 \r
7441                         a = a || {};\r
7442                         a.value = v;\r
7443 \r
7444                         if (t.isRendered())\r
7445                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
7446 \r
7447                         o = {\r
7448                                 title : n,\r
7449                                 value : v,\r
7450                                 attribs : a\r
7451                         };\r
7452 \r
7453                         t.items.push(o);\r
7454                         t.onAdd.dispatch(t, o);\r
7455                 },\r
7456 \r
7457                 getLength : function() {\r
7458                         return DOM.get(this.id).options.length - 1;\r
7459                 },\r
7460 \r
7461                 renderHTML : function() {\r
7462                         var h, t = this;\r
7463 \r
7464                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
7465 \r
7466                         each(t.items, function(it) {\r
7467                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
7468                         });\r
7469 \r
7470                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
7471 \r
7472                         return h;\r
7473                 },\r
7474 \r
7475                 postRender : function() {\r
7476                         var t = this, ch;\r
7477 \r
7478                         t.rendered = true;\r
7479 \r
7480                         function onChange(e) {\r
7481                                 var v = t.items[e.target.selectedIndex - 1];\r
7482 \r
7483                                 if (v && (v = v.value)) {\r
7484                                         t.onChange.dispatch(t, v);\r
7485 \r
7486                                         if (t.settings.onselect)\r
7487                                                 t.settings.onselect(v);\r
7488                                 }\r
7489                         };\r
7490 \r
7491                         Event.add(t.id, 'change', onChange);\r
7492 \r
7493                         // Accessibility keyhandler\r
7494                         Event.add(t.id, 'keydown', function(e) {\r
7495                                 var bf;\r
7496 \r
7497                                 Event.remove(t.id, 'change', ch);\r
7498 \r
7499                                 bf = Event.add(t.id, 'blur', function() {\r
7500                                         Event.add(t.id, 'change', onChange);\r
7501                                         Event.remove(t.id, 'blur', bf);\r
7502                                 });\r
7503 \r
7504                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
7505                                         onChange(e);\r
7506                                         return Event.cancel(e);\r
7507                                 }\r
7508                         });\r
7509 \r
7510                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
7511                 }\r
7512         });\r
7513 })(tinymce);(function(tinymce) {\r
7514         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
7515 \r
7516         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
7517                 MenuButton : function(id, s) {\r
7518                         this.parent(id, s);\r
7519 \r
7520                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7521 \r
7522                         s.menu_container = s.menu_container || DOM.doc.body;\r
7523                 },\r
7524 \r
7525                 showMenu : function() {\r
7526                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
7527 \r
7528                         if (t.isDisabled())\r
7529                                 return;\r
7530 \r
7531                         if (!t.isMenuRendered) {\r
7532                                 t.renderMenu();\r
7533                                 t.isMenuRendered = true;\r
7534                         }\r
7535 \r
7536                         if (t.isMenuVisible)\r
7537                                 return t.hideMenu();\r
7538 \r
7539                         p1 = DOM.getPos(t.settings.menu_container);\r
7540                         p2 = DOM.getPos(e);\r
7541 \r
7542                         m = t.menu;\r
7543                         m.settings.offset_x = p2.x;\r
7544                         m.settings.offset_y = p2.y;\r
7545                         m.settings.vp_offset_x = p2.x;\r
7546                         m.settings.vp_offset_y = p2.y;\r
7547                         m.settings.keyboard_focus = t._focused;\r
7548                         m.showMenu(0, e.clientHeight);\r
7549 \r
7550                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7551                         t.setState('Selected', 1);\r
7552 \r
7553                         t.isMenuVisible = 1;\r
7554                 },\r
7555 \r
7556                 renderMenu : function() {\r
7557                         var t = this, m;\r
7558 \r
7559                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7560                                 menu_line : 1,\r
7561                                 'class' : this.classPrefix + 'Menu',\r
7562                                 icons : t.settings.icons\r
7563                         });\r
7564 \r
7565                         m.onHideMenu.add(t.hideMenu, t);\r
7566 \r
7567                         t.onRenderMenu.dispatch(t, m);\r
7568                         t.menu = m;\r
7569                 },\r
7570 \r
7571                 hideMenu : function(e) {\r
7572                         var t = this;\r
7573 \r
7574                         // Prevent double toogles by canceling the mouse click event to the button\r
7575                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
7576                                 return;\r
7577 \r
7578                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7579                                 t.setState('Selected', 0);\r
7580                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7581                                 if (t.menu)\r
7582                                         t.menu.hideMenu();\r
7583                         }\r
7584 \r
7585                         t.isMenuVisible = 0;\r
7586                 },\r
7587 \r
7588                 postRender : function() {\r
7589                         var t = this, s = t.settings;\r
7590 \r
7591                         Event.add(t.id, 'click', function() {\r
7592                                 if (!t.isDisabled()) {\r
7593                                         if (s.onclick)\r
7594                                                 s.onclick(t.value);\r
7595 \r
7596                                         t.showMenu();\r
7597                                 }\r
7598                         });\r
7599                 }\r
7600         });\r
7601 })(tinymce);\r
7602 (function(tinymce) {\r
7603         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
7604 \r
7605         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
7606                 SplitButton : function(id, s) {\r
7607                         this.parent(id, s);\r
7608                         this.classPrefix = 'mceSplitButton';\r
7609                 },\r
7610 \r
7611                 renderHTML : function() {\r
7612                         var h, t = this, s = t.settings, h1;\r
7613 \r
7614                         h = '<tbody><tr>';\r
7615 \r
7616                         if (s.image)\r
7617                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
7618                         else\r
7619                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
7620 \r
7621                         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
7622         \r
7623                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
7624                         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
7625 \r
7626                         h += '</tr></tbody>';\r
7627 \r
7628                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
7629                 },\r
7630 \r
7631                 postRender : function() {\r
7632                         var t = this, s = t.settings;\r
7633 \r
7634                         if (s.onclick) {\r
7635                                 Event.add(t.id + '_action', 'click', function() {\r
7636                                         if (!t.isDisabled())\r
7637                                                 s.onclick(t.value);\r
7638                                 });\r
7639                         }\r
7640 \r
7641                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
7642                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
7643                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
7644 \r
7645                         // Old IE doesn't have hover on all elements\r
7646                         if (tinymce.isIE6 || !DOM.boxModel) {\r
7647                                 Event.add(t.id, 'mouseover', function() {\r
7648                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
7649                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
7650                                 });\r
7651 \r
7652                                 Event.add(t.id, 'mouseout', function() {\r
7653                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
7654                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
7655                                 });\r
7656                         }\r
7657                 },\r
7658 \r
7659                 destroy : function() {\r
7660                         this.parent();\r
7661 \r
7662                         Event.clear(this.id + '_action');\r
7663                         Event.clear(this.id + '_open');\r
7664                 }\r
7665         });\r
7666 })(tinymce);\r
7667 (function(tinymce) {\r
7668         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
7669 \r
7670         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
7671                 ColorSplitButton : function(id, s) {\r
7672                         var t = this;\r
7673 \r
7674                         t.parent(id, s);\r
7675 \r
7676                         t.settings = s = tinymce.extend({\r
7677                                 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
7678                                 grid_width : 8,\r
7679                                 default_color : '#888888'\r
7680                         }, t.settings);\r
7681 \r
7682                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
7683 \r
7684                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
7685 \r
7686                         t.value = s.default_color;\r
7687                 },\r
7688 \r
7689                 showMenu : function() {\r
7690                         var t = this, r, p, e, p2;\r
7691 \r
7692                         if (t.isDisabled())\r
7693                                 return;\r
7694 \r
7695                         if (!t.isMenuRendered) {\r
7696                                 t.renderMenu();\r
7697                                 t.isMenuRendered = true;\r
7698                         }\r
7699 \r
7700                         if (t.isMenuVisible)\r
7701                                 return t.hideMenu();\r
7702 \r
7703                         e = DOM.get(t.id);\r
7704                         DOM.show(t.id + '_menu');\r
7705                         DOM.addClass(e, 'mceSplitButtonSelected');\r
7706                         p2 = DOM.getPos(e);\r
7707                         DOM.setStyles(t.id + '_menu', {\r
7708                                 left : p2.x,\r
7709                                 top : p2.y + e.clientHeight,\r
7710                                 zIndex : 200000\r
7711                         });\r
7712                         e = 0;\r
7713 \r
7714                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7715                         t.onShowMenu.dispatch(t);\r
7716 \r
7717                         if (t._focused) {\r
7718                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
7719                                         if (e.keyCode == 27)\r
7720                                                 t.hideMenu();\r
7721                                 });\r
7722 \r
7723                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
7724                         }\r
7725 \r
7726                         t.isMenuVisible = 1;\r
7727                 },\r
7728 \r
7729                 hideMenu : function(e) {\r
7730                         var t = this;\r
7731 \r
7732                         // Prevent double toogles by canceling the mouse click event to the button\r
7733                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
7734                                 return;\r
7735 \r
7736                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
7737                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
7738                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7739                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
7740                                 DOM.hide(t.id + '_menu');\r
7741                         }\r
7742 \r
7743                         t.onHideMenu.dispatch(t);\r
7744 \r
7745                         t.isMenuVisible = 0;\r
7746                 },\r
7747 \r
7748                 renderMenu : function() {\r
7749                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
7750 \r
7751                         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
7752                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
7753                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
7754 \r
7755                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
7756                         tb = DOM.add(n, 'tbody');\r
7757 \r
7758                         // Generate color grid\r
7759                         i = 0;\r
7760                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
7761                                 c = c.replace(/^#/, '');\r
7762 \r
7763                                 if (!i--) {\r
7764                                         tr = DOM.add(tb, 'tr');\r
7765                                         i = s.grid_width - 1;\r
7766                                 }\r
7767 \r
7768                                 n = DOM.add(tr, 'td');\r
7769 \r
7770                                 n = DOM.add(n, 'a', {\r
7771                                         href : 'javascript:;',\r
7772                                         style : {\r
7773                                                 backgroundColor : '#' + c\r
7774                                         },\r
7775                                         _mce_color : '#' + c\r
7776                                 });\r
7777                         });\r
7778 \r
7779                         if (s.more_colors_func) {\r
7780                                 n = DOM.add(tb, 'tr');\r
7781                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
7782                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
7783 \r
7784                                 Event.add(n, 'click', function(e) {\r
7785                                         s.more_colors_func.call(s.more_colors_scope || this);\r
7786                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7787                                 });\r
7788                         }\r
7789 \r
7790                         DOM.addClass(m, 'mceColorSplitMenu');\r
7791 \r
7792                         Event.add(t.id + '_menu', 'click', function(e) {\r
7793                                 var c;\r
7794 \r
7795                                 e = e.target;\r
7796 \r
7797                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
7798                                         t.setColor(c);\r
7799 \r
7800                                 return Event.cancel(e); // Prevent IE auto save warning\r
7801                         });\r
7802 \r
7803                         return w;\r
7804                 },\r
7805 \r
7806                 setColor : function(c) {\r
7807                         var t = this;\r
7808 \r
7809                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
7810 \r
7811                         t.value = c;\r
7812                         t.hideMenu();\r
7813                         t.settings.onselect(c);\r
7814                 },\r
7815 \r
7816                 postRender : function() {\r
7817                         var t = this, id = t.id;\r
7818 \r
7819                         t.parent();\r
7820                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
7821                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
7822                 },\r
7823 \r
7824                 destroy : function() {\r
7825                         this.parent();\r
7826 \r
7827                         Event.clear(this.id + '_menu');\r
7828                         Event.clear(this.id + '_more');\r
7829                         DOM.remove(this.id + '_menu');\r
7830                 }\r
7831         });\r
7832 })(tinymce);\r
7833 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
7834         renderHTML : function() {\r
7835                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
7836 \r
7837                 cl = t.controls;\r
7838                 for (i=0; i<cl.length; i++) {\r
7839                         // Get current control, prev control, next control and if the control is a list box or not\r
7840                         co = cl[i];\r
7841                         pr = cl[i - 1];\r
7842                         nx = cl[i + 1];\r
7843 \r
7844                         // Add toolbar start\r
7845                         if (i === 0) {\r
7846                                 c = 'mceToolbarStart';\r
7847 \r
7848                                 if (co.Button)\r
7849                                         c += ' mceToolbarStartButton';\r
7850                                 else if (co.SplitButton)\r
7851                                         c += ' mceToolbarStartSplitButton';\r
7852                                 else if (co.ListBox)\r
7853                                         c += ' mceToolbarStartListBox';\r
7854 \r
7855                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
7856                         }\r
7857 \r
7858                         // Add toolbar end before list box and after the previous button\r
7859                         // This is to fix the o2k7 editor skins\r
7860                         if (pr && co.ListBox) {\r
7861                                 if (pr.Button || pr.SplitButton)\r
7862                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
7863                         }\r
7864 \r
7865                         // Render control HTML\r
7866 \r
7867                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
7868                         if (dom.stdMode)\r
7869                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
7870                         else\r
7871                                 h += '<td>' + co.renderHTML() + '</td>';\r
7872 \r
7873                         // Add toolbar start after list box and before the next button\r
7874                         // This is to fix the o2k7 editor skins\r
7875                         if (nx && co.ListBox) {\r
7876                                 if (nx.Button || nx.SplitButton)\r
7877                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
7878                         }\r
7879                 }\r
7880 \r
7881                 c = 'mceToolbarEnd';\r
7882 \r
7883                 if (co.Button)\r
7884                         c += ' mceToolbarEndButton';\r
7885                 else if (co.SplitButton)\r
7886                         c += ' mceToolbarEndSplitButton';\r
7887                 else if (co.ListBox)\r
7888                         c += ' mceToolbarEndListBox';\r
7889 \r
7890                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
7891 \r
7892                 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
7893         }\r
7894 });\r
7895 (function(tinymce) {\r
7896         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
7897 \r
7898         tinymce.create('tinymce.AddOnManager', {\r
7899                 items : [],\r
7900                 urls : {},\r
7901                 lookup : {},\r
7902 \r
7903                 onAdd : new Dispatcher(this),\r
7904 \r
7905                 get : function(n) {\r
7906                         return this.lookup[n];\r
7907                 },\r
7908 \r
7909                 requireLangPack : function(n) {\r
7910                         var s = tinymce.settings;\r
7911 \r
7912                         if (s && s.language)\r
7913                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
7914                 },\r
7915 \r
7916                 add : function(id, o) {\r
7917                         this.items.push(o);\r
7918                         this.lookup[id] = o;\r
7919                         this.onAdd.dispatch(this, id, o);\r
7920 \r
7921                         return o;\r
7922                 },\r
7923 \r
7924                 load : function(n, u, cb, s) {\r
7925                         var t = this;\r
7926 \r
7927                         if (t.urls[n])\r
7928                                 return;\r
7929 \r
7930                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
7931                                 u = tinymce.baseURL + '/' +  u;\r
7932 \r
7933                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
7934                         tinymce.ScriptLoader.add(u, cb, s);\r
7935                 }\r
7936         });\r
7937 \r
7938         // Create plugin and theme managers\r
7939         tinymce.PluginManager = new tinymce.AddOnManager();\r
7940         tinymce.ThemeManager = new tinymce.AddOnManager();\r
7941 }(tinymce));\r
7942 \r
7943 (function(tinymce) {\r
7944         // Shorten names\r
7945         var each = tinymce.each, extend = tinymce.extend,\r
7946                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
7947                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
7948                 explode = tinymce.explode,\r
7949                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
7950 \r
7951         // Setup some URLs where the editor API is located and where the document is\r
7952         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
7953         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
7954                 tinymce.documentBaseURL += '/';\r
7955 \r
7956         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
7957 \r
7958         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
7959 \r
7960         // Add before unload listener\r
7961         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
7962         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
7963         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
7964 \r
7965         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
7966         Event.add(window, 'beforeunload', function(e) {\r
7967                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
7968         });\r
7969 \r
7970         tinymce.onAddEditor = new Dispatcher(tinymce);\r
7971 \r
7972         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
7973 \r
7974         tinymce.EditorManager = extend(tinymce, {\r
7975                 editors : [],\r
7976 \r
7977                 i18n : {},\r
7978 \r
7979                 activeEditor : null,\r
7980 \r
7981                 init : function(s) {\r
7982                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
7983 \r
7984                         function execCallback(se, n, s) {\r
7985                                 var f = se[n];\r
7986 \r
7987                                 if (!f)\r
7988                                         return;\r
7989 \r
7990                                 if (tinymce.is(f, 'string')) {\r
7991                                         s = f.replace(/\.\w+$/, '');\r
7992                                         s = s ? tinymce.resolve(s) : 0;\r
7993                                         f = tinymce.resolve(f);\r
7994                                 }\r
7995 \r
7996                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
7997                         };\r
7998 \r
7999                         s = extend({\r
8000                                 theme : "simple",\r
8001                                 language : "en"\r
8002                         }, s);\r
8003 \r
8004                         t.settings = s;\r
8005 \r
8006                         // Legacy call\r
8007                         Event.add(document, 'init', function() {\r
8008                                 var l, co;\r
8009 \r
8010                                 execCallback(s, 'onpageload');\r
8011 \r
8012                                 switch (s.mode) {\r
8013                                         case "exact":\r
8014                                                 l = s.elements || '';\r
8015 \r
8016                                                 if(l.length > 0) {\r
8017                                                         each(explode(l), function(v) {\r
8018                                                                 if (DOM.get(v)) {\r
8019                                                                         ed = new tinymce.Editor(v, s);\r
8020                                                                         el.push(ed);\r
8021                                                                         ed.render(1);\r
8022                                                                 } else {\r
8023                                                                         each(document.forms, function(f) {\r
8024                                                                                 each(f.elements, function(e) {\r
8025                                                                                         if (e.name === v) {\r
8026                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8027                                                                                                 DOM.setAttrib(e, 'id', v);\r
8028 \r
8029                                                                                                 ed = new tinymce.Editor(v, s);\r
8030                                                                                                 el.push(ed);\r
8031                                                                                                 ed.render(1);\r
8032                                                                                         }\r
8033                                                                                 });\r
8034                                                                         });\r
8035                                                                 }\r
8036                                                         });\r
8037                                                 }\r
8038                                                 break;\r
8039 \r
8040                                         case "textareas":\r
8041                                         case "specific_textareas":\r
8042                                                 function hasClass(n, c) {\r
8043                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8044                                                 };\r
8045 \r
8046                                                 each(DOM.select('textarea'), function(v) {\r
8047                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8048                                                                 return;\r
8049 \r
8050                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8051                                                                 // Can we use the name\r
8052                                                                 e = DOM.get(v.name);\r
8053                                                                 if (!v.id && !e)\r
8054                                                                         v.id = v.name;\r
8055 \r
8056                                                                 // Generate unique name if missing or already exists\r
8057                                                                 if (!v.id || t.get(v.id))\r
8058                                                                         v.id = DOM.uniqueId();\r
8059 \r
8060                                                                 ed = new tinymce.Editor(v.id, s);\r
8061                                                                 el.push(ed);\r
8062                                                                 ed.render(1);\r
8063                                                         }\r
8064                                                 });\r
8065                                                 break;\r
8066                                 }\r
8067 \r
8068                                 // Call onInit when all editors are initialized\r
8069                                 if (s.oninit) {\r
8070                                         l = co = 0;\r
8071 \r
8072                                         each(el, function(ed) {\r
8073                                                 co++;\r
8074 \r
8075                                                 if (!ed.initialized) {\r
8076                                                         // Wait for it\r
8077                                                         ed.onInit.add(function() {\r
8078                                                                 l++;\r
8079 \r
8080                                                                 // All done\r
8081                                                                 if (l == co)\r
8082                                                                         execCallback(s, 'oninit');\r
8083                                                         });\r
8084                                                 } else\r
8085                                                         l++;\r
8086 \r
8087                                                 // All done\r
8088                                                 if (l == co)\r
8089                                                         execCallback(s, 'oninit');                                      \r
8090                                         });\r
8091                                 }\r
8092                         });\r
8093                 },\r
8094 \r
8095                 get : function(id) {\r
8096                         if (id === undefined)\r
8097                                 return this.editors;\r
8098 \r
8099                         return this.editors[id];\r
8100                 },\r
8101 \r
8102                 getInstanceById : function(id) {\r
8103                         return this.get(id);\r
8104                 },\r
8105 \r
8106                 add : function(editor) {\r
8107                         var self = this, editors = self.editors;\r
8108 \r
8109                         // Add named and index editor instance\r
8110                         editors[editor.id] = editor;\r
8111                         editors.push(editor);\r
8112 \r
8113                         self._setActive(editor);\r
8114                         self.onAddEditor.dispatch(self, editor);\r
8115 \r
8116 \r
8117                         // Patch the tinymce.Editor instance with jQuery adapter logic\r
8118                         if (tinymce.adapter)\r
8119                                 tinymce.adapter.patchEditor(editor);\r
8120 \r
8121 \r
8122                         return editor;\r
8123                 },\r
8124 \r
8125                 remove : function(editor) {\r
8126                         var t = this, i, editors = t.editors;\r
8127 \r
8128                         // Not in the collection\r
8129                         if (!editors[editor.id])\r
8130                                 return null;\r
8131 \r
8132                         delete editors[editor.id];\r
8133 \r
8134                         for (i = 0; i < editors.length; i++) {\r
8135                                 if (editors[i] == editor) {\r
8136                                         editors.splice(i, 1);\r
8137                                         break;\r
8138                                 }\r
8139                         }\r
8140 \r
8141                         // Select another editor since the active one was removed\r
8142                         if (t.activeEditor == editor)\r
8143                                 t._setActive(editors[0]);\r
8144 \r
8145                         editor.destroy();\r
8146                         t.onRemoveEditor.dispatch(t, editor);\r
8147 \r
8148                         return editor;\r
8149                 },\r
8150 \r
8151                 execCommand : function(c, u, v) {\r
8152                         var t = this, ed = t.get(v), w;\r
8153 \r
8154                         // Manager commands\r
8155                         switch (c) {\r
8156                                 case "mceFocus":\r
8157                                         ed.focus();\r
8158                                         return true;\r
8159 \r
8160                                 case "mceAddEditor":\r
8161                                 case "mceAddControl":\r
8162                                         if (!t.get(v))\r
8163                                                 new tinymce.Editor(v, t.settings).render();\r
8164 \r
8165                                         return true;\r
8166 \r
8167                                 case "mceAddFrameControl":\r
8168                                         w = v.window;\r
8169 \r
8170                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
8171                                         w.tinyMCE = tinyMCE;\r
8172                                         w.tinymce = tinymce;\r
8173 \r
8174                                         tinymce.DOM.doc = w.document;\r
8175                                         tinymce.DOM.win = w;\r
8176 \r
8177                                         ed = new tinymce.Editor(v.element_id, v);\r
8178                                         ed.render();\r
8179 \r
8180                                         // Fix IE memory leaks\r
8181                                         if (tinymce.isIE) {\r
8182                                                 function clr() {\r
8183                                                         ed.destroy();\r
8184                                                         w.detachEvent('onunload', clr);\r
8185                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
8186                                                 };\r
8187 \r
8188                                                 w.attachEvent('onunload', clr);\r
8189                                         }\r
8190 \r
8191                                         v.page_window = null;\r
8192 \r
8193                                         return true;\r
8194 \r
8195                                 case "mceRemoveEditor":\r
8196                                 case "mceRemoveControl":\r
8197                                         if (ed)\r
8198                                                 ed.remove();\r
8199 \r
8200                                         return true;\r
8201 \r
8202                                 case 'mceToggleEditor':\r
8203                                         if (!ed) {\r
8204                                                 t.execCommand('mceAddControl', 0, v);\r
8205                                                 return true;\r
8206                                         }\r
8207 \r
8208                                         if (ed.isHidden())\r
8209                                                 ed.show();\r
8210                                         else\r
8211                                                 ed.hide();\r
8212 \r
8213                                         return true;\r
8214                         }\r
8215 \r
8216                         // Run command on active editor\r
8217                         if (t.activeEditor)\r
8218                                 return t.activeEditor.execCommand(c, u, v);\r
8219 \r
8220                         return false;\r
8221                 },\r
8222 \r
8223                 execInstanceCommand : function(id, c, u, v) {\r
8224                         var ed = this.get(id);\r
8225 \r
8226                         if (ed)\r
8227                                 return ed.execCommand(c, u, v);\r
8228 \r
8229                         return false;\r
8230                 },\r
8231 \r
8232                 triggerSave : function() {\r
8233                         each(this.editors, function(e) {\r
8234                                 e.save();\r
8235                         });\r
8236                 },\r
8237 \r
8238                 addI18n : function(p, o) {\r
8239                         var lo, i18n = this.i18n;\r
8240 \r
8241                         if (!tinymce.is(p, 'string')) {\r
8242                                 each(p, function(o, lc) {\r
8243                                         each(o, function(o, g) {\r
8244                                                 each(o, function(o, k) {\r
8245                                                         if (g === 'common')\r
8246                                                                 i18n[lc + '.' + k] = o;\r
8247                                                         else\r
8248                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
8249                                                 });\r
8250                                         });\r
8251                                 });\r
8252                         } else {\r
8253                                 each(o, function(o, k) {\r
8254                                         i18n[p + '.' + k] = o;\r
8255                                 });\r
8256                         }\r
8257                 },\r
8258 \r
8259                 // Private methods\r
8260 \r
8261                 _setActive : function(editor) {\r
8262                         this.selectedInstance = this.activeEditor = editor;\r
8263                 }\r
8264         });\r
8265 })(tinymce);\r
8266 \r
8267 (function(tinymce) {\r
8268         // Shorten these names\r
8269         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
8270                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
8271                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
8272                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8273                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
8274 \r
8275         tinymce.create('tinymce.Editor', {\r
8276                 Editor : function(id, s) {\r
8277                         var t = this;\r
8278 \r
8279                         t.id = t.editorId = id;\r
8280 \r
8281                         t.execCommands = {};\r
8282                         t.queryStateCommands = {};\r
8283                         t.queryValueCommands = {};\r
8284 \r
8285                         t.isNotDirty = false;\r
8286 \r
8287                         t.plugins = {};\r
8288 \r
8289                         // Add events to the editor\r
8290                         each([\r
8291                                 'onPreInit',\r
8292 \r
8293                                 'onBeforeRenderUI',\r
8294 \r
8295                                 'onPostRender',\r
8296 \r
8297                                 'onInit',\r
8298 \r
8299                                 'onRemove',\r
8300 \r
8301                                 'onActivate',\r
8302 \r
8303                                 'onDeactivate',\r
8304 \r
8305                                 'onClick',\r
8306 \r
8307                                 'onEvent',\r
8308 \r
8309                                 'onMouseUp',\r
8310 \r
8311                                 'onMouseDown',\r
8312 \r
8313                                 'onDblClick',\r
8314 \r
8315                                 'onKeyDown',\r
8316 \r
8317                                 'onKeyUp',\r
8318 \r
8319                                 'onKeyPress',\r
8320 \r
8321                                 'onContextMenu',\r
8322 \r
8323                                 'onSubmit',\r
8324 \r
8325                                 'onReset',\r
8326 \r
8327                                 'onPaste',\r
8328 \r
8329                                 'onPreProcess',\r
8330 \r
8331                                 'onPostProcess',\r
8332 \r
8333                                 'onBeforeSetContent',\r
8334 \r
8335                                 'onBeforeGetContent',\r
8336 \r
8337                                 'onSetContent',\r
8338 \r
8339                                 'onGetContent',\r
8340 \r
8341                                 'onLoadContent',\r
8342 \r
8343                                 'onSaveContent',\r
8344 \r
8345                                 'onNodeChange',\r
8346 \r
8347                                 'onChange',\r
8348 \r
8349                                 'onBeforeExecCommand',\r
8350 \r
8351                                 'onExecCommand',\r
8352 \r
8353                                 'onUndo',\r
8354 \r
8355                                 'onRedo',\r
8356 \r
8357                                 'onVisualAid',\r
8358 \r
8359                                 'onSetProgressState'\r
8360                         ], function(e) {\r
8361                                 t[e] = new Dispatcher(t);\r
8362                         });\r
8363 \r
8364                         t.settings = s = extend({\r
8365                                 id : id,\r
8366                                 language : 'en',\r
8367                                 docs_language : 'en',\r
8368                                 theme : 'simple',\r
8369                                 skin : 'default',\r
8370                                 delta_width : 0,\r
8371                                 delta_height : 0,\r
8372                                 popup_css : '',\r
8373                                 plugins : '',\r
8374                                 document_base_url : tinymce.documentBaseURL,\r
8375                                 add_form_submit_trigger : 1,\r
8376                                 submit_patch : 1,\r
8377                                 add_unload_trigger : 1,\r
8378                                 convert_urls : 1,\r
8379                                 relative_urls : 1,\r
8380                                 remove_script_host : 1,\r
8381                                 table_inline_editing : 0,\r
8382                                 object_resizing : 1,\r
8383                                 cleanup : 1,\r
8384                                 accessibility_focus : 1,\r
8385                                 custom_shortcuts : 1,\r
8386                                 custom_undo_redo_keyboard_shortcuts : 1,\r
8387                                 custom_undo_redo_restore_selection : 1,\r
8388                                 custom_undo_redo : 1,\r
8389                                 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
8390                                 visual_table_class : 'mceItemTable',\r
8391                                 visual : 1,\r
8392                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
8393                                 apply_source_formatting : 1,\r
8394                                 directionality : 'ltr',\r
8395                                 forced_root_block : 'p',\r
8396                                 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
8397                                 hidden_input : 1,\r
8398                                 padd_empty_editor : 1,\r
8399                                 render_ui : 1,\r
8400                                 init_theme : 1,\r
8401                                 force_p_newlines : 1,\r
8402                                 indentation : '30px',\r
8403                                 keep_styles : 1,\r
8404                                 fix_table_elements : 1,\r
8405                                 inline_styles : 1,\r
8406                                 convert_fonts_to_spans : true\r
8407                         }, s);\r
8408 \r
8409                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
8410                                 base_uri : tinyMCE.baseURI\r
8411                         });\r
8412 \r
8413                         t.baseURI = tinymce.baseURI;\r
8414 \r
8415                         // Call setup\r
8416                         t.execCallback('setup', t);\r
8417                 },\r
8418 \r
8419                 render : function(nst) {\r
8420                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
8421 \r
8422                         // Page is not loaded yet, wait for it\r
8423                         if (!Event.domLoaded) {\r
8424                                 Event.add(document, 'init', function() {\r
8425                                         t.render();\r
8426                                 });\r
8427                                 return;\r
8428                         }\r
8429 \r
8430                         tinyMCE.settings = s;\r
8431 \r
8432                         // Element not found, then skip initialization\r
8433                         if (!t.getElement())\r
8434                                 return;\r
8435 \r
8436                         // Add hidden input for non input elements inside form elements\r
8437                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
8438                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
8439 \r
8440                         if (tinymce.WindowManager)\r
8441                                 t.windowManager = new tinymce.WindowManager(t);\r
8442 \r
8443                         if (s.encoding == 'xml') {\r
8444                                 t.onGetContent.add(function(ed, o) {\r
8445                                         if (o.save)\r
8446                                                 o.content = DOM.encode(o.content);\r
8447                                 });\r
8448                         }\r
8449 \r
8450                         if (s.add_form_submit_trigger) {\r
8451                                 t.onSubmit.addToTop(function() {\r
8452                                         if (t.initialized) {\r
8453                                                 t.save();\r
8454                                                 t.isNotDirty = 1;\r
8455                                         }\r
8456                                 });\r
8457                         }\r
8458 \r
8459                         if (s.add_unload_trigger) {\r
8460                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
8461                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
8462                                                 t.save({format : 'raw', no_events : true});\r
8463                                 });\r
8464                         }\r
8465 \r
8466                         tinymce.addUnload(t.destroy, t);\r
8467 \r
8468                         if (s.submit_patch) {\r
8469                                 t.onBeforeRenderUI.add(function() {\r
8470                                         var n = t.getElement().form;\r
8471 \r
8472                                         if (!n)\r
8473                                                 return;\r
8474 \r
8475                                         // Already patched\r
8476                                         if (n._mceOldSubmit)\r
8477                                                 return;\r
8478 \r
8479                                         // Check page uses id="submit" or name="submit" for it's submit button\r
8480                                         if (!n.submit.nodeType && !n.submit.length) {\r
8481                                                 t.formElement = n;\r
8482                                                 n._mceOldSubmit = n.submit;\r
8483                                                 n.submit = function() {\r
8484                                                         // Save all instances\r
8485                                                         tinymce.triggerSave();\r
8486                                                         t.isNotDirty = 1;\r
8487 \r
8488                                                         return t.formElement._mceOldSubmit(t.formElement);\r
8489                                                 };\r
8490                                         }\r
8491 \r
8492                                         n = null;\r
8493                                 });\r
8494                         }\r
8495 \r
8496                         // Load scripts\r
8497                         function loadScripts() {\r
8498                                 if (s.language)\r
8499                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
8500 \r
8501                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
8502                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
8503 \r
8504                                 each(explode(s.plugins), function(p) {\r
8505                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
8506                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
8507                                                 if (p == 'safari')\r
8508                                                         return;\r
8509 \r
8510                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
8511                                         }\r
8512                                 });\r
8513 \r
8514                                 // Init when que is loaded\r
8515                                 sl.loadQueue(function() {\r
8516                                         if (!t.removed)\r
8517                                                 t.init();\r
8518                                 });\r
8519                         };\r
8520 \r
8521                         loadScripts();\r
8522                 },\r
8523 \r
8524                 init : function() {\r
8525                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
8526 \r
8527                         tinymce.add(t);\r
8528 \r
8529                         if (s.theme) {\r
8530                                 s.theme = s.theme.replace(/-/, '');\r
8531                                 o = ThemeManager.get(s.theme);\r
8532                                 t.theme = new o();\r
8533 \r
8534                                 if (t.theme.init && s.init_theme)\r
8535                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
8536                         }\r
8537 \r
8538                         // Create all plugins\r
8539                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
8540                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
8541 \r
8542                                 if (c) {\r
8543                                         po = new c(t, u);\r
8544 \r
8545                                         t.plugins[p] = po;\r
8546 \r
8547                                         if (po.init)\r
8548                                                 po.init(t, u);\r
8549                                 }\r
8550                         });\r
8551 \r
8552                         // Setup popup CSS path(s)\r
8553                         if (s.popup_css !== false) {\r
8554                                 if (s.popup_css)\r
8555                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
8556                                 else\r
8557                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
8558                         }\r
8559 \r
8560                         if (s.popup_css_add)\r
8561                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
8562 \r
8563                         t.controlManager = new tinymce.ControlManager(t);\r
8564 \r
8565                         if (s.custom_undo_redo) {\r
8566                                 // Add initial undo level\r
8567                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
8568                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
8569                                                 if (!t.undoManager.hasUndo())\r
8570                                                         t.undoManager.add();\r
8571                                         }\r
8572                                 });\r
8573 \r
8574                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
8575                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
8576                                                 t.undoManager.add();\r
8577                                 });\r
8578                         }\r
8579 \r
8580                         t.onExecCommand.add(function(ed, c) {\r
8581                                 // Don't refresh the select lists until caret move\r
8582                                 if (!/^(FontName|FontSize)$/.test(c))\r
8583                                         t.nodeChanged();\r
8584                         });\r
8585 \r
8586                         // Remove ghost selections on images and tables in Gecko\r
8587                         if (isGecko) {\r
8588                                 function repaint(a, o) {\r
8589                                         if (!o || !o.initial)\r
8590                                                 t.execCommand('mceRepaint');\r
8591                                 };\r
8592 \r
8593                                 t.onUndo.add(repaint);\r
8594                                 t.onRedo.add(repaint);\r
8595                                 t.onSetContent.add(repaint);\r
8596                         }\r
8597 \r
8598                         // Enables users to override the control factory\r
8599                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
8600 \r
8601                         // Measure box\r
8602                         if (s.render_ui) {\r
8603                                 w = s.width || e.style.width || e.offsetWidth;\r
8604                                 h = s.height || e.style.height || e.offsetHeight;\r
8605                                 t.orgDisplay = e.style.display;\r
8606                                 re = /^[0-9\.]+(|px)$/i;\r
8607 \r
8608                                 if (re.test('' + w))\r
8609                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
8610 \r
8611                                 if (re.test('' + h))\r
8612                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
8613 \r
8614                                 // Render UI\r
8615                                 o = t.theme.renderUI({\r
8616                                         targetNode : e,\r
8617                                         width : w,\r
8618                                         height : h,\r
8619                                         deltaWidth : s.delta_width,\r
8620                                         deltaHeight : s.delta_height\r
8621                                 });\r
8622 \r
8623                                 t.editorContainer = o.editorContainer;\r
8624                         }\r
8625 \r
8626 \r
8627                         // User specified a document.domain value\r
8628                         if (document.domain && location.hostname != document.domain)\r
8629                                 tinymce.relaxedDomain = document.domain;\r
8630 \r
8631                         // Resize editor\r
8632                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
8633                                 width : w,\r
8634                                 height : h\r
8635                         });\r
8636 \r
8637                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
8638                         if (h < 100)\r
8639                                 h = 100;\r
8640 \r
8641                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
8642 \r
8643                         // We only need to override paths if we have to\r
8644                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
8645                         if (s.document_base_url != tinymce.documentBaseURL)\r
8646                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
8647 \r
8648                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
8649 \r
8650                         if (tinymce.relaxedDomain)\r
8651                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
8652 \r
8653                         bi = s.body_id || 'tinymce';\r
8654                         if (bi.indexOf('=') != -1) {\r
8655                                 bi = t.getParam('body_id', '', 'hash');\r
8656                                 bi = bi[t.id] || bi;\r
8657                         }\r
8658 \r
8659                         bc = s.body_class || '';\r
8660                         if (bc.indexOf('=') != -1) {\r
8661                                 bc = t.getParam('body_class', '', 'hash');\r
8662                                 bc = bc[t.id] || '';\r
8663                         }\r
8664 \r
8665                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
8666 \r
8667                         // Domain relaxing enabled, then set document domain\r
8668                         if (tinymce.relaxedDomain) {\r
8669                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
8670                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
8671                                         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
8672                                 else if (tinymce.isOpera)\r
8673                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
8674                         }\r
8675 \r
8676                         // Create iframe\r
8677                         n = DOM.add(o.iframeContainer, 'iframe', {\r
8678                                 id : t.id + "_ifr",\r
8679                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
8680                                 frameBorder : '0',\r
8681                                 style : {\r
8682                                         width : '100%',\r
8683                                         height : h\r
8684                                 }\r
8685                         });\r
8686 \r
8687                         t.contentAreaContainer = o.iframeContainer;\r
8688                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
8689                         DOM.get(t.id).style.display = 'none';\r
8690 \r
8691                         if (!isIE || !tinymce.relaxedDomain)\r
8692                                 t.setupIframe();\r
8693 \r
8694                         e = n = o = null; // Cleanup\r
8695                 },\r
8696 \r
8697                 setupIframe : function() {\r
8698                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
8699 \r
8700                         // Setup iframe body\r
8701                         if (!isIE || !tinymce.relaxedDomain) {\r
8702                                 d.open();\r
8703                                 d.write(t.iframeHTML);\r
8704                                 d.close();\r
8705                         }\r
8706 \r
8707                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
8708                         if (!isIE) {\r
8709                                 try {\r
8710                                         if (!s.readonly)\r
8711                                                 d.designMode = 'On';\r
8712                                 } catch (ex) {\r
8713                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
8714                                         // The design mode will be set ones the editor is focused\r
8715                                 }\r
8716                         }\r
8717 \r
8718                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
8719                         if (isIE) {\r
8720                                 // It will not steal focus if we hide it while setting contentEditable\r
8721                                 b = t.getBody();\r
8722                                 DOM.hide(b);\r
8723 \r
8724                                 if (!s.readonly)\r
8725                                         b.contentEditable = true;\r
8726 \r
8727                                 DOM.show(b);\r
8728                         }\r
8729 \r
8730                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
8731                                 keep_values : true,\r
8732                                 url_converter : t.convertURL,\r
8733                                 url_converter_scope : t,\r
8734                                 hex_colors : s.force_hex_style_colors,\r
8735                                 class_filter : s.class_filter,\r
8736                                 update_styles : 1,\r
8737                                 fix_ie_paragraphs : 1,\r
8738                                 valid_styles : s.valid_styles\r
8739                         });\r
8740 \r
8741                         t.schema = new tinymce.dom.Schema();\r
8742 \r
8743                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
8744                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
8745                                 dom : t.dom,\r
8746                                 schema : t.schema\r
8747                         }));\r
8748 \r
8749                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
8750 \r
8751                         t.formatter = new tinymce.Formatter(this);\r
8752 \r
8753                         // Register default formats\r
8754                         t.formatter.register({\r
8755                                 alignleft : [\r
8756                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
8757                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
8758                                 ],\r
8759 \r
8760                                 aligncenter : [\r
8761                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
8762                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
8763                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
8764                                 ],\r
8765 \r
8766                                 alignright : [\r
8767                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
8768                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
8769                                 ],\r
8770 \r
8771                                 alignfull : [\r
8772                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
8773                                 ],\r
8774 \r
8775                                 bold : [\r
8776                                         {inline : 'strong'},\r
8777                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
8778                                         {inline : 'b'}\r
8779                                 ],\r
8780 \r
8781                                 italic : [\r
8782                                         {inline : 'em'},\r
8783                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
8784                                         {inline : 'i'}\r
8785                                 ],\r
8786 \r
8787                                 underline : [\r
8788                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
8789                                         {inline : 'u'}\r
8790                                 ],\r
8791 \r
8792                                 strikethrough : [\r
8793                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
8794                                         {inline : 'u'}\r
8795                                 ],\r
8796 \r
8797                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
8798                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
8799                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
8800                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
8801                                 blockquote : {block : 'blockquote', wrapper : 1},\r
8802 \r
8803                                 removeformat : [\r
8804                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
8805                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
8806                                         {selector : '*', attributes : ['style', 'class'], expand : false, deep : true}\r
8807                                 ]\r
8808                         });\r
8809 \r
8810                         // Register default block formats\r
8811                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
8812                                 t.formatter.register(name, {block : name});\r
8813                         });\r
8814 \r
8815                         // Register user defined formats\r
8816                         t.formatter.register(t.settings.formats);\r
8817 \r
8818                         t.undoManager = new tinymce.UndoManager(t);\r
8819 \r
8820                         // Pass through\r
8821                         t.undoManager.onAdd.add(function(um, l) {\r
8822                                 if (!l.initial)\r
8823                                         return t.onChange.dispatch(t, l, um);\r
8824                         });\r
8825 \r
8826                         t.undoManager.onUndo.add(function(um, l) {\r
8827                                 return t.onUndo.dispatch(t, l, um);\r
8828                         });\r
8829 \r
8830                         t.undoManager.onRedo.add(function(um, l) {\r
8831                                 return t.onRedo.dispatch(t, l, um);\r
8832                         });\r
8833 \r
8834                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
8835                                 forced_root_block : s.forced_root_block\r
8836                         });\r
8837 \r
8838                         t.editorCommands = new tinymce.EditorCommands(t);\r
8839 \r
8840                         // Pass through\r
8841                         t.serializer.onPreProcess.add(function(se, o) {\r
8842                                 return t.onPreProcess.dispatch(t, o, se);\r
8843                         });\r
8844 \r
8845                         t.serializer.onPostProcess.add(function(se, o) {\r
8846                                 return t.onPostProcess.dispatch(t, o, se);\r
8847                         });\r
8848 \r
8849                         t.onPreInit.dispatch(t);\r
8850 \r
8851                         if (!s.gecko_spellcheck)\r
8852                                 t.getBody().spellcheck = 0;\r
8853 \r
8854                         if (!s.readonly)\r
8855                                 t._addEvents();\r
8856 \r
8857                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
8858                         t.onPostRender.dispatch(t);\r
8859 \r
8860                         if (s.directionality)\r
8861                                 t.getBody().dir = s.directionality;\r
8862 \r
8863                         if (s.nowrap)\r
8864                                 t.getBody().style.whiteSpace = "nowrap";\r
8865 \r
8866                         if (s.custom_elements) {\r
8867                                 function handleCustom(ed, o) {\r
8868                                         each(explode(s.custom_elements), function(v) {\r
8869                                                 var n;\r
8870 \r
8871                                                 if (v.indexOf('~') === 0) {\r
8872                                                         v = v.substring(1);\r
8873                                                         n = 'span';\r
8874                                                 } else\r
8875                                                         n = 'div';\r
8876 \r
8877                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
8878                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
8879                                         });\r
8880                                 };\r
8881 \r
8882                                 t.onBeforeSetContent.add(handleCustom);\r
8883                                 t.onPostProcess.add(function(ed, o) {\r
8884                                         if (o.set)\r
8885                                                 handleCustom(ed, o);\r
8886                                 });\r
8887                         }\r
8888 \r
8889                         if (s.handle_node_change_callback) {\r
8890                                 t.onNodeChange.add(function(ed, cm, n) {\r
8891                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
8892                                 });\r
8893                         }\r
8894 \r
8895                         if (s.save_callback) {\r
8896                                 t.onSaveContent.add(function(ed, o) {\r
8897                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
8898 \r
8899                                         if (h)\r
8900                                                 o.content = h;\r
8901                                 });\r
8902                         }\r
8903 \r
8904                         if (s.onchange_callback) {\r
8905                                 t.onChange.add(function(ed, l) {\r
8906                                         t.execCallback('onchange_callback', t, l);\r
8907                                 });\r
8908                         }\r
8909 \r
8910                         if (s.convert_newlines_to_brs) {\r
8911                                 t.onBeforeSetContent.add(function(ed, o) {\r
8912                                         if (o.initial)\r
8913                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
8914                                 });\r
8915                         }\r
8916 \r
8917                         if (s.fix_nesting && isIE) {\r
8918                                 t.onBeforeSetContent.add(function(ed, o) {\r
8919                                         o.content = t._fixNesting(o.content);\r
8920                                 });\r
8921                         }\r
8922 \r
8923                         if (s.preformatted) {\r
8924                                 t.onPostProcess.add(function(ed, o) {\r
8925                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
8926                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
8927 \r
8928                                         if (o.set)\r
8929                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
8930                                 });\r
8931                         }\r
8932 \r
8933                         if (s.verify_css_classes) {\r
8934                                 t.serializer.attribValueFilter = function(n, v) {\r
8935                                         var s, cl;\r
8936 \r
8937                                         if (n == 'class') {\r
8938                                                 // Build regexp for classes\r
8939                                                 if (!t.classesRE) {\r
8940                                                         cl = t.dom.getClasses();\r
8941 \r
8942                                                         if (cl.length > 0) {\r
8943                                                                 s = '';\r
8944 \r
8945                                                                 each (cl, function(o) {\r
8946                                                                         s += (s ? '|' : '') + o['class'];\r
8947                                                                 });\r
8948 \r
8949                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
8950                                                         }\r
8951                                                 }\r
8952 \r
8953                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
8954                                         }\r
8955 \r
8956                                         return v;\r
8957                                 };\r
8958                         }\r
8959 \r
8960                         if (s.cleanup_callback) {\r
8961                                 t.onBeforeSetContent.add(function(ed, o) {\r
8962                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
8963                                 });\r
8964 \r
8965                                 t.onPreProcess.add(function(ed, o) {\r
8966                                         if (o.set)\r
8967                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
8968 \r
8969                                         if (o.get)\r
8970                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
8971                                 });\r
8972 \r
8973                                 t.onPostProcess.add(function(ed, o) {\r
8974                                         if (o.set)\r
8975                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
8976 \r
8977                                         if (o.get)                                              \r
8978                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
8979                                 });\r
8980                         }\r
8981 \r
8982                         if (s.save_callback) {\r
8983                                 t.onGetContent.add(function(ed, o) {\r
8984                                         if (o.save)\r
8985                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
8986                                 });\r
8987                         }\r
8988 \r
8989                         if (s.handle_event_callback) {\r
8990                                 t.onEvent.add(function(ed, e, o) {\r
8991                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
8992                                                 Event.cancel(e);\r
8993                                 });\r
8994                         }\r
8995 \r
8996                         // Add visual aids when new contents is added\r
8997                         t.onSetContent.add(function() {\r
8998                                 t.addVisual(t.getBody());\r
8999                         });\r
9000 \r
9001                         // Remove empty contents\r
9002                         if (s.padd_empty_editor) {\r
9003                                 t.onPostProcess.add(function(ed, o) {\r
9004                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9005                                 });\r
9006                         }\r
9007 \r
9008                         if (isGecko) {\r
9009                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9010                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9011                                 function fixLinks(ed, o) {\r
9012                                         each(ed.dom.select('a'), function(n) {\r
9013                                                 var pn = n.parentNode;\r
9014 \r
9015                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9016                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9017                                         });\r
9018                                 };\r
9019 \r
9020                                 t.onExecCommand.add(function(ed, cmd) {\r
9021                                         if (cmd === 'CreateLink')\r
9022                                                 fixLinks(ed);\r
9023                                 });\r
9024 \r
9025                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9026 \r
9027                                 if (!s.readonly) {\r
9028                                         try {\r
9029                                                 // Design mode must be set here once again to fix a bug where\r
9030                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9031                                                 d.designMode = 'Off';\r
9032                                                 d.designMode = 'On';\r
9033                                         } catch (ex) {\r
9034                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9035                                                 // The design mode will be set ones the editor is focused\r
9036                                         }\r
9037                                 }\r
9038                         }\r
9039 \r
9040                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9041                         setTimeout(function () {\r
9042                                 if (t.removed)\r
9043                                         return;\r
9044 \r
9045                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9046                                 t.startContent = t.getContent({format : 'raw'});\r
9047                                 t.initialized = true;\r
9048 \r
9049                                 t.onInit.dispatch(t);\r
9050                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9051                                 t.execCallback('init_instance_callback', t);\r
9052                                 t.focus(true);\r
9053                                 t.nodeChanged({initial : 1});\r
9054 \r
9055                                 // Load specified content CSS last\r
9056                                 if (s.content_css) {\r
9057                                         tinymce.each(explode(s.content_css), function(u) {\r
9058                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9059                                         });\r
9060                                 }\r
9061 \r
9062                                 // Handle auto focus\r
9063                                 if (s.auto_focus) {\r
9064                                         setTimeout(function () {\r
9065                                                 var ed = tinymce.get(s.auto_focus);\r
9066 \r
9067                                                 ed.selection.select(ed.getBody(), 1);\r
9068                                                 ed.selection.collapse(1);\r
9069                                                 ed.getWin().focus();\r
9070                                         }, 100);\r
9071                                 }\r
9072                         }, 1);\r
9073         \r
9074                         e = null;\r
9075                 },\r
9076 \r
9077 \r
9078                 focus : function(sf) {\r
9079                         var oed, t = this, ce = t.settings.content_editable;\r
9080 \r
9081                         if (!sf) {\r
9082                                 // Is not content editable or the selection is outside the area in IE\r
9083                                 // the IE statement is needed to avoid bluring if element selections inside layers since\r
9084                                 // the layer is like it's own document in IE\r
9085                                 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))\r
9086                                         t.getWin().focus();\r
9087 \r
9088                         }\r
9089 \r
9090                         if (tinymce.activeEditor != t) {\r
9091                                 if ((oed = tinymce.activeEditor) != null)\r
9092                                         oed.onDeactivate.dispatch(oed, t);\r
9093 \r
9094                                 t.onActivate.dispatch(t, oed);\r
9095                         }\r
9096 \r
9097                         tinymce._setActive(t);\r
9098                 },\r
9099 \r
9100                 execCallback : function(n) {\r
9101                         var t = this, f = t.settings[n], s;\r
9102 \r
9103                         if (!f)\r
9104                                 return;\r
9105 \r
9106                         // Look through lookup\r
9107                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
9108                                 f = s.func;\r
9109                                 s = s.scope;\r
9110                         }\r
9111 \r
9112                         if (is(f, 'string')) {\r
9113                                 s = f.replace(/\.\w+$/, '');\r
9114                                 s = s ? tinymce.resolve(s) : 0;\r
9115                                 f = tinymce.resolve(f);\r
9116                                 t.callbackLookup = t.callbackLookup || {};\r
9117                                 t.callbackLookup[n] = {func : f, scope : s};\r
9118                         }\r
9119 \r
9120                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
9121                 },\r
9122 \r
9123                 translate : function(s) {\r
9124                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
9125 \r
9126                         if (!s)\r
9127                                 return '';\r
9128 \r
9129                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
9130                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
9131                         });\r
9132                 },\r
9133 \r
9134                 getLang : function(n, dv) {\r
9135                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
9136                 },\r
9137 \r
9138                 getParam : function(n, dv, ty) {\r
9139                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
9140 \r
9141                         if (ty === 'hash') {\r
9142                                 o = {};\r
9143 \r
9144                                 if (is(v, 'string')) {\r
9145                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
9146                                                 v = v.split('=');\r
9147 \r
9148                                                 if (v.length > 1)\r
9149                                                         o[tr(v[0])] = tr(v[1]);\r
9150                                                 else\r
9151                                                         o[tr(v[0])] = tr(v);\r
9152                                         });\r
9153                                 } else\r
9154                                         o = v;\r
9155 \r
9156                                 return o;\r
9157                         }\r
9158 \r
9159                         return v;\r
9160                 },\r
9161 \r
9162                 nodeChanged : function(o) {\r
9163                         var t = this, s = t.selection, n = s.getNode() || t.getBody();\r
9164 \r
9165                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
9166                         if (t.initialized) {\r
9167                                 o = o || {};\r
9168                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
9169 \r
9170                                 // Get parents and add them to object\r
9171                                 o.parents = [];\r
9172                                 t.dom.getParent(n, function(node) {\r
9173                                         if (node.nodeName == 'BODY')\r
9174                                                 return true;\r
9175 \r
9176                                         o.parents.push(node);\r
9177                                 });\r
9178 \r
9179                                 t.onNodeChange.dispatch(\r
9180                                         t,\r
9181                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
9182                                         n,\r
9183                                         s.isCollapsed(),\r
9184                                         o\r
9185                                 );\r
9186                         }\r
9187                 },\r
9188 \r
9189                 addButton : function(n, s) {\r
9190                         var t = this;\r
9191 \r
9192                         t.buttons = t.buttons || {};\r
9193                         t.buttons[n] = s;\r
9194                 },\r
9195 \r
9196                 addCommand : function(n, f, s) {\r
9197                         this.execCommands[n] = {func : f, scope : s || this};\r
9198                 },\r
9199 \r
9200                 addQueryStateHandler : function(n, f, s) {\r
9201                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
9202                 },\r
9203 \r
9204                 addQueryValueHandler : function(n, f, s) {\r
9205                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
9206                 },\r
9207 \r
9208                 addShortcut : function(pa, desc, cmd_func, sc) {\r
9209                         var t = this, c;\r
9210 \r
9211                         if (!t.settings.custom_shortcuts)\r
9212                                 return false;\r
9213 \r
9214                         t.shortcuts = t.shortcuts || {};\r
9215 \r
9216                         if (is(cmd_func, 'string')) {\r
9217                                 c = cmd_func;\r
9218 \r
9219                                 cmd_func = function() {\r
9220                                         t.execCommand(c, false, null);\r
9221                                 };\r
9222                         }\r
9223 \r
9224                         if (is(cmd_func, 'object')) {\r
9225                                 c = cmd_func;\r
9226 \r
9227                                 cmd_func = function() {\r
9228                                         t.execCommand(c[0], c[1], c[2]);\r
9229                                 };\r
9230                         }\r
9231 \r
9232                         each(explode(pa), function(pa) {\r
9233                                 var o = {\r
9234                                         func : cmd_func,\r
9235                                         scope : sc || this,\r
9236                                         desc : desc,\r
9237                                         alt : false,\r
9238                                         ctrl : false,\r
9239                                         shift : false\r
9240                                 };\r
9241 \r
9242                                 each(explode(pa, '+'), function(v) {\r
9243                                         switch (v) {\r
9244                                                 case 'alt':\r
9245                                                 case 'ctrl':\r
9246                                                 case 'shift':\r
9247                                                         o[v] = true;\r
9248                                                         break;\r
9249 \r
9250                                                 default:\r
9251                                                         o.charCode = v.charCodeAt(0);\r
9252                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
9253                                         }\r
9254                                 });\r
9255 \r
9256                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
9257                         });\r
9258 \r
9259                         return true;\r
9260                 },\r
9261 \r
9262                 execCommand : function(cmd, ui, val, a) {\r
9263                         var t = this, s = 0, o, st;\r
9264 \r
9265                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
9266                                 t.focus();\r
9267 \r
9268                         o = {};\r
9269                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
9270                         if (o.terminate)\r
9271                                 return false;\r
9272 \r
9273                         // Command callback\r
9274                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
9275                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9276                                 return true;\r
9277                         }\r
9278 \r
9279                         // Registred commands\r
9280                         if (o = t.execCommands[cmd]) {\r
9281                                 st = o.func.call(o.scope, ui, val);\r
9282 \r
9283                                 // Fall through on true\r
9284                                 if (st !== true) {\r
9285                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9286                                         return st;\r
9287                                 }\r
9288                         }\r
9289 \r
9290                         // Plugin commands\r
9291                         each(t.plugins, function(p) {\r
9292                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
9293                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9294                                         s = 1;\r
9295                                         return false;\r
9296                                 }\r
9297                         });\r
9298 \r
9299                         if (s)\r
9300                                 return true;\r
9301 \r
9302                         // Theme commands\r
9303                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
9304                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9305                                 return true;\r
9306                         }\r
9307 \r
9308                         // Execute global commands\r
9309                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
9310                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9311                                 return true;\r
9312                         }\r
9313 \r
9314                         // Editor commands\r
9315                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
9316                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9317                                 return true;\r
9318                         }\r
9319 \r
9320                         // Browser commands\r
9321                         t.getDoc().execCommand(cmd, ui, val);\r
9322                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9323                 },\r
9324 \r
9325                 queryCommandState : function(cmd) {\r
9326                         var t = this, o, s;\r
9327 \r
9328                         // Is hidden then return undefined\r
9329                         if (t._isHidden())\r
9330                                 return;\r
9331 \r
9332                         // Registred commands\r
9333                         if (o = t.queryStateCommands[cmd]) {\r
9334                                 s = o.func.call(o.scope);\r
9335 \r
9336                                 // Fall though on true\r
9337                                 if (s !== true)\r
9338                                         return s;\r
9339                         }\r
9340 \r
9341                         // Registred commands\r
9342                         o = t.editorCommands.queryCommandState(cmd);\r
9343                         if (o !== -1)\r
9344                                 return o;\r
9345 \r
9346                         // Browser commands\r
9347                         try {\r
9348                                 return this.getDoc().queryCommandState(cmd);\r
9349                         } catch (ex) {\r
9350                                 // Fails sometimes see bug: 1896577\r
9351                         }\r
9352                 },\r
9353 \r
9354                 queryCommandValue : function(c) {\r
9355                         var t = this, o, s;\r
9356 \r
9357                         // Is hidden then return undefined\r
9358                         if (t._isHidden())\r
9359                                 return;\r
9360 \r
9361                         // Registred commands\r
9362                         if (o = t.queryValueCommands[c]) {\r
9363                                 s = o.func.call(o.scope);\r
9364 \r
9365                                 // Fall though on true\r
9366                                 if (s !== true)\r
9367                                         return s;\r
9368                         }\r
9369 \r
9370                         // Registred commands\r
9371                         o = t.editorCommands.queryCommandValue(c);\r
9372                         if (is(o))\r
9373                                 return o;\r
9374 \r
9375                         // Browser commands\r
9376                         try {\r
9377                                 return this.getDoc().queryCommandValue(c);\r
9378                         } catch (ex) {\r
9379                                 // Fails sometimes see bug: 1896577\r
9380                         }\r
9381                 },\r
9382 \r
9383                 show : function() {\r
9384                         var t = this;\r
9385 \r
9386                         DOM.show(t.getContainer());\r
9387                         DOM.hide(t.id);\r
9388                         t.load();\r
9389                 },\r
9390 \r
9391                 hide : function() {\r
9392                         var t = this, d = t.getDoc();\r
9393 \r
9394                         // Fixed bug where IE has a blinking cursor left from the editor\r
9395                         if (isIE && d)\r
9396                                 d.execCommand('SelectAll');\r
9397 \r
9398                         // We must save before we hide so Safari doesn't crash\r
9399                         t.save();\r
9400                         DOM.hide(t.getContainer());\r
9401                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
9402                 },\r
9403 \r
9404                 isHidden : function() {\r
9405                         return !DOM.isHidden(this.id);\r
9406                 },\r
9407 \r
9408                 setProgressState : function(b, ti, o) {\r
9409                         this.onSetProgressState.dispatch(this, b, ti, o);\r
9410 \r
9411                         return b;\r
9412                 },\r
9413 \r
9414                 load : function(o) {\r
9415                         var t = this, e = t.getElement(), h;\r
9416 \r
9417                         if (e) {\r
9418                                 o = o || {};\r
9419                                 o.load = true;\r
9420 \r
9421                                 // Double encode existing entities in the value\r
9422                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
9423                                 o.element = e;\r
9424 \r
9425                                 if (!o.no_events)\r
9426                                         t.onLoadContent.dispatch(t, o);\r
9427 \r
9428                                 o.element = e = null;\r
9429 \r
9430                                 return h;\r
9431                         }\r
9432                 },\r
9433 \r
9434                 save : function(o) {\r
9435                         var t = this, e = t.getElement(), h, f;\r
9436 \r
9437                         if (!e || !t.initialized)\r
9438                                 return;\r
9439 \r
9440                         o = o || {};\r
9441                         o.save = true;\r
9442 \r
9443                         // Add undo level will trigger onchange event\r
9444                         if (!o.no_events) {\r
9445                                 t.undoManager.typing = 0;\r
9446                                 t.undoManager.add();\r
9447                         }\r
9448 \r
9449                         o.element = e;\r
9450                         h = o.content = t.getContent(o);\r
9451 \r
9452                         if (!o.no_events)\r
9453                                 t.onSaveContent.dispatch(t, o);\r
9454 \r
9455                         h = o.content;\r
9456 \r
9457                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
9458                                 e.innerHTML = h;\r
9459 \r
9460                                 // Update hidden form element\r
9461                                 if (f = DOM.getParent(t.id, 'form')) {\r
9462                                         each(f.elements, function(e) {\r
9463                                                 if (e.name == t.id) {\r
9464                                                         e.value = h;\r
9465                                                         return false;\r
9466                                                 }\r
9467                                         });\r
9468                                 }\r
9469                         } else\r
9470                                 e.value = h;\r
9471 \r
9472                         o.element = e = null;\r
9473 \r
9474                         return h;\r
9475                 },\r
9476 \r
9477                 setContent : function(h, o) {\r
9478                         var t = this;\r
9479 \r
9480                         o = o || {};\r
9481                         o.format = o.format || 'html';\r
9482                         o.set = true;\r
9483                         o.content = h;\r
9484 \r
9485                         if (!o.no_events)\r
9486                                 t.onBeforeSetContent.dispatch(t, o);\r
9487 \r
9488                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
9489                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
9490                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
9491                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
9492                                 o.format = 'raw';\r
9493                         }\r
9494 \r
9495                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
9496 \r
9497                         if (o.format != 'raw' && t.settings.cleanup) {\r
9498                                 o.getInner = true;\r
9499                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
9500                         }\r
9501 \r
9502                         if (!o.no_events)\r
9503                                 t.onSetContent.dispatch(t, o);\r
9504 \r
9505                         return o.content;\r
9506                 },\r
9507 \r
9508                 getContent : function(o) {\r
9509                         var t = this, h;\r
9510 \r
9511                         o = o || {};\r
9512                         o.format = o.format || 'html';\r
9513                         o.get = true;\r
9514 \r
9515                         if (!o.no_events)\r
9516                                 t.onBeforeGetContent.dispatch(t, o);\r
9517 \r
9518                         if (o.format != 'raw' && t.settings.cleanup) {\r
9519                                 o.getInner = true;\r
9520                                 h = t.serializer.serialize(t.getBody(), o);\r
9521                         } else\r
9522                                 h = t.getBody().innerHTML;\r
9523 \r
9524                         h = h.replace(/^\s*|\s*$/g, '');\r
9525                         o.content = h;\r
9526 \r
9527                         if (!o.no_events)\r
9528                                 t.onGetContent.dispatch(t, o);\r
9529 \r
9530                         return o.content;\r
9531                 },\r
9532 \r
9533                 isDirty : function() {\r
9534                         var t = this;\r
9535 \r
9536                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
9537                 },\r
9538 \r
9539                 getContainer : function() {\r
9540                         var t = this;\r
9541 \r
9542                         if (!t.container)\r
9543                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
9544 \r
9545                         return t.container;\r
9546                 },\r
9547 \r
9548                 getContentAreaContainer : function() {\r
9549                         return this.contentAreaContainer;\r
9550                 },\r
9551 \r
9552                 getElement : function() {\r
9553                         return DOM.get(this.settings.content_element || this.id);\r
9554                 },\r
9555 \r
9556                 getWin : function() {\r
9557                         var t = this, e;\r
9558 \r
9559                         if (!t.contentWindow) {\r
9560                                 e = DOM.get(t.id + "_ifr");\r
9561 \r
9562                                 if (e)\r
9563                                         t.contentWindow = e.contentWindow;\r
9564                         }\r
9565 \r
9566                         return t.contentWindow;\r
9567                 },\r
9568 \r
9569                 getDoc : function() {\r
9570                         var t = this, w;\r
9571 \r
9572                         if (!t.contentDocument) {\r
9573                                 w = t.getWin();\r
9574 \r
9575                                 if (w)\r
9576                                         t.contentDocument = w.document;\r
9577                         }\r
9578 \r
9579                         return t.contentDocument;\r
9580                 },\r
9581 \r
9582                 getBody : function() {\r
9583                         return this.bodyElement || this.getDoc().body;\r
9584                 },\r
9585 \r
9586                 convertURL : function(u, n, e) {\r
9587                         var t = this, s = t.settings;\r
9588 \r
9589                         // Use callback instead\r
9590                         if (s.urlconverter_callback)\r
9591                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
9592 \r
9593                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
9594                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
9595                                 return u;\r
9596 \r
9597                         // Convert to relative\r
9598                         if (s.relative_urls)\r
9599                                 return t.documentBaseURI.toRelative(u);\r
9600 \r
9601                         // Convert to absolute\r
9602                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
9603 \r
9604                         return u;\r
9605                 },\r
9606 \r
9607                 addVisual : function(e) {\r
9608                         var t = this, s = t.settings;\r
9609 \r
9610                         e = e || t.getBody();\r
9611 \r
9612                         if (!is(t.hasVisual))\r
9613                                 t.hasVisual = s.visual;\r
9614 \r
9615                         each(t.dom.select('table,a', e), function(e) {\r
9616                                 var v;\r
9617 \r
9618                                 switch (e.nodeName) {\r
9619                                         case 'TABLE':\r
9620                                                 v = t.dom.getAttrib(e, 'border');\r
9621 \r
9622                                                 if (!v || v == '0') {\r
9623                                                         if (t.hasVisual)\r
9624                                                                 t.dom.addClass(e, s.visual_table_class);\r
9625                                                         else\r
9626                                                                 t.dom.removeClass(e, s.visual_table_class);\r
9627                                                 }\r
9628 \r
9629                                                 return;\r
9630 \r
9631                                         case 'A':\r
9632                                                 v = t.dom.getAttrib(e, 'name');\r
9633 \r
9634                                                 if (v) {\r
9635                                                         if (t.hasVisual)\r
9636                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
9637                                                         else\r
9638                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
9639                                                 }\r
9640 \r
9641                                                 return;\r
9642                                 }\r
9643                         });\r
9644 \r
9645                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
9646                 },\r
9647 \r
9648                 remove : function() {\r
9649                         var t = this, e = t.getContainer();\r
9650 \r
9651                         t.removed = 1; // Cancels post remove event execution\r
9652                         t.hide();\r
9653 \r
9654                         t.execCallback('remove_instance_callback', t);\r
9655                         t.onRemove.dispatch(t);\r
9656 \r
9657                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
9658                         t.onExecCommand.listeners = [];\r
9659 \r
9660                         tinymce.remove(t);\r
9661                         DOM.remove(e);\r
9662                 },\r
9663 \r
9664                 destroy : function(s) {\r
9665                         var t = this;\r
9666 \r
9667                         // One time is enough\r
9668                         if (t.destroyed)\r
9669                                 return;\r
9670 \r
9671                         if (!s) {\r
9672                                 tinymce.removeUnload(t.destroy);\r
9673                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
9674 \r
9675                                 // Manual destroy\r
9676                                 if (t.theme && t.theme.destroy)\r
9677                                         t.theme.destroy();\r
9678 \r
9679                                 // Destroy controls, selection and dom\r
9680                                 t.controlManager.destroy();\r
9681                                 t.selection.destroy();\r
9682                                 t.dom.destroy();\r
9683 \r
9684                                 // Remove all events\r
9685 \r
9686                                 // Don't clear the window or document if content editable\r
9687                                 // is enabled since other instances might still be present\r
9688                                 if (!t.settings.content_editable) {\r
9689                                         Event.clear(t.getWin());\r
9690                                         Event.clear(t.getDoc());\r
9691                                 }\r
9692 \r
9693                                 Event.clear(t.getBody());\r
9694                                 Event.clear(t.formElement);\r
9695                         }\r
9696 \r
9697                         if (t.formElement) {\r
9698                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
9699                                 t.formElement._mceOldSubmit = null;\r
9700                         }\r
9701 \r
9702                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
9703 \r
9704                         if (t.selection)\r
9705                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
9706 \r
9707                         t.destroyed = 1;\r
9708                 },\r
9709 \r
9710                 // Internal functions\r
9711 \r
9712                 _addEvents : function() {\r
9713                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
9714                         var t = this, i, s = t.settings, lo = {\r
9715                                 mouseup : 'onMouseUp',\r
9716                                 mousedown : 'onMouseDown',\r
9717                                 click : 'onClick',\r
9718                                 keyup : 'onKeyUp',\r
9719                                 keydown : 'onKeyDown',\r
9720                                 keypress : 'onKeyPress',\r
9721                                 submit : 'onSubmit',\r
9722                                 reset : 'onReset',\r
9723                                 contextmenu : 'onContextMenu',\r
9724                                 dblclick : 'onDblClick',\r
9725                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
9726                         };\r
9727 \r
9728                         function eventHandler(e, o) {\r
9729                                 var ty = e.type;\r
9730 \r
9731                                 // Don't fire events when it's removed\r
9732                                 if (t.removed)\r
9733                                         return;\r
9734 \r
9735                                 // Generic event handler\r
9736                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
9737                                         // Specific event handler\r
9738                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
9739                                 }\r
9740                         };\r
9741 \r
9742                         // Add DOM events\r
9743                         each(lo, function(v, k) {\r
9744                                 switch (k) {\r
9745                                         case 'contextmenu':\r
9746                                                 if (tinymce.isOpera) {\r
9747                                                         // Fake contextmenu on Opera\r
9748                                                         t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
9749                                                                 if (e.ctrlKey) {\r
9750                                                                         e.fakeType = 'contextmenu';\r
9751                                                                         eventHandler(e);\r
9752                                                                 }\r
9753                                                         });\r
9754                                                 } else\r
9755                                                         t.dom.bind(t.getBody(), k, eventHandler);\r
9756                                                 break;\r
9757 \r
9758                                         case 'paste':\r
9759                                                 t.dom.bind(t.getBody(), k, function(e) {\r
9760                                                         eventHandler(e);\r
9761                                                 });\r
9762                                                 break;\r
9763 \r
9764                                         case 'submit':\r
9765                                         case 'reset':\r
9766                                                 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
9767                                                 break;\r
9768 \r
9769                                         default:\r
9770                                                 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
9771                                 }\r
9772                         });\r
9773 \r
9774                         t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
9775                                 t.focus(true);\r
9776                         });\r
9777 \r
9778 \r
9779                         // Fixes bug where a specified document_base_uri could result in broken images\r
9780                         // This will also fix drag drop of images in Gecko\r
9781                         if (tinymce.isGecko) {\r
9782                                 // Convert all images to absolute URLs\r
9783 /*                              t.onSetContent.add(function(ed, o) {\r
9784                                         each(ed.dom.select('img'), function(e) {\r
9785                                                 var v;\r
9786 \r
9787                                                 if (v = e.getAttribute('_mce_src'))\r
9788                                                         e.src = t.documentBaseURI.toAbsolute(v);\r
9789                                         })\r
9790                                 });*/\r
9791 \r
9792                                 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
9793                                         var v;\r
9794 \r
9795                                         e = e.target;\r
9796 \r
9797                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
9798                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
9799                                 });\r
9800                         }\r
9801 \r
9802                         // Set various midas options in Gecko\r
9803                         if (isGecko) {\r
9804                                 function setOpts() {\r
9805                                         var t = this, d = t.getDoc(), s = t.settings;\r
9806 \r
9807                                         if (isGecko && !s.readonly) {\r
9808                                                 if (t._isHidden()) {\r
9809                                                         try {\r
9810                                                                 if (!s.content_editable)\r
9811                                                                         d.designMode = 'On';\r
9812                                                         } catch (ex) {\r
9813                                                                 // Fails if it's hidden\r
9814                                                         }\r
9815                                                 }\r
9816 \r
9817                                                 try {\r
9818                                                         // Try new Gecko method\r
9819                                                         d.execCommand("styleWithCSS", 0, false);\r
9820                                                 } catch (ex) {\r
9821                                                         // Use old method\r
9822                                                         if (!t._isHidden())\r
9823                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
9824                                                 }\r
9825 \r
9826                                                 if (!s.table_inline_editing)\r
9827                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
9828 \r
9829                                                 if (!s.object_resizing)\r
9830                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
9831                                         }\r
9832                                 };\r
9833 \r
9834                                 t.onBeforeExecCommand.add(setOpts);\r
9835                                 t.onMouseDown.add(setOpts);\r
9836                         }\r
9837 \r
9838                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
9839                         // WebKit can't even do simple things like selecting an image\r
9840                         // This also fixes so it's possible to select mceItemAnchors\r
9841                         if (tinymce.isWebKit) {\r
9842                                 t.onClick.add(function(ed, e) {\r
9843                                         e = e.target;\r
9844 \r
9845                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
9846                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor')))\r
9847                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
9848                                 });\r
9849                         }\r
9850 \r
9851                         // Add node change handlers\r
9852                         t.onMouseUp.add(t.nodeChanged);\r
9853                         t.onClick.add(t.nodeChanged);\r
9854                         t.onKeyUp.add(function(ed, e) {\r
9855                                 var c = e.keyCode;\r
9856 \r
9857                                 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
9858                                         t.nodeChanged();\r
9859                         });\r
9860 \r
9861                         // Add reset handler\r
9862                         t.onReset.add(function() {\r
9863                                 t.setContent(t.startContent, {format : 'raw'});\r
9864                         });\r
9865 \r
9866                         // Add shortcuts\r
9867                         if (s.custom_shortcuts) {\r
9868                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
9869                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
9870                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
9871                                 }\r
9872 \r
9873                                 // Add default shortcuts for gecko\r
9874                                 if (isGecko) {\r
9875                                         t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
9876                                         t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
9877                                         t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
9878                                 }\r
9879 \r
9880                                 // BlockFormat shortcuts keys\r
9881                                 for (i=1; i<=6; i++)\r
9882                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
9883 \r
9884                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
9885                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
9886                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
9887 \r
9888                                 function find(e) {\r
9889                                         var v = null;\r
9890 \r
9891                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
9892                                                 return v;\r
9893 \r
9894                                         each(t.shortcuts, function(o) {\r
9895                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
9896                                                         return;\r
9897                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
9898                                                         return;\r
9899 \r
9900                                                 if (o.alt != e.altKey)\r
9901                                                         return;\r
9902 \r
9903                                                 if (o.shift != e.shiftKey)\r
9904                                                         return;\r
9905 \r
9906                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
9907                                                         v = o;\r
9908                                                         return false;\r
9909                                                 }\r
9910                                         });\r
9911 \r
9912                                         return v;\r
9913                                 };\r
9914 \r
9915                                 t.onKeyUp.add(function(ed, e) {\r
9916                                         var o = find(e);\r
9917 \r
9918                                         if (o)\r
9919                                                 return Event.cancel(e);\r
9920                                 });\r
9921 \r
9922                                 t.onKeyPress.add(function(ed, e) {\r
9923                                         var o = find(e);\r
9924 \r
9925                                         if (o)\r
9926                                                 return Event.cancel(e);\r
9927                                 });\r
9928 \r
9929                                 t.onKeyDown.add(function(ed, e) {\r
9930                                         var o = find(e);\r
9931 \r
9932                                         if (o) {\r
9933                                                 o.func.call(o.scope);\r
9934                                                 return Event.cancel(e);\r
9935                                         }\r
9936                                 });\r
9937                         }\r
9938 \r
9939                         if (tinymce.isIE) {\r
9940                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
9941                                 // It will also block mceItemNoResize items\r
9942                                 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
9943                                         var re = t.resizeInfo, cb;\r
9944 \r
9945                                         e = e.target;\r
9946 \r
9947                                         // Don't do this action for non image elements\r
9948                                         if (e.nodeName !== 'IMG')\r
9949                                                 return;\r
9950 \r
9951                                         if (re)\r
9952                                                 t.dom.unbind(re.node, re.ev, re.cb);\r
9953 \r
9954                                         if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
9955                                                 ev = 'resizeend';\r
9956                                                 cb = t.dom.bind(e, ev, function(e) {\r
9957                                                         var v;\r
9958 \r
9959                                                         e = e.target;\r
9960 \r
9961                                                         if (v = t.dom.getStyle(e, 'width')) {\r
9962                                                                 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
9963                                                                 t.dom.setStyle(e, 'width', '');\r
9964                                                         }\r
9965 \r
9966                                                         if (v = t.dom.getStyle(e, 'height')) {\r
9967                                                                 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
9968                                                                 t.dom.setStyle(e, 'height', '');\r
9969                                                         }\r
9970                                                 });\r
9971                                         } else {\r
9972                                                 ev = 'resizestart';\r
9973                                                 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
9974                                         }\r
9975 \r
9976                                         re = t.resizeInfo = {\r
9977                                                 node : e,\r
9978                                                 ev : ev,\r
9979                                                 cb : cb\r
9980                                         };\r
9981                                 });\r
9982 \r
9983                                 t.onKeyDown.add(function(ed, e) {\r
9984                                         switch (e.keyCode) {\r
9985                                                 case 8:\r
9986                                                         // Fix IE control + backspace browser bug\r
9987                                                         if (t.selection.getRng().item) {\r
9988                                                                 t.selection.getRng().item(0).removeNode();\r
9989                                                                 return Event.cancel(e);\r
9990                                                         }\r
9991                                         }\r
9992                                 });\r
9993 \r
9994                                 /*if (t.dom.boxModel) {\r
9995                                         t.getBody().style.height = '100%';\r
9996 \r
9997                                         Event.add(t.getWin(), 'resize', function(e) {\r
9998                                                 var docElm = t.getDoc().documentElement;\r
9999 \r
10000                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10001                                         });\r
10002                                 }*/\r
10003                         }\r
10004 \r
10005                         if (tinymce.isOpera) {\r
10006                                 t.onClick.add(function(ed, e) {\r
10007                                         Event.prevent(e);\r
10008                                 });\r
10009                         }\r
10010 \r
10011                         // Add custom undo/redo handlers\r
10012                         if (s.custom_undo_redo) {\r
10013                                 function addUndo() {\r
10014                                         t.undoManager.typing = 0;\r
10015                                         t.undoManager.add();\r
10016                                 };\r
10017 \r
10018                                 t.dom.bind(t.getDoc(), 'focusout', function(e) {\r
10019                                         if (!t.removed && t.undoManager.typing)\r
10020                                                 addUndo();\r
10021                                 });\r
10022 \r
10023                                 t.onKeyUp.add(function(ed, e) {\r
10024                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10025                                                 addUndo();\r
10026                                 });\r
10027 \r
10028                                 t.onKeyDown.add(function(ed, e) {\r
10029                                         // Is caracter positon keys\r
10030                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10031                                                 if (t.undoManager.typing)\r
10032                                                         addUndo();\r
10033 \r
10034                                                 return;\r
10035                                         }\r
10036 \r
10037                                         if (!t.undoManager.typing) {\r
10038                                                 t.undoManager.add();\r
10039                                                 t.undoManager.typing = 1;\r
10040                                         }\r
10041                                 });\r
10042 \r
10043                                 t.onMouseDown.add(function() {\r
10044                                         if (t.undoManager.typing)\r
10045                                                 addUndo();\r
10046                                 });\r
10047                         }\r
10048                 },\r
10049 \r
10050                 _isHidden : function() {\r
10051                         var s;\r
10052 \r
10053                         if (!isGecko)\r
10054                                 return 0;\r
10055 \r
10056                         // Weird, wheres that cursor selection?\r
10057                         s = this.selection.getSel();\r
10058                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
10059                 },\r
10060 \r
10061                 // Fix for bug #1867292\r
10062                 _fixNesting : function(s) {\r
10063                         var d = [], i;\r
10064 \r
10065                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
10066                                 var e;\r
10067 \r
10068                                 // Handle end element\r
10069                                 if (b === '/') {\r
10070                                         if (!d.length)\r
10071                                                 return '';\r
10072 \r
10073                                         if (c !== d[d.length - 1].tag) {\r
10074                                                 for (i=d.length - 1; i>=0; i--) {\r
10075                                                         if (d[i].tag === c) {\r
10076                                                                 d[i].close = 1;\r
10077                                                                 break;\r
10078                                                         }\r
10079                                                 }\r
10080 \r
10081                                                 return '';\r
10082                                         } else {\r
10083                                                 d.pop();\r
10084 \r
10085                                                 if (d.length && d[d.length - 1].close) {\r
10086                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
10087                                                         d.pop();\r
10088                                                 }\r
10089                                         }\r
10090                                 } else {\r
10091                                         // Ignore these\r
10092                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
10093                                                 return a;\r
10094 \r
10095                                         // Ignore closed ones\r
10096                                         if (/\/>$/.test(a))\r
10097                                                 return a;\r
10098 \r
10099                                         d.push({tag : c}); // Push start element\r
10100                                 }\r
10101 \r
10102                                 return a;\r
10103                         });\r
10104 \r
10105                         // End all open tags\r
10106                         for (i=d.length - 1; i>=0; i--)\r
10107                                 s += '</' + d[i].tag + '>';\r
10108 \r
10109                         return s;\r
10110                 }\r
10111         });\r
10112 })(tinymce);\r
10113 (function(tinymce) {\r
10114         // Added for compression purposes\r
10115         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
10116 \r
10117         tinymce.EditorCommands = function(editor) {\r
10118                 var dom = editor.dom,\r
10119                         selection = editor.selection,\r
10120                         commands = {state: {}, exec : {}, value : {}},\r
10121                         settings = editor.settings,\r
10122                         bookmark;\r
10123 \r
10124                 function execCommand(command, ui, value) {\r
10125                         var func;\r
10126 \r
10127                         command = command.toLowerCase();\r
10128                         if (func = commands.exec[command]) {\r
10129                                 func(command, ui, value);\r
10130                                 return TRUE;\r
10131                         }\r
10132 \r
10133                         return FALSE;\r
10134                 };\r
10135 \r
10136                 function queryCommandState(command) {\r
10137                         var func;\r
10138 \r
10139                         command = command.toLowerCase();\r
10140                         if (func = commands.state[command])\r
10141                                 return func(command);\r
10142 \r
10143                         return -1;\r
10144                 };\r
10145 \r
10146                 function queryCommandValue(command) {\r
10147                         var func;\r
10148 \r
10149                         command = command.toLowerCase();\r
10150                         if (func = commands.value[command])\r
10151                                 return func(command);\r
10152 \r
10153                         return FALSE;\r
10154                 };\r
10155 \r
10156                 function addCommands(command_list, type) {\r
10157                         type = type || 'exec';\r
10158 \r
10159                         each(command_list, function(callback, command) {\r
10160                                 each(command.toLowerCase().split(','), function(command) {\r
10161                                         commands[type][command] = callback;\r
10162                                 });\r
10163                         });\r
10164                 };\r
10165 \r
10166                 // Expose public methods\r
10167                 tinymce.extend(this, {\r
10168                         execCommand : execCommand,\r
10169                         queryCommandState : queryCommandState,\r
10170                         queryCommandValue : queryCommandValue,\r
10171                         addCommands : addCommands\r
10172                 });\r
10173 \r
10174                 // Private methods\r
10175 \r
10176                 function execNativeCommand(command, ui, value) {\r
10177                         if (ui === undefined)\r
10178                                 ui = FALSE;\r
10179 \r
10180                         if (value === undefined)\r
10181                                 value = null;\r
10182 \r
10183                         return editor.getDoc().execCommand(command, ui, value);\r
10184                 };\r
10185 \r
10186                 function isFormatMatch(name) {\r
10187                         return editor.formatter.match(name);\r
10188                 };\r
10189 \r
10190                 function toggleFormat(name, value) {\r
10191                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
10192                 };\r
10193 \r
10194                 function storeSelection(type) {\r
10195                         bookmark = selection.getBookmark(type);\r
10196                 };\r
10197 \r
10198                 function restoreSelection() {\r
10199                         selection.moveToBookmark(bookmark);\r
10200                 };\r
10201 \r
10202                 // Add execCommand overrides\r
10203                 addCommands({\r
10204                         // Ignore these, added for compatibility\r
10205                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
10206 \r
10207                         // Add undo manager logic\r
10208                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
10209                                 editor.undoManager.add();\r
10210                         },\r
10211 \r
10212                         'Cut,Copy,Paste' : function(command) {\r
10213                                 var doc = editor.getDoc(), failed;\r
10214 \r
10215                                 // Try executing the native command\r
10216                                 try {\r
10217                                         execNativeCommand(command);\r
10218                                 } catch (ex) {\r
10219                                         // Command failed\r
10220                                         failed = TRUE;\r
10221                                 }\r
10222 \r
10223                                 // Present alert message about clipboard access not being available\r
10224                                 if (failed || !doc.queryCommandEnabled(command)) {\r
10225                                         if (tinymce.isGecko) {\r
10226                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
10227                                                         if (state)\r
10228                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
10229                                                 });\r
10230                                         } else\r
10231                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
10232                                 }\r
10233                         },\r
10234 \r
10235                         // Override unlink command\r
10236                         unlink : function(command) {\r
10237                                 if (selection.isCollapsed())\r
10238                                         selection.select(selection.getNode());\r
10239 \r
10240                                 execNativeCommand(command);\r
10241                                 selection.collapse(FALSE);\r
10242                         },\r
10243 \r
10244                         // Override justify commands to use the text formatter engine\r
10245                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
10246                                 var align = command.substring(7);\r
10247 \r
10248                                 // Remove all other alignments first\r
10249                                 each('left,center,right,full'.split(','), function(name) {\r
10250                                         if (align != name)\r
10251                                                 editor.formatter.remove('align' + name);\r
10252                                 });\r
10253 \r
10254                                 toggleFormat('align' + align);\r
10255                         },\r
10256 \r
10257                         // Override list commands to fix WebKit bug\r
10258                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
10259                                 var listElm, listParent;\r
10260 \r
10261                                 execNativeCommand(command);\r
10262 \r
10263                                 // WebKit produces lists within block elements so we need to split them\r
10264                                 // we will replace the native list creation logic to custom logic later on\r
10265                                 // TODO: Remove this when the list creation logic is removed\r
10266                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
10267                                 if (listElm) {\r
10268                                         listParent = listElm.parentNode;\r
10269 \r
10270                                         // If list is within a text block then split that block\r
10271                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
10272                                                 storeSelection();\r
10273                                                 dom.split(listParent, listElm);\r
10274                                                 restoreSelection();\r
10275                                         }\r
10276                                 }\r
10277                         },\r
10278 \r
10279                         // Override commands to use the text formatter engine\r
10280                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
10281                                 toggleFormat(command);\r
10282                         },\r
10283 \r
10284                         // Override commands to use the text formatter engine\r
10285                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
10286                                 toggleFormat(command, value);\r
10287                         },\r
10288 \r
10289                         FontSize : function(command, ui, value) {\r
10290                                 var fontClasses, fontSizes;\r
10291 \r
10292                                 // Convert font size 1-7 to styles\r
10293                                 if (value >= 1 && value <= 7) {\r
10294                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
10295                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
10296 \r
10297                                         if (fontClasses)\r
10298                                                 value = fontClasses[value - 1] || value;\r
10299                                         else\r
10300                                                 value = fontSizes[value - 1] || value;\r
10301                                 }\r
10302 \r
10303                                 toggleFormat(command, value);\r
10304                         },\r
10305 \r
10306                         RemoveFormat : function(command) {\r
10307                                 editor.formatter.remove(command);\r
10308                         },\r
10309 \r
10310                         mceBlockQuote : function(command) {\r
10311                                 toggleFormat('blockquote');\r
10312                         },\r
10313 \r
10314                         FormatBlock : function(command, ui, value) {\r
10315                                 return toggleFormat(value);\r
10316                         },\r
10317 \r
10318                         mceCleanup : function() {\r
10319                                 storeSelection();\r
10320                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
10321                                 restoreSelection();\r
10322                         },\r
10323 \r
10324                         mceRemoveNode : function(command, ui, value) {\r
10325                                 var node = value || selection.getNode();\r
10326 \r
10327                                 // Make sure that the body node isn't removed\r
10328                                 if (node != ed.getBody()) {\r
10329                                         storeSelection();\r
10330                                         editor.dom.remove(node, TRUE);\r
10331                                         restoreSelection();\r
10332                                 }\r
10333                         },\r
10334 \r
10335                         mceSelectNodeDepth : function(command, ui, value) {\r
10336                                 var counter = 0;\r
10337 \r
10338                                 dom.getParent(selection.getNode(), function(node) {\r
10339                                         if (node.nodeType == 1 && counter++ == value) {\r
10340                                                 selection.select(node);\r
10341                                                 return FALSE;\r
10342                                         }\r
10343                                 }, editor.getBody());\r
10344                         },\r
10345 \r
10346                         mceSelectNode : function(command, ui, value) {\r
10347                                 selection.select(value);\r
10348                         },\r
10349 \r
10350                         mceInsertContent : function(command, ui, value) {\r
10351                                 selection.setContent(value);\r
10352                         },\r
10353 \r
10354                         mceInsertRawHTML : function(command, ui, value) {\r
10355                                 selection.setContent('tiny_mce_marker');\r
10356                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value));\r
10357                         },\r
10358 \r
10359                         mceSetContent : function(command, ui, value) {\r
10360                                 editor.setContent(value);\r
10361                         },\r
10362 \r
10363                         'Indent,Outdent' : function(command) {\r
10364                                 var intentValue, indentUnit, value;\r
10365 \r
10366                                 // Setup indent level\r
10367                                 intentValue = settings.indentation;\r
10368                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
10369                                 intentValue = parseInt(intentValue);\r
10370 \r
10371                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
10372                                         each(selection.getSelectedBlocks(), function(element) {\r
10373                                                 if (command == 'outdent') {\r
10374                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
10375                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
10376                                                 } else\r
10377                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
10378                                         });\r
10379                                 } else\r
10380                                         execNativeCommand(command);\r
10381                         },\r
10382 \r
10383                         mceRepaint : function() {\r
10384                                 var bookmark;\r
10385 \r
10386                                 if (tinymce.isGecko) {\r
10387                                         try {\r
10388                                                 storeSelection(TRUE);\r
10389 \r
10390                                                 if (selection.getSel())\r
10391                                                         selection.getSel().selectAllChildren(editor.getBody());\r
10392 \r
10393                                                 selection.collapse(TRUE);\r
10394                                                 restoreSelection();\r
10395                                         } catch (ex) {\r
10396                                                 // Ignore\r
10397                                         }\r
10398                                 }\r
10399                         },\r
10400 \r
10401                         InsertHorizontalRule : function() {\r
10402                                 selection.setContent('<hr />');\r
10403                         },\r
10404 \r
10405                         mceToggleVisualAid : function() {\r
10406                                 editor.hasVisual = !editor.hasVisual;\r
10407                                 editor.addVisual();\r
10408                         },\r
10409 \r
10410                         mceReplaceContent : function(command, ui, value) {\r
10411                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
10412                         },\r
10413 \r
10414                         mceInsertLink : function(command, ui, value) {\r
10415                                 var link = dom.getParent(selection.getNode(), 'a');\r
10416 \r
10417                                 if (tinymce.is(value, 'string'))\r
10418                                         value = {href : value};\r
10419 \r
10420                                 if (!link) {\r
10421                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
10422                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
10423                                                 dom.setAttribs(link, value);\r
10424                                         });\r
10425                                 } else {\r
10426                                         if (value.href)\r
10427                                                 dom.setAttribs(link, value);\r
10428                                         else\r
10429                                                 ed.dom.remove(link, TRUE);\r
10430                                 }\r
10431                         }\r
10432                 });\r
10433 \r
10434                 // Add queryCommandState overrides\r
10435                 addCommands({\r
10436                         // Override justify commands\r
10437                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
10438                                 return isFormatMatch('align' + command.substring(7));\r
10439                         },\r
10440 \r
10441                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
10442                                 return isFormatMatch(command);\r
10443                         },\r
10444 \r
10445                         mceBlockQuote : function() {\r
10446                                 return isFormatMatch('blockquote');\r
10447                         },\r
10448 \r
10449                         Outdent : function() {\r
10450                                 var node;\r
10451 \r
10452                                 if (settings.inline_styles) {\r
10453                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
10454                                                 return TRUE;\r
10455 \r
10456                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
10457                                                 return TRUE;\r
10458                                 }\r
10459 \r
10460                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
10461                         },\r
10462 \r
10463                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
10464                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
10465                         }\r
10466                 }, 'state');\r
10467 \r
10468                 // Add queryCommandValue overrides\r
10469                 addCommands({\r
10470                         'FontSize,FontName' : function(command) {\r
10471                                 var value = 0, parent;\r
10472 \r
10473                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
10474                                         if (command == 'fontsize')\r
10475                                                 value = parent.style.fontSize;\r
10476                                         else\r
10477                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
10478                                 }\r
10479 \r
10480                                 return value;\r
10481                         }\r
10482                 }, 'value');\r
10483 \r
10484                 // Add undo manager logic\r
10485                 if (settings.custom_undo_redo) {\r
10486                         addCommands({\r
10487                                 Undo : function() {\r
10488                                         editor.undoManager.undo();\r
10489                                 },\r
10490 \r
10491                                 Redo : function() {\r
10492                                         editor.undoManager.redo();\r
10493                                 }\r
10494                         });\r
10495                 }\r
10496         };\r
10497 })(tinymce);(function(tinymce) {\r
10498         tinymce.create('tinymce.UndoManager', {\r
10499                 index : 0,\r
10500                 data : null,\r
10501                 typing : 0,\r
10502 \r
10503                 UndoManager : function(ed) {\r
10504                         var t = this, Dispatcher = tinymce.util.Dispatcher;\r
10505 \r
10506                         t.editor = ed;\r
10507                         t.data = [];\r
10508                         t.onAdd = new Dispatcher(this);\r
10509                         t.onUndo = new Dispatcher(this);\r
10510                         t.onRedo = new Dispatcher(this);\r
10511                 },\r
10512 \r
10513                 add : function(l) {\r
10514                         var t = this, i, ed = t.editor, b, s = ed.settings, la;\r
10515 \r
10516                         l = l || {};\r
10517                         l.content = l.content || ed.getContent({format : 'raw', no_events : 1});\r
10518                         l.content = l.content.replace(/^\s*|\s*$/g, '');\r
10519 \r
10520                         // Add undo level if needed\r
10521                         la = t.data[t.index];\r
10522                         if (la && la.content == l.content) {\r
10523                                 if (t.index > 0 || t.data.length == 1)\r
10524                                         return null;\r
10525                         }\r
10526 \r
10527                         // Time to compress\r
10528                         if (s.custom_undo_redo_levels) {\r
10529                                 if (t.data.length > s.custom_undo_redo_levels) {\r
10530                                         for (i = 0; i < t.data.length - 1; i++)\r
10531                                                 t.data[i] = t.data[i + 1];\r
10532 \r
10533                                         t.data.length--;\r
10534                                         t.index = t.data.length;\r
10535                                 }\r
10536                         }\r
10537 \r
10538                         if (s.custom_undo_redo_restore_selection)\r
10539                                 l.bookmark = b = l.bookmark || ed.selection.getBookmark(2, true);\r
10540 \r
10541                         // Crop array if needed\r
10542                         if (t.index < t.data.length - 1) {\r
10543                                 // Treat first level as initial\r
10544                                 if (t.index == 0)\r
10545                                         t.data = [];\r
10546                                 else\r
10547                                         t.data.length = t.index + 1;\r
10548                         }\r
10549 \r
10550                         t.data.push(l);\r
10551                         t.index = t.data.length - 1;\r
10552 \r
10553                         t.onAdd.dispatch(t, l);\r
10554                         ed.isNotDirty = 0;\r
10555 \r
10556                         //console.log(t.index);\r
10557                         //console.dir(t.data);\r
10558 \r
10559                         return l;\r
10560                 },\r
10561 \r
10562                 undo : function() {\r
10563                         var t = this, ed = t.editor, l = l, i;\r
10564 \r
10565                         if (t.typing) {\r
10566                                 t.add();\r
10567                                 t.typing = 0;\r
10568                         }\r
10569 \r
10570                         if (t.index > 0) {\r
10571                                 l = t.data[--t.index];\r
10572 \r
10573                                 ed.setContent(l.content, {format : 'raw'});\r
10574                                 ed.selection.moveToBookmark(l.bookmark);\r
10575 \r
10576                                 t.onUndo.dispatch(t, l);\r
10577                         }\r
10578 \r
10579                         return l;\r
10580                 },\r
10581 \r
10582                 redo : function() {\r
10583                         var t = this, ed = t.editor, l = null;\r
10584 \r
10585                         if (t.index < t.data.length - 1) {\r
10586                                 l = t.data[++t.index];\r
10587                                 ed.setContent(l.content, {format : 'raw'});\r
10588                                 ed.selection.moveToBookmark(l.bookmark);\r
10589 \r
10590                                 t.onRedo.dispatch(t, l);\r
10591                         }\r
10592 \r
10593                         return l;\r
10594                 },\r
10595 \r
10596                 clear : function() {\r
10597                         var t = this;\r
10598 \r
10599                         t.data = [];\r
10600                         t.index = 0;\r
10601                         t.typing = 0;\r
10602                 },\r
10603 \r
10604                 hasUndo : function() {\r
10605                         return this.index > 0 || this.typing;\r
10606                 },\r
10607 \r
10608                 hasRedo : function() {\r
10609                         return this.index < this.data.length - 1;\r
10610                 }\r
10611         });\r
10612 })(tinymce);\r
10613 (function(tinymce) {\r
10614         // Shorten names\r
10615         var Event = tinymce.dom.Event,\r
10616                 isIE = tinymce.isIE,\r
10617                 isGecko = tinymce.isGecko,\r
10618                 isOpera = tinymce.isOpera,\r
10619                 each = tinymce.each,\r
10620                 extend = tinymce.extend,\r
10621                 TRUE = true,\r
10622                 FALSE = false;\r
10623 \r
10624         // Checks if the selection/caret is at the end of the specified block element\r
10625         function isAtEnd(rng, par) {\r
10626                 var rng2 = par.ownerDocument.createRange();\r
10627 \r
10628                 rng2.setStart(rng.endContainer, rng.endOffset);\r
10629                 rng2.setEndAfter(par);\r
10630 \r
10631                 // 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
10632                 return rng2.cloneContents().textContent.length == 0;\r
10633         };\r
10634 \r
10635         function isEmpty(n) {\r
10636                 n = n.innerHTML;\r
10637 \r
10638                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
10639                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
10640 \r
10641                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
10642         };\r
10643 \r
10644         function splitList(selection, dom, li) {\r
10645                 var listBlock, block;\r
10646 \r
10647                 if (isEmpty(li)) {\r
10648                         listBlock = dom.getParent(li, 'ul,ol');\r
10649 \r
10650                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
10651                                 dom.split(listBlock, li);\r
10652                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
10653                                 dom.replace(block, li);\r
10654                                 selection.select(block, 1);\r
10655                         }\r
10656 \r
10657                         return FALSE;\r
10658                 }\r
10659 \r
10660                 return TRUE;\r
10661         };\r
10662 \r
10663         tinymce.create('tinymce.ForceBlocks', {\r
10664                 ForceBlocks : function(ed) {\r
10665                         var t = this, s = ed.settings, elm;\r
10666 \r
10667                         t.editor = ed;\r
10668                         t.dom = ed.dom;\r
10669                         elm = (s.forced_root_block || 'p').toLowerCase();\r
10670                         s.element = elm.toUpperCase();\r
10671 \r
10672                         ed.onPreInit.add(t.setup, t);\r
10673 \r
10674                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
10675                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
10676                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
10677                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
10678                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
10679 \r
10680                         function padd(ed, o) {\r
10681                                 if (isOpera)\r
10682                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
10683 \r
10684                                 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
10685 \r
10686                                 if (!isIE && !isOpera && o.set) {\r
10687                                         // Use &nbsp; instead of BR in padded paragraphs\r
10688                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
10689                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
10690                                 } else\r
10691                                         o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
10692                         };\r
10693 \r
10694                         ed.onBeforeSetContent.add(padd);\r
10695                         ed.onPostProcess.add(padd);\r
10696 \r
10697                         if (s.forced_root_block) {\r
10698                                 ed.onInit.add(t.forceRoots, t);\r
10699                                 ed.onSetContent.add(t.forceRoots, t);\r
10700                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
10701                         }\r
10702                 },\r
10703 \r
10704                 setup : function() {\r
10705                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
10706 \r
10707                         // Force root blocks when typing and when getting output\r
10708                         if (s.forced_root_block) {\r
10709                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
10710                                 ed.onKeyUp.add(t.forceRoots, t);\r
10711                                 ed.onPreProcess.add(t.forceRoots, t);\r
10712                         }\r
10713 \r
10714                         if (s.force_br_newlines) {\r
10715                                 // Force IE to produce BRs on enter\r
10716                                 if (isIE) {\r
10717                                         ed.onKeyPress.add(function(ed, e) {\r
10718                                                 var n;\r
10719 \r
10720                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
10721                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
10722                                                         n = dom.get('__');\r
10723                                                         n.removeAttribute('id');\r
10724                                                         selection.select(n);\r
10725                                                         selection.collapse();\r
10726                                                         return Event.cancel(e);\r
10727                                                 }\r
10728                                         });\r
10729                                 }\r
10730                         }\r
10731 \r
10732                         if (!isIE && s.force_p_newlines) {\r
10733                                 ed.onKeyPress.add(function(ed, e) {\r
10734                                         if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
10735                                                 Event.cancel(e);\r
10736                                 });\r
10737 \r
10738                                 if (isGecko) {\r
10739                                         ed.onKeyDown.add(function(ed, e) {\r
10740                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
10741                                                         t.backspaceDelete(e, e.keyCode == 8);\r
10742                                         });\r
10743                                 }\r
10744                         }\r
10745 \r
10746                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
10747                         if (tinymce.isWebKit) {\r
10748                                 function insertBr(ed) {\r
10749                                         var rng = selection.getRng(), br;\r
10750 \r
10751                                         // Insert BR element\r
10752                                         rng.insertNode(br = dom.create('br'));\r
10753 \r
10754                                         // Place caret after BR\r
10755                                         rng.setStartAfter(br);\r
10756                                         rng.setEndAfter(br);\r
10757                                         selection.setRng(rng);\r
10758 \r
10759                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
10760                                         if (selection.getSel().focusNode == br.previousSibling) {\r
10761                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
10762                                                 selection.collapse(TRUE);\r
10763                                         }\r
10764 \r
10765                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
10766                                         ed.getWin().scrollTo(0, dom.getPos(selection.getRng().startContainer).y);\r
10767                                 };\r
10768 \r
10769                                 ed.onKeyPress.add(function(ed, e) {\r
10770                                         if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) {\r
10771                                                 insertBr(ed);\r
10772                                                 Event.cancel(e);\r
10773                                         }\r
10774                                 });\r
10775                         }\r
10776 \r
10777                         // Padd empty inline elements within block elements\r
10778                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
10779                         ed.onPreProcess.add(function(ed, o) {\r
10780                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
10781                                         if (isEmpty(p)) {\r
10782                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
10783                                                         if (!n.hasChildNodes()) {\r
10784                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
10785                                                                 return FALSE; // Break the loop one padding is enough\r
10786                                                         }\r
10787                                                 });\r
10788                                         }\r
10789                                 });\r
10790                         });\r
10791 \r
10792                         // IE specific fixes\r
10793                         if (isIE) {\r
10794                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
10795                                 if (s.element != 'P') {\r
10796                                         ed.onKeyPress.add(function(ed, e) {\r
10797                                                 t.lastElm = selection.getNode().nodeName;\r
10798                                         });\r
10799 \r
10800                                         ed.onKeyUp.add(function(ed, e) {\r
10801                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
10802 \r
10803                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
10804                                                         n = dom.rename(n, s.element);\r
10805                                                         selection.select(n);\r
10806                                                         selection.collapse();\r
10807                                                         ed.nodeChanged();\r
10808                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
10809                                                         bl = dom.getParent(n, 'p');\r
10810 \r
10811                                                         if (bl) {\r
10812                                                                 dom.rename(bl, s.element);\r
10813                                                                 ed.nodeChanged();\r
10814                                                         }\r
10815                                                 }\r
10816                                         });\r
10817                                 }\r
10818                         }\r
10819                 },\r
10820 \r
10821                 find : function(n, t, s) {\r
10822                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
10823 \r
10824                         while (n = w.nextNode()) {\r
10825                                 c++;\r
10826 \r
10827                                 // Index by node\r
10828                                 if (t == 0 && n == s)\r
10829                                         return c;\r
10830 \r
10831                                 // Node by index\r
10832                                 if (t == 1 && c == s)\r
10833                                         return n;\r
10834                         }\r
10835 \r
10836                         return -1;\r
10837                 },\r
10838 \r
10839                 forceRoots : function(ed, e) {\r
10840                         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
10841                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
10842 \r
10843                         // Fix for bug #1863847\r
10844                         //if (e && e.keyCode == 13)\r
10845                         //      return TRUE;\r
10846 \r
10847                         // Wrap non blocks into blocks\r
10848                         for (i = nl.length - 1; i >= 0; i--) {\r
10849                                 nx = nl[i];\r
10850 \r
10851                                 // Ignore internal elements\r
10852                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
10853                                         bl = null;\r
10854                                         continue;\r
10855                                 }\r
10856 \r
10857                                 // Is text or non block element\r
10858                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
10859                                         if (!bl) {\r
10860                                                 // Create new block but ignore whitespace\r
10861                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
10862                                                         // Store selection\r
10863                                                         if (si == -2 && r) {\r
10864                                                                 if (!isIE) {\r
10865                                                                         // If selection is element then mark it\r
10866                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
10867                                                                                 // Save the id of the selected element\r
10868                                                                                 eid = n.getAttribute("id");\r
10869                                                                                 n.setAttribute("id", "__mce");\r
10870                                                                         } else {\r
10871                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
10872                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
10873                                                                                         so = r.startOffset;\r
10874                                                                                         eo = r.endOffset;\r
10875                                                                                         si = t.find(b, 0, r.startContainer);\r
10876                                                                                         ei = t.find(b, 0, r.endContainer);\r
10877                                                                                 }\r
10878                                                                         }\r
10879                                                                 } else {\r
10880                                                                         tr = d.body.createTextRange();\r
10881                                                                         tr.moveToElementText(b);\r
10882                                                                         tr.collapse(1);\r
10883                                                                         bp = tr.move('character', c) * -1;\r
10884 \r
10885                                                                         tr = r.duplicate();\r
10886                                                                         tr.collapse(1);\r
10887                                                                         sp = tr.move('character', c) * -1;\r
10888 \r
10889                                                                         tr = r.duplicate();\r
10890                                                                         tr.collapse(0);\r
10891                                                                         le = (tr.move('character', c) * -1) - sp;\r
10892 \r
10893                                                                         si = sp - bp;\r
10894                                                                         ei = le;\r
10895                                                                 }\r
10896                                                         }\r
10897 \r
10898                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
10899                                                         // See: http://support.microsoft.com/kb/829907\r
10900                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
10901                                                         nx.parentNode.replaceChild(bl, nx);\r
10902                                                         bl.appendChild(nx);\r
10903                                                 }\r
10904                                         } else {\r
10905                                                 if (bl.hasChildNodes())\r
10906                                                         bl.insertBefore(nx, bl.firstChild);\r
10907                                                 else\r
10908                                                         bl.appendChild(nx);\r
10909                                         }\r
10910                                 } else\r
10911                                         bl = null; // Time to create new block\r
10912                         }\r
10913 \r
10914                         // Restore selection\r
10915                         if (si != -2) {\r
10916                                 if (!isIE) {\r
10917                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
10918                                         r = d.createRange();\r
10919 \r
10920                                         // Select last location or generated block\r
10921                                         if (si != -1)\r
10922                                                 r.setStart(t.find(b, 1, si), so);\r
10923                                         else\r
10924                                                 r.setStart(bl, 0);\r
10925 \r
10926                                         // Select last location or generated block\r
10927                                         if (ei != -1)\r
10928                                                 r.setEnd(t.find(b, 1, ei), eo);\r
10929                                         else\r
10930                                                 r.setEnd(bl, 0);\r
10931 \r
10932                                         if (s) {\r
10933                                                 s.removeAllRanges();\r
10934                                                 s.addRange(r);\r
10935                                         }\r
10936                                 } else {\r
10937                                         try {\r
10938                                                 r = s.createRange();\r
10939                                                 r.moveToElementText(b);\r
10940                                                 r.collapse(1);\r
10941                                                 r.moveStart('character', si);\r
10942                                                 r.moveEnd('character', ei);\r
10943                                                 r.select();\r
10944                                         } catch (ex) {\r
10945                                                 // Ignore\r
10946                                         }\r
10947                                 }\r
10948                         } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
10949                                 // Restore the id of the selected element\r
10950                                 if (eid)\r
10951                                         n.setAttribute('id', eid);\r
10952                                 else\r
10953                                         n.removeAttribute('id');\r
10954 \r
10955                                 // Move caret before selected element\r
10956                                 r = d.createRange();\r
10957                                 r.setStartBefore(n);\r
10958                                 r.setEndBefore(n);\r
10959                                 se.setRng(r);\r
10960                         }\r
10961                 },\r
10962 \r
10963                 getParentBlock : function(n) {\r
10964                         var d = this.dom;\r
10965 \r
10966                         return d.getParent(n, d.isBlock);\r
10967                 },\r
10968 \r
10969                 insertPara : function(e) {\r
10970                         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
10971                         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
10972 \r
10973                         // If root blocks are forced then use Operas default behavior since it's really good\r
10974 // Removed due to bug: #1853816\r
10975 //                      if (se.forced_root_block && isOpera)\r
10976 //                              return TRUE;\r
10977 \r
10978                         // Setup before range\r
10979                         rb = d.createRange();\r
10980 \r
10981                         // If is before the first block element and in body, then move it into first block element\r
10982                         rb.setStart(s.anchorNode, s.anchorOffset);\r
10983                         rb.collapse(TRUE);\r
10984 \r
10985                         // Setup after range\r
10986                         ra = d.createRange();\r
10987 \r
10988                         // If is before the first block element and in body, then move it into first block element\r
10989                         ra.setStart(s.focusNode, s.focusOffset);\r
10990                         ra.collapse(TRUE);\r
10991 \r
10992                         // Setup start/end points\r
10993                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
10994                         sn = dir ? s.anchorNode : s.focusNode;\r
10995                         so = dir ? s.anchorOffset : s.focusOffset;\r
10996                         en = dir ? s.focusNode : s.anchorNode;\r
10997                         eo = dir ? s.focusOffset : s.anchorOffset;\r
10998 \r
10999                         // If selection is in empty table cell\r
11000                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
11001                                 if (sn.firstChild.nodeName == 'BR')\r
11002                                         dom.remove(sn.firstChild); // Remove BR\r
11003 \r
11004                                 // Create two new block elements\r
11005                                 if (sn.childNodes.length == 0) {\r
11006                                         ed.dom.add(sn, se.element, null, '<br />');\r
11007                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11008                                 } else {\r
11009                                         n = sn.innerHTML;\r
11010                                         sn.innerHTML = '';\r
11011                                         ed.dom.add(sn, se.element, null, n);\r
11012                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11013                                 }\r
11014 \r
11015                                 // Move caret into the last one\r
11016                                 r = d.createRange();\r
11017                                 r.selectNodeContents(aft);\r
11018                                 r.collapse(1);\r
11019                                 ed.selection.setRng(r);\r
11020 \r
11021                                 return FALSE;\r
11022                         }\r
11023 \r
11024                         // If the caret is in an invalid location in FF we need to move it into the first block\r
11025                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
11026                                 sn = en = sn.firstChild;\r
11027                                 so = eo = 0;\r
11028                                 rb = d.createRange();\r
11029                                 rb.setStart(sn, 0);\r
11030                                 ra = d.createRange();\r
11031                                 ra.setStart(en, 0);\r
11032                         }\r
11033 \r
11034                         // Never use body as start or end node\r
11035                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11036                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
11037                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11038                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
11039 \r
11040                         // Get start and end blocks\r
11041                         sb = t.getParentBlock(sn);\r
11042                         eb = t.getParentBlock(en);\r
11043                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
11044 \r
11045                         // Return inside list use default browser behavior\r
11046                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
11047                                 if (n.nodeName == 'LI')\r
11048                                         return splitList(ed.selection, t.dom, n);\r
11049 \r
11050                                 return TRUE;\r
11051                         }\r
11052 \r
11053                         // If caption or absolute layers then always generate new blocks within\r
11054                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11055                                 bn = se.element;\r
11056                                 sb = null;\r
11057                         }\r
11058 \r
11059                         // If caption or absolute layers then always generate new blocks within\r
11060                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11061                                 bn = se.element;\r
11062                                 eb = null;\r
11063                         }\r
11064 \r
11065                         // Use P instead\r
11066                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
11067                                 bn = se.element;\r
11068                                 sb = eb = null;\r
11069                         }\r
11070 \r
11071                         // Setup new before and after blocks\r
11072                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
11073                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
11074 \r
11075                         // Remove id from after clone\r
11076                         aft.removeAttribute('id');\r
11077 \r
11078                         // Is header and cursor is at the end, then force paragraph under\r
11079                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
11080                                 aft = ed.dom.create(se.element);\r
11081 \r
11082                         // Find start chop node\r
11083                         n = sc = sn;\r
11084                         do {\r
11085                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11086                                         break;\r
11087 \r
11088                                 sc = n;\r
11089                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
11090 \r
11091                         // Find end chop node\r
11092                         n = ec = en;\r
11093                         do {\r
11094                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11095                                         break;\r
11096 \r
11097                                 ec = n;\r
11098                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
11099 \r
11100                         // Place first chop part into before block element\r
11101                         if (sc.nodeName == bn)\r
11102                                 rb.setStart(sc, 0);\r
11103                         else\r
11104                                 rb.setStartBefore(sc);\r
11105 \r
11106                         rb.setEnd(sn, so);\r
11107                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11108 \r
11109                         // Place secnd chop part within new block element\r
11110                         try {\r
11111                                 ra.setEndAfter(ec);\r
11112                         } catch(ex) {\r
11113                                 //console.debug(s.focusNode, s.focusOffset);\r
11114                         }\r
11115 \r
11116                         ra.setStart(en, eo);\r
11117                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11118 \r
11119                         // Create range around everything\r
11120                         r = d.createRange();\r
11121                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
11122                                 r.setStartBefore(sc.parentNode);\r
11123                         } else {\r
11124                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
11125                                         r.setStartBefore(rb.startContainer);\r
11126                                 else\r
11127                                         r.setStart(rb.startContainer, rb.startOffset);\r
11128                         }\r
11129 \r
11130                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
11131                                 r.setEndAfter(ec.parentNode);\r
11132                         else\r
11133                                 r.setEnd(ra.endContainer, ra.endOffset);\r
11134 \r
11135                         // Delete and replace it with new block elements\r
11136                         r.deleteContents();\r
11137 \r
11138                         if (isOpera)\r
11139                                 ed.getWin().scrollTo(0, vp.y);\r
11140 \r
11141                         // Never wrap blocks in blocks\r
11142                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
11143                                 bef.innerHTML = bef.firstChild.innerHTML;\r
11144 \r
11145                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
11146                                 aft.innerHTML = aft.firstChild.innerHTML;\r
11147 \r
11148                         // Padd empty blocks\r
11149                         if (isEmpty(bef))\r
11150                                 bef.innerHTML = '<br />';\r
11151 \r
11152                         function appendStyles(e, en) {\r
11153                                 var nl = [], nn, n, i;\r
11154 \r
11155                                 e.innerHTML = '';\r
11156 \r
11157                                 // Make clones of style elements\r
11158                                 if (se.keep_styles) {\r
11159                                         n = en;\r
11160                                         do {\r
11161                                                 // We only want style specific elements\r
11162                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
11163                                                         nn = n.cloneNode(FALSE);\r
11164                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
11165                                                         nl.push(nn);\r
11166                                                 }\r
11167                                         } while (n = n.parentNode);\r
11168                                 }\r
11169 \r
11170                                 // Append style elements to aft\r
11171                                 if (nl.length > 0) {\r
11172                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
11173                                                 nn = nn.appendChild(nl[i]);\r
11174 \r
11175                                         // Padd most inner style element\r
11176                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11177                                         return nl[0]; // Move caret to most inner element\r
11178                                 } else\r
11179                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11180                         };\r
11181 \r
11182                         // Fill empty afterblook with current style\r
11183                         if (isEmpty(aft))\r
11184                                 car = appendStyles(aft, en);\r
11185 \r
11186                         // Opera needs this one backwards for older versions\r
11187                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
11188                                 r.insertNode(bef);\r
11189                                 r.insertNode(aft);\r
11190                         } else {\r
11191                                 r.insertNode(aft);\r
11192                                 r.insertNode(bef);\r
11193                         }\r
11194 \r
11195                         // Normalize\r
11196                         aft.normalize();\r
11197                         bef.normalize();\r
11198 \r
11199                         function first(n) {\r
11200                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
11201                         };\r
11202 \r
11203                         // Move cursor and scroll into view\r
11204                         r = d.createRange();\r
11205                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
11206                         r.collapse(1);\r
11207                         s.removeAllRanges();\r
11208                         s.addRange(r);\r
11209 \r
11210                         // 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
11211                         y = ed.dom.getPos(aft).y;\r
11212                         ch = aft.clientHeight;\r
11213 \r
11214                         // Is element within viewport\r
11215                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
11216                                 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
11217                                 //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
11218                         }\r
11219 \r
11220                         return FALSE;\r
11221                 },\r
11222 \r
11223                 backspaceDelete : function(e, bs) {\r
11224                         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
11225 \r
11226                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
11227                         // This workaround removes the element by hand and moves the caret to the previous element\r
11228                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
11229                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
11230                                         // Find previous block element\r
11231                                         n = sc;\r
11232                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
11233 \r
11234                                         if (n) {\r
11235                                                 if (sc != b.firstChild) {\r
11236                                                         // Find last text node\r
11237                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
11238                                                         while (tn = w.nextNode())\r
11239                                                                 n = tn;\r
11240 \r
11241                                                         // Place caret at the end of last text node\r
11242                                                         r = ed.getDoc().createRange();\r
11243                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
11244                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
11245                                                         se.setRng(r);\r
11246 \r
11247                                                         // Remove the target container\r
11248                                                         ed.dom.remove(sc);\r
11249                                                 }\r
11250 \r
11251                                                 return Event.cancel(e);\r
11252                                         }\r
11253                                 }\r
11254                         }\r
11255 \r
11256                         // Gecko generates BR elements here and there, we don't like those so lets remove them\r
11257                         function handler(e) {\r
11258                                 var pr;\r
11259 \r
11260                                 e = e.target;\r
11261 \r
11262                                 // A new BR was created in a block element, remove it\r
11263                                 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {\r
11264                                         pr = e.previousSibling;\r
11265 \r
11266                                         Event.remove(b, 'DOMNodeInserted', handler);\r
11267 \r
11268                                         // Is there whitespace at the end of the node before then we might need the pesky BR\r
11269                                         // to place the caret at a correct location see bug: #2013943\r
11270                                         if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))\r
11271                                                 return;\r
11272 \r
11273                                         // Only remove BR elements that got inserted in the middle of the text\r
11274                                         if (e.previousSibling || e.nextSibling)\r
11275                                                 ed.dom.remove(e);\r
11276                                 }\r
11277                         };\r
11278 \r
11279                         // Listen for new nodes\r
11280                         Event._add(b, 'DOMNodeInserted', handler);\r
11281 \r
11282                         // Remove listener\r
11283                         window.setTimeout(function() {\r
11284                                 Event._remove(b, 'DOMNodeInserted', handler);\r
11285                         }, 1);\r
11286                 }\r
11287         });\r
11288 })(tinymce);\r
11289 (function(tinymce) {\r
11290         // Shorten names\r
11291         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
11292 \r
11293         tinymce.create('tinymce.ControlManager', {\r
11294                 ControlManager : function(ed, s) {\r
11295                         var t = this, i;\r
11296 \r
11297                         s = s || {};\r
11298                         t.editor = ed;\r
11299                         t.controls = {};\r
11300                         t.onAdd = new tinymce.util.Dispatcher(t);\r
11301                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
11302                         t.prefix = s.prefix || ed.id + '_';\r
11303                         t._cls = {};\r
11304 \r
11305                         t.onPostRender.add(function() {\r
11306                                 each(t.controls, function(c) {\r
11307                                         c.postRender();\r
11308                                 });\r
11309                         });\r
11310                 },\r
11311 \r
11312                 get : function(id) {\r
11313                         return this.controls[this.prefix + id] || this.controls[id];\r
11314                 },\r
11315 \r
11316                 setActive : function(id, s) {\r
11317                         var c = null;\r
11318 \r
11319                         if (c = this.get(id))\r
11320                                 c.setActive(s);\r
11321 \r
11322                         return c;\r
11323                 },\r
11324 \r
11325                 setDisabled : function(id, s) {\r
11326                         var c = null;\r
11327 \r
11328                         if (c = this.get(id))\r
11329                                 c.setDisabled(s);\r
11330 \r
11331                         return c;\r
11332                 },\r
11333 \r
11334                 add : function(c) {\r
11335                         var t = this;\r
11336 \r
11337                         if (c) {\r
11338                                 t.controls[c.id] = c;\r
11339                                 t.onAdd.dispatch(c, t);\r
11340                         }\r
11341 \r
11342                         return c;\r
11343                 },\r
11344 \r
11345                 createControl : function(n) {\r
11346                         var c, t = this, ed = t.editor;\r
11347 \r
11348                         each(ed.plugins, function(p) {\r
11349                                 if (p.createControl) {\r
11350                                         c = p.createControl(n, t);\r
11351 \r
11352                                         if (c)\r
11353                                                 return false;\r
11354                                 }\r
11355                         });\r
11356 \r
11357                         switch (n) {\r
11358                                 case "|":\r
11359                                 case "separator":\r
11360                                         return t.createSeparator();\r
11361                         }\r
11362 \r
11363                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
11364                                 return t.createButton(n, c);\r
11365 \r
11366                         return t.add(c);\r
11367                 },\r
11368 \r
11369                 createDropMenu : function(id, s, cc) {\r
11370                         var t = this, ed = t.editor, c, bm, v, cls;\r
11371 \r
11372                         s = extend({\r
11373                                 'class' : 'mceDropDown',\r
11374                                 constrain : ed.settings.constrain_menus\r
11375                         }, s);\r
11376 \r
11377                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
11378                         if (v = ed.getParam('skin_variant'))\r
11379                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
11380 \r
11381                         id = t.prefix + id;\r
11382                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
11383                         c = t.controls[id] = new cls(id, s);\r
11384                         c.onAddItem.add(function(c, o) {\r
11385                                 var s = o.settings;\r
11386 \r
11387                                 s.title = ed.getLang(s.title, s.title);\r
11388 \r
11389                                 if (!s.onclick) {\r
11390                                         s.onclick = function(v) {\r
11391                                                 if (s.cmd)\r
11392                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
11393                                         };\r
11394                                 }\r
11395                         });\r
11396 \r
11397                         ed.onRemove.add(function() {\r
11398                                 c.destroy();\r
11399                         });\r
11400 \r
11401                         // Fix for bug #1897785, #1898007\r
11402                         if (tinymce.isIE) {\r
11403                                 c.onShowMenu.add(function() {\r
11404                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
11405                                         ed.focus();\r
11406 \r
11407                                         bm = ed.selection.getBookmark(1);\r
11408                                 });\r
11409 \r
11410                                 c.onHideMenu.add(function() {\r
11411                                         if (bm) {\r
11412                                                 ed.selection.moveToBookmark(bm);\r
11413                                                 bm = 0;\r
11414                                         }\r
11415                                 });\r
11416                         }\r
11417 \r
11418                         return t.add(c);\r
11419                 },\r
11420 \r
11421                 createListBox : function(id, s, cc) {\r
11422                         var t = this, ed = t.editor, cmd, c, cls;\r
11423 \r
11424                         if (t.get(id))\r
11425                                 return null;\r
11426 \r
11427                         s.title = ed.translate(s.title);\r
11428                         s.scope = s.scope || ed;\r
11429 \r
11430                         if (!s.onselect) {\r
11431                                 s.onselect = function(v) {\r
11432                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11433                                 };\r
11434                         }\r
11435 \r
11436                         s = extend({\r
11437                                 title : s.title,\r
11438                                 'class' : 'mce_' + id,\r
11439                                 scope : s.scope,\r
11440                                 control_manager : t\r
11441                         }, s);\r
11442 \r
11443                         id = t.prefix + id;\r
11444 \r
11445                         if (ed.settings.use_native_selects)\r
11446                                 c = new tinymce.ui.NativeListBox(id, s);\r
11447                         else {\r
11448                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
11449                                 c = new cls(id, s);\r
11450                         }\r
11451 \r
11452                         t.controls[id] = c;\r
11453 \r
11454                         // Fix focus problem in Safari\r
11455                         if (tinymce.isWebKit) {\r
11456                                 c.onPostRender.add(function(c, n) {\r
11457                                         // Store bookmark on mousedown\r
11458                                         Event.add(n, 'mousedown', function() {\r
11459                                                 ed.bookmark = ed.selection.getBookmark(1);\r
11460                                         });\r
11461 \r
11462                                         // Restore on focus, since it might be lost\r
11463                                         Event.add(n, 'focus', function() {\r
11464                                                 ed.selection.moveToBookmark(ed.bookmark);\r
11465                                                 ed.bookmark = null;\r
11466                                         });\r
11467                                 });\r
11468                         }\r
11469 \r
11470                         if (c.hideMenu)\r
11471                                 ed.onMouseDown.add(c.hideMenu, c);\r
11472 \r
11473                         return t.add(c);\r
11474                 },\r
11475 \r
11476                 createButton : function(id, s, cc) {\r
11477                         var t = this, ed = t.editor, o, c, cls;\r
11478 \r
11479                         if (t.get(id))\r
11480                                 return null;\r
11481 \r
11482                         s.title = ed.translate(s.title);\r
11483                         s.label = ed.translate(s.label);\r
11484                         s.scope = s.scope || ed;\r
11485 \r
11486                         if (!s.onclick && !s.menu_button) {\r
11487                                 s.onclick = function() {\r
11488                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
11489                                 };\r
11490                         }\r
11491 \r
11492                         s = extend({\r
11493                                 title : s.title,\r
11494                                 'class' : 'mce_' + id,\r
11495                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
11496                                 scope : s.scope,\r
11497                                 control_manager : t\r
11498                         }, s);\r
11499 \r
11500                         id = t.prefix + id;\r
11501 \r
11502                         if (s.menu_button) {\r
11503                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
11504                                 c = new cls(id, s);\r
11505                                 ed.onMouseDown.add(c.hideMenu, c);\r
11506                         } else {\r
11507                                 cls = t._cls.button || tinymce.ui.Button;\r
11508                                 c = new cls(id, s);\r
11509                         }\r
11510 \r
11511                         return t.add(c);\r
11512                 },\r
11513 \r
11514                 createMenuButton : function(id, s, cc) {\r
11515                         s = s || {};\r
11516                         s.menu_button = 1;\r
11517 \r
11518                         return this.createButton(id, s, cc);\r
11519                 },\r
11520 \r
11521                 createSplitButton : function(id, s, cc) {\r
11522                         var t = this, ed = t.editor, cmd, c, cls;\r
11523 \r
11524                         if (t.get(id))\r
11525                                 return null;\r
11526 \r
11527                         s.title = ed.translate(s.title);\r
11528                         s.scope = s.scope || ed;\r
11529 \r
11530                         if (!s.onclick) {\r
11531                                 s.onclick = function(v) {\r
11532                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11533                                 };\r
11534                         }\r
11535 \r
11536                         if (!s.onselect) {\r
11537                                 s.onselect = function(v) {\r
11538                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11539                                 };\r
11540                         }\r
11541 \r
11542                         s = extend({\r
11543                                 title : s.title,\r
11544                                 'class' : 'mce_' + id,\r
11545                                 scope : s.scope,\r
11546                                 control_manager : t\r
11547                         }, s);\r
11548 \r
11549                         id = t.prefix + id;\r
11550                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
11551                         c = t.add(new cls(id, s));\r
11552                         ed.onMouseDown.add(c.hideMenu, c);\r
11553 \r
11554                         return c;\r
11555                 },\r
11556 \r
11557                 createColorSplitButton : function(id, s, cc) {\r
11558                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
11559 \r
11560                         if (t.get(id))\r
11561                                 return null;\r
11562 \r
11563                         s.title = ed.translate(s.title);\r
11564                         s.scope = s.scope || ed;\r
11565 \r
11566                         if (!s.onclick) {\r
11567                                 s.onclick = function(v) {\r
11568                                         if (tinymce.isIE)\r
11569                                                 bm = ed.selection.getBookmark(1);\r
11570 \r
11571                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11572                                 };\r
11573                         }\r
11574 \r
11575                         if (!s.onselect) {\r
11576                                 s.onselect = function(v) {\r
11577                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11578                                 };\r
11579                         }\r
11580 \r
11581                         s = extend({\r
11582                                 title : s.title,\r
11583                                 'class' : 'mce_' + id,\r
11584                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
11585                                 scope : s.scope,\r
11586                                 more_colors_title : ed.getLang('more_colors')\r
11587                         }, s);\r
11588 \r
11589                         id = t.prefix + id;\r
11590                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
11591                         c = new cls(id, s);\r
11592                         ed.onMouseDown.add(c.hideMenu, c);\r
11593 \r
11594                         // Remove the menu element when the editor is removed\r
11595                         ed.onRemove.add(function() {\r
11596                                 c.destroy();\r
11597                         });\r
11598 \r
11599                         // Fix for bug #1897785, #1898007\r
11600                         if (tinymce.isIE) {\r
11601                                 c.onShowMenu.add(function() {\r
11602                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
11603                                         ed.focus();\r
11604                                         bm = ed.selection.getBookmark(1);\r
11605                                 });\r
11606 \r
11607                                 c.onHideMenu.add(function() {\r
11608                                         if (bm) {\r
11609                                                 ed.selection.moveToBookmark(bm);\r
11610                                                 bm = 0;\r
11611                                         }\r
11612                                 });\r
11613                         }\r
11614 \r
11615                         return t.add(c);\r
11616                 },\r
11617 \r
11618                 createToolbar : function(id, s, cc) {\r
11619                         var c, t = this, cls;\r
11620 \r
11621                         id = t.prefix + id;\r
11622                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
11623                         c = new cls(id, s);\r
11624 \r
11625                         if (t.get(id))\r
11626                                 return null;\r
11627 \r
11628                         return t.add(c);\r
11629                 },\r
11630 \r
11631                 createSeparator : function(cc) {\r
11632                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
11633 \r
11634                         return new cls();\r
11635                 },\r
11636 \r
11637                 setControlType : function(n, c) {\r
11638                         return this._cls[n.toLowerCase()] = c;\r
11639                 },\r
11640         \r
11641                 destroy : function() {\r
11642                         each(this.controls, function(c) {\r
11643                                 c.destroy();\r
11644                         });\r
11645 \r
11646                         this.controls = null;\r
11647                 }\r
11648         });\r
11649 })(tinymce);\r
11650 (function(tinymce) {\r
11651         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
11652 \r
11653         tinymce.create('tinymce.WindowManager', {\r
11654                 WindowManager : function(ed) {\r
11655                         var t = this;\r
11656 \r
11657                         t.editor = ed;\r
11658                         t.onOpen = new Dispatcher(t);\r
11659                         t.onClose = new Dispatcher(t);\r
11660                         t.params = {};\r
11661                         t.features = {};\r
11662                 },\r
11663 \r
11664                 open : function(s, p) {\r
11665                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
11666 \r
11667                         // Default some options\r
11668                         s = s || {};\r
11669                         p = p || {};\r
11670                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
11671                         sh = isOpera ? vp.h : screen.height;\r
11672                         s.name = s.name || 'mc_' + new Date().getTime();\r
11673                         s.width = parseInt(s.width || 320);\r
11674                         s.height = parseInt(s.height || 240);\r
11675                         s.resizable = true;\r
11676                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
11677                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
11678                         p.inline = false;\r
11679                         p.mce_width = s.width;\r
11680                         p.mce_height = s.height;\r
11681                         p.mce_auto_focus = s.auto_focus;\r
11682 \r
11683                         if (mo) {\r
11684                                 if (isIE) {\r
11685                                         s.center = true;\r
11686                                         s.help = false;\r
11687                                         s.dialogWidth = s.width + 'px';\r
11688                                         s.dialogHeight = s.height + 'px';\r
11689                                         s.scroll = s.scrollbars || false;\r
11690                                 }\r
11691                         }\r
11692 \r
11693                         // Build features string\r
11694                         each(s, function(v, k) {\r
11695                                 if (tinymce.is(v, 'boolean'))\r
11696                                         v = v ? 'yes' : 'no';\r
11697 \r
11698                                 if (!/^(name|url)$/.test(k)) {\r
11699                                         if (isIE && mo)\r
11700                                                 f += (f ? ';' : '') + k + ':' + v;\r
11701                                         else\r
11702                                                 f += (f ? ',' : '') + k + '=' + v;\r
11703                                 }\r
11704                         });\r
11705 \r
11706                         t.features = s;\r
11707                         t.params = p;\r
11708                         t.onOpen.dispatch(t, s, p);\r
11709 \r
11710                         u = s.url || s.file;\r
11711                         u = tinymce._addVer(u);\r
11712 \r
11713                         try {\r
11714                                 if (isIE && mo) {\r
11715                                         w = 1;\r
11716                                         window.showModalDialog(u, window, f);\r
11717                                 } else\r
11718                                         w = window.open(u, s.name, f);\r
11719                         } catch (ex) {\r
11720                                 // Ignore\r
11721                         }\r
11722 \r
11723                         if (!w)\r
11724                                 alert(t.editor.getLang('popup_blocked'));\r
11725                 },\r
11726 \r
11727                 close : function(w) {\r
11728                         w.close();\r
11729                         this.onClose.dispatch(this);\r
11730                 },\r
11731 \r
11732                 createInstance : function(cl, a, b, c, d, e) {\r
11733                         var f = tinymce.resolve(cl);\r
11734 \r
11735                         return new f(a, b, c, d, e);\r
11736                 },\r
11737 \r
11738                 confirm : function(t, cb, s, w) {\r
11739                         w = w || window;\r
11740 \r
11741                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
11742                 },\r
11743 \r
11744                 alert : function(tx, cb, s, w) {\r
11745                         var t = this;\r
11746 \r
11747                         w = w || window;\r
11748                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
11749 \r
11750                         if (cb)\r
11751                                 cb.call(s || t);\r
11752                 },\r
11753 \r
11754                 resizeBy : function(dw, dh, win) {\r
11755                         win.resizeBy(dw, dh);\r
11756                 },\r
11757 \r
11758                 // Internal functions\r
11759 \r
11760                 _decode : function(s) {\r
11761                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
11762                 }\r
11763         });\r
11764 }(tinymce));(function(tinymce) {\r
11765         function CommandManager() {\r
11766                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
11767 \r
11768                 function add(collection, cmd, func, scope) {\r
11769                         if (typeof(cmd) == 'string')\r
11770                                 cmd = [cmd];\r
11771 \r
11772                         tinymce.each(cmd, function(cmd) {\r
11773                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
11774                         });\r
11775                 };\r
11776 \r
11777                 tinymce.extend(this, {\r
11778                         add : function(cmd, func, scope) {\r
11779                                 add(execCommands, cmd, func, scope);\r
11780                         },\r
11781 \r
11782                         addQueryStateHandler : function(cmd, func, scope) {\r
11783                                 add(queryStateCommands, cmd, func, scope);\r
11784                         },\r
11785 \r
11786                         addQueryValueHandler : function(cmd, func, scope) {\r
11787                                 add(queryValueCommands, cmd, func, scope);\r
11788                         },\r
11789 \r
11790                         execCommand : function(scope, cmd, ui, value, args) {\r
11791                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
11792                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
11793                                                 return true;\r
11794                                 }\r
11795                         },\r
11796 \r
11797                         queryCommandValue : function() {\r
11798                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
11799                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
11800                         },\r
11801 \r
11802                         queryCommandState : function() {\r
11803                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
11804                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
11805                         }\r
11806                 });\r
11807         };\r
11808 \r
11809         tinymce.GlobalCommands = new CommandManager();\r
11810 })(tinymce);(function(tinymce) {\r
11811         tinymce.Formatter = function(ed) {\r
11812                 var formats = {},\r
11813                         each = tinymce.each,\r
11814                         dom = ed.dom,\r
11815                         selection = ed.selection,\r
11816                         TreeWalker = tinymce.dom.TreeWalker,\r
11817                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
11818                         isValid = ed.schema.isValid,\r
11819                         isBlock = dom.isBlock,\r
11820                         forcedRootBlock = ed.settings.forced_root_block,\r
11821                         nodeIndex = dom.nodeIndex,\r
11822                         INVISIBLE_CHAR = '\uFEFF',\r
11823                         MCE_ATTR_RE = /^(src|href|style)$/,\r
11824                         FALSE = false,\r
11825                         TRUE = true,\r
11826                         undefined,\r
11827                         caretHandler,\r
11828                         pendingFormats;\r
11829 \r
11830                 function getParents(node, selector) {\r
11831                         return dom.getParents(node, selector, dom.getRoot());\r
11832                 };\r
11833 \r
11834                 function resetPending() {\r
11835                         // Needs reset\r
11836                         if (!pendingFormats || pendingFormats.apply.length || pendingFormats.remove.length)\r
11837                                 pendingFormats = {apply : [], remove : []};\r
11838                 };\r
11839 \r
11840                 ed.onMouseUp.add(resetPending);\r
11841                 resetPending();\r
11842 \r
11843                 // Public functions\r
11844 \r
11845                 function get(name) {\r
11846                         return name ? formats[name] : formats;\r
11847                 };\r
11848 \r
11849                 function register(name, format) {\r
11850                         if (name) {\r
11851                                 if (typeof(name) !== 'string') {\r
11852                                         each(name, function(format, name) {\r
11853                                                 register(name, format);\r
11854                                         });\r
11855                                 } else {\r
11856                                         // Force format into array and add it to internal collection\r
11857                                         format = format.length ? format : [format];\r
11858 \r
11859                                         each(format, function(format) {\r
11860                                                 // Set deep to false by default on selector formats this to avoid removing\r
11861                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
11862                                                 if (format.deep === undefined)\r
11863                                                         format.deep = !format.selector;\r
11864 \r
11865                                                 // Default to true\r
11866                                                 if (format.split === undefined)\r
11867                                                         format.split = !format.selector;\r
11868 \r
11869                                                 // Default to true\r
11870                                                 if (format.remove === undefined && format.selector)\r
11871                                                         format.remove = 'none';\r
11872 \r
11873                                                 // Split classes if needed\r
11874                                                 if (typeof(format.classes) === 'string')\r
11875                                                         format.classes = format.classes.split(/\s+/);\r
11876                                         });\r
11877 \r
11878                                         formats[name] = format;\r
11879                                 }\r
11880                         }\r
11881                 };\r
11882 \r
11883                 function apply(name, vars, node) {\r
11884                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
11885 \r
11886                         function moveStart(rng) {\r
11887                                 var container = rng.startContainer,\r
11888                                         offset = rng.startOffset,\r
11889                                         walker, node;\r
11890 \r
11891                                 // Move startContainer/startOffset in to a suitable node\r
11892                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
11893                                         walker = new TreeWalker(container.childNodes[offset]);\r
11894                                         for (node = walker.current(); node; node = walker.next()) {\r
11895                                                 if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) {\r
11896                                                         rng.setStart(node, 0);\r
11897                                                         break;\r
11898                                                 }\r
11899                                         }\r
11900                                 }\r
11901 \r
11902                                 return rng;\r
11903                         };\r
11904 \r
11905                         function setElementFormat(elm, fmt) {\r
11906                                 fmt = fmt || format;\r
11907 \r
11908                                 if (elm) {\r
11909                                         each(fmt.styles, function(value, name) {\r
11910                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
11911                                         });\r
11912 \r
11913                                         each(fmt.attributes, function(value, name) {\r
11914                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
11915                                         });\r
11916 \r
11917                                         each(fmt.classes, function(value) {\r
11918                                                 value = replaceVars(value, vars);\r
11919 \r
11920                                                 if (!dom.hasClass(elm, value))\r
11921                                                         dom.addClass(elm, value);\r
11922                                         });\r
11923                                 }\r
11924                         };\r
11925 \r
11926                         function applyRngStyle(rng) {\r
11927                                 var newWrappers = [], wrapName, wrapElm;\r
11928 \r
11929                                 // Setup wrapper element\r
11930                                 wrapName = format.inline || format.block;\r
11931                                 wrapElm = dom.create(wrapName);\r
11932                                 setElementFormat(wrapElm);\r
11933 \r
11934                                 rangeUtils.walk(rng, function(nodes) {\r
11935                                         var currentWrapElm;\r
11936 \r
11937                                         function process(node) {\r
11938                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase();\r
11939 \r
11940                                                 // Stop wrapping on br elements\r
11941                                                 if (isEq(nodeName, 'br')) {\r
11942                                                         currentWrapElm = 0;\r
11943 \r
11944                                                         // Remove any br elements when we wrap things\r
11945                                                         if (format.block)\r
11946                                                                 dom.remove(node);\r
11947 \r
11948                                                         return;\r
11949                                                 }\r
11950 \r
11951                                                 // If node is wrapper type\r
11952                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
11953                                                         currentWrapElm = 0;\r
11954                                                         return;\r
11955                                                 }\r
11956 \r
11957                                                 // Can we rename the block\r
11958                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
11959                                                         node = dom.rename(node, wrapName);\r
11960                                                         setElementFormat(node);\r
11961                                                         newWrappers.push(node);\r
11962                                                         currentWrapElm = 0;\r
11963                                                         return;\r
11964                                                 }\r
11965 \r
11966                                                 // Handle selector patterns\r
11967                                                 if (format.selector) {\r
11968                                                         // Look for matching formats\r
11969                                                         each(formatList, function(format) {\r
11970                                                                 if (dom.is(node, format.selector))\r
11971                                                                         setElementFormat(node, format);\r
11972                                                         });\r
11973 \r
11974                                                         return;\r
11975                                                 }\r
11976 \r
11977                                                 // Is it valid to wrap this item\r
11978                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
11979                                                         // Start wrapping\r
11980                                                         if (!currentWrapElm) {\r
11981                                                                 // Wrap the node\r
11982                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
11983                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
11984                                                                 newWrappers.push(currentWrapElm);\r
11985                                                         }\r
11986 \r
11987                                                         currentWrapElm.appendChild(node);\r
11988                                                 } else {\r
11989                                                         // Start a new wrapper for possible children\r
11990                                                         currentWrapElm = 0;\r
11991 \r
11992                                                         each(tinymce.grep(node.childNodes), process);\r
11993 \r
11994                                                         // End the last wrapper\r
11995                                                         currentWrapElm = 0;\r
11996                                                 }\r
11997                                         };\r
11998 \r
11999                                         // Process siblings from range\r
12000                                         each(nodes, process);\r
12001                                 });\r
12002 \r
12003                                 // Cleanup\r
12004                                 each(newWrappers, function(node) {\r
12005                                         var childCount;\r
12006 \r
12007                                         function getChildCount(node) {\r
12008                                                 var count = 0;\r
12009 \r
12010                                                 each(node.childNodes, function(node) {\r
12011                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
12012                                                                 count++;\r
12013                                                 });\r
12014 \r
12015                                                 return count;\r
12016                                         };\r
12017 \r
12018                                         function mergeStyles(node) {\r
12019                                                 var child, clone;\r
12020 \r
12021                                                 each(node.childNodes, function(node) {\r
12022                                                         if (node.nodeType == 1 && !isBookmarkNode(node)) {\r
12023                                                                 child = node;\r
12024                                                                 return FALSE; // break loop\r
12025                                                         }\r
12026                                                 });\r
12027 \r
12028                                                 // If child was found and of the same type as the current node\r
12029                                                 if (child && matchName(child, format)) {\r
12030                                                         clone = child.cloneNode(FALSE);\r
12031                                                         setElementFormat(clone);\r
12032 \r
12033                                                         dom.replace(clone, node, TRUE);\r
12034                                                         dom.remove(child, 1);\r
12035 \r
12036                                                         return TRUE;\r
12037                                                 }\r
12038                                         };\r
12039 \r
12040                                         childCount = getChildCount(node);\r
12041 \r
12042                                         // Remove empty nodes\r
12043                                         if (childCount === 0) {\r
12044                                                 dom.remove(node, 1);\r
12045                                                 return;\r
12046                                         }\r
12047 \r
12048                                         if (format.inline || format.wrapper) {\r
12049                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
12050                                                 if (!format.exact && childCount === 1) {\r
12051                                                         if (mergeStyles(node))\r
12052                                                                 return;\r
12053                                                 }\r
12054 \r
12055                                                 // Remove/merge children\r
12056                                                 each(formatList, function(format) {\r
12057                                                         // Merge all children of similar type will move styles from child to parent\r
12058                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
12059                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
12060                                                         each(dom.select(format.inline, node), function(child) {\r
12061                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
12062                                                         });\r
12063                                                 });\r
12064 \r
12065                                                 // Look for parent with similar style format\r
12066                                                 dom.getParent(node.parentNode, function(parent) {\r
12067                                                         if (matchNode(parent, name, vars)) {\r
12068                                                                 dom.remove(node, 1);\r
12069                                                                 node = 0;\r
12070                                                                 return TRUE;\r
12071                                                         }\r
12072                                                 });\r
12073 \r
12074                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
12075                                                 if (node) {\r
12076                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
12077                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
12078                                                 }\r
12079                                         }\r
12080                                 });\r
12081                         };\r
12082 \r
12083                         if (format) {\r
12084                                 if (node) {\r
12085                                         rng = dom.createRng();\r
12086 \r
12087                                         rng.setStartBefore(node);\r
12088                                         rng.setEndAfter(node);\r
12089 \r
12090                                         applyRngStyle(rng);\r
12091                                 } else {\r
12092                                         if (!selection.isCollapsed() || !format.inline) {\r
12093                                                 // Apply formatting to selection\r
12094                                                 bookmark = selection.getBookmark();\r
12095                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
12096 \r
12097                                                 selection.moveToBookmark(bookmark);\r
12098                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
12099                                                 ed.nodeChanged();\r
12100                                         } else\r
12101                                                 performCaretAction('apply', name, vars);\r
12102                                 }\r
12103                         }\r
12104                 };\r
12105 \r
12106                 function remove(name, vars, node) {\r
12107                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
12108 \r
12109                         // Merges the styles for each node\r
12110                         function process(node) {\r
12111                                 var children, i, l;\r
12112 \r
12113                                 // Grab the children first since the nodelist might be changed\r
12114                                 children = tinymce.grep(node.childNodes);\r
12115 \r
12116                                 // Process current node\r
12117                                 for (i = 0, l = formatList.length; i < l; i++) {\r
12118                                         if (removeFormat(formatList[i], vars, node, node))\r
12119                                                 break;\r
12120                                 }\r
12121 \r
12122                                 // Process the children\r
12123                                 if (format.deep) {\r
12124                                         for (i = 0, l = children.length; i < l; i++)\r
12125                                                 process(children[i]);\r
12126                                 }\r
12127                         };\r
12128 \r
12129                         function findFormatRoot(container) {\r
12130                                 var formatRoot;\r
12131 \r
12132                                 // Find format root\r
12133                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
12134                                         // Find format root element\r
12135                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
12136                                                 // If the matched format has a remove none flag we shouldn't split it\r
12137                                                 if (!isBlock(parent) && matchNode(parent, name, vars))\r
12138                                                         formatRoot = parent;\r
12139                                         }\r
12140                                 });\r
12141 \r
12142                                 return formatRoot;\r
12143                         };\r
12144 \r
12145                         function wrapAndSplit(format_root, container, target, split) {\r
12146                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
12147 \r
12148                                 // Format root found then clone formats and split it\r
12149                                 if (format_root) {\r
12150                                         formatRootParent = format_root.parentNode;\r
12151 \r
12152                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
12153                                                 clone = parent.cloneNode(FALSE);\r
12154 \r
12155                                                 for (i = 0; i < formatList.length; i++) {\r
12156                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
12157                                                                 clone = 0;\r
12158                                                                 break;\r
12159                                                         }\r
12160                                                 }\r
12161 \r
12162                                                 // Build wrapper node\r
12163                                                 if (clone) {\r
12164                                                         if (lastClone)\r
12165                                                                 clone.appendChild(lastClone);\r
12166 \r
12167                                                         if (!firstClone)\r
12168                                                                 firstClone = clone;\r
12169 \r
12170                                                         lastClone = clone;\r
12171                                                 }\r
12172                                         }\r
12173 \r
12174                                         if (split)\r
12175                                                 container = dom.split(format_root, container);\r
12176 \r
12177                                         // Wrap container in cloned formats\r
12178                                         if (lastClone) {\r
12179                                                 target.parentNode.insertBefore(lastClone, target);\r
12180                                                 firstClone.appendChild(target);\r
12181                                         }\r
12182                                 }\r
12183 \r
12184                                 return container;\r
12185                         };\r
12186 \r
12187                         function splitToFormatRoot(container) {\r
12188                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
12189                         };\r
12190 \r
12191                         function unwrap(start) {\r
12192                                 var node = dom.get(start ? '_start' : '_end'),\r
12193                                         out = node[start ? 'firstChild' : 'lastChild'];\r
12194 \r
12195                                 dom.remove(node, 1);\r
12196 \r
12197                                 return out;\r
12198                         };\r
12199 \r
12200                         function removeRngStyle(rng) {\r
12201                                 var startContainer, endContainer;\r
12202 \r
12203                                 rng = expandRng(rng, formatList, TRUE);\r
12204 \r
12205                                 if (format.split) {\r
12206                                         startContainer = getContainer(rng, TRUE);\r
12207                                         endContainer = getContainer(rng);\r
12208 \r
12209                                         if (startContainer != endContainer) {\r
12210                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
12211                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
12212                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
12213 \r
12214                                                 // Split start/end\r
12215                                                 splitToFormatRoot(startContainer);\r
12216                                                 splitToFormatRoot(endContainer);\r
12217 \r
12218                                                 // Unwrap start/end to get real elements again\r
12219                                                 startContainer = unwrap(TRUE);\r
12220                                                 endContainer = unwrap();\r
12221                                         } else\r
12222                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
12223 \r
12224                                         // Update range positions since they might have changed after the split operations\r
12225                                         rng.startContainer = startContainer.parentNode;\r
12226                                         rng.startOffset = nodeIndex(startContainer);\r
12227                                         rng.endContainer = endContainer.parentNode;\r
12228                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
12229                                 }\r
12230 \r
12231                                 // Remove items between start/end\r
12232                                 rangeUtils.walk(rng, function(nodes) {\r
12233                                         each(nodes, function(node) {\r
12234                                                 process(node);\r
12235                                         });\r
12236                                 });\r
12237                         };\r
12238 \r
12239                         // Handle node\r
12240                         if (node) {\r
12241                                 rng = dom.createRng();\r
12242                                 rng.setStartBefore(node);\r
12243                                 rng.setEndAfter(node);\r
12244                                 removeRngStyle(rng);\r
12245                                 return;\r
12246                         }\r
12247 \r
12248                         if (!selection.isCollapsed() || !format.inline) {\r
12249                                 bookmark = selection.getBookmark();\r
12250                                 removeRngStyle(selection.getRng(TRUE));\r
12251                                 selection.moveToBookmark(bookmark);\r
12252                                 ed.nodeChanged();\r
12253                         } else\r
12254                                 performCaretAction('remove', name, vars);\r
12255                 };\r
12256 \r
12257                 function toggle(name, vars, node) {\r
12258                         if (match(name, vars, node))\r
12259                                 remove(name, vars, node);\r
12260                         else\r
12261                                 apply(name, vars, node);\r
12262                 };\r
12263 \r
12264                 function matchNode(node, name, vars) {\r
12265                         var formatList = get(name), format, i, classes;\r
12266 \r
12267                         function matchItems(node, format, item_name) {\r
12268                                 var key, value, items = format[item_name], i;\r
12269 \r
12270                                 // Check all items\r
12271                                 if (items) {\r
12272                                         // Non indexed object\r
12273                                         if (items.length === undefined) {\r
12274                                                 for (key in items) {\r
12275                                                         if (items.hasOwnProperty(key)) {\r
12276                                                                 if (item_name === 'attributes')\r
12277                                                                         value = dom.getAttrib(node, key);\r
12278                                                                 else\r
12279                                                                         value = getStyle(node, key);\r
12280 \r
12281                                                                 if (!isEq(value, replaceVars(items[key], vars)))\r
12282                                                                         return;\r
12283                                                         }\r
12284                                                 }\r
12285                                         } else {\r
12286                                                 // Only one match needed for indexed arrays\r
12287                                                 for (i = 0; i < items.length; i++) {\r
12288                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
12289                                                                 return TRUE;\r
12290                                                 }\r
12291                                         }\r
12292                                 }\r
12293 \r
12294                                 return TRUE;\r
12295                         };\r
12296 \r
12297                         if (formatList && node) {\r
12298                                 // Check each format in list\r
12299                                 for (i = 0; i < formatList.length; i++) {\r
12300                                         format = formatList[i];\r
12301 \r
12302                                         // Name name, attributes, styles and classes\r
12303                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
12304                                                 // Match classes\r
12305                                                 if (classes = format.classes) {\r
12306                                                         for (i = 0; i < classes.length; i++) {\r
12307                                                                 if (!dom.hasClass(node, classes[i]))\r
12308                                                                         return;\r
12309                                                         }\r
12310                                                 }\r
12311 \r
12312                                                 return TRUE;\r
12313                                         }\r
12314                                 }\r
12315                         }\r
12316                 };\r
12317 \r
12318                 function match(name, vars, node) {\r
12319                         var startNode, i;\r
12320 \r
12321                         function matchParents(node) {\r
12322                                 // Find first node with similar format settings\r
12323                                 node = dom.getParent(node, function(node) {\r
12324                                         return !!matchNode(node, name, vars);\r
12325                                 });\r
12326 \r
12327                                 // Do an exact check on the similar format element\r
12328                                 return matchNode(node, name, vars);\r
12329                         };\r
12330 \r
12331                         // Check specified node\r
12332                         if (node)\r
12333                                 return matchParents(node);\r
12334 \r
12335                         // Check pending formats\r
12336                         if (selection.isCollapsed()) {\r
12337                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
12338                                         if (pendingFormats.apply[i].name == name)\r
12339                                                 return true;\r
12340                                 }\r
12341 \r
12342                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
12343                                         if (pendingFormats.remove[i].name == name)\r
12344                                                 return false;\r
12345                                 }\r
12346 \r
12347                                 return matchParents(selection.getNode());\r
12348                         }\r
12349 \r
12350                         // Check selected node\r
12351                         node = selection.getNode();\r
12352                         if (matchParents(node))\r
12353                                 return TRUE;\r
12354 \r
12355                         // Check start node if it's different\r
12356                         startNode = selection.getStart();\r
12357                         if (startNode != node) {\r
12358                                 if (matchParents(startNode))\r
12359                                         return TRUE;\r
12360                         }\r
12361 \r
12362                         return FALSE;\r
12363                 };\r
12364 \r
12365                 function canApply(name) {\r
12366                         var formatList = get(name), startNode, parents, i, x, selector;\r
12367 \r
12368                         if (formatList) {\r
12369                                 startNode = selection.getStart();\r
12370                                 parents = getParents(startNode);\r
12371 \r
12372                                 for (x = formatList.length - 1; x >= 0; x--) {\r
12373                                         selector = formatList[x].selector;\r
12374 \r
12375                                         // Format is not selector based, then always return TRUE\r
12376                                         if (!selector)\r
12377                                                 return TRUE;\r
12378 \r
12379                                         for (i = parents.length - 1; i >= 0; i--) {\r
12380                                                 if (dom.is(parents[i], selector))\r
12381                                                         return TRUE;\r
12382                                         }\r
12383                                 }\r
12384                         }\r
12385 \r
12386                         return FALSE;\r
12387                 };\r
12388 \r
12389                 // Expose to public\r
12390                 tinymce.extend(this, {\r
12391                         get : get,\r
12392                         register : register,\r
12393                         apply : apply,\r
12394                         remove : remove,\r
12395                         toggle : toggle,\r
12396                         match : match,\r
12397                         matchNode : matchNode,\r
12398                         canApply : canApply\r
12399                 });\r
12400 \r
12401                 // Private functions\r
12402 \r
12403                 function matchName(node, format) {\r
12404                         // Check for inline match\r
12405                         if (isEq(node, format.inline))\r
12406                                 return TRUE;\r
12407 \r
12408                         // Check for block match\r
12409                         if (isEq(node, format.block))\r
12410                                 return TRUE;\r
12411 \r
12412                         // Check for selector match\r
12413                         if (format.selector)\r
12414                                 return dom.is(node, format.selector);\r
12415                 };\r
12416 \r
12417                 function isEq(str1, str2) {\r
12418                         str1 = str1 || '';\r
12419                         str2 = str2 || '';\r
12420 \r
12421                         str1 = str1.nodeName || str1;\r
12422                         str2 = str2.nodeName || str2;\r
12423 \r
12424                         return str1.toLowerCase() == str2.toLowerCase();\r
12425                 };\r
12426 \r
12427                 function getStyle(node, name) {\r
12428                         var styleVal = dom.getStyle(node, name);\r
12429 \r
12430                         // Force the format to hex\r
12431                         if (name == 'color' || name == 'backgroundColor')\r
12432                                 styleVal = dom.toHex(styleVal);\r
12433 \r
12434                         // Opera will return bold as 700\r
12435                         if (name == 'fontWeight' && styleVal == 700)\r
12436                                 styleVal = 'bold';\r
12437 \r
12438                         return '' + styleVal;\r
12439                 };\r
12440 \r
12441                 function replaceVars(value, vars) {\r
12442                         if (typeof(value) != "string")\r
12443                                 value = value(vars);\r
12444                         else if (vars) {\r
12445                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
12446                                         return vars[name] || str;\r
12447                                 });\r
12448                         }\r
12449 \r
12450                         return value;\r
12451                 };\r
12452 \r
12453                 function isWhiteSpaceNode(node) {\r
12454                         return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue);\r
12455                 };\r
12456 \r
12457                 function wrap(node, name, attrs) {\r
12458                         var wrapper = dom.create(name, attrs);\r
12459 \r
12460                         node.parentNode.insertBefore(wrapper, node);\r
12461                         wrapper.appendChild(node);\r
12462 \r
12463                         return wrapper;\r
12464                 };\r
12465 \r
12466                 function expandRng(rng, format, remove) {\r
12467                         var startContainer = rng.startContainer,\r
12468                                 startOffset = rng.startOffset,\r
12469                                 endContainer = rng.endContainer,\r
12470                                 endOffset = rng.endOffset, sibling, lastIdx;\r
12471 \r
12472                         // This function walks up the tree if there is no siblings before/after the node\r
12473                         function findParentContainer(container, child_name, sibling_name, root) {\r
12474                                 var parent, child;\r
12475 \r
12476                                 root = root || dom.getRoot();\r
12477 \r
12478                                 for (;;) {\r
12479                                         // Check if we can move up are we at root level or body level\r
12480                                         parent = container.parentNode;\r
12481 \r
12482                                         // Stop expanding on block elements or root depending on format\r
12483                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
12484                                                 return container;\r
12485 \r
12486                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
12487                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
12488                                                         return container;\r
12489 \r
12490                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
12491                                                         return container;\r
12492                                         }\r
12493 \r
12494                                         container = container.parentNode;\r
12495                                 }\r
12496 \r
12497                                 return container;\r
12498                         };\r
12499 \r
12500                         // If index based start position then resolve it\r
12501                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
12502                                 lastIdx = startContainer.childNodes.length - 1;\r
12503                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
12504 \r
12505                                 if (startContainer.nodeType == 3)\r
12506                                         startOffset = 0;\r
12507                         }\r
12508 \r
12509                         // If index based end position then resolve it\r
12510                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
12511                                 lastIdx = endContainer.childNodes.length - 1;\r
12512                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
12513 \r
12514                                 if (endContainer.nodeType == 3)\r
12515                                         endOffset = endContainer.nodeValue.length;\r
12516                         }\r
12517 \r
12518                         // Exclude bookmark nodes if possible\r
12519                         if (isBookmarkNode(startContainer.parentNode))\r
12520                                 startContainer = startContainer.parentNode;\r
12521 \r
12522                         if (isBookmarkNode(startContainer))\r
12523                                 startContainer = startContainer.nextSibling || startContainer;\r
12524 \r
12525                         if (isBookmarkNode(endContainer.parentNode))\r
12526                                 endContainer = endContainer.parentNode;\r
12527 \r
12528                         if (isBookmarkNode(endContainer))\r
12529                                 endContainer = endContainer.previousSibling || endContainer;\r
12530 \r
12531                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
12532                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
12533                         // This will reduce the number of wrapper elements that needs to be created\r
12534                         // Move start point up the tree\r
12535                         if (format[0].inline || format[0].block_expand) {\r
12536                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
12537                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
12538                         }\r
12539 \r
12540                         // Expand start/end container to matching selector\r
12541                         if (format[0].selector && format[0].expand !== FALSE) {\r
12542                                 function findSelectorEndPoint(container, sibling_name) {\r
12543                                         var parents, i, y;\r
12544 \r
12545                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
12546                                                 container = container[sibling_name];\r
12547 \r
12548                                         parents = getParents(container);\r
12549                                         for (i = 0; i < parents.length; i++) {\r
12550                                                 for (y = 0; y < format.length; y++) {\r
12551                                                         if (dom.is(parents[i], format[y].selector))\r
12552                                                                 return parents[i];\r
12553                                                 }\r
12554                                         }\r
12555 \r
12556                                         return container;\r
12557                                 };\r
12558 \r
12559                                 // Find new startContainer/endContainer if there is better one\r
12560                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
12561                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
12562                         }\r
12563 \r
12564                         // Expand start/end container to matching block element or text node\r
12565                         if (format[0].block || format[0].selector) {\r
12566                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
12567                                         var node;\r
12568 \r
12569                                         // Expand to block of similar type\r
12570                                         if (!format[0].wrapper)\r
12571                                                 node = dom.getParent(container, format[0].block);\r
12572 \r
12573                                         // Expand to first wrappable block element or any block element\r
12574                                         if (!node)\r
12575                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
12576 \r
12577                                         // Exclude inner lists from wrapping\r
12578                                         if (node && format[0].wrapper)\r
12579                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
12580 \r
12581                                         // Didn't find a block element look for first/last wrappable element\r
12582                                         if (!node) {\r
12583                                                 node = container;\r
12584 \r
12585                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
12586                                                         node = node[sibling_name];\r
12587 \r
12588                                                         // Break on BR but include it will be removed later on\r
12589                                                         // we can't remove it now since we need to check if it can be wrapped\r
12590                                                         if (isEq(node, 'br'))\r
12591                                                                 break;\r
12592                                                 }\r
12593                                         }\r
12594 \r
12595                                         return node || container;\r
12596                                 };\r
12597 \r
12598                                 // Find new startContainer/endContainer if there is better one\r
12599                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
12600                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
12601 \r
12602                                 // Non block element then try to expand up the leaf\r
12603                                 if (format[0].block) {\r
12604                                         if (!isBlock(startContainer))\r
12605                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
12606 \r
12607                                         if (!isBlock(endContainer))\r
12608                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
12609                                 }\r
12610                         }\r
12611 \r
12612                         // Setup index for startContainer\r
12613                         if (startContainer.nodeType == 1) {\r
12614                                 startOffset = nodeIndex(startContainer);\r
12615                                 startContainer = startContainer.parentNode;\r
12616                         }\r
12617 \r
12618                         // Setup index for endContainer\r
12619                         if (endContainer.nodeType == 1) {\r
12620                                 endOffset = nodeIndex(endContainer) + 1;\r
12621                                 endContainer = endContainer.parentNode;\r
12622                         }\r
12623 \r
12624                         // Return new range like object\r
12625                         return {\r
12626                                 startContainer : startContainer,\r
12627                                 startOffset : startOffset,\r
12628                                 endContainer : endContainer,\r
12629                                 endOffset : endOffset\r
12630                         };\r
12631                 }\r
12632 \r
12633                 function removeFormat(format, vars, node, compare_node) {\r
12634                         var i, attrs, stylesModified;\r
12635 \r
12636                         // Check if node matches format\r
12637                         if (!matchName(node, format))\r
12638                                 return FALSE;\r
12639 \r
12640                         // Should we compare with format attribs and styles\r
12641                         if (format.remove != 'all') {\r
12642                                 // Remove styles\r
12643                                 each(format.styles, function(value, name) {\r
12644                                         value = replaceVars(value, vars);\r
12645 \r
12646                                         // Indexed array\r
12647                                         if (typeof(name) === 'number') {\r
12648                                                 name = value;\r
12649                                                 compare_node = 0;\r
12650                                         }\r
12651 \r
12652                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
12653                                                 dom.setStyle(node, name, '');\r
12654 \r
12655                                         stylesModified = 1;\r
12656                                 });\r
12657 \r
12658                                 // Remove style attribute if it's empty\r
12659                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
12660                                         node.removeAttribute('style');\r
12661                                         node.removeAttribute('_mce_style');\r
12662                                 }\r
12663 \r
12664                                 // Remove attributes\r
12665                                 each(format.attributes, function(value, name) {\r
12666                                         var valueOut;\r
12667 \r
12668                                         value = replaceVars(value, vars);\r
12669 \r
12670                                         // Indexed array\r
12671                                         if (typeof(name) === 'number') {\r
12672                                                 name = value;\r
12673                                                 compare_node = 0;\r
12674                                         }\r
12675 \r
12676                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
12677                                                 // Keep internal classes\r
12678                                                 if (name == 'class') {\r
12679                                                         value = dom.getAttrib(node, name);\r
12680                                                         if (value) {\r
12681                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
12682                                                                 valueOut = '';\r
12683                                                                 each(value.split(/\s+/), function(cls) {\r
12684                                                                         if (/mce\w+/.test(cls))\r
12685                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
12686                                                                 });\r
12687 \r
12688                                                                 // We got some internal classes left\r
12689                                                                 if (valueOut) {\r
12690                                                                         dom.setAttrib(node, name, valueOut);\r
12691                                                                         return;\r
12692                                                                 }\r
12693                                                         }\r
12694                                                 }\r
12695 \r
12696                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
12697                                                 if (name == "class")\r
12698                                                         node.removeAttribute('className');\r
12699 \r
12700                                                 // Remove mce prefixed attributes\r
12701                                                 if (MCE_ATTR_RE.test(name))\r
12702                                                         node.removeAttribute('_mce_' + name);\r
12703 \r
12704                                                 node.removeAttribute(name);\r
12705                                         }\r
12706                                 });\r
12707 \r
12708                                 // Remove classes\r
12709                                 each(format.classes, function(value) {\r
12710                                         value = replaceVars(value, vars);\r
12711 \r
12712                                         if (!compare_node || dom.hasClass(compare_node, value))\r
12713                                                 dom.removeClass(node, value);\r
12714                                 });\r
12715 \r
12716                                 // Check for non internal attributes\r
12717                                 attrs = dom.getAttribs(node);\r
12718                                 for (i = 0; i < attrs.length; i++) {\r
12719                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
12720                                                 return FALSE;\r
12721                                 }\r
12722                         }\r
12723 \r
12724                         // Remove the inline child if it's empty for example <b> or <span>\r
12725                         if (format.remove != 'none') {\r
12726                                 removeNode(node, format);\r
12727                                 return TRUE;\r
12728                         }\r
12729                 };\r
12730 \r
12731                 function removeNode(node, format) {\r
12732                         var parentNode = node.parentNode, rootBlockElm;\r
12733 \r
12734                         if (format.block) {\r
12735                                 if (!forcedRootBlock) {\r
12736                                         function find(node, next, inc) {\r
12737                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
12738 \r
12739                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
12740                                         };\r
12741 \r
12742                                         // Append BR elements if needed before we remove the block\r
12743                                         if (isBlock(node) && !isBlock(parentNode)) {\r
12744                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
12745                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
12746 \r
12747                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
12748                                                         node.appendChild(dom.create('br'));\r
12749                                         }\r
12750                                 } else {\r
12751                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
12752                                         if (parentNode == dom.getRoot()) {\r
12753                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
12754                                                         each(tinymce.grep(node.childNodes), function(node) {\r
12755                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
12756                                                                         if (!rootBlockElm)\r
12757                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
12758                                                                         else\r
12759                                                                                 rootBlockElm.appendChild(node);\r
12760                                                                 } else\r
12761                                                                         rootBlockElm = 0;\r
12762                                                         });\r
12763                                                 }\r
12764                                         }\r
12765                                 }\r
12766                         }\r
12767 \r
12768                         dom.remove(node, 1);\r
12769                 };\r
12770 \r
12771                 function getNonWhiteSpaceSibling(node, next, inc) {\r
12772                         if (node) {\r
12773                                 next = next ? 'nextSibling' : 'previousSibling';\r
12774 \r
12775                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
12776                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
12777                                                 return node;\r
12778                                 }\r
12779                         }\r
12780                 };\r
12781 \r
12782                 function isBookmarkNode(node) {\r
12783                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
12784                 };\r
12785 \r
12786                 function mergeSiblings(prev, next) {\r
12787                         var marker, sibling, tmpSibling;\r
12788 \r
12789                         function compareElements(node1, node2) {\r
12790                                 // Not the same name\r
12791                                 if (node1.nodeName != node2.nodeName)\r
12792                                         return FALSE;\r
12793 \r
12794                                 function getAttribs(node) {\r
12795                                         var attribs = {};\r
12796 \r
12797                                         each(dom.getAttribs(node), function(attr) {\r
12798                                                 var name = attr.nodeName.toLowerCase();\r
12799 \r
12800                                                 // Don't compare internal attributes or style/class\r
12801                                                 if (name.indexOf('_') !== 0 && name !== 'class' && name !== 'style')\r
12802                                                         attribs[name] = dom.getAttrib(node, name);\r
12803                                         });\r
12804 \r
12805                                         return attribs;\r
12806                                 };\r
12807 \r
12808                                 function compareObjects(obj1, obj2) {\r
12809                                         var value, name;\r
12810 \r
12811                                         for (name in obj1) {\r
12812                                                 // Obj1 has item obj2 doesn't have\r
12813                                                 if (obj1.hasOwnProperty(name)) {\r
12814                                                         value = obj2[name];\r
12815 \r
12816                                                         // Obj2 doesn't have obj1 item\r
12817                                                         if (value === undefined)\r
12818                                                                 return FALSE;\r
12819 \r
12820                                                         // Obj2 item has a different value\r
12821                                                         if (obj1[name] != value)\r
12822                                                                 return FALSE;\r
12823 \r
12824                                                         // Delete similar value\r
12825                                                         delete obj2[name];\r
12826                                                 }\r
12827                                         }\r
12828 \r
12829                                         // Check if obj 2 has something obj 1 doesn't have\r
12830                                         for (name in obj2) {\r
12831                                                 // Obj2 has item obj1 doesn't have\r
12832                                                 if (obj2.hasOwnProperty(name))\r
12833                                                         return FALSE;\r
12834                                         }\r
12835 \r
12836                                         return TRUE;\r
12837                                 };\r
12838 \r
12839                                 // Attribs are not the same\r
12840                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
12841                                         return FALSE;\r
12842 \r
12843                                 // Styles are not the same\r
12844                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
12845                                         return FALSE;\r
12846 \r
12847                                 return TRUE;\r
12848                         };\r
12849 \r
12850                         // Check if next/prev exists and that they are elements\r
12851                         if (prev && next) {\r
12852                                 function findElementSibling(node, sibling_name) {\r
12853                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
12854                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
12855                                                         return node;\r
12856 \r
12857                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
12858                                                         return sibling;\r
12859                                         }\r
12860 \r
12861                                         return node;\r
12862                                 };\r
12863 \r
12864                                 // If previous sibling is empty then jump over it\r
12865                                 prev = findElementSibling(prev, 'previousSibling');\r
12866                                 next = findElementSibling(next, 'nextSibling');\r
12867 \r
12868                                 // Compare next and previous nodes\r
12869                                 if (compareElements(prev, next)) {\r
12870                                         // Append nodes between\r
12871                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
12872                                                 tmpSibling = sibling;\r
12873                                                 sibling = sibling.nextSibling;\r
12874                                                 prev.appendChild(tmpSibling);\r
12875                                         }\r
12876 \r
12877                                         // Remove next node\r
12878                                         dom.remove(next);\r
12879 \r
12880                                         // Move children into prev node\r
12881                                         each(tinymce.grep(next.childNodes), function(node) {\r
12882                                                 prev.appendChild(node);\r
12883                                         });\r
12884 \r
12885                                         return prev;\r
12886                                 }\r
12887                         }\r
12888 \r
12889                         return next;\r
12890                 };\r
12891 \r
12892                 function isTextBlock(name) {\r
12893                         return /^(h[1-6]|p|div|pre|address)$/.test(name);\r
12894                 };\r
12895 \r
12896                 function getContainer(rng, start) {\r
12897                         var container, offset, lastIdx;\r
12898 \r
12899                         container = rng[start ? 'startContainer' : 'endContainer'];\r
12900                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
12901 \r
12902                         if (container.nodeType == 1) {\r
12903                                 lastIdx = container.childNodes.length - 1;\r
12904 \r
12905                                 if (!start && offset)\r
12906                                         offset--;\r
12907 \r
12908                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
12909                         }\r
12910 \r
12911                         return container;\r
12912                 };\r
12913 \r
12914                 function performCaretAction(type, name, vars) {\r
12915                         var i, rng, selectedNode = selection.getNode().parentNode,\r
12916                                 doc = ed.getDoc(), marker = 'mceinline',\r
12917                                 events = ['onKeyDown', 'onKeyUp', 'onKeyPress'],\r
12918                                 currentPendingFormats = pendingFormats[type],\r
12919                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
12920 \r
12921                         // Check if it already exists\r
12922                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
12923                                 if (currentPendingFormats[i].name == name)\r
12924                                         return;\r
12925                         }\r
12926 \r
12927                         currentPendingFormats.push({name : name, vars : vars});\r
12928 \r
12929                         // Check if it's in the oter type\r
12930                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
12931                                 if (otherPendingFormats[i].name == name)\r
12932                                         otherPendingFormats.splice(i, 1);\r
12933                         }\r
12934 \r
12935                         function unbind() {\r
12936                                 if (caretHandler) {\r
12937                                         each(events, function(event) {\r
12938                                                 ed[event].remove(caretHandler);\r
12939                                         });\r
12940 \r
12941                                         caretHandler = 0;\r
12942                                 }\r
12943                         };\r
12944 \r
12945                         function perform(caret_node) {\r
12946                                 // Apply pending formats\r
12947                                 each(pendingFormats.apply.reverse(), function(item) {\r
12948                                         apply(item.name, item.vars, caret_node);\r
12949                                 });\r
12950 \r
12951                                 // Remove pending formats\r
12952                                 each(pendingFormats.remove.reverse(), function(item) {\r
12953                                         remove(item.name, item.vars, caret_node);\r
12954                                 });\r
12955 \r
12956                                 dom.remove(caret_node, 1);\r
12957                                 resetPending();\r
12958                         };\r
12959 \r
12960                         function isMarker(node) {\r
12961                                 return node.face == marker || node.style.fontFamily == marker;\r
12962                         };\r
12963 \r
12964                         unbind();\r
12965 \r
12966                         doc.execCommand('FontName', false, marker);\r
12967 \r
12968                         // IE will convert the current word\r
12969                         each(dom.select('font,span', selectedNode), function(node) {\r
12970                                 var bookmark;\r
12971 \r
12972                                 if (isMarker(node)) {\r
12973                                         bookmark = selection.getBookmark();\r
12974                                         perform(node);\r
12975                                         selection.moveToBookmark(bookmark);\r
12976                                         ed.nodeChanged();\r
12977                                         selectedNode = 0;\r
12978                                 }\r
12979                         });\r
12980 \r
12981                         if (selectedNode) {\r
12982                                 caretHandler = function(ed, e) {\r
12983                                         each(dom.select('font,span', selectedNode), function(node) {\r
12984                                                 var bookmark, textNode;\r
12985 \r
12986                                                 // Look for marker\r
12987                                                 if (node.face == marker || node.style.fontFamily == marker) {\r
12988                                                         textNode = node.firstChild;\r
12989 \r
12990                                                         perform(node);\r
12991 \r
12992                                                         rng = dom.createRng();\r
12993                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
12994                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
12995                                                         selection.setRng(rng);\r
12996                                                         ed.nodeChanged();\r
12997 \r
12998                                                         unbind();\r
12999                                                 }\r
13000                                         });\r
13001 \r
13002                                         // Always unbind and clear pending styles on keyup\r
13003                                         if (e.type == 'keyup') {\r
13004                                                 unbind();\r
13005                                                 resetPending();\r
13006                                         }\r
13007                                 };\r
13008 \r
13009                                 each(events, function(event) {\r
13010                                         ed[event].addToTop(caretHandler);\r
13011                                 });\r
13012                         }\r
13013                 }\r
13014         };\r
13015 })(tinymce);\r
13016 tinymce.onAddEditor.add(function(tinymce, ed) {\r
13017         var filters, fontSizes, dom, settings = ed.settings;\r
13018 \r
13019         if (settings.inline_styles) {\r
13020                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
13021 \r
13022                 function replaceWithSpan(node, styles) {\r
13023                         dom.replace(dom.create('span', {\r
13024                                 style : styles\r
13025                         }), node, 1);\r
13026                 };\r
13027 \r
13028                 filters = {\r
13029                         font : function(dom, node) {\r
13030                                 replaceWithSpan(node, {\r
13031                                         backgroundColor : node.style.backgroundColor,\r
13032                                         color : node.color,\r
13033                                         fontFamily : node.face,\r
13034                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
13035                                 });\r
13036                         },\r
13037 \r
13038                         u : function(dom, node) {\r
13039                                 replaceWithSpan(node, {\r
13040                                         textDecoration : 'underline'\r
13041                                 });\r
13042                         },\r
13043 \r
13044                         strike : function(dom, node) {\r
13045                                 replaceWithSpan(node, {\r
13046                                         textDecoration : 'line-through'\r
13047                                 });\r
13048                         }\r
13049                 };\r
13050 \r
13051                 function convert(editor, params) {\r
13052                         dom = editor.dom;\r
13053 \r
13054                         if (settings.convert_fonts_to_spans) {\r
13055                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
13056                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
13057                                 });\r
13058                         }\r
13059                 };\r
13060 \r
13061                 ed.onPreProcess.add(convert);\r
13062 \r
13063                 ed.onInit.add(function() {\r
13064                         ed.selection.onSetContent.add(convert);\r
13065                 });\r
13066         }\r
13067 });\r