]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/TinyMCE/js/tiny_mce_src.js
Merge commit 'origin/0.9.x' into 0.9.x
[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         var tinymce = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '3.8',\r
9 \r
10                 releaseDate : '2010-06-30',\r
11 \r
12                 _init : function() {\r
13                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
14 \r
15                         t.isOpera = win.opera && opera.buildNumber;\r
16 \r
17                         t.isWebKit = /WebKit/.test(ua);\r
18 \r
19                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
20 \r
21                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
22 \r
23                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
24 \r
25                         t.isMac = ua.indexOf('Mac') != -1;\r
26 \r
27                         t.isAir = /adobeair/i.test(ua);\r
28 \r
29                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
30 \r
31                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
32                         if (win.tinyMCEPreInit) {\r
33                                 t.suffix = tinyMCEPreInit.suffix;\r
34                                 t.baseURL = tinyMCEPreInit.base;\r
35                                 t.query = tinyMCEPreInit.query;\r
36                                 return;\r
37                         }\r
38 \r
39                         // Get suffix and base\r
40                         t.suffix = '';\r
41 \r
42                         // If base element found, add that infront of baseURL\r
43                         nl = d.getElementsByTagName('base');\r
44                         for (i=0; i<nl.length; i++) {\r
45                                 if (v = nl[i].href) {\r
46                                         // Host only value like http://site.com or http://site.com:8008\r
47                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
48                                                 v += '/';\r
49 \r
50                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
51                                 }\r
52                         }\r
53 \r
54                         function getBase(n) {\r
55                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {\r
56                                         if (/_(src|dev)\.js/g.test(n.src))\r
57                                                 t.suffix = '_src';\r
58 \r
59                                         if ((p = n.src.indexOf('?')) != -1)\r
60                                                 t.query = n.src.substring(p + 1);\r
61 \r
62                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
63 \r
64                                         // If path to script is relative and a base href was found add that one infront\r
65                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
66                                         // so this logic will basically only be executed on older IE versions\r
67                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
68                                                 t.baseURL = base + t.baseURL;\r
69 \r
70                                         return t.baseURL;\r
71                                 }\r
72 \r
73                                 return null;\r
74                         };\r
75 \r
76                         // Check document\r
77                         nl = d.getElementsByTagName('script');\r
78                         for (i=0; i<nl.length; i++) {\r
79                                 if (getBase(nl[i]))\r
80                                         return;\r
81                         }\r
82 \r
83                         // Check head\r
84                         n = d.getElementsByTagName('head')[0];\r
85                         if (n) {\r
86                                 nl = n.getElementsByTagName('script');\r
87                                 for (i=0; i<nl.length; i++) {\r
88                                         if (getBase(nl[i]))\r
89                                                 return;\r
90                                 }\r
91                         }\r
92 \r
93                         return;\r
94                 },\r
95 \r
96                 is : function(o, t) {\r
97                         if (!t)\r
98                                 return o !== undefined;\r
99 \r
100                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
101                                 return true;\r
102 \r
103                         return typeof(o) == t;\r
104                 },\r
105 \r
106                 each : function(o, cb, s) {\r
107                         var n, l;\r
108 \r
109                         if (!o)\r
110                                 return 0;\r
111 \r
112                         s = s || o;\r
113 \r
114                         if (o.length !== undefined) {\r
115                                 // Indexed arrays, needed for Safari\r
116                                 for (n=0, l = o.length; n < l; n++) {\r
117                                         if (cb.call(s, o[n], n, o) === false)\r
118                                                 return 0;\r
119                                 }\r
120                         } else {\r
121                                 // Hashtables\r
122                                 for (n in o) {\r
123                                         if (o.hasOwnProperty(n)) {\r
124                                                 if (cb.call(s, o[n], n, o) === false)\r
125                                                         return 0;\r
126                                         }\r
127                                 }\r
128                         }\r
129 \r
130                         return 1;\r
131                 },\r
132 \r
133 \r
134                 trim : function(s) {\r
135                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
136                 },\r
137 \r
138                 create : function(s, p) {\r
139                         var t = this, sp, ns, cn, scn, c, de = 0;\r
140 \r
141                         // Parse : <prefix> <class>:<super class>\r
142                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
143                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
144 \r
145                         // Create namespace for new class\r
146                         ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
147 \r
148                         // Class already exists\r
149                         if (ns[cn])\r
150                                 return;\r
151 \r
152                         // Make pure static class\r
153                         if (s[2] == 'static') {\r
154                                 ns[cn] = p;\r
155 \r
156                                 if (this.onCreate)\r
157                                         this.onCreate(s[2], s[3], ns[cn]);\r
158 \r
159                                 return;\r
160                         }\r
161 \r
162                         // Create default constructor\r
163                         if (!p[cn]) {\r
164                                 p[cn] = function() {};\r
165                                 de = 1;\r
166                         }\r
167 \r
168                         // Add constructor and methods\r
169                         ns[cn] = p[cn];\r
170                         t.extend(ns[cn].prototype, p);\r
171 \r
172                         // Extend\r
173                         if (s[5]) {\r
174                                 sp = t.resolve(s[5]).prototype;\r
175                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
176 \r
177                                 // Extend constructor\r
178                                 c = ns[cn];\r
179                                 if (de) {\r
180                                         // Add passthrough constructor\r
181                                         ns[cn] = function() {\r
182                                                 return sp[scn].apply(this, arguments);\r
183                                         };\r
184                                 } else {\r
185                                         // Add inherit constructor\r
186                                         ns[cn] = function() {\r
187                                                 this.parent = sp[scn];\r
188                                                 return c.apply(this, arguments);\r
189                                         };\r
190                                 }\r
191                                 ns[cn].prototype[cn] = ns[cn];\r
192 \r
193                                 // Add super methods\r
194                                 t.each(sp, function(f, n) {\r
195                                         ns[cn].prototype[n] = sp[n];\r
196                                 });\r
197 \r
198                                 // Add overridden methods\r
199                                 t.each(p, function(f, n) {\r
200                                         // Extend methods if needed\r
201                                         if (sp[n]) {\r
202                                                 ns[cn].prototype[n] = function() {\r
203                                                         this.parent = sp[n];\r
204                                                         return f.apply(this, arguments);\r
205                                                 };\r
206                                         } else {\r
207                                                 if (n != cn)\r
208                                                         ns[cn].prototype[n] = f;\r
209                                         }\r
210                                 });\r
211                         }\r
212 \r
213                         // Add static methods\r
214                         t.each(p['static'], function(f, n) {\r
215                                 ns[cn][n] = f;\r
216                         });\r
217 \r
218                         if (this.onCreate)\r
219                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
220                 },\r
221 \r
222                 walk : function(o, f, n, s) {\r
223                         s = s || this;\r
224 \r
225                         if (o) {\r
226                                 if (n)\r
227                                         o = o[n];\r
228 \r
229                                 tinymce.each(o, function(o, i) {\r
230                                         if (f.call(s, o, i, n) === false)\r
231                                                 return false;\r
232 \r
233                                         tinymce.walk(o, f, n, s);\r
234                                 });\r
235                         }\r
236                 },\r
237 \r
238                 createNS : function(n, o) {\r
239                         var i, v;\r
240 \r
241                         o = o || win;\r
242 \r
243                         n = n.split('.');\r
244                         for (i=0; i<n.length; i++) {\r
245                                 v = n[i];\r
246 \r
247                                 if (!o[v])\r
248                                         o[v] = {};\r
249 \r
250                                 o = o[v];\r
251                         }\r
252 \r
253                         return o;\r
254                 },\r
255 \r
256                 resolve : function(n, o) {\r
257                         var i, l;\r
258 \r
259                         o = o || win;\r
260 \r
261                         n = n.split('.');\r
262                         for (i = 0, l = n.length; i < l; i++) {\r
263                                 o = o[n[i]];\r
264 \r
265                                 if (!o)\r
266                                         break;\r
267                         }\r
268 \r
269                         return o;\r
270                 },\r
271 \r
272                 addUnload : function(f, s) {\r
273                         var t = this;\r
274 \r
275                         f = {func : f, scope : s || this};\r
276 \r
277                         if (!t.unloads) {\r
278                                 function unload() {\r
279                                         var li = t.unloads, o, n;\r
280 \r
281                                         if (li) {\r
282                                                 // Call unload handlers\r
283                                                 for (n in li) {\r
284                                                         o = li[n];\r
285 \r
286                                                         if (o && o.func)\r
287                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
288                                                 }\r
289 \r
290                                                 // Detach unload function\r
291                                                 if (win.detachEvent) {\r
292                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
293                                                         win.detachEvent('onunload', unload);\r
294                                                 } else if (win.removeEventListener)\r
295                                                         win.removeEventListener('unload', unload, false);\r
296 \r
297                                                 // Destroy references\r
298                                                 t.unloads = o = li = w = unload = 0;\r
299 \r
300                                                 // Run garbarge collector on IE\r
301                                                 if (win.CollectGarbage)\r
302                                                         CollectGarbage();\r
303                                         }\r
304                                 };\r
305 \r
306                                 function fakeUnload() {\r
307                                         var d = document;\r
308 \r
309                                         // Is there things still loading, then do some magic\r
310                                         if (d.readyState == 'interactive') {\r
311                                                 function stop() {\r
312                                                         // Prevent memory leak\r
313                                                         d.detachEvent('onstop', stop);\r
314 \r
315                                                         // Call unload handler\r
316                                                         if (unload)\r
317                                                                 unload();\r
318 \r
319                                                         d = 0;\r
320                                                 };\r
321 \r
322                                                 // Fire unload when the currently loading page is stopped\r
323                                                 if (d)\r
324                                                         d.attachEvent('onstop', stop);\r
325 \r
326                                                 // Remove onstop listener after a while to prevent the unload function\r
327                                                 // to execute if the user presses cancel in an onbeforeunload\r
328                                                 // confirm dialog and then presses the browser stop button\r
329                                                 win.setTimeout(function() {\r
330                                                         if (d)\r
331                                                                 d.detachEvent('onstop', stop);\r
332                                                 }, 0);\r
333                                         }\r
334                                 };\r
335 \r
336                                 // Attach unload handler\r
337                                 if (win.attachEvent) {\r
338                                         win.attachEvent('onunload', unload);\r
339                                         win.attachEvent('onbeforeunload', fakeUnload);\r
340                                 } else if (win.addEventListener)\r
341                                         win.addEventListener('unload', unload, false);\r
342 \r
343                                 // Setup initial unload handler array\r
344                                 t.unloads = [f];\r
345                         } else\r
346                                 t.unloads.push(f);\r
347 \r
348                         return f;\r
349                 },\r
350 \r
351                 removeUnload : function(f) {\r
352                         var u = this.unloads, r = null;\r
353 \r
354                         tinymce.each(u, function(o, i) {\r
355                                 if (o && o.func == f) {\r
356                                         u.splice(i, 1);\r
357                                         r = f;\r
358                                         return false;\r
359                                 }\r
360                         });\r
361 \r
362                         return r;\r
363                 },\r
364 \r
365                 explode : function(s, d) {\r
366                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
367                 },\r
368 \r
369                 _addVer : function(u) {\r
370                         var v;\r
371 \r
372                         if (!this.query)\r
373                                 return u;\r
374 \r
375                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
376 \r
377                         if (u.indexOf('#') == -1)\r
378                                 return u + v;\r
379 \r
380                         return u.replace('#', v + '#');\r
381                 }\r
382 \r
383                 };\r
384 \r
385         // Initialize the API\r
386         tinymce._init();\r
387 \r
388         // Expose tinymce namespace to the global namespace (window)\r
389         win.tinymce = win.tinyMCE = tinymce;\r
390 })(window);\r
391 \r
392 (function($, tinymce) {\r
393         var is = tinymce.is, attrRegExp = /^(href|src|style)$/i, undefined;\r
394 \r
395         // jQuery is undefined\r
396         if (!$)\r
397                 return alert("Load jQuery first!");\r
398 \r
399         // Stick jQuery into the tinymce namespace\r
400         tinymce.$ = $;\r
401 \r
402         // Setup adapter\r
403         tinymce.adapter = {\r
404                 patchEditor : function(editor) {\r
405                         var fn = $.fn;\r
406 \r
407                         // Adapt the css function to make sure that the _mce_style\r
408                         // attribute gets updated with the new style information\r
409                         function css(name, value) {\r
410                                 var self = this;\r
411 \r
412                                 // Remove _mce_style when set operation occurs\r
413                                 if (value)\r
414                                         self.removeAttr('_mce_style');\r
415 \r
416                                 return fn.css.apply(self, arguments);\r
417                         };\r
418 \r
419                         // Apapt the attr function to make sure that it uses the _mce_ prefixed variants\r
420                         function attr(name, value) {\r
421                                 var self = this;\r
422 \r
423                                 // Update/retrive _mce_ attribute variants\r
424                                 if (attrRegExp.test(name)) {\r
425                                         if (value !== undefined) {\r
426                                                 // Use TinyMCE behavior when setting the specifc attributes\r
427                                                 self.each(function(i, node) {\r
428                                                         editor.dom.setAttrib(node, name, value);\r
429                                                 });\r
430 \r
431                                                 return self;\r
432                                         } else\r
433                                                 return self.attr('_mce_' + name);\r
434                                 }\r
435 \r
436                                 // Default behavior\r
437                                 return fn.attr.apply(self, arguments);\r
438                         };\r
439 \r
440                         function htmlPatchFunc(func) {\r
441                                 // Returns a modified function that processes\r
442                                 // the HTML before executing the action this makes sure\r
443                                 // that href/src etc gets moved into the _mce_ variants\r
444                                 return function(content) {\r
445                                         if (content)\r
446                                                 content = editor.dom.processHTML(content);\r
447 \r
448                                         return func.call(this, content);\r
449                                 };\r
450                         };\r
451 \r
452                         // Patch various jQuery functions to handle tinymce specific attribute and content behavior\r
453                         // we don't patch the jQuery.fn directly since it will most likely break compatibility\r
454                         // with other jQuery logic on the page. Only instances created by TinyMCE should be patched.\r
455                         function patch(jq) {\r
456                                 // Patch some functions, only patch the object once\r
457                                 if (jq.css !== css) {\r
458                                         // Patch css/attr to use the _mce_ prefixed attribute variants\r
459                                         jq.css = css;\r
460                                         jq.attr = attr;\r
461 \r
462                                         // Patch HTML functions to use the DOMUtils.processHTML filter logic\r
463                                         jq.html = htmlPatchFunc(fn.html);\r
464                                         jq.append = htmlPatchFunc(fn.append);\r
465                                         jq.prepend = htmlPatchFunc(fn.prepend);\r
466                                         jq.after = htmlPatchFunc(fn.after);\r
467                                         jq.before = htmlPatchFunc(fn.before);\r
468                                         jq.replaceWith = htmlPatchFunc(fn.replaceWith);\r
469                                         jq.tinymce = editor;\r
470 \r
471                                         // Each pushed jQuery instance needs to be patched\r
472                                         // as well for example when traversing the DOM\r
473                                         jq.pushStack = function() {\r
474                                                 return patch(fn.pushStack.apply(this, arguments));\r
475                                         };\r
476                                 }\r
477 \r
478                                 return jq;\r
479                         };\r
480 \r
481                         // Add a $ function on each editor instance this one is scoped for the editor document object\r
482                         // this way you can do chaining like this tinymce.get(0).$('p').append('text').css('color', 'red');\r
483                         editor.$ = function(selector, scope) {\r
484                                 var doc = editor.getDoc();\r
485 \r
486                                 return patch($(selector || doc, doc || scope));\r
487                         };\r
488                 }\r
489         };\r
490 \r
491         // Patch in core NS functions\r
492         tinymce.extend = $.extend;\r
493         tinymce.extend(tinymce, {\r
494                 map : $.map,\r
495                 grep : function(a, f) {return $.grep(a, f || function(){return 1;});},\r
496                 inArray : function(a, v) {return $.inArray(v, a || []);}\r
497 \r
498                 /* Didn't iterate stylesheets\r
499                 each : function(o, cb, s) {\r
500                         if (!o)\r
501                                 return 0;\r
502 \r
503                         var r = 1;\r
504 \r
505                         $.each(o, function(nr, el){\r
506                                 if (cb.call(s, el, nr, o) === false) {\r
507                                         r = 0;\r
508                                         return false;\r
509                                 }\r
510                         });\r
511 \r
512                         return r;\r
513                 }*/\r
514         });\r
515 \r
516         // Patch in functions in various clases\r
517         // Add a "#ifndefjquery" statement around each core API function you add below\r
518         var patches = {\r
519                 'tinymce.dom.DOMUtils' : {\r
520                         /*\r
521                         addClass : function(e, c) {\r
522                                 if (is(e, 'array') && is(e[0], 'string'))\r
523                                         e = e.join(',#');\r
524                                 return (e && $(is(e, 'string') ? '#' + e : e)\r
525                                         .addClass(c)\r
526                                         .attr('class')) || false;\r
527                         },\r
528 \r
529                         hasClass : function(n, c) {\r
530                                 return $(is(n, 'string') ? '#' + n : n).hasClass(c);\r
531                         },\r
532 \r
533                         removeClass : function(e, c) {\r
534                                 if (!e)\r
535                                         return false;\r
536 \r
537                                 var r = [];\r
538 \r
539                                 $(is(e, 'string') ? '#' + e : e)\r
540                                         .removeClass(c)\r
541                                         .each(function(){\r
542                                                 r.push(this.className);\r
543                                         });\r
544 \r
545                                 return r.length == 1 ? r[0] : r;\r
546                         },\r
547                         */\r
548 \r
549                         select : function(pattern, scope) {\r
550                                 var t = this;\r
551 \r
552                                 return $.find(pattern, t.get(scope) || t.get(t.settings.root_element) || t.doc, []);\r
553                         },\r
554 \r
555                         is : function(n, patt) {\r
556                                 return $(this.get(n)).is(patt);\r
557                         }\r
558 \r
559                         /*\r
560                         show : function(e) {\r
561                                 if (is(e, 'array') && is(e[0], 'string'))\r
562                                         e = e.join(',#');\r
563 \r
564                                 $(is(e, 'string') ? '#' + e : e).css('display', 'block');\r
565                         },\r
566 \r
567                         hide : function(e) {\r
568                                 if (is(e, 'array') && is(e[0], 'string'))\r
569                                         e = e.join(',#');\r
570 \r
571                                 $(is(e, 'string') ? '#' + e : e).css('display', 'none');\r
572                         },\r
573 \r
574                         isHidden : function(e) {\r
575                                 return $(is(e, 'string') ? '#' + e : e).is(':hidden');\r
576                         },\r
577 \r
578                         insertAfter : function(n, e) {\r
579                                 return $(is(e, 'string') ? '#' + e : e).after(n);\r
580                         },\r
581 \r
582                         replace : function(o, n, k) {\r
583                                 n = $(is(n, 'string') ? '#' + n : n);\r
584 \r
585                                 if (k)\r
586                                         n.children().appendTo(o);\r
587 \r
588                                 n.replaceWith(o);\r
589                         },\r
590 \r
591                         setStyle : function(n, na, v) {\r
592                                 if (is(n, 'array') && is(n[0], 'string'))\r
593                                         n = n.join(',#');\r
594 \r
595                                 $(is(n, 'string') ? '#' + n : n).css(na, v);\r
596                         },\r
597 \r
598                         getStyle : function(n, na, c) {\r
599                                 return $(is(n, 'string') ? '#' + n : n).css(na);\r
600                         },\r
601 \r
602                         setStyles : function(e, o) {\r
603                                 if (is(e, 'array') && is(e[0], 'string'))\r
604                                         e = e.join(',#');\r
605                                 $(is(e, 'string') ? '#' + e : e).css(o);\r
606                         },\r
607 \r
608                         setAttrib : function(e, n, v) {\r
609                                 var t = this, s = t.settings;\r
610 \r
611                                 if (is(e, 'array') && is(e[0], 'string'))\r
612                                         e = e.join(',#');\r
613 \r
614                                 e = $(is(e, 'string') ? '#' + e : e);\r
615 \r
616                                 switch (n) {\r
617                                         case "style":\r
618                                                 e.each(function(i, v){\r
619                                                         if (s.keep_values)\r
620                                                                 $(v).attr('_mce_style', v);\r
621 \r
622                                                         v.style.cssText = v;\r
623                                                 });\r
624                                                 break;\r
625 \r
626                                         case "class":\r
627                                                 e.each(function(){\r
628                                                         this.className = v;\r
629                                                 });\r
630                                                 break;\r
631 \r
632                                         case "src":\r
633                                         case "href":\r
634                                                 e.each(function(i, v){\r
635                                                         if (s.keep_values) {\r
636                                                                 if (s.url_converter)\r
637                                                                         v = s.url_converter.call(s.url_converter_scope || t, v, n, v);\r
638 \r
639                                                                 t.setAttrib(v, '_mce_' + n, v);\r
640                                                         }\r
641                                                 });\r
642 \r
643                                                 break;\r
644                                 }\r
645 \r
646                                 if (v !== null && v.length !== 0)\r
647                                         e.attr(n, '' + v);\r
648                                 else\r
649                                         e.removeAttr(n);\r
650                         },\r
651 \r
652                         setAttribs : function(e, o) {\r
653                                 var t = this;\r
654 \r
655                                 $.each(o, function(n, v){\r
656                                         t.setAttrib(e,n,v);\r
657                                 });\r
658                         }\r
659                         */\r
660                 }\r
661 \r
662 /*\r
663                 'tinymce.dom.Event' : {\r
664                         add : function (o, n, f, s) {\r
665                                 var lo, cb;\r
666 \r
667                                 cb = function(e) {\r
668                                         e.target = e.target || this;\r
669                                         f.call(s || this, e);\r
670                                 };\r
671 \r
672                                 if (is(o, 'array') && is(o[0], 'string'))\r
673                                         o = o.join(',#');\r
674                                 o = $(is(o, 'string') ? '#' + o : o);\r
675                                 if (n == 'init') {\r
676                                         o.ready(cb, s);\r
677                                 } else {\r
678                                         if (s) {\r
679                                                 o.bind(n, s, cb);\r
680                                         } else {\r
681                                                 o.bind(n, cb);\r
682                                         }\r
683                                 }\r
684 \r
685                                 lo = this._jqLookup || (this._jqLookup = []);\r
686                                 lo.push({func : f, cfunc : cb});\r
687 \r
688                                 return cb;\r
689                         },\r
690 \r
691                         remove : function(o, n, f) {\r
692                                 // Find cfunc\r
693                                 $(this._jqLookup).each(function() {\r
694                                         if (this.func === f)\r
695                                                 f = this.cfunc;\r
696                                 });\r
697 \r
698                                 if (is(o, 'array') && is(o[0], 'string'))\r
699                                         o = o.join(',#');\r
700 \r
701                                 $(is(o, 'string') ? '#' + o : o).unbind(n,f);\r
702 \r
703                                 return true;\r
704                         }\r
705                 }\r
706 */\r
707         };\r
708 \r
709         // Patch functions after a class is created\r
710         tinymce.onCreate = function(ty, c, p) {\r
711                 tinymce.extend(p, patches[c]);\r
712         };\r
713 })(window.jQuery, tinymce);\r
714 \r
715 \r
716 \r
717 tinymce.create('tinymce.util.Dispatcher', {\r
718         scope : null,\r
719         listeners : null,\r
720 \r
721         Dispatcher : function(s) {\r
722                 this.scope = s || this;\r
723                 this.listeners = [];\r
724         },\r
725 \r
726         add : function(cb, s) {\r
727                 this.listeners.push({cb : cb, scope : s || this.scope});\r
728 \r
729                 return cb;\r
730         },\r
731 \r
732         addToTop : function(cb, s) {\r
733                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
734 \r
735                 return cb;\r
736         },\r
737 \r
738         remove : function(cb) {\r
739                 var l = this.listeners, o = null;\r
740 \r
741                 tinymce.each(l, function(c, i) {\r
742                         if (cb == c.cb) {\r
743                                 o = cb;\r
744                                 l.splice(i, 1);\r
745                                 return false;\r
746                         }\r
747                 });\r
748 \r
749                 return o;\r
750         },\r
751 \r
752         dispatch : function() {\r
753                 var s, a = arguments, i, li = this.listeners, c;\r
754 \r
755                 // Needs to be a real loop since the listener count might change while looping\r
756                 // And this is also more efficient\r
757                 for (i = 0; i<li.length; i++) {\r
758                         c = li[i];\r
759                         s = c.cb.apply(c.scope, a);\r
760 \r
761                         if (s === false)\r
762                                 break;\r
763                 }\r
764 \r
765                 return s;\r
766         }\r
767 \r
768         });\r
769 \r
770 (function() {\r
771         var each = tinymce.each;\r
772 \r
773         tinymce.create('tinymce.util.URI', {\r
774                 URI : function(u, s) {\r
775                         var t = this, o, a, b;\r
776 \r
777                         // Trim whitespace\r
778                         u = tinymce.trim(u);\r
779 \r
780                         // Default settings\r
781                         s = t.settings = s || {};\r
782 \r
783                         // Strange app protocol or local anchor\r
784                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
785                                 t.source = u;\r
786                                 return;\r
787                         }\r
788 \r
789                         // Absolute path with no host, fake host and protocol\r
790                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
791                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
792 \r
793                         // Relative path http:// or protocol relative //path\r
794                         if (!/^\w*:?\/\//.test(u))\r
795                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
796 \r
797                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
798                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
799                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
800                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
801                                 var s = u[i];\r
802 \r
803                                 // Zope 3 workaround, they use @@something\r
804                                 if (s)\r
805                                         s = s.replace(/\(mce_at\)/g, '@@');\r
806 \r
807                                 t[v] = s;\r
808                         });\r
809 \r
810                         if (b = s.base_uri) {\r
811                                 if (!t.protocol)\r
812                                         t.protocol = b.protocol;\r
813 \r
814                                 if (!t.userInfo)\r
815                                         t.userInfo = b.userInfo;\r
816 \r
817                                 if (!t.port && t.host == 'mce_host')\r
818                                         t.port = b.port;\r
819 \r
820                                 if (!t.host || t.host == 'mce_host')\r
821                                         t.host = b.host;\r
822 \r
823                                 t.source = '';\r
824                         }\r
825 \r
826                         //t.path = t.path || '/';\r
827                 },\r
828 \r
829                 setPath : function(p) {\r
830                         var t = this;\r
831 \r
832                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
833 \r
834                         // Update path parts\r
835                         t.path = p[0];\r
836                         t.directory = p[1];\r
837                         t.file = p[2];\r
838 \r
839                         // Rebuild source\r
840                         t.source = '';\r
841                         t.getURI();\r
842                 },\r
843 \r
844                 toRelative : function(u) {\r
845                         var t = this, o;\r
846 \r
847                         if (u === "./")\r
848                                 return u;\r
849 \r
850                         u = new tinymce.util.URI(u, {base_uri : t});\r
851 \r
852                         // Not on same domain/port or protocol\r
853                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
854                                 return u.getURI();\r
855 \r
856                         o = t.toRelPath(t.path, u.path);\r
857 \r
858                         // Add query\r
859                         if (u.query)\r
860                                 o += '?' + u.query;\r
861 \r
862                         // Add anchor\r
863                         if (u.anchor)\r
864                                 o += '#' + u.anchor;\r
865 \r
866                         return o;\r
867                 },\r
868         \r
869                 toAbsolute : function(u, nh) {\r
870                         var u = new tinymce.util.URI(u, {base_uri : this});\r
871 \r
872                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
873                 },\r
874 \r
875                 toRelPath : function(base, path) {\r
876                         var items, bp = 0, out = '', i, l;\r
877 \r
878                         // Split the paths\r
879                         base = base.substring(0, base.lastIndexOf('/'));\r
880                         base = base.split('/');\r
881                         items = path.split('/');\r
882 \r
883                         if (base.length >= items.length) {\r
884                                 for (i = 0, l = base.length; i < l; i++) {\r
885                                         if (i >= items.length || base[i] != items[i]) {\r
886                                                 bp = i + 1;\r
887                                                 break;\r
888                                         }\r
889                                 }\r
890                         }\r
891 \r
892                         if (base.length < items.length) {\r
893                                 for (i = 0, l = items.length; i < l; i++) {\r
894                                         if (i >= base.length || base[i] != items[i]) {\r
895                                                 bp = i + 1;\r
896                                                 break;\r
897                                         }\r
898                                 }\r
899                         }\r
900 \r
901                         if (bp == 1)\r
902                                 return path;\r
903 \r
904                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
905                                 out += "../";\r
906 \r
907                         for (i = bp - 1, l = items.length; i < l; i++) {\r
908                                 if (i != bp - 1)\r
909                                         out += "/" + items[i];\r
910                                 else\r
911                                         out += items[i];\r
912                         }\r
913 \r
914                         return out;\r
915                 },\r
916 \r
917                 toAbsPath : function(base, path) {\r
918                         var i, nb = 0, o = [], tr, outPath;\r
919 \r
920                         // Split paths\r
921                         tr = /\/$/.test(path) ? '/' : '';\r
922                         base = base.split('/');\r
923                         path = path.split('/');\r
924 \r
925                         // Remove empty chunks\r
926                         each(base, function(k) {\r
927                                 if (k)\r
928                                         o.push(k);\r
929                         });\r
930 \r
931                         base = o;\r
932 \r
933                         // Merge relURLParts chunks\r
934                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
935                                 // Ignore empty or .\r
936                                 if (path[i].length == 0 || path[i] == ".")\r
937                                         continue;\r
938 \r
939                                 // Is parent\r
940                                 if (path[i] == '..') {\r
941                                         nb++;\r
942                                         continue;\r
943                                 }\r
944 \r
945                                 // Move up\r
946                                 if (nb > 0) {\r
947                                         nb--;\r
948                                         continue;\r
949                                 }\r
950 \r
951                                 o.push(path[i]);\r
952                         }\r
953 \r
954                         i = base.length - nb;\r
955 \r
956                         // If /a/b/c or /\r
957                         if (i <= 0)\r
958                                 outPath = o.reverse().join('/');\r
959                         else\r
960                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
961 \r
962                         // Add front / if it's needed\r
963                         if (outPath.indexOf('/') !== 0)\r
964                                 outPath = '/' + outPath;\r
965 \r
966                         // Add traling / if it's needed\r
967                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
968                                 outPath += tr;\r
969 \r
970                         return outPath;\r
971                 },\r
972 \r
973                 getURI : function(nh) {\r
974                         var s, t = this;\r
975 \r
976                         // Rebuild source\r
977                         if (!t.source || nh) {\r
978                                 s = '';\r
979 \r
980                                 if (!nh) {\r
981                                         if (t.protocol)\r
982                                                 s += t.protocol + '://';\r
983 \r
984                                         if (t.userInfo)\r
985                                                 s += t.userInfo + '@';\r
986 \r
987                                         if (t.host)\r
988                                                 s += t.host;\r
989 \r
990                                         if (t.port)\r
991                                                 s += ':' + t.port;\r
992                                 }\r
993 \r
994                                 if (t.path)\r
995                                         s += t.path;\r
996 \r
997                                 if (t.query)\r
998                                         s += '?' + t.query;\r
999 \r
1000                                 if (t.anchor)\r
1001                                         s += '#' + t.anchor;\r
1002 \r
1003                                 t.source = s;\r
1004                         }\r
1005 \r
1006                         return t.source;\r
1007                 }\r
1008         });\r
1009 })();\r
1010 \r
1011 (function() {\r
1012         var each = tinymce.each;\r
1013 \r
1014         tinymce.create('static tinymce.util.Cookie', {\r
1015                 getHash : function(n) {\r
1016                         var v = this.get(n), h;\r
1017 \r
1018                         if (v) {\r
1019                                 each(v.split('&'), function(v) {\r
1020                                         v = v.split('=');\r
1021                                         h = h || {};\r
1022                                         h[unescape(v[0])] = unescape(v[1]);\r
1023                                 });\r
1024                         }\r
1025 \r
1026                         return h;\r
1027                 },\r
1028 \r
1029                 setHash : function(n, v, e, p, d, s) {\r
1030                         var o = '';\r
1031 \r
1032                         each(v, function(v, k) {\r
1033                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
1034                         });\r
1035 \r
1036                         this.set(n, o, e, p, d, s);\r
1037                 },\r
1038 \r
1039                 get : function(n) {\r
1040                         var c = document.cookie, e, p = n + "=", b;\r
1041 \r
1042                         // Strict mode\r
1043                         if (!c)\r
1044                                 return;\r
1045 \r
1046                         b = c.indexOf("; " + p);\r
1047 \r
1048                         if (b == -1) {\r
1049                                 b = c.indexOf(p);\r
1050 \r
1051                                 if (b != 0)\r
1052                                         return null;\r
1053                         } else\r
1054                                 b += 2;\r
1055 \r
1056                         e = c.indexOf(";", b);\r
1057 \r
1058                         if (e == -1)\r
1059                                 e = c.length;\r
1060 \r
1061                         return unescape(c.substring(b + p.length, e));\r
1062                 },\r
1063 \r
1064                 set : function(n, v, e, p, d, s) {\r
1065                         document.cookie = n + "=" + escape(v) +\r
1066                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
1067                                 ((p) ? "; path=" + escape(p) : "") +\r
1068                                 ((d) ? "; domain=" + d : "") +\r
1069                                 ((s) ? "; secure" : "");\r
1070                 },\r
1071 \r
1072                 remove : function(n, p) {\r
1073                         var d = new Date();\r
1074 \r
1075                         d.setTime(d.getTime() - 1000);\r
1076 \r
1077                         this.set(n, '', d, p, d);\r
1078                 }\r
1079         });\r
1080 })();\r
1081 \r
1082 tinymce.create('static tinymce.util.JSON', {\r
1083         serialize : function(o) {\r
1084                 var i, v, s = tinymce.util.JSON.serialize, t;\r
1085 \r
1086                 if (o == null)\r
1087                         return 'null';\r
1088 \r
1089                 t = typeof o;\r
1090 \r
1091                 if (t == 'string') {\r
1092                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
1093 \r
1094                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
1095                                 i = v.indexOf(b);\r
1096 \r
1097                                 if (i + 1)\r
1098                                         return '\\' + v.charAt(i + 1);\r
1099 \r
1100                                 a = b.charCodeAt().toString(16);\r
1101 \r
1102                                 return '\\u' + '0000'.substring(a.length) + a;\r
1103                         }) + '"';\r
1104                 }\r
1105 \r
1106                 if (t == 'object') {\r
1107                         if (o.hasOwnProperty && o instanceof Array) {\r
1108                                         for (i=0, v = '['; i<o.length; i++)\r
1109                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
1110 \r
1111                                         return v + ']';\r
1112                                 }\r
1113 \r
1114                                 v = '{';\r
1115 \r
1116                                 for (i in o)\r
1117                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
1118 \r
1119                                 return v + '}';\r
1120                 }\r
1121 \r
1122                 return '' + o;\r
1123         },\r
1124 \r
1125         parse : function(s) {\r
1126                 try {\r
1127                         return eval('(' + s + ')');\r
1128                 } catch (ex) {\r
1129                         // Ignore\r
1130                 }\r
1131         }\r
1132 \r
1133         });\r
1134 \r
1135 tinymce.create('static tinymce.util.XHR', {\r
1136         send : function(o) {\r
1137                 var x, t, w = window, c = 0;\r
1138 \r
1139                 // Default settings\r
1140                 o.scope = o.scope || this;\r
1141                 o.success_scope = o.success_scope || o.scope;\r
1142                 o.error_scope = o.error_scope || o.scope;\r
1143                 o.async = o.async === false ? false : true;\r
1144                 o.data = o.data || '';\r
1145 \r
1146                 function get(s) {\r
1147                         x = 0;\r
1148 \r
1149                         try {\r
1150                                 x = new ActiveXObject(s);\r
1151                         } catch (ex) {\r
1152                         }\r
1153 \r
1154                         return x;\r
1155                 };\r
1156 \r
1157                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
1158 \r
1159                 if (x) {\r
1160                         if (x.overrideMimeType)\r
1161                                 x.overrideMimeType(o.content_type);\r
1162 \r
1163                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
1164 \r
1165                         if (o.content_type)\r
1166                                 x.setRequestHeader('Content-Type', o.content_type);\r
1167 \r
1168                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
1169 \r
1170                         x.send(o.data);\r
1171 \r
1172                         function ready() {\r
1173                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
1174                                         if (o.success && c < 10000 && x.status == 200)\r
1175                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
1176                                         else if (o.error)\r
1177                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
1178 \r
1179                                         x = null;\r
1180                                 } else\r
1181                                         w.setTimeout(ready, 10);\r
1182                         };\r
1183 \r
1184                         // Syncronous request\r
1185                         if (!o.async)\r
1186                                 return ready();\r
1187 \r
1188                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
1189                         t = w.setTimeout(ready, 10);\r
1190                 }\r
1191         }\r
1192 });\r
1193 \r
1194 (function() {\r
1195         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
1196 \r
1197         tinymce.create('tinymce.util.JSONRequest', {\r
1198                 JSONRequest : function(s) {\r
1199                         this.settings = extend({\r
1200                         }, s);\r
1201                         this.count = 0;\r
1202                 },\r
1203 \r
1204                 send : function(o) {\r
1205                         var ecb = o.error, scb = o.success;\r
1206 \r
1207                         o = extend(this.settings, o);\r
1208 \r
1209                         o.success = function(c, x) {\r
1210                                 c = JSON.parse(c);\r
1211 \r
1212                                 if (typeof(c) == 'undefined') {\r
1213                                         c = {\r
1214                                                 error : 'JSON Parse error.'\r
1215                                         };\r
1216                                 }\r
1217 \r
1218                                 if (c.error)\r
1219                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
1220                                 else\r
1221                                         scb.call(o.success_scope || o.scope, c.result);\r
1222                         };\r
1223 \r
1224                         o.error = function(ty, x) {\r
1225                                 ecb.call(o.error_scope || o.scope, ty, x);\r
1226                         };\r
1227 \r
1228                         o.data = JSON.serialize({\r
1229                                 id : o.id || 'c' + (this.count++),\r
1230                                 method : o.method,\r
1231                                 params : o.params\r
1232                         });\r
1233 \r
1234                         // JSON content type for Ruby on rails. Bug: #1883287\r
1235                         o.content_type = 'application/json';\r
1236 \r
1237                         XHR.send(o);\r
1238                 },\r
1239 \r
1240                 'static' : {\r
1241                         sendRPC : function(o) {\r
1242                                 return new tinymce.util.JSONRequest().send(o);\r
1243                         }\r
1244                 }\r
1245         });\r
1246 }());\r
1247 (function(tinymce) {\r
1248         // Shorten names\r
1249         var each = tinymce.each,\r
1250                 is = tinymce.is,\r
1251                 isWebKit = tinymce.isWebKit,\r
1252                 isIE = tinymce.isIE,\r
1253                 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
1254                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
1255                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
1256                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
1257                 encodeCharsRe = /[<>&\"]/g,\r
1258                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
1259                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
1260                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
1261 \r
1262         function makeMap(str) {\r
1263                 var map = {}, i;\r
1264 \r
1265                 str = str.split(',');\r
1266                 for (i = str.length; i >= 0; i--)\r
1267                         map[str[i]] = 1;\r
1268 \r
1269                 return map;\r
1270         };\r
1271 \r
1272         tinymce.create('tinymce.dom.DOMUtils', {\r
1273                 doc : null,\r
1274                 root : null,\r
1275                 files : null,\r
1276                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1277                 props : {\r
1278                         "for" : "htmlFor",\r
1279                         "class" : "className",\r
1280                         className : "className",\r
1281                         checked : "checked",\r
1282                         disabled : "disabled",\r
1283                         maxlength : "maxLength",\r
1284                         readonly : "readOnly",\r
1285                         selected : "selected",\r
1286                         value : "value",\r
1287                         id : "id",\r
1288                         name : "name",\r
1289                         type : "type"\r
1290                 },\r
1291 \r
1292                 DOMUtils : function(d, s) {\r
1293                         var t = this, globalStyle;\r
1294 \r
1295                         t.doc = d;\r
1296                         t.win = window;\r
1297                         t.files = {};\r
1298                         t.cssFlicker = false;\r
1299                         t.counter = 0;\r
1300                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
1301                         t.stdMode = d.documentMode === 8;\r
1302 \r
1303                         t.settings = s = tinymce.extend({\r
1304                                 keep_values : false,\r
1305                                 hex_colors : 1,\r
1306                                 process_html : 1\r
1307                         }, s);\r
1308 \r
1309                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1310                         if (tinymce.isIE6) {\r
1311                                 try {\r
1312                                         d.execCommand('BackgroundImageCache', false, true);\r
1313                                 } catch (e) {\r
1314                                         t.cssFlicker = true;\r
1315                                 }\r
1316                         }\r
1317 \r
1318                         // Build styles list\r
1319                         if (s.valid_styles) {\r
1320                                 t._styles = {};\r
1321 \r
1322                                 // Convert styles into a rule list\r
1323                                 each(s.valid_styles, function(value, key) {\r
1324                                         t._styles[key] = tinymce.explode(value);\r
1325                                 });\r
1326                         }\r
1327 \r
1328                         tinymce.addUnload(t.destroy, t);\r
1329                 },\r
1330 \r
1331                 getRoot : function() {\r
1332                         var t = this, s = t.settings;\r
1333 \r
1334                         return (s && t.get(s.root_element)) || t.doc.body;\r
1335                 },\r
1336 \r
1337                 getViewPort : function(w) {\r
1338                         var d, b;\r
1339 \r
1340                         w = !w ? this.win : w;\r
1341                         d = w.document;\r
1342                         b = this.boxModel ? d.documentElement : d.body;\r
1343 \r
1344                         // Returns viewport size excluding scrollbars\r
1345                         return {\r
1346                                 x : w.pageXOffset || b.scrollLeft,\r
1347                                 y : w.pageYOffset || b.scrollTop,\r
1348                                 w : w.innerWidth || b.clientWidth,\r
1349                                 h : w.innerHeight || b.clientHeight\r
1350                         };\r
1351                 },\r
1352 \r
1353                 getRect : function(e) {\r
1354                         var p, t = this, sr;\r
1355 \r
1356                         e = t.get(e);\r
1357                         p = t.getPos(e);\r
1358                         sr = t.getSize(e);\r
1359 \r
1360                         return {\r
1361                                 x : p.x,\r
1362                                 y : p.y,\r
1363                                 w : sr.w,\r
1364                                 h : sr.h\r
1365                         };\r
1366                 },\r
1367 \r
1368                 getSize : function(e) {\r
1369                         var t = this, w, h;\r
1370 \r
1371                         e = t.get(e);\r
1372                         w = t.getStyle(e, 'width');\r
1373                         h = t.getStyle(e, 'height');\r
1374 \r
1375                         // Non pixel value, then force offset/clientWidth\r
1376                         if (w.indexOf('px') === -1)\r
1377                                 w = 0;\r
1378 \r
1379                         // Non pixel value, then force offset/clientWidth\r
1380                         if (h.indexOf('px') === -1)\r
1381                                 h = 0;\r
1382 \r
1383                         return {\r
1384                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1385                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1386                         };\r
1387                 },\r
1388 \r
1389                 getParent : function(n, f, r) {\r
1390                         return this.getParents(n, f, r, false);\r
1391                 },\r
1392 \r
1393                 getParents : function(n, f, r, c) {\r
1394                         var t = this, na, se = t.settings, o = [];\r
1395 \r
1396                         n = t.get(n);\r
1397                         c = c === undefined;\r
1398 \r
1399                         if (se.strict_root)\r
1400                                 r = r || t.getRoot();\r
1401 \r
1402                         // Wrap node name as func\r
1403                         if (is(f, 'string')) {\r
1404                                 na = f;\r
1405 \r
1406                                 if (f === '*') {\r
1407                                         f = function(n) {return n.nodeType == 1;};\r
1408                                 } else {\r
1409                                         f = function(n) {\r
1410                                                 return t.is(n, na);\r
1411                                         };\r
1412                                 }\r
1413                         }\r
1414 \r
1415                         while (n) {\r
1416                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1417                                         break;\r
1418 \r
1419                                 if (!f || f(n)) {\r
1420                                         if (c)\r
1421                                                 o.push(n);\r
1422                                         else\r
1423                                                 return n;\r
1424                                 }\r
1425 \r
1426                                 n = n.parentNode;\r
1427                         }\r
1428 \r
1429                         return c ? o : null;\r
1430                 },\r
1431 \r
1432                 get : function(e) {\r
1433                         var n;\r
1434 \r
1435                         if (e && this.doc && typeof(e) == 'string') {\r
1436                                 n = e;\r
1437                                 e = this.doc.getElementById(e);\r
1438 \r
1439                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1440                                 if (e && e.id !== n)\r
1441                                         return this.doc.getElementsByName(n)[1];\r
1442                         }\r
1443 \r
1444                         return e;\r
1445                 },\r
1446 \r
1447                 getNext : function(node, selector) {\r
1448                         return this._findSib(node, selector, 'nextSibling');\r
1449                 },\r
1450 \r
1451                 getPrev : function(node, selector) {\r
1452                         return this._findSib(node, selector, 'previousSibling');\r
1453                 },\r
1454 \r
1455 \r
1456                 add : function(p, n, a, h, c) {\r
1457                         var t = this;\r
1458 \r
1459                         return this.run(p, function(p) {\r
1460                                 var e, k;\r
1461 \r
1462                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1463                                 t.setAttribs(e, a);\r
1464 \r
1465                                 if (h) {\r
1466                                         if (h.nodeType)\r
1467                                                 e.appendChild(h);\r
1468                                         else\r
1469                                                 t.setHTML(e, h);\r
1470                                 }\r
1471 \r
1472                                 return !c ? p.appendChild(e) : e;\r
1473                         });\r
1474                 },\r
1475 \r
1476                 create : function(n, a, h) {\r
1477                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1478                 },\r
1479 \r
1480                 createHTML : function(n, a, h) {\r
1481                         var o = '', t = this, k;\r
1482 \r
1483                         o += '<' + n;\r
1484 \r
1485                         for (k in a) {\r
1486                                 if (a.hasOwnProperty(k))\r
1487                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1488                         }\r
1489 \r
1490                         if (tinymce.is(h))\r
1491                                 return o + '>' + h + '</' + n + '>';\r
1492 \r
1493                         return o + ' />';\r
1494                 },\r
1495 \r
1496                 remove : function(node, keep_children) {\r
1497                         return this.run(node, function(node) {\r
1498                                 var parent, child;\r
1499 \r
1500                                 parent = node.parentNode;\r
1501 \r
1502                                 if (!parent)\r
1503                                         return null;\r
1504 \r
1505                                 if (keep_children) {\r
1506                                         while (child = node.firstChild) {\r
1507                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
1508                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
1509                                                         parent.insertBefore(child, node);\r
1510                                                 else\r
1511                                                         node.removeChild(child);\r
1512                                         }\r
1513                                 }\r
1514 \r
1515                                 return parent.removeChild(node);\r
1516                         });\r
1517                 },\r
1518 \r
1519                 setStyle : function(n, na, v) {\r
1520                         var t = this;\r
1521 \r
1522                         return t.run(n, function(e) {\r
1523                                 var s, i;\r
1524 \r
1525                                 s = e.style;\r
1526 \r
1527                                 // Camelcase it, if needed\r
1528                                 na = na.replace(/-(\D)/g, function(a, b){\r
1529                                         return b.toUpperCase();\r
1530                                 });\r
1531 \r
1532                                 // Default px suffix on these\r
1533                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1534                                         v += 'px';\r
1535 \r
1536                                 switch (na) {\r
1537                                         case 'opacity':\r
1538                                                 // IE specific opacity\r
1539                                                 if (isIE) {\r
1540                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1541 \r
1542                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1543                                                                 s.display = 'inline-block';\r
1544                                                 }\r
1545 \r
1546                                                 // Fix for older browsers\r
1547                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1548                                                 break;\r
1549 \r
1550                                         case 'float':\r
1551                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1552                                                 break;\r
1553                                         \r
1554                                         default:\r
1555                                                 s[na] = v || '';\r
1556                                 }\r
1557 \r
1558                                 // Force update of the style data\r
1559                                 if (t.settings.update_styles)\r
1560                                         t.setAttrib(e, '_mce_style');\r
1561                         });\r
1562                 },\r
1563 \r
1564                 getStyle : function(n, na, c) {\r
1565                         n = this.get(n);\r
1566 \r
1567                         if (!n)\r
1568                                 return false;\r
1569 \r
1570                         // Gecko\r
1571                         if (this.doc.defaultView && c) {\r
1572                                 // Remove camelcase\r
1573                                 na = na.replace(/[A-Z]/g, function(a){\r
1574                                         return '-' + a;\r
1575                                 });\r
1576 \r
1577                                 try {\r
1578                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1579                                 } catch (ex) {\r
1580                                         // Old safari might fail\r
1581                                         return null;\r
1582                                 }\r
1583                         }\r
1584 \r
1585                         // Camelcase it, if needed\r
1586                         na = na.replace(/-(\D)/g, function(a, b){\r
1587                                 return b.toUpperCase();\r
1588                         });\r
1589 \r
1590                         if (na == 'float')\r
1591                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1592 \r
1593                         // IE & Opera\r
1594                         if (n.currentStyle && c)\r
1595                                 return n.currentStyle[na];\r
1596 \r
1597                         return n.style[na];\r
1598                 },\r
1599 \r
1600                 setStyles : function(e, o) {\r
1601                         var t = this, s = t.settings, ol;\r
1602 \r
1603                         ol = s.update_styles;\r
1604                         s.update_styles = 0;\r
1605 \r
1606                         each(o, function(v, n) {\r
1607                                 t.setStyle(e, n, v);\r
1608                         });\r
1609 \r
1610                         // Update style info\r
1611                         s.update_styles = ol;\r
1612                         if (s.update_styles)\r
1613                                 t.setAttrib(e, s.cssText);\r
1614                 },\r
1615 \r
1616                 setAttrib : function(e, n, v) {\r
1617                         var t = this;\r
1618 \r
1619                         // Whats the point\r
1620                         if (!e || !n)\r
1621                                 return;\r
1622 \r
1623                         // Strict XML mode\r
1624                         if (t.settings.strict)\r
1625                                 n = n.toLowerCase();\r
1626 \r
1627                         return this.run(e, function(e) {\r
1628                                 var s = t.settings;\r
1629 \r
1630                                 switch (n) {\r
1631                                         case "style":\r
1632                                                 if (!is(v, 'string')) {\r
1633                                                         each(v, function(v, n) {\r
1634                                                                 t.setStyle(e, n, v);\r
1635                                                         });\r
1636 \r
1637                                                         return;\r
1638                                                 }\r
1639 \r
1640                                                 // No mce_style for elements with these since they might get resized by the user\r
1641                                                 if (s.keep_values) {\r
1642                                                         if (v && !t._isRes(v))\r
1643                                                                 e.setAttribute('_mce_style', v, 2);\r
1644                                                         else\r
1645                                                                 e.removeAttribute('_mce_style', 2);\r
1646                                                 }\r
1647 \r
1648                                                 e.style.cssText = v;\r
1649                                                 break;\r
1650 \r
1651                                         case "class":\r
1652                                                 e.className = v || ''; // Fix IE null bug\r
1653                                                 break;\r
1654 \r
1655                                         case "src":\r
1656                                         case "href":\r
1657                                                 if (s.keep_values) {\r
1658                                                         if (s.url_converter)\r
1659                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1660 \r
1661                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1662                                                 }\r
1663 \r
1664                                                 break;\r
1665                                         \r
1666                                         case "shape":\r
1667                                                 e.setAttribute('_mce_style', v);\r
1668                                                 break;\r
1669                                 }\r
1670 \r
1671                                 if (is(v) && v !== null && v.length !== 0)\r
1672                                         e.setAttribute(n, '' + v, 2);\r
1673                                 else\r
1674                                         e.removeAttribute(n, 2);\r
1675                         });\r
1676                 },\r
1677 \r
1678                 setAttribs : function(e, o) {\r
1679                         var t = this;\r
1680 \r
1681                         return this.run(e, function(e) {\r
1682                                 each(o, function(v, n) {\r
1683                                         t.setAttrib(e, n, v);\r
1684                                 });\r
1685                         });\r
1686                 },\r
1687 \r
1688                 getAttrib : function(e, n, dv) {\r
1689                         var v, t = this;\r
1690 \r
1691                         e = t.get(e);\r
1692 \r
1693                         if (!e || e.nodeType !== 1)\r
1694                                 return false;\r
1695 \r
1696                         if (!is(dv))\r
1697                                 dv = '';\r
1698 \r
1699                         // Try the mce variant for these\r
1700                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1701                                 v = e.getAttribute("_mce_" + n);\r
1702 \r
1703                                 if (v)\r
1704                                         return v;\r
1705                         }\r
1706 \r
1707                         if (isIE && t.props[n]) {\r
1708                                 v = e[t.props[n]];\r
1709                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1710                         }\r
1711 \r
1712                         if (!v)\r
1713                                 v = e.getAttribute(n, 2);\r
1714 \r
1715                         // Check boolean attribs\r
1716                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1717                                 if (e[t.props[n]] === true && v === '')\r
1718                                         return n;\r
1719 \r
1720                                 return v ? n : '';\r
1721                         }\r
1722 \r
1723                         // Inner input elements will override attributes on form elements\r
1724                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1725                                 return e.getAttributeNode(n).nodeValue;\r
1726 \r
1727                         if (n === 'style') {\r
1728                                 v = v || e.style.cssText;\r
1729 \r
1730                                 if (v) {\r
1731                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1732 \r
1733                                         if (t.settings.keep_values && !t._isRes(v))\r
1734                                                 e.setAttribute('_mce_style', v);\r
1735                                 }\r
1736                         }\r
1737 \r
1738                         // Remove Apple and WebKit stuff\r
1739                         if (isWebKit && n === "class" && v)\r
1740                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1741 \r
1742                         // Handle IE issues\r
1743                         if (isIE) {\r
1744                                 switch (n) {\r
1745                                         case 'rowspan':\r
1746                                         case 'colspan':\r
1747                                                 // IE returns 1 as default value\r
1748                                                 if (v === 1)\r
1749                                                         v = '';\r
1750 \r
1751                                                 break;\r
1752 \r
1753                                         case 'size':\r
1754                                                 // IE returns +0 as default value for size\r
1755                                                 if (v === '+0' || v === 20 || v === 0)\r
1756                                                         v = '';\r
1757 \r
1758                                                 break;\r
1759 \r
1760                                         case 'width':\r
1761                                         case 'height':\r
1762                                         case 'vspace':\r
1763                                         case 'checked':\r
1764                                         case 'disabled':\r
1765                                         case 'readonly':\r
1766                                                 if (v === 0)\r
1767                                                         v = '';\r
1768 \r
1769                                                 break;\r
1770 \r
1771                                         case 'hspace':\r
1772                                                 // IE returns -1 as default value\r
1773                                                 if (v === -1)\r
1774                                                         v = '';\r
1775 \r
1776                                                 break;\r
1777 \r
1778                                         case 'maxlength':\r
1779                                         case 'tabindex':\r
1780                                                 // IE returns default value\r
1781                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1782                                                         v = '';\r
1783 \r
1784                                                 break;\r
1785 \r
1786                                         case 'multiple':\r
1787                                         case 'compact':\r
1788                                         case 'noshade':\r
1789                                         case 'nowrap':\r
1790                                                 if (v === 65535)\r
1791                                                         return n;\r
1792 \r
1793                                                 return dv;\r
1794 \r
1795                                         case 'shape':\r
1796                                                 v = v.toLowerCase();\r
1797                                                 break;\r
1798 \r
1799                                         default:\r
1800                                                 // IE has odd anonymous function for event attributes\r
1801                                                 if (n.indexOf('on') === 0 && v)\r
1802                                                         v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1803                                 }\r
1804                         }\r
1805 \r
1806                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1807                 },\r
1808 \r
1809                 getPos : function(n, ro) {\r
1810                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1811 \r
1812                         n = t.get(n);\r
1813                         ro = ro || d.body;\r
1814 \r
1815                         if (n) {\r
1816                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1817                                 if (isIE && !t.stdMode) {\r
1818                                         n = n.getBoundingClientRect();\r
1819                                         e = t.boxModel ? d.documentElement : d.body;\r
1820                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1821                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\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                                                 e.removeAttribute('className');\r
2051                                         }\r
2052 \r
2053                                         return v;\r
2054                                 }\r
2055 \r
2056                                 return e.className;\r
2057                         });\r
2058                 },\r
2059 \r
2060                 hasClass : function(n, c) {\r
2061                         n = this.get(n);\r
2062 \r
2063                         if (!n || !c)\r
2064                                 return false;\r
2065 \r
2066                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
2067                 },\r
2068 \r
2069                 show : function(e) {\r
2070                         return this.setStyle(e, 'display', 'block');\r
2071                 },\r
2072 \r
2073                 hide : function(e) {\r
2074                         return this.setStyle(e, 'display', 'none');\r
2075                 },\r
2076 \r
2077                 isHidden : function(e) {\r
2078                         e = this.get(e);\r
2079 \r
2080                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
2081                 },\r
2082 \r
2083                 uniqueId : function(p) {\r
2084                         return (!p ? 'mce_' : p) + (this.counter++);\r
2085                 },\r
2086 \r
2087                 setHTML : function(e, h) {\r
2088                         var t = this;\r
2089 \r
2090                         return this.run(e, function(e) {\r
2091                                 var x, i, nl, n, p, x;\r
2092 \r
2093                                 h = t.processHTML(h);\r
2094 \r
2095                                 if (isIE) {\r
2096                                         function set() {\r
2097                                                 // Remove all child nodes\r
2098                                                 while (e.firstChild)\r
2099                                                         e.firstChild.removeNode();\r
2100 \r
2101                                                 try {\r
2102                                                         // IE will remove comments from the beginning\r
2103                                                         // unless you padd the contents with something\r
2104                                                         e.innerHTML = '<br />' + h;\r
2105                                                         e.removeChild(e.firstChild);\r
2106                                                 } catch (ex) {\r
2107                                                         // 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
2108                                                         // This seems to fix this problem\r
2109 \r
2110                                                         // Create new div with HTML contents and a BR infront to keep comments\r
2111                                                         x = t.create('div');\r
2112                                                         x.innerHTML = '<br />' + h;\r
2113 \r
2114                                                         // Add all children from div to target\r
2115                                                         each (x.childNodes, function(n, i) {\r
2116                                                                 // Skip br element\r
2117                                                                 if (i)\r
2118                                                                         e.appendChild(n);\r
2119                                                         });\r
2120                                                 }\r
2121                                         };\r
2122 \r
2123                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
2124                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
2125                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
2126                                         if (t.settings.fix_ie_paragraphs)\r
2127                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
2128 \r
2129                                         set();\r
2130 \r
2131                                         if (t.settings.fix_ie_paragraphs) {\r
2132                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
2133                                                 nl = e.getElementsByTagName("p");\r
2134                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
2135                                                         n = nl[i];\r
2136 \r
2137                                                         if (!n.hasChildNodes()) {\r
2138                                                                 if (!n._mce_keep) {\r
2139                                                                         x = 1; // Is broken\r
2140                                                                         break;\r
2141                                                                 }\r
2142 \r
2143                                                                 n.removeAttribute('_mce_keep');\r
2144                                                         }\r
2145                                                 }\r
2146                                         }\r
2147 \r
2148                                         // Time to fix the madness IE left us\r
2149                                         if (x) {\r
2150                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
2151                                                 // after we use innerHTML we can fix the DOM tree\r
2152                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
2153                                                 h = h.replace(/<\/p>/gi, '</div>');\r
2154 \r
2155                                                 // Set the new HTML with DIVs\r
2156                                                 set();\r
2157 \r
2158                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
2159                                                 // This is needed since IE has a annoying bug see above for details\r
2160                                                 // This is a slow process but it has to be done. :(\r
2161                                                 if (t.settings.fix_ie_paragraphs) {\r
2162                                                         nl = e.getElementsByTagName("DIV");\r
2163                                                         for (i = nl.length - 1; i >= 0; i--) {\r
2164                                                                 n = nl[i];\r
2165 \r
2166                                                                 // Is it a temp div\r
2167                                                                 if (n._mce_tmp) {\r
2168                                                                         // Create new paragraph\r
2169                                                                         p = t.doc.createElement('p');\r
2170 \r
2171                                                                         // Copy all attributes\r
2172                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
2173                                                                                 var v;\r
2174 \r
2175                                                                                 if (b !== '_mce_tmp') {\r
2176                                                                                         v = n.getAttribute(b);\r
2177 \r
2178                                                                                         if (!v && b === 'class')\r
2179                                                                                                 v = n.className;\r
2180 \r
2181                                                                                         p.setAttribute(b, v);\r
2182                                                                                 }\r
2183                                                                         });\r
2184 \r
2185                                                                         // Append all children to new paragraph\r
2186                                                                         for (x = 0; x<n.childNodes.length; x++)\r
2187                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
2188 \r
2189                                                                         // Replace div with new paragraph\r
2190                                                                         n.swapNode(p);\r
2191                                                                 }\r
2192                                                         }\r
2193                                                 }\r
2194                                         }\r
2195                                 } else\r
2196                                         e.innerHTML = h;\r
2197 \r
2198                                 return h;\r
2199                         });\r
2200                 },\r
2201 \r
2202                 processHTML : function(h) {\r
2203                         var t = this, s = t.settings, codeBlocks = [];\r
2204 \r
2205                         if (!s.process_html)\r
2206                                 return h;\r
2207 \r
2208                         if (isIE) {\r
2209                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
2210                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
2211                         }\r
2212 \r
2213                         // Fix some issues\r
2214                         h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
2215 \r
2216                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
2217                         if (s.keep_values) {\r
2218                                 // Wrap scripts and styles in comments for serialization purposes\r
2219                                 if (/<script|noscript|style/i.test(h)) {\r
2220                                         function trim(s) {\r
2221                                                 // Remove prefix and suffix code for element\r
2222                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
2223                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
2224                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
2225                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
2226 \r
2227                                                 return s;\r
2228                                         };\r
2229 \r
2230                                         // Wrap the script contents in CDATA and keep them from executing\r
2231                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
2232                                                 // Force type attribute\r
2233                                                 if (!attribs)\r
2234                                                         attribs = ' type="text/javascript"';\r
2235 \r
2236                                                 // Convert the src attribute of the scripts\r
2237                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
2238                                                         if (s.url_converter)\r
2239                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
2240 \r
2241                                                         return '_mce_src="' + url + '"';\r
2242                                                 });\r
2243 \r
2244                                                 // Wrap text contents\r
2245                                                 if (tinymce.trim(text)) {\r
2246                                                         codeBlocks.push(trim(text));\r
2247                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2248                                                 }\r
2249 \r
2250                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2251                                         });\r
2252 \r
2253                                         // Wrap style elements\r
2254                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2255                                                 // Wrap text contents\r
2256                                                 if (text) {\r
2257                                                         codeBlocks.push(trim(text));\r
2258                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2259                                                 }\r
2260 \r
2261                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2262                                         });\r
2263 \r
2264                                         // Wrap noscript elements\r
2265                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2266                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2267                                         });\r
2268                                 }\r
2269 \r
2270                                 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
2271 \r
2272                                 // This function processes the attributes in the HTML string to force boolean\r
2273                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2274                                 function processTags(html) {\r
2275                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2276                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2277                                                         var mceValue;\r
2278 \r
2279                                                         name = name.toLowerCase();\r
2280                                                         value = value || val2 || val3 || "";\r
2281 \r
2282                                                         // Treat boolean attributes\r
2283                                                         if (boolAttrs[name]) {\r
2284                                                                 // false or 0 is treated as a missing attribute\r
2285                                                                 if (value === 'false' || value === '0')\r
2286                                                                         return;\r
2287 \r
2288                                                                 return name + '="' + name + '"';\r
2289                                                         }\r
2290 \r
2291                                                         // Is attribute one that needs special treatment\r
2292                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2293                                                                 mceValue = t.decode(value);\r
2294 \r
2295                                                                 // Convert URLs to relative/absolute ones\r
2296                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2297                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2298 \r
2299                                                                 // Process styles lowercases them and compresses them\r
2300                                                                 if (name == 'style')\r
2301                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2302 \r
2303                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2304                                                         }\r
2305 \r
2306                                                         return match;\r
2307                                                 }) + end + '>';\r
2308                                         });\r
2309                                 };\r
2310 \r
2311                                 h = processTags(h);\r
2312 \r
2313                                 // Restore script blocks\r
2314                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2315                                         return codeBlocks[idx];\r
2316                                 });\r
2317                         }\r
2318 \r
2319                         return h;\r
2320                 },\r
2321 \r
2322                 getOuterHTML : function(e) {\r
2323                         var d;\r
2324 \r
2325                         e = this.get(e);\r
2326 \r
2327                         if (!e)\r
2328                                 return null;\r
2329 \r
2330                         if (e.outerHTML !== undefined)\r
2331                                 return e.outerHTML;\r
2332 \r
2333                         d = (e.ownerDocument || this.doc).createElement("body");\r
2334                         d.appendChild(e.cloneNode(true));\r
2335 \r
2336                         return d.innerHTML;\r
2337                 },\r
2338 \r
2339                 setOuterHTML : function(e, h, d) {\r
2340                         var t = this;\r
2341 \r
2342                         function setHTML(e, h, d) {\r
2343                                 var n, tp;\r
2344 \r
2345                                 tp = d.createElement("body");\r
2346                                 tp.innerHTML = h;\r
2347 \r
2348                                 n = tp.lastChild;\r
2349                                 while (n) {\r
2350                                         t.insertAfter(n.cloneNode(true), e);\r
2351                                         n = n.previousSibling;\r
2352                                 }\r
2353 \r
2354                                 t.remove(e);\r
2355                         };\r
2356 \r
2357                         return this.run(e, function(e) {\r
2358                                 e = t.get(e);\r
2359 \r
2360                                 // Only set HTML on elements\r
2361                                 if (e.nodeType == 1) {\r
2362                                         d = d || e.ownerDocument || t.doc;\r
2363 \r
2364                                         if (isIE) {\r
2365                                                 try {\r
2366                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2367                                                         if (isIE && e.nodeType == 1)\r
2368                                                                 e.outerHTML = h;\r
2369                                                         else\r
2370                                                                 setHTML(e, h, d);\r
2371                                                 } catch (ex) {\r
2372                                                         // Fix for unknown runtime error\r
2373                                                         setHTML(e, h, d);\r
2374                                                 }\r
2375                                         } else\r
2376                                                 setHTML(e, h, d);\r
2377                                 }\r
2378                         });\r
2379                 },\r
2380 \r
2381                 decode : function(s) {\r
2382                         var e, n, v;\r
2383 \r
2384                         // Look for entities to decode\r
2385                         if (/&[\w#]+;/.test(s)) {\r
2386                                 // Decode the entities using a div element not super efficient but less code\r
2387                                 e = this.doc.createElement("div");\r
2388                                 e.innerHTML = s;\r
2389                                 n = e.firstChild;\r
2390                                 v = '';\r
2391 \r
2392                                 if (n) {\r
2393                                         do {\r
2394                                                 v += n.nodeValue;\r
2395                                         } while (n = n.nextSibling);\r
2396                                 }\r
2397 \r
2398                                 return v || s;\r
2399                         }\r
2400 \r
2401                         return s;\r
2402                 },\r
2403 \r
2404                 encode : function(str) {\r
2405                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2406                                 return encodedChars[chr];\r
2407                         });\r
2408                 },\r
2409 \r
2410                 insertAfter : function(node, reference_node) {\r
2411                         reference_node = this.get(reference_node);\r
2412 \r
2413                         return this.run(node, function(node) {\r
2414                                 var parent, nextSibling;\r
2415 \r
2416                                 parent = reference_node.parentNode;\r
2417                                 nextSibling = reference_node.nextSibling;\r
2418 \r
2419                                 if (nextSibling)\r
2420                                         parent.insertBefore(node, nextSibling);\r
2421                                 else\r
2422                                         parent.appendChild(node);\r
2423 \r
2424                                 return node;\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                                 return o.parentNode.replaceChild(n, o);\r
2451                         });\r
2452                 },\r
2453 \r
2454                 rename : function(elm, name) {\r
2455                         var t = this, newElm;\r
2456 \r
2457                         if (elm.nodeName != name.toUpperCase()) {\r
2458                                 // Rename block element\r
2459                                 newElm = t.create(name);\r
2460 \r
2461                                 // Copy attribs to new block\r
2462                                 each(t.getAttribs(elm), function(attr_node) {\r
2463                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2464                                 });\r
2465 \r
2466                                 // Replace block\r
2467                                 t.replace(newElm, elm, 1);\r
2468                         }\r
2469 \r
2470                         return newElm || elm;\r
2471                 },\r
2472 \r
2473                 findCommonAncestor : function(a, b) {\r
2474                         var ps = a, pe;\r
2475 \r
2476                         while (ps) {\r
2477                                 pe = b;\r
2478 \r
2479                                 while (pe && ps != pe)\r
2480                                         pe = pe.parentNode;\r
2481 \r
2482                                 if (ps == pe)\r
2483                                         break;\r
2484 \r
2485                                 ps = ps.parentNode;\r
2486                         }\r
2487 \r
2488                         if (!ps && a.ownerDocument)\r
2489                                 return a.ownerDocument.documentElement;\r
2490 \r
2491                         return ps;\r
2492                 },\r
2493 \r
2494                 toHex : function(s) {\r
2495                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2496 \r
2497                         function hex(s) {\r
2498                                 s = parseInt(s).toString(16);\r
2499 \r
2500                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2501                         };\r
2502 \r
2503                         if (c) {\r
2504                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2505 \r
2506                                 return s;\r
2507                         }\r
2508 \r
2509                         return s;\r
2510                 },\r
2511 \r
2512                 getClasses : function() {\r
2513                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2514 \r
2515                         if (t.classes)\r
2516                                 return t.classes;\r
2517 \r
2518                         function addClasses(s) {\r
2519                                 // IE style imports\r
2520                                 each(s.imports, function(r) {\r
2521                                         addClasses(r);\r
2522                                 });\r
2523 \r
2524                                 each(s.cssRules || s.rules, function(r) {\r
2525                                         // Real type or fake it on IE\r
2526                                         switch (r.type || 1) {\r
2527                                                 // Rule\r
2528                                                 case 1:\r
2529                                                         if (r.selectorText) {\r
2530                                                                 each(r.selectorText.split(','), function(v) {\r
2531                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2532 \r
2533                                                                         // Is internal or it doesn't contain a class\r
2534                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2535                                                                                 return;\r
2536 \r
2537                                                                         // Remove everything but class name\r
2538                                                                         ov = v;\r
2539                                                                         v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2540 \r
2541                                                                         // Filter classes\r
2542                                                                         if (f && !(v = f(v, ov)))\r
2543                                                                                 return;\r
2544 \r
2545                                                                         if (!lo[v]) {\r
2546                                                                                 cl.push({'class' : v});\r
2547                                                                                 lo[v] = 1;\r
2548                                                                         }\r
2549                                                                 });\r
2550                                                         }\r
2551                                                         break;\r
2552 \r
2553                                                 // Import\r
2554                                                 case 3:\r
2555                                                         addClasses(r.styleSheet);\r
2556                                                         break;\r
2557                                         }\r
2558                                 });\r
2559                         };\r
2560 \r
2561                         try {\r
2562                                 each(t.doc.styleSheets, addClasses);\r
2563                         } catch (ex) {\r
2564                                 // Ignore\r
2565                         }\r
2566 \r
2567                         if (cl.length > 0)\r
2568                                 t.classes = cl;\r
2569 \r
2570                         return cl;\r
2571                 },\r
2572 \r
2573                 run : function(e, f, s) {\r
2574                         var t = this, o;\r
2575 \r
2576                         if (t.doc && typeof(e) === 'string')\r
2577                                 e = t.get(e);\r
2578 \r
2579                         if (!e)\r
2580                                 return false;\r
2581 \r
2582                         s = s || this;\r
2583                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2584                                 o = [];\r
2585 \r
2586                                 each(e, function(e, i) {\r
2587                                         if (e) {\r
2588                                                 if (typeof(e) == 'string')\r
2589                                                         e = t.doc.getElementById(e);\r
2590 \r
2591                                                 o.push(f.call(s, e, i));\r
2592                                         }\r
2593                                 });\r
2594 \r
2595                                 return o;\r
2596                         }\r
2597 \r
2598                         return f.call(s, e);\r
2599                 },\r
2600 \r
2601                 getAttribs : function(n) {\r
2602                         var o;\r
2603 \r
2604                         n = this.get(n);\r
2605 \r
2606                         if (!n)\r
2607                                 return [];\r
2608 \r
2609                         if (isIE) {\r
2610                                 o = [];\r
2611 \r
2612                                 // Object will throw exception in IE\r
2613                                 if (n.nodeName == 'OBJECT')\r
2614                                         return n.attributes;\r
2615 \r
2616                                 // IE doesn't keep the selected attribute if you clone option elements\r
2617                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2618                                         o.push({specified : 1, nodeName : 'selected'});\r
2619 \r
2620                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2621                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2622                                         o.push({specified : 1, nodeName : a});\r
2623                                 });\r
2624 \r
2625                                 return o;\r
2626                         }\r
2627 \r
2628                         return n.attributes;\r
2629                 },\r
2630 \r
2631                 destroy : function(s) {\r
2632                         var t = this;\r
2633 \r
2634                         if (t.events)\r
2635                                 t.events.destroy();\r
2636 \r
2637                         t.win = t.doc = t.root = t.events = null;\r
2638 \r
2639                         // Manual destroy then remove unload handler\r
2640                         if (!s)\r
2641                                 tinymce.removeUnload(t.destroy);\r
2642                 },\r
2643 \r
2644                 createRng : function() {\r
2645                         var d = this.doc;\r
2646 \r
2647                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2648                 },\r
2649 \r
2650                 nodeIndex : function(node, normalized) {\r
2651                         var idx = 0, lastNodeType, lastNode, nodeType;\r
2652 \r
2653                         if (node) {\r
2654                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2655                                         nodeType = node.nodeType;\r
2656 \r
2657                                         // Normalize text nodes\r
2658                                         if (normalized && nodeType == 3) {\r
2659                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
2660                                                         continue;\r
2661                                         }\r
2662 \r
2663                                         idx++;\r
2664                                         lastNodeType = nodeType;\r
2665                                 }\r
2666                         }\r
2667 \r
2668                         return idx;\r
2669                 },\r
2670 \r
2671                 split : function(pe, e, re) {\r
2672                         var t = this, r = t.createRng(), bef, aft, pa;\r
2673 \r
2674                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2675                         // but we don't want that in our code since it serves no purpose for the end user\r
2676                         // For example if this is chopped:\r
2677                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2678                         // would produce:\r
2679                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2680                         // this function will then trim of empty edges and produce:\r
2681                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2682                         function trim(node) {\r
2683                                 var i, children = node.childNodes;\r
2684 \r
2685                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2686                                         return;\r
2687 \r
2688                                 for (i = children.length - 1; i >= 0; i--)\r
2689                                         trim(children[i]);\r
2690 \r
2691                                 if (node.nodeType != 9) {\r
2692                                         // Keep non whitespace text nodes\r
2693                                         if (node.nodeType == 3 && node.nodeValue.length > 0)\r
2694                                                 return;\r
2695 \r
2696                                         if (node.nodeType == 1) {\r
2697                                                 // If the only child is a bookmark then move it up\r
2698                                                 children = node.childNodes;\r
2699                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2700                                                         node.parentNode.insertBefore(children[0], node);\r
2701 \r
2702                                                 // Keep non empty elements or img, hr etc\r
2703                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2704                                                         return;\r
2705                                         }\r
2706 \r
2707                                         t.remove(node);\r
2708                                 }\r
2709 \r
2710                                 return node;\r
2711                         };\r
2712 \r
2713                         if (pe && e) {\r
2714                                 // Get before chunk\r
2715                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2716                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2717                                 bef = r.extractContents();\r
2718 \r
2719                                 // Get after chunk\r
2720                                 r = t.createRng();\r
2721                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2722                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2723                                 aft = r.extractContents();\r
2724 \r
2725                                 // Insert before chunk\r
2726                                 pa = pe.parentNode;\r
2727                                 pa.insertBefore(trim(bef), pe);\r
2728 \r
2729                                 // Insert middle chunk\r
2730                                 if (re)\r
2731                                         pa.replaceChild(re, e);\r
2732                                 else\r
2733                                         pa.insertBefore(e, pe);\r
2734 \r
2735                                 // Insert after chunk\r
2736                                 pa.insertBefore(trim(aft), pe);\r
2737                                 t.remove(pe);\r
2738 \r
2739                                 return re || e;\r
2740                         }\r
2741                 },\r
2742 \r
2743                 bind : function(target, name, func, scope) {\r
2744                         var t = this;\r
2745 \r
2746                         if (!t.events)\r
2747                                 t.events = new tinymce.dom.EventUtils();\r
2748 \r
2749                         return t.events.add(target, name, func, scope || this);\r
2750                 },\r
2751 \r
2752                 unbind : function(target, name, func) {\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.remove(target, name, func);\r
2759                 },\r
2760 \r
2761 \r
2762                 _findSib : function(node, selector, name) {\r
2763                         var t = this, f = selector;\r
2764 \r
2765                         if (node) {\r
2766                                 // If expression make a function of it using is\r
2767                                 if (is(f, 'string')) {\r
2768                                         f = function(node) {\r
2769                                                 return t.is(node, selector);\r
2770                                         };\r
2771                                 }\r
2772 \r
2773                                 // Loop all siblings\r
2774                                 for (node = node[name]; node; node = node[name]) {\r
2775                                         if (f(node))\r
2776                                                 return node;\r
2777                                 }\r
2778                         }\r
2779 \r
2780                         return null;\r
2781                 },\r
2782 \r
2783                 _isRes : function(c) {\r
2784                         // Is live resizble element\r
2785                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2786                 }\r
2787 \r
2788                 /*\r
2789                 walk : function(n, f, s) {\r
2790                         var d = this.doc, w;\r
2791 \r
2792                         if (d.createTreeWalker) {\r
2793                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2794 \r
2795                                 while ((n = w.nextNode()) != null)\r
2796                                         f.call(s || this, n);\r
2797                         } else\r
2798                                 tinymce.walk(n, f, 'childNodes', s);\r
2799                 }\r
2800                 */\r
2801 \r
2802                 /*\r
2803                 toRGB : function(s) {\r
2804                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2805 \r
2806                         if (c) {\r
2807                                 // #FFF -> #FFFFFF\r
2808                                 if (!is(c[3]))\r
2809                                         c[3] = c[2] = c[1];\r
2810 \r
2811                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2812                         }\r
2813 \r
2814                         return s;\r
2815                 }\r
2816                 */\r
2817         });\r
2818 \r
2819         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2820 })(tinymce);\r
2821 \r
2822 (function(ns) {\r
2823         // Range constructor\r
2824         function Range(dom) {\r
2825                 var t = this,\r
2826                         doc = dom.doc,\r
2827                         EXTRACT = 0,\r
2828                         CLONE = 1,\r
2829                         DELETE = 2,\r
2830                         TRUE = true,\r
2831                         FALSE = false,\r
2832                         START_OFFSET = 'startOffset',\r
2833                         START_CONTAINER = 'startContainer',\r
2834                         END_CONTAINER = 'endContainer',\r
2835                         END_OFFSET = 'endOffset',\r
2836                         extend = tinymce.extend,\r
2837                         nodeIndex = dom.nodeIndex;\r
2838 \r
2839                 extend(t, {\r
2840                         // Inital states\r
2841                         startContainer : doc,\r
2842                         startOffset : 0,\r
2843                         endContainer : doc,\r
2844                         endOffset : 0,\r
2845                         collapsed : TRUE,\r
2846                         commonAncestorContainer : doc,\r
2847 \r
2848                         // Range constants\r
2849                         START_TO_START : 0,\r
2850                         START_TO_END : 1,\r
2851                         END_TO_END : 2,\r
2852                         END_TO_START : 3,\r
2853 \r
2854                         // Public methods\r
2855                         setStart : setStart,\r
2856                         setEnd : setEnd,\r
2857                         setStartBefore : setStartBefore,\r
2858                         setStartAfter : setStartAfter,\r
2859                         setEndBefore : setEndBefore,\r
2860                         setEndAfter : setEndAfter,\r
2861                         collapse : collapse,\r
2862                         selectNode : selectNode,\r
2863                         selectNodeContents : selectNodeContents,\r
2864                         compareBoundaryPoints : compareBoundaryPoints,\r
2865                         deleteContents : deleteContents,\r
2866                         extractContents : extractContents,\r
2867                         cloneContents : cloneContents,\r
2868                         insertNode : insertNode,\r
2869                         surroundContents : surroundContents,\r
2870                         cloneRange : cloneRange\r
2871                 });\r
2872 \r
2873                 function setStart(n, o) {\r
2874                         _setEndPoint(TRUE, n, o);\r
2875                 };\r
2876 \r
2877                 function setEnd(n, o) {\r
2878                         _setEndPoint(FALSE, n, o);\r
2879                 };\r
2880 \r
2881                 function setStartBefore(n) {\r
2882                         setStart(n.parentNode, nodeIndex(n));\r
2883                 };\r
2884 \r
2885                 function setStartAfter(n) {\r
2886                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2887                 };\r
2888 \r
2889                 function setEndBefore(n) {\r
2890                         setEnd(n.parentNode, nodeIndex(n));\r
2891                 };\r
2892 \r
2893                 function setEndAfter(n) {\r
2894                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2895                 };\r
2896 \r
2897                 function collapse(ts) {\r
2898                         if (ts) {\r
2899                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2900                                 t[END_OFFSET] = t[START_OFFSET];\r
2901                         } else {\r
2902                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2903                                 t[START_OFFSET] = t[END_OFFSET];\r
2904                         }\r
2905 \r
2906                         t.collapsed = TRUE;\r
2907                 };\r
2908 \r
2909                 function selectNode(n) {\r
2910                         setStartBefore(n);\r
2911                         setEndAfter(n);\r
2912                 };\r
2913 \r
2914                 function selectNodeContents(n) {\r
2915                         setStart(n, 0);\r
2916                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2917                 };\r
2918 \r
2919                 function compareBoundaryPoints(h, r) {\r
2920                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2921 \r
2922                         // Check START_TO_START\r
2923                         if (h === 0)\r
2924                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2925 \r
2926                         // Check START_TO_END\r
2927                         if (h === 1)\r
2928                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2929 \r
2930                         // Check END_TO_END\r
2931                         if (h === 2)\r
2932                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2933 \r
2934                         // Check END_TO_START\r
2935                         if (h === 3)\r
2936                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2937                 };\r
2938 \r
2939                 function deleteContents() {\r
2940                         _traverse(DELETE);\r
2941                 };\r
2942 \r
2943                 function extractContents() {\r
2944                         return _traverse(EXTRACT);\r
2945                 };\r
2946 \r
2947                 function cloneContents() {\r
2948                         return _traverse(CLONE);\r
2949                 };\r
2950 \r
2951                 function insertNode(n) {\r
2952                         var startContainer = this[START_CONTAINER],\r
2953                                 startOffset = this[START_OFFSET], nn, o;\r
2954 \r
2955                         // Node is TEXT_NODE or CDATA\r
2956                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2957                                 if (!startOffset) {\r
2958                                         // At the start of text\r
2959                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2960                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2961                                         // At the end of text\r
2962                                         dom.insertAfter(n, startContainer);\r
2963                                 } else {\r
2964                                         // Middle, need to split\r
2965                                         nn = startContainer.splitText(startOffset);\r
2966                                         startContainer.parentNode.insertBefore(n, nn);\r
2967                                 }\r
2968                         } else {\r
2969                                 // Insert element node\r
2970                                 if (startContainer.childNodes.length > 0)\r
2971                                         o = startContainer.childNodes[startOffset];\r
2972 \r
2973                                 if (o)\r
2974                                         startContainer.insertBefore(n, o);\r
2975                                 else\r
2976                                         startContainer.appendChild(n);\r
2977                         }\r
2978                 };\r
2979 \r
2980                 function surroundContents(n) {\r
2981                         var f = t.extractContents();\r
2982 \r
2983                         t.insertNode(n);\r
2984                         n.appendChild(f);\r
2985                         t.selectNode(n);\r
2986                 };\r
2987 \r
2988                 function cloneRange() {\r
2989                         return extend(new Range(dom), {\r
2990                                 startContainer : t[START_CONTAINER],\r
2991                                 startOffset : t[START_OFFSET],\r
2992                                 endContainer : t[END_CONTAINER],\r
2993                                 endOffset : t[END_OFFSET],\r
2994                                 collapsed : t.collapsed,\r
2995                                 commonAncestorContainer : t.commonAncestorContainer\r
2996                         });\r
2997                 };\r
2998 \r
2999                 // Private methods\r
3000 \r
3001                 function _getSelectedNode(container, offset) {\r
3002                         var child;\r
3003 \r
3004                         if (container.nodeType == 3 /* TEXT_NODE */)\r
3005                                 return container;\r
3006 \r
3007                         if (offset < 0)\r
3008                                 return container;\r
3009 \r
3010                         child = container.firstChild;\r
3011                         while (child && offset > 0) {\r
3012                                 --offset;\r
3013                                 child = child.nextSibling;\r
3014                         }\r
3015 \r
3016                         if (child)\r
3017                                 return child;\r
3018 \r
3019                         return container;\r
3020                 };\r
3021 \r
3022                 function _isCollapsed() {\r
3023                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
3024                 };\r
3025 \r
3026                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
3027                         var c, offsetC, n, cmnRoot, childA, childB;\r
3028 \r
3029                         // In the first case the boundary-points have the same container. A is before B\r
3030                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
3031                         // equal to the offset of B, and A is after B if its offset is greater than the\r
3032                         // offset of B.\r
3033                         if (containerA == containerB) {\r
3034                                 if (offsetA == offsetB)\r
3035                                         return 0; // equal\r
3036 \r
3037                                 if (offsetA < offsetB)\r
3038                                         return -1; // before\r
3039 \r
3040                                 return 1; // after\r
3041                         }\r
3042 \r
3043                         // In the second case a child node C of the container of A is an ancestor\r
3044                         // container of B. In this case, A is before B if the offset of A is less than or\r
3045                         // equal to the index of the child node C and A is after B otherwise.\r
3046                         c = containerB;\r
3047                         while (c && c.parentNode != containerA)\r
3048                                 c = c.parentNode;\r
3049 \r
3050                         if (c) {\r
3051                                 offsetC = 0;\r
3052                                 n = containerA.firstChild;\r
3053 \r
3054                                 while (n != c && offsetC < offsetA) {\r
3055                                         offsetC++;\r
3056                                         n = n.nextSibling;\r
3057                                 }\r
3058 \r
3059                                 if (offsetA <= offsetC)\r
3060                                         return -1; // before\r
3061 \r
3062                                 return 1; // after\r
3063                         }\r
3064 \r
3065                         // In the third case a child node C of the container of B is an ancestor container\r
3066                         // of A. In this case, A is before B if the index of the child node C is less than\r
3067                         // the offset of B and A is after B otherwise.\r
3068                         c = containerA;\r
3069                         while (c && c.parentNode != containerB) {\r
3070                                 c = c.parentNode;\r
3071                         }\r
3072 \r
3073                         if (c) {\r
3074                                 offsetC = 0;\r
3075                                 n = containerB.firstChild;\r
3076 \r
3077                                 while (n != c && offsetC < offsetB) {\r
3078                                         offsetC++;\r
3079                                         n = n.nextSibling;\r
3080                                 }\r
3081 \r
3082                                 if (offsetC < offsetB)\r
3083                                         return -1; // before\r
3084 \r
3085                                 return 1; // after\r
3086                         }\r
3087 \r
3088                         // In the fourth case, none of three other cases hold: the containers of A and B\r
3089                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
3090                         // the container of A is before the container of B in a pre-order traversal of the\r
3091                         // Ranges' context tree and A is after B otherwise.\r
3092                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
3093                         childA = containerA;\r
3094 \r
3095                         while (childA && childA.parentNode != cmnRoot)\r
3096                                 childA = childA.parentNode;\r
3097 \r
3098                         if (!childA)\r
3099                                 childA = cmnRoot;\r
3100 \r
3101                         childB = containerB;\r
3102                         while (childB && childB.parentNode != cmnRoot)\r
3103                                 childB = childB.parentNode;\r
3104 \r
3105                         if (!childB)\r
3106                                 childB = cmnRoot;\r
3107 \r
3108                         if (childA == childB)\r
3109                                 return 0; // equal\r
3110 \r
3111                         n = cmnRoot.firstChild;\r
3112                         while (n) {\r
3113                                 if (n == childA)\r
3114                                         return -1; // before\r
3115 \r
3116                                 if (n == childB)\r
3117                                         return 1; // after\r
3118 \r
3119                                 n = n.nextSibling;\r
3120                         }\r
3121                 };\r
3122 \r
3123                 function _setEndPoint(st, n, o) {\r
3124                         var ec, sc;\r
3125 \r
3126                         if (st) {\r
3127                                 t[START_CONTAINER] = n;\r
3128                                 t[START_OFFSET] = o;\r
3129                         } else {\r
3130                                 t[END_CONTAINER] = n;\r
3131                                 t[END_OFFSET] = o;\r
3132                         }\r
3133 \r
3134                         // If one boundary-point of a Range is set to have a root container\r
3135                         // other than the current one for the Range, the Range is collapsed to\r
3136                         // the new position. This enforces the restriction that both boundary-\r
3137                         // points of a Range must have the same root container.\r
3138                         ec = t[END_CONTAINER];\r
3139                         while (ec.parentNode)\r
3140                                 ec = ec.parentNode;\r
3141 \r
3142                         sc = t[START_CONTAINER];\r
3143                         while (sc.parentNode)\r
3144                                 sc = sc.parentNode;\r
3145 \r
3146                         if (sc == ec) {\r
3147                                 // The start position of a Range is guaranteed to never be after the\r
3148                                 // end position. To enforce this restriction, if the start is set to\r
3149                                 // be at a position after the end, the Range is collapsed to that\r
3150                                 // position.\r
3151                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
3152                                         t.collapse(st);\r
3153                         } else\r
3154                                 t.collapse(st);\r
3155 \r
3156                         t.collapsed = _isCollapsed();\r
3157                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
3158                 };\r
3159 \r
3160                 function _traverse(how) {\r
3161                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
3162 \r
3163                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
3164                                 return _traverseSameContainer(how);\r
3165 \r
3166                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
3167                                 if (p == t[START_CONTAINER])\r
3168                                         return _traverseCommonStartContainer(c, how);\r
3169 \r
3170                                 ++endContainerDepth;\r
3171                         }\r
3172 \r
3173                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
3174                                 if (p == t[END_CONTAINER])\r
3175                                         return _traverseCommonEndContainer(c, how);\r
3176 \r
3177                                 ++startContainerDepth;\r
3178                         }\r
3179 \r
3180                         depthDiff = startContainerDepth - endContainerDepth;\r
3181 \r
3182                         startNode = t[START_CONTAINER];\r
3183                         while (depthDiff > 0) {\r
3184                                 startNode = startNode.parentNode;\r
3185                                 depthDiff--;\r
3186                         }\r
3187 \r
3188                         endNode = t[END_CONTAINER];\r
3189                         while (depthDiff < 0) {\r
3190                                 endNode = endNode.parentNode;\r
3191                                 depthDiff++;\r
3192                         }\r
3193 \r
3194                         // ascend the ancestor hierarchy until we have a common parent.\r
3195                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
3196                                 startNode = sp;\r
3197                                 endNode = ep;\r
3198                         }\r
3199 \r
3200                         return _traverseCommonAncestors(startNode, endNode, how);\r
3201                 };\r
3202 \r
3203                  function _traverseSameContainer(how) {\r
3204                         var frag, s, sub, n, cnt, sibling, xferNode;\r
3205 \r
3206                         if (how != DELETE)\r
3207                                 frag = doc.createDocumentFragment();\r
3208 \r
3209                         // If selection is empty, just return the fragment\r
3210                         if (t[START_OFFSET] == t[END_OFFSET])\r
3211                                 return frag;\r
3212 \r
3213                         // Text node needs special case handling\r
3214                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
3215                                 // get the substring\r
3216                                 s = t[START_CONTAINER].nodeValue;\r
3217                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
3218 \r
3219                                 // set the original text node to its new value\r
3220                                 if (how != CLONE) {\r
3221                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
3222 \r
3223                                         // Nothing is partially selected, so collapse to start point\r
3224                                         t.collapse(TRUE);\r
3225                                 }\r
3226 \r
3227                                 if (how == DELETE)\r
3228                                         return;\r
3229 \r
3230                                 frag.appendChild(doc.createTextNode(sub));\r
3231                                 return frag;\r
3232                         }\r
3233 \r
3234                         // Copy nodes between the start/end offsets.\r
3235                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
3236                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
3237 \r
3238                         while (cnt > 0) {\r
3239                                 sibling = n.nextSibling;\r
3240                                 xferNode = _traverseFullySelected(n, how);\r
3241 \r
3242                                 if (frag)\r
3243                                         frag.appendChild( xferNode );\r
3244 \r
3245                                 --cnt;\r
3246                                 n = sibling;\r
3247                         }\r
3248 \r
3249                         // Nothing is partially selected, so collapse to start point\r
3250                         if (how != CLONE)\r
3251                                 t.collapse(TRUE);\r
3252 \r
3253                         return frag;\r
3254                 };\r
3255 \r
3256                 function _traverseCommonStartContainer(endAncestor, how) {\r
3257                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3258 \r
3259                         if (how != DELETE)\r
3260                                 frag = doc.createDocumentFragment();\r
3261 \r
3262                         n = _traverseRightBoundary(endAncestor, how);\r
3263 \r
3264                         if (frag)\r
3265                                 frag.appendChild(n);\r
3266 \r
3267                         endIdx = nodeIndex(endAncestor);\r
3268                         cnt = endIdx - t[START_OFFSET];\r
3269 \r
3270                         if (cnt <= 0) {\r
3271                                 // Collapse to just before the endAncestor, which\r
3272                                 // is partially selected.\r
3273                                 if (how != CLONE) {\r
3274                                         t.setEndBefore(endAncestor);\r
3275                                         t.collapse(FALSE);\r
3276                                 }\r
3277 \r
3278                                 return frag;\r
3279                         }\r
3280 \r
3281                         n = endAncestor.previousSibling;\r
3282                         while (cnt > 0) {\r
3283                                 sibling = n.previousSibling;\r
3284                                 xferNode = _traverseFullySelected(n, how);\r
3285 \r
3286                                 if (frag)\r
3287                                         frag.insertBefore(xferNode, frag.firstChild);\r
3288 \r
3289                                 --cnt;\r
3290                                 n = sibling;\r
3291                         }\r
3292 \r
3293                         // Collapse to just before the endAncestor, which\r
3294                         // is partially selected.\r
3295                         if (how != CLONE) {\r
3296                                 t.setEndBefore(endAncestor);\r
3297                                 t.collapse(FALSE);\r
3298                         }\r
3299 \r
3300                         return frag;\r
3301                 };\r
3302 \r
3303                 function _traverseCommonEndContainer(startAncestor, how) {\r
3304                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3305 \r
3306                         if (how != DELETE)\r
3307                                 frag = doc.createDocumentFragment();\r
3308 \r
3309                         n = _traverseLeftBoundary(startAncestor, how);\r
3310                         if (frag)\r
3311                                 frag.appendChild(n);\r
3312 \r
3313                         startIdx = nodeIndex(startAncestor);\r
3314                         ++startIdx;  // Because we already traversed it....\r
3315 \r
3316                         cnt = t[END_OFFSET] - startIdx;\r
3317                         n = startAncestor.nextSibling;\r
3318                         while (cnt > 0) {\r
3319                                 sibling = n.nextSibling;\r
3320                                 xferNode = _traverseFullySelected(n, how);\r
3321 \r
3322                                 if (frag)\r
3323                                         frag.appendChild(xferNode);\r
3324 \r
3325                                 --cnt;\r
3326                                 n = sibling;\r
3327                         }\r
3328 \r
3329                         if (how != CLONE) {\r
3330                                 t.setStartAfter(startAncestor);\r
3331                                 t.collapse(TRUE);\r
3332                         }\r
3333 \r
3334                         return frag;\r
3335                 };\r
3336 \r
3337                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3338                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3339 \r
3340                         if (how != DELETE)\r
3341                                 frag = doc.createDocumentFragment();\r
3342 \r
3343                         n = _traverseLeftBoundary(startAncestor, how);\r
3344                         if (frag)\r
3345                                 frag.appendChild(n);\r
3346 \r
3347                         commonParent = startAncestor.parentNode;\r
3348                         startOffset = nodeIndex(startAncestor);\r
3349                         endOffset = nodeIndex(endAncestor);\r
3350                         ++startOffset;\r
3351 \r
3352                         cnt = endOffset - startOffset;\r
3353                         sibling = startAncestor.nextSibling;\r
3354 \r
3355                         while (cnt > 0) {\r
3356                                 nextSibling = sibling.nextSibling;\r
3357                                 n = _traverseFullySelected(sibling, how);\r
3358 \r
3359                                 if (frag)\r
3360                                         frag.appendChild(n);\r
3361 \r
3362                                 sibling = nextSibling;\r
3363                                 --cnt;\r
3364                         }\r
3365 \r
3366                         n = _traverseRightBoundary(endAncestor, how);\r
3367 \r
3368                         if (frag)\r
3369                                 frag.appendChild(n);\r
3370 \r
3371                         if (how != CLONE) {\r
3372                                 t.setStartAfter(startAncestor);\r
3373                                 t.collapse(TRUE);\r
3374                         }\r
3375 \r
3376                         return frag;\r
3377                 };\r
3378 \r
3379                 function _traverseRightBoundary(root, how) {\r
3380                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3381 \r
3382                         if (next == root)\r
3383                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3384 \r
3385                         parent = next.parentNode;\r
3386                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3387 \r
3388                         while (parent) {\r
3389                                 while (next) {\r
3390                                         prevSibling = next.previousSibling;\r
3391                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3392 \r
3393                                         if (how != DELETE)\r
3394                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3395 \r
3396                                         isFullySelected = TRUE;\r
3397                                         next = prevSibling;\r
3398                                 }\r
3399 \r
3400                                 if (parent == root)\r
3401                                         return clonedParent;\r
3402 \r
3403                                 next = parent.previousSibling;\r
3404                                 parent = parent.parentNode;\r
3405 \r
3406                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3407 \r
3408                                 if (how != DELETE)\r
3409                                         clonedGrandParent.appendChild(clonedParent);\r
3410 \r
3411                                 clonedParent = clonedGrandParent;\r
3412                         }\r
3413                 };\r
3414 \r
3415                 function _traverseLeftBoundary(root, how) {\r
3416                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3417 \r
3418                         if (next == root)\r
3419                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3420 \r
3421                         parent = next.parentNode;\r
3422                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3423 \r
3424                         while (parent) {\r
3425                                 while (next) {\r
3426                                         nextSibling = next.nextSibling;\r
3427                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3428 \r
3429                                         if (how != DELETE)\r
3430                                                 clonedParent.appendChild(clonedChild);\r
3431 \r
3432                                         isFullySelected = TRUE;\r
3433                                         next = nextSibling;\r
3434                                 }\r
3435 \r
3436                                 if (parent == root)\r
3437                                         return clonedParent;\r
3438 \r
3439                                 next = parent.nextSibling;\r
3440                                 parent = parent.parentNode;\r
3441 \r
3442                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3443 \r
3444                                 if (how != DELETE)\r
3445                                         clonedGrandParent.appendChild(clonedParent);\r
3446 \r
3447                                 clonedParent = clonedGrandParent;\r
3448                         }\r
3449                 };\r
3450 \r
3451                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3452                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3453 \r
3454                         if (isFullySelected)\r
3455                                 return _traverseFullySelected(n, how);\r
3456 \r
3457                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3458                                 txtValue = n.nodeValue;\r
3459 \r
3460                                 if (isLeft) {\r
3461                                         offset = t[START_OFFSET];\r
3462                                         newNodeValue = txtValue.substring(offset);\r
3463                                         oldNodeValue = txtValue.substring(0, offset);\r
3464                                 } else {\r
3465                                         offset = t[END_OFFSET];\r
3466                                         newNodeValue = txtValue.substring(0, offset);\r
3467                                         oldNodeValue = txtValue.substring(offset);\r
3468                                 }\r
3469 \r
3470                                 if (how != CLONE)\r
3471                                         n.nodeValue = oldNodeValue;\r
3472 \r
3473                                 if (how == DELETE)\r
3474                                         return;\r
3475 \r
3476                                 newNode = n.cloneNode(FALSE);\r
3477                                 newNode.nodeValue = newNodeValue;\r
3478 \r
3479                                 return newNode;\r
3480                         }\r
3481 \r
3482                         if (how == DELETE)\r
3483                                 return;\r
3484 \r
3485                         return n.cloneNode(FALSE);\r
3486                 };\r
3487 \r
3488                 function _traverseFullySelected(n, how) {\r
3489                         if (how != DELETE)\r
3490                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3491 \r
3492                         n.parentNode.removeChild(n);\r
3493                 };\r
3494         };\r
3495 \r
3496         ns.Range = Range;\r
3497 })(tinymce.dom);\r
3498 \r
3499 (function() {\r
3500         function Selection(selection) {\r
3501                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3502 \r
3503                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3504                 function getRange() {\r
3505                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
3506 \r
3507                         // If selection is outside the current document just return an empty range\r
3508                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3509                         if (element.ownerDocument != dom.doc)\r
3510                                 return domRange;\r
3511 \r
3512                         // Handle control selection or text selection of a image\r
3513                         if (ieRange.item || !element.hasChildNodes()) {\r
3514                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3515                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3516 \r
3517                                 return domRange;\r
3518                         }\r
3519 \r
3520                         collapsed = selection.isCollapsed();\r
3521 \r
3522                         function findEndPoint(start) {\r
3523                                 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
3524 \r
3525                                 // Setup temp range and collapse it\r
3526                                 checkRng = ieRange.duplicate();\r
3527                                 checkRng.collapse(start);\r
3528 \r
3529                                 // Create marker and insert it at the end of the endpoints parent\r
3530                                 marker = dom.create('a');\r
3531                                 parent = checkRng.parentElement();\r
3532 \r
3533                                 // If parent doesn't have any children then set the container to that parent and the index to 0\r
3534                                 if (!parent.hasChildNodes()) {\r
3535                                         domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
3536                                         return;\r
3537                                 }\r
3538 \r
3539                                 parent.appendChild(marker);\r
3540                                 checkRng.moveToElementText(marker);\r
3541                                 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3542                                 if (position > 0) {\r
3543                                         // The position is after the end of the parent element.\r
3544                                         // This is the case where IE puts the caret to the left edge of a table.\r
3545                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
3546                                         dom.remove(marker);\r
3547                                         return;\r
3548                                 }\r
3549 \r
3550                                 // Setup node list and endIndex\r
3551                                 nodes = tinymce.grep(parent.childNodes);\r
3552                                 endIndex = nodes.length - 1;\r
3553                                 // Perform a binary search for the position\r
3554                                 while (startIndex <= endIndex) {\r
3555                                         index = Math.floor((startIndex + endIndex) / 2);\r
3556 \r
3557                                         // Insert marker and check it's position relative to the selection\r
3558                                         parent.insertBefore(marker, nodes[index]);\r
3559                                         checkRng.moveToElementText(marker);\r
3560                                         position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3561                                         if (position > 0) {\r
3562                                                 // Marker is to the right\r
3563                                                 startIndex = index + 1;\r
3564                                         } else if (position < 0) {\r
3565                                                 // Marker is to the left\r
3566                                                 endIndex = index - 1;\r
3567                                         } else {\r
3568                                                 // Maker is where we are\r
3569                                                 found = true;\r
3570                                                 break;\r
3571                                         }\r
3572                                 }\r
3573 \r
3574                                 // Setup container\r
3575                                 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
3576 \r
3577                                 // Handle element selection\r
3578                                 if (container.nodeType == 1) {\r
3579                                         dom.remove(marker);\r
3580 \r
3581                                         // Find offset and container\r
3582                                         offset = dom.nodeIndex(container);\r
3583                                         container = container.parentNode;\r
3584 \r
3585                                         // Move the offset if we are setting the end or the position is after an element\r
3586                                         if (!start || index > 0)\r
3587                                                 offset++;\r
3588                                 } else {\r
3589                                         // Calculate offset within text node\r
3590                                         if (position > 0 || index == 0) {\r
3591                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3592                                                 offset = checkRng.text.length;\r
3593                                         } else {\r
3594                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3595                                                 offset = container.nodeValue.length - checkRng.text.length;\r
3596                                         }\r
3597 \r
3598                                         dom.remove(marker);\r
3599                                 }\r
3600 \r
3601                                 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
3602                         };\r
3603 \r
3604                         // Find start point\r
3605                         findEndPoint(true);\r
3606 \r
3607                         // Find end point if needed\r
3608                         if (!collapsed)\r
3609                                 findEndPoint();\r
3610 \r
3611                         return domRange;\r
3612                 };\r
3613 \r
3614                 this.addRange = function(rng) {\r
3615                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
3616 \r
3617                         function setEndPoint(start) {\r
3618                                 var container, offset, marker, tmpRng, nodes;\r
3619 \r
3620                                 marker = dom.create('a');\r
3621                                 container = start ? startContainer : endContainer;\r
3622                                 offset = start ? startOffset : endOffset;\r
3623                                 tmpRng = ieRng.duplicate();\r
3624 \r
3625                                 if (container == doc) {\r
3626                                         container = body;\r
3627                                         offset = 0;\r
3628                                 }\r
3629 \r
3630                                 if (container.nodeType == 3) {\r
3631                                         container.parentNode.insertBefore(marker, container);\r
3632                                         tmpRng.moveToElementText(marker);\r
3633                                         tmpRng.moveStart('character', offset);\r
3634                                         dom.remove(marker);\r
3635                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3636                                 } else {\r
3637                                         nodes = container.childNodes;\r
3638 \r
3639                                         if (nodes.length) {\r
3640                                                 if (offset >= nodes.length) {\r
3641                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
3642                                                 } else {\r
3643                                                         container.insertBefore(marker, nodes[offset]);\r
3644                                                 }\r
3645 \r
3646                                                 tmpRng.moveToElementText(marker);\r
3647                                         } else {\r
3648                                                 // Empty node selection for example <div>|</div>\r
3649                                                 marker = doc.createTextNode(invisibleChar);\r
3650                                                 container.appendChild(marker);\r
3651                                                 tmpRng.moveToElementText(marker.parentNode);\r
3652                                                 tmpRng.collapse(TRUE);\r
3653                                         }\r
3654 \r
3655                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3656                                         dom.remove(marker);\r
3657                                 }\r
3658                         }\r
3659 \r
3660                         // Destroy cached range\r
3661                         this.destroy();\r
3662 \r
3663                         // Setup some shorter versions\r
3664                         startContainer = rng.startContainer;\r
3665                         startOffset = rng.startOffset;\r
3666                         endContainer = rng.endContainer;\r
3667                         endOffset = rng.endOffset;\r
3668                         ieRng = body.createTextRange();\r
3669 \r
3670                         // If single element selection then try making a control selection out of it\r
3671                         if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
3672                                 if (startOffset == endOffset - 1) {\r
3673                                         try {\r
3674                                                 ctrlRng = body.createControlRange();\r
3675                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
3676                                                 ctrlRng.select();\r
3677                                                 ctrlRng.scrollIntoView();\r
3678                                                 return;\r
3679                                         } catch (ex) {\r
3680                                                 // Ignore\r
3681                                         }\r
3682                                 }\r
3683                         }\r
3684 \r
3685                         // Set start/end point of selection\r
3686                         setEndPoint(true);\r
3687                         setEndPoint();\r
3688 \r
3689                         // Select the new range and scroll it into view\r
3690                         ieRng.select();\r
3691                         ieRng.scrollIntoView();\r
3692                 };\r
3693 \r
3694                 this.getRangeAt = function() {\r
3695                         // Setup new range if the cache is empty\r
3696                         if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
3697                                 range = getRange();\r
3698 \r
3699                                 // Store away text range for next call\r
3700                                 lastIERng = selection.getRng();\r
3701                         }\r
3702 \r
3703                         // IE will say that the range is equal then produce an invalid argument exception\r
3704                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3705                         // This hack will invalidate the range cache if the exception occurs\r
3706                         try {\r
3707                                 range.startContainer.nextSibling;\r
3708                         } catch (ex) {\r
3709                                 range = getRange();\r
3710                                 lastIERng = null;\r
3711                         }\r
3712 \r
3713                         // Return cached range\r
3714                         return range;\r
3715                 };\r
3716 \r
3717                 this.destroy = function() {\r
3718                         // Destroy cached range and last IE range to avoid memory leaks\r
3719                         lastIERng = range = null;\r
3720                 };\r
3721 \r
3722                 // 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
3723                 if (selection.dom.boxModel) {\r
3724                         (function() {\r
3725                                 var doc = dom.doc, body = doc.body, started, startRng;\r
3726 \r
3727                                 // Make HTML element unselectable since we are going to handle selection by hand\r
3728                                 doc.documentElement.unselectable = TRUE;\r
3729 \r
3730                                 // Return range from point or null if it failed\r
3731                                 function rngFromPoint(x, y) {\r
3732                                         var rng = body.createTextRange();\r
3733 \r
3734                                         try {\r
3735                                                 rng.moveToPoint(x, y);\r
3736                                         } catch (ex) {\r
3737                                                 // IE sometimes throws and exception, so lets just ignore it\r
3738                                                 rng = null;\r
3739                                         }\r
3740 \r
3741                                         return rng;\r
3742                                 };\r
3743 \r
3744                                 // Fires while the selection is changing\r
3745                                 function selectionChange(e) {\r
3746                                         var pointRng;\r
3747 \r
3748                                         // Check if the button is down or not\r
3749                                         if (e.button) {\r
3750                                                 // Create range from mouse position\r
3751                                                 pointRng = rngFromPoint(e.x, e.y);\r
3752 \r
3753                                                 if (pointRng) {\r
3754                                                         // Check if pointRange is before/after selection then change the endPoint\r
3755                                                         if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
3756                                                                 pointRng.setEndPoint('StartToStart', startRng);\r
3757                                                         else\r
3758                                                                 pointRng.setEndPoint('EndToEnd', startRng);\r
3759 \r
3760                                                         pointRng.select();\r
3761                                                 }\r
3762                                         } else\r
3763                                                 endSelection();\r
3764                                 }\r
3765 \r
3766                                 // Removes listeners\r
3767                                 function endSelection() {\r
3768                                         dom.unbind(doc, 'mouseup', endSelection);\r
3769                                         dom.unbind(doc, 'mousemove', selectionChange);\r
3770                                         started = 0;\r
3771                                 };\r
3772 \r
3773                                 // Detect when user selects outside BODY\r
3774                                 dom.bind(doc, 'mousedown', function(e) {\r
3775                                         if (e.target.nodeName === 'HTML') {\r
3776                                                 if (started)\r
3777                                                         endSelection();\r
3778 \r
3779                                                 started = 1;\r
3780 \r
3781                                                 // Setup start position\r
3782                                                 startRng = rngFromPoint(e.x, e.y);\r
3783                                                 if (startRng) {\r
3784                                                         // Listen for selection change events\r
3785                                                         dom.bind(doc, 'mouseup', endSelection);\r
3786                                                         dom.bind(doc, 'mousemove', selectionChange);\r
3787 \r
3788                                                         startRng.select();\r
3789                                                 }\r
3790                                         }\r
3791                                 });\r
3792                         })();\r
3793                 }\r
3794         };\r
3795 \r
3796         // Expose the selection object\r
3797         tinymce.dom.TridentSelection = Selection;\r
3798 })();\r
3799 \r
3800 \r
3801 (function(tinymce) {\r
3802         // Shorten names\r
3803         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
3804 \r
3805         tinymce.create('tinymce.dom.EventUtils', {\r
3806                 EventUtils : function() {\r
3807                         this.inits = [];\r
3808                         this.events = [];\r
3809                 },\r
3810 \r
3811                 add : function(o, n, f, s) {\r
3812                         var cb, t = this, el = t.events, r;\r
3813 \r
3814                         if (n instanceof Array) {\r
3815                                 r = [];\r
3816 \r
3817                                 each(n, function(n) {\r
3818                                         r.push(t.add(o, n, f, s));\r
3819                                 });\r
3820 \r
3821                                 return r;\r
3822                         }\r
3823 \r
3824                         // Handle array\r
3825                         if (o && o.hasOwnProperty && o instanceof Array) {\r
3826                                 r = [];\r
3827 \r
3828                                 each(o, function(o) {\r
3829                                         o = DOM.get(o);\r
3830                                         r.push(t.add(o, n, f, s));\r
3831                                 });\r
3832 \r
3833                                 return r;\r
3834                         }\r
3835 \r
3836                         o = DOM.get(o);\r
3837 \r
3838                         if (!o)\r
3839                                 return;\r
3840 \r
3841                         // Setup event callback\r
3842                         cb = function(e) {\r
3843                                 // Is all events disabled\r
3844                                 if (t.disabled)\r
3845                                         return;\r
3846 \r
3847                                 e = e || window.event;\r
3848 \r
3849                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
3850                                 if (e && isIE) {\r
3851                                         if (!e.target)\r
3852                                                 e.target = e.srcElement;\r
3853 \r
3854                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
3855                                         tinymce.extend(e, t._stoppers);\r
3856                                 }\r
3857 \r
3858                                 if (!s)\r
3859                                         return f(e);\r
3860 \r
3861                                 return f.call(s, e);\r
3862                         };\r
3863 \r
3864                         if (n == 'unload') {\r
3865                                 tinymce.unloads.unshift({func : cb});\r
3866                                 return cb;\r
3867                         }\r
3868 \r
3869                         if (n == 'init') {\r
3870                                 if (t.domLoaded)\r
3871                                         cb();\r
3872                                 else\r
3873                                         t.inits.push(cb);\r
3874 \r
3875                                 return cb;\r
3876                         }\r
3877 \r
3878                         // Store away listener reference\r
3879                         el.push({\r
3880                                 obj : o,\r
3881                                 name : n,\r
3882                                 func : f,\r
3883                                 cfunc : cb,\r
3884                                 scope : s\r
3885                         });\r
3886 \r
3887                         t._add(o, n, cb);\r
3888 \r
3889                         return f;\r
3890                 },\r
3891 \r
3892                 remove : function(o, n, f) {\r
3893                         var t = this, a = t.events, s = false, r;\r
3894 \r
3895                         // Handle array\r
3896                         if (o && o.hasOwnProperty && o instanceof Array) {\r
3897                                 r = [];\r
3898 \r
3899                                 each(o, function(o) {\r
3900                                         o = DOM.get(o);\r
3901                                         r.push(t.remove(o, n, f));\r
3902                                 });\r
3903 \r
3904                                 return r;\r
3905                         }\r
3906 \r
3907                         o = DOM.get(o);\r
3908 \r
3909                         each(a, function(e, i) {\r
3910                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
3911                                         a.splice(i, 1);\r
3912                                         t._remove(o, n, e.cfunc);\r
3913                                         s = true;\r
3914                                         return false;\r
3915                                 }\r
3916                         });\r
3917 \r
3918                         return s;\r
3919                 },\r
3920 \r
3921                 clear : function(o) {\r
3922                         var t = this, a = t.events, i, e;\r
3923 \r
3924                         if (o) {\r
3925                                 o = DOM.get(o);\r
3926 \r
3927                                 for (i = a.length - 1; i >= 0; i--) {\r
3928                                         e = a[i];\r
3929 \r
3930                                         if (e.obj === o) {\r
3931                                                 t._remove(e.obj, e.name, e.cfunc);\r
3932                                                 e.obj = e.cfunc = null;\r
3933                                                 a.splice(i, 1);\r
3934                                         }\r
3935                                 }\r
3936                         }\r
3937                 },\r
3938 \r
3939                 cancel : function(e) {\r
3940                         if (!e)\r
3941                                 return false;\r
3942 \r
3943                         this.stop(e);\r
3944 \r
3945                         return this.prevent(e);\r
3946                 },\r
3947 \r
3948                 stop : function(e) {\r
3949                         if (e.stopPropagation)\r
3950                                 e.stopPropagation();\r
3951                         else\r
3952                                 e.cancelBubble = true;\r
3953 \r
3954                         return false;\r
3955                 },\r
3956 \r
3957                 prevent : function(e) {\r
3958                         if (e.preventDefault)\r
3959                                 e.preventDefault();\r
3960                         else\r
3961                                 e.returnValue = false;\r
3962 \r
3963                         return false;\r
3964                 },\r
3965 \r
3966                 destroy : function() {\r
3967                         var t = this;\r
3968 \r
3969                         each(t.events, function(e, i) {\r
3970                                 t._remove(e.obj, e.name, e.cfunc);\r
3971                                 e.obj = e.cfunc = null;\r
3972                         });\r
3973 \r
3974                         t.events = [];\r
3975                         t = null;\r
3976                 },\r
3977 \r
3978                 _add : function(o, n, f) {\r
3979                         if (o.attachEvent)\r
3980                                 o.attachEvent('on' + n, f);\r
3981                         else if (o.addEventListener)\r
3982                                 o.addEventListener(n, f, false);\r
3983                         else\r
3984                                 o['on' + n] = f;\r
3985                 },\r
3986 \r
3987                 _remove : function(o, n, f) {\r
3988                         if (o) {\r
3989                                 try {\r
3990                                         if (o.detachEvent)\r
3991                                                 o.detachEvent('on' + n, f);\r
3992                                         else if (o.removeEventListener)\r
3993                                                 o.removeEventListener(n, f, false);\r
3994                                         else\r
3995                                                 o['on' + n] = null;\r
3996                                 } catch (ex) {\r
3997                                         // Might fail with permission denined on IE so we just ignore that\r
3998                                 }\r
3999                         }\r
4000                 },\r
4001 \r
4002                 _pageInit : function(win) {\r
4003                         var t = this;\r
4004 \r
4005                         // Keep it from running more than once\r
4006                         if (t.domLoaded)\r
4007                                 return;\r
4008 \r
4009                         t.domLoaded = true;\r
4010 \r
4011                         each(t.inits, function(c) {\r
4012                                 c();\r
4013                         });\r
4014 \r
4015                         t.inits = [];\r
4016                 },\r
4017 \r
4018                 _wait : function(win) {\r
4019                         var t = this, doc = win.document;\r
4020 \r
4021                         // No need since the document is already loaded\r
4022                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4023                                 t.domLoaded = 1;\r
4024                                 return;\r
4025                         }\r
4026 \r
4027                         // Use IE method\r
4028                         if (doc.attachEvent) {\r
4029                                 doc.attachEvent("onreadystatechange", function() {\r
4030                                         if (doc.readyState === "complete") {\r
4031                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4032                                                 t._pageInit(win);\r
4033                                         }\r
4034                                 });\r
4035 \r
4036                                 if (doc.documentElement.doScroll && win == win.top) {\r
4037                                         (function() {\r
4038                                                 if (t.domLoaded)\r
4039                                                         return;\r
4040 \r
4041                                                 try {\r
4042                                                         // If IE is used, use the trick by Diego Perini\r
4043                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4044                                                         doc.documentElement.doScroll("left");\r
4045                                                 } catch (ex) {\r
4046                                                         setTimeout(arguments.callee, 0);\r
4047                                                         return;\r
4048                                                 }\r
4049 \r
4050                                                 t._pageInit(win);\r
4051                                         })();\r
4052                                 }\r
4053                         } else if (doc.addEventListener) {\r
4054                                 t._add(win, 'DOMContentLoaded', function() {\r
4055                                         t._pageInit(win);\r
4056                                 });\r
4057                         }\r
4058 \r
4059                         t._add(win, 'load', function() {\r
4060                                 t._pageInit(win);\r
4061                         });\r
4062                 },\r
4063 \r
4064                 _stoppers : {\r
4065                         preventDefault :  function() {\r
4066                                 this.returnValue = false;\r
4067                         },\r
4068 \r
4069                         stopPropagation : function() {\r
4070                                 this.cancelBubble = true;\r
4071                         }\r
4072                 }\r
4073         });\r
4074 \r
4075         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4076 \r
4077         // Dispatch DOM content loaded event for IE and Safari\r
4078         Event._wait(window);\r
4079 \r
4080         tinymce.addUnload(function() {\r
4081                 Event.destroy();\r
4082         });\r
4083 })(tinymce);\r
4084 \r
4085 (function(tinymce) {\r
4086         tinymce.dom.Element = function(id, settings) {\r
4087                 var t = this, dom, el;\r
4088 \r
4089                 t.settings = settings = settings || {};\r
4090                 t.id = id;\r
4091                 t.dom = dom = settings.dom || tinymce.DOM;\r
4092 \r
4093                 // Only IE leaks DOM references, this is a lot faster\r
4094                 if (!tinymce.isIE)\r
4095                         el = dom.get(t.id);\r
4096 \r
4097                 tinymce.each(\r
4098                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4099                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4100                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4101                                 'isHidden,setHTML,get').split(/,/)\r
4102                         , function(k) {\r
4103                                 t[k] = function() {\r
4104                                         var a = [id], i;\r
4105 \r
4106                                         for (i = 0; i < arguments.length; i++)\r
4107                                                 a.push(arguments[i]);\r
4108 \r
4109                                         a = dom[k].apply(dom, a);\r
4110                                         t.update(k);\r
4111 \r
4112                                         return a;\r
4113                                 };\r
4114                 });\r
4115 \r
4116                 tinymce.extend(t, {\r
4117                         on : function(n, f, s) {\r
4118                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
4119                         },\r
4120 \r
4121                         getXY : function() {\r
4122                                 return {\r
4123                                         x : parseInt(t.getStyle('left')),\r
4124                                         y : parseInt(t.getStyle('top'))\r
4125                                 };\r
4126                         },\r
4127 \r
4128                         getSize : function() {\r
4129                                 var n = dom.get(t.id);\r
4130 \r
4131                                 return {\r
4132                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
4133                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
4134                                 };\r
4135                         },\r
4136 \r
4137                         moveTo : function(x, y) {\r
4138                                 t.setStyles({left : x, top : y});\r
4139                         },\r
4140 \r
4141                         moveBy : function(x, y) {\r
4142                                 var p = t.getXY();\r
4143 \r
4144                                 t.moveTo(p.x + x, p.y + y);\r
4145                         },\r
4146 \r
4147                         resizeTo : function(w, h) {\r
4148                                 t.setStyles({width : w, height : h});\r
4149                         },\r
4150 \r
4151                         resizeBy : function(w, h) {\r
4152                                 var s = t.getSize();\r
4153 \r
4154                                 t.resizeTo(s.w + w, s.h + h);\r
4155                         },\r
4156 \r
4157                         update : function(k) {\r
4158                                 var b;\r
4159 \r
4160                                 if (tinymce.isIE6 && settings.blocker) {\r
4161                                         k = k || '';\r
4162 \r
4163                                         // Ignore getters\r
4164                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4165                                                 return;\r
4166 \r
4167                                         // Remove blocker on remove\r
4168                                         if (k == 'remove') {\r
4169                                                 dom.remove(t.blocker);\r
4170                                                 return;\r
4171                                         }\r
4172 \r
4173                                         if (!t.blocker) {\r
4174                                                 t.blocker = dom.uniqueId();\r
4175                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
4176                                                 dom.setStyle(b, 'opacity', 0);\r
4177                                         } else\r
4178                                                 b = dom.get(t.blocker);\r
4179 \r
4180                                         dom.setStyles(b, {\r
4181                                                 left : t.getStyle('left', 1),\r
4182                                                 top : t.getStyle('top', 1),\r
4183                                                 width : t.getStyle('width', 1),\r
4184                                                 height : t.getStyle('height', 1),\r
4185                                                 display : t.getStyle('display', 1),\r
4186                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
4187                                         });\r
4188                                 }\r
4189                         }\r
4190                 });\r
4191         };\r
4192 })(tinymce);\r
4193 \r
4194 (function(tinymce) {\r
4195         function trimNl(s) {\r
4196                 return s.replace(/[\n\r]+/g, '');\r
4197         };\r
4198 \r
4199         // Shorten names\r
4200         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
4201 \r
4202         tinymce.create('tinymce.dom.Selection', {\r
4203                 Selection : function(dom, win, serializer) {\r
4204                         var t = this;\r
4205 \r
4206                         t.dom = dom;\r
4207                         t.win = win;\r
4208                         t.serializer = serializer;\r
4209 \r
4210                         // Add events\r
4211                         each([\r
4212                                 'onBeforeSetContent',\r
4213                                 'onBeforeGetContent',\r
4214                                 'onSetContent',\r
4215                                 'onGetContent'\r
4216                         ], function(e) {\r
4217                                 t[e] = new tinymce.util.Dispatcher(t);\r
4218                         });\r
4219 \r
4220                         // No W3C Range support\r
4221                         if (!t.win.getSelection)\r
4222                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
4223 \r
4224                         // Prevent leaks\r
4225                         tinymce.addUnload(t.destroy, t);\r
4226                 },\r
4227 \r
4228                 getContent : function(s) {\r
4229                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
4230 \r
4231                         s = s || {};\r
4232                         wb = wa = '';\r
4233                         s.get = true;\r
4234                         s.format = s.format || 'html';\r
4235                         t.onBeforeGetContent.dispatch(t, s);\r
4236 \r
4237                         if (s.format == 'text')\r
4238                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
4239 \r
4240                         if (r.cloneContents) {\r
4241                                 n = r.cloneContents();\r
4242 \r
4243                                 if (n)\r
4244                                         e.appendChild(n);\r
4245                         } else if (is(r.item) || is(r.htmlText))\r
4246                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
4247                         else\r
4248                                 e.innerHTML = r.toString();\r
4249 \r
4250                         // Keep whitespace before and after\r
4251                         if (/^\s/.test(e.innerHTML))\r
4252                                 wb = ' ';\r
4253 \r
4254                         if (/\s+$/.test(e.innerHTML))\r
4255                                 wa = ' ';\r
4256 \r
4257                         s.getInner = true;\r
4258 \r
4259                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
4260                         t.onGetContent.dispatch(t, s);\r
4261 \r
4262                         return s.content;\r
4263                 },\r
4264 \r
4265                 setContent : function(h, s) {\r
4266                         var t = this, r = t.getRng(), c, d = t.win.document;\r
4267 \r
4268                         s = s || {format : 'html'};\r
4269                         s.set = true;\r
4270                         h = s.content = t.dom.processHTML(h);\r
4271 \r
4272                         // Dispatch before set content event\r
4273                         t.onBeforeSetContent.dispatch(t, s);\r
4274                         h = s.content;\r
4275 \r
4276                         if (r.insertNode) {\r
4277                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
4278                                 h += '<span id="__caret">_</span>';\r
4279 \r
4280                                 // Delete and insert new node\r
4281                                 \r
4282                                 if (r.startContainer == d && r.endContainer ==  d) {\r
4283                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
4284                                         d.body.innerHTML = h;\r
4285                                 } else {\r
4286                                         r.deleteContents();\r
4287                                         if (d.body.childNodes.length == 0) {\r
4288                                                 d.body.innerHTML = h;\r
4289                                         } else {\r
4290                                                 r.insertNode(r.createContextualFragment(h));\r
4291                                         }\r
4292                                 }\r
4293 \r
4294                                 // Move to caret marker\r
4295                                 c = t.dom.get('__caret');\r
4296                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
4297                                 r = d.createRange();\r
4298                                 r.setStartBefore(c);\r
4299                                 r.setEndBefore(c);\r
4300                                 t.setRng(r);\r
4301 \r
4302                                 // Remove the caret position\r
4303                                 t.dom.remove('__caret');\r
4304                         } else {\r
4305                                 if (r.item) {\r
4306                                         // Delete content and get caret text selection\r
4307                                         d.execCommand('Delete', false, null);\r
4308                                         r = t.getRng();\r
4309                                 }\r
4310 \r
4311                                 r.pasteHTML(h);\r
4312                         }\r
4313 \r
4314                         // Dispatch set content event\r
4315                         t.onSetContent.dispatch(t, s);\r
4316                 },\r
4317 \r
4318                 getStart : function() {\r
4319                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
4320 \r
4321                         if (rng.duplicate || rng.item) {\r
4322                                 // Control selection, return first item\r
4323                                 if (rng.item)\r
4324                                         return rng.item(0);\r
4325 \r
4326                                 // Get start element\r
4327                                 checkRng = rng.duplicate();\r
4328                                 checkRng.collapse(1);\r
4329                                 startElement = checkRng.parentElement();\r
4330 \r
4331                                 // Check if range parent is inside the start element, then return the inner parent element\r
4332                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
4333                                 parentElement = node = rng.parentElement();\r
4334                                 while (node = node.parentNode) {\r
4335                                         if (node == startElement) {\r
4336                                                 startElement = parentElement;\r
4337                                                 break;\r
4338                                         }\r
4339                                 }\r
4340 \r
4341                                 // If start element is body element try to move to the first child if it exists\r
4342                                 if (startElement && startElement.nodeName == 'BODY')\r
4343                                         return startElement.firstChild || startElement;\r
4344 \r
4345                                 return startElement;\r
4346                         } else {\r
4347                                 startElement = rng.startContainer;\r
4348 \r
4349                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
4350                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
4351 \r
4352                                 if (startElement && startElement.nodeType == 3)\r
4353                                         return startElement.parentNode;\r
4354 \r
4355                                 return startElement;\r
4356                         }\r
4357                 },\r
4358 \r
4359                 getEnd : function() {\r
4360                         var t = this, r = t.getRng(), e, eo;\r
4361 \r
4362                         if (r.duplicate || r.item) {\r
4363                                 if (r.item)\r
4364                                         return r.item(0);\r
4365 \r
4366                                 r = r.duplicate();\r
4367                                 r.collapse(0);\r
4368                                 e = r.parentElement();\r
4369 \r
4370                                 if (e && e.nodeName == 'BODY')\r
4371                                         return e.lastChild || e;\r
4372 \r
4373                                 return e;\r
4374                         } else {\r
4375                                 e = r.endContainer;\r
4376                                 eo = r.endOffset;\r
4377 \r
4378                                 if (e.nodeType == 1 && e.hasChildNodes())\r
4379                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
4380 \r
4381                                 if (e && e.nodeType == 3)\r
4382                                         return e.parentNode;\r
4383 \r
4384                                 return e;\r
4385                         }\r
4386                 },\r
4387 \r
4388                 getBookmark : function(type, normalized) {\r
4389                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
4390 \r
4391                         function findIndex(name, element) {\r
4392                                 var index = 0;\r
4393 \r
4394                                 each(dom.select(name), function(node, i) {\r
4395                                         if (node == element)\r
4396                                                 index = i;\r
4397                                 });\r
4398 \r
4399                                 return index;\r
4400                         };\r
4401 \r
4402                         if (type == 2) {\r
4403                                 function getLocation() {\r
4404                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
4405 \r
4406                                         function getPoint(rng, start) {\r
4407                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
4408                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
4409 \r
4410                                                 if (container.nodeType == 3) {\r
4411                                                         if (normalized) {\r
4412                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
4413                                                                         offset += node.nodeValue.length;\r
4414                                                         }\r
4415 \r
4416                                                         point.push(offset);\r
4417                                                 } else {\r
4418                                                         childNodes = container.childNodes;\r
4419 \r
4420                                                         if (offset >= childNodes.length && childNodes.length) {\r
4421                                                                 after = 1;\r
4422                                                                 offset = Math.max(0, childNodes.length - 1);\r
4423                                                         }\r
4424 \r
4425                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
4426                                                 }\r
4427 \r
4428                                                 for (; container && container != root; container = container.parentNode)\r
4429                                                         point.push(t.dom.nodeIndex(container, normalized));\r
4430 \r
4431                                                 return point;\r
4432                                         };\r
4433 \r
4434                                         bookmark.start = getPoint(rng, true);\r
4435 \r
4436                                         if (!t.isCollapsed())\r
4437                                                 bookmark.end = getPoint(rng);\r
4438 \r
4439                                         return bookmark;\r
4440                                 };\r
4441 \r
4442                                 return getLocation();\r
4443                         }\r
4444 \r
4445                         // Handle simple range\r
4446                         if (type)\r
4447                                 return {rng : t.getRng()};\r
4448 \r
4449                         rng = t.getRng();\r
4450                         id = dom.uniqueId();\r
4451                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
4452                         styles = 'overflow:hidden;line-height:0px';\r
4453 \r
4454                         // Explorer method\r
4455                         if (rng.duplicate || rng.item) {\r
4456                                 // Text selection\r
4457                                 if (!rng.item) {\r
4458                                         rng2 = rng.duplicate();\r
4459 \r
4460                                         // Insert start marker\r
4461                                         rng.collapse();\r
4462                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
4463 \r
4464                                         // Insert end marker\r
4465                                         if (!collapsed) {\r
4466                                                 rng2.collapse(false);\r
4467                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
4468                                         }\r
4469                                 } else {\r
4470                                         // Control selection\r
4471                                         element = rng.item(0);\r
4472                                         name = element.nodeName;\r
4473 \r
4474                                         return {name : name, index : findIndex(name, element)};\r
4475                                 }\r
4476                         } else {\r
4477                                 element = t.getNode();\r
4478                                 name = element.nodeName;\r
4479                                 if (name == 'IMG')\r
4480                                         return {name : name, index : findIndex(name, element)};\r
4481 \r
4482                                 // W3C method\r
4483                                 rng2 = rng.cloneRange();\r
4484 \r
4485                                 // Insert end marker\r
4486                                 if (!collapsed) {\r
4487                                         rng2.collapse(false);\r
4488                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
4489                                 }\r
4490 \r
4491                                 rng.collapse(true);\r
4492                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
4493                         }\r
4494 \r
4495                         t.moveToBookmark({id : id, keep : 1});\r
4496 \r
4497                         return {id : id};\r
4498                 },\r
4499 \r
4500                 moveToBookmark : function(bookmark) {\r
4501                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
4502 \r
4503                         // Clear selection cache\r
4504                         if (t.tridentSel)\r
4505                                 t.tridentSel.destroy();\r
4506 \r
4507                         if (bookmark) {\r
4508                                 if (bookmark.start) {\r
4509                                         rng = dom.createRng();\r
4510                                         root = dom.getRoot();\r
4511 \r
4512                                         function setEndPoint(start) {\r
4513                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
4514 \r
4515                                                 if (point) {\r
4516                                                         // Find container node\r
4517                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
4518                                                                 children = node.childNodes;\r
4519 \r
4520                                                                 if (children.length)\r
4521                                                                         node = children[point[i]];\r
4522                                                         }\r
4523 \r
4524                                                         // Set offset within container node\r
4525                                                         if (start)\r
4526                                                                 rng.setStart(node, point[0]);\r
4527                                                         else\r
4528                                                                 rng.setEnd(node, point[0]);\r
4529                                                 }\r
4530                                         };\r
4531 \r
4532                                         setEndPoint(true);\r
4533                                         setEndPoint();\r
4534 \r
4535                                         t.setRng(rng);\r
4536                                 } else if (bookmark.id) {\r
4537                                         function restoreEndPoint(suffix) {\r
4538                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
4539 \r
4540                                                 if (marker) {\r
4541                                                         node = marker.parentNode;\r
4542 \r
4543                                                         if (suffix == 'start') {\r
4544                                                                 if (!keep) {\r
4545                                                                         idx = dom.nodeIndex(marker);\r
4546                                                                 } else {\r
4547                                                                         node = marker.firstChild;\r
4548                                                                         idx = 1;\r
4549                                                                 }\r
4550 \r
4551                                                                 startContainer = endContainer = node;\r
4552                                                                 startOffset = endOffset = idx;\r
4553                                                         } else {\r
4554                                                                 if (!keep) {\r
4555                                                                         idx = dom.nodeIndex(marker);\r
4556                                                                 } else {\r
4557                                                                         node = marker.firstChild;\r
4558                                                                         idx = 1;\r
4559                                                                 }\r
4560 \r
4561                                                                 endContainer = node;\r
4562                                                                 endOffset = idx;\r
4563                                                         }\r
4564 \r
4565                                                         if (!keep) {\r
4566                                                                 prev = marker.previousSibling;\r
4567                                                                 next = marker.nextSibling;\r
4568 \r
4569                                                                 // Remove all marker text nodes\r
4570                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
4571                                                                         if (node.nodeType == 3)\r
4572                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
4573                                                                 });\r
4574 \r
4575                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
4576                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
4577                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
4578                                                                         dom.remove(marker, 1);\r
4579 \r
4580                                                                 // If siblings are text nodes then merge them\r
4581                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) {\r
4582                                                                         idx = prev.nodeValue.length;\r
4583                                                                         prev.appendData(next.nodeValue);\r
4584                                                                         dom.remove(next);\r
4585 \r
4586                                                                         if (suffix == 'start') {\r
4587                                                                                 startContainer = endContainer = prev;\r
4588                                                                                 startOffset = endOffset = idx;\r
4589                                                                         } else {\r
4590                                                                                 endContainer = prev;\r
4591                                                                                 endOffset = idx;\r
4592                                                                         }\r
4593                                                                 }\r
4594                                                         }\r
4595                                                 }\r
4596                                         };\r
4597 \r
4598                                         function addBogus(node) {\r
4599                                                 // Adds a bogus BR element for empty block elements\r
4600                                                 // on non IE browsers just to have a place to put the caret\r
4601                                                 if (!isIE && dom.isBlock(node) && !node.innerHTML)\r
4602                                                         node.innerHTML = '<br _mce_bogus="1" />';\r
4603 \r
4604                                                 return node;\r
4605                                         };\r
4606 \r
4607                                         // Restore start/end points\r
4608                                         restoreEndPoint('start');\r
4609                                         restoreEndPoint('end');\r
4610 \r
4611                                         rng = dom.createRng();\r
4612                                         rng.setStart(addBogus(startContainer), startOffset);\r
4613                                         rng.setEnd(addBogus(endContainer), endOffset);\r
4614                                         t.setRng(rng);\r
4615                                 } else if (bookmark.name) {\r
4616                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
4617                                 } else if (bookmark.rng)\r
4618                                         t.setRng(bookmark.rng);\r
4619                         }\r
4620                 },\r
4621 \r
4622                 select : function(node, content) {\r
4623                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
4624 \r
4625                         idx = dom.nodeIndex(node);\r
4626                         rng.setStart(node.parentNode, idx);\r
4627                         rng.setEnd(node.parentNode, idx + 1);\r
4628 \r
4629                         // Find first/last text node or BR element\r
4630                         if (content) {\r
4631                                 function setPoint(node, start) {\r
4632                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
4633 \r
4634                                         do {\r
4635                                                 // Text node\r
4636                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
4637                                                         if (start)\r
4638                                                                 rng.setStart(node, 0);\r
4639                                                         else\r
4640                                                                 rng.setEnd(node, node.nodeValue.length);\r
4641 \r
4642                                                         return;\r
4643                                                 }\r
4644 \r
4645                                                 // BR element\r
4646                                                 if (node.nodeName == 'BR') {\r
4647                                                         if (start)\r
4648                                                                 rng.setStartBefore(node);\r
4649                                                         else\r
4650                                                                 rng.setEndBefore(node);\r
4651 \r
4652                                                         return;\r
4653                                                 }\r
4654                                         } while (node = (start ? walker.next() : walker.prev()));\r
4655                                 };\r
4656 \r
4657                                 setPoint(node, 1);\r
4658                                 setPoint(node);\r
4659                         }\r
4660 \r
4661                         t.setRng(rng);\r
4662 \r
4663                         return node;\r
4664                 },\r
4665 \r
4666                 isCollapsed : function() {\r
4667                         var t = this, r = t.getRng(), s = t.getSel();\r
4668 \r
4669                         if (!r || r.item)\r
4670                                 return false;\r
4671 \r
4672                         if (r.compareEndPoints)\r
4673                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
4674 \r
4675                         return !s || r.collapsed;\r
4676                 },\r
4677 \r
4678                 collapse : function(b) {\r
4679                         var t = this, r = t.getRng(), n;\r
4680 \r
4681                         // Control range on IE\r
4682                         if (r.item) {\r
4683                                 n = r.item(0);\r
4684                                 r = this.win.document.body.createTextRange();\r
4685                                 r.moveToElementText(n);\r
4686                         }\r
4687 \r
4688                         r.collapse(!!b);\r
4689                         t.setRng(r);\r
4690                 },\r
4691 \r
4692                 getSel : function() {\r
4693                         var t = this, w = this.win;\r
4694 \r
4695                         return w.getSelection ? w.getSelection() : w.document.selection;\r
4696                 },\r
4697 \r
4698                 getRng : function(w3c) {\r
4699                         var t = this, s, r;\r
4700 \r
4701                         // Found tridentSel object then we need to use that one\r
4702                         if (w3c && t.tridentSel)\r
4703                                 return t.tridentSel.getRangeAt(0);\r
4704 \r
4705                         try {\r
4706                                 if (s = t.getSel())\r
4707                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
4708                         } catch (ex) {\r
4709                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
4710                         }\r
4711 \r
4712                         // No range found then create an empty one\r
4713                         // This can occur when the editor is placed in a hidden container element on Gecko\r
4714                         // Or on IE when there was an exception\r
4715                         if (!r)\r
4716                                 r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();\r
4717 \r
4718                         if (t.selectedRange && t.explicitRange) {\r
4719                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
4720                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
4721                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
4722                                         r = t.explicitRange;\r
4723                                 } else {\r
4724                                         t.selectedRange = null;\r
4725                                         t.explicitRange = null;\r
4726                                 }\r
4727                         }\r
4728                         return r;\r
4729                 },\r
4730 \r
4731                 setRng : function(r) {\r
4732                         var s, t = this;\r
4733                         \r
4734                         if (!t.tridentSel) {\r
4735                                 s = t.getSel();\r
4736 \r
4737                                 if (s) {\r
4738                                         t.explicitRange = r;\r
4739                                         s.removeAllRanges();\r
4740                                         s.addRange(r);\r
4741                                         t.selectedRange = s.getRangeAt(0);\r
4742                                 }\r
4743                         } else {\r
4744                                 // Is W3C Range\r
4745                                 if (r.cloneRange) {\r
4746                                         t.tridentSel.addRange(r);\r
4747                                         return;\r
4748                                 }\r
4749 \r
4750                                 // Is IE specific range\r
4751                                 try {\r
4752                                         r.select();\r
4753                                 } catch (ex) {\r
4754                                         // Needed for some odd IE bug #1843306\r
4755                                 }\r
4756                         }\r
4757                 },\r
4758 \r
4759                 setNode : function(n) {\r
4760                         var t = this;\r
4761 \r
4762                         t.setContent(t.dom.getOuterHTML(n));\r
4763 \r
4764                         return n;\r
4765                 },\r
4766 \r
4767                 getNode : function() {\r
4768                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
4769 \r
4770                         if (rng.setStart) {\r
4771                                 // Range maybe lost after the editor is made visible again\r
4772                                 if (!rng)\r
4773                                         return t.dom.getRoot();\r
4774 \r
4775                                 elm = rng.commonAncestorContainer;\r
4776 \r
4777                                 // Handle selection a image or other control like element such as anchors\r
4778                                 if (!rng.collapsed) {\r
4779                                         if (rng.startContainer == rng.endContainer) {\r
4780                                                 if (rng.startOffset - rng.endOffset < 2) {\r
4781                                                         if (rng.startContainer.hasChildNodes())\r
4782                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
4783                                                 }\r
4784                                         }\r
4785 \r
4786                                         // If the anchor node is a element instead of a text node then return this element\r
4787                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
4788                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
4789                                 }\r
4790 \r
4791                                 if (elm && elm.nodeType == 3)\r
4792                                         return elm.parentNode;\r
4793 \r
4794                                 return elm;\r
4795                         }\r
4796 \r
4797                         return rng.item ? rng.item(0) : rng.parentElement();\r
4798                 },\r
4799 \r
4800                 getSelectedBlocks : function(st, en) {\r
4801                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
4802 \r
4803                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
4804                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
4805 \r
4806                         if (sb)\r
4807                                 bl.push(sb);\r
4808 \r
4809                         if (sb && eb && sb != eb) {\r
4810                                 n = sb;\r
4811 \r
4812                                 while ((n = n.nextSibling) && n != eb) {\r
4813                                         if (dom.isBlock(n))\r
4814                                                 bl.push(n);\r
4815                                 }\r
4816                         }\r
4817 \r
4818                         if (eb && sb != eb)\r
4819                                 bl.push(eb);\r
4820 \r
4821                         return bl;\r
4822                 },\r
4823 \r
4824                 destroy : function(s) {\r
4825                         var t = this;\r
4826 \r
4827                         t.win = null;\r
4828 \r
4829                         if (t.tridentSel)\r
4830                                 t.tridentSel.destroy();\r
4831 \r
4832                         // Manual destroy then remove unload handler\r
4833                         if (!s)\r
4834                                 tinymce.removeUnload(t.destroy);\r
4835                 }\r
4836         });\r
4837 })(tinymce);\r
4838 \r
4839 (function(tinymce) {\r
4840         tinymce.create('tinymce.dom.XMLWriter', {\r
4841                 node : null,\r
4842 \r
4843                 XMLWriter : function(s) {\r
4844                         // Get XML document\r
4845                         function getXML() {\r
4846                                 var i = document.implementation;\r
4847 \r
4848                                 if (!i || !i.createDocument) {\r
4849                                         // Try IE objects\r
4850                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
4851                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
4852                                 } else\r
4853                                         return i.createDocument('', '', null);\r
4854                         };\r
4855 \r
4856                         this.doc = getXML();\r
4857                         \r
4858                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
4859                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
4860 \r
4861                         this.reset();\r
4862                 },\r
4863 \r
4864                 reset : function() {\r
4865                         var t = this, d = t.doc;\r
4866 \r
4867                         if (d.firstChild)\r
4868                                 d.removeChild(d.firstChild);\r
4869 \r
4870                         t.node = d.appendChild(d.createElement("html"));\r
4871                 },\r
4872 \r
4873                 writeStartElement : function(n) {\r
4874                         var t = this;\r
4875 \r
4876                         t.node = t.node.appendChild(t.doc.createElement(n));\r
4877                 },\r
4878 \r
4879                 writeAttribute : function(n, v) {\r
4880                         if (this.valid)\r
4881                                 v = v.replace(/>/g, '%MCGT%');\r
4882 \r
4883                         this.node.setAttribute(n, v);\r
4884                 },\r
4885 \r
4886                 writeEndElement : function() {\r
4887                         this.node = this.node.parentNode;\r
4888                 },\r
4889 \r
4890                 writeFullEndElement : function() {\r
4891                         var t = this, n = t.node;\r
4892 \r
4893                         n.appendChild(t.doc.createTextNode(""));\r
4894                         t.node = n.parentNode;\r
4895                 },\r
4896 \r
4897                 writeText : function(v) {\r
4898                         if (this.valid)\r
4899                                 v = v.replace(/>/g, '%MCGT%');\r
4900 \r
4901                         this.node.appendChild(this.doc.createTextNode(v));\r
4902                 },\r
4903 \r
4904                 writeCDATA : function(v) {\r
4905                         this.node.appendChild(this.doc.createCDATASection(v));\r
4906                 },\r
4907 \r
4908                 writeComment : function(v) {\r
4909                         // Fix for bug #2035694\r
4910                         if (tinymce.isIE)\r
4911                                 v = v.replace(/^\-|\-$/g, ' ');\r
4912 \r
4913                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
4914                 },\r
4915 \r
4916                 getContent : function() {\r
4917                         var h;\r
4918 \r
4919                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
4920                         h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
4921                         h = h.replace(/ ?\/>/g, ' />');\r
4922 \r
4923                         if (this.valid)\r
4924                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
4925 \r
4926                         return h;\r
4927                 }\r
4928         });\r
4929 })(tinymce);\r
4930 \r
4931 (function(tinymce) {\r
4932         tinymce.create('tinymce.dom.StringWriter', {\r
4933                 str : null,\r
4934                 tags : null,\r
4935                 count : 0,\r
4936                 settings : null,\r
4937                 indent : null,\r
4938 \r
4939                 StringWriter : function(s) {\r
4940                         this.settings = tinymce.extend({\r
4941                                 indent_char : ' ',\r
4942                                 indentation : 0\r
4943                         }, s);\r
4944 \r
4945                         this.reset();\r
4946                 },\r
4947 \r
4948                 reset : function() {\r
4949                         this.indent = '';\r
4950                         this.str = "";\r
4951                         this.tags = [];\r
4952                         this.count = 0;\r
4953                 },\r
4954 \r
4955                 writeStartElement : function(n) {\r
4956                         this._writeAttributesEnd();\r
4957                         this.writeRaw('<' + n);\r
4958                         this.tags.push(n);\r
4959                         this.inAttr = true;\r
4960                         this.count++;\r
4961                         this.elementCount = this.count;\r
4962                 },\r
4963 \r
4964                 writeAttribute : function(n, v) {\r
4965                         var t = this;\r
4966 \r
4967                         t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
4968                 },\r
4969 \r
4970                 writeEndElement : function() {\r
4971                         var n;\r
4972 \r
4973                         if (this.tags.length > 0) {\r
4974                                 n = this.tags.pop();\r
4975 \r
4976                                 if (this._writeAttributesEnd(1))\r
4977                                         this.writeRaw('</' + n + '>');\r
4978 \r
4979                                 if (this.settings.indentation > 0)\r
4980                                         this.writeRaw('\n');\r
4981                         }\r
4982                 },\r
4983 \r
4984                 writeFullEndElement : function() {\r
4985                         if (this.tags.length > 0) {\r
4986                                 this._writeAttributesEnd();\r
4987                                 this.writeRaw('</' + this.tags.pop() + '>');\r
4988 \r
4989                                 if (this.settings.indentation > 0)\r
4990                                         this.writeRaw('\n');\r
4991                         }\r
4992                 },\r
4993 \r
4994                 writeText : function(v) {\r
4995                         this._writeAttributesEnd();\r
4996                         this.writeRaw(this.encode(v));\r
4997                         this.count++;\r
4998                 },\r
4999 \r
5000                 writeCDATA : function(v) {\r
5001                         this._writeAttributesEnd();\r
5002                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5003                         this.count++;\r
5004                 },\r
5005 \r
5006                 writeComment : function(v) {\r
5007                         this._writeAttributesEnd();\r
5008                         this.writeRaw('<!-- ' + v + '-->');\r
5009                         this.count++;\r
5010                 },\r
5011 \r
5012                 writeRaw : function(v) {\r
5013                         this.str += v;\r
5014                 },\r
5015 \r
5016                 encode : function(s) {\r
5017                         return s.replace(/[<>&"]/g, function(v) {\r
5018                                 switch (v) {\r
5019                                         case '<':\r
5020                                                 return '&lt;';\r
5021 \r
5022                                         case '>':\r
5023                                                 return '&gt;';\r
5024 \r
5025                                         case '&':\r
5026                                                 return '&amp;';\r
5027 \r
5028                                         case '"':\r
5029                                                 return '&quot;';\r
5030                                 }\r
5031 \r
5032                                 return v;\r
5033                         });\r
5034                 },\r
5035 \r
5036                 getContent : function() {\r
5037                         return this.str;\r
5038                 },\r
5039 \r
5040                 _writeAttributesEnd : function(s) {\r
5041                         if (!this.inAttr)\r
5042                                 return;\r
5043 \r
5044                         this.inAttr = false;\r
5045 \r
5046                         if (s && this.elementCount == this.count) {\r
5047                                 this.writeRaw(' />');\r
5048                                 return false;\r
5049                         }\r
5050 \r
5051                         this.writeRaw('>');\r
5052 \r
5053                         return true;\r
5054                 }\r
5055         });\r
5056 })(tinymce);\r
5057 \r
5058 (function(tinymce) {\r
5059         // Shorten names\r
5060         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5061 \r
5062         function wildcardToRE(s) {\r
5063                 return s.replace(/([?+*])/g, '.$1');\r
5064         };\r
5065 \r
5066         tinymce.create('tinymce.dom.Serializer', {\r
5067                 Serializer : function(s) {\r
5068                         var t = this;\r
5069 \r
5070                         t.key = 0;\r
5071                         t.onPreProcess = new Dispatcher(t);\r
5072                         t.onPostProcess = new Dispatcher(t);\r
5073 \r
5074                         try {\r
5075                                 t.writer = new tinymce.dom.XMLWriter();\r
5076                         } catch (ex) {\r
5077                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5078                                 t.writer = new tinymce.dom.StringWriter();\r
5079                         }\r
5080 \r
5081                         // Default settings\r
5082                         t.settings = s = extend({\r
5083                                 dom : tinymce.DOM,\r
5084                                 valid_nodes : 0,\r
5085                                 node_filter : 0,\r
5086                                 attr_filter : 0,\r
5087                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5088                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5089                                 entity_encoding : 'named',\r
5090                                 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
5091                                 valid_elements : '*[*]',\r
5092                                 extended_valid_elements : 0,\r
5093                                 invalid_elements : 0,\r
5094                                 fix_table_elements : 1,\r
5095                                 fix_list_elements : true,\r
5096                                 fix_content_duplication : true,\r
5097                                 convert_fonts_to_spans : false,\r
5098                                 font_size_classes : 0,\r
5099                                 apply_source_formatting : 0,\r
5100                                 indent_mode : 'simple',\r
5101                                 indent_char : '\t',\r
5102                                 indent_levels : 1,\r
5103                                 remove_linebreaks : 1,\r
5104                                 remove_redundant_brs : 1,\r
5105                                 element_format : 'xhtml'\r
5106                         }, s);\r
5107 \r
5108                         t.dom = s.dom;\r
5109                         t.schema = s.schema;\r
5110 \r
5111                         // Use raw entities if no entities are defined\r
5112                         if (s.entity_encoding == 'named' && !s.entities)\r
5113                                 s.entity_encoding = 'raw';\r
5114 \r
5115                         if (s.remove_redundant_brs) {\r
5116                                 t.onPostProcess.add(function(se, o) {\r
5117                                         // Remove single BR at end of block elements since they get rendered\r
5118                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5119                                                 // Check if it's a single element\r
5120                                                 if (/^<br \/>\s*<\//.test(a))\r
5121                                                         return '</' + c + '>';\r
5122 \r
5123                                                 return a;\r
5124                                         });\r
5125                                 });\r
5126                         }\r
5127 \r
5128                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
5129                         if (s.element_format == 'html') {\r
5130                                 t.onPostProcess.add(function(se, o) {\r
5131                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
5132                                 });\r
5133                         }\r
5134 \r
5135                         if (s.fix_list_elements) {\r
5136                                 t.onPreProcess.add(function(se, o) {\r
5137                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
5138 \r
5139                                         function prevNode(e, n) {\r
5140                                                 var a = n.split(','), i;\r
5141 \r
5142                                                 while ((e = e.previousSibling) != null) {\r
5143                                                         for (i=0; i<a.length; i++) {\r
5144                                                                 if (e.nodeName == a[i])\r
5145                                                                         return e;\r
5146                                                         }\r
5147                                                 }\r
5148 \r
5149                                                 return null;\r
5150                                         };\r
5151 \r
5152                                         for (x=0; x<a.length; x++) {\r
5153                                                 nl = t.dom.select(a[x], o.node);\r
5154 \r
5155                                                 for (i=0; i<nl.length; i++) {\r
5156                                                         n = nl[i];\r
5157                                                         p = n.parentNode;\r
5158 \r
5159                                                         if (r.test(p.nodeName)) {\r
5160                                                                 np = prevNode(n, 'LI');\r
5161 \r
5162                                                                 if (!np) {\r
5163                                                                         np = t.dom.create('li');\r
5164                                                                         np.innerHTML = '&nbsp;';\r
5165                                                                         np.appendChild(n);\r
5166                                                                         p.insertBefore(np, p.firstChild);\r
5167                                                                 } else\r
5168                                                                         np.appendChild(n);\r
5169                                                         }\r
5170                                                 }\r
5171                                         }\r
5172                                 });\r
5173                         }\r
5174 \r
5175                         if (s.fix_table_elements) {\r
5176                                 t.onPreProcess.add(function(se, o) {\r
5177                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
5178                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
5179                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
5180                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
5181                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
5182 \r
5183                                                         if (parent.nodeName != 'TABLE') {\r
5184                                                                 try {\r
5185                                                                         t.dom.split(parent, n);\r
5186                                                                 } catch (ex) {\r
5187                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
5188                                                                 }\r
5189                                                         }\r
5190                                                 });\r
5191                                         }\r
5192                                 });\r
5193                         }\r
5194                 },\r
5195 \r
5196                 setEntities : function(s) {\r
5197                         var t = this, a, i, l = {}, v;\r
5198 \r
5199                         // No need to setup more than once\r
5200                         if (t.entityLookup)\r
5201                                 return;\r
5202 \r
5203                         // Build regex and lookup array\r
5204                         a = s.split(',');\r
5205                         for (i = 0; i < a.length; i += 2) {\r
5206                                 v = a[i];\r
5207 \r
5208                                 // Don't add default &amp; &quot; etc.\r
5209                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
5210                                         continue;\r
5211 \r
5212                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
5213 \r
5214                                 v = parseInt(a[i]).toString(16);\r
5215                         }\r
5216 \r
5217                         t.entityLookup = l;\r
5218                 },\r
5219 \r
5220                 setRules : function(s) {\r
5221                         var t = this;\r
5222 \r
5223                         t._setup();\r
5224                         t.rules = {};\r
5225                         t.wildRules = [];\r
5226                         t.validElements = {};\r
5227 \r
5228                         return t.addRules(s);\r
5229                 },\r
5230 \r
5231                 addRules : function(s) {\r
5232                         var t = this, dr;\r
5233 \r
5234                         if (!s)\r
5235                                 return;\r
5236 \r
5237                         t._setup();\r
5238 \r
5239                         each(s.split(','), function(s) {\r
5240                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
5241 \r
5242                                 // Extend with default rules\r
5243                                 if (dr)\r
5244                                         at = tinymce.extend([], dr.attribs);\r
5245 \r
5246                                 // Parse attributes\r
5247                                 if (p.length > 1) {\r
5248                                         each(p[1].split('|'), function(s) {\r
5249                                                 var ar = {}, i;\r
5250 \r
5251                                                 at = at || [];\r
5252 \r
5253                                                 // Parse attribute rule\r
5254                                                 s = s.replace(/::/g, '~');\r
5255                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
5256                                                 s[2] = s[2].replace(/~/g, ':');\r
5257 \r
5258                                                 // Add required attributes\r
5259                                                 if (s[1] == '!') {\r
5260                                                         ra = ra || [];\r
5261                                                         ra.push(s[2]);\r
5262                                                 }\r
5263 \r
5264                                                 // Remove inherited attributes\r
5265                                                 if (s[1] == '-') {\r
5266                                                         for (i = 0; i <at.length; i++) {\r
5267                                                                 if (at[i].name == s[2]) {\r
5268                                                                         at.splice(i, 1);\r
5269                                                                         return;\r
5270                                                                 }\r
5271                                                         }\r
5272                                                 }\r
5273 \r
5274                                                 switch (s[3]) {\r
5275                                                         // Add default attrib values\r
5276                                                         case '=':\r
5277                                                                 ar.defaultVal = s[4] || '';\r
5278                                                                 break;\r
5279 \r
5280                                                         // Add forced attrib values\r
5281                                                         case ':':\r
5282                                                                 ar.forcedVal = s[4];\r
5283                                                                 break;\r
5284 \r
5285                                                         // Add validation values\r
5286                                                         case '<':\r
5287                                                                 ar.validVals = s[4].split('?');\r
5288                                                                 break;\r
5289                                                 }\r
5290 \r
5291                                                 if (/[*.?]/.test(s[2])) {\r
5292                                                         wat = wat || [];\r
5293                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
5294                                                         wat.push(ar);\r
5295                                                 } else {\r
5296                                                         ar.name = s[2];\r
5297                                                         at.push(ar);\r
5298                                                 }\r
5299 \r
5300                                                 va.push(s[2]);\r
5301                                         });\r
5302                                 }\r
5303 \r
5304                                 // Handle element names\r
5305                                 each(tn, function(s, i) {\r
5306                                         var pr = s.charAt(0), x = 1, ru = {};\r
5307 \r
5308                                         // Extend with default rule data\r
5309                                         if (dr) {\r
5310                                                 if (dr.noEmpty)\r
5311                                                         ru.noEmpty = dr.noEmpty;\r
5312 \r
5313                                                 if (dr.fullEnd)\r
5314                                                         ru.fullEnd = dr.fullEnd;\r
5315 \r
5316                                                 if (dr.padd)\r
5317                                                         ru.padd = dr.padd;\r
5318                                         }\r
5319 \r
5320                                         // Handle prefixes\r
5321                                         switch (pr) {\r
5322                                                 case '-':\r
5323                                                         ru.noEmpty = true;\r
5324                                                         break;\r
5325 \r
5326                                                 case '+':\r
5327                                                         ru.fullEnd = true;\r
5328                                                         break;\r
5329 \r
5330                                                 case '#':\r
5331                                                         ru.padd = true;\r
5332                                                         break;\r
5333 \r
5334                                                 default:\r
5335                                                         x = 0;\r
5336                                         }\r
5337 \r
5338                                         tn[i] = s = s.substring(x);\r
5339                                         t.validElements[s] = 1;\r
5340 \r
5341                                         // Add element name or element regex\r
5342                                         if (/[*.?]/.test(tn[0])) {\r
5343                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
5344                                                 t.wildRules = t.wildRules || {};\r
5345                                                 t.wildRules.push(ru);\r
5346                                         } else {\r
5347                                                 ru.name = tn[0];\r
5348 \r
5349                                                 // Store away default rule\r
5350                                                 if (tn[0] == '@')\r
5351                                                         dr = ru;\r
5352 \r
5353                                                 t.rules[s] = ru;\r
5354                                         }\r
5355 \r
5356                                         ru.attribs = at;\r
5357 \r
5358                                         if (ra)\r
5359                                                 ru.requiredAttribs = ra;\r
5360 \r
5361                                         if (wat) {\r
5362                                                 // Build valid attributes regexp\r
5363                                                 s = '';\r
5364                                                 each(va, function(v) {\r
5365                                                         if (s)\r
5366                                                                 s += '|';\r
5367 \r
5368                                                         s += '(' + wildcardToRE(v) + ')';\r
5369                                                 });\r
5370                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
5371                                                 ru.wildAttribs = wat;\r
5372                                         }\r
5373                                 });\r
5374                         });\r
5375 \r
5376                         // Build valid elements regexp\r
5377                         s = '';\r
5378                         each(t.validElements, function(v, k) {\r
5379                                 if (s)\r
5380                                         s += '|';\r
5381 \r
5382                                 if (k != '@')\r
5383                                         s += k;\r
5384                         });\r
5385                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
5386 \r
5387                         //console.debug(t.validElementsRE.toString());\r
5388                         //console.dir(t.rules);\r
5389                         //console.dir(t.wildRules);\r
5390                 },\r
5391 \r
5392                 findRule : function(n) {\r
5393                         var t = this, rl = t.rules, i, r;\r
5394 \r
5395                         t._setup();\r
5396 \r
5397                         // Exact match\r
5398                         r = rl[n];\r
5399                         if (r)\r
5400                                 return r;\r
5401 \r
5402                         // Try wildcards\r
5403                         rl = t.wildRules;\r
5404                         for (i = 0; i < rl.length; i++) {\r
5405                                 if (rl[i].nameRE.test(n))\r
5406                                         return rl[i];\r
5407                         }\r
5408 \r
5409                         return null;\r
5410                 },\r
5411 \r
5412                 findAttribRule : function(ru, n) {\r
5413                         var i, wa = ru.wildAttribs;\r
5414 \r
5415                         for (i = 0; i < wa.length; i++) {\r
5416                                 if (wa[i].nameRE.test(n))\r
5417                                         return wa[i];\r
5418                         }\r
5419 \r
5420                         return null;\r
5421                 },\r
5422 \r
5423                 serialize : function(n, o) {\r
5424                         var h, t = this, doc, oldDoc, impl, selected;\r
5425 \r
5426                         t._setup();\r
5427                         o = o || {};\r
5428                         o.format = o.format || 'html';\r
5429                         t.processObj = o;\r
5430 \r
5431                         // IE looses the selected attribute on option elements so we need to store it\r
5432                         // See: http://support.microsoft.com/kb/829907\r
5433                         if (isIE) {\r
5434                                 selected = [];\r
5435                                 each(n.getElementsByTagName('option'), function(n) {\r
5436                                         var v = t.dom.getAttrib(n, 'selected');\r
5437 \r
5438                                         selected.push(v ? v : null);\r
5439                                 });\r
5440                         }\r
5441 \r
5442                         n = n.cloneNode(true);\r
5443 \r
5444                         // IE looses the selected attribute on option elements so we need to restore it\r
5445                         if (isIE) {\r
5446                                 each(n.getElementsByTagName('option'), function(n, i) {\r
5447                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
5448                                 });\r
5449                         }\r
5450 \r
5451                         // Nodes needs to be attached to something in WebKit/Opera\r
5452                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
5453                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
5454                         // This fix will make DOM ranges and make Sizzle happy!\r
5455                         impl = n.ownerDocument.implementation;\r
5456                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
5457                                 // Create an empty HTML document\r
5458                                 doc = impl.createHTMLDocument("");\r
5459 \r
5460                                 // Add the element or it's children if it's a body element to the new document\r
5461                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
5462                                         doc.body.appendChild(doc.importNode(node, true));\r
5463                                 });\r
5464 \r
5465                                 // Grab first child or body element for serialization\r
5466                                 if (n.nodeName != 'BODY')\r
5467                                         n = doc.body.firstChild;\r
5468                                 else\r
5469                                         n = doc.body;\r
5470 \r
5471                                 // set the new document in DOMUtils so createElement etc works\r
5472                                 oldDoc = t.dom.doc;\r
5473                                 t.dom.doc = doc;\r
5474                         }\r
5475 \r
5476                         t.key = '' + (parseInt(t.key) + 1);\r
5477 \r
5478                         // Pre process\r
5479                         if (!o.no_events) {\r
5480                                 o.node = n;\r
5481                                 t.onPreProcess.dispatch(t, o);\r
5482                         }\r
5483 \r
5484                         // Serialize HTML DOM into a string\r
5485                         t.writer.reset();\r
5486                         t._info = o;\r
5487                         t._serializeNode(n, o.getInner);\r
5488 \r
5489                         // Post process\r
5490                         o.content = t.writer.getContent();\r
5491 \r
5492                         // Restore the old document if it was changed\r
5493                         if (oldDoc)\r
5494                                 t.dom.doc = oldDoc;\r
5495 \r
5496                         if (!o.no_events)\r
5497                                 t.onPostProcess.dispatch(t, o);\r
5498 \r
5499                         t._postProcess(o);\r
5500                         o.node = null;\r
5501 \r
5502                         return tinymce.trim(o.content);\r
5503                 },\r
5504 \r
5505                 // Internal functions\r
5506 \r
5507                 _postProcess : function(o) {\r
5508                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
5509 \r
5510                         if (o.format == 'html') {\r
5511                                 // Protect some elements\r
5512                                 p = t._protect({\r
5513                                         content : h,\r
5514                                         patterns : [\r
5515                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
5516                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
5517                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
5518                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
5519                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
5520                                         ]\r
5521                                 });\r
5522 \r
5523                                 h = p.content;\r
5524 \r
5525                                 // Entity encode\r
5526                                 if (s.entity_encoding !== 'raw')\r
5527                                         h = t._encode(h);\r
5528 \r
5529                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
5530 /*                              if (o.set)\r
5531                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
5532                                 else\r
5533                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
5534 \r
5535                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
5536                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
5537                                 // This process is only done when getting contents out from the editor.\r
5538                                 if (!o.set) {\r
5539                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
5540                                         h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
5541 \r
5542                                         if (s.remove_linebreaks) {\r
5543                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
5544                                                 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
5545                                                 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
5546                                                 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
5547                                                 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
5548                                                 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
5549                                         }\r
5550 \r
5551                                         // Simple indentation\r
5552                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
5553                                                 // Add line breaks before and after block elements\r
5554                                                 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
5555                                                 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
5556                                                 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
5557                                                 h = h.replace(/\n\n/g, '\n');\r
5558                                         }\r
5559                                 }\r
5560 \r
5561                                 h = t._unprotect(h, p);\r
5562 \r
5563                                 // Restore CDATA sections\r
5564                                 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
5565 \r
5566                                 // Restore the \u00a0 character if raw mode is enabled\r
5567                                 if (s.entity_encoding == 'raw')\r
5568                                         h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
5569 \r
5570                                 // Restore noscript elements\r
5571                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
5572                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
5573                                 });\r
5574                         }\r
5575 \r
5576                         o.content = h;\r
5577                 },\r
5578 \r
5579                 _serializeNode : function(n, inner) {\r
5580                         var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type, scopeName;\r
5581 \r
5582                         if (!s.node_filter || s.node_filter(n)) {\r
5583                                 switch (n.nodeType) {\r
5584                                         case 1: // Element\r
5585                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
5586                                                         return;\r
5587 \r
5588                                                 iv = keep = false;\r
5589                                                 hc = n.hasChildNodes();\r
5590                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
5591 \r
5592                                                 // Get internal type\r
5593                                                 type = n.getAttribute('_mce_type');\r
5594                                                 if (type) {\r
5595                                                         if (!t._info.cleanup) {\r
5596                                                                 iv = true;\r
5597                                                                 return;\r
5598                                                         } else\r
5599                                                                 keep = 1;\r
5600                                                 }\r
5601 \r
5602                                                 // Add correct prefix on IE\r
5603                                                 if (isIE) {\r
5604                                                         scopeName = n.scopeName;\r
5605                                                         if (scopeName && scopeName !== 'HTML' && scopeName !== 'html')\r
5606                                                                 nn = scopeName + ':' + nn;\r
5607                                                 }\r
5608 \r
5609                                                 // Remove mce prefix on IE needed for the abbr element\r
5610                                                 if (nn.indexOf('mce:') === 0)\r
5611                                                         nn = nn.substring(4);\r
5612 \r
5613                                                 // Check if valid\r
5614                                                 if (!keep) {\r
5615                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
5616                                                                 iv = true;\r
5617                                                                 break;\r
5618                                                         }\r
5619                                                 }\r
5620 \r
5621                                                 if (isIE) {\r
5622                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
5623                                                         if (s.fix_content_duplication) {\r
5624                                                                 if (n._mce_serialized == t.key)\r
5625                                                                         return;\r
5626 \r
5627                                                                 n._mce_serialized = t.key;\r
5628                                                         }\r
5629 \r
5630                                                         // IE sometimes adds a / infront of the node name\r
5631                                                         if (nn.charAt(0) == '/')\r
5632                                                                 nn = nn.substring(1);\r
5633                                                 } else if (isGecko) {\r
5634                                                         // Ignore br elements\r
5635                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
5636                                                                 return;\r
5637                                                 }\r
5638 \r
5639                                                 // Check if valid child\r
5640                                                 if (s.validate_children) {\r
5641                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
5642                                                                 iv = true;\r
5643                                                                 break;\r
5644                                                         }\r
5645 \r
5646                                                         t.elementName = nn;\r
5647                                                 }\r
5648 \r
5649                                                 ru = t.findRule(nn);\r
5650                                                 \r
5651                                                 // No valid rule for this element could be found then skip it\r
5652                                                 if (!ru) {\r
5653                                                         iv = true;\r
5654                                                         break;\r
5655                                                 }\r
5656 \r
5657                                                 nn = ru.name || nn;\r
5658                                                 closed = s.closed.test(nn);\r
5659 \r
5660                                                 // Skip empty nodes or empty node name in IE\r
5661                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
5662                                                         iv = true;\r
5663                                                         break;\r
5664                                                 }\r
5665 \r
5666                                                 // Check required\r
5667                                                 if (ru.requiredAttribs) {\r
5668                                                         a = ru.requiredAttribs;\r
5669 \r
5670                                                         for (i = a.length - 1; i >= 0; i--) {\r
5671                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
5672                                                                         break;\r
5673                                                         }\r
5674 \r
5675                                                         // None of the required was there\r
5676                                                         if (i == -1) {\r
5677                                                                 iv = true;\r
5678                                                                 break;\r
5679                                                         }\r
5680                                                 }\r
5681 \r
5682                                                 w.writeStartElement(nn);\r
5683 \r
5684                                                 // Add ordered attributes\r
5685                                                 if (ru.attribs) {\r
5686                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
5687                                                                 a = at[i];\r
5688                                                                 v = t._getAttrib(n, a);\r
5689 \r
5690                                                                 if (v !== null)\r
5691                                                                         w.writeAttribute(a.name, v);\r
5692                                                         }\r
5693                                                 }\r
5694 \r
5695                                                 // Add wild attributes\r
5696                                                 if (ru.validAttribsRE) {\r
5697                                                         at = t.dom.getAttribs(n);\r
5698                                                         for (i=at.length-1; i>-1; i--) {\r
5699                                                                 no = at[i];\r
5700 \r
5701                                                                 if (no.specified) {\r
5702                                                                         a = no.nodeName.toLowerCase();\r
5703 \r
5704                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
5705                                                                                 continue;\r
5706 \r
5707                                                                         ar = t.findAttribRule(ru, a);\r
5708                                                                         v = t._getAttrib(n, ar, a);\r
5709 \r
5710                                                                         if (v !== null)\r
5711                                                                                 w.writeAttribute(a, v);\r
5712                                                                 }\r
5713                                                         }\r
5714                                                 }\r
5715 \r
5716                                                 // Keep type attribute\r
5717                                                 if (type && keep)\r
5718                                                         w.writeAttribute('_mce_type', type);\r
5719 \r
5720                                                 // Write text from script\r
5721                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
5722                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
5723                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
5724                                                         hc = false;\r
5725                                                         break;\r
5726                                                 }\r
5727 \r
5728                                                 // Padd empty nodes with a &nbsp;\r
5729                                                 if (ru.padd) {\r
5730                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
5731                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
5732                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
5733                                                                         w.writeText('\u00a0');\r
5734                                                         } else if (!hc)\r
5735                                                                 w.writeText('\u00a0'); // No children then padd it\r
5736                                                 }\r
5737 \r
5738                                                 break;\r
5739 \r
5740                                         case 3: // Text\r
5741                                                 // Check if valid child\r
5742                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
5743                                                         return;\r
5744 \r
5745                                                 return w.writeText(n.nodeValue);\r
5746 \r
5747                                         case 4: // CDATA\r
5748                                                 return w.writeCDATA(n.nodeValue);\r
5749 \r
5750                                         case 8: // Comment\r
5751                                                 return w.writeComment(n.nodeValue);\r
5752                                 }\r
5753                         } else if (n.nodeType == 1)\r
5754                                 hc = n.hasChildNodes();\r
5755 \r
5756                         if (hc && !closed) {\r
5757                                 cn = n.firstChild;\r
5758 \r
5759                                 while (cn) {\r
5760                                         t._serializeNode(cn);\r
5761                                         t.elementName = nn;\r
5762                                         cn = cn.nextSibling;\r
5763                                 }\r
5764                         }\r
5765 \r
5766                         // Write element end\r
5767                         if (!iv) {\r
5768                                 if (!closed)\r
5769                                         w.writeFullEndElement();\r
5770                                 else\r
5771                                         w.writeEndElement();\r
5772                         }\r
5773                 },\r
5774 \r
5775                 _protect : function(o) {\r
5776                         var t = this;\r
5777 \r
5778                         o.items = o.items || [];\r
5779 \r
5780                         function enc(s) {\r
5781                                 return s.replace(/[\r\n\\]/g, function(c) {\r
5782                                         if (c === '\n')\r
5783                                                 return '\\n';\r
5784                                         else if (c === '\\')\r
5785                                                 return '\\\\';\r
5786 \r
5787                                         return '\\r';\r
5788                                 });\r
5789                         };\r
5790 \r
5791                         function dec(s) {\r
5792                                 return s.replace(/\\[\\rn]/g, function(c) {\r
5793                                         if (c === '\\n')\r
5794                                                 return '\n';\r
5795                                         else if (c === '\\\\')\r
5796                                                 return '\\';\r
5797 \r
5798                                         return '\r';\r
5799                                 });\r
5800                         };\r
5801 \r
5802                         each(o.patterns, function(p) {\r
5803                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
5804                                         b = dec(b);\r
5805 \r
5806                                         if (p.encode)\r
5807                                                 b = t._encode(b);\r
5808 \r
5809                                         o.items.push(b);\r
5810                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
5811                                 }));\r
5812                         });\r
5813 \r
5814                         return o;\r
5815                 },\r
5816 \r
5817                 _unprotect : function(h, o) {\r
5818                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
5819                                 return o.items[parseInt(b)];\r
5820                         });\r
5821 \r
5822                         o.items = [];\r
5823 \r
5824                         return h;\r
5825                 },\r
5826 \r
5827                 _encode : function(h) {\r
5828                         var t = this, s = t.settings, l;\r
5829 \r
5830                         // Entity encode\r
5831                         if (s.entity_encoding !== 'raw') {\r
5832                                 if (s.entity_encoding.indexOf('named') != -1) {\r
5833                                         t.setEntities(s.entities);\r
5834                                         l = t.entityLookup;\r
5835 \r
5836                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
5837                                                 var v;\r
5838 \r
5839                                                 if (v = l[a])\r
5840                                                         a = '&' + v + ';';\r
5841 \r
5842                                                 return a;\r
5843                                         });\r
5844                                 }\r
5845 \r
5846                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
5847                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
5848                                                 return '&#' + a.charCodeAt(0) + ';';\r
5849                                         });\r
5850                                 }\r
5851                         }\r
5852 \r
5853                         return h;\r
5854                 },\r
5855 \r
5856                 _setup : function() {\r
5857                         var t = this, s = this.settings;\r
5858 \r
5859                         if (t.done)\r
5860                                 return;\r
5861 \r
5862                         t.done = 1;\r
5863 \r
5864                         t.setRules(s.valid_elements);\r
5865                         t.addRules(s.extended_valid_elements);\r
5866 \r
5867                         if (s.invalid_elements)\r
5868                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
5869 \r
5870                         if (s.attrib_value_filter)\r
5871                                 t.attribValueFilter = s.attribValueFilter;\r
5872                 },\r
5873 \r
5874                 _getAttrib : function(n, a, na) {\r
5875                         var i, v;\r
5876 \r
5877                         na = na || a.name;\r
5878 \r
5879                         if (a.forcedVal && (v = a.forcedVal)) {\r
5880                                 if (v === '{$uid}')\r
5881                                         return this.dom.uniqueId();\r
5882 \r
5883                                 return v;\r
5884                         }\r
5885 \r
5886                         v = this.dom.getAttrib(n, na);\r
5887 \r
5888                         switch (na) {\r
5889                                 case 'rowspan':\r
5890                                 case 'colspan':\r
5891                                         // Whats the point? Remove usless attribute value\r
5892                                         if (v == '1')\r
5893                                                 v = '';\r
5894 \r
5895                                         break;\r
5896                         }\r
5897 \r
5898                         if (this.attribValueFilter)\r
5899                                 v = this.attribValueFilter(na, v, n);\r
5900 \r
5901                         if (a.validVals) {\r
5902                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
5903                                         if (v == a.validVals[i])\r
5904                                                 break;\r
5905                                 }\r
5906 \r
5907                                 if (i == -1)\r
5908                                         return null;\r
5909                         }\r
5910 \r
5911                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
5912                                 v = a.defaultVal;\r
5913 \r
5914                                 if (v === '{$uid}')\r
5915                                         return this.dom.uniqueId();\r
5916 \r
5917                                 return v;\r
5918                         } else {\r
5919                                 // Remove internal mceItemXX classes when content is extracted from editor\r
5920                                 if (na == 'class' && this.processObj.get)\r
5921                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
5922                         }\r
5923 \r
5924                         if (v === '')\r
5925                                 return null;\r
5926 \r
5927 \r
5928                         return v;\r
5929                 }\r
5930         });\r
5931 })(tinymce);\r
5932 \r
5933 (function(tinymce) {\r
5934         tinymce.dom.ScriptLoader = function(settings) {\r
5935                 var QUEUED = 0,\r
5936                         LOADING = 1,\r
5937                         LOADED = 2,\r
5938                         states = {},\r
5939                         queue = [],\r
5940                         scriptLoadedCallbacks = {},\r
5941                         queueLoadedCallbacks = [],\r
5942                         loading = 0,\r
5943                         undefined;\r
5944 \r
5945                 function loadScript(url, callback) {\r
5946                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
5947 \r
5948                         // Execute callback when script is loaded\r
5949                         function done() {\r
5950                                 dom.remove(id);\r
5951 \r
5952                                 if (elm)\r
5953                                         elm.onreadystatechange = elm.onload = elm = null;\r
5954 \r
5955                                 callback();\r
5956                         };\r
5957 \r
5958                         id = dom.uniqueId();\r
5959 \r
5960                         if (tinymce.isIE6) {\r
5961                                 uri = new tinymce.util.URI(url);\r
5962                                 loc = location;\r
5963 \r
5964                                 // If script is from same domain and we\r
5965                                 // use IE 6 then use XHR since it's more reliable\r
5966                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
5967                                         tinymce.util.XHR.send({\r
5968                                                 url : tinymce._addVer(uri.getURI()),\r
5969                                                 success : function(content) {\r
5970                                                         // Create new temp script element\r
5971                                                         var script = dom.create('script', {\r
5972                                                                 type : 'text/javascript'\r
5973                                                         });\r
5974 \r
5975                                                         // Evaluate script in global scope\r
5976                                                         script.text = content;\r
5977                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
5978                                                         dom.remove(script);\r
5979 \r
5980                                                         done();\r
5981                                                 }\r
5982                                         });\r
5983 \r
5984                                         return;\r
5985                                 }\r
5986                         }\r
5987 \r
5988                         // Create new script element\r
5989                         elm = dom.create('script', {\r
5990                                 id : id,\r
5991                                 type : 'text/javascript',\r
5992                                 src : tinymce._addVer(url)\r
5993                         });\r
5994 \r
5995                         // Add onload and readystate listeners\r
5996                         elm.onload = done;\r
5997                         elm.onreadystatechange = function() {\r
5998                                 var state = elm.readyState;\r
5999 \r
6000                                 // Loaded state is passed on IE 6 however there\r
6001                                 // are known issues with this method but we can't use\r
6002                                 // XHR in a cross domain loading\r
6003                                 if (state == 'complete' || state == 'loaded')\r
6004                                         done();\r
6005                         };\r
6006 \r
6007                         // Most browsers support this feature so we report errors\r
6008                         // for those at least to help users track their missing plugins etc\r
6009                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6010                         /*elm.onerror = function() {\r
6011                                 alert('Failed to load: ' + url);\r
6012                         };*/\r
6013 \r
6014                         // Add script to document\r
6015                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6016                 };\r
6017 \r
6018                 this.isDone = function(url) {\r
6019                         return states[url] == LOADED;\r
6020                 };\r
6021 \r
6022                 this.markDone = function(url) {\r
6023                         states[url] = LOADED;\r
6024                 };\r
6025 \r
6026                 this.add = this.load = function(url, callback, scope) {\r
6027                         var item, state = states[url];\r
6028 \r
6029                         // Add url to load queue\r
6030                         if (state == undefined) {\r
6031                                 queue.push(url);\r
6032                                 states[url] = QUEUED;\r
6033                         }\r
6034 \r
6035                         if (callback) {\r
6036                                 // Store away callback for later execution\r
6037                                 if (!scriptLoadedCallbacks[url])\r
6038                                         scriptLoadedCallbacks[url] = [];\r
6039 \r
6040                                 scriptLoadedCallbacks[url].push({\r
6041                                         func : callback,\r
6042                                         scope : scope || this\r
6043                                 });\r
6044                         }\r
6045                 };\r
6046 \r
6047                 this.loadQueue = function(callback, scope) {\r
6048                         this.loadScripts(queue, callback, scope);\r
6049                 };\r
6050 \r
6051                 this.loadScripts = function(scripts, callback, scope) {\r
6052                         var loadScripts;\r
6053 \r
6054                         function execScriptLoadedCallbacks(url) {\r
6055                                 // Execute URL callback functions\r
6056                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6057                                         callback.func.call(callback.scope);\r
6058                                 });\r
6059 \r
6060                                 scriptLoadedCallbacks[url] = undefined;\r
6061                         };\r
6062 \r
6063                         queueLoadedCallbacks.push({\r
6064                                 func : callback,\r
6065                                 scope : scope || this\r
6066                         });\r
6067 \r
6068                         loadScripts = function() {\r
6069                                 var loadingScripts = tinymce.grep(scripts);\r
6070 \r
6071                                 // Current scripts has been handled\r
6072                                 scripts.length = 0;\r
6073 \r
6074                                 // Load scripts that needs to be loaded\r
6075                                 tinymce.each(loadingScripts, function(url) {\r
6076                                         // Script is already loaded then execute script callbacks directly\r
6077                                         if (states[url] == LOADED) {\r
6078                                                 execScriptLoadedCallbacks(url);\r
6079                                                 return;\r
6080                                         }\r
6081 \r
6082                                         // Is script not loading then start loading it\r
6083                                         if (states[url] != LOADING) {\r
6084                                                 states[url] = LOADING;\r
6085                                                 loading++;\r
6086 \r
6087                                                 loadScript(url, function() {\r
6088                                                         states[url] = LOADED;\r
6089                                                         loading--;\r
6090 \r
6091                                                         execScriptLoadedCallbacks(url);\r
6092 \r
6093                                                         // Load more scripts if they where added by the recently loaded script\r
6094                                                         loadScripts();\r
6095                                                 });\r
6096                                         }\r
6097                                 });\r
6098 \r
6099                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6100                                 if (!loading) {\r
6101                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6102                                                 callback.func.call(callback.scope);\r
6103                                         });\r
6104 \r
6105                                         queueLoadedCallbacks.length = 0;\r
6106                                 }\r
6107                         };\r
6108 \r
6109                         loadScripts();\r
6110                 };\r
6111         };\r
6112 \r
6113         // Global script loader\r
6114         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6115 })(tinymce);\r
6116 \r
6117 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
6118         var node = start_node;\r
6119 \r
6120         function findSibling(node, start_name, sibling_name, shallow) {\r
6121                 var sibling, parent;\r
6122 \r
6123                 if (node) {\r
6124                         // Walk into nodes if it has a start\r
6125                         if (!shallow && node[start_name])\r
6126                                 return node[start_name];\r
6127 \r
6128                         // Return the sibling if it has one\r
6129                         if (node != root_node) {\r
6130                                 sibling = node[sibling_name];\r
6131                                 if (sibling)\r
6132                                         return sibling;\r
6133 \r
6134                                 // Walk up the parents to look for siblings\r
6135                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
6136                                         sibling = parent[sibling_name];\r
6137                                         if (sibling)\r
6138                                                 return sibling;\r
6139                                 }\r
6140                         }\r
6141                 }\r
6142         };\r
6143 \r
6144         this.current = function() {\r
6145                 return node;\r
6146         };\r
6147 \r
6148         this.next = function(shallow) {\r
6149                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
6150         };\r
6151 \r
6152         this.prev = function(shallow) {\r
6153                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
6154         };\r
6155 };\r
6156 \r
6157 (function() {\r
6158         var transitional = {};\r
6159 \r
6160         function unpack(lookup, data) {\r
6161                 var key;\r
6162 \r
6163                 function replace(value) {\r
6164                         return value.replace(/[A-Z]+/g, function(key) {\r
6165                                 return replace(lookup[key]);\r
6166                         });\r
6167                 };\r
6168 \r
6169                 // Unpack lookup\r
6170                 for (key in lookup) {\r
6171                         if (lookup.hasOwnProperty(key))\r
6172                                 lookup[key] = replace(lookup[key]);\r
6173                 }\r
6174 \r
6175                 // Unpack and parse data into object map\r
6176                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
6177                         var i, map = {};\r
6178 \r
6179                         children = children.split(/\|/);\r
6180 \r
6181                         for (i = children.length - 1; i >= 0; i--)\r
6182                                 map[children[i]] = 1;\r
6183 \r
6184                         transitional[name] = map;\r
6185                 });\r
6186         };\r
6187 \r
6188         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
6189         // we will later include the attributes here and use it as a default for valid elements but it\r
6190         // requires us to rewrite the serializer engine\r
6191         unpack({\r
6192                 Z : '#|H|K|N|O|P',\r
6193                 Y : '#|X|form|R|Q',\r
6194                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
6195                 W : 'pre|hr|blockquote|address|center|noframes',\r
6196                 U : 'ul|ol|dl|menu|dir',\r
6197                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
6198                 T : 'h1|h2|h3|h4|h5|h6',\r
6199                 ZB : '#|X|S|Q',\r
6200                 S : 'R|P',\r
6201                 ZA : '#|a|G|J|M|O|P',\r
6202                 R : '#|a|H|K|N|O',\r
6203                 Q : 'noscript|P',\r
6204                 P : 'ins|del|script',\r
6205                 O : 'input|select|textarea|label|button',\r
6206                 N : 'M|L',\r
6207                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
6208                 L : 'sub|sup',\r
6209                 K : 'J|I',\r
6210                 J : 'tt|i|b|u|s|strike',\r
6211                 I : 'big|small|font|basefont',\r
6212                 H : 'G|F',\r
6213                 G : 'br|span|bdo',\r
6214                 F : 'object|applet|img|map|iframe'\r
6215         }, 'script[]' + \r
6216                 'style[]' + \r
6217                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
6218                 'param[]' + \r
6219                 'p[S]' + \r
6220                 'a[Z]' + \r
6221                 'br[]' + \r
6222                 'span[S]' + \r
6223                 'bdo[S]' + \r
6224                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
6225                 'h1[S]' + \r
6226                 'img[]' + \r
6227                 'map[X|form|Q|area]' + \r
6228                 'h2[S]' + \r
6229                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
6230                 'h3[S]' + \r
6231                 'tt[S]' + \r
6232                 'i[S]' + \r
6233                 'b[S]' + \r
6234                 'u[S]' + \r
6235                 's[S]' + \r
6236                 'strike[S]' + \r
6237                 'big[S]' + \r
6238                 'small[S]' + \r
6239                 'font[S]' + \r
6240                 'basefont[]' + \r
6241                 'em[S]' + \r
6242                 'strong[S]' + \r
6243                 'dfn[S]' + \r
6244                 'code[S]' + \r
6245                 'q[S]' + \r
6246                 'samp[S]' + \r
6247                 'kbd[S]' + \r
6248                 'var[S]' + \r
6249                 'cite[S]' + \r
6250                 'abbr[S]' + \r
6251                 'acronym[S]' + \r
6252                 'sub[S]' + \r
6253                 'sup[S]' + \r
6254                 'input[]' + \r
6255                 'select[optgroup|option]' + \r
6256                 'optgroup[option]' + \r
6257                 'option[]' + \r
6258                 'textarea[]' + \r
6259                 'label[S]' + \r
6260                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
6261                 'h4[S]' + \r
6262                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
6263                 'h5[S]' + \r
6264                 'del[#|X|form|a|H|K|N|O|Q]' + \r
6265                 'h6[S]' + \r
6266                 'div[#|X|form|a|H|K|N|O|Q]' + \r
6267                 'ul[li]' + \r
6268                 'li[#|X|form|a|H|K|N|O|Q]' + \r
6269                 'ol[li]' + \r
6270                 'dl[dt|dd]' + \r
6271                 'dt[S]' + \r
6272                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
6273                 'menu[li]' + \r
6274                 'dir[li]' + \r
6275                 'pre[ZA]' + \r
6276                 'hr[]' + \r
6277                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
6278                 'address[S|p]' + \r
6279                 'center[#|X|form|a|H|K|N|O|Q]' + \r
6280                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
6281                 'isindex[]' + \r
6282                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
6283                 'legend[S]' + \r
6284                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
6285                 'caption[S]' + \r
6286                 'col[]' + \r
6287                 'colgroup[col]' + \r
6288                 'thead[tr]' + \r
6289                 'tr[th|td]' + \r
6290                 'th[#|X|form|a|H|K|N|O|Q]' + \r
6291                 'form[#|X|a|H|K|N|O|Q]' + \r
6292                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
6293                 'td[#|X|form|a|H|K|N|O|Q]' + \r
6294                 'tfoot[tr]' + \r
6295                 'tbody[tr]' + \r
6296                 'area[]' + \r
6297                 'base[]' + \r
6298                 'body[#|X|form|a|H|K|N|O|Q]'\r
6299         );\r
6300 \r
6301         tinymce.dom.Schema = function() {\r
6302                 var t = this, elements = transitional;\r
6303 \r
6304                 t.isValid = function(name, child_name) {\r
6305                         var element = elements[name];\r
6306 \r
6307                         return !!(element && (!child_name || element[child_name]));\r
6308                 };\r
6309         };\r
6310 })();\r
6311 (function(tinymce) {\r
6312         tinymce.dom.RangeUtils = function(dom) {\r
6313                 var INVISIBLE_CHAR = '\uFEFF';\r
6314 \r
6315                 this.walk = function(rng, callback) {\r
6316                         var startContainer = rng.startContainer,\r
6317                                 startOffset = rng.startOffset,\r
6318                                 endContainer = rng.endContainer,\r
6319                                 endOffset = rng.endOffset,\r
6320                                 ancestor, startPoint,\r
6321                                 endPoint, node, parent, siblings, nodes;\r
6322 \r
6323                         // Handle table cell selection the table plugin enables\r
6324                         // you to fake select table cells and perform formatting actions on them\r
6325                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
6326                         if (nodes.length > 0) {\r
6327                                 tinymce.each(nodes, function(node) {\r
6328                                         callback([node]);\r
6329                                 });\r
6330 \r
6331                                 return;\r
6332                         }\r
6333 \r
6334                         function collectSiblings(node, name, end_node) {\r
6335                                 var siblings = [];\r
6336 \r
6337                                 for (; node && node != end_node; node = node[name])\r
6338                                         siblings.push(node);\r
6339 \r
6340                                 return siblings;\r
6341                         };\r
6342 \r
6343                         function findEndPoint(node, root) {\r
6344                                 do {\r
6345                                         if (node.parentNode == root)\r
6346                                                 return node;\r
6347 \r
6348                                         node = node.parentNode;\r
6349                                 } while(node);\r
6350                         };\r
6351 \r
6352                         function walkBoundary(start_node, end_node, next) {\r
6353                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
6354 \r
6355                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
6356                                         parent = node.parentNode;\r
6357                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
6358 \r
6359                                         if (siblings.length) {\r
6360                                                 if (!next)\r
6361                                                         siblings.reverse();\r
6362 \r
6363                                                 callback(siblings);\r
6364                                         }\r
6365                                 }\r
6366                         };\r
6367 \r
6368                         // If index based start position then resolve it\r
6369                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
6370                                 startContainer = startContainer.childNodes[startOffset];\r
6371 \r
6372                         // If index based end position then resolve it\r
6373                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
6374                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
6375 \r
6376                         // Find common ancestor and end points\r
6377                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
6378 \r
6379                         // Same container\r
6380                         if (startContainer == endContainer)\r
6381                                 return callback([startContainer]);\r
6382 \r
6383                         // Process left side\r
6384                         for (node = startContainer; node; node = node.parentNode) {\r
6385                                 if (node == endContainer)\r
6386                                         return walkBoundary(startContainer, ancestor, true);\r
6387 \r
6388                                 if (node == ancestor)\r
6389                                         break;\r
6390                         }\r
6391 \r
6392                         // Process right side\r
6393                         for (node = endContainer; node; node = node.parentNode) {\r
6394                                 if (node == startContainer)\r
6395                                         return walkBoundary(endContainer, ancestor);\r
6396 \r
6397                                 if (node == ancestor)\r
6398                                         break;\r
6399                         }\r
6400 \r
6401                         // Find start/end point\r
6402                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
6403                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
6404 \r
6405                         // Walk left leaf\r
6406                         walkBoundary(startContainer, startPoint, true);\r
6407 \r
6408                         // Walk the middle from start to end point\r
6409                         siblings = collectSiblings(\r
6410                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
6411                                 'nextSibling',\r
6412                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
6413                         );\r
6414 \r
6415                         if (siblings.length)\r
6416                                 callback(siblings);\r
6417 \r
6418                         // Walk right leaf\r
6419                         walkBoundary(endContainer, endPoint);\r
6420                 };\r
6421 \r
6422                 /*              this.split = function(rng) {\r
6423                         var startContainer = rng.startContainer,\r
6424                                 startOffset = rng.startOffset,\r
6425                                 endContainer = rng.endContainer,\r
6426                                 endOffset = rng.endOffset;\r
6427 \r
6428                         function splitText(node, offset) {\r
6429                                 if (offset == node.nodeValue.length)\r
6430                                         node.appendData(INVISIBLE_CHAR);\r
6431 \r
6432                                 node = node.splitText(offset);\r
6433 \r
6434                                 if (node.nodeValue === INVISIBLE_CHAR)\r
6435                                         node.nodeValue = '';\r
6436 \r
6437                                 return node;\r
6438                         };\r
6439 \r
6440                         // Handle single text node\r
6441                         if (startContainer == endContainer) {\r
6442                                 if (startContainer.nodeType == 3) {\r
6443                                         if (startOffset != 0)\r
6444                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
6445 \r
6446                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
6447                                                 splitText(startContainer, endOffset - startOffset);\r
6448                                 }\r
6449                         } else {\r
6450                                 // Split startContainer text node if needed\r
6451                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
6452                                         startContainer = splitText(startContainer, startOffset);\r
6453                                         startOffset = 0;\r
6454                                 }\r
6455 \r
6456                                 // Split endContainer text node if needed\r
6457                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
6458                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
6459                                         endOffset = endContainer.nodeValue.length;\r
6460                                 }\r
6461                         }\r
6462 \r
6463                         return {\r
6464                                 startContainer : startContainer,\r
6465                                 startOffset : startOffset,\r
6466                                 endContainer : endContainer,\r
6467                                 endOffset : endOffset\r
6468                         };\r
6469                 };\r
6470 */\r
6471         };\r
6472 \r
6473         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
6474                 if (rng1 && rng2) {\r
6475                         // Compare native IE ranges\r
6476                         if (rng1.item || rng1.duplicate) {\r
6477                                 // Both are control ranges and the selected element matches\r
6478                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
6479                                         return true;\r
6480 \r
6481                                 // Both are text ranges and the range matches\r
6482                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
6483                                         return true;\r
6484                         } else {\r
6485                                 // Compare w3c ranges\r
6486                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
6487                         }\r
6488                 }\r
6489 \r
6490                 return false;\r
6491         };\r
6492 })(tinymce);\r
6493 \r
6494 (function(tinymce) {\r
6495         // Shorten class names\r
6496         var DOM = tinymce.DOM, is = tinymce.is;\r
6497 \r
6498         tinymce.create('tinymce.ui.Control', {\r
6499                 Control : function(id, s) {\r
6500                         this.id = id;\r
6501                         this.settings = s = s || {};\r
6502                         this.rendered = false;\r
6503                         this.onRender = new tinymce.util.Dispatcher(this);\r
6504                         this.classPrefix = '';\r
6505                         this.scope = s.scope || this;\r
6506                         this.disabled = 0;\r
6507                         this.active = 0;\r
6508                 },\r
6509 \r
6510                 setDisabled : function(s) {\r
6511                         var e;\r
6512 \r
6513                         if (s != this.disabled) {\r
6514                                 e = DOM.get(this.id);\r
6515 \r
6516                                 // Add accessibility title for unavailable actions\r
6517                                 if (e && this.settings.unavailable_prefix) {\r
6518                                         if (s) {\r
6519                                                 this.prevTitle = e.title;\r
6520                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
6521                                         } else\r
6522                                                 e.title = this.prevTitle;\r
6523                                 }\r
6524 \r
6525                                 this.setState('Disabled', s);\r
6526                                 this.setState('Enabled', !s);\r
6527                                 this.disabled = s;\r
6528                         }\r
6529                 },\r
6530 \r
6531                 isDisabled : function() {\r
6532                         return this.disabled;\r
6533                 },\r
6534 \r
6535                 setActive : function(s) {\r
6536                         if (s != this.active) {\r
6537                                 this.setState('Active', s);\r
6538                                 this.active = s;\r
6539                         }\r
6540                 },\r
6541 \r
6542                 isActive : function() {\r
6543                         return this.active;\r
6544                 },\r
6545 \r
6546                 setState : function(c, s) {\r
6547                         var n = DOM.get(this.id);\r
6548 \r
6549                         c = this.classPrefix + c;\r
6550 \r
6551                         if (s)\r
6552                                 DOM.addClass(n, c);\r
6553                         else\r
6554                                 DOM.removeClass(n, c);\r
6555                 },\r
6556 \r
6557                 isRendered : function() {\r
6558                         return this.rendered;\r
6559                 },\r
6560 \r
6561                 renderHTML : function() {\r
6562                 },\r
6563 \r
6564                 renderTo : function(n) {\r
6565                         DOM.setHTML(n, this.renderHTML());\r
6566                 },\r
6567 \r
6568                 postRender : function() {\r
6569                         var t = this, b;\r
6570 \r
6571                         // Set pending states\r
6572                         if (is(t.disabled)) {\r
6573                                 b = t.disabled;\r
6574                                 t.disabled = -1;\r
6575                                 t.setDisabled(b);\r
6576                         }\r
6577 \r
6578                         if (is(t.active)) {\r
6579                                 b = t.active;\r
6580                                 t.active = -1;\r
6581                                 t.setActive(b);\r
6582                         }\r
6583                 },\r
6584 \r
6585                 remove : function() {\r
6586                         DOM.remove(this.id);\r
6587                         this.destroy();\r
6588                 },\r
6589 \r
6590                 destroy : function() {\r
6591                         tinymce.dom.Event.clear(this.id);\r
6592                 }\r
6593         });\r
6594 })(tinymce);\r
6595 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
6596         Container : function(id, s) {\r
6597                 this.parent(id, s);\r
6598 \r
6599                 this.controls = [];\r
6600 \r
6601                 this.lookup = {};\r
6602         },\r
6603 \r
6604         add : function(c) {\r
6605                 this.lookup[c.id] = c;\r
6606                 this.controls.push(c);\r
6607 \r
6608                 return c;\r
6609         },\r
6610 \r
6611         get : function(n) {\r
6612                 return this.lookup[n];\r
6613         }\r
6614 });\r
6615 \r
6616 \r
6617 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
6618         Separator : function(id, s) {\r
6619                 this.parent(id, s);\r
6620                 this.classPrefix = 'mceSeparator';\r
6621         },\r
6622 \r
6623         renderHTML : function() {\r
6624                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
6625         }\r
6626 });\r
6627 \r
6628 (function(tinymce) {\r
6629         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
6630 \r
6631         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
6632                 MenuItem : function(id, s) {\r
6633                         this.parent(id, s);\r
6634                         this.classPrefix = 'mceMenuItem';\r
6635                 },\r
6636 \r
6637                 setSelected : function(s) {\r
6638                         this.setState('Selected', s);\r
6639                         this.selected = s;\r
6640                 },\r
6641 \r
6642                 isSelected : function() {\r
6643                         return this.selected;\r
6644                 },\r
6645 \r
6646                 postRender : function() {\r
6647                         var t = this;\r
6648                         \r
6649                         t.parent();\r
6650 \r
6651                         // Set pending state\r
6652                         if (is(t.selected))\r
6653                                 t.setSelected(t.selected);\r
6654                 }\r
6655         });\r
6656 })(tinymce);\r
6657 \r
6658 (function(tinymce) {\r
6659         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
6660 \r
6661         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
6662                 Menu : function(id, s) {\r
6663                         var t = this;\r
6664 \r
6665                         t.parent(id, s);\r
6666                         t.items = {};\r
6667                         t.collapsed = false;\r
6668                         t.menuCount = 0;\r
6669                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
6670                 },\r
6671 \r
6672                 expand : function(d) {\r
6673                         var t = this;\r
6674 \r
6675                         if (d) {\r
6676                                 walk(t, function(o) {\r
6677                                         if (o.expand)\r
6678                                                 o.expand();\r
6679                                 }, 'items', t);\r
6680                         }\r
6681 \r
6682                         t.collapsed = false;\r
6683                 },\r
6684 \r
6685                 collapse : function(d) {\r
6686                         var t = this;\r
6687 \r
6688                         if (d) {\r
6689                                 walk(t, function(o) {\r
6690                                         if (o.collapse)\r
6691                                                 o.collapse();\r
6692                                 }, 'items', t);\r
6693                         }\r
6694 \r
6695                         t.collapsed = true;\r
6696                 },\r
6697 \r
6698                 isCollapsed : function() {\r
6699                         return this.collapsed;\r
6700                 },\r
6701 \r
6702                 add : function(o) {\r
6703                         if (!o.settings)\r
6704                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
6705 \r
6706                         this.onAddItem.dispatch(this, o);\r
6707 \r
6708                         return this.items[o.id] = o;\r
6709                 },\r
6710 \r
6711                 addSeparator : function() {\r
6712                         return this.add({separator : true});\r
6713                 },\r
6714 \r
6715                 addMenu : function(o) {\r
6716                         if (!o.collapse)\r
6717                                 o = this.createMenu(o);\r
6718 \r
6719                         this.menuCount++;\r
6720 \r
6721                         return this.add(o);\r
6722                 },\r
6723 \r
6724                 hasMenus : function() {\r
6725                         return this.menuCount !== 0;\r
6726                 },\r
6727 \r
6728                 remove : function(o) {\r
6729                         delete this.items[o.id];\r
6730                 },\r
6731 \r
6732                 removeAll : function() {\r
6733                         var t = this;\r
6734 \r
6735                         walk(t, function(o) {\r
6736                                 if (o.removeAll)\r
6737                                         o.removeAll();\r
6738                                 else\r
6739                                         o.remove();\r
6740 \r
6741                                 o.destroy();\r
6742                         }, 'items', t);\r
6743 \r
6744                         t.items = {};\r
6745                 },\r
6746 \r
6747                 createMenu : function(o) {\r
6748                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
6749 \r
6750                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
6751 \r
6752                         return m;\r
6753                 }\r
6754         });\r
6755 })(tinymce);\r
6756 (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);\r
7084 (function(tinymce) {\r
7085         var DOM = tinymce.DOM;\r
7086 \r
7087         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7088                 Button : function(id, s) {\r
7089                         this.parent(id, s);\r
7090                         this.classPrefix = 'mceButton';\r
7091                 },\r
7092 \r
7093                 renderHTML : function() {\r
7094                         var cp = this.classPrefix, s = this.settings, h, l;\r
7095 \r
7096                         l = DOM.encode(s.label || '');\r
7097                         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
7098 \r
7099                         if (s.image)\r
7100                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7101                         else\r
7102                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7103 \r
7104                         return h;\r
7105                 },\r
7106 \r
7107                 postRender : function() {\r
7108                         var t = this, s = t.settings;\r
7109 \r
7110                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7111                                 if (!t.isDisabled())\r
7112                                         return s.onclick.call(s.scope, e);\r
7113                         });\r
7114                 }\r
7115         });\r
7116 })(tinymce);\r
7117 \r
7118 (function(tinymce) {\r
7119         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7120 \r
7121         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
7122                 ListBox : function(id, s) {\r
7123                         var t = this;\r
7124 \r
7125                         t.parent(id, s);\r
7126 \r
7127                         t.items = [];\r
7128 \r
7129                         t.onChange = new Dispatcher(t);\r
7130 \r
7131                         t.onPostRender = new Dispatcher(t);\r
7132 \r
7133                         t.onAdd = new Dispatcher(t);\r
7134 \r
7135                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7136 \r
7137                         t.classPrefix = 'mceListBox';\r
7138                 },\r
7139 \r
7140                 select : function(va) {\r
7141                         var t = this, fv, f;\r
7142 \r
7143                         if (va == undefined)\r
7144                                 return t.selectByIndex(-1);\r
7145 \r
7146                         // Is string or number make function selector\r
7147                         if (va && va.call)\r
7148                                 f = va;\r
7149                         else {\r
7150                                 f = function(v) {\r
7151                                         return v == va;\r
7152                                 };\r
7153                         }\r
7154 \r
7155                         // Do we need to do something?\r
7156                         if (va != t.selectedValue) {\r
7157                                 // Find item\r
7158                                 each(t.items, function(o, i) {\r
7159                                         if (f(o.value)) {\r
7160                                                 fv = 1;\r
7161                                                 t.selectByIndex(i);\r
7162                                                 return false;\r
7163                                         }\r
7164                                 });\r
7165 \r
7166                                 if (!fv)\r
7167                                         t.selectByIndex(-1);\r
7168                         }\r
7169                 },\r
7170 \r
7171                 selectByIndex : function(idx) {\r
7172                         var t = this, e, o;\r
7173 \r
7174                         if (idx != t.selectedIndex) {\r
7175                                 e = DOM.get(t.id + '_text');\r
7176                                 o = t.items[idx];\r
7177 \r
7178                                 if (o) {\r
7179                                         t.selectedValue = o.value;\r
7180                                         t.selectedIndex = idx;\r
7181                                         DOM.setHTML(e, DOM.encode(o.title));\r
7182                                         DOM.removeClass(e, 'mceTitle');\r
7183                                 } else {\r
7184                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
7185                                         DOM.addClass(e, 'mceTitle');\r
7186                                         t.selectedValue = t.selectedIndex = null;\r
7187                                 }\r
7188 \r
7189                                 e = 0;\r
7190                         }\r
7191                 },\r
7192 \r
7193                 add : function(n, v, o) {\r
7194                         var t = this;\r
7195 \r
7196                         o = o || {};\r
7197                         o = tinymce.extend(o, {\r
7198                                 title : n,\r
7199                                 value : v\r
7200                         });\r
7201 \r
7202                         t.items.push(o);\r
7203                         t.onAdd.dispatch(t, o);\r
7204                 },\r
7205 \r
7206                 getLength : function() {\r
7207                         return this.items.length;\r
7208                 },\r
7209 \r
7210                 renderHTML : function() {\r
7211                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
7212 \r
7213                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
7214                         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
7215                         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
7216                         h += '</tr></tbody></table>';\r
7217 \r
7218                         return h;\r
7219                 },\r
7220 \r
7221                 showMenu : function() {\r
7222                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
7223 \r
7224                         if (t.isDisabled() || t.items.length == 0)\r
7225                                 return;\r
7226 \r
7227                         if (t.menu && t.menu.isMenuVisible)\r
7228                                 return t.hideMenu();\r
7229 \r
7230                         if (!t.isMenuRendered) {\r
7231                                 t.renderMenu();\r
7232                                 t.isMenuRendered = true;\r
7233                         }\r
7234 \r
7235                         p1 = DOM.getPos(this.settings.menu_container);\r
7236                         p2 = DOM.getPos(e);\r
7237 \r
7238                         m = t.menu;\r
7239                         m.settings.offset_x = p2.x;\r
7240                         m.settings.offset_y = p2.y;\r
7241                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
7242 \r
7243                         // Select in menu\r
7244                         if (t.oldID)\r
7245                                 m.items[t.oldID].setSelected(0);\r
7246 \r
7247                         each(t.items, function(o) {\r
7248                                 if (o.value === t.selectedValue) {\r
7249                                         m.items[o.id].setSelected(1);\r
7250                                         t.oldID = o.id;\r
7251                                 }\r
7252                         });\r
7253 \r
7254                         m.showMenu(0, e.clientHeight);\r
7255 \r
7256                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7257                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
7258 \r
7259                         //DOM.get(t.id + '_text').focus();\r
7260                 },\r
7261 \r
7262                 hideMenu : function(e) {\r
7263                         var t = this;\r
7264 \r
7265                         if (t.menu && t.menu.isMenuVisible) {\r
7266                                 // Prevent double toogles by canceling the mouse click event to the button\r
7267                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
7268                                         return;\r
7269 \r
7270                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7271                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
7272                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7273                                         t.menu.hideMenu();\r
7274                                 }\r
7275                         }\r
7276                 },\r
7277 \r
7278                 renderMenu : function() {\r
7279                         var t = this, m;\r
7280 \r
7281                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7282                                 menu_line : 1,\r
7283                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
7284                                 max_width : 150,\r
7285                                 max_height : 150\r
7286                         });\r
7287 \r
7288                         m.onHideMenu.add(t.hideMenu, t);\r
7289 \r
7290                         m.add({\r
7291                                 title : t.settings.title,\r
7292                                 'class' : 'mceMenuItemTitle',\r
7293                                 onclick : function() {\r
7294                                         if (t.settings.onselect('') !== false)\r
7295                                                 t.select(''); // Must be runned after\r
7296                                 }\r
7297                         });\r
7298 \r
7299                         each(t.items, function(o) {\r
7300                                 // No value then treat it as a title\r
7301                                 if (o.value === undefined) {\r
7302                                         m.add({\r
7303                                                 title : o.title,\r
7304                                                 'class' : 'mceMenuItemTitle',\r
7305                                                 onclick : function() {\r
7306                                                         if (t.settings.onselect('') !== false)\r
7307                                                                 t.select(''); // Must be runned after\r
7308                                                 }\r
7309                                         });\r
7310                                 } else {\r
7311                                         o.id = DOM.uniqueId();\r
7312                                         o.onclick = function() {\r
7313                                                 if (t.settings.onselect(o.value) !== false)\r
7314                                                         t.select(o.value); // Must be runned after\r
7315                                         };\r
7316 \r
7317                                         m.add(o);\r
7318                                 }\r
7319                         });\r
7320 \r
7321                         t.onRenderMenu.dispatch(t, m);\r
7322                         t.menu = m;\r
7323                 },\r
7324 \r
7325                 postRender : function() {\r
7326                         var t = this, cp = t.classPrefix;\r
7327 \r
7328                         Event.add(t.id, 'click', t.showMenu, t);\r
7329                         Event.add(t.id + '_text', 'focus', function() {\r
7330                                 if (!t._focused) {\r
7331                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
7332                                                 var idx = -1, v, kc = e.keyCode;\r
7333 \r
7334                                                 // Find current index\r
7335                                                 each(t.items, function(v, i) {\r
7336                                                         if (t.selectedValue == v.value)\r
7337                                                                 idx = i;\r
7338                                                 });\r
7339 \r
7340                                                 // Move up/down\r
7341                                                 if (kc == 38)\r
7342                                                         v = t.items[idx - 1];\r
7343                                                 else if (kc == 40)\r
7344                                                         v = t.items[idx + 1];\r
7345                                                 else if (kc == 13) {\r
7346                                                         // Fake select on enter\r
7347                                                         v = t.selectedValue;\r
7348                                                         t.selectedValue = null; // Needs to be null to fake change\r
7349                                                         t.settings.onselect(v);\r
7350                                                         return Event.cancel(e);\r
7351                                                 }\r
7352 \r
7353                                                 if (v) {\r
7354                                                         t.hideMenu();\r
7355                                                         t.select(v.value);\r
7356                                                 }\r
7357                                         });\r
7358                                 }\r
7359 \r
7360                                 t._focused = 1;\r
7361                         });\r
7362                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
7363 \r
7364                         // Old IE doesn't have hover on all elements\r
7365                         if (tinymce.isIE6 || !DOM.boxModel) {\r
7366                                 Event.add(t.id, 'mouseover', function() {\r
7367                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7368                                                 DOM.addClass(t.id, cp + 'Hover');\r
7369                                 });\r
7370 \r
7371                                 Event.add(t.id, 'mouseout', function() {\r
7372                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7373                                                 DOM.removeClass(t.id, cp + 'Hover');\r
7374                                 });\r
7375                         }\r
7376 \r
7377                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
7378                 },\r
7379 \r
7380                 destroy : function() {\r
7381                         this.parent();\r
7382 \r
7383                         Event.clear(this.id + '_text');\r
7384                         Event.clear(this.id + '_open');\r
7385                 }\r
7386         });\r
7387 })(tinymce);\r
7388 (function(tinymce) {\r
7389         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7390 \r
7391         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
7392                 NativeListBox : function(id, s) {\r
7393                         this.parent(id, s);\r
7394                         this.classPrefix = 'mceNativeListBox';\r
7395                 },\r
7396 \r
7397                 setDisabled : function(s) {\r
7398                         DOM.get(this.id).disabled = s;\r
7399                 },\r
7400 \r
7401                 isDisabled : function() {\r
7402                         return DOM.get(this.id).disabled;\r
7403                 },\r
7404 \r
7405                 select : function(va) {\r
7406                         var t = this, fv, f;\r
7407 \r
7408                         if (va == undefined)\r
7409                                 return t.selectByIndex(-1);\r
7410 \r
7411                         // Is string or number make function selector\r
7412                         if (va && va.call)\r
7413                                 f = va;\r
7414                         else {\r
7415                                 f = function(v) {\r
7416                                         return v == va;\r
7417                                 };\r
7418                         }\r
7419 \r
7420                         // Do we need to do something?\r
7421                         if (va != t.selectedValue) {\r
7422                                 // Find item\r
7423                                 each(t.items, function(o, i) {\r
7424                                         if (f(o.value)) {\r
7425                                                 fv = 1;\r
7426                                                 t.selectByIndex(i);\r
7427                                                 return false;\r
7428                                         }\r
7429                                 });\r
7430 \r
7431                                 if (!fv)\r
7432                                         t.selectByIndex(-1);\r
7433                         }\r
7434                 },\r
7435 \r
7436                 selectByIndex : function(idx) {\r
7437                         DOM.get(this.id).selectedIndex = idx + 1;\r
7438                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
7439                 },\r
7440 \r
7441                 add : function(n, v, a) {\r
7442                         var o, t = this;\r
7443 \r
7444                         a = a || {};\r
7445                         a.value = v;\r
7446 \r
7447                         if (t.isRendered())\r
7448                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
7449 \r
7450                         o = {\r
7451                                 title : n,\r
7452                                 value : v,\r
7453                                 attribs : a\r
7454                         };\r
7455 \r
7456                         t.items.push(o);\r
7457                         t.onAdd.dispatch(t, o);\r
7458                 },\r
7459 \r
7460                 getLength : function() {\r
7461                         return this.items.length;\r
7462                 },\r
7463 \r
7464                 renderHTML : function() {\r
7465                         var h, t = this;\r
7466 \r
7467                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
7468 \r
7469                         each(t.items, function(it) {\r
7470                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
7471                         });\r
7472 \r
7473                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
7474 \r
7475                         return h;\r
7476                 },\r
7477 \r
7478                 postRender : function() {\r
7479                         var t = this, ch;\r
7480 \r
7481                         t.rendered = true;\r
7482 \r
7483                         function onChange(e) {\r
7484                                 var v = t.items[e.target.selectedIndex - 1];\r
7485 \r
7486                                 if (v && (v = v.value)) {\r
7487                                         t.onChange.dispatch(t, v);\r
7488 \r
7489                                         if (t.settings.onselect)\r
7490                                                 t.settings.onselect(v);\r
7491                                 }\r
7492                         };\r
7493 \r
7494                         Event.add(t.id, 'change', onChange);\r
7495 \r
7496                         // Accessibility keyhandler\r
7497                         Event.add(t.id, 'keydown', function(e) {\r
7498                                 var bf;\r
7499 \r
7500                                 Event.remove(t.id, 'change', ch);\r
7501 \r
7502                                 bf = Event.add(t.id, 'blur', function() {\r
7503                                         Event.add(t.id, 'change', onChange);\r
7504                                         Event.remove(t.id, 'blur', bf);\r
7505                                 });\r
7506 \r
7507                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
7508                                         onChange(e);\r
7509                                         return Event.cancel(e);\r
7510                                 }\r
7511                         });\r
7512 \r
7513                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
7514                 }\r
7515         });\r
7516 })(tinymce);\r
7517 (function(tinymce) {\r
7518         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
7519 \r
7520         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
7521                 MenuButton : function(id, s) {\r
7522                         this.parent(id, s);\r
7523 \r
7524                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7525 \r
7526                         s.menu_container = s.menu_container || DOM.doc.body;\r
7527                 },\r
7528 \r
7529                 showMenu : function() {\r
7530                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
7531 \r
7532                         if (t.isDisabled())\r
7533                                 return;\r
7534 \r
7535                         if (!t.isMenuRendered) {\r
7536                                 t.renderMenu();\r
7537                                 t.isMenuRendered = true;\r
7538                         }\r
7539 \r
7540                         if (t.isMenuVisible)\r
7541                                 return t.hideMenu();\r
7542 \r
7543                         p1 = DOM.getPos(t.settings.menu_container);\r
7544                         p2 = DOM.getPos(e);\r
7545 \r
7546                         m = t.menu;\r
7547                         m.settings.offset_x = p2.x;\r
7548                         m.settings.offset_y = p2.y;\r
7549                         m.settings.vp_offset_x = p2.x;\r
7550                         m.settings.vp_offset_y = p2.y;\r
7551                         m.settings.keyboard_focus = t._focused;\r
7552                         m.showMenu(0, e.clientHeight);\r
7553 \r
7554                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7555                         t.setState('Selected', 1);\r
7556 \r
7557                         t.isMenuVisible = 1;\r
7558                 },\r
7559 \r
7560                 renderMenu : function() {\r
7561                         var t = this, m;\r
7562 \r
7563                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7564                                 menu_line : 1,\r
7565                                 'class' : this.classPrefix + 'Menu',\r
7566                                 icons : t.settings.icons\r
7567                         });\r
7568 \r
7569                         m.onHideMenu.add(t.hideMenu, t);\r
7570 \r
7571                         t.onRenderMenu.dispatch(t, m);\r
7572                         t.menu = m;\r
7573                 },\r
7574 \r
7575                 hideMenu : function(e) {\r
7576                         var t = this;\r
7577 \r
7578                         // Prevent double toogles by canceling the mouse click event to the button\r
7579                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
7580                                 return;\r
7581 \r
7582                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7583                                 t.setState('Selected', 0);\r
7584                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7585                                 if (t.menu)\r
7586                                         t.menu.hideMenu();\r
7587                         }\r
7588 \r
7589                         t.isMenuVisible = 0;\r
7590                 },\r
7591 \r
7592                 postRender : function() {\r
7593                         var t = this, s = t.settings;\r
7594 \r
7595                         Event.add(t.id, 'click', function() {\r
7596                                 if (!t.isDisabled()) {\r
7597                                         if (s.onclick)\r
7598                                                 s.onclick(t.value);\r
7599 \r
7600                                         t.showMenu();\r
7601                                 }\r
7602                         });\r
7603                 }\r
7604         });\r
7605 })(tinymce);\r
7606 \r
7607 (function(tinymce) {\r
7608         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
7609 \r
7610         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
7611                 SplitButton : function(id, s) {\r
7612                         this.parent(id, s);\r
7613                         this.classPrefix = 'mceSplitButton';\r
7614                 },\r
7615 \r
7616                 renderHTML : function() {\r
7617                         var h, t = this, s = t.settings, h1;\r
7618 \r
7619                         h = '<tbody><tr>';\r
7620 \r
7621                         if (s.image)\r
7622                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
7623                         else\r
7624                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
7625 \r
7626                         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
7627         \r
7628                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
7629                         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
7630 \r
7631                         h += '</tr></tbody>';\r
7632 \r
7633                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
7634                 },\r
7635 \r
7636                 postRender : function() {\r
7637                         var t = this, s = t.settings;\r
7638 \r
7639                         if (s.onclick) {\r
7640                                 Event.add(t.id + '_action', 'click', function() {\r
7641                                         if (!t.isDisabled())\r
7642                                                 s.onclick(t.value);\r
7643                                 });\r
7644                         }\r
7645 \r
7646                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
7647                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
7648                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
7649 \r
7650                         // Old IE doesn't have hover on all elements\r
7651                         if (tinymce.isIE6 || !DOM.boxModel) {\r
7652                                 Event.add(t.id, 'mouseover', function() {\r
7653                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
7654                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
7655                                 });\r
7656 \r
7657                                 Event.add(t.id, 'mouseout', function() {\r
7658                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
7659                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
7660                                 });\r
7661                         }\r
7662                 },\r
7663 \r
7664                 destroy : function() {\r
7665                         this.parent();\r
7666 \r
7667                         Event.clear(this.id + '_action');\r
7668                         Event.clear(this.id + '_open');\r
7669                 }\r
7670         });\r
7671 })(tinymce);\r
7672 \r
7673 (function(tinymce) {\r
7674         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
7675 \r
7676         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
7677                 ColorSplitButton : function(id, s) {\r
7678                         var t = this;\r
7679 \r
7680                         t.parent(id, s);\r
7681 \r
7682                         t.settings = s = tinymce.extend({\r
7683                                 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
7684                                 grid_width : 8,\r
7685                                 default_color : '#888888'\r
7686                         }, t.settings);\r
7687 \r
7688                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
7689 \r
7690                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
7691 \r
7692                         t.value = s.default_color;\r
7693                 },\r
7694 \r
7695                 showMenu : function() {\r
7696                         var t = this, r, p, e, p2;\r
7697 \r
7698                         if (t.isDisabled())\r
7699                                 return;\r
7700 \r
7701                         if (!t.isMenuRendered) {\r
7702                                 t.renderMenu();\r
7703                                 t.isMenuRendered = true;\r
7704                         }\r
7705 \r
7706                         if (t.isMenuVisible)\r
7707                                 return t.hideMenu();\r
7708 \r
7709                         e = DOM.get(t.id);\r
7710                         DOM.show(t.id + '_menu');\r
7711                         DOM.addClass(e, 'mceSplitButtonSelected');\r
7712                         p2 = DOM.getPos(e);\r
7713                         DOM.setStyles(t.id + '_menu', {\r
7714                                 left : p2.x,\r
7715                                 top : p2.y + e.clientHeight,\r
7716                                 zIndex : 200000\r
7717                         });\r
7718                         e = 0;\r
7719 \r
7720                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7721                         t.onShowMenu.dispatch(t);\r
7722 \r
7723                         if (t._focused) {\r
7724                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
7725                                         if (e.keyCode == 27)\r
7726                                                 t.hideMenu();\r
7727                                 });\r
7728 \r
7729                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
7730                         }\r
7731 \r
7732                         t.isMenuVisible = 1;\r
7733                 },\r
7734 \r
7735                 hideMenu : function(e) {\r
7736                         var t = this;\r
7737 \r
7738                         // Prevent double toogles by canceling the mouse click event to the button\r
7739                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
7740                                 return;\r
7741 \r
7742                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
7743                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
7744                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7745                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
7746                                 DOM.hide(t.id + '_menu');\r
7747                         }\r
7748 \r
7749                         t.onHideMenu.dispatch(t);\r
7750 \r
7751                         t.isMenuVisible = 0;\r
7752                 },\r
7753 \r
7754                 renderMenu : function() {\r
7755                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
7756 \r
7757                         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
7758                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
7759                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
7760 \r
7761                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
7762                         tb = DOM.add(n, 'tbody');\r
7763 \r
7764                         // Generate color grid\r
7765                         i = 0;\r
7766                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
7767                                 c = c.replace(/^#/, '');\r
7768 \r
7769                                 if (!i--) {\r
7770                                         tr = DOM.add(tb, 'tr');\r
7771                                         i = s.grid_width - 1;\r
7772                                 }\r
7773 \r
7774                                 n = DOM.add(tr, 'td');\r
7775 \r
7776                                 n = DOM.add(n, 'a', {\r
7777                                         href : 'javascript:;',\r
7778                                         style : {\r
7779                                                 backgroundColor : '#' + c\r
7780                                         },\r
7781                                         _mce_color : '#' + c\r
7782                                 });\r
7783                         });\r
7784 \r
7785                         if (s.more_colors_func) {\r
7786                                 n = DOM.add(tb, 'tr');\r
7787                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
7788                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
7789 \r
7790                                 Event.add(n, 'click', function(e) {\r
7791                                         s.more_colors_func.call(s.more_colors_scope || this);\r
7792                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7793                                 });\r
7794                         }\r
7795 \r
7796                         DOM.addClass(m, 'mceColorSplitMenu');\r
7797 \r
7798                         Event.add(t.id + '_menu', 'click', function(e) {\r
7799                                 var c;\r
7800 \r
7801                                 e = e.target;\r
7802 \r
7803                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
7804                                         t.setColor(c);\r
7805 \r
7806                                 return Event.cancel(e); // Prevent IE auto save warning\r
7807                         });\r
7808 \r
7809                         return w;\r
7810                 },\r
7811 \r
7812                 setColor : function(c) {\r
7813                         var t = this;\r
7814 \r
7815                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
7816 \r
7817                         t.value = c;\r
7818                         t.hideMenu();\r
7819                         t.settings.onselect(c);\r
7820                 },\r
7821 \r
7822                 postRender : function() {\r
7823                         var t = this, id = t.id;\r
7824 \r
7825                         t.parent();\r
7826                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
7827                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
7828                 },\r
7829 \r
7830                 destroy : function() {\r
7831                         this.parent();\r
7832 \r
7833                         Event.clear(this.id + '_menu');\r
7834                         Event.clear(this.id + '_more');\r
7835                         DOM.remove(this.id + '_menu');\r
7836                 }\r
7837         });\r
7838 })(tinymce);\r
7839 \r
7840 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
7841         renderHTML : function() {\r
7842                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
7843 \r
7844                 cl = t.controls;\r
7845                 for (i=0; i<cl.length; i++) {\r
7846                         // Get current control, prev control, next control and if the control is a list box or not\r
7847                         co = cl[i];\r
7848                         pr = cl[i - 1];\r
7849                         nx = cl[i + 1];\r
7850 \r
7851                         // Add toolbar start\r
7852                         if (i === 0) {\r
7853                                 c = 'mceToolbarStart';\r
7854 \r
7855                                 if (co.Button)\r
7856                                         c += ' mceToolbarStartButton';\r
7857                                 else if (co.SplitButton)\r
7858                                         c += ' mceToolbarStartSplitButton';\r
7859                                 else if (co.ListBox)\r
7860                                         c += ' mceToolbarStartListBox';\r
7861 \r
7862                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
7863                         }\r
7864 \r
7865                         // Add toolbar end before list box and after the previous button\r
7866                         // This is to fix the o2k7 editor skins\r
7867                         if (pr && co.ListBox) {\r
7868                                 if (pr.Button || pr.SplitButton)\r
7869                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
7870                         }\r
7871 \r
7872                         // Render control HTML\r
7873 \r
7874                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
7875                         if (dom.stdMode)\r
7876                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
7877                         else\r
7878                                 h += '<td>' + co.renderHTML() + '</td>';\r
7879 \r
7880                         // Add toolbar start after list box and before the next button\r
7881                         // This is to fix the o2k7 editor skins\r
7882                         if (nx && co.ListBox) {\r
7883                                 if (nx.Button || nx.SplitButton)\r
7884                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
7885                         }\r
7886                 }\r
7887 \r
7888                 c = 'mceToolbarEnd';\r
7889 \r
7890                 if (co.Button)\r
7891                         c += ' mceToolbarEndButton';\r
7892                 else if (co.SplitButton)\r
7893                         c += ' mceToolbarEndSplitButton';\r
7894                 else if (co.ListBox)\r
7895                         c += ' mceToolbarEndListBox';\r
7896 \r
7897                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
7898 \r
7899                 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
7900         }\r
7901 });\r
7902 \r
7903 (function(tinymce) {\r
7904         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
7905 \r
7906         tinymce.create('tinymce.AddOnManager', {\r
7907                 items : [],\r
7908                 urls : {},\r
7909                 lookup : {},\r
7910 \r
7911                 onAdd : new Dispatcher(this),\r
7912 \r
7913                 get : function(n) {\r
7914                         return this.lookup[n];\r
7915                 },\r
7916 \r
7917                 requireLangPack : function(n) {\r
7918                         var s = tinymce.settings;\r
7919 \r
7920                         if (s && s.language)\r
7921                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
7922                 },\r
7923 \r
7924                 add : function(id, o) {\r
7925                         this.items.push(o);\r
7926                         this.lookup[id] = o;\r
7927                         this.onAdd.dispatch(this, id, o);\r
7928 \r
7929                         return o;\r
7930                 },\r
7931 \r
7932                 load : function(n, u, cb, s) {\r
7933                         var t = this;\r
7934 \r
7935                         if (t.urls[n])\r
7936                                 return;\r
7937 \r
7938                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
7939                                 u = tinymce.baseURL + '/' +  u;\r
7940 \r
7941                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
7942                         tinymce.ScriptLoader.add(u, cb, s);\r
7943                 }\r
7944         });\r
7945 \r
7946         // Create plugin and theme managers\r
7947         tinymce.PluginManager = new tinymce.AddOnManager();\r
7948         tinymce.ThemeManager = new tinymce.AddOnManager();\r
7949 }(tinymce));\r
7950 \r
7951 (function(tinymce) {\r
7952         // Shorten names\r
7953         var each = tinymce.each, extend = tinymce.extend,\r
7954                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
7955                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
7956                 explode = tinymce.explode,\r
7957                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
7958 \r
7959         // Setup some URLs where the editor API is located and where the document is\r
7960         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
7961         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
7962                 tinymce.documentBaseURL += '/';\r
7963 \r
7964         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
7965 \r
7966         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
7967 \r
7968         // Add before unload listener\r
7969         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
7970         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
7971         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
7972 \r
7973         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
7974         Event.add(window, 'beforeunload', function(e) {\r
7975                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
7976         });\r
7977 \r
7978         tinymce.onAddEditor = new Dispatcher(tinymce);\r
7979 \r
7980         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
7981 \r
7982         tinymce.EditorManager = extend(tinymce, {\r
7983                 editors : [],\r
7984 \r
7985                 i18n : {},\r
7986 \r
7987                 activeEditor : null,\r
7988 \r
7989                 init : function(s) {\r
7990                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
7991 \r
7992                         function execCallback(se, n, s) {\r
7993                                 var f = se[n];\r
7994 \r
7995                                 if (!f)\r
7996                                         return;\r
7997 \r
7998                                 if (tinymce.is(f, 'string')) {\r
7999                                         s = f.replace(/\.\w+$/, '');\r
8000                                         s = s ? tinymce.resolve(s) : 0;\r
8001                                         f = tinymce.resolve(f);\r
8002                                 }\r
8003 \r
8004                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8005                         };\r
8006 \r
8007                         s = extend({\r
8008                                 theme : "simple",\r
8009                                 language : "en"\r
8010                         }, s);\r
8011 \r
8012                         t.settings = s;\r
8013 \r
8014                         // Legacy call\r
8015                         Event.add(document, 'init', function() {\r
8016                                 var l, co;\r
8017 \r
8018                                 execCallback(s, 'onpageload');\r
8019 \r
8020                                 switch (s.mode) {\r
8021                                         case "exact":\r
8022                                                 l = s.elements || '';\r
8023 \r
8024                                                 if(l.length > 0) {\r
8025                                                         each(explode(l), function(v) {\r
8026                                                                 if (DOM.get(v)) {\r
8027                                                                         ed = new tinymce.Editor(v, s);\r
8028                                                                         el.push(ed);\r
8029                                                                         ed.render(1);\r
8030                                                                 } else {\r
8031                                                                         each(document.forms, function(f) {\r
8032                                                                                 each(f.elements, function(e) {\r
8033                                                                                         if (e.name === v) {\r
8034                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8035                                                                                                 DOM.setAttrib(e, 'id', v);\r
8036 \r
8037                                                                                                 ed = new tinymce.Editor(v, s);\r
8038                                                                                                 el.push(ed);\r
8039                                                                                                 ed.render(1);\r
8040                                                                                         }\r
8041                                                                                 });\r
8042                                                                         });\r
8043                                                                 }\r
8044                                                         });\r
8045                                                 }\r
8046                                                 break;\r
8047 \r
8048                                         case "textareas":\r
8049                                         case "specific_textareas":\r
8050                                                 function hasClass(n, c) {\r
8051                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8052                                                 };\r
8053 \r
8054                                                 each(DOM.select('textarea'), function(v) {\r
8055                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8056                                                                 return;\r
8057 \r
8058                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8059                                                                 // Can we use the name\r
8060                                                                 e = DOM.get(v.name);\r
8061                                                                 if (!v.id && !e)\r
8062                                                                         v.id = v.name;\r
8063 \r
8064                                                                 // Generate unique name if missing or already exists\r
8065                                                                 if (!v.id || t.get(v.id))\r
8066                                                                         v.id = DOM.uniqueId();\r
8067 \r
8068                                                                 ed = new tinymce.Editor(v.id, s);\r
8069                                                                 el.push(ed);\r
8070                                                                 ed.render(1);\r
8071                                                         }\r
8072                                                 });\r
8073                                                 break;\r
8074                                 }\r
8075 \r
8076                                 // Call onInit when all editors are initialized\r
8077                                 if (s.oninit) {\r
8078                                         l = co = 0;\r
8079 \r
8080                                         each(el, function(ed) {\r
8081                                                 co++;\r
8082 \r
8083                                                 if (!ed.initialized) {\r
8084                                                         // Wait for it\r
8085                                                         ed.onInit.add(function() {\r
8086                                                                 l++;\r
8087 \r
8088                                                                 // All done\r
8089                                                                 if (l == co)\r
8090                                                                         execCallback(s, 'oninit');\r
8091                                                         });\r
8092                                                 } else\r
8093                                                         l++;\r
8094 \r
8095                                                 // All done\r
8096                                                 if (l == co)\r
8097                                                         execCallback(s, 'oninit');                                      \r
8098                                         });\r
8099                                 }\r
8100                         });\r
8101                 },\r
8102 \r
8103                 get : function(id) {\r
8104                         if (id === undefined)\r
8105                                 return this.editors;\r
8106 \r
8107                         return this.editors[id];\r
8108                 },\r
8109 \r
8110                 getInstanceById : function(id) {\r
8111                         return this.get(id);\r
8112                 },\r
8113 \r
8114                 add : function(editor) {\r
8115                         var self = this, editors = self.editors;\r
8116 \r
8117                         // Add named and index editor instance\r
8118                         editors[editor.id] = editor;\r
8119                         editors.push(editor);\r
8120 \r
8121                         self._setActive(editor);\r
8122                         self.onAddEditor.dispatch(self, editor);\r
8123 \r
8124 \r
8125                         // Patch the tinymce.Editor instance with jQuery adapter logic\r
8126                         if (tinymce.adapter)\r
8127                                 tinymce.adapter.patchEditor(editor);\r
8128 \r
8129 \r
8130                         return editor;\r
8131                 },\r
8132 \r
8133                 remove : function(editor) {\r
8134                         var t = this, i, editors = t.editors;\r
8135 \r
8136                         // Not in the collection\r
8137                         if (!editors[editor.id])\r
8138                                 return null;\r
8139 \r
8140                         delete editors[editor.id];\r
8141 \r
8142                         for (i = 0; i < editors.length; i++) {\r
8143                                 if (editors[i] == editor) {\r
8144                                         editors.splice(i, 1);\r
8145                                         break;\r
8146                                 }\r
8147                         }\r
8148 \r
8149                         // Select another editor since the active one was removed\r
8150                         if (t.activeEditor == editor)\r
8151                                 t._setActive(editors[0]);\r
8152 \r
8153                         editor.destroy();\r
8154                         t.onRemoveEditor.dispatch(t, editor);\r
8155 \r
8156                         return editor;\r
8157                 },\r
8158 \r
8159                 execCommand : function(c, u, v) {\r
8160                         var t = this, ed = t.get(v), w;\r
8161 \r
8162                         // Manager commands\r
8163                         switch (c) {\r
8164                                 case "mceFocus":\r
8165                                         ed.focus();\r
8166                                         return true;\r
8167 \r
8168                                 case "mceAddEditor":\r
8169                                 case "mceAddControl":\r
8170                                         if (!t.get(v))\r
8171                                                 new tinymce.Editor(v, t.settings).render();\r
8172 \r
8173                                         return true;\r
8174 \r
8175                                 case "mceAddFrameControl":\r
8176                                         w = v.window;\r
8177 \r
8178                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
8179                                         w.tinyMCE = tinyMCE;\r
8180                                         w.tinymce = tinymce;\r
8181 \r
8182                                         tinymce.DOM.doc = w.document;\r
8183                                         tinymce.DOM.win = w;\r
8184 \r
8185                                         ed = new tinymce.Editor(v.element_id, v);\r
8186                                         ed.render();\r
8187 \r
8188                                         // Fix IE memory leaks\r
8189                                         if (tinymce.isIE) {\r
8190                                                 function clr() {\r
8191                                                         ed.destroy();\r
8192                                                         w.detachEvent('onunload', clr);\r
8193                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
8194                                                 };\r
8195 \r
8196                                                 w.attachEvent('onunload', clr);\r
8197                                         }\r
8198 \r
8199                                         v.page_window = null;\r
8200 \r
8201                                         return true;\r
8202 \r
8203                                 case "mceRemoveEditor":\r
8204                                 case "mceRemoveControl":\r
8205                                         if (ed)\r
8206                                                 ed.remove();\r
8207 \r
8208                                         return true;\r
8209 \r
8210                                 case 'mceToggleEditor':\r
8211                                         if (!ed) {\r
8212                                                 t.execCommand('mceAddControl', 0, v);\r
8213                                                 return true;\r
8214                                         }\r
8215 \r
8216                                         if (ed.isHidden())\r
8217                                                 ed.show();\r
8218                                         else\r
8219                                                 ed.hide();\r
8220 \r
8221                                         return true;\r
8222                         }\r
8223 \r
8224                         // Run command on active editor\r
8225                         if (t.activeEditor)\r
8226                                 return t.activeEditor.execCommand(c, u, v);\r
8227 \r
8228                         return false;\r
8229                 },\r
8230 \r
8231                 execInstanceCommand : function(id, c, u, v) {\r
8232                         var ed = this.get(id);\r
8233 \r
8234                         if (ed)\r
8235                                 return ed.execCommand(c, u, v);\r
8236 \r
8237                         return false;\r
8238                 },\r
8239 \r
8240                 triggerSave : function() {\r
8241                         each(this.editors, function(e) {\r
8242                                 e.save();\r
8243                         });\r
8244                 },\r
8245 \r
8246                 addI18n : function(p, o) {\r
8247                         var lo, i18n = this.i18n;\r
8248 \r
8249                         if (!tinymce.is(p, 'string')) {\r
8250                                 each(p, function(o, lc) {\r
8251                                         each(o, function(o, g) {\r
8252                                                 each(o, function(o, k) {\r
8253                                                         if (g === 'common')\r
8254                                                                 i18n[lc + '.' + k] = o;\r
8255                                                         else\r
8256                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
8257                                                 });\r
8258                                         });\r
8259                                 });\r
8260                         } else {\r
8261                                 each(o, function(o, k) {\r
8262                                         i18n[p + '.' + k] = o;\r
8263                                 });\r
8264                         }\r
8265                 },\r
8266 \r
8267                 // Private methods\r
8268 \r
8269                 _setActive : function(editor) {\r
8270                         this.selectedInstance = this.activeEditor = editor;\r
8271                 }\r
8272         });\r
8273 })(tinymce);\r
8274 \r
8275 (function(tinymce) {\r
8276         // Shorten these names\r
8277         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
8278                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
8279                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
8280                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8281                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
8282 \r
8283         tinymce.create('tinymce.Editor', {\r
8284                 Editor : function(id, s) {\r
8285                         var t = this;\r
8286 \r
8287                         t.id = t.editorId = id;\r
8288 \r
8289                         t.execCommands = {};\r
8290                         t.queryStateCommands = {};\r
8291                         t.queryValueCommands = {};\r
8292 \r
8293                         t.isNotDirty = false;\r
8294 \r
8295                         t.plugins = {};\r
8296 \r
8297                         // Add events to the editor\r
8298                         each([\r
8299                                 'onPreInit',\r
8300 \r
8301                                 'onBeforeRenderUI',\r
8302 \r
8303                                 'onPostRender',\r
8304 \r
8305                                 'onInit',\r
8306 \r
8307                                 'onRemove',\r
8308 \r
8309                                 'onActivate',\r
8310 \r
8311                                 'onDeactivate',\r
8312 \r
8313                                 'onClick',\r
8314 \r
8315                                 'onEvent',\r
8316 \r
8317                                 'onMouseUp',\r
8318 \r
8319                                 'onMouseDown',\r
8320 \r
8321                                 'onDblClick',\r
8322 \r
8323                                 'onKeyDown',\r
8324 \r
8325                                 'onKeyUp',\r
8326 \r
8327                                 'onKeyPress',\r
8328 \r
8329                                 'onContextMenu',\r
8330 \r
8331                                 'onSubmit',\r
8332 \r
8333                                 'onReset',\r
8334 \r
8335                                 'onPaste',\r
8336 \r
8337                                 'onPreProcess',\r
8338 \r
8339                                 'onPostProcess',\r
8340 \r
8341                                 'onBeforeSetContent',\r
8342 \r
8343                                 'onBeforeGetContent',\r
8344 \r
8345                                 'onSetContent',\r
8346 \r
8347                                 'onGetContent',\r
8348 \r
8349                                 'onLoadContent',\r
8350 \r
8351                                 'onSaveContent',\r
8352 \r
8353                                 'onNodeChange',\r
8354 \r
8355                                 'onChange',\r
8356 \r
8357                                 'onBeforeExecCommand',\r
8358 \r
8359                                 'onExecCommand',\r
8360 \r
8361                                 'onUndo',\r
8362 \r
8363                                 'onRedo',\r
8364 \r
8365                                 'onVisualAid',\r
8366 \r
8367                                 'onSetProgressState'\r
8368                         ], function(e) {\r
8369                                 t[e] = new Dispatcher(t);\r
8370                         });\r
8371 \r
8372                         t.settings = s = extend({\r
8373                                 id : id,\r
8374                                 language : 'en',\r
8375                                 docs_language : 'en',\r
8376                                 theme : 'simple',\r
8377                                 skin : 'default',\r
8378                                 delta_width : 0,\r
8379                                 delta_height : 0,\r
8380                                 popup_css : '',\r
8381                                 plugins : '',\r
8382                                 document_base_url : tinymce.documentBaseURL,\r
8383                                 add_form_submit_trigger : 1,\r
8384                                 submit_patch : 1,\r
8385                                 add_unload_trigger : 1,\r
8386                                 convert_urls : 1,\r
8387                                 relative_urls : 1,\r
8388                                 remove_script_host : 1,\r
8389                                 table_inline_editing : 0,\r
8390                                 object_resizing : 1,\r
8391                                 cleanup : 1,\r
8392                                 accessibility_focus : 1,\r
8393                                 custom_shortcuts : 1,\r
8394                                 custom_undo_redo_keyboard_shortcuts : 1,\r
8395                                 custom_undo_redo_restore_selection : 1,\r
8396                                 custom_undo_redo : 1,\r
8397                                 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
8398                                 visual_table_class : 'mceItemTable',\r
8399                                 visual : 1,\r
8400                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
8401                                 apply_source_formatting : 1,\r
8402                                 directionality : 'ltr',\r
8403                                 forced_root_block : 'p',\r
8404                                 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
8405                                 hidden_input : 1,\r
8406                                 padd_empty_editor : 1,\r
8407                                 render_ui : 1,\r
8408                                 init_theme : 1,\r
8409                                 force_p_newlines : 1,\r
8410                                 indentation : '30px',\r
8411                                 keep_styles : 1,\r
8412                                 fix_table_elements : 1,\r
8413                                 inline_styles : 1,\r
8414                                 convert_fonts_to_spans : true\r
8415                         }, s);\r
8416 \r
8417                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
8418                                 base_uri : tinyMCE.baseURI\r
8419                         });\r
8420 \r
8421                         t.baseURI = tinymce.baseURI;\r
8422 \r
8423                         // Call setup\r
8424                         t.execCallback('setup', t);\r
8425                 },\r
8426 \r
8427                 render : function(nst) {\r
8428                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
8429 \r
8430                         // Page is not loaded yet, wait for it\r
8431                         if (!Event.domLoaded) {\r
8432                                 Event.add(document, 'init', function() {\r
8433                                         t.render();\r
8434                                 });\r
8435                                 return;\r
8436                         }\r
8437 \r
8438                         tinyMCE.settings = s;\r
8439 \r
8440                         // Element not found, then skip initialization\r
8441                         if (!t.getElement())\r
8442                                 return;\r
8443 \r
8444                         // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
8445                         // browser says it has contentEditable support but there is no visible caret\r
8446                         // We will remove this check ones Apple implements full contentEditable support\r
8447                         if (tinymce.isIDevice)\r
8448                                 return;\r
8449 \r
8450                         // Add hidden input for non input elements inside form elements\r
8451                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
8452                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
8453 \r
8454                         if (tinymce.WindowManager)\r
8455                                 t.windowManager = new tinymce.WindowManager(t);\r
8456 \r
8457                         if (s.encoding == 'xml') {\r
8458                                 t.onGetContent.add(function(ed, o) {\r
8459                                         if (o.save)\r
8460                                                 o.content = DOM.encode(o.content);\r
8461                                 });\r
8462                         }\r
8463 \r
8464                         if (s.add_form_submit_trigger) {\r
8465                                 t.onSubmit.addToTop(function() {\r
8466                                         if (t.initialized) {\r
8467                                                 t.save();\r
8468                                                 t.isNotDirty = 1;\r
8469                                         }\r
8470                                 });\r
8471                         }\r
8472 \r
8473                         if (s.add_unload_trigger) {\r
8474                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
8475                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
8476                                                 t.save({format : 'raw', no_events : true});\r
8477                                 });\r
8478                         }\r
8479 \r
8480                         tinymce.addUnload(t.destroy, t);\r
8481 \r
8482                         if (s.submit_patch) {\r
8483                                 t.onBeforeRenderUI.add(function() {\r
8484                                         var n = t.getElement().form;\r
8485 \r
8486                                         if (!n)\r
8487                                                 return;\r
8488 \r
8489                                         // Already patched\r
8490                                         if (n._mceOldSubmit)\r
8491                                                 return;\r
8492 \r
8493                                         // Check page uses id="submit" or name="submit" for it's submit button\r
8494                                         if (!n.submit.nodeType && !n.submit.length) {\r
8495                                                 t.formElement = n;\r
8496                                                 n._mceOldSubmit = n.submit;\r
8497                                                 n.submit = function() {\r
8498                                                         // Save all instances\r
8499                                                         tinymce.triggerSave();\r
8500                                                         t.isNotDirty = 1;\r
8501 \r
8502                                                         return t.formElement._mceOldSubmit(t.formElement);\r
8503                                                 };\r
8504                                         }\r
8505 \r
8506                                         n = null;\r
8507                                 });\r
8508                         }\r
8509 \r
8510                         // Load scripts\r
8511                         function loadScripts() {\r
8512                                 if (s.language)\r
8513                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
8514 \r
8515                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
8516                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
8517 \r
8518                                 each(explode(s.plugins), function(p) {\r
8519                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
8520                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
8521                                                 if (p == 'safari')\r
8522                                                         return;\r
8523 \r
8524                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
8525                                         }\r
8526                                 });\r
8527 \r
8528                                 // Init when que is loaded\r
8529                                 sl.loadQueue(function() {\r
8530                                         if (!t.removed)\r
8531                                                 t.init();\r
8532                                 });\r
8533                         };\r
8534 \r
8535                         loadScripts();\r
8536                 },\r
8537 \r
8538                 init : function() {\r
8539                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
8540 \r
8541                         tinymce.add(t);\r
8542 \r
8543                         if (s.theme) {\r
8544                                 s.theme = s.theme.replace(/-/, '');\r
8545                                 o = ThemeManager.get(s.theme);\r
8546                                 t.theme = new o();\r
8547 \r
8548                                 if (t.theme.init && s.init_theme)\r
8549                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
8550                         }\r
8551 \r
8552                         // Create all plugins\r
8553                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
8554                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
8555 \r
8556                                 if (c) {\r
8557                                         po = new c(t, u);\r
8558 \r
8559                                         t.plugins[p] = po;\r
8560 \r
8561                                         if (po.init)\r
8562                                                 po.init(t, u);\r
8563                                 }\r
8564                         });\r
8565 \r
8566                         // Setup popup CSS path(s)\r
8567                         if (s.popup_css !== false) {\r
8568                                 if (s.popup_css)\r
8569                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
8570                                 else\r
8571                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
8572                         }\r
8573 \r
8574                         if (s.popup_css_add)\r
8575                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
8576 \r
8577                         t.controlManager = new tinymce.ControlManager(t);\r
8578 \r
8579                         if (s.custom_undo_redo) {\r
8580                                 // Add initial undo level\r
8581                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
8582                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
8583                                                 if (!t.undoManager.hasUndo())\r
8584                                                         t.undoManager.add();\r
8585                                         }\r
8586                                 });\r
8587 \r
8588                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
8589                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
8590                                                 t.undoManager.add();\r
8591                                 });\r
8592                         }\r
8593 \r
8594                         t.onExecCommand.add(function(ed, c) {\r
8595                                 // Don't refresh the select lists until caret move\r
8596                                 if (!/^(FontName|FontSize)$/.test(c))\r
8597                                         t.nodeChanged();\r
8598                         });\r
8599 \r
8600                         // Remove ghost selections on images and tables in Gecko\r
8601                         if (isGecko) {\r
8602                                 function repaint(a, o) {\r
8603                                         if (!o || !o.initial)\r
8604                                                 t.execCommand('mceRepaint');\r
8605                                 };\r
8606 \r
8607                                 t.onUndo.add(repaint);\r
8608                                 t.onRedo.add(repaint);\r
8609                                 t.onSetContent.add(repaint);\r
8610                         }\r
8611 \r
8612                         // Enables users to override the control factory\r
8613                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
8614 \r
8615                         // Measure box\r
8616                         if (s.render_ui) {\r
8617                                 w = s.width || e.style.width || e.offsetWidth;\r
8618                                 h = s.height || e.style.height || e.offsetHeight;\r
8619                                 t.orgDisplay = e.style.display;\r
8620                                 re = /^[0-9\.]+(|px)$/i;\r
8621 \r
8622                                 if (re.test('' + w))\r
8623                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
8624 \r
8625                                 if (re.test('' + h))\r
8626                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
8627 \r
8628                                 // Render UI\r
8629                                 o = t.theme.renderUI({\r
8630                                         targetNode : e,\r
8631                                         width : w,\r
8632                                         height : h,\r
8633                                         deltaWidth : s.delta_width,\r
8634                                         deltaHeight : s.delta_height\r
8635                                 });\r
8636 \r
8637                                 t.editorContainer = o.editorContainer;\r
8638                         }\r
8639 \r
8640 \r
8641                         // User specified a document.domain value\r
8642                         if (document.domain && location.hostname != document.domain)\r
8643                                 tinymce.relaxedDomain = document.domain;\r
8644 \r
8645                         // Resize editor\r
8646                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
8647                                 width : w,\r
8648                                 height : h\r
8649                         });\r
8650 \r
8651                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
8652                         if (h < 100)\r
8653                                 h = 100;\r
8654 \r
8655                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
8656 \r
8657                         // We only need to override paths if we have to\r
8658                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
8659                         if (s.document_base_url != tinymce.documentBaseURL)\r
8660                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
8661 \r
8662                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
8663 \r
8664                         if (tinymce.relaxedDomain)\r
8665                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
8666 \r
8667                         bi = s.body_id || 'tinymce';\r
8668                         if (bi.indexOf('=') != -1) {\r
8669                                 bi = t.getParam('body_id', '', 'hash');\r
8670                                 bi = bi[t.id] || bi;\r
8671                         }\r
8672 \r
8673                         bc = s.body_class || '';\r
8674                         if (bc.indexOf('=') != -1) {\r
8675                                 bc = t.getParam('body_class', '', 'hash');\r
8676                                 bc = bc[t.id] || '';\r
8677                         }\r
8678 \r
8679                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
8680 \r
8681                         // Domain relaxing enabled, then set document domain\r
8682                         if (tinymce.relaxedDomain) {\r
8683                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
8684                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
8685                                         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
8686                                 else if (tinymce.isOpera)\r
8687                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
8688                         }\r
8689 \r
8690                         // Create iframe\r
8691                         n = DOM.add(o.iframeContainer, 'iframe', {\r
8692                                 id : t.id + "_ifr",\r
8693                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
8694                                 frameBorder : '0',\r
8695                                 style : {\r
8696                                         width : '100%',\r
8697                                         height : h\r
8698                                 }\r
8699                         });\r
8700 \r
8701                         t.contentAreaContainer = o.iframeContainer;\r
8702                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
8703                         DOM.get(t.id).style.display = 'none';\r
8704 \r
8705                         if (!isIE || !tinymce.relaxedDomain)\r
8706                                 t.setupIframe();\r
8707 \r
8708                         e = n = o = null; // Cleanup\r
8709                 },\r
8710 \r
8711                 setupIframe : function() {\r
8712                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
8713 \r
8714                         // Setup iframe body\r
8715                         if (!isIE || !tinymce.relaxedDomain) {\r
8716                                 d.open();\r
8717                                 d.write(t.iframeHTML);\r
8718                                 d.close();\r
8719                         }\r
8720 \r
8721                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
8722                         if (!isIE) {\r
8723                                 try {\r
8724                                         if (!s.readonly)\r
8725                                                 d.designMode = 'On';\r
8726                                 } catch (ex) {\r
8727                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
8728                                         // The design mode will be set ones the editor is focused\r
8729                                 }\r
8730                         }\r
8731 \r
8732                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
8733                         if (isIE) {\r
8734                                 // It will not steal focus if we hide it while setting contentEditable\r
8735                                 b = t.getBody();\r
8736                                 DOM.hide(b);\r
8737 \r
8738                                 if (!s.readonly)\r
8739                                         b.contentEditable = true;\r
8740 \r
8741                                 DOM.show(b);\r
8742                         }\r
8743 \r
8744                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
8745                                 keep_values : true,\r
8746                                 url_converter : t.convertURL,\r
8747                                 url_converter_scope : t,\r
8748                                 hex_colors : s.force_hex_style_colors,\r
8749                                 class_filter : s.class_filter,\r
8750                                 update_styles : 1,\r
8751                                 fix_ie_paragraphs : 1,\r
8752                                 valid_styles : s.valid_styles\r
8753                         });\r
8754 \r
8755                         t.schema = new tinymce.dom.Schema();\r
8756 \r
8757                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
8758                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
8759                                 dom : t.dom,\r
8760                                 schema : t.schema\r
8761                         }));\r
8762 \r
8763                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
8764 \r
8765                         t.formatter = new tinymce.Formatter(this);\r
8766 \r
8767                         // Register default formats\r
8768                         t.formatter.register({\r
8769                                 alignleft : [\r
8770                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
8771                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
8772                                 ],\r
8773 \r
8774                                 aligncenter : [\r
8775                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
8776                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
8777                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
8778                                 ],\r
8779 \r
8780                                 alignright : [\r
8781                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
8782                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
8783                                 ],\r
8784 \r
8785                                 alignfull : [\r
8786                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
8787                                 ],\r
8788 \r
8789                                 bold : [\r
8790                                         {inline : 'strong'},\r
8791                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
8792                                         {inline : 'b'}\r
8793                                 ],\r
8794 \r
8795                                 italic : [\r
8796                                         {inline : 'em'},\r
8797                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
8798                                         {inline : 'i'}\r
8799                                 ],\r
8800 \r
8801                                 underline : [\r
8802                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
8803                                         {inline : 'u'}\r
8804                                 ],\r
8805 \r
8806                                 strikethrough : [\r
8807                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
8808                                         {inline : 'u'}\r
8809                                 ],\r
8810 \r
8811                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
8812                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
8813                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
8814                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
8815                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
8816                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
8817 \r
8818                                 removeformat : [\r
8819                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
8820                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
8821                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
8822                                 ]\r
8823                         });\r
8824 \r
8825                         // Register default block formats\r
8826                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
8827                                 t.formatter.register(name, {block : name, remove : 'all'});\r
8828                         });\r
8829 \r
8830                         // Register user defined formats\r
8831                         t.formatter.register(t.settings.formats);\r
8832 \r
8833                         t.undoManager = new tinymce.UndoManager(t);\r
8834 \r
8835                         // Pass through\r
8836                         t.undoManager.onAdd.add(function(um, l) {\r
8837                                 if (!l.initial)\r
8838                                         return t.onChange.dispatch(t, l, um);\r
8839                         });\r
8840 \r
8841                         t.undoManager.onUndo.add(function(um, l) {\r
8842                                 return t.onUndo.dispatch(t, l, um);\r
8843                         });\r
8844 \r
8845                         t.undoManager.onRedo.add(function(um, l) {\r
8846                                 return t.onRedo.dispatch(t, l, um);\r
8847                         });\r
8848 \r
8849                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
8850                                 forced_root_block : s.forced_root_block\r
8851                         });\r
8852 \r
8853                         t.editorCommands = new tinymce.EditorCommands(t);\r
8854 \r
8855                         // Pass through\r
8856                         t.serializer.onPreProcess.add(function(se, o) {\r
8857                                 return t.onPreProcess.dispatch(t, o, se);\r
8858                         });\r
8859 \r
8860                         t.serializer.onPostProcess.add(function(se, o) {\r
8861                                 return t.onPostProcess.dispatch(t, o, se);\r
8862                         });\r
8863 \r
8864                         t.onPreInit.dispatch(t);\r
8865 \r
8866                         if (!s.gecko_spellcheck)\r
8867                                 t.getBody().spellcheck = 0;\r
8868 \r
8869                         if (!s.readonly)\r
8870                                 t._addEvents();\r
8871 \r
8872                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
8873                         t.onPostRender.dispatch(t);\r
8874 \r
8875                         if (s.directionality)\r
8876                                 t.getBody().dir = s.directionality;\r
8877 \r
8878                         if (s.nowrap)\r
8879                                 t.getBody().style.whiteSpace = "nowrap";\r
8880 \r
8881                         if (s.custom_elements) {\r
8882                                 function handleCustom(ed, o) {\r
8883                                         each(explode(s.custom_elements), function(v) {\r
8884                                                 var n;\r
8885 \r
8886                                                 if (v.indexOf('~') === 0) {\r
8887                                                         v = v.substring(1);\r
8888                                                         n = 'span';\r
8889                                                 } else\r
8890                                                         n = 'div';\r
8891 \r
8892                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
8893                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
8894                                         });\r
8895                                 };\r
8896 \r
8897                                 t.onBeforeSetContent.add(handleCustom);\r
8898                                 t.onPostProcess.add(function(ed, o) {\r
8899                                         if (o.set)\r
8900                                                 handleCustom(ed, o);\r
8901                                 });\r
8902                         }\r
8903 \r
8904                         if (s.handle_node_change_callback) {\r
8905                                 t.onNodeChange.add(function(ed, cm, n) {\r
8906                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
8907                                 });\r
8908                         }\r
8909 \r
8910                         if (s.save_callback) {\r
8911                                 t.onSaveContent.add(function(ed, o) {\r
8912                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
8913 \r
8914                                         if (h)\r
8915                                                 o.content = h;\r
8916                                 });\r
8917                         }\r
8918 \r
8919                         if (s.onchange_callback) {\r
8920                                 t.onChange.add(function(ed, l) {\r
8921                                         t.execCallback('onchange_callback', t, l);\r
8922                                 });\r
8923                         }\r
8924 \r
8925                         if (s.convert_newlines_to_brs) {\r
8926                                 t.onBeforeSetContent.add(function(ed, o) {\r
8927                                         if (o.initial)\r
8928                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
8929                                 });\r
8930                         }\r
8931 \r
8932                         if (s.fix_nesting && isIE) {\r
8933                                 t.onBeforeSetContent.add(function(ed, o) {\r
8934                                         o.content = t._fixNesting(o.content);\r
8935                                 });\r
8936                         }\r
8937 \r
8938                         if (s.preformatted) {\r
8939                                 t.onPostProcess.add(function(ed, o) {\r
8940                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
8941                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
8942 \r
8943                                         if (o.set)\r
8944                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
8945                                 });\r
8946                         }\r
8947 \r
8948                         if (s.verify_css_classes) {\r
8949                                 t.serializer.attribValueFilter = function(n, v) {\r
8950                                         var s, cl;\r
8951 \r
8952                                         if (n == 'class') {\r
8953                                                 // Build regexp for classes\r
8954                                                 if (!t.classesRE) {\r
8955                                                         cl = t.dom.getClasses();\r
8956 \r
8957                                                         if (cl.length > 0) {\r
8958                                                                 s = '';\r
8959 \r
8960                                                                 each (cl, function(o) {\r
8961                                                                         s += (s ? '|' : '') + o['class'];\r
8962                                                                 });\r
8963 \r
8964                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
8965                                                         }\r
8966                                                 }\r
8967 \r
8968                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
8969                                         }\r
8970 \r
8971                                         return v;\r
8972                                 };\r
8973                         }\r
8974 \r
8975                         if (s.cleanup_callback) {\r
8976                                 t.onBeforeSetContent.add(function(ed, o) {\r
8977                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
8978                                 });\r
8979 \r
8980                                 t.onPreProcess.add(function(ed, o) {\r
8981                                         if (o.set)\r
8982                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
8983 \r
8984                                         if (o.get)\r
8985                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
8986                                 });\r
8987 \r
8988                                 t.onPostProcess.add(function(ed, o) {\r
8989                                         if (o.set)\r
8990                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
8991 \r
8992                                         if (o.get)                                              \r
8993                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
8994                                 });\r
8995                         }\r
8996 \r
8997                         if (s.save_callback) {\r
8998                                 t.onGetContent.add(function(ed, o) {\r
8999                                         if (o.save)\r
9000                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9001                                 });\r
9002                         }\r
9003 \r
9004                         if (s.handle_event_callback) {\r
9005                                 t.onEvent.add(function(ed, e, o) {\r
9006                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9007                                                 Event.cancel(e);\r
9008                                 });\r
9009                         }\r
9010 \r
9011                         // Add visual aids when new contents is added\r
9012                         t.onSetContent.add(function() {\r
9013                                 t.addVisual(t.getBody());\r
9014                         });\r
9015 \r
9016                         // Remove empty contents\r
9017                         if (s.padd_empty_editor) {\r
9018                                 t.onPostProcess.add(function(ed, o) {\r
9019                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9020                                 });\r
9021                         }\r
9022 \r
9023                         if (isGecko) {\r
9024                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9025                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9026                                 function fixLinks(ed, o) {\r
9027                                         each(ed.dom.select('a'), function(n) {\r
9028                                                 var pn = n.parentNode;\r
9029 \r
9030                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9031                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9032                                         });\r
9033                                 };\r
9034 \r
9035                                 t.onExecCommand.add(function(ed, cmd) {\r
9036                                         if (cmd === 'CreateLink')\r
9037                                                 fixLinks(ed);\r
9038                                 });\r
9039 \r
9040                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9041 \r
9042                                 if (!s.readonly) {\r
9043                                         try {\r
9044                                                 // Design mode must be set here once again to fix a bug where\r
9045                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9046                                                 d.designMode = 'Off';\r
9047                                                 d.designMode = 'On';\r
9048                                         } catch (ex) {\r
9049                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9050                                                 // The design mode will be set ones the editor is focused\r
9051                                         }\r
9052                                 }\r
9053                         }\r
9054 \r
9055                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9056                         setTimeout(function () {\r
9057                                 if (t.removed)\r
9058                                         return;\r
9059 \r
9060                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9061                                 t.startContent = t.getContent({format : 'raw'});\r
9062                                 t.initialized = true;\r
9063 \r
9064                                 t.onInit.dispatch(t);\r
9065                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9066                                 t.execCallback('init_instance_callback', t);\r
9067                                 t.focus(true);\r
9068                                 t.nodeChanged({initial : 1});\r
9069 \r
9070                                 // Load specified content CSS last\r
9071                                 if (s.content_css) {\r
9072                                         tinymce.each(explode(s.content_css), function(u) {\r
9073                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9074                                         });\r
9075                                 }\r
9076 \r
9077                                 // Handle auto focus\r
9078                                 if (s.auto_focus) {\r
9079                                         setTimeout(function () {\r
9080                                                 var ed = tinymce.get(s.auto_focus);\r
9081 \r
9082                                                 ed.selection.select(ed.getBody(), 1);\r
9083                                                 ed.selection.collapse(1);\r
9084                                                 ed.getWin().focus();\r
9085                                         }, 100);\r
9086                                 }\r
9087                         }, 1);\r
9088         \r
9089                         e = null;\r
9090                 },\r
9091 \r
9092 \r
9093                 focus : function(sf) {\r
9094                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
9095 \r
9096                         if (!sf) {\r
9097                                 // Get selected control element\r
9098                                 ieRng = t.selection.getRng();\r
9099                                 if (ieRng.item) {\r
9100                                         controlElm = ieRng.item(0);\r
9101                                 }\r
9102 \r
9103                                 // Is not content editable\r
9104                                 if (!ce)\r
9105                                         t.getWin().focus();\r
9106 \r
9107                                 // Restore selected control element\r
9108                                 // This is needed when for example an image is selected within a\r
9109                                 // layer a call to focus will then remove the control selection\r
9110                                 if (controlElm && controlElm.ownerDocument == doc) {\r
9111                                         ieRng = doc.body.createControlRange();\r
9112                                         ieRng.addElement(controlElm);\r
9113                                         ieRng.select();\r
9114                                 }\r
9115 \r
9116                         }\r
9117 \r
9118                         if (tinymce.activeEditor != t) {\r
9119                                 if ((oed = tinymce.activeEditor) != null)\r
9120                                         oed.onDeactivate.dispatch(oed, t);\r
9121 \r
9122                                 t.onActivate.dispatch(t, oed);\r
9123                         }\r
9124 \r
9125                         tinymce._setActive(t);\r
9126                 },\r
9127 \r
9128                 execCallback : function(n) {\r
9129                         var t = this, f = t.settings[n], s;\r
9130 \r
9131                         if (!f)\r
9132                                 return;\r
9133 \r
9134                         // Look through lookup\r
9135                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
9136                                 f = s.func;\r
9137                                 s = s.scope;\r
9138                         }\r
9139 \r
9140                         if (is(f, 'string')) {\r
9141                                 s = f.replace(/\.\w+$/, '');\r
9142                                 s = s ? tinymce.resolve(s) : 0;\r
9143                                 f = tinymce.resolve(f);\r
9144                                 t.callbackLookup = t.callbackLookup || {};\r
9145                                 t.callbackLookup[n] = {func : f, scope : s};\r
9146                         }\r
9147 \r
9148                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
9149                 },\r
9150 \r
9151                 translate : function(s) {\r
9152                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
9153 \r
9154                         if (!s)\r
9155                                 return '';\r
9156 \r
9157                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
9158                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
9159                         });\r
9160                 },\r
9161 \r
9162                 getLang : function(n, dv) {\r
9163                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
9164                 },\r
9165 \r
9166                 getParam : function(n, dv, ty) {\r
9167                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
9168 \r
9169                         if (ty === 'hash') {\r
9170                                 o = {};\r
9171 \r
9172                                 if (is(v, 'string')) {\r
9173                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
9174                                                 v = v.split('=');\r
9175 \r
9176                                                 if (v.length > 1)\r
9177                                                         o[tr(v[0])] = tr(v[1]);\r
9178                                                 else\r
9179                                                         o[tr(v[0])] = tr(v);\r
9180                                         });\r
9181                                 } else\r
9182                                         o = v;\r
9183 \r
9184                                 return o;\r
9185                         }\r
9186 \r
9187                         return v;\r
9188                 },\r
9189 \r
9190                 nodeChanged : function(o) {\r
9191                         var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody();\r
9192 \r
9193                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
9194                         if (t.initialized) {\r
9195                                 o = o || {};\r
9196                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
9197 \r
9198                                 // Get parents and add them to object\r
9199                                 o.parents = [];\r
9200                                 t.dom.getParent(n, function(node) {\r
9201                                         if (node.nodeName == 'BODY')\r
9202                                                 return true;\r
9203 \r
9204                                         o.parents.push(node);\r
9205                                 });\r
9206 \r
9207                                 t.onNodeChange.dispatch(\r
9208                                         t,\r
9209                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
9210                                         n,\r
9211                                         s.isCollapsed(),\r
9212                                         o\r
9213                                 );\r
9214                         }\r
9215                 },\r
9216 \r
9217                 addButton : function(n, s) {\r
9218                         var t = this;\r
9219 \r
9220                         t.buttons = t.buttons || {};\r
9221                         t.buttons[n] = s;\r
9222                 },\r
9223 \r
9224                 addCommand : function(n, f, s) {\r
9225                         this.execCommands[n] = {func : f, scope : s || this};\r
9226                 },\r
9227 \r
9228                 addQueryStateHandler : function(n, f, s) {\r
9229                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
9230                 },\r
9231 \r
9232                 addQueryValueHandler : function(n, f, s) {\r
9233                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
9234                 },\r
9235 \r
9236                 addShortcut : function(pa, desc, cmd_func, sc) {\r
9237                         var t = this, c;\r
9238 \r
9239                         if (!t.settings.custom_shortcuts)\r
9240                                 return false;\r
9241 \r
9242                         t.shortcuts = t.shortcuts || {};\r
9243 \r
9244                         if (is(cmd_func, 'string')) {\r
9245                                 c = cmd_func;\r
9246 \r
9247                                 cmd_func = function() {\r
9248                                         t.execCommand(c, false, null);\r
9249                                 };\r
9250                         }\r
9251 \r
9252                         if (is(cmd_func, 'object')) {\r
9253                                 c = cmd_func;\r
9254 \r
9255                                 cmd_func = function() {\r
9256                                         t.execCommand(c[0], c[1], c[2]);\r
9257                                 };\r
9258                         }\r
9259 \r
9260                         each(explode(pa), function(pa) {\r
9261                                 var o = {\r
9262                                         func : cmd_func,\r
9263                                         scope : sc || this,\r
9264                                         desc : desc,\r
9265                                         alt : false,\r
9266                                         ctrl : false,\r
9267                                         shift : false\r
9268                                 };\r
9269 \r
9270                                 each(explode(pa, '+'), function(v) {\r
9271                                         switch (v) {\r
9272                                                 case 'alt':\r
9273                                                 case 'ctrl':\r
9274                                                 case 'shift':\r
9275                                                         o[v] = true;\r
9276                                                         break;\r
9277 \r
9278                                                 default:\r
9279                                                         o.charCode = v.charCodeAt(0);\r
9280                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
9281                                         }\r
9282                                 });\r
9283 \r
9284                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
9285                         });\r
9286 \r
9287                         return true;\r
9288                 },\r
9289 \r
9290                 execCommand : function(cmd, ui, val, a) {\r
9291                         var t = this, s = 0, o, st;\r
9292 \r
9293                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
9294                                 t.focus();\r
9295 \r
9296                         o = {};\r
9297                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
9298                         if (o.terminate)\r
9299                                 return false;\r
9300 \r
9301                         // Command callback\r
9302                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
9303                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9304                                 return true;\r
9305                         }\r
9306 \r
9307                         // Registred commands\r
9308                         if (o = t.execCommands[cmd]) {\r
9309                                 st = o.func.call(o.scope, ui, val);\r
9310 \r
9311                                 // Fall through on true\r
9312                                 if (st !== true) {\r
9313                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9314                                         return st;\r
9315                                 }\r
9316                         }\r
9317 \r
9318                         // Plugin commands\r
9319                         each(t.plugins, function(p) {\r
9320                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
9321                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9322                                         s = 1;\r
9323                                         return false;\r
9324                                 }\r
9325                         });\r
9326 \r
9327                         if (s)\r
9328                                 return true;\r
9329 \r
9330                         // Theme commands\r
9331                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
9332                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9333                                 return true;\r
9334                         }\r
9335 \r
9336                         // Execute global commands\r
9337                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
9338                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9339                                 return true;\r
9340                         }\r
9341 \r
9342                         // Editor commands\r
9343                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
9344                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9345                                 return true;\r
9346                         }\r
9347 \r
9348                         // Browser commands\r
9349                         t.getDoc().execCommand(cmd, ui, val);\r
9350                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9351                 },\r
9352 \r
9353                 queryCommandState : function(cmd) {\r
9354                         var t = this, o, s;\r
9355 \r
9356                         // Is hidden then return undefined\r
9357                         if (t._isHidden())\r
9358                                 return;\r
9359 \r
9360                         // Registred commands\r
9361                         if (o = t.queryStateCommands[cmd]) {\r
9362                                 s = o.func.call(o.scope);\r
9363 \r
9364                                 // Fall though on true\r
9365                                 if (s !== true)\r
9366                                         return s;\r
9367                         }\r
9368 \r
9369                         // Registred commands\r
9370                         o = t.editorCommands.queryCommandState(cmd);\r
9371                         if (o !== -1)\r
9372                                 return o;\r
9373 \r
9374                         // Browser commands\r
9375                         try {\r
9376                                 return this.getDoc().queryCommandState(cmd);\r
9377                         } catch (ex) {\r
9378                                 // Fails sometimes see bug: 1896577\r
9379                         }\r
9380                 },\r
9381 \r
9382                 queryCommandValue : function(c) {\r
9383                         var t = this, o, s;\r
9384 \r
9385                         // Is hidden then return undefined\r
9386                         if (t._isHidden())\r
9387                                 return;\r
9388 \r
9389                         // Registred commands\r
9390                         if (o = t.queryValueCommands[c]) {\r
9391                                 s = o.func.call(o.scope);\r
9392 \r
9393                                 // Fall though on true\r
9394                                 if (s !== true)\r
9395                                         return s;\r
9396                         }\r
9397 \r
9398                         // Registred commands\r
9399                         o = t.editorCommands.queryCommandValue(c);\r
9400                         if (is(o))\r
9401                                 return o;\r
9402 \r
9403                         // Browser commands\r
9404                         try {\r
9405                                 return this.getDoc().queryCommandValue(c);\r
9406                         } catch (ex) {\r
9407                                 // Fails sometimes see bug: 1896577\r
9408                         }\r
9409                 },\r
9410 \r
9411                 show : function() {\r
9412                         var t = this;\r
9413 \r
9414                         DOM.show(t.getContainer());\r
9415                         DOM.hide(t.id);\r
9416                         t.load();\r
9417                 },\r
9418 \r
9419                 hide : function() {\r
9420                         var t = this, d = t.getDoc();\r
9421 \r
9422                         // Fixed bug where IE has a blinking cursor left from the editor\r
9423                         if (isIE && d)\r
9424                                 d.execCommand('SelectAll');\r
9425 \r
9426                         // We must save before we hide so Safari doesn't crash\r
9427                         t.save();\r
9428                         DOM.hide(t.getContainer());\r
9429                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
9430                 },\r
9431 \r
9432                 isHidden : function() {\r
9433                         return !DOM.isHidden(this.id);\r
9434                 },\r
9435 \r
9436                 setProgressState : function(b, ti, o) {\r
9437                         this.onSetProgressState.dispatch(this, b, ti, o);\r
9438 \r
9439                         return b;\r
9440                 },\r
9441 \r
9442                 load : function(o) {\r
9443                         var t = this, e = t.getElement(), h;\r
9444 \r
9445                         if (e) {\r
9446                                 o = o || {};\r
9447                                 o.load = true;\r
9448 \r
9449                                 // Double encode existing entities in the value\r
9450                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
9451                                 o.element = e;\r
9452 \r
9453                                 if (!o.no_events)\r
9454                                         t.onLoadContent.dispatch(t, o);\r
9455 \r
9456                                 o.element = e = null;\r
9457 \r
9458                                 return h;\r
9459                         }\r
9460                 },\r
9461 \r
9462                 save : function(o) {\r
9463                         var t = this, e = t.getElement(), h, f;\r
9464 \r
9465                         if (!e || !t.initialized)\r
9466                                 return;\r
9467 \r
9468                         o = o || {};\r
9469                         o.save = true;\r
9470 \r
9471                         // Add undo level will trigger onchange event\r
9472                         if (!o.no_events) {\r
9473                                 t.undoManager.typing = 0;\r
9474                                 t.undoManager.add();\r
9475                         }\r
9476 \r
9477                         o.element = e;\r
9478                         h = o.content = t.getContent(o);\r
9479 \r
9480                         if (!o.no_events)\r
9481                                 t.onSaveContent.dispatch(t, o);\r
9482 \r
9483                         h = o.content;\r
9484 \r
9485                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
9486                                 e.innerHTML = h;\r
9487 \r
9488                                 // Update hidden form element\r
9489                                 if (f = DOM.getParent(t.id, 'form')) {\r
9490                                         each(f.elements, function(e) {\r
9491                                                 if (e.name == t.id) {\r
9492                                                         e.value = h;\r
9493                                                         return false;\r
9494                                                 }\r
9495                                         });\r
9496                                 }\r
9497                         } else\r
9498                                 e.value = h;\r
9499 \r
9500                         o.element = e = null;\r
9501 \r
9502                         return h;\r
9503                 },\r
9504 \r
9505                 setContent : function(h, o) {\r
9506                         var t = this;\r
9507 \r
9508                         o = o || {};\r
9509                         o.format = o.format || 'html';\r
9510                         o.set = true;\r
9511                         o.content = h;\r
9512 \r
9513                         if (!o.no_events)\r
9514                                 t.onBeforeSetContent.dispatch(t, o);\r
9515 \r
9516                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
9517                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
9518                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
9519                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
9520                                 o.format = 'raw';\r
9521                         }\r
9522 \r
9523                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
9524 \r
9525                         if (o.format != 'raw' && t.settings.cleanup) {\r
9526                                 o.getInner = true;\r
9527                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
9528                         }\r
9529 \r
9530                         if (!o.no_events)\r
9531                                 t.onSetContent.dispatch(t, o);\r
9532 \r
9533                         return o.content;\r
9534                 },\r
9535 \r
9536                 getContent : function(o) {\r
9537                         var t = this, h;\r
9538 \r
9539                         o = o || {};\r
9540                         o.format = o.format || 'html';\r
9541                         o.get = true;\r
9542 \r
9543                         if (!o.no_events)\r
9544                                 t.onBeforeGetContent.dispatch(t, o);\r
9545 \r
9546                         if (o.format != 'raw' && t.settings.cleanup) {\r
9547                                 o.getInner = true;\r
9548                                 h = t.serializer.serialize(t.getBody(), o);\r
9549                         } else\r
9550                                 h = t.getBody().innerHTML;\r
9551 \r
9552                         h = h.replace(/^\s*|\s*$/g, '');\r
9553                         o.content = h;\r
9554 \r
9555                         if (!o.no_events)\r
9556                                 t.onGetContent.dispatch(t, o);\r
9557 \r
9558                         return o.content;\r
9559                 },\r
9560 \r
9561                 isDirty : function() {\r
9562                         var t = this;\r
9563 \r
9564                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
9565                 },\r
9566 \r
9567                 getContainer : function() {\r
9568                         var t = this;\r
9569 \r
9570                         if (!t.container)\r
9571                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
9572 \r
9573                         return t.container;\r
9574                 },\r
9575 \r
9576                 getContentAreaContainer : function() {\r
9577                         return this.contentAreaContainer;\r
9578                 },\r
9579 \r
9580                 getElement : function() {\r
9581                         return DOM.get(this.settings.content_element || this.id);\r
9582                 },\r
9583 \r
9584                 getWin : function() {\r
9585                         var t = this, e;\r
9586 \r
9587                         if (!t.contentWindow) {\r
9588                                 e = DOM.get(t.id + "_ifr");\r
9589 \r
9590                                 if (e)\r
9591                                         t.contentWindow = e.contentWindow;\r
9592                         }\r
9593 \r
9594                         return t.contentWindow;\r
9595                 },\r
9596 \r
9597                 getDoc : function() {\r
9598                         var t = this, w;\r
9599 \r
9600                         if (!t.contentDocument) {\r
9601                                 w = t.getWin();\r
9602 \r
9603                                 if (w)\r
9604                                         t.contentDocument = w.document;\r
9605                         }\r
9606 \r
9607                         return t.contentDocument;\r
9608                 },\r
9609 \r
9610                 getBody : function() {\r
9611                         return this.bodyElement || this.getDoc().body;\r
9612                 },\r
9613 \r
9614                 convertURL : function(u, n, e) {\r
9615                         var t = this, s = t.settings;\r
9616 \r
9617                         // Use callback instead\r
9618                         if (s.urlconverter_callback)\r
9619                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
9620 \r
9621                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
9622                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
9623                                 return u;\r
9624 \r
9625                         // Convert to relative\r
9626                         if (s.relative_urls)\r
9627                                 return t.documentBaseURI.toRelative(u);\r
9628 \r
9629                         // Convert to absolute\r
9630                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
9631 \r
9632                         return u;\r
9633                 },\r
9634 \r
9635                 addVisual : function(e) {\r
9636                         var t = this, s = t.settings;\r
9637 \r
9638                         e = e || t.getBody();\r
9639 \r
9640                         if (!is(t.hasVisual))\r
9641                                 t.hasVisual = s.visual;\r
9642 \r
9643                         each(t.dom.select('table,a', e), function(e) {\r
9644                                 var v;\r
9645 \r
9646                                 switch (e.nodeName) {\r
9647                                         case 'TABLE':\r
9648                                                 v = t.dom.getAttrib(e, 'border');\r
9649 \r
9650                                                 if (!v || v == '0') {\r
9651                                                         if (t.hasVisual)\r
9652                                                                 t.dom.addClass(e, s.visual_table_class);\r
9653                                                         else\r
9654                                                                 t.dom.removeClass(e, s.visual_table_class);\r
9655                                                 }\r
9656 \r
9657                                                 return;\r
9658 \r
9659                                         case 'A':\r
9660                                                 v = t.dom.getAttrib(e, 'name');\r
9661 \r
9662                                                 if (v) {\r
9663                                                         if (t.hasVisual)\r
9664                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
9665                                                         else\r
9666                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
9667                                                 }\r
9668 \r
9669                                                 return;\r
9670                                 }\r
9671                         });\r
9672 \r
9673                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
9674                 },\r
9675 \r
9676                 remove : function() {\r
9677                         var t = this, e = t.getContainer();\r
9678 \r
9679                         t.removed = 1; // Cancels post remove event execution\r
9680                         t.hide();\r
9681 \r
9682                         t.execCallback('remove_instance_callback', t);\r
9683                         t.onRemove.dispatch(t);\r
9684 \r
9685                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
9686                         t.onExecCommand.listeners = [];\r
9687 \r
9688                         tinymce.remove(t);\r
9689                         DOM.remove(e);\r
9690                 },\r
9691 \r
9692                 destroy : function(s) {\r
9693                         var t = this;\r
9694 \r
9695                         // One time is enough\r
9696                         if (t.destroyed)\r
9697                                 return;\r
9698 \r
9699                         if (!s) {\r
9700                                 tinymce.removeUnload(t.destroy);\r
9701                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
9702 \r
9703                                 // Manual destroy\r
9704                                 if (t.theme && t.theme.destroy)\r
9705                                         t.theme.destroy();\r
9706 \r
9707                                 // Destroy controls, selection and dom\r
9708                                 t.controlManager.destroy();\r
9709                                 t.selection.destroy();\r
9710                                 t.dom.destroy();\r
9711 \r
9712                                 // Remove all events\r
9713 \r
9714                                 // Don't clear the window or document if content editable\r
9715                                 // is enabled since other instances might still be present\r
9716                                 if (!t.settings.content_editable) {\r
9717                                         Event.clear(t.getWin());\r
9718                                         Event.clear(t.getDoc());\r
9719                                 }\r
9720 \r
9721                                 Event.clear(t.getBody());\r
9722                                 Event.clear(t.formElement);\r
9723                         }\r
9724 \r
9725                         if (t.formElement) {\r
9726                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
9727                                 t.formElement._mceOldSubmit = null;\r
9728                         }\r
9729 \r
9730                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
9731 \r
9732                         if (t.selection)\r
9733                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
9734 \r
9735                         t.destroyed = 1;\r
9736                 },\r
9737 \r
9738                 // Internal functions\r
9739 \r
9740                 _addEvents : function() {\r
9741                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
9742                         var t = this, i, s = t.settings, lo = {\r
9743                                 mouseup : 'onMouseUp',\r
9744                                 mousedown : 'onMouseDown',\r
9745                                 click : 'onClick',\r
9746                                 keyup : 'onKeyUp',\r
9747                                 keydown : 'onKeyDown',\r
9748                                 keypress : 'onKeyPress',\r
9749                                 submit : 'onSubmit',\r
9750                                 reset : 'onReset',\r
9751                                 contextmenu : 'onContextMenu',\r
9752                                 dblclick : 'onDblClick',\r
9753                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
9754                         };\r
9755 \r
9756                         function eventHandler(e, o) {\r
9757                                 var ty = e.type;\r
9758 \r
9759                                 // Don't fire events when it's removed\r
9760                                 if (t.removed)\r
9761                                         return;\r
9762 \r
9763                                 // Generic event handler\r
9764                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
9765                                         // Specific event handler\r
9766                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
9767                                 }\r
9768                         };\r
9769 \r
9770                         // Add DOM events\r
9771                         each(lo, function(v, k) {\r
9772                                 switch (k) {\r
9773                                         case 'contextmenu':\r
9774                                                 if (tinymce.isOpera) {\r
9775                                                         // Fake contextmenu on Opera\r
9776                                                         t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
9777                                                                 if (e.ctrlKey) {\r
9778                                                                         e.fakeType = 'contextmenu';\r
9779                                                                         eventHandler(e);\r
9780                                                                 }\r
9781                                                         });\r
9782                                                 } else\r
9783                                                         t.dom.bind(t.getBody(), k, eventHandler);\r
9784                                                 break;\r
9785 \r
9786                                         case 'paste':\r
9787                                                 t.dom.bind(t.getBody(), k, function(e) {\r
9788                                                         eventHandler(e);\r
9789                                                 });\r
9790                                                 break;\r
9791 \r
9792                                         case 'submit':\r
9793                                         case 'reset':\r
9794                                                 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
9795                                                 break;\r
9796 \r
9797                                         default:\r
9798                                                 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
9799                                 }\r
9800                         });\r
9801 \r
9802                         t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
9803                                 t.focus(true);\r
9804                         });\r
9805 \r
9806 \r
9807                         // Fixes bug where a specified document_base_uri could result in broken images\r
9808                         // This will also fix drag drop of images in Gecko\r
9809                         if (tinymce.isGecko) {\r
9810                                 // Convert all images to absolute URLs\r
9811 /*                              t.onSetContent.add(function(ed, o) {\r
9812                                         each(ed.dom.select('img'), function(e) {\r
9813                                                 var v;\r
9814 \r
9815                                                 if (v = e.getAttribute('_mce_src'))\r
9816                                                         e.src = t.documentBaseURI.toAbsolute(v);\r
9817                                         })\r
9818                                 });*/\r
9819 \r
9820                                 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
9821                                         var v;\r
9822 \r
9823                                         e = e.target;\r
9824 \r
9825                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
9826                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
9827                                 });\r
9828                         }\r
9829 \r
9830                         // Set various midas options in Gecko\r
9831                         if (isGecko) {\r
9832                                 function setOpts() {\r
9833                                         var t = this, d = t.getDoc(), s = t.settings;\r
9834 \r
9835                                         if (isGecko && !s.readonly) {\r
9836                                                 if (t._isHidden()) {\r
9837                                                         try {\r
9838                                                                 if (!s.content_editable)\r
9839                                                                         d.designMode = 'On';\r
9840                                                         } catch (ex) {\r
9841                                                                 // Fails if it's hidden\r
9842                                                         }\r
9843                                                 }\r
9844 \r
9845                                                 try {\r
9846                                                         // Try new Gecko method\r
9847                                                         d.execCommand("styleWithCSS", 0, false);\r
9848                                                 } catch (ex) {\r
9849                                                         // Use old method\r
9850                                                         if (!t._isHidden())\r
9851                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
9852                                                 }\r
9853 \r
9854                                                 if (!s.table_inline_editing)\r
9855                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
9856 \r
9857                                                 if (!s.object_resizing)\r
9858                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
9859                                         }\r
9860                                 };\r
9861 \r
9862                                 t.onBeforeExecCommand.add(setOpts);\r
9863                                 t.onMouseDown.add(setOpts);\r
9864                         }\r
9865 \r
9866                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
9867                         // WebKit can't even do simple things like selecting an image\r
9868                         // This also fixes so it's possible to select mceItemAnchors\r
9869                         if (tinymce.isWebKit) {\r
9870                                 t.onClick.add(function(ed, e) {\r
9871                                         e = e.target;\r
9872 \r
9873                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
9874                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor')))\r
9875                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
9876                                 });\r
9877                         }\r
9878 \r
9879                         // Add node change handlers\r
9880                         t.onMouseUp.add(t.nodeChanged);\r
9881                         //t.onClick.add(t.nodeChanged);\r
9882                         t.onKeyUp.add(function(ed, e) {\r
9883                                 var c = e.keyCode;\r
9884 \r
9885                                 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
9886                                         t.nodeChanged();\r
9887                         });\r
9888 \r
9889                         // Add reset handler\r
9890                         t.onReset.add(function() {\r
9891                                 t.setContent(t.startContent, {format : 'raw'});\r
9892                         });\r
9893 \r
9894                         // Add shortcuts\r
9895                         if (s.custom_shortcuts) {\r
9896                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
9897                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
9898                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
9899                                 }\r
9900 \r
9901                                 // Add default shortcuts for gecko\r
9902                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
9903                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
9904                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
9905 \r
9906                                 // BlockFormat shortcuts keys\r
9907                                 for (i=1; i<=6; i++)\r
9908                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
9909 \r
9910                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
9911                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
9912                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
9913 \r
9914                                 function find(e) {\r
9915                                         var v = null;\r
9916 \r
9917                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
9918                                                 return v;\r
9919 \r
9920                                         each(t.shortcuts, function(o) {\r
9921                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
9922                                                         return;\r
9923                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
9924                                                         return;\r
9925 \r
9926                                                 if (o.alt != e.altKey)\r
9927                                                         return;\r
9928 \r
9929                                                 if (o.shift != e.shiftKey)\r
9930                                                         return;\r
9931 \r
9932                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
9933                                                         v = o;\r
9934                                                         return false;\r
9935                                                 }\r
9936                                         });\r
9937 \r
9938                                         return v;\r
9939                                 };\r
9940 \r
9941                                 t.onKeyUp.add(function(ed, e) {\r
9942                                         var o = find(e);\r
9943 \r
9944                                         if (o)\r
9945                                                 return Event.cancel(e);\r
9946                                 });\r
9947 \r
9948                                 t.onKeyPress.add(function(ed, e) {\r
9949                                         var o = find(e);\r
9950 \r
9951                                         if (o)\r
9952                                                 return Event.cancel(e);\r
9953                                 });\r
9954 \r
9955                                 t.onKeyDown.add(function(ed, e) {\r
9956                                         var o = find(e);\r
9957 \r
9958                                         if (o) {\r
9959                                                 o.func.call(o.scope);\r
9960                                                 return Event.cancel(e);\r
9961                                         }\r
9962                                 });\r
9963                         }\r
9964 \r
9965                         if (tinymce.isIE) {\r
9966                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
9967                                 // It will also block mceItemNoResize items\r
9968                                 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
9969                                         var re = t.resizeInfo, cb;\r
9970 \r
9971                                         e = e.target;\r
9972 \r
9973                                         // Don't do this action for non image elements\r
9974                                         if (e.nodeName !== 'IMG')\r
9975                                                 return;\r
9976 \r
9977                                         if (re)\r
9978                                                 t.dom.unbind(re.node, re.ev, re.cb);\r
9979 \r
9980                                         if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
9981                                                 ev = 'resizeend';\r
9982                                                 cb = t.dom.bind(e, ev, function(e) {\r
9983                                                         var v;\r
9984 \r
9985                                                         e = e.target;\r
9986 \r
9987                                                         if (v = t.dom.getStyle(e, 'width')) {\r
9988                                                                 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
9989                                                                 t.dom.setStyle(e, 'width', '');\r
9990                                                         }\r
9991 \r
9992                                                         if (v = t.dom.getStyle(e, 'height')) {\r
9993                                                                 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
9994                                                                 t.dom.setStyle(e, 'height', '');\r
9995                                                         }\r
9996                                                 });\r
9997                                         } else {\r
9998                                                 ev = 'resizestart';\r
9999                                                 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
10000                                         }\r
10001 \r
10002                                         re = t.resizeInfo = {\r
10003                                                 node : e,\r
10004                                                 ev : ev,\r
10005                                                 cb : cb\r
10006                                         };\r
10007                                 });\r
10008 \r
10009                                 t.onKeyDown.add(function(ed, e) {\r
10010                                         switch (e.keyCode) {\r
10011                                                 case 8:\r
10012                                                         // Fix IE control + backspace browser bug\r
10013                                                         if (t.selection.getRng().item) {\r
10014                                                                 ed.dom.remove(t.selection.getRng().item(0));\r
10015                                                                 return Event.cancel(e);\r
10016                                                         }\r
10017                                         }\r
10018                                 });\r
10019 \r
10020                                 /*if (t.dom.boxModel) {\r
10021                                         t.getBody().style.height = '100%';\r
10022 \r
10023                                         Event.add(t.getWin(), 'resize', function(e) {\r
10024                                                 var docElm = t.getDoc().documentElement;\r
10025 \r
10026                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10027                                         });\r
10028                                 }*/\r
10029                         }\r
10030 \r
10031                         if (tinymce.isOpera) {\r
10032                                 t.onClick.add(function(ed, e) {\r
10033                                         Event.prevent(e);\r
10034                                 });\r
10035                         }\r
10036 \r
10037                         // Add custom undo/redo handlers\r
10038                         if (s.custom_undo_redo) {\r
10039                                 function addUndo() {\r
10040                                         t.undoManager.typing = 0;\r
10041                                         t.undoManager.add();\r
10042                                 };\r
10043 \r
10044                                 t.dom.bind(t.getDoc(), 'focusout', function(e) {\r
10045                                         if (!t.removed && t.undoManager.typing)\r
10046                                                 addUndo();\r
10047                                 });\r
10048 \r
10049                                 t.onKeyUp.add(function(ed, e) {\r
10050                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10051                                                 addUndo();\r
10052                                 });\r
10053 \r
10054                                 t.onKeyDown.add(function(ed, e) {\r
10055                                         var rng, parent, bookmark;\r
10056 \r
10057                                         // IE has a really odd bug where the DOM might include an node that doesn't have\r
10058                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
10059                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
10060                                         // after you delete contents from it. See: #3008923\r
10061                                         if (isIE && e.keyCode == 46) {\r
10062                                                 rng = t.selection.getRng();\r
10063 \r
10064                                                 if (rng.parentElement) {\r
10065                                                         parent = rng.parentElement();\r
10066 \r
10067                                                         // Select next word when ctrl key is used in combo with delete\r
10068                                                         if (e.ctrlKey) {\r
10069                                                                 rng.moveEnd('word', 1);\r
10070                                                                 rng.select();\r
10071                                                         }\r
10072 \r
10073                                                         // Delete contents\r
10074                                                         t.selection.getSel().clear();\r
10075 \r
10076                                                         // Check if we are within the same parent\r
10077                                                         if (rng.parentElement() == parent) {\r
10078                                                                 bookmark = t.selection.getBookmark();\r
10079 \r
10080                                                                 try {\r
10081                                                                         // Update the HTML and hopefully it will remove the artifacts\r
10082                                                                         parent.innerHTML = parent.innerHTML;\r
10083                                                                 } catch (ex) {\r
10084                                                                         // And since it's IE it can sometimes produce an unknown runtime error\r
10085                                                                 }\r
10086 \r
10087                                                                 // Restore the caret position\r
10088                                                                 t.selection.moveToBookmark(bookmark);\r
10089                                                         }\r
10090 \r
10091                                                         // Block the default delete behavior since it might be broken\r
10092                                                         e.preventDefault();\r
10093                                                         return;\r
10094                                                 }\r
10095                                         }\r
10096 \r
10097                                         // Is caracter positon keys\r
10098                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10099                                                 if (t.undoManager.typing)\r
10100                                                         addUndo();\r
10101 \r
10102                                                 return;\r
10103                                         }\r
10104 \r
10105                                         if (!t.undoManager.typing) {\r
10106                                                 t.undoManager.add();\r
10107                                                 t.undoManager.typing = 1;\r
10108                                         }\r
10109                                 });\r
10110 \r
10111                                 t.onMouseDown.add(function() {\r
10112                                         if (t.undoManager.typing)\r
10113                                                 addUndo();\r
10114                                 });\r
10115                         }\r
10116                 },\r
10117 \r
10118                 _isHidden : function() {\r
10119                         var s;\r
10120 \r
10121                         if (!isGecko)\r
10122                                 return 0;\r
10123 \r
10124                         // Weird, wheres that cursor selection?\r
10125                         s = this.selection.getSel();\r
10126                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
10127                 },\r
10128 \r
10129                 // Fix for bug #1867292\r
10130                 _fixNesting : function(s) {\r
10131                         var d = [], i;\r
10132 \r
10133                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
10134                                 var e;\r
10135 \r
10136                                 // Handle end element\r
10137                                 if (b === '/') {\r
10138                                         if (!d.length)\r
10139                                                 return '';\r
10140 \r
10141                                         if (c !== d[d.length - 1].tag) {\r
10142                                                 for (i=d.length - 1; i>=0; i--) {\r
10143                                                         if (d[i].tag === c) {\r
10144                                                                 d[i].close = 1;\r
10145                                                                 break;\r
10146                                                         }\r
10147                                                 }\r
10148 \r
10149                                                 return '';\r
10150                                         } else {\r
10151                                                 d.pop();\r
10152 \r
10153                                                 if (d.length && d[d.length - 1].close) {\r
10154                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
10155                                                         d.pop();\r
10156                                                 }\r
10157                                         }\r
10158                                 } else {\r
10159                                         // Ignore these\r
10160                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
10161                                                 return a;\r
10162 \r
10163                                         // Ignore closed ones\r
10164                                         if (/\/>$/.test(a))\r
10165                                                 return a;\r
10166 \r
10167                                         d.push({tag : c}); // Push start element\r
10168                                 }\r
10169 \r
10170                                 return a;\r
10171                         });\r
10172 \r
10173                         // End all open tags\r
10174                         for (i=d.length - 1; i>=0; i--)\r
10175                                 s += '</' + d[i].tag + '>';\r
10176 \r
10177                         return s;\r
10178                 }\r
10179         });\r
10180 })(tinymce);\r
10181 \r
10182 (function(tinymce) {\r
10183         // Added for compression purposes\r
10184         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
10185 \r
10186         tinymce.EditorCommands = function(editor) {\r
10187                 var dom = editor.dom,\r
10188                         selection = editor.selection,\r
10189                         commands = {state: {}, exec : {}, value : {}},\r
10190                         settings = editor.settings,\r
10191                         bookmark;\r
10192 \r
10193                 function execCommand(command, ui, value) {\r
10194                         var func;\r
10195 \r
10196                         command = command.toLowerCase();\r
10197                         if (func = commands.exec[command]) {\r
10198                                 func(command, ui, value);\r
10199                                 return TRUE;\r
10200                         }\r
10201 \r
10202                         return FALSE;\r
10203                 };\r
10204 \r
10205                 function queryCommandState(command) {\r
10206                         var func;\r
10207 \r
10208                         command = command.toLowerCase();\r
10209                         if (func = commands.state[command])\r
10210                                 return func(command);\r
10211 \r
10212                         return -1;\r
10213                 };\r
10214 \r
10215                 function queryCommandValue(command) {\r
10216                         var func;\r
10217 \r
10218                         command = command.toLowerCase();\r
10219                         if (func = commands.value[command])\r
10220                                 return func(command);\r
10221 \r
10222                         return FALSE;\r
10223                 };\r
10224 \r
10225                 function addCommands(command_list, type) {\r
10226                         type = type || 'exec';\r
10227 \r
10228                         each(command_list, function(callback, command) {\r
10229                                 each(command.toLowerCase().split(','), function(command) {\r
10230                                         commands[type][command] = callback;\r
10231                                 });\r
10232                         });\r
10233                 };\r
10234 \r
10235                 // Expose public methods\r
10236                 tinymce.extend(this, {\r
10237                         execCommand : execCommand,\r
10238                         queryCommandState : queryCommandState,\r
10239                         queryCommandValue : queryCommandValue,\r
10240                         addCommands : addCommands\r
10241                 });\r
10242 \r
10243                 // Private methods\r
10244 \r
10245                 function execNativeCommand(command, ui, value) {\r
10246                         if (ui === undefined)\r
10247                                 ui = FALSE;\r
10248 \r
10249                         if (value === undefined)\r
10250                                 value = null;\r
10251 \r
10252                         return editor.getDoc().execCommand(command, ui, value);\r
10253                 };\r
10254 \r
10255                 function isFormatMatch(name) {\r
10256                         return editor.formatter.match(name);\r
10257                 };\r
10258 \r
10259                 function toggleFormat(name, value) {\r
10260                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
10261                 };\r
10262 \r
10263                 function storeSelection(type) {\r
10264                         bookmark = selection.getBookmark(type);\r
10265                 };\r
10266 \r
10267                 function restoreSelection() {\r
10268                         selection.moveToBookmark(bookmark);\r
10269                 };\r
10270 \r
10271                 // Add execCommand overrides\r
10272                 addCommands({\r
10273                         // Ignore these, added for compatibility\r
10274                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
10275 \r
10276                         // Add undo manager logic\r
10277                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
10278                                 editor.undoManager.add();\r
10279                         },\r
10280 \r
10281                         'Cut,Copy,Paste' : function(command) {\r
10282                                 var doc = editor.getDoc(), failed;\r
10283 \r
10284                                 // Try executing the native command\r
10285                                 try {\r
10286                                         execNativeCommand(command);\r
10287                                 } catch (ex) {\r
10288                                         // Command failed\r
10289                                         failed = TRUE;\r
10290                                 }\r
10291 \r
10292                                 // Present alert message about clipboard access not being available\r
10293                                 if (failed || !doc.queryCommandSupported(command)) {\r
10294                                         if (tinymce.isGecko) {\r
10295                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
10296                                                         if (state)\r
10297                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
10298                                                 });\r
10299                                         } else\r
10300                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
10301                                 }\r
10302                         },\r
10303 \r
10304                         // Override unlink command\r
10305                         unlink : function(command) {\r
10306                                 if (selection.isCollapsed())\r
10307                                         selection.select(selection.getNode());\r
10308 \r
10309                                 execNativeCommand(command);\r
10310                                 selection.collapse(FALSE);\r
10311                         },\r
10312 \r
10313                         // Override justify commands to use the text formatter engine\r
10314                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
10315                                 var align = command.substring(7);\r
10316 \r
10317                                 // Remove all other alignments first\r
10318                                 each('left,center,right,full'.split(','), function(name) {\r
10319                                         if (align != name)\r
10320                                                 editor.formatter.remove('align' + name);\r
10321                                 });\r
10322 \r
10323                                 toggleFormat('align' + align);\r
10324                         },\r
10325 \r
10326                         // Override list commands to fix WebKit bug\r
10327                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
10328                                 var listElm, listParent;\r
10329 \r
10330                                 execNativeCommand(command);\r
10331 \r
10332                                 // WebKit produces lists within block elements so we need to split them\r
10333                                 // we will replace the native list creation logic to custom logic later on\r
10334                                 // TODO: Remove this when the list creation logic is removed\r
10335                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
10336                                 if (listElm) {\r
10337                                         listParent = listElm.parentNode;\r
10338 \r
10339                                         // If list is within a text block then split that block\r
10340                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
10341                                                 storeSelection();\r
10342                                                 dom.split(listParent, listElm);\r
10343                                                 restoreSelection();\r
10344                                         }\r
10345                                 }\r
10346                         },\r
10347 \r
10348                         // Override commands to use the text formatter engine\r
10349                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
10350                                 toggleFormat(command);\r
10351                         },\r
10352 \r
10353                         // Override commands to use the text formatter engine\r
10354                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
10355                                 toggleFormat(command, value);\r
10356                         },\r
10357 \r
10358                         FontSize : function(command, ui, value) {\r
10359                                 var fontClasses, fontSizes;\r
10360 \r
10361                                 // Convert font size 1-7 to styles\r
10362                                 if (value >= 1 && value <= 7) {\r
10363                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
10364                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
10365 \r
10366                                         if (fontClasses)\r
10367                                                 value = fontClasses[value - 1] || value;\r
10368                                         else\r
10369                                                 value = fontSizes[value - 1] || value;\r
10370                                 }\r
10371 \r
10372                                 toggleFormat(command, value);\r
10373                         },\r
10374 \r
10375                         RemoveFormat : function(command) {\r
10376                                 editor.formatter.remove(command);\r
10377                         },\r
10378 \r
10379                         mceBlockQuote : function(command) {\r
10380                                 toggleFormat('blockquote');\r
10381                         },\r
10382 \r
10383                         FormatBlock : function(command, ui, value) {\r
10384                                 return toggleFormat(value || 'p');\r
10385                         },\r
10386 \r
10387                         mceCleanup : function() {\r
10388                                 var bookmark = selection.getBookmark();\r
10389 \r
10390                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
10391 \r
10392                                 selection.moveToBookmark(bookmark);\r
10393                         },\r
10394 \r
10395                         mceRemoveNode : function(command, ui, value) {\r
10396                                 var node = value || selection.getNode();\r
10397 \r
10398                                 // Make sure that the body node isn't removed\r
10399                                 if (node != editor.getBody()) {\r
10400                                         storeSelection();\r
10401                                         editor.dom.remove(node, TRUE);\r
10402                                         restoreSelection();\r
10403                                 }\r
10404                         },\r
10405 \r
10406                         mceSelectNodeDepth : function(command, ui, value) {\r
10407                                 var counter = 0;\r
10408 \r
10409                                 dom.getParent(selection.getNode(), function(node) {\r
10410                                         if (node.nodeType == 1 && counter++ == value) {\r
10411                                                 selection.select(node);\r
10412                                                 return FALSE;\r
10413                                         }\r
10414                                 }, editor.getBody());\r
10415                         },\r
10416 \r
10417                         mceSelectNode : function(command, ui, value) {\r
10418                                 selection.select(value);\r
10419                         },\r
10420 \r
10421                         mceInsertContent : function(command, ui, value) {\r
10422                                 selection.setContent(value);\r
10423                         },\r
10424 \r
10425                         mceInsertRawHTML : function(command, ui, value) {\r
10426                                 selection.setContent('tiny_mce_marker');\r
10427                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value));\r
10428                         },\r
10429 \r
10430                         mceSetContent : function(command, ui, value) {\r
10431                                 editor.setContent(value);\r
10432                         },\r
10433 \r
10434                         'Indent,Outdent' : function(command) {\r
10435                                 var intentValue, indentUnit, value;\r
10436 \r
10437                                 // Setup indent level\r
10438                                 intentValue = settings.indentation;\r
10439                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
10440                                 intentValue = parseInt(intentValue);\r
10441 \r
10442                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
10443                                         each(selection.getSelectedBlocks(), function(element) {\r
10444                                                 if (command == 'outdent') {\r
10445                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
10446                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
10447                                                 } else\r
10448                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
10449                                         });\r
10450                                 } else\r
10451                                         execNativeCommand(command);\r
10452                         },\r
10453 \r
10454                         mceRepaint : function() {\r
10455                                 var bookmark;\r
10456 \r
10457                                 if (tinymce.isGecko) {\r
10458                                         try {\r
10459                                                 storeSelection(TRUE);\r
10460 \r
10461                                                 if (selection.getSel())\r
10462                                                         selection.getSel().selectAllChildren(editor.getBody());\r
10463 \r
10464                                                 selection.collapse(TRUE);\r
10465                                                 restoreSelection();\r
10466                                         } catch (ex) {\r
10467                                                 // Ignore\r
10468                                         }\r
10469                                 }\r
10470                         },\r
10471 \r
10472                         mceToggleFormat : function(command, ui, value) {\r
10473                                 editor.formatter.toggle(value);\r
10474                         },\r
10475 \r
10476                         InsertHorizontalRule : function() {\r
10477                                 selection.setContent('<hr />');\r
10478                         },\r
10479 \r
10480                         mceToggleVisualAid : function() {\r
10481                                 editor.hasVisual = !editor.hasVisual;\r
10482                                 editor.addVisual();\r
10483                         },\r
10484 \r
10485                         mceReplaceContent : function(command, ui, value) {\r
10486                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
10487                         },\r
10488 \r
10489                         mceInsertLink : function(command, ui, value) {\r
10490                                 var link = dom.getParent(selection.getNode(), 'a');\r
10491 \r
10492                                 if (tinymce.is(value, 'string'))\r
10493                                         value = {href : value};\r
10494 \r
10495                                 if (!link) {\r
10496                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
10497                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
10498                                                 dom.setAttribs(link, value);\r
10499                                         });\r
10500                                 } else {\r
10501                                         if (value.href)\r
10502                                                 dom.setAttribs(link, value);\r
10503                                         else\r
10504                                                 editor.dom.remove(link, TRUE);\r
10505                                 }\r
10506                         },\r
10507                         \r
10508                         selectAll : function() {\r
10509                                 var root = dom.getRoot(), rng = dom.createRng();\r
10510 \r
10511                                 rng.setStart(root, 0);\r
10512                                 rng.setEnd(root, root.childNodes.length);\r
10513 \r
10514                                 editor.selection.setRng(rng);\r
10515                         }\r
10516                 });\r
10517 \r
10518                 // Add queryCommandState overrides\r
10519                 addCommands({\r
10520                         // Override justify commands\r
10521                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
10522                                 return isFormatMatch('align' + command.substring(7));\r
10523                         },\r
10524 \r
10525                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
10526                                 return isFormatMatch(command);\r
10527                         },\r
10528 \r
10529                         mceBlockQuote : function() {\r
10530                                 return isFormatMatch('blockquote');\r
10531                         },\r
10532 \r
10533                         Outdent : function() {\r
10534                                 var node;\r
10535 \r
10536                                 if (settings.inline_styles) {\r
10537                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
10538                                                 return TRUE;\r
10539 \r
10540                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
10541                                                 return TRUE;\r
10542                                 }\r
10543 \r
10544                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
10545                         },\r
10546 \r
10547                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
10548                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
10549                         }\r
10550                 }, 'state');\r
10551 \r
10552                 // Add queryCommandValue overrides\r
10553                 addCommands({\r
10554                         'FontSize,FontName' : function(command) {\r
10555                                 var value = 0, parent;\r
10556 \r
10557                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
10558                                         if (command == 'fontsize')\r
10559                                                 value = parent.style.fontSize;\r
10560                                         else\r
10561                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
10562                                 }\r
10563 \r
10564                                 return value;\r
10565                         }\r
10566                 }, 'value');\r
10567 \r
10568                 // Add undo manager logic\r
10569                 if (settings.custom_undo_redo) {\r
10570                         addCommands({\r
10571                                 Undo : function() {\r
10572                                         editor.undoManager.undo();\r
10573                                 },\r
10574 \r
10575                                 Redo : function() {\r
10576                                         editor.undoManager.redo();\r
10577                                 }\r
10578                         });\r
10579                 }\r
10580         };\r
10581 })(tinymce);\r
10582 (function(tinymce) {\r
10583         var Dispatcher = tinymce.util.Dispatcher;\r
10584 \r
10585         tinymce.UndoManager = function(editor) {\r
10586                 var self, index = 0, data = [];\r
10587 \r
10588                 function getContent() {\r
10589                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
10590                 };\r
10591 \r
10592                 return self = {\r
10593                         typing : 0,\r
10594 \r
10595                         onAdd : new Dispatcher(self),\r
10596                         onUndo : new Dispatcher(self),\r
10597                         onRedo : new Dispatcher(self),\r
10598 \r
10599                         add : function(level) {\r
10600                                 var i, settings = editor.settings, lastLevel;\r
10601 \r
10602                                 level = level || {};\r
10603                                 level.content = getContent();\r
10604 \r
10605                                 // Add undo level if needed\r
10606                                 lastLevel = data[index];\r
10607                                 if (lastLevel && lastLevel.content == level.content) {\r
10608                                         if (index > 0 || data.length == 1)\r
10609                                                 return null;\r
10610                                 }\r
10611 \r
10612                                 // Time to compress\r
10613                                 if (settings.custom_undo_redo_levels) {\r
10614                                         if (data.length > settings.custom_undo_redo_levels) {\r
10615                                                 for (i = 0; i < data.length - 1; i++)\r
10616                                                         data[i] = data[i + 1];\r
10617 \r
10618                                                 data.length--;\r
10619                                                 index = data.length;\r
10620                                         }\r
10621                                 }\r
10622 \r
10623                                 // Get a non intrusive normalized bookmark\r
10624                                 level.bookmark = editor.selection.getBookmark(2, true);\r
10625 \r
10626                                 // Crop array if needed\r
10627                                 if (index < data.length - 1) {\r
10628                                         // Treat first level as initial\r
10629                                         if (index == 0)\r
10630                                                 data = [];\r
10631                                         else\r
10632                                                 data.length = index + 1;\r
10633                                 }\r
10634 \r
10635                                 data.push(level);\r
10636                                 index = data.length - 1;\r
10637 \r
10638                                 self.onAdd.dispatch(self, level);\r
10639                                 editor.isNotDirty = 0;\r
10640 \r
10641                                 return level;\r
10642                         },\r
10643 \r
10644                         undo : function() {\r
10645                                 var level, i;\r
10646 \r
10647                                 if (self.typing) {\r
10648                                         self.add();\r
10649                                         self.typing = 0;\r
10650                                 }\r
10651 \r
10652                                 if (index > 0) {\r
10653                                         level = data[--index];\r
10654 \r
10655                                         editor.setContent(level.content, {format : 'raw'});\r
10656                                         editor.selection.moveToBookmark(level.bookmark);\r
10657 \r
10658                                         self.onUndo.dispatch(self, level);\r
10659                                 }\r
10660 \r
10661                                 return level;\r
10662                         },\r
10663 \r
10664                         redo : function() {\r
10665                                 var level;\r
10666 \r
10667                                 if (index < data.length - 1) {\r
10668                                         level = data[++index];\r
10669 \r
10670                                         editor.setContent(level.content, {format : 'raw'});\r
10671                                         editor.selection.moveToBookmark(level.bookmark);\r
10672 \r
10673                                         self.onRedo.dispatch(self, level);\r
10674                                 }\r
10675 \r
10676                                 return level;\r
10677                         },\r
10678 \r
10679                         clear : function() {\r
10680                                 data = [];\r
10681                                 index = self.typing = 0;\r
10682                         },\r
10683 \r
10684                         hasUndo : function() {\r
10685                                 return index > 0 || self.typing;\r
10686                         },\r
10687 \r
10688                         hasRedo : function() {\r
10689                                 return index < data.length - 1;\r
10690                         }\r
10691                 };\r
10692         };\r
10693 })(tinymce);\r
10694 \r
10695 (function(tinymce) {\r
10696         // Shorten names\r
10697         var Event = tinymce.dom.Event,\r
10698                 isIE = tinymce.isIE,\r
10699                 isGecko = tinymce.isGecko,\r
10700                 isOpera = tinymce.isOpera,\r
10701                 each = tinymce.each,\r
10702                 extend = tinymce.extend,\r
10703                 TRUE = true,\r
10704                 FALSE = false;\r
10705 \r
10706         function cloneFormats(node) {\r
10707                 var clone, temp, inner;\r
10708 \r
10709                 do {\r
10710                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
10711                                 if (clone) {\r
10712                                         temp = node.cloneNode(false);\r
10713                                         temp.appendChild(clone);\r
10714                                         clone = temp;\r
10715                                 } else {\r
10716                                         clone = inner = node.cloneNode(false);\r
10717                                 }\r
10718 \r
10719                                 clone.removeAttribute('id');\r
10720                         }\r
10721                 } while (node = node.parentNode);\r
10722 \r
10723                 if (clone)\r
10724                         return {wrapper : clone, inner : inner};\r
10725         };\r
10726 \r
10727         // Checks if the selection/caret is at the end of the specified block element\r
10728         function isAtEnd(rng, par) {\r
10729                 var rng2 = par.ownerDocument.createRange();\r
10730 \r
10731                 rng2.setStart(rng.endContainer, rng.endOffset);\r
10732                 rng2.setEndAfter(par);\r
10733 \r
10734                 // 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
10735                 return rng2.cloneContents().textContent.length == 0;\r
10736         };\r
10737 \r
10738         function isEmpty(n) {\r
10739                 n = n.innerHTML;\r
10740 \r
10741                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
10742                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
10743 \r
10744                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
10745         };\r
10746 \r
10747         function splitList(selection, dom, li) {\r
10748                 var listBlock, block;\r
10749 \r
10750                 if (isEmpty(li)) {\r
10751                         listBlock = dom.getParent(li, 'ul,ol');\r
10752 \r
10753                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
10754                                 dom.split(listBlock, li);\r
10755                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
10756                                 dom.replace(block, li);\r
10757                                 selection.select(block, 1);\r
10758                         }\r
10759 \r
10760                         return FALSE;\r
10761                 }\r
10762 \r
10763                 return TRUE;\r
10764         };\r
10765 \r
10766         tinymce.create('tinymce.ForceBlocks', {\r
10767                 ForceBlocks : function(ed) {\r
10768                         var t = this, s = ed.settings, elm;\r
10769 \r
10770                         t.editor = ed;\r
10771                         t.dom = ed.dom;\r
10772                         elm = (s.forced_root_block || 'p').toLowerCase();\r
10773                         s.element = elm.toUpperCase();\r
10774 \r
10775                         ed.onPreInit.add(t.setup, t);\r
10776 \r
10777                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
10778                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
10779                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
10780                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
10781                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
10782 \r
10783                         function padd(ed, o) {\r
10784                                 if (isOpera)\r
10785                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
10786 \r
10787                                 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
10788 \r
10789                                 if (!isIE && !isOpera && o.set) {\r
10790                                         // Use &nbsp; instead of BR in padded paragraphs\r
10791                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
10792                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
10793                                 } else\r
10794                                         o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
10795                         };\r
10796 \r
10797                         ed.onBeforeSetContent.add(padd);\r
10798                         ed.onPostProcess.add(padd);\r
10799 \r
10800                         if (s.forced_root_block) {\r
10801                                 ed.onInit.add(t.forceRoots, t);\r
10802                                 ed.onSetContent.add(t.forceRoots, t);\r
10803                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
10804                         }\r
10805                 },\r
10806 \r
10807                 setup : function() {\r
10808                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
10809 \r
10810                         // Force root blocks when typing and when getting output\r
10811                         if (s.forced_root_block) {\r
10812                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
10813                                 ed.onKeyUp.add(t.forceRoots, t);\r
10814                                 ed.onPreProcess.add(t.forceRoots, t);\r
10815                         }\r
10816 \r
10817                         if (s.force_br_newlines) {\r
10818                                 // Force IE to produce BRs on enter\r
10819                                 if (isIE) {\r
10820                                         ed.onKeyPress.add(function(ed, e) {\r
10821                                                 var n;\r
10822 \r
10823                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
10824                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
10825                                                         n = dom.get('__');\r
10826                                                         n.removeAttribute('id');\r
10827                                                         selection.select(n);\r
10828                                                         selection.collapse();\r
10829                                                         return Event.cancel(e);\r
10830                                                 }\r
10831                                         });\r
10832                                 }\r
10833                         }\r
10834 \r
10835                         if (s.force_p_newlines) {\r
10836                                 if (!isIE) {\r
10837                                         ed.onKeyPress.add(function(ed, e) {\r
10838                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
10839                                                         Event.cancel(e);\r
10840                                         });\r
10841                                 } else {\r
10842                                         // Ungly hack to for IE to preserve the formatting when you press\r
10843                                         // enter at the end of a block element with formatted contents\r
10844                                         // This logic overrides the browsers default logic with\r
10845                                         // custom logic that enables us to control the output\r
10846                                         tinymce.addUnload(function() {\r
10847                                                 t._previousFormats = 0; // Fix IE leak\r
10848                                         });\r
10849 \r
10850                                         ed.onKeyPress.add(function(ed, e) {\r
10851                                                 t._previousFormats = 0;\r
10852 \r
10853                                                 // Clone the current formats, this will later be applied to the new block contents\r
10854                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
10855                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
10856                                         });\r
10857 \r
10858                                         ed.onKeyUp.add(function(ed, e) {\r
10859                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
10860                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
10861                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
10862 \r
10863                                                         // Parent is an empty block\r
10864                                                         if (!parent.hasChildNodes()) {\r
10865                                                                 parent = dom.getParent(parent, dom.isBlock);\r
10866 \r
10867                                                                 if (parent) {\r
10868                                                                         parent.innerHTML = '';\r
10869         \r
10870                                                                         if (t._previousFormats) {\r
10871                                                                                 parent.appendChild(fmt.wrapper);\r
10872                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
10873                                                                         } else\r
10874                                                                                 parent.innerHTML = '\uFEFF';\r
10875 \r
10876                                                                         selection.select(parent, 1);\r
10877                                                                         ed.getDoc().execCommand('Delete', false, null);\r
10878                                                                 }\r
10879                                                         }\r
10880                                                 }\r
10881                                         });\r
10882                                 }\r
10883 \r
10884                                 if (isGecko) {\r
10885                                         ed.onKeyDown.add(function(ed, e) {\r
10886                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
10887                                                         t.backspaceDelete(e, e.keyCode == 8);\r
10888                                         });\r
10889                                 }\r
10890                         }\r
10891 \r
10892                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
10893                         if (tinymce.isWebKit) {\r
10894                                 function insertBr(ed) {\r
10895                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
10896 \r
10897                                         // Insert BR element\r
10898                                         rng.insertNode(br = dom.create('br'));\r
10899 \r
10900                                         // Place caret after BR\r
10901                                         rng.setStartAfter(br);\r
10902                                         rng.setEndAfter(br);\r
10903                                         selection.setRng(rng);\r
10904 \r
10905                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
10906                                         if (selection.getSel().focusNode == br.previousSibling) {\r
10907                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
10908                                                 selection.collapse(TRUE);\r
10909                                         }\r
10910 \r
10911                                         // Create a temporary DIV after the BR and get the position as it\r
10912                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
10913                                         dom.insertAfter(div, br);\r
10914                                         divYPos = dom.getPos(div).y;\r
10915                                         dom.remove(div);\r
10916 \r
10917                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
10918                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
10919                                                 ed.getWin().scrollTo(0, divYPos);\r
10920                                 };\r
10921 \r
10922                                 ed.onKeyPress.add(function(ed, e) {\r
10923                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
10924                                                 insertBr(ed);\r
10925                                                 Event.cancel(e);\r
10926                                         }\r
10927                                 });\r
10928                         }\r
10929 \r
10930                         // Padd empty inline elements within block elements\r
10931                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
10932                         ed.onPreProcess.add(function(ed, o) {\r
10933                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
10934                                         if (isEmpty(p)) {\r
10935                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
10936                                                         if (!n.hasChildNodes()) {\r
10937                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
10938                                                                 return FALSE; // Break the loop one padding is enough\r
10939                                                         }\r
10940                                                 });\r
10941                                         }\r
10942                                 });\r
10943                         });\r
10944 \r
10945                         // IE specific fixes\r
10946                         if (isIE) {\r
10947                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
10948                                 if (s.element != 'P') {\r
10949                                         ed.onKeyPress.add(function(ed, e) {\r
10950                                                 t.lastElm = selection.getNode().nodeName;\r
10951                                         });\r
10952 \r
10953                                         ed.onKeyUp.add(function(ed, e) {\r
10954                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
10955 \r
10956                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
10957                                                         n = dom.rename(n, s.element);\r
10958                                                         selection.select(n);\r
10959                                                         selection.collapse();\r
10960                                                         ed.nodeChanged();\r
10961                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
10962                                                         bl = dom.getParent(n, 'p');\r
10963 \r
10964                                                         if (bl) {\r
10965                                                                 dom.rename(bl, s.element);\r
10966                                                                 ed.nodeChanged();\r
10967                                                         }\r
10968                                                 }\r
10969                                         });\r
10970                                 }\r
10971                         }\r
10972                 },\r
10973 \r
10974                 find : function(n, t, s) {\r
10975                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
10976 \r
10977                         while (n = w.nextNode()) {\r
10978                                 c++;\r
10979 \r
10980                                 // Index by node\r
10981                                 if (t == 0 && n == s)\r
10982                                         return c;\r
10983 \r
10984                                 // Node by index\r
10985                                 if (t == 1 && c == s)\r
10986                                         return n;\r
10987                         }\r
10988 \r
10989                         return -1;\r
10990                 },\r
10991 \r
10992                 forceRoots : function(ed, e) {\r
10993                         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
10994                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
10995 \r
10996                         // Fix for bug #1863847\r
10997                         //if (e && e.keyCode == 13)\r
10998                         //      return TRUE;\r
10999 \r
11000                         // Wrap non blocks into blocks\r
11001                         for (i = nl.length - 1; i >= 0; i--) {\r
11002                                 nx = nl[i];\r
11003 \r
11004                                 // Ignore internal elements\r
11005                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
11006                                         bl = null;\r
11007                                         continue;\r
11008                                 }\r
11009 \r
11010                                 // Is text or non block element\r
11011                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11012                                         if (!bl) {\r
11013                                                 // Create new block but ignore whitespace\r
11014                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11015                                                         // Store selection\r
11016                                                         if (si == -2 && r) {\r
11017                                                                 if (!isIE) {\r
11018                                                                         // If selection is element then mark it\r
11019                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11020                                                                                 // Save the id of the selected element\r
11021                                                                                 eid = n.getAttribute("id");\r
11022                                                                                 n.setAttribute("id", "__mce");\r
11023                                                                         } else {\r
11024                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
11025                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11026                                                                                         so = r.startOffset;\r
11027                                                                                         eo = r.endOffset;\r
11028                                                                                         si = t.find(b, 0, r.startContainer);\r
11029                                                                                         ei = t.find(b, 0, r.endContainer);\r
11030                                                                                 }\r
11031                                                                         }\r
11032                                                                 } else {\r
11033                                                                         // Force control range into text range\r
11034                                                                         if (r.item) {\r
11035                                                                                 tr = d.body.createTextRange();\r
11036                                                                                 tr.moveToElementText(r.item(0));\r
11037                                                                                 r = tr;\r
11038                                                                         }\r
11039 \r
11040                                                                         tr = d.body.createTextRange();\r
11041                                                                         tr.moveToElementText(b);\r
11042                                                                         tr.collapse(1);\r
11043                                                                         bp = tr.move('character', c) * -1;\r
11044 \r
11045                                                                         tr = r.duplicate();\r
11046                                                                         tr.collapse(1);\r
11047                                                                         sp = tr.move('character', c) * -1;\r
11048 \r
11049                                                                         tr = r.duplicate();\r
11050                                                                         tr.collapse(0);\r
11051                                                                         le = (tr.move('character', c) * -1) - sp;\r
11052 \r
11053                                                                         si = sp - bp;\r
11054                                                                         ei = le;\r
11055                                                                 }\r
11056                                                         }\r
11057 \r
11058                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11059                                                         // See: http://support.microsoft.com/kb/829907\r
11060                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
11061                                                         nx.parentNode.replaceChild(bl, nx);\r
11062                                                         bl.appendChild(nx);\r
11063                                                 }\r
11064                                         } else {\r
11065                                                 if (bl.hasChildNodes())\r
11066                                                         bl.insertBefore(nx, bl.firstChild);\r
11067                                                 else\r
11068                                                         bl.appendChild(nx);\r
11069                                         }\r
11070                                 } else\r
11071                                         bl = null; // Time to create new block\r
11072                         }\r
11073 \r
11074                         // Restore selection\r
11075                         if (si != -2) {\r
11076                                 if (!isIE) {\r
11077                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
11078                                         r = d.createRange();\r
11079 \r
11080                                         // Select last location or generated block\r
11081                                         if (si != -1)\r
11082                                                 r.setStart(t.find(b, 1, si), so);\r
11083                                         else\r
11084                                                 r.setStart(bl, 0);\r
11085 \r
11086                                         // Select last location or generated block\r
11087                                         if (ei != -1)\r
11088                                                 r.setEnd(t.find(b, 1, ei), eo);\r
11089                                         else\r
11090                                                 r.setEnd(bl, 0);\r
11091 \r
11092                                         if (s) {\r
11093                                                 s.removeAllRanges();\r
11094                                                 s.addRange(r);\r
11095                                         }\r
11096                                 } else {\r
11097                                         try {\r
11098                                                 r = s.createRange();\r
11099                                                 r.moveToElementText(b);\r
11100                                                 r.collapse(1);\r
11101                                                 r.moveStart('character', si);\r
11102                                                 r.moveEnd('character', ei);\r
11103                                                 r.select();\r
11104                                         } catch (ex) {\r
11105                                                 // Ignore\r
11106                                         }\r
11107                                 }\r
11108                         } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
11109                                 // Restore the id of the selected element\r
11110                                 if (eid)\r
11111                                         n.setAttribute('id', eid);\r
11112                                 else\r
11113                                         n.removeAttribute('id');\r
11114 \r
11115                                 // Move caret before selected element\r
11116                                 r = d.createRange();\r
11117                                 r.setStartBefore(n);\r
11118                                 r.setEndBefore(n);\r
11119                                 se.setRng(r);\r
11120                         }\r
11121                 },\r
11122 \r
11123                 getParentBlock : function(n) {\r
11124                         var d = this.dom;\r
11125 \r
11126                         return d.getParent(n, d.isBlock);\r
11127                 },\r
11128 \r
11129                 insertPara : function(e) {\r
11130                         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
11131                         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
11132 \r
11133                         // If root blocks are forced then use Operas default behavior since it's really good\r
11134 // Removed due to bug: #1853816\r
11135 //                      if (se.forced_root_block && isOpera)\r
11136 //                              return TRUE;\r
11137 \r
11138                         // Setup before range\r
11139                         rb = d.createRange();\r
11140 \r
11141                         // If is before the first block element and in body, then move it into first block element\r
11142                         rb.setStart(s.anchorNode, s.anchorOffset);\r
11143                         rb.collapse(TRUE);\r
11144 \r
11145                         // Setup after range\r
11146                         ra = d.createRange();\r
11147 \r
11148                         // If is before the first block element and in body, then move it into first block element\r
11149                         ra.setStart(s.focusNode, s.focusOffset);\r
11150                         ra.collapse(TRUE);\r
11151 \r
11152                         // Setup start/end points\r
11153                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
11154                         sn = dir ? s.anchorNode : s.focusNode;\r
11155                         so = dir ? s.anchorOffset : s.focusOffset;\r
11156                         en = dir ? s.focusNode : s.anchorNode;\r
11157                         eo = dir ? s.focusOffset : s.anchorOffset;\r
11158 \r
11159                         // If selection is in empty table cell\r
11160                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
11161                                 if (sn.firstChild.nodeName == 'BR')\r
11162                                         dom.remove(sn.firstChild); // Remove BR\r
11163 \r
11164                                 // Create two new block elements\r
11165                                 if (sn.childNodes.length == 0) {\r
11166                                         ed.dom.add(sn, se.element, null, '<br />');\r
11167                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11168                                 } else {\r
11169                                         n = sn.innerHTML;\r
11170                                         sn.innerHTML = '';\r
11171                                         ed.dom.add(sn, se.element, null, n);\r
11172                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11173                                 }\r
11174 \r
11175                                 // Move caret into the last one\r
11176                                 r = d.createRange();\r
11177                                 r.selectNodeContents(aft);\r
11178                                 r.collapse(1);\r
11179                                 ed.selection.setRng(r);\r
11180 \r
11181                                 return FALSE;\r
11182                         }\r
11183 \r
11184                         // If the caret is in an invalid location in FF we need to move it into the first block\r
11185                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
11186                                 sn = en = sn.firstChild;\r
11187                                 so = eo = 0;\r
11188                                 rb = d.createRange();\r
11189                                 rb.setStart(sn, 0);\r
11190                                 ra = d.createRange();\r
11191                                 ra.setStart(en, 0);\r
11192                         }\r
11193 \r
11194                         // Never use body as start or end node\r
11195                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11196                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
11197                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
11198                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
11199 \r
11200                         // Get start and end blocks\r
11201                         sb = t.getParentBlock(sn);\r
11202                         eb = t.getParentBlock(en);\r
11203                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
11204 \r
11205                         // Return inside list use default browser behavior\r
11206                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
11207                                 if (n.nodeName == 'LI')\r
11208                                         return splitList(ed.selection, t.dom, n);\r
11209 \r
11210                                 return TRUE;\r
11211                         }\r
11212 \r
11213                         // If caption or absolute layers then always generate new blocks within\r
11214                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11215                                 bn = se.element;\r
11216                                 sb = null;\r
11217                         }\r
11218 \r
11219                         // If caption or absolute layers then always generate new blocks within\r
11220                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
11221                                 bn = se.element;\r
11222                                 eb = null;\r
11223                         }\r
11224 \r
11225                         // Use P instead\r
11226                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
11227                                 bn = se.element;\r
11228                                 sb = eb = null;\r
11229                         }\r
11230 \r
11231                         // Setup new before and after blocks\r
11232                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
11233                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
11234 \r
11235                         // Remove id from after clone\r
11236                         aft.removeAttribute('id');\r
11237 \r
11238                         // Is header and cursor is at the end, then force paragraph under\r
11239                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
11240                                 aft = ed.dom.create(se.element);\r
11241 \r
11242                         // Find start chop node\r
11243                         n = sc = sn;\r
11244                         do {\r
11245                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11246                                         break;\r
11247 \r
11248                                 sc = n;\r
11249                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
11250 \r
11251                         // Find end chop node\r
11252                         n = ec = en;\r
11253                         do {\r
11254                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
11255                                         break;\r
11256 \r
11257                                 ec = n;\r
11258                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
11259 \r
11260                         // Place first chop part into before block element\r
11261                         if (sc.nodeName == bn)\r
11262                                 rb.setStart(sc, 0);\r
11263                         else\r
11264                                 rb.setStartBefore(sc);\r
11265 \r
11266                         rb.setEnd(sn, so);\r
11267                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11268 \r
11269                         // Place secnd chop part within new block element\r
11270                         try {\r
11271                                 ra.setEndAfter(ec);\r
11272                         } catch(ex) {\r
11273                                 //console.debug(s.focusNode, s.focusOffset);\r
11274                         }\r
11275 \r
11276                         ra.setStart(en, eo);\r
11277                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
11278 \r
11279                         // Create range around everything\r
11280                         r = d.createRange();\r
11281                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
11282                                 r.setStartBefore(sc.parentNode);\r
11283                         } else {\r
11284                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
11285                                         r.setStartBefore(rb.startContainer);\r
11286                                 else\r
11287                                         r.setStart(rb.startContainer, rb.startOffset);\r
11288                         }\r
11289 \r
11290                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
11291                                 r.setEndAfter(ec.parentNode);\r
11292                         else\r
11293                                 r.setEnd(ra.endContainer, ra.endOffset);\r
11294 \r
11295                         // Delete and replace it with new block elements\r
11296                         r.deleteContents();\r
11297 \r
11298                         if (isOpera)\r
11299                                 ed.getWin().scrollTo(0, vp.y);\r
11300 \r
11301                         // Never wrap blocks in blocks\r
11302                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
11303                                 bef.innerHTML = bef.firstChild.innerHTML;\r
11304 \r
11305                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
11306                                 aft.innerHTML = aft.firstChild.innerHTML;\r
11307 \r
11308                         // Padd empty blocks\r
11309                         if (isEmpty(bef))\r
11310                                 bef.innerHTML = '<br />';\r
11311 \r
11312                         function appendStyles(e, en) {\r
11313                                 var nl = [], nn, n, i;\r
11314 \r
11315                                 e.innerHTML = '';\r
11316 \r
11317                                 // Make clones of style elements\r
11318                                 if (se.keep_styles) {\r
11319                                         n = en;\r
11320                                         do {\r
11321                                                 // We only want style specific elements\r
11322                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
11323                                                         nn = n.cloneNode(FALSE);\r
11324                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
11325                                                         nl.push(nn);\r
11326                                                 }\r
11327                                         } while (n = n.parentNode);\r
11328                                 }\r
11329 \r
11330                                 // Append style elements to aft\r
11331                                 if (nl.length > 0) {\r
11332                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
11333                                                 nn = nn.appendChild(nl[i]);\r
11334 \r
11335                                         // Padd most inner style element\r
11336                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11337                                         return nl[0]; // Move caret to most inner element\r
11338                                 } else\r
11339                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
11340                         };\r
11341 \r
11342                         // Fill empty afterblook with current style\r
11343                         if (isEmpty(aft))\r
11344                                 car = appendStyles(aft, en);\r
11345 \r
11346                         // Opera needs this one backwards for older versions\r
11347                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
11348                                 r.insertNode(bef);\r
11349                                 r.insertNode(aft);\r
11350                         } else {\r
11351                                 r.insertNode(aft);\r
11352                                 r.insertNode(bef);\r
11353                         }\r
11354 \r
11355                         // Normalize\r
11356                         aft.normalize();\r
11357                         bef.normalize();\r
11358 \r
11359                         function first(n) {\r
11360                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
11361                         };\r
11362 \r
11363                         // Move cursor and scroll into view\r
11364                         r = d.createRange();\r
11365                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
11366                         r.collapse(1);\r
11367                         s.removeAllRanges();\r
11368                         s.addRange(r);\r
11369 \r
11370                         // 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
11371                         y = ed.dom.getPos(aft).y;\r
11372                         ch = aft.clientHeight;\r
11373 \r
11374                         // Is element within viewport\r
11375                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
11376                                 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
11377                                 //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
11378                         }\r
11379 \r
11380                         return FALSE;\r
11381                 },\r
11382 \r
11383                 backspaceDelete : function(e, bs) {\r
11384                         var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;\r
11385 \r
11386                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
11387                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
11388                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
11389 \r
11390                                 // Walk the dom backwards until we find a text node\r
11391                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
11392                                         if (n.nodeType == 3) {\r
11393                                                 r.setStart(n, n.nodeValue.length);\r
11394                                                 r.collapse(true);\r
11395                                                 se.setRng(r);\r
11396                                                 return;\r
11397                                         }\r
11398                                 }\r
11399                         }\r
11400 \r
11401                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
11402                         // This workaround removes the element by hand and moves the caret to the previous element\r
11403                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
11404                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
11405                                         // Find previous block element\r
11406                                         n = sc;\r
11407                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
11408 \r
11409                                         if (n) {\r
11410                                                 if (sc != b.firstChild) {\r
11411                                                         // Find last text node\r
11412                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
11413                                                         while (tn = w.nextNode())\r
11414                                                                 n = tn;\r
11415 \r
11416                                                         // Place caret at the end of last text node\r
11417                                                         r = ed.getDoc().createRange();\r
11418                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
11419                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
11420                                                         se.setRng(r);\r
11421 \r
11422                                                         // Remove the target container\r
11423                                                         ed.dom.remove(sc);\r
11424                                                 }\r
11425 \r
11426                                                 return Event.cancel(e);\r
11427                                         }\r
11428                                 }\r
11429                         }\r
11430                 }\r
11431         });\r
11432 })(tinymce);\r
11433 \r
11434 (function(tinymce) {\r
11435         // Shorten names\r
11436         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
11437 \r
11438         tinymce.create('tinymce.ControlManager', {\r
11439                 ControlManager : function(ed, s) {\r
11440                         var t = this, i;\r
11441 \r
11442                         s = s || {};\r
11443                         t.editor = ed;\r
11444                         t.controls = {};\r
11445                         t.onAdd = new tinymce.util.Dispatcher(t);\r
11446                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
11447                         t.prefix = s.prefix || ed.id + '_';\r
11448                         t._cls = {};\r
11449 \r
11450                         t.onPostRender.add(function() {\r
11451                                 each(t.controls, function(c) {\r
11452                                         c.postRender();\r
11453                                 });\r
11454                         });\r
11455                 },\r
11456 \r
11457                 get : function(id) {\r
11458                         return this.controls[this.prefix + id] || this.controls[id];\r
11459                 },\r
11460 \r
11461                 setActive : function(id, s) {\r
11462                         var c = null;\r
11463 \r
11464                         if (c = this.get(id))\r
11465                                 c.setActive(s);\r
11466 \r
11467                         return c;\r
11468                 },\r
11469 \r
11470                 setDisabled : function(id, s) {\r
11471                         var c = null;\r
11472 \r
11473                         if (c = this.get(id))\r
11474                                 c.setDisabled(s);\r
11475 \r
11476                         return c;\r
11477                 },\r
11478 \r
11479                 add : function(c) {\r
11480                         var t = this;\r
11481 \r
11482                         if (c) {\r
11483                                 t.controls[c.id] = c;\r
11484                                 t.onAdd.dispatch(c, t);\r
11485                         }\r
11486 \r
11487                         return c;\r
11488                 },\r
11489 \r
11490                 createControl : function(n) {\r
11491                         var c, t = this, ed = t.editor;\r
11492 \r
11493                         each(ed.plugins, function(p) {\r
11494                                 if (p.createControl) {\r
11495                                         c = p.createControl(n, t);\r
11496 \r
11497                                         if (c)\r
11498                                                 return false;\r
11499                                 }\r
11500                         });\r
11501 \r
11502                         switch (n) {\r
11503                                 case "|":\r
11504                                 case "separator":\r
11505                                         return t.createSeparator();\r
11506                         }\r
11507 \r
11508                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
11509                                 return t.createButton(n, c);\r
11510 \r
11511                         return t.add(c);\r
11512                 },\r
11513 \r
11514                 createDropMenu : function(id, s, cc) {\r
11515                         var t = this, ed = t.editor, c, bm, v, cls;\r
11516 \r
11517                         s = extend({\r
11518                                 'class' : 'mceDropDown',\r
11519                                 constrain : ed.settings.constrain_menus\r
11520                         }, s);\r
11521 \r
11522                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
11523                         if (v = ed.getParam('skin_variant'))\r
11524                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
11525 \r
11526                         id = t.prefix + id;\r
11527                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
11528                         c = t.controls[id] = new cls(id, s);\r
11529                         c.onAddItem.add(function(c, o) {\r
11530                                 var s = o.settings;\r
11531 \r
11532                                 s.title = ed.getLang(s.title, s.title);\r
11533 \r
11534                                 if (!s.onclick) {\r
11535                                         s.onclick = function(v) {\r
11536                                                 if (s.cmd)\r
11537                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
11538                                         };\r
11539                                 }\r
11540                         });\r
11541 \r
11542                         ed.onRemove.add(function() {\r
11543                                 c.destroy();\r
11544                         });\r
11545 \r
11546                         // Fix for bug #1897785, #1898007\r
11547                         if (tinymce.isIE) {\r
11548                                 c.onShowMenu.add(function() {\r
11549                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
11550                                         ed.focus();\r
11551 \r
11552                                         bm = ed.selection.getBookmark(1);\r
11553                                 });\r
11554 \r
11555                                 c.onHideMenu.add(function() {\r
11556                                         if (bm) {\r
11557                                                 ed.selection.moveToBookmark(bm);\r
11558                                                 bm = 0;\r
11559                                         }\r
11560                                 });\r
11561                         }\r
11562 \r
11563                         return t.add(c);\r
11564                 },\r
11565 \r
11566                 createListBox : function(id, s, cc) {\r
11567                         var t = this, ed = t.editor, cmd, c, cls;\r
11568 \r
11569                         if (t.get(id))\r
11570                                 return null;\r
11571 \r
11572                         s.title = ed.translate(s.title);\r
11573                         s.scope = s.scope || ed;\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                                 scope : s.scope,\r
11585                                 control_manager : t\r
11586                         }, s);\r
11587 \r
11588                         id = t.prefix + id;\r
11589 \r
11590                         if (ed.settings.use_native_selects)\r
11591                                 c = new tinymce.ui.NativeListBox(id, s);\r
11592                         else {\r
11593                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
11594                                 c = new cls(id, s);\r
11595                         }\r
11596 \r
11597                         t.controls[id] = c;\r
11598 \r
11599                         // Fix focus problem in Safari\r
11600                         if (tinymce.isWebKit) {\r
11601                                 c.onPostRender.add(function(c, n) {\r
11602                                         // Store bookmark on mousedown\r
11603                                         Event.add(n, 'mousedown', function() {\r
11604                                                 ed.bookmark = ed.selection.getBookmark(1);\r
11605                                         });\r
11606 \r
11607                                         // Restore on focus, since it might be lost\r
11608                                         Event.add(n, 'focus', function() {\r
11609                                                 ed.selection.moveToBookmark(ed.bookmark);\r
11610                                                 ed.bookmark = null;\r
11611                                         });\r
11612                                 });\r
11613                         }\r
11614 \r
11615                         if (c.hideMenu)\r
11616                                 ed.onMouseDown.add(c.hideMenu, c);\r
11617 \r
11618                         return t.add(c);\r
11619                 },\r
11620 \r
11621                 createButton : function(id, s, cc) {\r
11622                         var t = this, ed = t.editor, o, c, cls;\r
11623 \r
11624                         if (t.get(id))\r
11625                                 return null;\r
11626 \r
11627                         s.title = ed.translate(s.title);\r
11628                         s.label = ed.translate(s.label);\r
11629                         s.scope = s.scope || ed;\r
11630 \r
11631                         if (!s.onclick && !s.menu_button) {\r
11632                                 s.onclick = function() {\r
11633                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
11634                                 };\r
11635                         }\r
11636 \r
11637                         s = extend({\r
11638                                 title : s.title,\r
11639                                 'class' : 'mce_' + id,\r
11640                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
11641                                 scope : s.scope,\r
11642                                 control_manager : t\r
11643                         }, s);\r
11644 \r
11645                         id = t.prefix + id;\r
11646 \r
11647                         if (s.menu_button) {\r
11648                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
11649                                 c = new cls(id, s);\r
11650                                 ed.onMouseDown.add(c.hideMenu, c);\r
11651                         } else {\r
11652                                 cls = t._cls.button || tinymce.ui.Button;\r
11653                                 c = new cls(id, s);\r
11654                         }\r
11655 \r
11656                         return t.add(c);\r
11657                 },\r
11658 \r
11659                 createMenuButton : function(id, s, cc) {\r
11660                         s = s || {};\r
11661                         s.menu_button = 1;\r
11662 \r
11663                         return this.createButton(id, s, cc);\r
11664                 },\r
11665 \r
11666                 createSplitButton : function(id, s, cc) {\r
11667                         var t = this, ed = t.editor, cmd, c, cls;\r
11668 \r
11669                         if (t.get(id))\r
11670                                 return null;\r
11671 \r
11672                         s.title = ed.translate(s.title);\r
11673                         s.scope = s.scope || ed;\r
11674 \r
11675                         if (!s.onclick) {\r
11676                                 s.onclick = function(v) {\r
11677                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11678                                 };\r
11679                         }\r
11680 \r
11681                         if (!s.onselect) {\r
11682                                 s.onselect = function(v) {\r
11683                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11684                                 };\r
11685                         }\r
11686 \r
11687                         s = extend({\r
11688                                 title : s.title,\r
11689                                 'class' : 'mce_' + id,\r
11690                                 scope : s.scope,\r
11691                                 control_manager : t\r
11692                         }, s);\r
11693 \r
11694                         id = t.prefix + id;\r
11695                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
11696                         c = t.add(new cls(id, s));\r
11697                         ed.onMouseDown.add(c.hideMenu, c);\r
11698 \r
11699                         return c;\r
11700                 },\r
11701 \r
11702                 createColorSplitButton : function(id, s, cc) {\r
11703                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
11704 \r
11705                         if (t.get(id))\r
11706                                 return null;\r
11707 \r
11708                         s.title = ed.translate(s.title);\r
11709                         s.scope = s.scope || ed;\r
11710 \r
11711                         if (!s.onclick) {\r
11712                                 s.onclick = function(v) {\r
11713                                         if (tinymce.isIE)\r
11714                                                 bm = ed.selection.getBookmark(1);\r
11715 \r
11716                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11717                                 };\r
11718                         }\r
11719 \r
11720                         if (!s.onselect) {\r
11721                                 s.onselect = function(v) {\r
11722                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
11723                                 };\r
11724                         }\r
11725 \r
11726                         s = extend({\r
11727                                 title : s.title,\r
11728                                 'class' : 'mce_' + id,\r
11729                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
11730                                 scope : s.scope,\r
11731                                 more_colors_title : ed.getLang('more_colors')\r
11732                         }, s);\r
11733 \r
11734                         id = t.prefix + id;\r
11735                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
11736                         c = new cls(id, s);\r
11737                         ed.onMouseDown.add(c.hideMenu, c);\r
11738 \r
11739                         // Remove the menu element when the editor is removed\r
11740                         ed.onRemove.add(function() {\r
11741                                 c.destroy();\r
11742                         });\r
11743 \r
11744                         // Fix for bug #1897785, #1898007\r
11745                         if (tinymce.isIE) {\r
11746                                 c.onShowMenu.add(function() {\r
11747                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
11748                                         ed.focus();\r
11749                                         bm = ed.selection.getBookmark(1);\r
11750                                 });\r
11751 \r
11752                                 c.onHideMenu.add(function() {\r
11753                                         if (bm) {\r
11754                                                 ed.selection.moveToBookmark(bm);\r
11755                                                 bm = 0;\r
11756                                         }\r
11757                                 });\r
11758                         }\r
11759 \r
11760                         return t.add(c);\r
11761                 },\r
11762 \r
11763                 createToolbar : function(id, s, cc) {\r
11764                         var c, t = this, cls;\r
11765 \r
11766                         id = t.prefix + id;\r
11767                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
11768                         c = new cls(id, s);\r
11769 \r
11770                         if (t.get(id))\r
11771                                 return null;\r
11772 \r
11773                         return t.add(c);\r
11774                 },\r
11775 \r
11776                 createSeparator : function(cc) {\r
11777                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
11778 \r
11779                         return new cls();\r
11780                 },\r
11781 \r
11782                 setControlType : function(n, c) {\r
11783                         return this._cls[n.toLowerCase()] = c;\r
11784                 },\r
11785         \r
11786                 destroy : function() {\r
11787                         each(this.controls, function(c) {\r
11788                                 c.destroy();\r
11789                         });\r
11790 \r
11791                         this.controls = null;\r
11792                 }\r
11793         });\r
11794 })(tinymce);\r
11795 \r
11796 (function(tinymce) {\r
11797         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
11798 \r
11799         tinymce.create('tinymce.WindowManager', {\r
11800                 WindowManager : function(ed) {\r
11801                         var t = this;\r
11802 \r
11803                         t.editor = ed;\r
11804                         t.onOpen = new Dispatcher(t);\r
11805                         t.onClose = new Dispatcher(t);\r
11806                         t.params = {};\r
11807                         t.features = {};\r
11808                 },\r
11809 \r
11810                 open : function(s, p) {\r
11811                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
11812 \r
11813                         // Default some options\r
11814                         s = s || {};\r
11815                         p = p || {};\r
11816                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
11817                         sh = isOpera ? vp.h : screen.height;\r
11818                         s.name = s.name || 'mc_' + new Date().getTime();\r
11819                         s.width = parseInt(s.width || 320);\r
11820                         s.height = parseInt(s.height || 240);\r
11821                         s.resizable = true;\r
11822                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
11823                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
11824                         p.inline = false;\r
11825                         p.mce_width = s.width;\r
11826                         p.mce_height = s.height;\r
11827                         p.mce_auto_focus = s.auto_focus;\r
11828 \r
11829                         if (mo) {\r
11830                                 if (isIE) {\r
11831                                         s.center = true;\r
11832                                         s.help = false;\r
11833                                         s.dialogWidth = s.width + 'px';\r
11834                                         s.dialogHeight = s.height + 'px';\r
11835                                         s.scroll = s.scrollbars || false;\r
11836                                 }\r
11837                         }\r
11838 \r
11839                         // Build features string\r
11840                         each(s, function(v, k) {\r
11841                                 if (tinymce.is(v, 'boolean'))\r
11842                                         v = v ? 'yes' : 'no';\r
11843 \r
11844                                 if (!/^(name|url)$/.test(k)) {\r
11845                                         if (isIE && mo)\r
11846                                                 f += (f ? ';' : '') + k + ':' + v;\r
11847                                         else\r
11848                                                 f += (f ? ',' : '') + k + '=' + v;\r
11849                                 }\r
11850                         });\r
11851 \r
11852                         t.features = s;\r
11853                         t.params = p;\r
11854                         t.onOpen.dispatch(t, s, p);\r
11855 \r
11856                         u = s.url || s.file;\r
11857                         u = tinymce._addVer(u);\r
11858 \r
11859                         try {\r
11860                                 if (isIE && mo) {\r
11861                                         w = 1;\r
11862                                         window.showModalDialog(u, window, f);\r
11863                                 } else\r
11864                                         w = window.open(u, s.name, f);\r
11865                         } catch (ex) {\r
11866                                 // Ignore\r
11867                         }\r
11868 \r
11869                         if (!w)\r
11870                                 alert(t.editor.getLang('popup_blocked'));\r
11871                 },\r
11872 \r
11873                 close : function(w) {\r
11874                         w.close();\r
11875                         this.onClose.dispatch(this);\r
11876                 },\r
11877 \r
11878                 createInstance : function(cl, a, b, c, d, e) {\r
11879                         var f = tinymce.resolve(cl);\r
11880 \r
11881                         return new f(a, b, c, d, e);\r
11882                 },\r
11883 \r
11884                 confirm : function(t, cb, s, w) {\r
11885                         w = w || window;\r
11886 \r
11887                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
11888                 },\r
11889 \r
11890                 alert : function(tx, cb, s, w) {\r
11891                         var t = this;\r
11892 \r
11893                         w = w || window;\r
11894                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
11895 \r
11896                         if (cb)\r
11897                                 cb.call(s || t);\r
11898                 },\r
11899 \r
11900                 resizeBy : function(dw, dh, win) {\r
11901                         win.resizeBy(dw, dh);\r
11902                 },\r
11903 \r
11904                 // Internal functions\r
11905 \r
11906                 _decode : function(s) {\r
11907                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
11908                 }\r
11909         });\r
11910 }(tinymce));\r
11911 (function(tinymce) {\r
11912         function CommandManager() {\r
11913                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
11914 \r
11915                 function add(collection, cmd, func, scope) {\r
11916                         if (typeof(cmd) == 'string')\r
11917                                 cmd = [cmd];\r
11918 \r
11919                         tinymce.each(cmd, function(cmd) {\r
11920                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
11921                         });\r
11922                 };\r
11923 \r
11924                 tinymce.extend(this, {\r
11925                         add : function(cmd, func, scope) {\r
11926                                 add(execCommands, cmd, func, scope);\r
11927                         },\r
11928 \r
11929                         addQueryStateHandler : function(cmd, func, scope) {\r
11930                                 add(queryStateCommands, cmd, func, scope);\r
11931                         },\r
11932 \r
11933                         addQueryValueHandler : function(cmd, func, scope) {\r
11934                                 add(queryValueCommands, cmd, func, scope);\r
11935                         },\r
11936 \r
11937                         execCommand : function(scope, cmd, ui, value, args) {\r
11938                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
11939                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
11940                                                 return true;\r
11941                                 }\r
11942                         },\r
11943 \r
11944                         queryCommandValue : function() {\r
11945                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
11946                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
11947                         },\r
11948 \r
11949                         queryCommandState : function() {\r
11950                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
11951                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
11952                         }\r
11953                 });\r
11954         };\r
11955 \r
11956         tinymce.GlobalCommands = new CommandManager();\r
11957 })(tinymce);\r
11958 (function(tinymce) {\r
11959         tinymce.Formatter = function(ed) {\r
11960                 var formats = {},\r
11961                         each = tinymce.each,\r
11962                         dom = ed.dom,\r
11963                         selection = ed.selection,\r
11964                         TreeWalker = tinymce.dom.TreeWalker,\r
11965                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
11966                         isValid = ed.schema.isValid,\r
11967                         isBlock = dom.isBlock,\r
11968                         forcedRootBlock = ed.settings.forced_root_block,\r
11969                         nodeIndex = dom.nodeIndex,\r
11970                         INVISIBLE_CHAR = '\uFEFF',\r
11971                         MCE_ATTR_RE = /^(src|href|style)$/,\r
11972                         FALSE = false,\r
11973                         TRUE = true,\r
11974                         undefined,\r
11975                         pendingFormats = {apply : [], remove : []};\r
11976 \r
11977                 function isArray(obj) {\r
11978                         return obj instanceof Array;\r
11979                 };\r
11980 \r
11981                 function getParents(node, selector) {\r
11982                         return dom.getParents(node, selector, dom.getRoot());\r
11983                 };\r
11984 \r
11985                 function isCaretNode(node) {\r
11986                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
11987                 };\r
11988 \r
11989                 // Public functions\r
11990 \r
11991                 function get(name) {\r
11992                         return name ? formats[name] : formats;\r
11993                 };\r
11994 \r
11995                 function register(name, format) {\r
11996                         if (name) {\r
11997                                 if (typeof(name) !== 'string') {\r
11998                                         each(name, function(format, name) {\r
11999                                                 register(name, format);\r
12000                                         });\r
12001                                 } else {\r
12002                                         // Force format into array and add it to internal collection\r
12003                                         format = format.length ? format : [format];\r
12004 \r
12005                                         each(format, function(format) {\r
12006                                                 // Set deep to false by default on selector formats this to avoid removing\r
12007                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
12008                                                 if (format.deep === undefined)\r
12009                                                         format.deep = !format.selector;\r
12010 \r
12011                                                 // Default to true\r
12012                                                 if (format.split === undefined)\r
12013                                                         format.split = !format.selector || format.inline;\r
12014 \r
12015                                                 // Default to true\r
12016                                                 if (format.remove === undefined && format.selector && !format.inline)\r
12017                                                         format.remove = 'none';\r
12018 \r
12019                                                 // Mark format as a mixed format inline + block level\r
12020                                                 if (format.selector && format.inline) {\r
12021                                                         format.mixed = true;\r
12022                                                         format.block_expand = true;\r
12023                                                 }\r
12024 \r
12025                                                 // Split classes if needed\r
12026                                                 if (typeof(format.classes) === 'string')\r
12027                                                         format.classes = format.classes.split(/\s+/);\r
12028                                         });\r
12029 \r
12030                                         formats[name] = format;\r
12031                                 }\r
12032                         }\r
12033                 };\r
12034 \r
12035                 function apply(name, vars, node) {\r
12036                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
12037 \r
12038                         function moveStart(rng) {\r
12039                                 var container = rng.startContainer,\r
12040                                         offset = rng.startOffset,\r
12041                                         walker, node;\r
12042 \r
12043                                 // Move startContainer/startOffset in to a suitable node\r
12044                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
12045                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
12046 \r
12047                                         // Might fail if the offset is behind the last element in it's container\r
12048                                         if (container) {\r
12049                                                 walker = new TreeWalker(container, container.parentNode);\r
12050                                                 for (node = walker.current(); node; node = walker.next()) {\r
12051                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
12052                                                                 rng.setStart(node, 0);\r
12053                                                                 break;\r
12054                                                         }\r
12055                                                 }\r
12056                                         }\r
12057                                 }\r
12058 \r
12059                                 return rng;\r
12060                         };\r
12061 \r
12062                         function setElementFormat(elm, fmt) {\r
12063                                 fmt = fmt || format;\r
12064 \r
12065                                 if (elm) {\r
12066                                         each(fmt.styles, function(value, name) {\r
12067                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
12068                                         });\r
12069 \r
12070                                         each(fmt.attributes, function(value, name) {\r
12071                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
12072                                         });\r
12073 \r
12074                                         each(fmt.classes, function(value) {\r
12075                                                 value = replaceVars(value, vars);\r
12076 \r
12077                                                 if (!dom.hasClass(elm, value))\r
12078                                                         dom.addClass(elm, value);\r
12079                                         });\r
12080                                 }\r
12081                         };\r
12082 \r
12083                         function applyRngStyle(rng) {\r
12084                                 var newWrappers = [], wrapName, wrapElm;\r
12085 \r
12086                                 // Setup wrapper element\r
12087                                 wrapName = format.inline || format.block;\r
12088                                 wrapElm = dom.create(wrapName);\r
12089                                 setElementFormat(wrapElm);\r
12090 \r
12091                                 rangeUtils.walk(rng, function(nodes) {\r
12092                                         var currentWrapElm;\r
12093 \r
12094                                         function process(node) {\r
12095                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
12096 \r
12097                                                 // Stop wrapping on br elements\r
12098                                                 if (isEq(nodeName, 'br')) {\r
12099                                                         currentWrapElm = 0;\r
12100 \r
12101                                                         // Remove any br elements when we wrap things\r
12102                                                         if (format.block)\r
12103                                                                 dom.remove(node);\r
12104 \r
12105                                                         return;\r
12106                                                 }\r
12107 \r
12108                                                 // If node is wrapper type\r
12109                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
12110                                                         currentWrapElm = 0;\r
12111                                                         return;\r
12112                                                 }\r
12113 \r
12114                                                 // Can we rename the block\r
12115                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
12116                                                         node = dom.rename(node, wrapName);\r
12117                                                         setElementFormat(node);\r
12118                                                         newWrappers.push(node);\r
12119                                                         currentWrapElm = 0;\r
12120                                                         return;\r
12121                                                 }\r
12122 \r
12123                                                 // Handle selector patterns\r
12124                                                 if (format.selector) {\r
12125                                                         // Look for matching formats\r
12126                                                         each(formatList, function(format) {\r
12127                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
12128                                                                         setElementFormat(node, format);\r
12129                                                                         found = true;\r
12130                                                                 }\r
12131                                                         });\r
12132 \r
12133                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
12134                                                         if (!format.inline || found) {\r
12135                                                                 currentWrapElm = 0;\r
12136                                                                 return;\r
12137                                                         }\r
12138                                                 }\r
12139 \r
12140                                                 // Is it valid to wrap this item\r
12141                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
12142                                                         // Start wrapping\r
12143                                                         if (!currentWrapElm) {\r
12144                                                                 // Wrap the node\r
12145                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
12146                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
12147                                                                 newWrappers.push(currentWrapElm);\r
12148                                                         }\r
12149 \r
12150                                                         currentWrapElm.appendChild(node);\r
12151                                                 } else {\r
12152                                                         // Start a new wrapper for possible children\r
12153                                                         currentWrapElm = 0;\r
12154 \r
12155                                                         each(tinymce.grep(node.childNodes), process);\r
12156 \r
12157                                                         // End the last wrapper\r
12158                                                         currentWrapElm = 0;\r
12159                                                 }\r
12160                                         };\r
12161 \r
12162                                         // Process siblings from range\r
12163                                         each(nodes, process);\r
12164                                 });\r
12165 \r
12166                                 // Cleanup\r
12167                                 each(newWrappers, function(node) {\r
12168                                         var childCount;\r
12169 \r
12170                                         function getChildCount(node) {\r
12171                                                 var count = 0;\r
12172 \r
12173                                                 each(node.childNodes, function(node) {\r
12174                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
12175                                                                 count++;\r
12176                                                 });\r
12177 \r
12178                                                 return count;\r
12179                                         };\r
12180 \r
12181                                         function mergeStyles(node) {\r
12182                                                 var child, clone;\r
12183 \r
12184                                                 each(node.childNodes, function(node) {\r
12185                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
12186                                                                 child = node;\r
12187                                                                 return FALSE; // break loop\r
12188                                                         }\r
12189                                                 });\r
12190 \r
12191                                                 // If child was found and of the same type as the current node\r
12192                                                 if (child && matchName(child, format)) {\r
12193                                                         clone = child.cloneNode(FALSE);\r
12194                                                         setElementFormat(clone);\r
12195 \r
12196                                                         dom.replace(clone, node, TRUE);\r
12197                                                         dom.remove(child, 1);\r
12198                                                 }\r
12199 \r
12200                                                 return clone || node;\r
12201                                         };\r
12202 \r
12203                                         childCount = getChildCount(node);\r
12204 \r
12205                                         // Remove empty nodes\r
12206                                         if (childCount === 0) {\r
12207                                                 dom.remove(node, 1);\r
12208                                                 return;\r
12209                                         }\r
12210 \r
12211                                         if (format.inline || format.wrapper) {\r
12212                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
12213                                                 if (!format.exact && childCount === 1)\r
12214                                                         node = mergeStyles(node);\r
12215 \r
12216                                                 // Remove/merge children\r
12217                                                 each(formatList, function(format) {\r
12218                                                         // Merge all children of similar type will move styles from child to parent\r
12219                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
12220                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
12221                                                         each(dom.select(format.inline, node), function(child) {\r
12222                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
12223                                                         });\r
12224                                                 });\r
12225 \r
12226                                                 // Remove child if direct parent is of same type\r
12227                                                 if (matchNode(node.parentNode, name, vars)) {\r
12228                                                         dom.remove(node, 1);\r
12229                                                         node = 0;\r
12230                                                         return TRUE;\r
12231                                                 }\r
12232 \r
12233                                                 // Look for parent with similar style format\r
12234                                                 if (format.merge_with_parents) {\r
12235                                                         dom.getParent(node.parentNode, function(parent) {\r
12236                                                                 if (matchNode(parent, name, vars)) {\r
12237                                                                         dom.remove(node, 1);\r
12238                                                                         node = 0;\r
12239                                                                         return TRUE;\r
12240                                                                 }\r
12241                                                         });\r
12242                                                 }\r
12243 \r
12244                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
12245                                                 if (node) {\r
12246                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
12247                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
12248                                                 }\r
12249                                         }\r
12250                                 });\r
12251                         };\r
12252 \r
12253                         if (format) {\r
12254                                 if (node) {\r
12255                                         rng = dom.createRng();\r
12256 \r
12257                                         rng.setStartBefore(node);\r
12258                                         rng.setEndAfter(node);\r
12259 \r
12260                                         applyRngStyle(expandRng(rng, formatList));\r
12261                                 } else {\r
12262                                         if (!selection.isCollapsed() || !format.inline) {\r
12263                                                 // Apply formatting to selection\r
12264                                                 bookmark = selection.getBookmark();\r
12265                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
12266 \r
12267                                                 selection.moveToBookmark(bookmark);\r
12268                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
12269                                                 ed.nodeChanged();\r
12270                                         } else\r
12271                                                 performCaretAction('apply', name, vars);\r
12272                                 }\r
12273                         }\r
12274                 };\r
12275 \r
12276                 function remove(name, vars, node) {\r
12277                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
12278 \r
12279                         function moveStart(rng) {\r
12280                                 var container = rng.startContainer,\r
12281                                         offset = rng.startOffset,\r
12282                                         walker, node, nodes, tmpNode;\r
12283 \r
12284                                 // Convert text node into index if possible\r
12285                                 if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
12286                                         container = container.parentNode;\r
12287                                         offset = nodeIndex(container) + 1;\r
12288                                 }\r
12289 \r
12290                                 // Move startContainer/startOffset in to a suitable node\r
12291                                 if (container.nodeType == 1) {\r
12292                                         nodes = container.childNodes;\r
12293                                         container = nodes[Math.min(offset, nodes.length - 1)];\r
12294                                         walker = new TreeWalker(container);\r
12295 \r
12296                                         // If offset is at end of the parent node walk to the next one\r
12297                                         if (offset > nodes.length - 1)\r
12298                                                 walker.next();\r
12299 \r
12300                                         for (node = walker.current(); node; node = walker.next()) {\r
12301                                                 if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
12302                                                         // IE has a "neat" feature where it moves the start node into the closest element\r
12303                                                         // we can avoid this by inserting an element before it and then remove it after we set the selection\r
12304                                                         tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
12305                                                         node.parentNode.insertBefore(tmpNode, node);\r
12306 \r
12307                                                         // Set selection and remove tmpNode\r
12308                                                         rng.setStart(node, 0);\r
12309                                                         selection.setRng(rng);\r
12310                                                         dom.remove(tmpNode);\r
12311 \r
12312                                                         return;\r
12313                                                 }\r
12314                                         }\r
12315                                 }\r
12316                         };\r
12317 \r
12318                         // Merges the styles for each node\r
12319                         function process(node) {\r
12320                                 var children, i, l;\r
12321 \r
12322                                 // Grab the children first since the nodelist might be changed\r
12323                                 children = tinymce.grep(node.childNodes);\r
12324 \r
12325                                 // Process current node\r
12326                                 for (i = 0, l = formatList.length; i < l; i++) {\r
12327                                         if (removeFormat(formatList[i], vars, node, node))\r
12328                                                 break;\r
12329                                 }\r
12330 \r
12331                                 // Process the children\r
12332                                 if (format.deep) {\r
12333                                         for (i = 0, l = children.length; i < l; i++)\r
12334                                                 process(children[i]);\r
12335                                 }\r
12336                         };\r
12337 \r
12338                         function findFormatRoot(container) {\r
12339                                 var formatRoot;\r
12340 \r
12341                                 // Find format root\r
12342                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
12343                                         var format;\r
12344 \r
12345                                         // Find format root element\r
12346                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
12347                                                 // Is the node matching the format we are looking for\r
12348                                                 format = matchNode(parent, name, vars);\r
12349                                                 if (format && format.split !== false)\r
12350                                                         formatRoot = parent;\r
12351                                         }\r
12352                                 });\r
12353 \r
12354                                 return formatRoot;\r
12355                         };\r
12356 \r
12357                         function wrapAndSplit(format_root, container, target, split) {\r
12358                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
12359 \r
12360                                 // Format root found then clone formats and split it\r
12361                                 if (format_root) {\r
12362                                         formatRootParent = format_root.parentNode;\r
12363 \r
12364                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
12365                                                 clone = parent.cloneNode(FALSE);\r
12366 \r
12367                                                 for (i = 0; i < formatList.length; i++) {\r
12368                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
12369                                                                 clone = 0;\r
12370                                                                 break;\r
12371                                                         }\r
12372                                                 }\r
12373 \r
12374                                                 // Build wrapper node\r
12375                                                 if (clone) {\r
12376                                                         if (lastClone)\r
12377                                                                 clone.appendChild(lastClone);\r
12378 \r
12379                                                         if (!firstClone)\r
12380                                                                 firstClone = clone;\r
12381 \r
12382                                                         lastClone = clone;\r
12383                                                 }\r
12384                                         }\r
12385 \r
12386                                         // Never split block elements if the format is mixed\r
12387                                         if (split && (!format.mixed || !isBlock(format_root)))\r
12388                                                 container = dom.split(format_root, container);\r
12389 \r
12390                                         // Wrap container in cloned formats\r
12391                                         if (lastClone) {\r
12392                                                 target.parentNode.insertBefore(lastClone, target);\r
12393                                                 firstClone.appendChild(target);\r
12394                                         }\r
12395                                 }\r
12396 \r
12397                                 return container;\r
12398                         };\r
12399 \r
12400                         function splitToFormatRoot(container) {\r
12401                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
12402                         };\r
12403 \r
12404                         function unwrap(start) {\r
12405                                 var node = dom.get(start ? '_start' : '_end'),\r
12406                                         out = node[start ? 'firstChild' : 'lastChild'];\r
12407 \r
12408                                 // If the end is placed within the start the result will be removed\r
12409                                 // So this checks if the out node is a bookmark node if it is it\r
12410                                 // checks for another more suitable node\r
12411                                 if (isBookmarkNode(out))\r
12412                                         out = out[start ? 'firstChild' : 'lastChild'];\r
12413 \r
12414                                 dom.remove(node, true);\r
12415 \r
12416                                 return out;\r
12417                         };\r
12418 \r
12419                         function removeRngStyle(rng) {\r
12420                                 var startContainer, endContainer;\r
12421 \r
12422                                 rng = expandRng(rng, formatList, TRUE);\r
12423 \r
12424                                 if (format.split) {\r
12425                                         startContainer = getContainer(rng, TRUE);\r
12426                                         endContainer = getContainer(rng);\r
12427 \r
12428                                         if (startContainer != endContainer) {\r
12429                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
12430                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
12431                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
12432 \r
12433                                                 // Split start/end\r
12434                                                 splitToFormatRoot(startContainer);\r
12435                                                 splitToFormatRoot(endContainer);\r
12436 \r
12437                                                 // Unwrap start/end to get real elements again\r
12438                                                 startContainer = unwrap(TRUE);\r
12439                                                 endContainer = unwrap();\r
12440                                         } else\r
12441                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
12442 \r
12443                                         // Update range positions since they might have changed after the split operations\r
12444                                         rng.startContainer = startContainer.parentNode;\r
12445                                         rng.startOffset = nodeIndex(startContainer);\r
12446                                         rng.endContainer = endContainer.parentNode;\r
12447                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
12448                                 }\r
12449 \r
12450                                 // Remove items between start/end\r
12451                                 rangeUtils.walk(rng, function(nodes) {\r
12452                                         each(nodes, function(node) {\r
12453                                                 process(node);\r
12454                                         });\r
12455                                 });\r
12456                         };\r
12457 \r
12458                         // Handle node\r
12459                         if (node) {\r
12460                                 rng = dom.createRng();\r
12461                                 rng.setStartBefore(node);\r
12462                                 rng.setEndAfter(node);\r
12463                                 removeRngStyle(rng);\r
12464                                 return;\r
12465                         }\r
12466 \r
12467                         if (!selection.isCollapsed() || !format.inline) {\r
12468                                 bookmark = selection.getBookmark();\r
12469                                 removeRngStyle(selection.getRng(TRUE));\r
12470                                 selection.moveToBookmark(bookmark);\r
12471 \r
12472                                 // Check if start element still has formatting then we are at: "<b>text|</b>text" and need to move the start into the next text node\r
12473                                 if (match(name, vars, selection.getStart())) {\r
12474                                         moveStart(selection.getRng(true));\r
12475                                 }\r
12476 \r
12477                                 ed.nodeChanged();\r
12478                         } else\r
12479                                 performCaretAction('remove', name, vars);\r
12480                 };\r
12481 \r
12482                 function toggle(name, vars, node) {\r
12483                         if (match(name, vars, node))\r
12484                                 remove(name, vars, node);\r
12485                         else\r
12486                                 apply(name, vars, node);\r
12487                 };\r
12488 \r
12489                 function matchNode(node, name, vars, similar) {\r
12490                         var formatList = get(name), format, i, classes;\r
12491 \r
12492                         function matchItems(node, format, item_name) {\r
12493                                 var key, value, items = format[item_name], i;\r
12494 \r
12495                                 // Check all items\r
12496                                 if (items) {\r
12497                                         // Non indexed object\r
12498                                         if (items.length === undefined) {\r
12499                                                 for (key in items) {\r
12500                                                         if (items.hasOwnProperty(key)) {\r
12501                                                                 if (item_name === 'attributes')\r
12502                                                                         value = dom.getAttrib(node, key);\r
12503                                                                 else\r
12504                                                                         value = getStyle(node, key);\r
12505 \r
12506                                                                 if (similar && !value && !format.exact)\r
12507                                                                         return;\r
12508 \r
12509                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
12510                                                                         return;\r
12511                                                         }\r
12512                                                 }\r
12513                                         } else {\r
12514                                                 // Only one match needed for indexed arrays\r
12515                                                 for (i = 0; i < items.length; i++) {\r
12516                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
12517                                                                 return format;\r
12518                                                 }\r
12519                                         }\r
12520                                 }\r
12521 \r
12522                                 return format;\r
12523                         };\r
12524 \r
12525                         if (formatList && node) {\r
12526                                 // Check each format in list\r
12527                                 for (i = 0; i < formatList.length; i++) {\r
12528                                         format = formatList[i];\r
12529 \r
12530                                         // Name name, attributes, styles and classes\r
12531                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
12532                                                 // Match classes\r
12533                                                 if (classes = format.classes) {\r
12534                                                         for (i = 0; i < classes.length; i++) {\r
12535                                                                 if (!dom.hasClass(node, classes[i]))\r
12536                                                                         return;\r
12537                                                         }\r
12538                                                 }\r
12539 \r
12540                                                 return format;\r
12541                                         }\r
12542                                 }\r
12543                         }\r
12544                 };\r
12545 \r
12546                 function match(name, vars, node) {\r
12547                         var startNode, i;\r
12548 \r
12549                         function matchParents(node) {\r
12550                                 // Find first node with similar format settings\r
12551                                 node = dom.getParent(node, function(node) {\r
12552                                         return !!matchNode(node, name, vars, true);\r
12553                                 });\r
12554 \r
12555                                 // Do an exact check on the similar format element\r
12556                                 return matchNode(node, name, vars);\r
12557                         };\r
12558 \r
12559                         // Check specified node\r
12560                         if (node)\r
12561                                 return matchParents(node);\r
12562 \r
12563                         // Check pending formats\r
12564                         if (selection.isCollapsed()) {\r
12565                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
12566                                         if (pendingFormats.apply[i].name == name)\r
12567                                                 return true;\r
12568                                 }\r
12569 \r
12570                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
12571                                         if (pendingFormats.remove[i].name == name)\r
12572                                                 return false;\r
12573                                 }\r
12574 \r
12575                                 return matchParents(selection.getNode());\r
12576                         }\r
12577 \r
12578                         // Check selected node\r
12579                         node = selection.getNode();\r
12580                         if (matchParents(node))\r
12581                                 return TRUE;\r
12582 \r
12583                         // Check start node if it's different\r
12584                         startNode = selection.getStart();\r
12585                         if (startNode != node) {\r
12586                                 if (matchParents(startNode))\r
12587                                         return TRUE;\r
12588                         }\r
12589 \r
12590                         return FALSE;\r
12591                 };\r
12592 \r
12593                 function matchAll(names, vars) {\r
12594                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
12595 \r
12596                         // If the selection is collapsed then check pending formats\r
12597                         if (selection.isCollapsed()) {\r
12598                                 for (ni = 0; ni < names.length; ni++) {\r
12599                                         // If the name is to be removed, then stop it from being added\r
12600                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
12601                                                 name = names[ni];\r
12602 \r
12603                                                 if (pendingFormats.remove[i].name == name) {\r
12604                                                         checkedMap[name] = true;\r
12605                                                         break;\r
12606                                                 }\r
12607                                         }\r
12608                                 }\r
12609 \r
12610                                 // If the format is to be applied\r
12611                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
12612                                         for (ni = 0; ni < names.length; ni++) {\r
12613                                                 name = names[ni];\r
12614 \r
12615                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
12616                                                         checkedMap[name] = true;\r
12617                                                         matchedFormatNames.push(name);\r
12618                                                 }\r
12619                                         }\r
12620                                 }\r
12621                         }\r
12622 \r
12623                         // Check start of selection for formats\r
12624                         startElement = selection.getStart();\r
12625                         dom.getParent(startElement, function(node) {\r
12626                                 var i, name;\r
12627 \r
12628                                 for (i = 0; i < names.length; i++) {\r
12629                                         name = names[i];\r
12630 \r
12631                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
12632                                                 checkedMap[name] = true;\r
12633                                                 matchedFormatNames.push(name);\r
12634                                         }\r
12635                                 }\r
12636                         });\r
12637 \r
12638                         return matchedFormatNames;\r
12639                 };\r
12640 \r
12641                 function canApply(name) {\r
12642                         var formatList = get(name), startNode, parents, i, x, selector;\r
12643 \r
12644                         if (formatList) {\r
12645                                 startNode = selection.getStart();\r
12646                                 parents = getParents(startNode);\r
12647 \r
12648                                 for (x = formatList.length - 1; x >= 0; x--) {\r
12649                                         selector = formatList[x].selector;\r
12650 \r
12651                                         // Format is not selector based, then always return TRUE\r
12652                                         if (!selector)\r
12653                                                 return TRUE;\r
12654 \r
12655                                         for (i = parents.length - 1; i >= 0; i--) {\r
12656                                                 if (dom.is(parents[i], selector))\r
12657                                                         return TRUE;\r
12658                                         }\r
12659                                 }\r
12660                         }\r
12661 \r
12662                         return FALSE;\r
12663                 };\r
12664 \r
12665                 // Expose to public\r
12666                 tinymce.extend(this, {\r
12667                         get : get,\r
12668                         register : register,\r
12669                         apply : apply,\r
12670                         remove : remove,\r
12671                         toggle : toggle,\r
12672                         match : match,\r
12673                         matchAll : matchAll,\r
12674                         matchNode : matchNode,\r
12675                         canApply : canApply\r
12676                 });\r
12677 \r
12678                 // Private functions\r
12679 \r
12680                 function matchName(node, format) {\r
12681                         // Check for inline match\r
12682                         if (isEq(node, format.inline))\r
12683                                 return TRUE;\r
12684 \r
12685                         // Check for block match\r
12686                         if (isEq(node, format.block))\r
12687                                 return TRUE;\r
12688 \r
12689                         // Check for selector match\r
12690                         if (format.selector)\r
12691                                 return dom.is(node, format.selector);\r
12692                 };\r
12693 \r
12694                 function isEq(str1, str2) {\r
12695                         str1 = str1 || '';\r
12696                         str2 = str2 || '';\r
12697 \r
12698                         str1 = '' + (str1.nodeName || str1);\r
12699                         str2 = '' + (str2.nodeName || str2);\r
12700 \r
12701                         return str1.toLowerCase() == str2.toLowerCase();\r
12702                 };\r
12703 \r
12704                 function getStyle(node, name) {\r
12705                         var styleVal = dom.getStyle(node, name);\r
12706 \r
12707                         // Force the format to hex\r
12708                         if (name == 'color' || name == 'backgroundColor')\r
12709                                 styleVal = dom.toHex(styleVal);\r
12710 \r
12711                         // Opera will return bold as 700\r
12712                         if (name == 'fontWeight' && styleVal == 700)\r
12713                                 styleVal = 'bold';\r
12714 \r
12715                         return '' + styleVal;\r
12716                 };\r
12717 \r
12718                 function replaceVars(value, vars) {\r
12719                         if (typeof(value) != "string")\r
12720                                 value = value(vars);\r
12721                         else if (vars) {\r
12722                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
12723                                         return vars[name] || str;\r
12724                                 });\r
12725                         }\r
12726 \r
12727                         return value;\r
12728                 };\r
12729 \r
12730                 function isWhiteSpaceNode(node) {\r
12731                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
12732                 };\r
12733 \r
12734                 function wrap(node, name, attrs) {\r
12735                         var wrapper = dom.create(name, attrs);\r
12736 \r
12737                         node.parentNode.insertBefore(wrapper, node);\r
12738                         wrapper.appendChild(node);\r
12739 \r
12740                         return wrapper;\r
12741                 };\r
12742 \r
12743                 function expandRng(rng, format, remove) {\r
12744                         var startContainer = rng.startContainer,\r
12745                                 startOffset = rng.startOffset,\r
12746                                 endContainer = rng.endContainer,\r
12747                                 endOffset = rng.endOffset, sibling, lastIdx;\r
12748 \r
12749                         // This function walks up the tree if there is no siblings before/after the node\r
12750                         function findParentContainer(container, child_name, sibling_name, root) {\r
12751                                 var parent, child;\r
12752 \r
12753                                 root = root || dom.getRoot();\r
12754 \r
12755                                 for (;;) {\r
12756                                         // Check if we can move up are we at root level or body level\r
12757                                         parent = container.parentNode;\r
12758 \r
12759                                         // Stop expanding on block elements or root depending on format\r
12760                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
12761                                                 return container;\r
12762 \r
12763                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
12764                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
12765                                                         return container;\r
12766 \r
12767                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
12768                                                         return container;\r
12769                                         }\r
12770 \r
12771                                         container = container.parentNode;\r
12772                                 }\r
12773 \r
12774                                 return container;\r
12775                         };\r
12776 \r
12777                         // If index based start position then resolve it\r
12778                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
12779                                 lastIdx = startContainer.childNodes.length - 1;\r
12780                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
12781 \r
12782                                 if (startContainer.nodeType == 3)\r
12783                                         startOffset = 0;\r
12784                         }\r
12785 \r
12786                         // If index based end position then resolve it\r
12787                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
12788                                 lastIdx = endContainer.childNodes.length - 1;\r
12789                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
12790 \r
12791                                 if (endContainer.nodeType == 3)\r
12792                                         endOffset = endContainer.nodeValue.length;\r
12793                         }\r
12794 \r
12795                         // Exclude bookmark nodes if possible\r
12796                         if (isBookmarkNode(startContainer.parentNode))\r
12797                                 startContainer = startContainer.parentNode;\r
12798 \r
12799                         if (isBookmarkNode(startContainer))\r
12800                                 startContainer = startContainer.nextSibling || startContainer;\r
12801 \r
12802                         if (isBookmarkNode(endContainer.parentNode))\r
12803                                 endContainer = endContainer.parentNode;\r
12804 \r
12805                         if (isBookmarkNode(endContainer))\r
12806                                 endContainer = endContainer.previousSibling || endContainer;\r
12807 \r
12808                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
12809                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
12810                         // This will reduce the number of wrapper elements that needs to be created\r
12811                         // Move start point up the tree\r
12812                         if (format[0].inline || format[0].block_expand) {\r
12813                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
12814                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
12815                         }\r
12816 \r
12817                         // Expand start/end container to matching selector\r
12818                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
12819                                 function findSelectorEndPoint(container, sibling_name) {\r
12820                                         var parents, i, y;\r
12821 \r
12822                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
12823                                                 container = container[sibling_name];\r
12824 \r
12825                                         parents = getParents(container);\r
12826                                         for (i = 0; i < parents.length; i++) {\r
12827                                                 for (y = 0; y < format.length; y++) {\r
12828                                                         if (dom.is(parents[i], format[y].selector))\r
12829                                                                 return parents[i];\r
12830                                                 }\r
12831                                         }\r
12832 \r
12833                                         return container;\r
12834                                 };\r
12835 \r
12836                                 // Find new startContainer/endContainer if there is better one\r
12837                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
12838                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
12839                         }\r
12840 \r
12841                         // Expand start/end container to matching block element or text node\r
12842                         if (format[0].block || format[0].selector) {\r
12843                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
12844                                         var node;\r
12845 \r
12846                                         // Expand to block of similar type\r
12847                                         if (!format[0].wrapper)\r
12848                                                 node = dom.getParent(container, format[0].block);\r
12849 \r
12850                                         // Expand to first wrappable block element or any block element\r
12851                                         if (!node)\r
12852                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
12853 \r
12854                                         // Exclude inner lists from wrapping\r
12855                                         if (node && format[0].wrapper)\r
12856                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
12857 \r
12858                                         // Didn't find a block element look for first/last wrappable element\r
12859                                         if (!node) {\r
12860                                                 node = container;\r
12861 \r
12862                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
12863                                                         node = node[sibling_name];\r
12864 \r
12865                                                         // Break on BR but include it will be removed later on\r
12866                                                         // we can't remove it now since we need to check if it can be wrapped\r
12867                                                         if (isEq(node, 'br'))\r
12868                                                                 break;\r
12869                                                 }\r
12870                                         }\r
12871 \r
12872                                         return node || container;\r
12873                                 };\r
12874 \r
12875                                 // Find new startContainer/endContainer if there is better one\r
12876                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
12877                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
12878 \r
12879                                 // Non block element then try to expand up the leaf\r
12880                                 if (format[0].block) {\r
12881                                         if (!isBlock(startContainer))\r
12882                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
12883 \r
12884                                         if (!isBlock(endContainer))\r
12885                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
12886                                 }\r
12887                         }\r
12888 \r
12889                         // Setup index for startContainer\r
12890                         if (startContainer.nodeType == 1) {\r
12891                                 startOffset = nodeIndex(startContainer);\r
12892                                 startContainer = startContainer.parentNode;\r
12893                         }\r
12894 \r
12895                         // Setup index for endContainer\r
12896                         if (endContainer.nodeType == 1) {\r
12897                                 endOffset = nodeIndex(endContainer) + 1;\r
12898                                 endContainer = endContainer.parentNode;\r
12899                         }\r
12900 \r
12901                         // Return new range like object\r
12902                         return {\r
12903                                 startContainer : startContainer,\r
12904                                 startOffset : startOffset,\r
12905                                 endContainer : endContainer,\r
12906                                 endOffset : endOffset\r
12907                         };\r
12908                 }\r
12909 \r
12910                 function removeFormat(format, vars, node, compare_node) {\r
12911                         var i, attrs, stylesModified;\r
12912 \r
12913                         // Check if node matches format\r
12914                         if (!matchName(node, format))\r
12915                                 return FALSE;\r
12916 \r
12917                         // Should we compare with format attribs and styles\r
12918                         if (format.remove != 'all') {\r
12919                                 // Remove styles\r
12920                                 each(format.styles, function(value, name) {\r
12921                                         value = replaceVars(value, vars);\r
12922 \r
12923                                         // Indexed array\r
12924                                         if (typeof(name) === 'number') {\r
12925                                                 name = value;\r
12926                                                 compare_node = 0;\r
12927                                         }\r
12928 \r
12929                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
12930                                                 dom.setStyle(node, name, '');\r
12931 \r
12932                                         stylesModified = 1;\r
12933                                 });\r
12934 \r
12935                                 // Remove style attribute if it's empty\r
12936                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
12937                                         node.removeAttribute('style');\r
12938                                         node.removeAttribute('_mce_style');\r
12939                                 }\r
12940 \r
12941                                 // Remove attributes\r
12942                                 each(format.attributes, function(value, name) {\r
12943                                         var valueOut;\r
12944 \r
12945                                         value = replaceVars(value, vars);\r
12946 \r
12947                                         // Indexed array\r
12948                                         if (typeof(name) === 'number') {\r
12949                                                 name = value;\r
12950                                                 compare_node = 0;\r
12951                                         }\r
12952 \r
12953                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
12954                                                 // Keep internal classes\r
12955                                                 if (name == 'class') {\r
12956                                                         value = dom.getAttrib(node, name);\r
12957                                                         if (value) {\r
12958                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
12959                                                                 valueOut = '';\r
12960                                                                 each(value.split(/\s+/), function(cls) {\r
12961                                                                         if (/mce\w+/.test(cls))\r
12962                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
12963                                                                 });\r
12964 \r
12965                                                                 // We got some internal classes left\r
12966                                                                 if (valueOut) {\r
12967                                                                         dom.setAttrib(node, name, valueOut);\r
12968                                                                         return;\r
12969                                                                 }\r
12970                                                         }\r
12971                                                 }\r
12972 \r
12973                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
12974                                                 if (name == "class")\r
12975                                                         node.removeAttribute('className');\r
12976 \r
12977                                                 // Remove mce prefixed attributes\r
12978                                                 if (MCE_ATTR_RE.test(name))\r
12979                                                         node.removeAttribute('_mce_' + name);\r
12980 \r
12981                                                 node.removeAttribute(name);\r
12982                                         }\r
12983                                 });\r
12984 \r
12985                                 // Remove classes\r
12986                                 each(format.classes, function(value) {\r
12987                                         value = replaceVars(value, vars);\r
12988 \r
12989                                         if (!compare_node || dom.hasClass(compare_node, value))\r
12990                                                 dom.removeClass(node, value);\r
12991                                 });\r
12992 \r
12993                                 // Check for non internal attributes\r
12994                                 attrs = dom.getAttribs(node);\r
12995                                 for (i = 0; i < attrs.length; i++) {\r
12996                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
12997                                                 return FALSE;\r
12998                                 }\r
12999                         }\r
13000 \r
13001                         // Remove the inline child if it's empty for example <b> or <span>\r
13002                         if (format.remove != 'none') {\r
13003                                 removeNode(node, format);\r
13004                                 return TRUE;\r
13005                         }\r
13006                 };\r
13007 \r
13008                 function removeNode(node, format) {\r
13009                         var parentNode = node.parentNode, rootBlockElm;\r
13010 \r
13011                         if (format.block) {\r
13012                                 if (!forcedRootBlock) {\r
13013                                         function find(node, next, inc) {\r
13014                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
13015 \r
13016                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
13017                                         };\r
13018 \r
13019                                         // Append BR elements if needed before we remove the block\r
13020                                         if (isBlock(node) && !isBlock(parentNode)) {\r
13021                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
13022                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
13023 \r
13024                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
13025                                                         node.appendChild(dom.create('br'));\r
13026                                         }\r
13027                                 } else {\r
13028                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
13029                                         if (parentNode == dom.getRoot()) {\r
13030                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
13031                                                         each(tinymce.grep(node.childNodes), function(node) {\r
13032                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
13033                                                                         if (!rootBlockElm)\r
13034                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
13035                                                                         else\r
13036                                                                                 rootBlockElm.appendChild(node);\r
13037                                                                 } else\r
13038                                                                         rootBlockElm = 0;\r
13039                                                         });\r
13040                                                 }\r
13041                                         }\r
13042                                 }\r
13043                         }\r
13044 \r
13045                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
13046                         if (format.selector && format.inline && !isEq(format.inline, node))\r
13047                                 return;\r
13048 \r
13049                         dom.remove(node, 1);\r
13050                 };\r
13051 \r
13052                 function getNonWhiteSpaceSibling(node, next, inc) {\r
13053                         if (node) {\r
13054                                 next = next ? 'nextSibling' : 'previousSibling';\r
13055 \r
13056                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
13057                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
13058                                                 return node;\r
13059                                 }\r
13060                         }\r
13061                 };\r
13062 \r
13063                 function isBookmarkNode(node) {\r
13064                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
13065                 };\r
13066 \r
13067                 function mergeSiblings(prev, next) {\r
13068                         var marker, sibling, tmpSibling;\r
13069 \r
13070                         function compareElements(node1, node2) {\r
13071                                 // Not the same name\r
13072                                 if (node1.nodeName != node2.nodeName)\r
13073                                         return FALSE;\r
13074 \r
13075                                 function getAttribs(node) {\r
13076                                         var attribs = {};\r
13077 \r
13078                                         each(dom.getAttribs(node), function(attr) {\r
13079                                                 var name = attr.nodeName.toLowerCase();\r
13080 \r
13081                                                 // Don't compare internal attributes or style\r
13082                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
13083                                                         attribs[name] = dom.getAttrib(node, name);\r
13084                                         });\r
13085 \r
13086                                         return attribs;\r
13087                                 };\r
13088 \r
13089                                 function compareObjects(obj1, obj2) {\r
13090                                         var value, name;\r
13091 \r
13092                                         for (name in obj1) {\r
13093                                                 // Obj1 has item obj2 doesn't have\r
13094                                                 if (obj1.hasOwnProperty(name)) {\r
13095                                                         value = obj2[name];\r
13096 \r
13097                                                         // Obj2 doesn't have obj1 item\r
13098                                                         if (value === undefined)\r
13099                                                                 return FALSE;\r
13100 \r
13101                                                         // Obj2 item has a different value\r
13102                                                         if (obj1[name] != value)\r
13103                                                                 return FALSE;\r
13104 \r
13105                                                         // Delete similar value\r
13106                                                         delete obj2[name];\r
13107                                                 }\r
13108                                         }\r
13109 \r
13110                                         // Check if obj 2 has something obj 1 doesn't have\r
13111                                         for (name in obj2) {\r
13112                                                 // Obj2 has item obj1 doesn't have\r
13113                                                 if (obj2.hasOwnProperty(name))\r
13114                                                         return FALSE;\r
13115                                         }\r
13116 \r
13117                                         return TRUE;\r
13118                                 };\r
13119 \r
13120                                 // Attribs are not the same\r
13121                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
13122                                         return FALSE;\r
13123 \r
13124                                 // Styles are not the same\r
13125                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
13126                                         return FALSE;\r
13127 \r
13128                                 return TRUE;\r
13129                         };\r
13130 \r
13131                         // Check if next/prev exists and that they are elements\r
13132                         if (prev && next) {\r
13133                                 function findElementSibling(node, sibling_name) {\r
13134                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
13135                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13136                                                         return node;\r
13137 \r
13138                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13139                                                         return sibling;\r
13140                                         }\r
13141 \r
13142                                         return node;\r
13143                                 };\r
13144 \r
13145                                 // If previous sibling is empty then jump over it\r
13146                                 prev = findElementSibling(prev, 'previousSibling');\r
13147                                 next = findElementSibling(next, 'nextSibling');\r
13148 \r
13149                                 // Compare next and previous nodes\r
13150                                 if (compareElements(prev, next)) {\r
13151                                         // Append nodes between\r
13152                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
13153                                                 tmpSibling = sibling;\r
13154                                                 sibling = sibling.nextSibling;\r
13155                                                 prev.appendChild(tmpSibling);\r
13156                                         }\r
13157 \r
13158                                         // Remove next node\r
13159                                         dom.remove(next);\r
13160 \r
13161                                         // Move children into prev node\r
13162                                         each(tinymce.grep(next.childNodes), function(node) {\r
13163                                                 prev.appendChild(node);\r
13164                                         });\r
13165 \r
13166                                         return prev;\r
13167                                 }\r
13168                         }\r
13169 \r
13170                         return next;\r
13171                 };\r
13172 \r
13173                 function isTextBlock(name) {\r
13174                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
13175                 };\r
13176 \r
13177                 function getContainer(rng, start) {\r
13178                         var container, offset, lastIdx;\r
13179 \r
13180                         container = rng[start ? 'startContainer' : 'endContainer'];\r
13181                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
13182 \r
13183                         if (container.nodeType == 1) {\r
13184                                 lastIdx = container.childNodes.length - 1;\r
13185 \r
13186                                 if (!start && offset)\r
13187                                         offset--;\r
13188 \r
13189                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
13190                         }\r
13191 \r
13192                         return container;\r
13193                 };\r
13194 \r
13195                 function performCaretAction(type, name, vars) {\r
13196                         var i, currentPendingFormats = pendingFormats[type],\r
13197                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
13198 \r
13199                         function hasPending() {\r
13200                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
13201                         };\r
13202 \r
13203                         function resetPending() {\r
13204                                 pendingFormats.apply = [];\r
13205                                 pendingFormats.remove = [];\r
13206                         };\r
13207 \r
13208                         function perform(caret_node) {\r
13209                                 // Apply pending formats\r
13210                                 each(pendingFormats.apply.reverse(), function(item) {\r
13211                                         apply(item.name, item.vars, caret_node);\r
13212                                 });\r
13213 \r
13214                                 // Remove pending formats\r
13215                                 each(pendingFormats.remove.reverse(), function(item) {\r
13216                                         remove(item.name, item.vars, caret_node);\r
13217                                 });\r
13218 \r
13219                                 dom.remove(caret_node, 1);\r
13220                                 resetPending();\r
13221                         };\r
13222 \r
13223                         // Check if it already exists then ignore it\r
13224                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
13225                                 if (currentPendingFormats[i].name == name)\r
13226                                         return;\r
13227                         }\r
13228 \r
13229                         currentPendingFormats.push({name : name, vars : vars});\r
13230 \r
13231                         // Check if it's in the other type, then remove it\r
13232                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
13233                                 if (otherPendingFormats[i].name == name)\r
13234                                         otherPendingFormats.splice(i, 1);\r
13235                         }\r
13236 \r
13237                         // Pending apply or remove formats\r
13238                         if (hasPending()) {\r
13239                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
13240                                 pendingFormats.lastRng = selection.getRng();\r
13241 \r
13242                                 // IE will convert the current word\r
13243                                 each(dom.select('font,span'), function(node) {\r
13244                                         var bookmark;\r
13245 \r
13246                                         if (isCaretNode(node)) {\r
13247                                                 bookmark = selection.getBookmark();\r
13248                                                 perform(node);\r
13249                                                 selection.moveToBookmark(bookmark);\r
13250                                                 ed.nodeChanged();\r
13251                                         }\r
13252                                 });\r
13253 \r
13254                                 // Only register listeners once if we need to\r
13255                                 if (!pendingFormats.isListening && hasPending()) {\r
13256                                         pendingFormats.isListening = true;\r
13257 \r
13258                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
13259                                                 ed[event].addToTop(function(ed, e) {\r
13260                                                         // Do we have pending formats and is the selection moved has moved\r
13261                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
13262                                                                 each(dom.select('font,span'), function(node) {\r
13263                                                                         var textNode, rng;\r
13264 \r
13265                                                                         // Look for marker\r
13266                                                                         if (isCaretNode(node)) {\r
13267                                                                                 textNode = node.firstChild;\r
13268 \r
13269                                                                                 if (textNode) {\r
13270                                                                                         perform(node);\r
13271 \r
13272                                                                                         rng = dom.createRng();\r
13273                                                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
13274                                                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
13275                                                                                         selection.setRng(rng);\r
13276                                                                                         ed.nodeChanged();\r
13277                                                                                 } else\r
13278                                                                                         dom.remove(node);\r
13279                                                                         }\r
13280                                                                 });\r
13281 \r
13282                                                                 // Always unbind and clear pending styles on keyup\r
13283                                                                 if (e.type == 'keyup' || e.type == 'mouseup')\r
13284                                                                         resetPending();\r
13285                                                         }\r
13286                                                 });\r
13287                                         });\r
13288                                 }\r
13289                         }\r
13290                 };\r
13291         };\r
13292 })(tinymce);\r
13293 \r
13294 tinymce.onAddEditor.add(function(tinymce, ed) {\r
13295         var filters, fontSizes, dom, settings = ed.settings;\r
13296 \r
13297         if (settings.inline_styles) {\r
13298                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
13299 \r
13300                 function replaceWithSpan(node, styles) {\r
13301                         dom.replace(dom.create('span', {\r
13302                                 style : styles\r
13303                         }), node, 1);\r
13304                 };\r
13305 \r
13306                 filters = {\r
13307                         font : function(dom, node) {\r
13308                                 replaceWithSpan(node, {\r
13309                                         backgroundColor : node.style.backgroundColor,\r
13310                                         color : node.color,\r
13311                                         fontFamily : node.face,\r
13312                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
13313                                 });\r
13314                         },\r
13315 \r
13316                         u : function(dom, node) {\r
13317                                 replaceWithSpan(node, {\r
13318                                         textDecoration : 'underline'\r
13319                                 });\r
13320                         },\r
13321 \r
13322                         strike : function(dom, node) {\r
13323                                 replaceWithSpan(node, {\r
13324                                         textDecoration : 'line-through'\r
13325                                 });\r
13326                         }\r
13327                 };\r
13328 \r
13329                 function convert(editor, params) {\r
13330                         dom = editor.dom;\r
13331 \r
13332                         if (settings.convert_fonts_to_spans) {\r
13333                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
13334                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
13335                                 });\r
13336                         }\r
13337                 };\r
13338 \r
13339                 ed.onPreProcess.add(convert);\r
13340 \r
13341                 ed.onInit.add(function() {\r
13342                         ed.selection.onSetContent.add(convert);\r
13343                 });\r
13344         }\r
13345 });\r
13346 \r