]> git.donarmstrong.com Git - roundcube.git/blob - program/js/tiny_mce/tiny_mce_src.js
Imported Debian patch 0.5.1+dfsg-7
[roundcube.git] / program / js / tiny_mce / tiny_mce_src.js
1 (function(win) {\r
2         var whiteSpaceRe = /^\s*|\s*$/g,\r
3                 undefined;\r
4 \r
5         var tinymce = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '3.7',\r
9 \r
10                 releaseDate : '2010-06-10',\r
11 \r
12                 _init : function() {\r
13                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
14 \r
15                         t.isOpera = win.opera && opera.buildNumber;\r
16 \r
17                         t.isWebKit = /WebKit/.test(ua);\r
18 \r
19                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
20 \r
21                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
22 \r
23                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
24 \r
25                         t.isMac = ua.indexOf('Mac') != -1;\r
26 \r
27                         t.isAir = /adobeair/i.test(ua);\r
28 \r
29                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
30 \r
31                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
32                         if (win.tinyMCEPreInit) {\r
33                                 t.suffix = tinyMCEPreInit.suffix;\r
34                                 t.baseURL = tinyMCEPreInit.base;\r
35                                 t.query = tinyMCEPreInit.query;\r
36                                 return;\r
37                         }\r
38 \r
39                         // Get suffix and base\r
40                         t.suffix = '';\r
41 \r
42                         // If base element found, add that infront of baseURL\r
43                         nl = d.getElementsByTagName('base');\r
44                         for (i=0; i<nl.length; i++) {\r
45                                 if (v = nl[i].href) {\r
46                                         // Host only value like http://site.com or http://site.com:8008\r
47                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
48                                                 v += '/';\r
49 \r
50                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
51                                 }\r
52                         }\r
53 \r
54                         function getBase(n) {\r
55                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {\r
56                                         if (/_(src|dev)\.js/g.test(n.src))\r
57                                                 t.suffix = '_src';\r
58 \r
59                                         if ((p = n.src.indexOf('?')) != -1)\r
60                                                 t.query = n.src.substring(p + 1);\r
61 \r
62                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
63 \r
64                                         // If path to script is relative and a base href was found add that one infront\r
65                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
66                                         // so this logic will basically only be executed on older IE versions\r
67                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
68                                                 t.baseURL = base + t.baseURL;\r
69 \r
70                                         return t.baseURL;\r
71                                 }\r
72 \r
73                                 return null;\r
74                         };\r
75 \r
76                         // Check document\r
77                         nl = d.getElementsByTagName('script');\r
78                         for (i=0; i<nl.length; i++) {\r
79                                 if (getBase(nl[i]))\r
80                                         return;\r
81                         }\r
82 \r
83                         // Check head\r
84                         n = d.getElementsByTagName('head')[0];\r
85                         if (n) {\r
86                                 nl = n.getElementsByTagName('script');\r
87                                 for (i=0; i<nl.length; i++) {\r
88                                         if (getBase(nl[i]))\r
89                                                 return;\r
90                                 }\r
91                         }\r
92 \r
93                         return;\r
94                 },\r
95 \r
96                 is : function(o, t) {\r
97                         if (!t)\r
98                                 return o !== undefined;\r
99 \r
100                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
101                                 return true;\r
102 \r
103                         return typeof(o) == t;\r
104                 },\r
105 \r
106                 each : function(o, cb, s) {\r
107                         var n, l;\r
108 \r
109                         if (!o)\r
110                                 return 0;\r
111 \r
112                         s = s || o;\r
113 \r
114                         if (o.length !== undefined) {\r
115                                 // Indexed arrays, needed for Safari\r
116                                 for (n=0, l = o.length; n < l; n++) {\r
117                                         if (cb.call(s, o[n], n, o) === false)\r
118                                                 return 0;\r
119                                 }\r
120                         } else {\r
121                                 // Hashtables\r
122                                 for (n in o) {\r
123                                         if (o.hasOwnProperty(n)) {\r
124                                                 if (cb.call(s, o[n], n, o) === false)\r
125                                                         return 0;\r
126                                         }\r
127                                 }\r
128                         }\r
129 \r
130                         return 1;\r
131                 },\r
132 \r
133 \r
134                 map : function(a, f) {\r
135                         var o = [];\r
136 \r
137                         tinymce.each(a, function(v) {\r
138                                 o.push(f(v));\r
139                         });\r
140 \r
141                         return o;\r
142                 },\r
143 \r
144                 grep : function(a, f) {\r
145                         var o = [];\r
146 \r
147                         tinymce.each(a, function(v) {\r
148                                 if (!f || f(v))\r
149                                         o.push(v);\r
150                         });\r
151 \r
152                         return o;\r
153                 },\r
154 \r
155                 inArray : function(a, v) {\r
156                         var i, l;\r
157 \r
158                         if (a) {\r
159                                 for (i = 0, l = a.length; i < l; i++) {\r
160                                         if (a[i] === v)\r
161                                                 return i;\r
162                                 }\r
163                         }\r
164 \r
165                         return -1;\r
166                 },\r
167 \r
168                 extend : function(o, e) {\r
169                         var i, l, a = arguments;\r
170 \r
171                         for (i = 1, l = a.length; i < l; i++) {\r
172                                 e = a[i];\r
173 \r
174                                 tinymce.each(e, function(v, n) {\r
175                                         if (v !== undefined)\r
176                                                 o[n] = v;\r
177                                 });\r
178                         }\r
179 \r
180                         return o;\r
181                 },\r
182 \r
183 \r
184                 trim : function(s) {\r
185                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
186                 },\r
187 \r
188                 create : function(s, p) {\r
189                         var t = this, sp, ns, cn, scn, c, de = 0;\r
190 \r
191                         // Parse : <prefix> <class>:<super class>\r
192                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
193                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
194 \r
195                         // Create namespace for new class\r
196                         ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
197 \r
198                         // Class already exists\r
199                         if (ns[cn])\r
200                                 return;\r
201 \r
202                         // Make pure static class\r
203                         if (s[2] == 'static') {\r
204                                 ns[cn] = p;\r
205 \r
206                                 if (this.onCreate)\r
207                                         this.onCreate(s[2], s[3], ns[cn]);\r
208 \r
209                                 return;\r
210                         }\r
211 \r
212                         // Create default constructor\r
213                         if (!p[cn]) {\r
214                                 p[cn] = function() {};\r
215                                 de = 1;\r
216                         }\r
217 \r
218                         // Add constructor and methods\r
219                         ns[cn] = p[cn];\r
220                         t.extend(ns[cn].prototype, p);\r
221 \r
222                         // Extend\r
223                         if (s[5]) {\r
224                                 sp = t.resolve(s[5]).prototype;\r
225                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
226 \r
227                                 // Extend constructor\r
228                                 c = ns[cn];\r
229                                 if (de) {\r
230                                         // Add passthrough constructor\r
231                                         ns[cn] = function() {\r
232                                                 return sp[scn].apply(this, arguments);\r
233                                         };\r
234                                 } else {\r
235                                         // Add inherit constructor\r
236                                         ns[cn] = function() {\r
237                                                 this.parent = sp[scn];\r
238                                                 return c.apply(this, arguments);\r
239                                         };\r
240                                 }\r
241                                 ns[cn].prototype[cn] = ns[cn];\r
242 \r
243                                 // Add super methods\r
244                                 t.each(sp, function(f, n) {\r
245                                         ns[cn].prototype[n] = sp[n];\r
246                                 });\r
247 \r
248                                 // Add overridden methods\r
249                                 t.each(p, function(f, n) {\r
250                                         // Extend methods if needed\r
251                                         if (sp[n]) {\r
252                                                 ns[cn].prototype[n] = function() {\r
253                                                         this.parent = sp[n];\r
254                                                         return f.apply(this, arguments);\r
255                                                 };\r
256                                         } else {\r
257                                                 if (n != cn)\r
258                                                         ns[cn].prototype[n] = f;\r
259                                         }\r
260                                 });\r
261                         }\r
262 \r
263                         // Add static methods\r
264                         t.each(p['static'], function(f, n) {\r
265                                 ns[cn][n] = f;\r
266                         });\r
267 \r
268                         if (this.onCreate)\r
269                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
270                 },\r
271 \r
272                 walk : function(o, f, n, s) {\r
273                         s = s || this;\r
274 \r
275                         if (o) {\r
276                                 if (n)\r
277                                         o = o[n];\r
278 \r
279                                 tinymce.each(o, function(o, i) {\r
280                                         if (f.call(s, o, i, n) === false)\r
281                                                 return false;\r
282 \r
283                                         tinymce.walk(o, f, n, s);\r
284                                 });\r
285                         }\r
286                 },\r
287 \r
288                 createNS : function(n, o) {\r
289                         var i, v;\r
290 \r
291                         o = o || win;\r
292 \r
293                         n = n.split('.');\r
294                         for (i=0; i<n.length; i++) {\r
295                                 v = n[i];\r
296 \r
297                                 if (!o[v])\r
298                                         o[v] = {};\r
299 \r
300                                 o = o[v];\r
301                         }\r
302 \r
303                         return o;\r
304                 },\r
305 \r
306                 resolve : function(n, o) {\r
307                         var i, l;\r
308 \r
309                         o = o || win;\r
310 \r
311                         n = n.split('.');\r
312                         for (i = 0, l = n.length; i < l; i++) {\r
313                                 o = o[n[i]];\r
314 \r
315                                 if (!o)\r
316                                         break;\r
317                         }\r
318 \r
319                         return o;\r
320                 },\r
321 \r
322                 addUnload : function(f, s) {\r
323                         var t = this;\r
324 \r
325                         f = {func : f, scope : s || this};\r
326 \r
327                         if (!t.unloads) {\r
328                                 function unload() {\r
329                                         var li = t.unloads, o, n;\r
330 \r
331                                         if (li) {\r
332                                                 // Call unload handlers\r
333                                                 for (n in li) {\r
334                                                         o = li[n];\r
335 \r
336                                                         if (o && o.func)\r
337                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
338                                                 }\r
339 \r
340                                                 // Detach unload function\r
341                                                 if (win.detachEvent) {\r
342                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
343                                                         win.detachEvent('onunload', unload);\r
344                                                 } else if (win.removeEventListener)\r
345                                                         win.removeEventListener('unload', unload, false);\r
346 \r
347                                                 // Destroy references\r
348                                                 t.unloads = o = li = w = unload = 0;\r
349 \r
350                                                 // Run garbarge collector on IE\r
351                                                 if (win.CollectGarbage)\r
352                                                         CollectGarbage();\r
353                                         }\r
354                                 };\r
355 \r
356                                 function fakeUnload() {\r
357                                         var d = document;\r
358 \r
359                                         // Is there things still loading, then do some magic\r
360                                         if (d.readyState == 'interactive') {\r
361                                                 function stop() {\r
362                                                         // Prevent memory leak\r
363                                                         d.detachEvent('onstop', stop);\r
364 \r
365                                                         // Call unload handler\r
366                                                         if (unload)\r
367                                                                 unload();\r
368 \r
369                                                         d = 0;\r
370                                                 };\r
371 \r
372                                                 // Fire unload when the currently loading page is stopped\r
373                                                 if (d)\r
374                                                         d.attachEvent('onstop', stop);\r
375 \r
376                                                 // Remove onstop listener after a while to prevent the unload function\r
377                                                 // to execute if the user presses cancel in an onbeforeunload\r
378                                                 // confirm dialog and then presses the browser stop button\r
379                                                 win.setTimeout(function() {\r
380                                                         if (d)\r
381                                                                 d.detachEvent('onstop', stop);\r
382                                                 }, 0);\r
383                                         }\r
384                                 };\r
385 \r
386                                 // Attach unload handler\r
387                                 if (win.attachEvent) {\r
388                                         win.attachEvent('onunload', unload);\r
389                                         win.attachEvent('onbeforeunload', fakeUnload);\r
390                                 } else if (win.addEventListener)\r
391                                         win.addEventListener('unload', unload, false);\r
392 \r
393                                 // Setup initial unload handler array\r
394                                 t.unloads = [f];\r
395                         } else\r
396                                 t.unloads.push(f);\r
397 \r
398                         return f;\r
399                 },\r
400 \r
401                 removeUnload : function(f) {\r
402                         var u = this.unloads, r = null;\r
403 \r
404                         tinymce.each(u, function(o, i) {\r
405                                 if (o && o.func == f) {\r
406                                         u.splice(i, 1);\r
407                                         r = f;\r
408                                         return false;\r
409                                 }\r
410                         });\r
411 \r
412                         return r;\r
413                 },\r
414 \r
415                 explode : function(s, d) {\r
416                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
417                 },\r
418 \r
419                 _addVer : function(u) {\r
420                         var v;\r
421 \r
422                         if (!this.query)\r
423                                 return u;\r
424 \r
425                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
426 \r
427                         if (u.indexOf('#') == -1)\r
428                                 return u + v;\r
429 \r
430                         return u.replace('#', v + '#');\r
431                 }\r
432 \r
433                 };\r
434 \r
435         // Initialize the API\r
436         tinymce._init();\r
437 \r
438         // Expose tinymce namespace to the global namespace (window)\r
439         win.tinymce = win.tinyMCE = tinymce;\r
440 })(window);\r
441 \r
442 \r
443 tinymce.create('tinymce.util.Dispatcher', {\r
444         scope : null,\r
445         listeners : null,\r
446 \r
447         Dispatcher : function(s) {\r
448                 this.scope = s || this;\r
449                 this.listeners = [];\r
450         },\r
451 \r
452         add : function(cb, s) {\r
453                 this.listeners.push({cb : cb, scope : s || this.scope});\r
454 \r
455                 return cb;\r
456         },\r
457 \r
458         addToTop : function(cb, s) {\r
459                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
460 \r
461                 return cb;\r
462         },\r
463 \r
464         remove : function(cb) {\r
465                 var l = this.listeners, o = null;\r
466 \r
467                 tinymce.each(l, function(c, i) {\r
468                         if (cb == c.cb) {\r
469                                 o = cb;\r
470                                 l.splice(i, 1);\r
471                                 return false;\r
472                         }\r
473                 });\r
474 \r
475                 return o;\r
476         },\r
477 \r
478         dispatch : function() {\r
479                 var s, a = arguments, i, li = this.listeners, c;\r
480 \r
481                 // Needs to be a real loop since the listener count might change while looping\r
482                 // And this is also more efficient\r
483                 for (i = 0; i<li.length; i++) {\r
484                         c = li[i];\r
485                         s = c.cb.apply(c.scope, a);\r
486 \r
487                         if (s === false)\r
488                                 break;\r
489                 }\r
490 \r
491                 return s;\r
492         }\r
493 \r
494         });\r
495 \r
496 (function() {\r
497         var each = tinymce.each;\r
498 \r
499         tinymce.create('tinymce.util.URI', {\r
500                 URI : function(u, s) {\r
501                         var t = this, o, a, b;\r
502 \r
503                         // Trim whitespace\r
504                         u = tinymce.trim(u);\r
505 \r
506                         // Default settings\r
507                         s = t.settings = s || {};\r
508 \r
509                         // Strange app protocol or local anchor\r
510                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
511                                 t.source = u;\r
512                                 return;\r
513                         }\r
514 \r
515                         // Absolute path with no host, fake host and protocol\r
516                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
517                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
518 \r
519                         // Relative path http:// or protocol relative //path\r
520                         if (!/^\w*:?\/\//.test(u))\r
521                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
522 \r
523                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
524                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
525                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
526                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
527                                 var s = u[i];\r
528 \r
529                                 // Zope 3 workaround, they use @@something\r
530                                 if (s)\r
531                                         s = s.replace(/\(mce_at\)/g, '@@');\r
532 \r
533                                 t[v] = s;\r
534                         });\r
535 \r
536                         if (b = s.base_uri) {\r
537                                 if (!t.protocol)\r
538                                         t.protocol = b.protocol;\r
539 \r
540                                 if (!t.userInfo)\r
541                                         t.userInfo = b.userInfo;\r
542 \r
543                                 if (!t.port && t.host == 'mce_host')\r
544                                         t.port = b.port;\r
545 \r
546                                 if (!t.host || t.host == 'mce_host')\r
547                                         t.host = b.host;\r
548 \r
549                                 t.source = '';\r
550                         }\r
551 \r
552                         //t.path = t.path || '/';\r
553                 },\r
554 \r
555                 setPath : function(p) {\r
556                         var t = this;\r
557 \r
558                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
559 \r
560                         // Update path parts\r
561                         t.path = p[0];\r
562                         t.directory = p[1];\r
563                         t.file = p[2];\r
564 \r
565                         // Rebuild source\r
566                         t.source = '';\r
567                         t.getURI();\r
568                 },\r
569 \r
570                 toRelative : function(u) {\r
571                         var t = this, o;\r
572 \r
573                         if (u === "./")\r
574                                 return u;\r
575 \r
576                         u = new tinymce.util.URI(u, {base_uri : t});\r
577 \r
578                         // Not on same domain/port or protocol\r
579                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
580                                 return u.getURI();\r
581 \r
582                         o = t.toRelPath(t.path, u.path);\r
583 \r
584                         // Add query\r
585                         if (u.query)\r
586                                 o += '?' + u.query;\r
587 \r
588                         // Add anchor\r
589                         if (u.anchor)\r
590                                 o += '#' + u.anchor;\r
591 \r
592                         return o;\r
593                 },\r
594         \r
595                 toAbsolute : function(u, nh) {\r
596                         var u = new tinymce.util.URI(u, {base_uri : this});\r
597 \r
598                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
599                 },\r
600 \r
601                 toRelPath : function(base, path) {\r
602                         var items, bp = 0, out = '', i, l;\r
603 \r
604                         // Split the paths\r
605                         base = base.substring(0, base.lastIndexOf('/'));\r
606                         base = base.split('/');\r
607                         items = path.split('/');\r
608 \r
609                         if (base.length >= items.length) {\r
610                                 for (i = 0, l = base.length; i < l; i++) {\r
611                                         if (i >= items.length || base[i] != items[i]) {\r
612                                                 bp = i + 1;\r
613                                                 break;\r
614                                         }\r
615                                 }\r
616                         }\r
617 \r
618                         if (base.length < items.length) {\r
619                                 for (i = 0, l = items.length; i < l; i++) {\r
620                                         if (i >= base.length || base[i] != items[i]) {\r
621                                                 bp = i + 1;\r
622                                                 break;\r
623                                         }\r
624                                 }\r
625                         }\r
626 \r
627                         if (bp == 1)\r
628                                 return path;\r
629 \r
630                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
631                                 out += "../";\r
632 \r
633                         for (i = bp - 1, l = items.length; i < l; i++) {\r
634                                 if (i != bp - 1)\r
635                                         out += "/" + items[i];\r
636                                 else\r
637                                         out += items[i];\r
638                         }\r
639 \r
640                         return out;\r
641                 },\r
642 \r
643                 toAbsPath : function(base, path) {\r
644                         var i, nb = 0, o = [], tr, outPath;\r
645 \r
646                         // Split paths\r
647                         tr = /\/$/.test(path) ? '/' : '';\r
648                         base = base.split('/');\r
649                         path = path.split('/');\r
650 \r
651                         // Remove empty chunks\r
652                         each(base, function(k) {\r
653                                 if (k)\r
654                                         o.push(k);\r
655                         });\r
656 \r
657                         base = o;\r
658 \r
659                         // Merge relURLParts chunks\r
660                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
661                                 // Ignore empty or .\r
662                                 if (path[i].length == 0 || path[i] == ".")\r
663                                         continue;\r
664 \r
665                                 // Is parent\r
666                                 if (path[i] == '..') {\r
667                                         nb++;\r
668                                         continue;\r
669                                 }\r
670 \r
671                                 // Move up\r
672                                 if (nb > 0) {\r
673                                         nb--;\r
674                                         continue;\r
675                                 }\r
676 \r
677                                 o.push(path[i]);\r
678                         }\r
679 \r
680                         i = base.length - nb;\r
681 \r
682                         // If /a/b/c or /\r
683                         if (i <= 0)\r
684                                 outPath = o.reverse().join('/');\r
685                         else\r
686                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
687 \r
688                         // Add front / if it's needed\r
689                         if (outPath.indexOf('/') !== 0)\r
690                                 outPath = '/' + outPath;\r
691 \r
692                         // Add traling / if it's needed\r
693                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
694                                 outPath += tr;\r
695 \r
696                         return outPath;\r
697                 },\r
698 \r
699                 getURI : function(nh) {\r
700                         var s, t = this;\r
701 \r
702                         // Rebuild source\r
703                         if (!t.source || nh) {\r
704                                 s = '';\r
705 \r
706                                 if (!nh) {\r
707                                         if (t.protocol)\r
708                                                 s += t.protocol + '://';\r
709 \r
710                                         if (t.userInfo)\r
711                                                 s += t.userInfo + '@';\r
712 \r
713                                         if (t.host)\r
714                                                 s += t.host;\r
715 \r
716                                         if (t.port)\r
717                                                 s += ':' + t.port;\r
718                                 }\r
719 \r
720                                 if (t.path)\r
721                                         s += t.path;\r
722 \r
723                                 if (t.query)\r
724                                         s += '?' + t.query;\r
725 \r
726                                 if (t.anchor)\r
727                                         s += '#' + t.anchor;\r
728 \r
729                                 t.source = s;\r
730                         }\r
731 \r
732                         return t.source;\r
733                 }\r
734         });\r
735 })();\r
736 \r
737 (function() {\r
738         var each = tinymce.each;\r
739 \r
740         tinymce.create('static tinymce.util.Cookie', {\r
741                 getHash : function(n) {\r
742                         var v = this.get(n), h;\r
743 \r
744                         if (v) {\r
745                                 each(v.split('&'), function(v) {\r
746                                         v = v.split('=');\r
747                                         h = h || {};\r
748                                         h[unescape(v[0])] = unescape(v[1]);\r
749                                 });\r
750                         }\r
751 \r
752                         return h;\r
753                 },\r
754 \r
755                 setHash : function(n, v, e, p, d, s) {\r
756                         var o = '';\r
757 \r
758                         each(v, function(v, k) {\r
759                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
760                         });\r
761 \r
762                         this.set(n, o, e, p, d, s);\r
763                 },\r
764 \r
765                 get : function(n) {\r
766                         var c = document.cookie, e, p = n + "=", b;\r
767 \r
768                         // Strict mode\r
769                         if (!c)\r
770                                 return;\r
771 \r
772                         b = c.indexOf("; " + p);\r
773 \r
774                         if (b == -1) {\r
775                                 b = c.indexOf(p);\r
776 \r
777                                 if (b != 0)\r
778                                         return null;\r
779                         } else\r
780                                 b += 2;\r
781 \r
782                         e = c.indexOf(";", b);\r
783 \r
784                         if (e == -1)\r
785                                 e = c.length;\r
786 \r
787                         return unescape(c.substring(b + p.length, e));\r
788                 },\r
789 \r
790                 set : function(n, v, e, p, d, s) {\r
791                         document.cookie = n + "=" + escape(v) +\r
792                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
793                                 ((p) ? "; path=" + escape(p) : "") +\r
794                                 ((d) ? "; domain=" + d : "") +\r
795                                 ((s) ? "; secure" : "");\r
796                 },\r
797 \r
798                 remove : function(n, p) {\r
799                         var d = new Date();\r
800 \r
801                         d.setTime(d.getTime() - 1000);\r
802 \r
803                         this.set(n, '', d, p, d);\r
804                 }\r
805         });\r
806 })();\r
807 \r
808 tinymce.create('static tinymce.util.JSON', {\r
809         serialize : function(o) {\r
810                 var i, v, s = tinymce.util.JSON.serialize, t;\r
811 \r
812                 if (o == null)\r
813                         return 'null';\r
814 \r
815                 t = typeof o;\r
816 \r
817                 if (t == 'string') {\r
818                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
819 \r
820                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
821                                 i = v.indexOf(b);\r
822 \r
823                                 if (i + 1)\r
824                                         return '\\' + v.charAt(i + 1);\r
825 \r
826                                 a = b.charCodeAt().toString(16);\r
827 \r
828                                 return '\\u' + '0000'.substring(a.length) + a;\r
829                         }) + '"';\r
830                 }\r
831 \r
832                 if (t == 'object') {\r
833                         if (o.hasOwnProperty && o instanceof Array) {\r
834                                         for (i=0, v = '['; i<o.length; i++)\r
835                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
836 \r
837                                         return v + ']';\r
838                                 }\r
839 \r
840                                 v = '{';\r
841 \r
842                                 for (i in o)\r
843                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
844 \r
845                                 return v + '}';\r
846                 }\r
847 \r
848                 return '' + o;\r
849         },\r
850 \r
851         parse : function(s) {\r
852                 try {\r
853                         return eval('(' + s + ')');\r
854                 } catch (ex) {\r
855                         // Ignore\r
856                 }\r
857         }\r
858 \r
859         });\r
860 \r
861 tinymce.create('static tinymce.util.XHR', {\r
862         send : function(o) {\r
863                 var x, t, w = window, c = 0;\r
864 \r
865                 // Default settings\r
866                 o.scope = o.scope || this;\r
867                 o.success_scope = o.success_scope || o.scope;\r
868                 o.error_scope = o.error_scope || o.scope;\r
869                 o.async = o.async === false ? false : true;\r
870                 o.data = o.data || '';\r
871 \r
872                 function get(s) {\r
873                         x = 0;\r
874 \r
875                         try {\r
876                                 x = new ActiveXObject(s);\r
877                         } catch (ex) {\r
878                         }\r
879 \r
880                         return x;\r
881                 };\r
882 \r
883                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
884 \r
885                 if (x) {\r
886                         if (x.overrideMimeType)\r
887                                 x.overrideMimeType(o.content_type);\r
888 \r
889                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
890 \r
891                         if (o.content_type)\r
892                                 x.setRequestHeader('Content-Type', o.content_type);\r
893 \r
894                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
895 \r
896                         x.send(o.data);\r
897 \r
898                         function ready() {\r
899                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
900                                         if (o.success && c < 10000 && x.status == 200)\r
901                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
902                                         else if (o.error)\r
903                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
904 \r
905                                         x = null;\r
906                                 } else\r
907                                         w.setTimeout(ready, 10);\r
908                         };\r
909 \r
910                         // Syncronous request\r
911                         if (!o.async)\r
912                                 return ready();\r
913 \r
914                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
915                         t = w.setTimeout(ready, 10);\r
916                 }\r
917         }\r
918 });\r
919 \r
920 (function() {\r
921         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
922 \r
923         tinymce.create('tinymce.util.JSONRequest', {\r
924                 JSONRequest : function(s) {\r
925                         this.settings = extend({\r
926                         }, s);\r
927                         this.count = 0;\r
928                 },\r
929 \r
930                 send : function(o) {\r
931                         var ecb = o.error, scb = o.success;\r
932 \r
933                         o = extend(this.settings, o);\r
934 \r
935                         o.success = function(c, x) {\r
936                                 c = JSON.parse(c);\r
937 \r
938                                 if (typeof(c) == 'undefined') {\r
939                                         c = {\r
940                                                 error : 'JSON Parse error.'\r
941                                         };\r
942                                 }\r
943 \r
944                                 if (c.error)\r
945                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
946                                 else\r
947                                         scb.call(o.success_scope || o.scope, c.result);\r
948                         };\r
949 \r
950                         o.error = function(ty, x) {\r
951                                 ecb.call(o.error_scope || o.scope, ty, x);\r
952                         };\r
953 \r
954                         o.data = JSON.serialize({\r
955                                 id : o.id || 'c' + (this.count++),\r
956                                 method : o.method,\r
957                                 params : o.params\r
958                         });\r
959 \r
960                         // JSON content type for Ruby on rails. Bug: #1883287\r
961                         o.content_type = 'application/json';\r
962 \r
963                         XHR.send(o);\r
964                 },\r
965 \r
966                 'static' : {\r
967                         sendRPC : function(o) {\r
968                                 return new tinymce.util.JSONRequest().send(o);\r
969                         }\r
970                 }\r
971         });\r
972 }());\r
973 (function(tinymce) {\r
974         // Shorten names\r
975         var each = tinymce.each,\r
976                 is = tinymce.is,\r
977                 isWebKit = tinymce.isWebKit,\r
978                 isIE = tinymce.isIE,\r
979                 blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/,\r
980                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
981                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
982                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
983                 encodeCharsRe = /[<>&\"]/g,\r
984                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
985                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
986                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
987 \r
988         function makeMap(str) {\r
989                 var map = {}, i;\r
990 \r
991                 str = str.split(',');\r
992                 for (i = str.length; i >= 0; i--)\r
993                         map[str[i]] = 1;\r
994 \r
995                 return map;\r
996         };\r
997 \r
998         tinymce.create('tinymce.dom.DOMUtils', {\r
999                 doc : null,\r
1000                 root : null,\r
1001                 files : null,\r
1002                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1003                 props : {\r
1004                         "for" : "htmlFor",\r
1005                         "class" : "className",\r
1006                         className : "className",\r
1007                         checked : "checked",\r
1008                         disabled : "disabled",\r
1009                         maxlength : "maxLength",\r
1010                         readonly : "readOnly",\r
1011                         selected : "selected",\r
1012                         value : "value",\r
1013                         id : "id",\r
1014                         name : "name",\r
1015                         type : "type"\r
1016                 },\r
1017 \r
1018                 DOMUtils : function(d, s) {\r
1019                         var t = this, globalStyle;\r
1020 \r
1021                         t.doc = d;\r
1022                         t.win = window;\r
1023                         t.files = {};\r
1024                         t.cssFlicker = false;\r
1025                         t.counter = 0;\r
1026                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
1027                         t.stdMode = d.documentMode === 8;\r
1028 \r
1029                         t.settings = s = tinymce.extend({\r
1030                                 keep_values : false,\r
1031                                 hex_colors : 1,\r
1032                                 process_html : 1\r
1033                         }, s);\r
1034 \r
1035                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1036                         if (tinymce.isIE6) {\r
1037                                 try {\r
1038                                         d.execCommand('BackgroundImageCache', false, true);\r
1039                                 } catch (e) {\r
1040                                         t.cssFlicker = true;\r
1041                                 }\r
1042                         }\r
1043 \r
1044                         // Build styles list\r
1045                         if (s.valid_styles) {\r
1046                                 t._styles = {};\r
1047 \r
1048                                 // Convert styles into a rule list\r
1049                                 each(s.valid_styles, function(value, key) {\r
1050                                         t._styles[key] = tinymce.explode(value);\r
1051                                 });\r
1052                         }\r
1053 \r
1054                         tinymce.addUnload(t.destroy, t);\r
1055                 },\r
1056 \r
1057                 getRoot : function() {\r
1058                         var t = this, s = t.settings;\r
1059 \r
1060                         return (s && t.get(s.root_element)) || t.doc.body;\r
1061                 },\r
1062 \r
1063                 getViewPort : function(w) {\r
1064                         var d, b;\r
1065 \r
1066                         w = !w ? this.win : w;\r
1067                         d = w.document;\r
1068                         b = this.boxModel ? d.documentElement : d.body;\r
1069 \r
1070                         // Returns viewport size excluding scrollbars\r
1071                         return {\r
1072                                 x : w.pageXOffset || b.scrollLeft,\r
1073                                 y : w.pageYOffset || b.scrollTop,\r
1074                                 w : w.innerWidth || b.clientWidth,\r
1075                                 h : w.innerHeight || b.clientHeight\r
1076                         };\r
1077                 },\r
1078 \r
1079                 getRect : function(e) {\r
1080                         var p, t = this, sr;\r
1081 \r
1082                         e = t.get(e);\r
1083                         p = t.getPos(e);\r
1084                         sr = t.getSize(e);\r
1085 \r
1086                         return {\r
1087                                 x : p.x,\r
1088                                 y : p.y,\r
1089                                 w : sr.w,\r
1090                                 h : sr.h\r
1091                         };\r
1092                 },\r
1093 \r
1094                 getSize : function(e) {\r
1095                         var t = this, w, h;\r
1096 \r
1097                         e = t.get(e);\r
1098                         w = t.getStyle(e, 'width');\r
1099                         h = t.getStyle(e, 'height');\r
1100 \r
1101                         // Non pixel value, then force offset/clientWidth\r
1102                         if (w.indexOf('px') === -1)\r
1103                                 w = 0;\r
1104 \r
1105                         // Non pixel value, then force offset/clientWidth\r
1106                         if (h.indexOf('px') === -1)\r
1107                                 h = 0;\r
1108 \r
1109                         return {\r
1110                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1111                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1112                         };\r
1113                 },\r
1114 \r
1115                 getParent : function(n, f, r) {\r
1116                         return this.getParents(n, f, r, false);\r
1117                 },\r
1118 \r
1119                 getParents : function(n, f, r, c) {\r
1120                         var t = this, na, se = t.settings, o = [];\r
1121 \r
1122                         n = t.get(n);\r
1123                         c = c === undefined;\r
1124 \r
1125                         if (se.strict_root)\r
1126                                 r = r || t.getRoot();\r
1127 \r
1128                         // Wrap node name as func\r
1129                         if (is(f, 'string')) {\r
1130                                 na = f;\r
1131 \r
1132                                 if (f === '*') {\r
1133                                         f = function(n) {return n.nodeType == 1;};\r
1134                                 } else {\r
1135                                         f = function(n) {\r
1136                                                 return t.is(n, na);\r
1137                                         };\r
1138                                 }\r
1139                         }\r
1140 \r
1141                         while (n) {\r
1142                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1143                                         break;\r
1144 \r
1145                                 if (!f || f(n)) {\r
1146                                         if (c)\r
1147                                                 o.push(n);\r
1148                                         else\r
1149                                                 return n;\r
1150                                 }\r
1151 \r
1152                                 n = n.parentNode;\r
1153                         }\r
1154 \r
1155                         return c ? o : null;\r
1156                 },\r
1157 \r
1158                 get : function(e) {\r
1159                         var n;\r
1160 \r
1161                         if (e && this.doc && typeof(e) == 'string') {\r
1162                                 n = e;\r
1163                                 e = this.doc.getElementById(e);\r
1164 \r
1165                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1166                                 if (e && e.id !== n)\r
1167                                         return this.doc.getElementsByName(n)[1];\r
1168                         }\r
1169 \r
1170                         return e;\r
1171                 },\r
1172 \r
1173                 getNext : function(node, selector) {\r
1174                         return this._findSib(node, selector, 'nextSibling');\r
1175                 },\r
1176 \r
1177                 getPrev : function(node, selector) {\r
1178                         return this._findSib(node, selector, 'previousSibling');\r
1179                 },\r
1180 \r
1181 \r
1182                 select : function(pa, s) {\r
1183                         var t = this;\r
1184 \r
1185                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1186                 },\r
1187 \r
1188                 is : function(n, selector) {\r
1189                         var i;\r
1190 \r
1191                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
1192                         if (n.length === undefined) {\r
1193                                 // Simple all selector\r
1194                                 if (selector === '*')\r
1195                                         return n.nodeType == 1;\r
1196 \r
1197                                 // Simple selector just elements\r
1198                                 if (simpleSelectorRe.test(selector)) {\r
1199                                         selector = selector.toLowerCase().split(/,/);\r
1200                                         n = n.nodeName.toLowerCase();\r
1201 \r
1202                                         for (i = selector.length - 1; i >= 0; i--) {\r
1203                                                 if (selector[i] == n)\r
1204                                                         return true;\r
1205                                         }\r
1206 \r
1207                                         return false;\r
1208                                 }\r
1209                         }\r
1210 \r
1211                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
1212                 },\r
1213 \r
1214 \r
1215                 add : function(p, n, a, h, c) {\r
1216                         var t = this;\r
1217 \r
1218                         return this.run(p, function(p) {\r
1219                                 var e, k;\r
1220 \r
1221                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1222                                 t.setAttribs(e, a);\r
1223 \r
1224                                 if (h) {\r
1225                                         if (h.nodeType)\r
1226                                                 e.appendChild(h);\r
1227                                         else\r
1228                                                 t.setHTML(e, h);\r
1229                                 }\r
1230 \r
1231                                 return !c ? p.appendChild(e) : e;\r
1232                         });\r
1233                 },\r
1234 \r
1235                 create : function(n, a, h) {\r
1236                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1237                 },\r
1238 \r
1239                 createHTML : function(n, a, h) {\r
1240                         var o = '', t = this, k;\r
1241 \r
1242                         o += '<' + n;\r
1243 \r
1244                         for (k in a) {\r
1245                                 if (a.hasOwnProperty(k))\r
1246                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1247                         }\r
1248 \r
1249                         if (tinymce.is(h))\r
1250                                 return o + '>' + h + '</' + n + '>';\r
1251 \r
1252                         return o + ' />';\r
1253                 },\r
1254 \r
1255                 remove : function(node, keep_children) {\r
1256                         return this.run(node, function(node) {\r
1257                                 var parent, child;\r
1258 \r
1259                                 parent = node.parentNode;\r
1260 \r
1261                                 if (!parent)\r
1262                                         return null;\r
1263 \r
1264                                 if (keep_children) {\r
1265                                         while (child = node.firstChild) {\r
1266                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
1267                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
1268                                                         parent.insertBefore(child, node);\r
1269                                                 else\r
1270                                                         node.removeChild(child);\r
1271                                         }\r
1272                                 }\r
1273 \r
1274                                 return parent.removeChild(node);\r
1275                         });\r
1276                 },\r
1277 \r
1278                 setStyle : function(n, na, v) {\r
1279                         var t = this;\r
1280 \r
1281                         return t.run(n, function(e) {\r
1282                                 var s, i;\r
1283 \r
1284                                 s = e.style;\r
1285 \r
1286                                 // Camelcase it, if needed\r
1287                                 na = na.replace(/-(\D)/g, function(a, b){\r
1288                                         return b.toUpperCase();\r
1289                                 });\r
1290 \r
1291                                 // Default px suffix on these\r
1292                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1293                                         v += 'px';\r
1294 \r
1295                                 switch (na) {\r
1296                                         case 'opacity':\r
1297                                                 // IE specific opacity\r
1298                                                 if (isIE) {\r
1299                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1300 \r
1301                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1302                                                                 s.display = 'inline-block';\r
1303                                                 }\r
1304 \r
1305                                                 // Fix for older browsers\r
1306                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1307                                                 break;\r
1308 \r
1309                                         case 'float':\r
1310                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1311                                                 break;\r
1312                                         \r
1313                                         default:\r
1314                                                 s[na] = v || '';\r
1315                                 }\r
1316 \r
1317                                 // Force update of the style data\r
1318                                 if (t.settings.update_styles)\r
1319                                         t.setAttrib(e, '_mce_style');\r
1320                         });\r
1321                 },\r
1322 \r
1323                 getStyle : function(n, na, c) {\r
1324                         n = this.get(n);\r
1325 \r
1326                         if (!n)\r
1327                                 return false;\r
1328 \r
1329                         // Gecko\r
1330                         if (this.doc.defaultView && c) {\r
1331                                 // Remove camelcase\r
1332                                 na = na.replace(/[A-Z]/g, function(a){\r
1333                                         return '-' + a;\r
1334                                 });\r
1335 \r
1336                                 try {\r
1337                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1338                                 } catch (ex) {\r
1339                                         // Old safari might fail\r
1340                                         return null;\r
1341                                 }\r
1342                         }\r
1343 \r
1344                         // Camelcase it, if needed\r
1345                         na = na.replace(/-(\D)/g, function(a, b){\r
1346                                 return b.toUpperCase();\r
1347                         });\r
1348 \r
1349                         if (na == 'float')\r
1350                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1351 \r
1352                         // IE & Opera\r
1353                         if (n.currentStyle && c)\r
1354                                 return n.currentStyle[na];\r
1355 \r
1356                         return n.style[na];\r
1357                 },\r
1358 \r
1359                 setStyles : function(e, o) {\r
1360                         var t = this, s = t.settings, ol;\r
1361 \r
1362                         ol = s.update_styles;\r
1363                         s.update_styles = 0;\r
1364 \r
1365                         each(o, function(v, n) {\r
1366                                 t.setStyle(e, n, v);\r
1367                         });\r
1368 \r
1369                         // Update style info\r
1370                         s.update_styles = ol;\r
1371                         if (s.update_styles)\r
1372                                 t.setAttrib(e, s.cssText);\r
1373                 },\r
1374 \r
1375                 setAttrib : function(e, n, v) {\r
1376                         var t = this;\r
1377 \r
1378                         // Whats the point\r
1379                         if (!e || !n)\r
1380                                 return;\r
1381 \r
1382                         // Strict XML mode\r
1383                         if (t.settings.strict)\r
1384                                 n = n.toLowerCase();\r
1385 \r
1386                         return this.run(e, function(e) {\r
1387                                 var s = t.settings;\r
1388 \r
1389                                 switch (n) {\r
1390                                         case "style":\r
1391                                                 if (!is(v, 'string')) {\r
1392                                                         each(v, function(v, n) {\r
1393                                                                 t.setStyle(e, n, v);\r
1394                                                         });\r
1395 \r
1396                                                         return;\r
1397                                                 }\r
1398 \r
1399                                                 // No mce_style for elements with these since they might get resized by the user\r
1400                                                 if (s.keep_values) {\r
1401                                                         if (v && !t._isRes(v))\r
1402                                                                 e.setAttribute('_mce_style', v, 2);\r
1403                                                         else\r
1404                                                                 e.removeAttribute('_mce_style', 2);\r
1405                                                 }\r
1406 \r
1407                                                 e.style.cssText = v;\r
1408                                                 break;\r
1409 \r
1410                                         case "class":\r
1411                                                 e.className = v || ''; // Fix IE null bug\r
1412                                                 break;\r
1413 \r
1414                                         case "src":\r
1415                                         case "href":\r
1416                                                 if (s.keep_values) {\r
1417                                                         if (s.url_converter)\r
1418                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1419 \r
1420                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1421                                                 }\r
1422 \r
1423                                                 break;\r
1424                                         \r
1425                                         case "shape":\r
1426                                                 e.setAttribute('_mce_style', v);\r
1427                                                 break;\r
1428                                 }\r
1429 \r
1430                                 if (is(v) && v !== null && v.length !== 0)\r
1431                                         e.setAttribute(n, '' + v, 2);\r
1432                                 else\r
1433                                         e.removeAttribute(n, 2);\r
1434                         });\r
1435                 },\r
1436 \r
1437                 setAttribs : function(e, o) {\r
1438                         var t = this;\r
1439 \r
1440                         return this.run(e, function(e) {\r
1441                                 each(o, function(v, n) {\r
1442                                         t.setAttrib(e, n, v);\r
1443                                 });\r
1444                         });\r
1445                 },\r
1446 \r
1447                 getAttrib : function(e, n, dv) {\r
1448                         var v, t = this;\r
1449 \r
1450                         e = t.get(e);\r
1451 \r
1452                         if (!e || e.nodeType !== 1)\r
1453                                 return false;\r
1454 \r
1455                         if (!is(dv))\r
1456                                 dv = '';\r
1457 \r
1458                         // Try the mce variant for these\r
1459                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1460                                 v = e.getAttribute("_mce_" + n);\r
1461 \r
1462                                 if (v)\r
1463                                         return v;\r
1464                         }\r
1465 \r
1466                         if (isIE && t.props[n]) {\r
1467                                 v = e[t.props[n]];\r
1468                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1469                         }\r
1470 \r
1471                         if (!v)\r
1472                                 v = e.getAttribute(n, 2);\r
1473 \r
1474                         // Check boolean attribs\r
1475                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1476                                 if (e[t.props[n]] === true && v === '')\r
1477                                         return n;\r
1478 \r
1479                                 return v ? n : '';\r
1480                         }\r
1481 \r
1482                         // Inner input elements will override attributes on form elements\r
1483                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1484                                 return e.getAttributeNode(n).nodeValue;\r
1485 \r
1486                         if (n === 'style') {\r
1487                                 v = v || e.style.cssText;\r
1488 \r
1489                                 if (v) {\r
1490                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1491 \r
1492                                         if (t.settings.keep_values && !t._isRes(v))\r
1493                                                 e.setAttribute('_mce_style', v);\r
1494                                 }\r
1495                         }\r
1496 \r
1497                         // Remove Apple and WebKit stuff\r
1498                         if (isWebKit && n === "class" && v)\r
1499                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1500 \r
1501                         // Handle IE issues\r
1502                         if (isIE) {\r
1503                                 switch (n) {\r
1504                                         case 'rowspan':\r
1505                                         case 'colspan':\r
1506                                                 // IE returns 1 as default value\r
1507                                                 if (v === 1)\r
1508                                                         v = '';\r
1509 \r
1510                                                 break;\r
1511 \r
1512                                         case 'size':\r
1513                                                 // IE returns +0 as default value for size\r
1514                                                 if (v === '+0' || v === 20 || v === 0)\r
1515                                                         v = '';\r
1516 \r
1517                                                 break;\r
1518 \r
1519                                         case 'width':\r
1520                                         case 'height':\r
1521                                         case 'vspace':\r
1522                                         case 'checked':\r
1523                                         case 'disabled':\r
1524                                         case 'readonly':\r
1525                                                 if (v === 0)\r
1526                                                         v = '';\r
1527 \r
1528                                                 break;\r
1529 \r
1530                                         case 'hspace':\r
1531                                                 // IE returns -1 as default value\r
1532                                                 if (v === -1)\r
1533                                                         v = '';\r
1534 \r
1535                                                 break;\r
1536 \r
1537                                         case 'maxlength':\r
1538                                         case 'tabindex':\r
1539                                                 // IE returns default value\r
1540                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1541                                                         v = '';\r
1542 \r
1543                                                 break;\r
1544 \r
1545                                         case 'multiple':\r
1546                                         case 'compact':\r
1547                                         case 'noshade':\r
1548                                         case 'nowrap':\r
1549                                                 if (v === 65535)\r
1550                                                         return n;\r
1551 \r
1552                                                 return dv;\r
1553 \r
1554                                         case 'shape':\r
1555                                                 v = v.toLowerCase();\r
1556                                                 break;\r
1557 \r
1558                                         default:\r
1559                                                 // IE has odd anonymous function for event attributes\r
1560                                                 if (n.indexOf('on') === 0 && v)\r
1561                                                         v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1562                                 }\r
1563                         }\r
1564 \r
1565                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1566                 },\r
1567 \r
1568                 getPos : function(n, ro) {\r
1569                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1570 \r
1571                         n = t.get(n);\r
1572                         ro = ro || d.body;\r
1573 \r
1574                         if (n) {\r
1575                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1576                                 if (isIE && !t.stdMode) {\r
1577                                         n = n.getBoundingClientRect();\r
1578                                         e = t.boxModel ? d.documentElement : d.body;\r
1579                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1580                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1581                                         n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset\r
1582 \r
1583                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1584                                 }\r
1585 \r
1586                                 r = n;\r
1587                                 while (r && r != ro && r.nodeType) {\r
1588                                         x += r.offsetLeft || 0;\r
1589                                         y += r.offsetTop || 0;\r
1590                                         r = r.offsetParent;\r
1591                                 }\r
1592 \r
1593                                 r = n.parentNode;\r
1594                                 while (r && r != ro && r.nodeType) {\r
1595                                         x -= r.scrollLeft || 0;\r
1596                                         y -= r.scrollTop || 0;\r
1597                                         r = r.parentNode;\r
1598                                 }\r
1599                         }\r
1600 \r
1601                         return {x : x, y : y};\r
1602                 },\r
1603 \r
1604                 parseStyle : function(st) {\r
1605                         var t = this, s = t.settings, o = {};\r
1606 \r
1607                         if (!st)\r
1608                                 return o;\r
1609 \r
1610                         function compress(p, s, ot) {\r
1611                                 var t, r, b, l;\r
1612 \r
1613                                 // Get values and check it it needs compressing\r
1614                                 t = o[p + '-top' + s];\r
1615                                 if (!t)\r
1616                                         return;\r
1617 \r
1618                                 r = o[p + '-right' + s];\r
1619                                 if (t != r)\r
1620                                         return;\r
1621 \r
1622                                 b = o[p + '-bottom' + s];\r
1623                                 if (r != b)\r
1624                                         return;\r
1625 \r
1626                                 l = o[p + '-left' + s];\r
1627                                 if (b != l)\r
1628                                         return;\r
1629 \r
1630                                 // Compress\r
1631                                 o[ot] = l;\r
1632                                 delete o[p + '-top' + s];\r
1633                                 delete o[p + '-right' + s];\r
1634                                 delete o[p + '-bottom' + s];\r
1635                                 delete o[p + '-left' + s];\r
1636                         };\r
1637 \r
1638                         function compress2(ta, a, b, c) {\r
1639                                 var t;\r
1640 \r
1641                                 t = o[a];\r
1642                                 if (!t)\r
1643                                         return;\r
1644 \r
1645                                 t = o[b];\r
1646                                 if (!t)\r
1647                                         return;\r
1648 \r
1649                                 t = o[c];\r
1650                                 if (!t)\r
1651                                         return;\r
1652 \r
1653                                 // Compress\r
1654                                 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1655                                 delete o[a];\r
1656                                 delete o[b];\r
1657                                 delete o[c];\r
1658                         };\r
1659 \r
1660                         st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1661 \r
1662                         each(st.split(';'), function(v) {\r
1663                                 var sv, ur = [];\r
1664 \r
1665                                 if (v) {\r
1666                                         v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1667                                         v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1668                                         v = v.split(':');\r
1669                                         sv = tinymce.trim(v[1]);\r
1670                                         sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1671 \r
1672                                         sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1673                                                 return t.toHex(v);\r
1674                                         });\r
1675 \r
1676                                         if (s.url_converter) {\r
1677                                                 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1678                                                         return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1679                                                 });\r
1680                                         }\r
1681 \r
1682                                         o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1683                                 }\r
1684                         });\r
1685 \r
1686                         compress("border", "", "border");\r
1687                         compress("border", "-width", "border-width");\r
1688                         compress("border", "-color", "border-color");\r
1689                         compress("border", "-style", "border-style");\r
1690                         compress("padding", "", "padding");\r
1691                         compress("margin", "", "margin");\r
1692                         compress2('border', 'border-width', 'border-style', 'border-color');\r
1693 \r
1694                         if (isIE) {\r
1695                                 // Remove pointless border\r
1696                                 if (o.border == 'medium none')\r
1697                                         o.border = '';\r
1698                         }\r
1699 \r
1700                         return o;\r
1701                 },\r
1702 \r
1703                 serializeStyle : function(o, name) {\r
1704                         var t = this, s = '';\r
1705 \r
1706                         function add(v, k) {\r
1707                                 if (k && v) {\r
1708                                         // Remove browser specific styles like -moz- or -webkit-\r
1709                                         if (k.indexOf('-') === 0)\r
1710                                                 return;\r
1711 \r
1712                                         switch (k) {\r
1713                                                 case 'font-weight':\r
1714                                                         // Opera will output bold as 700\r
1715                                                         if (v == 700)\r
1716                                                                 v = 'bold';\r
1717 \r
1718                                                         break;\r
1719 \r
1720                                                 case 'color':\r
1721                                                 case 'background-color':\r
1722                                                         v = v.toLowerCase();\r
1723                                                         break;\r
1724                                         }\r
1725 \r
1726                                         s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1727                                 }\r
1728                         };\r
1729 \r
1730                         // Validate style output\r
1731                         if (name && t._styles) {\r
1732                                 each(t._styles['*'], function(name) {\r
1733                                         add(o[name], name);\r
1734                                 });\r
1735 \r
1736                                 each(t._styles[name.toLowerCase()], function(name) {\r
1737                                         add(o[name], name);\r
1738                                 });\r
1739                         } else\r
1740                                 each(o, add);\r
1741 \r
1742                         return s;\r
1743                 },\r
1744 \r
1745                 loadCSS : function(u) {\r
1746                         var t = this, d = t.doc, head;\r
1747 \r
1748                         if (!u)\r
1749                                 u = '';\r
1750 \r
1751                         head = t.select('head')[0];\r
1752 \r
1753                         each(u.split(','), function(u) {\r
1754                                 var link;\r
1755 \r
1756                                 if (t.files[u])\r
1757                                         return;\r
1758 \r
1759                                 t.files[u] = true;\r
1760                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1761 \r
1762                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1763                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1764                                 // It's ugly but it seems to work fine.\r
1765                                 if (isIE && d.documentMode) {\r
1766                                         link.onload = function() {\r
1767                                                 d.recalc();\r
1768                                                 link.onload = null;\r
1769                                         };\r
1770                                 }\r
1771 \r
1772                                 head.appendChild(link);\r
1773                         });\r
1774                 },\r
1775 \r
1776                 addClass : function(e, c) {\r
1777                         return this.run(e, function(e) {\r
1778                                 var o;\r
1779 \r
1780                                 if (!c)\r
1781                                         return 0;\r
1782 \r
1783                                 if (this.hasClass(e, c))\r
1784                                         return e.className;\r
1785 \r
1786                                 o = this.removeClass(e, c);\r
1787 \r
1788                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1789                         });\r
1790                 },\r
1791 \r
1792                 removeClass : function(e, c) {\r
1793                         var t = this, re;\r
1794 \r
1795                         return t.run(e, function(e) {\r
1796                                 var v;\r
1797 \r
1798                                 if (t.hasClass(e, c)) {\r
1799                                         if (!re)\r
1800                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1801 \r
1802                                         v = e.className.replace(re, ' ');\r
1803                                         v = tinymce.trim(v != ' ' ? v : '');\r
1804 \r
1805                                         e.className = v;\r
1806 \r
1807                                         // Empty class attr\r
1808                                         if (!v) {\r
1809                                                 e.removeAttribute('class');\r
1810                                                 e.removeAttribute('className');\r
1811                                         }\r
1812 \r
1813                                         return v;\r
1814                                 }\r
1815 \r
1816                                 return e.className;\r
1817                         });\r
1818                 },\r
1819 \r
1820                 hasClass : function(n, c) {\r
1821                         n = this.get(n);\r
1822 \r
1823                         if (!n || !c)\r
1824                                 return false;\r
1825 \r
1826                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1827                 },\r
1828 \r
1829                 show : function(e) {\r
1830                         return this.setStyle(e, 'display', 'block');\r
1831                 },\r
1832 \r
1833                 hide : function(e) {\r
1834                         return this.setStyle(e, 'display', 'none');\r
1835                 },\r
1836 \r
1837                 isHidden : function(e) {\r
1838                         e = this.get(e);\r
1839 \r
1840                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1841                 },\r
1842 \r
1843                 uniqueId : function(p) {\r
1844                         return (!p ? 'mce_' : p) + (this.counter++);\r
1845                 },\r
1846 \r
1847                 setHTML : function(e, h) {\r
1848                         var t = this;\r
1849 \r
1850                         return this.run(e, function(e) {\r
1851                                 var x, i, nl, n, p, x;\r
1852 \r
1853                                 h = t.processHTML(h);\r
1854 \r
1855                                 if (isIE) {\r
1856                                         function set() {\r
1857                                                 // Remove all child nodes\r
1858                                                 while (e.firstChild)\r
1859                                                         e.firstChild.removeNode();\r
1860 \r
1861                                                 try {\r
1862                                                         // IE will remove comments from the beginning\r
1863                                                         // unless you padd the contents with something\r
1864                                                         e.innerHTML = '<br />' + h;\r
1865                                                         e.removeChild(e.firstChild);\r
1866                                                 } catch (ex) {\r
1867                                                         // 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
1868                                                         // This seems to fix this problem\r
1869 \r
1870                                                         // Create new div with HTML contents and a BR infront to keep comments\r
1871                                                         x = t.create('div');\r
1872                                                         x.innerHTML = '<br />' + h;\r
1873 \r
1874                                                         // Add all children from div to target\r
1875                                                         each (x.childNodes, function(n, i) {\r
1876                                                                 // Skip br element\r
1877                                                                 if (i)\r
1878                                                                         e.appendChild(n);\r
1879                                                         });\r
1880                                                 }\r
1881                                         };\r
1882 \r
1883                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1884                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1885                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1886                                         if (t.settings.fix_ie_paragraphs)\r
1887                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
1888 \r
1889                                         set();\r
1890 \r
1891                                         if (t.settings.fix_ie_paragraphs) {\r
1892                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
1893                                                 nl = e.getElementsByTagName("p");\r
1894                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1895                                                         n = nl[i];\r
1896 \r
1897                                                         if (!n.hasChildNodes()) {\r
1898                                                                 if (!n._mce_keep) {\r
1899                                                                         x = 1; // Is broken\r
1900                                                                         break;\r
1901                                                                 }\r
1902 \r
1903                                                                 n.removeAttribute('_mce_keep');\r
1904                                                         }\r
1905                                                 }\r
1906                                         }\r
1907 \r
1908                                         // Time to fix the madness IE left us\r
1909                                         if (x) {\r
1910                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1911                                                 // after we use innerHTML we can fix the DOM tree\r
1912                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
1913                                                 h = h.replace(/<\/p>/gi, '</div>');\r
1914 \r
1915                                                 // Set the new HTML with DIVs\r
1916                                                 set();\r
1917 \r
1918                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
1919                                                 // This is needed since IE has a annoying bug see above for details\r
1920                                                 // This is a slow process but it has to be done. :(\r
1921                                                 if (t.settings.fix_ie_paragraphs) {\r
1922                                                         nl = e.getElementsByTagName("DIV");\r
1923                                                         for (i = nl.length - 1; i >= 0; i--) {\r
1924                                                                 n = nl[i];\r
1925 \r
1926                                                                 // Is it a temp div\r
1927                                                                 if (n._mce_tmp) {\r
1928                                                                         // Create new paragraph\r
1929                                                                         p = t.doc.createElement('p');\r
1930 \r
1931                                                                         // Copy all attributes\r
1932                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1933                                                                                 var v;\r
1934 \r
1935                                                                                 if (b !== '_mce_tmp') {\r
1936                                                                                         v = n.getAttribute(b);\r
1937 \r
1938                                                                                         if (!v && b === 'class')\r
1939                                                                                                 v = n.className;\r
1940 \r
1941                                                                                         p.setAttribute(b, v);\r
1942                                                                                 }\r
1943                                                                         });\r
1944 \r
1945                                                                         // Append all children to new paragraph\r
1946                                                                         for (x = 0; x<n.childNodes.length; x++)\r
1947                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
1948 \r
1949                                                                         // Replace div with new paragraph\r
1950                                                                         n.swapNode(p);\r
1951                                                                 }\r
1952                                                         }\r
1953                                                 }\r
1954                                         }\r
1955                                 } else\r
1956                                         e.innerHTML = h;\r
1957 \r
1958                                 return h;\r
1959                         });\r
1960                 },\r
1961 \r
1962                 processHTML : function(h) {\r
1963                         var t = this, s = t.settings, codeBlocks = [];\r
1964 \r
1965                         if (!s.process_html)\r
1966                                 return h;\r
1967 \r
1968                         if (isIE) {\r
1969                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
1970                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
1971                         }\r
1972 \r
1973                         // Fix some issues\r
1974                         h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
1975 \r
1976                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
1977                         if (s.keep_values) {\r
1978                                 // Wrap scripts and styles in comments for serialization purposes\r
1979                                 if (/<script|noscript|style/i.test(h)) {\r
1980                                         function trim(s) {\r
1981                                                 // Remove prefix and suffix code for element\r
1982                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
1983                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
1984                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
1985                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
1986 \r
1987                                                 return s;\r
1988                                         };\r
1989 \r
1990                                         // Wrap the script contents in CDATA and keep them from executing\r
1991                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
1992                                                 // Force type attribute\r
1993                                                 if (!attribs)\r
1994                                                         attribs = ' type="text/javascript"';\r
1995 \r
1996                                                 // Convert the src attribute of the scripts\r
1997                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
1998                                                         if (s.url_converter)\r
1999                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
2000 \r
2001                                                         return '_mce_src="' + url + '"';\r
2002                                                 });\r
2003 \r
2004                                                 // Wrap text contents\r
2005                                                 if (tinymce.trim(text)) {\r
2006                                                         codeBlocks.push(trim(text));\r
2007                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2008                                                 }\r
2009 \r
2010                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2011                                         });\r
2012 \r
2013                                         // Wrap style elements\r
2014                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2015                                                 // Wrap text contents\r
2016                                                 if (text) {\r
2017                                                         codeBlocks.push(trim(text));\r
2018                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2019                                                 }\r
2020 \r
2021                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2022                                         });\r
2023 \r
2024                                         // Wrap noscript elements\r
2025                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2026                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2027                                         });\r
2028                                 }\r
2029 \r
2030                                 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
2031 \r
2032                                 // This function processes the attributes in the HTML string to force boolean\r
2033                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2034                                 function processTags(html) {\r
2035                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2036                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2037                                                         var mceValue;\r
2038 \r
2039                                                         name = name.toLowerCase();\r
2040                                                         value = value || val2 || val3 || "";\r
2041 \r
2042                                                         // Treat boolean attributes\r
2043                                                         if (boolAttrs[name]) {\r
2044                                                                 // false or 0 is treated as a missing attribute\r
2045                                                                 if (value === 'false' || value === '0')\r
2046                                                                         return;\r
2047 \r
2048                                                                 return name + '="' + name + '"';\r
2049                                                         }\r
2050 \r
2051                                                         // Is attribute one that needs special treatment\r
2052                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2053                                                                 mceValue = t.decode(value);\r
2054 \r
2055                                                                 // Convert URLs to relative/absolute ones\r
2056                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2057                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2058 \r
2059                                                                 // Process styles lowercases them and compresses them\r
2060                                                                 if (name == 'style')\r
2061                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2062 \r
2063                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2064                                                         }\r
2065 \r
2066                                                         return match;\r
2067                                                 }) + end + '>';\r
2068                                         });\r
2069                                 };\r
2070 \r
2071                                 h = processTags(h);\r
2072 \r
2073                                 // Restore script blocks\r
2074                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2075                                         return codeBlocks[idx];\r
2076                                 });\r
2077                         }\r
2078 \r
2079                         return h;\r
2080                 },\r
2081 \r
2082                 getOuterHTML : function(e) {\r
2083                         var d;\r
2084 \r
2085                         e = this.get(e);\r
2086 \r
2087                         if (!e)\r
2088                                 return null;\r
2089 \r
2090                         if (e.outerHTML !== undefined)\r
2091                                 return e.outerHTML;\r
2092 \r
2093                         d = (e.ownerDocument || this.doc).createElement("body");\r
2094                         d.appendChild(e.cloneNode(true));\r
2095 \r
2096                         return d.innerHTML;\r
2097                 },\r
2098 \r
2099                 setOuterHTML : function(e, h, d) {\r
2100                         var t = this;\r
2101 \r
2102                         function setHTML(e, h, d) {\r
2103                                 var n, tp;\r
2104 \r
2105                                 tp = d.createElement("body");\r
2106                                 tp.innerHTML = h;\r
2107 \r
2108                                 n = tp.lastChild;\r
2109                                 while (n) {\r
2110                                         t.insertAfter(n.cloneNode(true), e);\r
2111                                         n = n.previousSibling;\r
2112                                 }\r
2113 \r
2114                                 t.remove(e);\r
2115                         };\r
2116 \r
2117                         return this.run(e, function(e) {\r
2118                                 e = t.get(e);\r
2119 \r
2120                                 // Only set HTML on elements\r
2121                                 if (e.nodeType == 1) {\r
2122                                         d = d || e.ownerDocument || t.doc;\r
2123 \r
2124                                         if (isIE) {\r
2125                                                 try {\r
2126                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2127                                                         if (isIE && e.nodeType == 1)\r
2128                                                                 e.outerHTML = h;\r
2129                                                         else\r
2130                                                                 setHTML(e, h, d);\r
2131                                                 } catch (ex) {\r
2132                                                         // Fix for unknown runtime error\r
2133                                                         setHTML(e, h, d);\r
2134                                                 }\r
2135                                         } else\r
2136                                                 setHTML(e, h, d);\r
2137                                 }\r
2138                         });\r
2139                 },\r
2140 \r
2141                 decode : function(s) {\r
2142                         var e, n, v;\r
2143 \r
2144                         // Look for entities to decode\r
2145                         if (/&[\w#]+;/.test(s)) {\r
2146                                 // Decode the entities using a div element not super efficient but less code\r
2147                                 e = this.doc.createElement("div");\r
2148                                 e.innerHTML = s;\r
2149                                 n = e.firstChild;\r
2150                                 v = '';\r
2151 \r
2152                                 if (n) {\r
2153                                         do {\r
2154                                                 v += n.nodeValue;\r
2155                                         } while (n = n.nextSibling);\r
2156                                 }\r
2157 \r
2158                                 return v || s;\r
2159                         }\r
2160 \r
2161                         return s;\r
2162                 },\r
2163 \r
2164                 encode : function(str) {\r
2165                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2166                                 return encodedChars[chr];\r
2167                         });\r
2168                 },\r
2169 \r
2170                 insertAfter : function(node, reference_node) {\r
2171                         reference_node = this.get(reference_node);\r
2172 \r
2173                         return this.run(node, function(node) {\r
2174                                 var parent, nextSibling;\r
2175 \r
2176                                 parent = reference_node.parentNode;\r
2177                                 nextSibling = reference_node.nextSibling;\r
2178 \r
2179                                 if (nextSibling)\r
2180                                         parent.insertBefore(node, nextSibling);\r
2181                                 else\r
2182                                         parent.appendChild(node);\r
2183 \r
2184                                 return node;\r
2185                         });\r
2186                 },\r
2187 \r
2188                 isBlock : function(n) {\r
2189                         if (n.nodeType && n.nodeType !== 1)\r
2190                                 return false;\r
2191 \r
2192                         n = n.nodeName || n;\r
2193 \r
2194                         return blockRe.test(n);\r
2195                 },\r
2196 \r
2197                 replace : function(n, o, k) {\r
2198                         var t = this;\r
2199 \r
2200                         if (is(o, 'array'))\r
2201                                 n = n.cloneNode(true);\r
2202 \r
2203                         return t.run(o, function(o) {\r
2204                                 if (k) {\r
2205                                         each(tinymce.grep(o.childNodes), function(c) {\r
2206                                                 n.appendChild(c);\r
2207                                         });\r
2208                                 }\r
2209 \r
2210                                 return o.parentNode.replaceChild(n, o);\r
2211                         });\r
2212                 },\r
2213 \r
2214                 rename : function(elm, name) {\r
2215                         var t = this, newElm;\r
2216 \r
2217                         if (elm.nodeName != name.toUpperCase()) {\r
2218                                 // Rename block element\r
2219                                 newElm = t.create(name);\r
2220 \r
2221                                 // Copy attribs to new block\r
2222                                 each(t.getAttribs(elm), function(attr_node) {\r
2223                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2224                                 });\r
2225 \r
2226                                 // Replace block\r
2227                                 t.replace(newElm, elm, 1);\r
2228                         }\r
2229 \r
2230                         return newElm || elm;\r
2231                 },\r
2232 \r
2233                 findCommonAncestor : function(a, b) {\r
2234                         var ps = a, pe;\r
2235 \r
2236                         while (ps) {\r
2237                                 pe = b;\r
2238 \r
2239                                 while (pe && ps != pe)\r
2240                                         pe = pe.parentNode;\r
2241 \r
2242                                 if (ps == pe)\r
2243                                         break;\r
2244 \r
2245                                 ps = ps.parentNode;\r
2246                         }\r
2247 \r
2248                         if (!ps && a.ownerDocument)\r
2249                                 return a.ownerDocument.documentElement;\r
2250 \r
2251                         return ps;\r
2252                 },\r
2253 \r
2254                 toHex : function(s) {\r
2255                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2256 \r
2257                         function hex(s) {\r
2258                                 s = parseInt(s).toString(16);\r
2259 \r
2260                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2261                         };\r
2262 \r
2263                         if (c) {\r
2264                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2265 \r
2266                                 return s;\r
2267                         }\r
2268 \r
2269                         return s;\r
2270                 },\r
2271 \r
2272                 getClasses : function() {\r
2273                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2274 \r
2275                         if (t.classes)\r
2276                                 return t.classes;\r
2277 \r
2278                         function addClasses(s) {\r
2279                                 // IE style imports\r
2280                                 each(s.imports, function(r) {\r
2281                                         addClasses(r);\r
2282                                 });\r
2283 \r
2284                                 each(s.cssRules || s.rules, function(r) {\r
2285                                         // Real type or fake it on IE\r
2286                                         switch (r.type || 1) {\r
2287                                                 // Rule\r
2288                                                 case 1:\r
2289                                                         if (r.selectorText) {\r
2290                                                                 each(r.selectorText.split(','), function(v) {\r
2291                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2292 \r
2293                                                                         // Is internal or it doesn't contain a class\r
2294                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2295                                                                                 return;\r
2296 \r
2297                                                                         // Remove everything but class name\r
2298                                                                         ov = v;\r
2299                                                                         v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2300 \r
2301                                                                         // Filter classes\r
2302                                                                         if (f && !(v = f(v, ov)))\r
2303                                                                                 return;\r
2304 \r
2305                                                                         if (!lo[v]) {\r
2306                                                                                 cl.push({'class' : v});\r
2307                                                                                 lo[v] = 1;\r
2308                                                                         }\r
2309                                                                 });\r
2310                                                         }\r
2311                                                         break;\r
2312 \r
2313                                                 // Import\r
2314                                                 case 3:\r
2315                                                         addClasses(r.styleSheet);\r
2316                                                         break;\r
2317                                         }\r
2318                                 });\r
2319                         };\r
2320 \r
2321                         try {\r
2322                                 each(t.doc.styleSheets, addClasses);\r
2323                         } catch (ex) {\r
2324                                 // Ignore\r
2325                         }\r
2326 \r
2327                         if (cl.length > 0)\r
2328                                 t.classes = cl;\r
2329 \r
2330                         return cl;\r
2331                 },\r
2332 \r
2333                 run : function(e, f, s) {\r
2334                         var t = this, o;\r
2335 \r
2336                         if (t.doc && typeof(e) === 'string')\r
2337                                 e = t.get(e);\r
2338 \r
2339                         if (!e)\r
2340                                 return false;\r
2341 \r
2342                         s = s || this;\r
2343                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2344                                 o = [];\r
2345 \r
2346                                 each(e, function(e, i) {\r
2347                                         if (e) {\r
2348                                                 if (typeof(e) == 'string')\r
2349                                                         e = t.doc.getElementById(e);\r
2350 \r
2351                                                 o.push(f.call(s, e, i));\r
2352                                         }\r
2353                                 });\r
2354 \r
2355                                 return o;\r
2356                         }\r
2357 \r
2358                         return f.call(s, e);\r
2359                 },\r
2360 \r
2361                 getAttribs : function(n) {\r
2362                         var o;\r
2363 \r
2364                         n = this.get(n);\r
2365 \r
2366                         if (!n)\r
2367                                 return [];\r
2368 \r
2369                         if (isIE) {\r
2370                                 o = [];\r
2371 \r
2372                                 // Object will throw exception in IE\r
2373                                 if (n.nodeName == 'OBJECT')\r
2374                                         return n.attributes;\r
2375 \r
2376                                 // IE doesn't keep the selected attribute if you clone option elements\r
2377                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2378                                         o.push({specified : 1, nodeName : 'selected'});\r
2379 \r
2380                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2381                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2382                                         o.push({specified : 1, nodeName : a});\r
2383                                 });\r
2384 \r
2385                                 return o;\r
2386                         }\r
2387 \r
2388                         return n.attributes;\r
2389                 },\r
2390 \r
2391                 destroy : function(s) {\r
2392                         var t = this;\r
2393 \r
2394                         if (t.events)\r
2395                                 t.events.destroy();\r
2396 \r
2397                         t.win = t.doc = t.root = t.events = null;\r
2398 \r
2399                         // Manual destroy then remove unload handler\r
2400                         if (!s)\r
2401                                 tinymce.removeUnload(t.destroy);\r
2402                 },\r
2403 \r
2404                 createRng : function() {\r
2405                         var d = this.doc;\r
2406 \r
2407                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2408                 },\r
2409 \r
2410                 nodeIndex : function(node, normalized) {\r
2411                         var idx = 0, lastNodeType, lastNode, nodeType;\r
2412 \r
2413                         if (node) {\r
2414                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2415                                         nodeType = node.nodeType;\r
2416 \r
2417                                         // Normalize text nodes\r
2418                                         if (normalized && nodeType == 3) {\r
2419                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
2420                                                         continue;\r
2421                                         }\r
2422 \r
2423                                         idx++;\r
2424                                         lastNodeType = nodeType;\r
2425                                 }\r
2426                         }\r
2427 \r
2428                         return idx;\r
2429                 },\r
2430 \r
2431                 split : function(pe, e, re) {\r
2432                         var t = this, r = t.createRng(), bef, aft, pa;\r
2433 \r
2434                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2435                         // but we don't want that in our code since it serves no purpose for the end user\r
2436                         // For example if this is chopped:\r
2437                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2438                         // would produce:\r
2439                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2440                         // this function will then trim of empty edges and produce:\r
2441                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2442                         function trim(node) {\r
2443                                 var i, children = node.childNodes;\r
2444 \r
2445                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2446                                         return;\r
2447 \r
2448                                 for (i = children.length - 1; i >= 0; i--)\r
2449                                         trim(children[i]);\r
2450 \r
2451                                 if (node.nodeType != 9) {\r
2452                                         // Keep non whitespace text nodes\r
2453                                         if (node.nodeType == 3 && node.nodeValue.length > 0)\r
2454                                                 return;\r
2455 \r
2456                                         if (node.nodeType == 1) {\r
2457                                                 // If the only child is a bookmark then move it up\r
2458                                                 children = node.childNodes;\r
2459                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2460                                                         node.parentNode.insertBefore(children[0], node);\r
2461 \r
2462                                                 // Keep non empty elements or img, hr etc\r
2463                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2464                                                         return;\r
2465                                         }\r
2466 \r
2467                                         t.remove(node);\r
2468                                 }\r
2469 \r
2470                                 return node;\r
2471                         };\r
2472 \r
2473                         if (pe && e) {\r
2474                                 // Get before chunk\r
2475                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2476                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2477                                 bef = r.extractContents();\r
2478 \r
2479                                 // Get after chunk\r
2480                                 r = t.createRng();\r
2481                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2482                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2483                                 aft = r.extractContents();\r
2484 \r
2485                                 // Insert before chunk\r
2486                                 pa = pe.parentNode;\r
2487                                 pa.insertBefore(trim(bef), pe);\r
2488 \r
2489                                 // Insert middle chunk\r
2490                                 if (re)\r
2491                                         pa.replaceChild(re, e);\r
2492                                 else\r
2493                                         pa.insertBefore(e, pe);\r
2494 \r
2495                                 // Insert after chunk\r
2496                                 pa.insertBefore(trim(aft), pe);\r
2497                                 t.remove(pe);\r
2498 \r
2499                                 return re || e;\r
2500                         }\r
2501                 },\r
2502 \r
2503                 bind : function(target, name, func, scope) {\r
2504                         var t = this;\r
2505 \r
2506                         if (!t.events)\r
2507                                 t.events = new tinymce.dom.EventUtils();\r
2508 \r
2509                         return t.events.add(target, name, func, scope || this);\r
2510                 },\r
2511 \r
2512                 unbind : function(target, name, func) {\r
2513                         var t = this;\r
2514 \r
2515                         if (!t.events)\r
2516                                 t.events = new tinymce.dom.EventUtils();\r
2517 \r
2518                         return t.events.remove(target, name, func);\r
2519                 },\r
2520 \r
2521 \r
2522                 _findSib : function(node, selector, name) {\r
2523                         var t = this, f = selector;\r
2524 \r
2525                         if (node) {\r
2526                                 // If expression make a function of it using is\r
2527                                 if (is(f, 'string')) {\r
2528                                         f = function(node) {\r
2529                                                 return t.is(node, selector);\r
2530                                         };\r
2531                                 }\r
2532 \r
2533                                 // Loop all siblings\r
2534                                 for (node = node[name]; node; node = node[name]) {\r
2535                                         if (f(node))\r
2536                                                 return node;\r
2537                                 }\r
2538                         }\r
2539 \r
2540                         return null;\r
2541                 },\r
2542 \r
2543                 _isRes : function(c) {\r
2544                         // Is live resizble element\r
2545                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2546                 }\r
2547 \r
2548                 /*\r
2549                 walk : function(n, f, s) {\r
2550                         var d = this.doc, w;\r
2551 \r
2552                         if (d.createTreeWalker) {\r
2553                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2554 \r
2555                                 while ((n = w.nextNode()) != null)\r
2556                                         f.call(s || this, n);\r
2557                         } else\r
2558                                 tinymce.walk(n, f, 'childNodes', s);\r
2559                 }\r
2560                 */\r
2561 \r
2562                 /*\r
2563                 toRGB : function(s) {\r
2564                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2565 \r
2566                         if (c) {\r
2567                                 // #FFF -> #FFFFFF\r
2568                                 if (!is(c[3]))\r
2569                                         c[3] = c[2] = c[1];\r
2570 \r
2571                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2572                         }\r
2573 \r
2574                         return s;\r
2575                 }\r
2576                 */\r
2577         });\r
2578 \r
2579         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2580 })(tinymce);\r
2581 \r
2582 (function(ns) {\r
2583         // Range constructor\r
2584         function Range(dom) {\r
2585                 var t = this,\r
2586                         doc = dom.doc,\r
2587                         EXTRACT = 0,\r
2588                         CLONE = 1,\r
2589                         DELETE = 2,\r
2590                         TRUE = true,\r
2591                         FALSE = false,\r
2592                         START_OFFSET = 'startOffset',\r
2593                         START_CONTAINER = 'startContainer',\r
2594                         END_CONTAINER = 'endContainer',\r
2595                         END_OFFSET = 'endOffset',\r
2596                         extend = tinymce.extend,\r
2597                         nodeIndex = dom.nodeIndex;\r
2598 \r
2599                 extend(t, {\r
2600                         // Inital states\r
2601                         startContainer : doc,\r
2602                         startOffset : 0,\r
2603                         endContainer : doc,\r
2604                         endOffset : 0,\r
2605                         collapsed : TRUE,\r
2606                         commonAncestorContainer : doc,\r
2607 \r
2608                         // Range constants\r
2609                         START_TO_START : 0,\r
2610                         START_TO_END : 1,\r
2611                         END_TO_END : 2,\r
2612                         END_TO_START : 3,\r
2613 \r
2614                         // Public methods\r
2615                         setStart : setStart,\r
2616                         setEnd : setEnd,\r
2617                         setStartBefore : setStartBefore,\r
2618                         setStartAfter : setStartAfter,\r
2619                         setEndBefore : setEndBefore,\r
2620                         setEndAfter : setEndAfter,\r
2621                         collapse : collapse,\r
2622                         selectNode : selectNode,\r
2623                         selectNodeContents : selectNodeContents,\r
2624                         compareBoundaryPoints : compareBoundaryPoints,\r
2625                         deleteContents : deleteContents,\r
2626                         extractContents : extractContents,\r
2627                         cloneContents : cloneContents,\r
2628                         insertNode : insertNode,\r
2629                         surroundContents : surroundContents,\r
2630                         cloneRange : cloneRange\r
2631                 });\r
2632 \r
2633                 function setStart(n, o) {\r
2634                         _setEndPoint(TRUE, n, o);\r
2635                 };\r
2636 \r
2637                 function setEnd(n, o) {\r
2638                         _setEndPoint(FALSE, n, o);\r
2639                 };\r
2640 \r
2641                 function setStartBefore(n) {\r
2642                         setStart(n.parentNode, nodeIndex(n));\r
2643                 };\r
2644 \r
2645                 function setStartAfter(n) {\r
2646                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2647                 };\r
2648 \r
2649                 function setEndBefore(n) {\r
2650                         setEnd(n.parentNode, nodeIndex(n));\r
2651                 };\r
2652 \r
2653                 function setEndAfter(n) {\r
2654                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2655                 };\r
2656 \r
2657                 function collapse(ts) {\r
2658                         if (ts) {\r
2659                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2660                                 t[END_OFFSET] = t[START_OFFSET];\r
2661                         } else {\r
2662                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2663                                 t[START_OFFSET] = t[END_OFFSET];\r
2664                         }\r
2665 \r
2666                         t.collapsed = TRUE;\r
2667                 };\r
2668 \r
2669                 function selectNode(n) {\r
2670                         setStartBefore(n);\r
2671                         setEndAfter(n);\r
2672                 };\r
2673 \r
2674                 function selectNodeContents(n) {\r
2675                         setStart(n, 0);\r
2676                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2677                 };\r
2678 \r
2679                 function compareBoundaryPoints(h, r) {\r
2680                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2681 \r
2682                         // Check START_TO_START\r
2683                         if (h === 0)\r
2684                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2685 \r
2686                         // Check START_TO_END\r
2687                         if (h === 1)\r
2688                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2689 \r
2690                         // Check END_TO_END\r
2691                         if (h === 2)\r
2692                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2693 \r
2694                         // Check END_TO_START\r
2695                         if (h === 3)\r
2696                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2697                 };\r
2698 \r
2699                 function deleteContents() {\r
2700                         _traverse(DELETE);\r
2701                 };\r
2702 \r
2703                 function extractContents() {\r
2704                         return _traverse(EXTRACT);\r
2705                 };\r
2706 \r
2707                 function cloneContents() {\r
2708                         return _traverse(CLONE);\r
2709                 };\r
2710 \r
2711                 function insertNode(n) {\r
2712                         var startContainer = this[START_CONTAINER],\r
2713                                 startOffset = this[START_OFFSET], nn, o;\r
2714 \r
2715                         // Node is TEXT_NODE or CDATA\r
2716                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2717                                 if (!startOffset) {\r
2718                                         // At the start of text\r
2719                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2720                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2721                                         // At the end of text\r
2722                                         dom.insertAfter(n, startContainer);\r
2723                                 } else {\r
2724                                         // Middle, need to split\r
2725                                         nn = startContainer.splitText(startOffset);\r
2726                                         startContainer.parentNode.insertBefore(n, nn);\r
2727                                 }\r
2728                         } else {\r
2729                                 // Insert element node\r
2730                                 if (startContainer.childNodes.length > 0)\r
2731                                         o = startContainer.childNodes[startOffset];\r
2732 \r
2733                                 if (o)\r
2734                                         startContainer.insertBefore(n, o);\r
2735                                 else\r
2736                                         startContainer.appendChild(n);\r
2737                         }\r
2738                 };\r
2739 \r
2740                 function surroundContents(n) {\r
2741                         var f = t.extractContents();\r
2742 \r
2743                         t.insertNode(n);\r
2744                         n.appendChild(f);\r
2745                         t.selectNode(n);\r
2746                 };\r
2747 \r
2748                 function cloneRange() {\r
2749                         return extend(new Range(dom), {\r
2750                                 startContainer : t[START_CONTAINER],\r
2751                                 startOffset : t[START_OFFSET],\r
2752                                 endContainer : t[END_CONTAINER],\r
2753                                 endOffset : t[END_OFFSET],\r
2754                                 collapsed : t.collapsed,\r
2755                                 commonAncestorContainer : t.commonAncestorContainer\r
2756                         });\r
2757                 };\r
2758 \r
2759                 // Private methods\r
2760 \r
2761                 function _getSelectedNode(container, offset) {\r
2762                         var child;\r
2763 \r
2764                         if (container.nodeType == 3 /* TEXT_NODE */)\r
2765                                 return container;\r
2766 \r
2767                         if (offset < 0)\r
2768                                 return container;\r
2769 \r
2770                         child = container.firstChild;\r
2771                         while (child && offset > 0) {\r
2772                                 --offset;\r
2773                                 child = child.nextSibling;\r
2774                         }\r
2775 \r
2776                         if (child)\r
2777                                 return child;\r
2778 \r
2779                         return container;\r
2780                 };\r
2781 \r
2782                 function _isCollapsed() {\r
2783                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
2784                 };\r
2785 \r
2786                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
2787                         var c, offsetC, n, cmnRoot, childA, childB;\r
2788 \r
2789                         // In the first case the boundary-points have the same container. A is before B\r
2790                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
2791                         // equal to the offset of B, and A is after B if its offset is greater than the\r
2792                         // offset of B.\r
2793                         if (containerA == containerB) {\r
2794                                 if (offsetA == offsetB)\r
2795                                         return 0; // equal\r
2796 \r
2797                                 if (offsetA < offsetB)\r
2798                                         return -1; // before\r
2799 \r
2800                                 return 1; // after\r
2801                         }\r
2802 \r
2803                         // In the second case a child node C of the container of A is an ancestor\r
2804                         // container of B. In this case, A is before B if the offset of A is less than or\r
2805                         // equal to the index of the child node C and A is after B otherwise.\r
2806                         c = containerB;\r
2807                         while (c && c.parentNode != containerA)\r
2808                                 c = c.parentNode;\r
2809 \r
2810                         if (c) {\r
2811                                 offsetC = 0;\r
2812                                 n = containerA.firstChild;\r
2813 \r
2814                                 while (n != c && offsetC < offsetA) {\r
2815                                         offsetC++;\r
2816                                         n = n.nextSibling;\r
2817                                 }\r
2818 \r
2819                                 if (offsetA <= offsetC)\r
2820                                         return -1; // before\r
2821 \r
2822                                 return 1; // after\r
2823                         }\r
2824 \r
2825                         // In the third case a child node C of the container of B is an ancestor container\r
2826                         // of A. In this case, A is before B if the index of the child node C is less than\r
2827                         // the offset of B and A is after B otherwise.\r
2828                         c = containerA;\r
2829                         while (c && c.parentNode != containerB) {\r
2830                                 c = c.parentNode;\r
2831                         }\r
2832 \r
2833                         if (c) {\r
2834                                 offsetC = 0;\r
2835                                 n = containerB.firstChild;\r
2836 \r
2837                                 while (n != c && offsetC < offsetB) {\r
2838                                         offsetC++;\r
2839                                         n = n.nextSibling;\r
2840                                 }\r
2841 \r
2842                                 if (offsetC < offsetB)\r
2843                                         return -1; // before\r
2844 \r
2845                                 return 1; // after\r
2846                         }\r
2847 \r
2848                         // In the fourth case, none of three other cases hold: the containers of A and B\r
2849                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
2850                         // the container of A is before the container of B in a pre-order traversal of the\r
2851                         // Ranges' context tree and A is after B otherwise.\r
2852                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
2853                         childA = containerA;\r
2854 \r
2855                         while (childA && childA.parentNode != cmnRoot)\r
2856                                 childA = childA.parentNode;\r
2857 \r
2858                         if (!childA)\r
2859                                 childA = cmnRoot;\r
2860 \r
2861                         childB = containerB;\r
2862                         while (childB && childB.parentNode != cmnRoot)\r
2863                                 childB = childB.parentNode;\r
2864 \r
2865                         if (!childB)\r
2866                                 childB = cmnRoot;\r
2867 \r
2868                         if (childA == childB)\r
2869                                 return 0; // equal\r
2870 \r
2871                         n = cmnRoot.firstChild;\r
2872                         while (n) {\r
2873                                 if (n == childA)\r
2874                                         return -1; // before\r
2875 \r
2876                                 if (n == childB)\r
2877                                         return 1; // after\r
2878 \r
2879                                 n = n.nextSibling;\r
2880                         }\r
2881                 };\r
2882 \r
2883                 function _setEndPoint(st, n, o) {\r
2884                         var ec, sc;\r
2885 \r
2886                         if (st) {\r
2887                                 t[START_CONTAINER] = n;\r
2888                                 t[START_OFFSET] = o;\r
2889                         } else {\r
2890                                 t[END_CONTAINER] = n;\r
2891                                 t[END_OFFSET] = o;\r
2892                         }\r
2893 \r
2894                         // If one boundary-point of a Range is set to have a root container\r
2895                         // other than the current one for the Range, the Range is collapsed to\r
2896                         // the new position. This enforces the restriction that both boundary-\r
2897                         // points of a Range must have the same root container.\r
2898                         ec = t[END_CONTAINER];\r
2899                         while (ec.parentNode)\r
2900                                 ec = ec.parentNode;\r
2901 \r
2902                         sc = t[START_CONTAINER];\r
2903                         while (sc.parentNode)\r
2904                                 sc = sc.parentNode;\r
2905 \r
2906                         if (sc == ec) {\r
2907                                 // The start position of a Range is guaranteed to never be after the\r
2908                                 // end position. To enforce this restriction, if the start is set to\r
2909                                 // be at a position after the end, the Range is collapsed to that\r
2910                                 // position.\r
2911                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
2912                                         t.collapse(st);\r
2913                         } else\r
2914                                 t.collapse(st);\r
2915 \r
2916                         t.collapsed = _isCollapsed();\r
2917                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
2918                 };\r
2919 \r
2920                 function _traverse(how) {\r
2921                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2922 \r
2923                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
2924                                 return _traverseSameContainer(how);\r
2925 \r
2926                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2927                                 if (p == t[START_CONTAINER])\r
2928                                         return _traverseCommonStartContainer(c, how);\r
2929 \r
2930                                 ++endContainerDepth;\r
2931                         }\r
2932 \r
2933                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2934                                 if (p == t[END_CONTAINER])\r
2935                                         return _traverseCommonEndContainer(c, how);\r
2936 \r
2937                                 ++startContainerDepth;\r
2938                         }\r
2939 \r
2940                         depthDiff = startContainerDepth - endContainerDepth;\r
2941 \r
2942                         startNode = t[START_CONTAINER];\r
2943                         while (depthDiff > 0) {\r
2944                                 startNode = startNode.parentNode;\r
2945                                 depthDiff--;\r
2946                         }\r
2947 \r
2948                         endNode = t[END_CONTAINER];\r
2949                         while (depthDiff < 0) {\r
2950                                 endNode = endNode.parentNode;\r
2951                                 depthDiff++;\r
2952                         }\r
2953 \r
2954                         // ascend the ancestor hierarchy until we have a common parent.\r
2955                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
2956                                 startNode = sp;\r
2957                                 endNode = ep;\r
2958                         }\r
2959 \r
2960                         return _traverseCommonAncestors(startNode, endNode, how);\r
2961                 };\r
2962 \r
2963                  function _traverseSameContainer(how) {\r
2964                         var frag, s, sub, n, cnt, sibling, xferNode;\r
2965 \r
2966                         if (how != DELETE)\r
2967                                 frag = doc.createDocumentFragment();\r
2968 \r
2969                         // If selection is empty, just return the fragment\r
2970                         if (t[START_OFFSET] == t[END_OFFSET])\r
2971                                 return frag;\r
2972 \r
2973                         // Text node needs special case handling\r
2974                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
2975                                 // get the substring\r
2976                                 s = t[START_CONTAINER].nodeValue;\r
2977                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
2978 \r
2979                                 // set the original text node to its new value\r
2980                                 if (how != CLONE) {\r
2981                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
2982 \r
2983                                         // Nothing is partially selected, so collapse to start point\r
2984                                         t.collapse(TRUE);\r
2985                                 }\r
2986 \r
2987                                 if (how == DELETE)\r
2988                                         return;\r
2989 \r
2990                                 frag.appendChild(doc.createTextNode(sub));\r
2991                                 return frag;\r
2992                         }\r
2993 \r
2994                         // Copy nodes between the start/end offsets.\r
2995                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
2996                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
2997 \r
2998                         while (cnt > 0) {\r
2999                                 sibling = n.nextSibling;\r
3000                                 xferNode = _traverseFullySelected(n, how);\r
3001 \r
3002                                 if (frag)\r
3003                                         frag.appendChild( xferNode );\r
3004 \r
3005                                 --cnt;\r
3006                                 n = sibling;\r
3007                         }\r
3008 \r
3009                         // Nothing is partially selected, so collapse to start point\r
3010                         if (how != CLONE)\r
3011                                 t.collapse(TRUE);\r
3012 \r
3013                         return frag;\r
3014                 };\r
3015 \r
3016                 function _traverseCommonStartContainer(endAncestor, how) {\r
3017                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3018 \r
3019                         if (how != DELETE)\r
3020                                 frag = doc.createDocumentFragment();\r
3021 \r
3022                         n = _traverseRightBoundary(endAncestor, how);\r
3023 \r
3024                         if (frag)\r
3025                                 frag.appendChild(n);\r
3026 \r
3027                         endIdx = nodeIndex(endAncestor);\r
3028                         cnt = endIdx - t[START_OFFSET];\r
3029 \r
3030                         if (cnt <= 0) {\r
3031                                 // Collapse to just before the endAncestor, which\r
3032                                 // is partially selected.\r
3033                                 if (how != CLONE) {\r
3034                                         t.setEndBefore(endAncestor);\r
3035                                         t.collapse(FALSE);\r
3036                                 }\r
3037 \r
3038                                 return frag;\r
3039                         }\r
3040 \r
3041                         n = endAncestor.previousSibling;\r
3042                         while (cnt > 0) {\r
3043                                 sibling = n.previousSibling;\r
3044                                 xferNode = _traverseFullySelected(n, how);\r
3045 \r
3046                                 if (frag)\r
3047                                         frag.insertBefore(xferNode, frag.firstChild);\r
3048 \r
3049                                 --cnt;\r
3050                                 n = sibling;\r
3051                         }\r
3052 \r
3053                         // Collapse to just before the endAncestor, which\r
3054                         // is partially selected.\r
3055                         if (how != CLONE) {\r
3056                                 t.setEndBefore(endAncestor);\r
3057                                 t.collapse(FALSE);\r
3058                         }\r
3059 \r
3060                         return frag;\r
3061                 };\r
3062 \r
3063                 function _traverseCommonEndContainer(startAncestor, how) {\r
3064                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3065 \r
3066                         if (how != DELETE)\r
3067                                 frag = doc.createDocumentFragment();\r
3068 \r
3069                         n = _traverseLeftBoundary(startAncestor, how);\r
3070                         if (frag)\r
3071                                 frag.appendChild(n);\r
3072 \r
3073                         startIdx = nodeIndex(startAncestor);\r
3074                         ++startIdx;  // Because we already traversed it....\r
3075 \r
3076                         cnt = t[END_OFFSET] - startIdx;\r
3077                         n = startAncestor.nextSibling;\r
3078                         while (cnt > 0) {\r
3079                                 sibling = n.nextSibling;\r
3080                                 xferNode = _traverseFullySelected(n, how);\r
3081 \r
3082                                 if (frag)\r
3083                                         frag.appendChild(xferNode);\r
3084 \r
3085                                 --cnt;\r
3086                                 n = sibling;\r
3087                         }\r
3088 \r
3089                         if (how != CLONE) {\r
3090                                 t.setStartAfter(startAncestor);\r
3091                                 t.collapse(TRUE);\r
3092                         }\r
3093 \r
3094                         return frag;\r
3095                 };\r
3096 \r
3097                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3098                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3099 \r
3100                         if (how != DELETE)\r
3101                                 frag = doc.createDocumentFragment();\r
3102 \r
3103                         n = _traverseLeftBoundary(startAncestor, how);\r
3104                         if (frag)\r
3105                                 frag.appendChild(n);\r
3106 \r
3107                         commonParent = startAncestor.parentNode;\r
3108                         startOffset = nodeIndex(startAncestor);\r
3109                         endOffset = nodeIndex(endAncestor);\r
3110                         ++startOffset;\r
3111 \r
3112                         cnt = endOffset - startOffset;\r
3113                         sibling = startAncestor.nextSibling;\r
3114 \r
3115                         while (cnt > 0) {\r
3116                                 nextSibling = sibling.nextSibling;\r
3117                                 n = _traverseFullySelected(sibling, how);\r
3118 \r
3119                                 if (frag)\r
3120                                         frag.appendChild(n);\r
3121 \r
3122                                 sibling = nextSibling;\r
3123                                 --cnt;\r
3124                         }\r
3125 \r
3126                         n = _traverseRightBoundary(endAncestor, how);\r
3127 \r
3128                         if (frag)\r
3129                                 frag.appendChild(n);\r
3130 \r
3131                         if (how != CLONE) {\r
3132                                 t.setStartAfter(startAncestor);\r
3133                                 t.collapse(TRUE);\r
3134                         }\r
3135 \r
3136                         return frag;\r
3137                 };\r
3138 \r
3139                 function _traverseRightBoundary(root, how) {\r
3140                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3141 \r
3142                         if (next == root)\r
3143                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3144 \r
3145                         parent = next.parentNode;\r
3146                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3147 \r
3148                         while (parent) {\r
3149                                 while (next) {\r
3150                                         prevSibling = next.previousSibling;\r
3151                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3152 \r
3153                                         if (how != DELETE)\r
3154                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3155 \r
3156                                         isFullySelected = TRUE;\r
3157                                         next = prevSibling;\r
3158                                 }\r
3159 \r
3160                                 if (parent == root)\r
3161                                         return clonedParent;\r
3162 \r
3163                                 next = parent.previousSibling;\r
3164                                 parent = parent.parentNode;\r
3165 \r
3166                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3167 \r
3168                                 if (how != DELETE)\r
3169                                         clonedGrandParent.appendChild(clonedParent);\r
3170 \r
3171                                 clonedParent = clonedGrandParent;\r
3172                         }\r
3173                 };\r
3174 \r
3175                 function _traverseLeftBoundary(root, how) {\r
3176                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3177 \r
3178                         if (next == root)\r
3179                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3180 \r
3181                         parent = next.parentNode;\r
3182                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3183 \r
3184                         while (parent) {\r
3185                                 while (next) {\r
3186                                         nextSibling = next.nextSibling;\r
3187                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3188 \r
3189                                         if (how != DELETE)\r
3190                                                 clonedParent.appendChild(clonedChild);\r
3191 \r
3192                                         isFullySelected = TRUE;\r
3193                                         next = nextSibling;\r
3194                                 }\r
3195 \r
3196                                 if (parent == root)\r
3197                                         return clonedParent;\r
3198 \r
3199                                 next = parent.nextSibling;\r
3200                                 parent = parent.parentNode;\r
3201 \r
3202                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3203 \r
3204                                 if (how != DELETE)\r
3205                                         clonedGrandParent.appendChild(clonedParent);\r
3206 \r
3207                                 clonedParent = clonedGrandParent;\r
3208                         }\r
3209                 };\r
3210 \r
3211                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3212                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3213 \r
3214                         if (isFullySelected)\r
3215                                 return _traverseFullySelected(n, how);\r
3216 \r
3217                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3218                                 txtValue = n.nodeValue;\r
3219 \r
3220                                 if (isLeft) {\r
3221                                         offset = t[START_OFFSET];\r
3222                                         newNodeValue = txtValue.substring(offset);\r
3223                                         oldNodeValue = txtValue.substring(0, offset);\r
3224                                 } else {\r
3225                                         offset = t[END_OFFSET];\r
3226                                         newNodeValue = txtValue.substring(0, offset);\r
3227                                         oldNodeValue = txtValue.substring(offset);\r
3228                                 }\r
3229 \r
3230                                 if (how != CLONE)\r
3231                                         n.nodeValue = oldNodeValue;\r
3232 \r
3233                                 if (how == DELETE)\r
3234                                         return;\r
3235 \r
3236                                 newNode = n.cloneNode(FALSE);\r
3237                                 newNode.nodeValue = newNodeValue;\r
3238 \r
3239                                 return newNode;\r
3240                         }\r
3241 \r
3242                         if (how == DELETE)\r
3243                                 return;\r
3244 \r
3245                         return n.cloneNode(FALSE);\r
3246                 };\r
3247 \r
3248                 function _traverseFullySelected(n, how) {\r
3249                         if (how != DELETE)\r
3250                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3251 \r
3252                         n.parentNode.removeChild(n);\r
3253                 };\r
3254         };\r
3255 \r
3256         ns.Range = Range;\r
3257 })(tinymce.dom);\r
3258 \r
3259 (function() {\r
3260         function Selection(selection) {\r
3261                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3262 \r
3263                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3264                 function getRange() {\r
3265                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
3266 \r
3267                         // If selection is outside the current document just return an empty range\r
3268                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3269                         if (element.ownerDocument != dom.doc)\r
3270                                 return domRange;\r
3271 \r
3272                         // Handle control selection or text selection of a image\r
3273                         if (ieRange.item || !element.hasChildNodes()) {\r
3274                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3275                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3276 \r
3277                                 return domRange;\r
3278                         }\r
3279 \r
3280                         collapsed = selection.isCollapsed();\r
3281 \r
3282                         function findEndPoint(start) {\r
3283                                 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
3284 \r
3285                                 // Setup temp range and collapse it\r
3286                                 checkRng = ieRange.duplicate();\r
3287                                 checkRng.collapse(start);\r
3288 \r
3289                                 // Create marker and insert it at the end of the endpoints parent\r
3290                                 marker = dom.create('a');\r
3291                                 parent = checkRng.parentElement();\r
3292 \r
3293                                 // If parent doesn't have any children then set the container to that parent and the index to 0\r
3294                                 if (!parent.hasChildNodes()) {\r
3295                                         domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
3296                                         return;\r
3297                                 }\r
3298 \r
3299                                 parent.appendChild(marker);\r
3300                                 checkRng.moveToElementText(marker);\r
3301                                 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3302                                 if (position > 0) {\r
3303                                         // The position is after the end of the parent element.\r
3304                                         // This is the case where IE puts the caret to the left edge of a table.\r
3305                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
3306                                         dom.remove(marker);\r
3307                                         return;\r
3308                                 }\r
3309 \r
3310                                 // Setup node list and endIndex\r
3311                                 nodes = tinymce.grep(parent.childNodes);\r
3312                                 endIndex = nodes.length - 1;\r
3313                                 // Perform a binary search for the position\r
3314                                 while (startIndex <= endIndex) {\r
3315                                         index = Math.floor((startIndex + endIndex) / 2);\r
3316 \r
3317                                         // Insert marker and check it's position relative to the selection\r
3318                                         parent.insertBefore(marker, nodes[index]);\r
3319                                         checkRng.moveToElementText(marker);\r
3320                                         position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3321                                         if (position > 0) {\r
3322                                                 // Marker is to the right\r
3323                                                 startIndex = index + 1;\r
3324                                         } else if (position < 0) {\r
3325                                                 // Marker is to the left\r
3326                                                 endIndex = index - 1;\r
3327                                         } else {\r
3328                                                 // Maker is where we are\r
3329                                                 found = true;\r
3330                                                 break;\r
3331                                         }\r
3332                                 }\r
3333 \r
3334                                 // Setup container\r
3335                                 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
3336 \r
3337                                 // Handle element selection\r
3338                                 if (container.nodeType == 1) {\r
3339                                         dom.remove(marker);\r
3340 \r
3341                                         // Find offset and container\r
3342                                         offset = dom.nodeIndex(container);\r
3343                                         container = container.parentNode;\r
3344 \r
3345                                         // Move the offset if we are setting the end or the position is after an element\r
3346                                         if (!start || index > 0)\r
3347                                                 offset++;\r
3348                                 } else {\r
3349                                         // Calculate offset within text node\r
3350                                         if (position > 0 || index == 0) {\r
3351                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3352                                                 offset = checkRng.text.length;\r
3353                                         } else {\r
3354                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3355                                                 offset = container.nodeValue.length - checkRng.text.length;\r
3356                                         }\r
3357 \r
3358                                         dom.remove(marker);\r
3359                                 }\r
3360 \r
3361                                 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
3362                         };\r
3363 \r
3364                         // Find start point\r
3365                         findEndPoint(true);\r
3366 \r
3367                         // Find end point if needed\r
3368                         if (!collapsed)\r
3369                                 findEndPoint();\r
3370 \r
3371                         return domRange;\r
3372                 };\r
3373 \r
3374                 this.addRange = function(rng) {\r
3375                         var ieRng, ieRng2, doc = selection.dom.doc, body = doc.body, startPos, endPos, sc, so, ec, eo, marker, lastIndex, skipStart, skipEnd;\r
3376 \r
3377                         this.destroy();\r
3378 \r
3379                         // Setup some shorter versions\r
3380                         sc = rng.startContainer;\r
3381                         so = rng.startOffset;\r
3382                         ec = rng.endContainer;\r
3383                         eo = rng.endOffset;\r
3384                         ieRng = body.createTextRange();\r
3385 \r
3386                         // If document selection move caret to first node in document\r
3387                         if (sc == doc || ec == doc) {\r
3388                                 ieRng = body.createTextRange();\r
3389                                 ieRng.collapse();\r
3390                                 ieRng.select();\r
3391                                 return;\r
3392                         }\r
3393 \r
3394                         // If child index resolve it\r
3395                         if (sc.nodeType == 1 && sc.hasChildNodes()) {\r
3396                                 lastIndex = sc.childNodes.length - 1;\r
3397 \r
3398                                 // Index is higher that the child count then we need to jump over the start container\r
3399                                 if (so > lastIndex) {\r
3400                                         skipStart = 1;\r
3401                                         sc = sc.childNodes[lastIndex];\r
3402                                 } else\r
3403                                         sc = sc.childNodes[so];\r
3404 \r
3405                                 // Child was text node then move offset to start of it\r
3406                                 if (sc.nodeType == 3)\r
3407                                         so = 0;\r
3408                         }\r
3409 \r
3410                         // If child index resolve it\r
3411                         if (ec.nodeType == 1 && ec.hasChildNodes()) {\r
3412                                 lastIndex = ec.childNodes.length - 1;\r
3413 \r
3414                                 if (eo == 0) {\r
3415                                         skipEnd = 1;\r
3416                                         ec = ec.childNodes[0];\r
3417                                 } else {\r
3418                                         ec = ec.childNodes[Math.min(lastIndex, eo - 1)];\r
3419 \r
3420                                         // Child was text node then move offset to end of text node\r
3421                                         if (ec.nodeType == 3)\r
3422                                                 eo = ec.nodeValue.length;\r
3423                                 }\r
3424                         }\r
3425 \r
3426                         // Single element selection\r
3427                         if (sc == ec && sc.nodeType == 1) {\r
3428                                 // Make control selection for some elements\r
3429                                 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {\r
3430                                         ieRng = body.createControlRange();\r
3431                                         ieRng.addElement(sc);\r
3432                                 } else {\r
3433                                         ieRng = body.createTextRange();\r
3434 \r
3435                                         // Padd empty elements with invisible character\r
3436                                         if (!sc.hasChildNodes() && sc.canHaveHTML)\r
3437                                                 sc.innerHTML = invisibleChar;\r
3438 \r
3439                                         // Select element contents\r
3440                                         ieRng.moveToElementText(sc);\r
3441 \r
3442                                         // If it's only containing a padding remove it so the caret remains\r
3443                                         if (sc.innerHTML == invisibleChar) {\r
3444                                                 ieRng.collapse(TRUE);\r
3445                                                 sc.removeChild(sc.firstChild);\r
3446                                         }\r
3447                                 }\r
3448 \r
3449                                 if (so == eo)\r
3450                                         ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);\r
3451 \r
3452                                 ieRng.select();\r
3453                                 ieRng.scrollIntoView();\r
3454                                 return;\r
3455                         }\r
3456 \r
3457                         // Create range and marker\r
3458                         ieRng = body.createTextRange();\r
3459                         marker = doc.createElement('span');\r
3460                         marker.innerHTML = ' ';\r
3461 \r
3462                         // Set start of range to startContainer/startOffset\r
3463                         if (sc.nodeType == 3) {\r
3464                                 // Insert marker after/before startContainer\r
3465                                 if (skipStart)\r
3466                                         dom.insertAfter(marker, sc);\r
3467                                 else\r
3468                                         sc.parentNode.insertBefore(marker, sc);\r
3469 \r
3470                                 // Select marker the caret to offset position\r
3471                                 ieRng.moveToElementText(marker);\r
3472                                 marker.parentNode.removeChild(marker);\r
3473 \r
3474                                 // Move if we need to, moving it 0 characters actually moves it!\r
3475                                 if (so > 0)\r
3476                                         ieRng.move('character', so);\r
3477                         } else {\r
3478                                 ieRng.moveToElementText(sc);\r
3479 \r
3480                                 if (skipStart)\r
3481                                         ieRng.collapse(FALSE);\r
3482                         }\r
3483 \r
3484                         // If same text container then we can do a more simple move\r
3485                         if (sc == ec && sc.nodeType == 3) {\r
3486                                 try {\r
3487                                         ieRng.moveEnd('character', eo - so);\r
3488                                         ieRng.select();\r
3489                                         ieRng.scrollIntoView();\r
3490                                 } catch (ex) {\r
3491                                         // Some times a Runtime error of the 800a025e type gets thrown\r
3492                                         // especially when the caret is placed before a table.\r
3493                                         // This is a somewhat strange location for the caret.\r
3494                                         // TODO: Find a better solution for this would possible require a rewrite of the setRng method\r
3495                                 }\r
3496 \r
3497                                 return;\r
3498                         }\r
3499 \r
3500                         // Set end of range to endContainer/endOffset\r
3501                         ieRng2 = body.createTextRange();\r
3502                         if (ec.nodeType == 3) {\r
3503                                 // Insert marker after/before startContainer\r
3504                                 ec.parentNode.insertBefore(marker, ec);\r
3505 \r
3506                                 // Move selection to end marker and move caret to end offset\r
3507                                 ieRng2.moveToElementText(marker);\r
3508                                 marker.parentNode.removeChild(marker);\r
3509                                 ieRng2.move('character', eo);\r
3510                                 ieRng.setEndPoint('EndToStart', ieRng2);\r
3511                         } else {\r
3512                                 ieRng2.moveToElementText(ec);\r
3513                                 ieRng2.collapse(!!skipEnd);\r
3514                                 ieRng.setEndPoint('EndToEnd', ieRng2);\r
3515                         }\r
3516 \r
3517                         ieRng.select();\r
3518                         ieRng.scrollIntoView();\r
3519                 };\r
3520 \r
3521                 this.getRangeAt = function() {\r
3522                         // Setup new range if the cache is empty\r
3523                         if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
3524                                 range = getRange();\r
3525 \r
3526                                 // Store away text range for next call\r
3527                                 lastIERng = selection.getRng();\r
3528                         }\r
3529 \r
3530                         // IE will say that the range is equal then produce an invalid argument exception\r
3531                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3532                         // This hack will invalidate the range cache if the exception occurs\r
3533                         try {\r
3534                                 range.startContainer.nextSibling;\r
3535                         } catch (ex) {\r
3536                                 range = getRange();\r
3537                                 lastIERng = null;\r
3538                         }\r
3539 \r
3540                         // Return cached range\r
3541                         return range;\r
3542                 };\r
3543 \r
3544                 this.destroy = function() {\r
3545                         // Destroy cached range and last IE range to avoid memory leaks\r
3546                         lastIERng = range = null;\r
3547                 };\r
3548 \r
3549                 // 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
3550                 if (selection.dom.boxModel) {\r
3551                         (function() {\r
3552                                 var doc = dom.doc, body = doc.body, started, startRng;\r
3553 \r
3554                                 // Make HTML element unselectable since we are going to handle selection by hand\r
3555                                 doc.documentElement.unselectable = TRUE;\r
3556 \r
3557                                 // Return range from point or null if it failed\r
3558                                 function rngFromPoint(x, y) {\r
3559                                         var rng = body.createTextRange();\r
3560 \r
3561                                         try {\r
3562                                                 rng.moveToPoint(x, y);\r
3563                                         } catch (ex) {\r
3564                                                 // IE sometimes throws and exception, so lets just ignore it\r
3565                                                 rng = null;\r
3566                                         }\r
3567 \r
3568                                         return rng;\r
3569                                 };\r
3570 \r
3571                                 // Fires while the selection is changing\r
3572                                 function selectionChange(e) {\r
3573                                         var pointRng;\r
3574 \r
3575                                         // Check if the button is down or not\r
3576                                         if (e.button) {\r
3577                                                 // Create range from mouse position\r
3578                                                 pointRng = rngFromPoint(e.x, e.y);\r
3579 \r
3580                                                 if (pointRng) {\r
3581                                                         // Check if pointRange is before/after selection then change the endPoint\r
3582                                                         if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
3583                                                                 pointRng.setEndPoint('StartToStart', startRng);\r
3584                                                         else\r
3585                                                                 pointRng.setEndPoint('EndToEnd', startRng);\r
3586 \r
3587                                                         pointRng.select();\r
3588                                                 }\r
3589                                         } else\r
3590                                                 endSelection();\r
3591                                 }\r
3592 \r
3593                                 // Removes listeners\r
3594                                 function endSelection() {\r
3595                                         dom.unbind(doc, 'mouseup', endSelection);\r
3596                                         dom.unbind(doc, 'mousemove', selectionChange);\r
3597                                         started = 0;\r
3598                                 };\r
3599 \r
3600                                 // Detect when user selects outside BODY\r
3601                                 dom.bind(doc, 'mousedown', function(e) {\r
3602                                         if (e.target.nodeName === 'HTML') {\r
3603                                                 if (started)\r
3604                                                         endSelection();\r
3605 \r
3606                                                 started = 1;\r
3607 \r
3608                                                 // Setup start position\r
3609                                                 startRng = rngFromPoint(e.x, e.y);\r
3610                                                 if (startRng) {\r
3611                                                         // Listen for selection change events\r
3612                                                         dom.bind(doc, 'mouseup', endSelection);\r
3613                                                         dom.bind(doc, 'mousemove', selectionChange);\r
3614 \r
3615                                                         startRng.select();\r
3616                                                 }\r
3617                                         }\r
3618                                 });\r
3619                         })();\r
3620                 }\r
3621         };\r
3622 \r
3623         // Expose the selection object\r
3624         tinymce.dom.TridentSelection = Selection;\r
3625 })();\r
3626 \r
3627 \r
3628 /*\r
3629  * Sizzle CSS Selector Engine - v1.0\r
3630  *  Copyright 2009, The Dojo Foundation\r
3631  *  Released under the MIT, BSD, and GPL Licenses.\r
3632  *  More information: http://sizzlejs.com/\r
3633  */\r
3634 (function(){\r
3635 \r
3636 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
3637         done = 0,\r
3638         toString = Object.prototype.toString,\r
3639         hasDuplicate = false,\r
3640         baseHasDuplicate = true;\r
3641 \r
3642 // Here we check if the JavaScript engine is using some sort of\r
3643 // optimization where it does not always call our comparision\r
3644 // function. If that is the case, discard the hasDuplicate value.\r
3645 //   Thus far that includes Google Chrome.\r
3646 [0, 0].sort(function(){\r
3647         baseHasDuplicate = false;\r
3648         return 0;\r
3649 });\r
3650 \r
3651 var Sizzle = function(selector, context, results, seed) {\r
3652         results = results || [];\r
3653         context = context || document;\r
3654 \r
3655         var origContext = context;\r
3656 \r
3657         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
3658                 return [];\r
3659         }\r
3660         \r
3661         if ( !selector || typeof selector !== "string" ) {\r
3662                 return results;\r
3663         }\r
3664 \r
3665         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
3666                 soFar = selector, ret, cur, pop, i;\r
3667         \r
3668         // Reset the position of the chunker regexp (start from head)\r
3669         do {\r
3670                 chunker.exec("");\r
3671                 m = chunker.exec(soFar);\r
3672 \r
3673                 if ( m ) {\r
3674                         soFar = m[3];\r
3675                 \r
3676                         parts.push( m[1] );\r
3677                 \r
3678                         if ( m[2] ) {\r
3679                                 extra = m[3];\r
3680                                 break;\r
3681                         }\r
3682                 }\r
3683         } while ( m );\r
3684 \r
3685         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
3686                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
3687                         set = posProcess( parts[0] + parts[1], context );\r
3688                 } else {\r
3689                         set = Expr.relative[ parts[0] ] ?\r
3690                                 [ context ] :\r
3691                                 Sizzle( parts.shift(), context );\r
3692 \r
3693                         while ( parts.length ) {\r
3694                                 selector = parts.shift();\r
3695 \r
3696                                 if ( Expr.relative[ selector ] ) {\r
3697                                         selector += parts.shift();\r
3698                                 }\r
3699                                 \r
3700                                 set = posProcess( selector, set );\r
3701                         }\r
3702                 }\r
3703         } else {\r
3704                 // Take a shortcut and set the context if the root selector is an ID\r
3705                 // (but not if it'll be faster if the inner selector is an ID)\r
3706                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
3707                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
3708                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
3709                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
3710                 }\r
3711 \r
3712                 if ( context ) {\r
3713                         ret = seed ?\r
3714                                 { expr: parts.pop(), set: makeArray(seed) } :\r
3715                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
3716                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
3717 \r
3718                         if ( parts.length > 0 ) {\r
3719                                 checkSet = makeArray(set);\r
3720                         } else {\r
3721                                 prune = false;\r
3722                         }\r
3723 \r
3724                         while ( parts.length ) {\r
3725                                 cur = parts.pop();\r
3726                                 pop = cur;\r
3727 \r
3728                                 if ( !Expr.relative[ cur ] ) {\r
3729                                         cur = "";\r
3730                                 } else {\r
3731                                         pop = parts.pop();\r
3732                                 }\r
3733 \r
3734                                 if ( pop == null ) {\r
3735                                         pop = context;\r
3736                                 }\r
3737 \r
3738                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
3739                         }\r
3740                 } else {\r
3741                         checkSet = parts = [];\r
3742                 }\r
3743         }\r
3744 \r
3745         if ( !checkSet ) {\r
3746                 checkSet = set;\r
3747         }\r
3748 \r
3749         if ( !checkSet ) {\r
3750                 Sizzle.error( cur || selector );\r
3751         }\r
3752 \r
3753         if ( toString.call(checkSet) === "[object Array]" ) {\r
3754                 if ( !prune ) {\r
3755                         results.push.apply( results, checkSet );\r
3756                 } else if ( context && context.nodeType === 1 ) {\r
3757                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3758                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
3759                                         results.push( set[i] );\r
3760                                 }\r
3761                         }\r
3762                 } else {\r
3763                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3764                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
3765                                         results.push( set[i] );\r
3766                                 }\r
3767                         }\r
3768                 }\r
3769         } else {\r
3770                 makeArray( checkSet, results );\r
3771         }\r
3772 \r
3773         if ( extra ) {\r
3774                 Sizzle( extra, origContext, results, seed );\r
3775                 Sizzle.uniqueSort( results );\r
3776         }\r
3777 \r
3778         return results;\r
3779 };\r
3780 \r
3781 Sizzle.uniqueSort = function(results){\r
3782         if ( sortOrder ) {\r
3783                 hasDuplicate = baseHasDuplicate;\r
3784                 results.sort(sortOrder);\r
3785 \r
3786                 if ( hasDuplicate ) {\r
3787                         for ( var i = 1; i < results.length; i++ ) {\r
3788                                 if ( results[i] === results[i-1] ) {\r
3789                                         results.splice(i--, 1);\r
3790                                 }\r
3791                         }\r
3792                 }\r
3793         }\r
3794 \r
3795         return results;\r
3796 };\r
3797 \r
3798 Sizzle.matches = function(expr, set){\r
3799         return Sizzle(expr, null, null, set);\r
3800 };\r
3801 \r
3802 Sizzle.find = function(expr, context, isXML){\r
3803         var set;\r
3804 \r
3805         if ( !expr ) {\r
3806                 return [];\r
3807         }\r
3808 \r
3809         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
3810                 var type = Expr.order[i], match;\r
3811                 \r
3812                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
3813                         var left = match[1];\r
3814                         match.splice(1,1);\r
3815 \r
3816                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
3817                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
3818                                 set = Expr.find[ type ]( match, context, isXML );\r
3819                                 if ( set != null ) {\r
3820                                         expr = expr.replace( Expr.match[ type ], "" );\r
3821                                         break;\r
3822                                 }\r
3823                         }\r
3824                 }\r
3825         }\r
3826 \r
3827         if ( !set ) {\r
3828                 set = context.getElementsByTagName("*");\r
3829         }\r
3830 \r
3831         return {set: set, expr: expr};\r
3832 };\r
3833 \r
3834 Sizzle.filter = function(expr, set, inplace, not){\r
3835         var old = expr, result = [], curLoop = set, match, anyFound,\r
3836                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
3837 \r
3838         while ( expr && set.length ) {\r
3839                 for ( var type in Expr.filter ) {\r
3840                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
3841                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
3842                                 anyFound = false;\r
3843 \r
3844                                 match.splice(1,1);\r
3845 \r
3846                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
3847                                         continue;\r
3848                                 }\r
3849 \r
3850                                 if ( curLoop === result ) {\r
3851                                         result = [];\r
3852                                 }\r
3853 \r
3854                                 if ( Expr.preFilter[ type ] ) {\r
3855                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
3856 \r
3857                                         if ( !match ) {\r
3858                                                 anyFound = found = true;\r
3859                                         } else if ( match === true ) {\r
3860                                                 continue;\r
3861                                         }\r
3862                                 }\r
3863 \r
3864                                 if ( match ) {\r
3865                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
3866                                                 if ( item ) {\r
3867                                                         found = filter( item, match, i, curLoop );\r
3868                                                         var pass = not ^ !!found;\r
3869 \r
3870                                                         if ( inplace && found != null ) {\r
3871                                                                 if ( pass ) {\r
3872                                                                         anyFound = true;\r
3873                                                                 } else {\r
3874                                                                         curLoop[i] = false;\r
3875                                                                 }\r
3876                                                         } else if ( pass ) {\r
3877                                                                 result.push( item );\r
3878                                                                 anyFound = true;\r
3879                                                         }\r
3880                                                 }\r
3881                                         }\r
3882                                 }\r
3883 \r
3884                                 if ( found !== undefined ) {\r
3885                                         if ( !inplace ) {\r
3886                                                 curLoop = result;\r
3887                                         }\r
3888 \r
3889                                         expr = expr.replace( Expr.match[ type ], "" );\r
3890 \r
3891                                         if ( !anyFound ) {\r
3892                                                 return [];\r
3893                                         }\r
3894 \r
3895                                         break;\r
3896                                 }\r
3897                         }\r
3898                 }\r
3899 \r
3900                 // Improper expression\r
3901                 if ( expr === old ) {\r
3902                         if ( anyFound == null ) {\r
3903                                 Sizzle.error( expr );\r
3904                         } else {\r
3905                                 break;\r
3906                         }\r
3907                 }\r
3908 \r
3909                 old = expr;\r
3910         }\r
3911 \r
3912         return curLoop;\r
3913 };\r
3914 \r
3915 Sizzle.error = function( msg ) {\r
3916         throw "Syntax error, unrecognized expression: " + msg;\r
3917 };\r
3918 \r
3919 var Expr = Sizzle.selectors = {\r
3920         order: [ "ID", "NAME", "TAG" ],\r
3921         match: {\r
3922                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3923                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3924                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
3925                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
3926                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
3927                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
3928                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
3929                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
3930         },\r
3931         leftMatch: {},\r
3932         attrMap: {\r
3933                 "class": "className",\r
3934                 "for": "htmlFor"\r
3935         },\r
3936         attrHandle: {\r
3937                 href: function(elem){\r
3938                         return elem.getAttribute("href");\r
3939                 }\r
3940         },\r
3941         relative: {\r
3942                 "+": function(checkSet, part){\r
3943                         var isPartStr = typeof part === "string",\r
3944                                 isTag = isPartStr && !/\W/.test(part),\r
3945                                 isPartStrNotTag = isPartStr && !isTag;\r
3946 \r
3947                         if ( isTag ) {\r
3948                                 part = part.toLowerCase();\r
3949                         }\r
3950 \r
3951                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
3952                                 if ( (elem = checkSet[i]) ) {\r
3953                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
3954 \r
3955                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
3956                                                 elem || false :\r
3957                                                 elem === part;\r
3958                                 }\r
3959                         }\r
3960 \r
3961                         if ( isPartStrNotTag ) {\r
3962                                 Sizzle.filter( part, checkSet, true );\r
3963                         }\r
3964                 },\r
3965                 ">": function(checkSet, part){\r
3966                         var isPartStr = typeof part === "string",\r
3967                                 elem, i = 0, l = checkSet.length;\r
3968 \r
3969                         if ( isPartStr && !/\W/.test(part) ) {\r
3970                                 part = part.toLowerCase();\r
3971 \r
3972                                 for ( ; i < l; i++ ) {\r
3973                                         elem = checkSet[i];\r
3974                                         if ( elem ) {\r
3975                                                 var parent = elem.parentNode;\r
3976                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
3977                                         }\r
3978                                 }\r
3979                         } else {\r
3980                                 for ( ; i < l; i++ ) {\r
3981                                         elem = checkSet[i];\r
3982                                         if ( elem ) {\r
3983                                                 checkSet[i] = isPartStr ?\r
3984                                                         elem.parentNode :\r
3985                                                         elem.parentNode === part;\r
3986                                         }\r
3987                                 }\r
3988 \r
3989                                 if ( isPartStr ) {\r
3990                                         Sizzle.filter( part, checkSet, true );\r
3991                                 }\r
3992                         }\r
3993                 },\r
3994                 "": function(checkSet, part, isXML){\r
3995                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
3996 \r
3997                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
3998                                 part = part.toLowerCase();\r
3999                                 nodeCheck = part;\r
4000                                 checkFn = dirNodeCheck;\r
4001                         }\r
4002 \r
4003                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
4004                 },\r
4005                 "~": function(checkSet, part, isXML){\r
4006                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
4007 \r
4008                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
4009                                 part = part.toLowerCase();\r
4010                                 nodeCheck = part;\r
4011                                 checkFn = dirNodeCheck;\r
4012                         }\r
4013 \r
4014                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
4015                 }\r
4016         },\r
4017         find: {\r
4018                 ID: function(match, context, isXML){\r
4019                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4020                                 var m = context.getElementById(match[1]);\r
4021                                 return m ? [m] : [];\r
4022                         }\r
4023                 },\r
4024                 NAME: function(match, context){\r
4025                         if ( typeof context.getElementsByName !== "undefined" ) {\r
4026                                 var ret = [], results = context.getElementsByName(match[1]);\r
4027 \r
4028                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
4029                                         if ( results[i].getAttribute("name") === match[1] ) {\r
4030                                                 ret.push( results[i] );\r
4031                                         }\r
4032                                 }\r
4033 \r
4034                                 return ret.length === 0 ? null : ret;\r
4035                         }\r
4036                 },\r
4037                 TAG: function(match, context){\r
4038                         return context.getElementsByTagName(match[1]);\r
4039                 }\r
4040         },\r
4041         preFilter: {\r
4042                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
4043                         match = " " + match[1].replace(/\\/g, "") + " ";\r
4044 \r
4045                         if ( isXML ) {\r
4046                                 return match;\r
4047                         }\r
4048 \r
4049                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
4050                                 if ( elem ) {\r
4051                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
4052                                                 if ( !inplace ) {\r
4053                                                         result.push( elem );\r
4054                                                 }\r
4055                                         } else if ( inplace ) {\r
4056                                                 curLoop[i] = false;\r
4057                                         }\r
4058                                 }\r
4059                         }\r
4060 \r
4061                         return false;\r
4062                 },\r
4063                 ID: function(match){\r
4064                         return match[1].replace(/\\/g, "");\r
4065                 },\r
4066                 TAG: function(match, curLoop){\r
4067                         return match[1].toLowerCase();\r
4068                 },\r
4069                 CHILD: function(match){\r
4070                         if ( match[1] === "nth" ) {\r
4071                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
4072                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
4073                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
4074                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
4075 \r
4076                                 // calculate the numbers (first)n+(last) including if they are negative\r
4077                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
4078                                 match[3] = test[3] - 0;\r
4079                         }\r
4080 \r
4081                         // TODO: Move to normal caching system\r
4082                         match[0] = done++;\r
4083 \r
4084                         return match;\r
4085                 },\r
4086                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
4087                         var name = match[1].replace(/\\/g, "");\r
4088                         \r
4089                         if ( !isXML && Expr.attrMap[name] ) {\r
4090                                 match[1] = Expr.attrMap[name];\r
4091                         }\r
4092 \r
4093                         if ( match[2] === "~=" ) {\r
4094                                 match[4] = " " + match[4] + " ";\r
4095                         }\r
4096 \r
4097                         return match;\r
4098                 },\r
4099                 PSEUDO: function(match, curLoop, inplace, result, not){\r
4100                         if ( match[1] === "not" ) {\r
4101                                 // If we're dealing with a complex expression, or a simple one\r
4102                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
4103                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
4104                                 } else {\r
4105                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
4106                                         if ( !inplace ) {\r
4107                                                 result.push.apply( result, ret );\r
4108                                         }\r
4109                                         return false;\r
4110                                 }\r
4111                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
4112                                 return true;\r
4113                         }\r
4114                         \r
4115                         return match;\r
4116                 },\r
4117                 POS: function(match){\r
4118                         match.unshift( true );\r
4119                         return match;\r
4120                 }\r
4121         },\r
4122         filters: {\r
4123                 enabled: function(elem){\r
4124                         return elem.disabled === false && elem.type !== "hidden";\r
4125                 },\r
4126                 disabled: function(elem){\r
4127                         return elem.disabled === true;\r
4128                 },\r
4129                 checked: function(elem){\r
4130                         return elem.checked === true;\r
4131                 },\r
4132                 selected: function(elem){\r
4133                         // Accessing this property makes selected-by-default\r
4134                         // options in Safari work properly\r
4135                         elem.parentNode.selectedIndex;\r
4136                         return elem.selected === true;\r
4137                 },\r
4138                 parent: function(elem){\r
4139                         return !!elem.firstChild;\r
4140                 },\r
4141                 empty: function(elem){\r
4142                         return !elem.firstChild;\r
4143                 },\r
4144                 has: function(elem, i, match){\r
4145                         return !!Sizzle( match[3], elem ).length;\r
4146                 },\r
4147                 header: function(elem){\r
4148                         return (/h\d/i).test( elem.nodeName );\r
4149                 },\r
4150                 text: function(elem){\r
4151                         return "text" === elem.type;\r
4152                 },\r
4153                 radio: function(elem){\r
4154                         return "radio" === elem.type;\r
4155                 },\r
4156                 checkbox: function(elem){\r
4157                         return "checkbox" === elem.type;\r
4158                 },\r
4159                 file: function(elem){\r
4160                         return "file" === elem.type;\r
4161                 },\r
4162                 password: function(elem){\r
4163                         return "password" === elem.type;\r
4164                 },\r
4165                 submit: function(elem){\r
4166                         return "submit" === elem.type;\r
4167                 },\r
4168                 image: function(elem){\r
4169                         return "image" === elem.type;\r
4170                 },\r
4171                 reset: function(elem){\r
4172                         return "reset" === elem.type;\r
4173                 },\r
4174                 button: function(elem){\r
4175                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
4176                 },\r
4177                 input: function(elem){\r
4178                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
4179                 }\r
4180         },\r
4181         setFilters: {\r
4182                 first: function(elem, i){\r
4183                         return i === 0;\r
4184                 },\r
4185                 last: function(elem, i, match, array){\r
4186                         return i === array.length - 1;\r
4187                 },\r
4188                 even: function(elem, i){\r
4189                         return i % 2 === 0;\r
4190                 },\r
4191                 odd: function(elem, i){\r
4192                         return i % 2 === 1;\r
4193                 },\r
4194                 lt: function(elem, i, match){\r
4195                         return i < match[3] - 0;\r
4196                 },\r
4197                 gt: function(elem, i, match){\r
4198                         return i > match[3] - 0;\r
4199                 },\r
4200                 nth: function(elem, i, match){\r
4201                         return match[3] - 0 === i;\r
4202                 },\r
4203                 eq: function(elem, i, match){\r
4204                         return match[3] - 0 === i;\r
4205                 }\r
4206         },\r
4207         filter: {\r
4208                 PSEUDO: function(elem, match, i, array){\r
4209                         var name = match[1], filter = Expr.filters[ name ];\r
4210 \r
4211                         if ( filter ) {\r
4212                                 return filter( elem, i, match, array );\r
4213                         } else if ( name === "contains" ) {\r
4214                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
4215                         } else if ( name === "not" ) {\r
4216                                 var not = match[3];\r
4217 \r
4218                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
4219                                         if ( not[j] === elem ) {\r
4220                                                 return false;\r
4221                                         }\r
4222                                 }\r
4223 \r
4224                                 return true;\r
4225                         } else {\r
4226                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
4227                         }\r
4228                 },\r
4229                 CHILD: function(elem, match){\r
4230                         var type = match[1], node = elem;\r
4231                         switch (type) {\r
4232                                 case 'only':\r
4233                                 case 'first':\r
4234                                         while ( (node = node.previousSibling) )  {\r
4235                                                 if ( node.nodeType === 1 ) { \r
4236                                                         return false; \r
4237                                                 }\r
4238                                         }\r
4239                                         if ( type === "first" ) { \r
4240                                                 return true; \r
4241                                         }\r
4242                                         node = elem;\r
4243                                 case 'last':\r
4244                                         while ( (node = node.nextSibling) )      {\r
4245                                                 if ( node.nodeType === 1 ) { \r
4246                                                         return false; \r
4247                                                 }\r
4248                                         }\r
4249                                         return true;\r
4250                                 case 'nth':\r
4251                                         var first = match[2], last = match[3];\r
4252 \r
4253                                         if ( first === 1 && last === 0 ) {\r
4254                                                 return true;\r
4255                                         }\r
4256                                         \r
4257                                         var doneName = match[0],\r
4258                                                 parent = elem.parentNode;\r
4259         \r
4260                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
4261                                                 var count = 0;\r
4262                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
4263                                                         if ( node.nodeType === 1 ) {\r
4264                                                                 node.nodeIndex = ++count;\r
4265                                                         }\r
4266                                                 } \r
4267                                                 parent.sizcache = doneName;\r
4268                                         }\r
4269                                         \r
4270                                         var diff = elem.nodeIndex - last;\r
4271                                         if ( first === 0 ) {\r
4272                                                 return diff === 0;\r
4273                                         } else {\r
4274                                                 return ( diff % first === 0 && diff / first >= 0 );\r
4275                                         }\r
4276                         }\r
4277                 },\r
4278                 ID: function(elem, match){\r
4279                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
4280                 },\r
4281                 TAG: function(elem, match){\r
4282                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
4283                 },\r
4284                 CLASS: function(elem, match){\r
4285                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
4286                                 .indexOf( match ) > -1;\r
4287                 },\r
4288                 ATTR: function(elem, match){\r
4289                         var name = match[1],\r
4290                                 result = Expr.attrHandle[ name ] ?\r
4291                                         Expr.attrHandle[ name ]( elem ) :\r
4292                                         elem[ name ] != null ?\r
4293                                                 elem[ name ] :\r
4294                                                 elem.getAttribute( name ),\r
4295                                 value = result + "",\r
4296                                 type = match[2],\r
4297                                 check = match[4];\r
4298 \r
4299                         return result == null ?\r
4300                                 type === "!=" :\r
4301                                 type === "=" ?\r
4302                                 value === check :\r
4303                                 type === "*=" ?\r
4304                                 value.indexOf(check) >= 0 :\r
4305                                 type === "~=" ?\r
4306                                 (" " + value + " ").indexOf(check) >= 0 :\r
4307                                 !check ?\r
4308                                 value && result !== false :\r
4309                                 type === "!=" ?\r
4310                                 value !== check :\r
4311                                 type === "^=" ?\r
4312                                 value.indexOf(check) === 0 :\r
4313                                 type === "$=" ?\r
4314                                 value.substr(value.length - check.length) === check :\r
4315                                 type === "|=" ?\r
4316                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
4317                                 false;\r
4318                 },\r
4319                 POS: function(elem, match, i, array){\r
4320                         var name = match[2], filter = Expr.setFilters[ name ];\r
4321 \r
4322                         if ( filter ) {\r
4323                                 return filter( elem, i, match, array );\r
4324                         }\r
4325                 }\r
4326         }\r
4327 };\r
4328 \r
4329 var origPOS = Expr.match.POS,\r
4330         fescape = function(all, num){\r
4331                 return "\\" + (num - 0 + 1);\r
4332         };\r
4333 \r
4334 for ( var type in Expr.match ) {\r
4335         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
4336         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
4337 }\r
4338 \r
4339 var makeArray = function(array, results) {\r
4340         array = Array.prototype.slice.call( array, 0 );\r
4341 \r
4342         if ( results ) {\r
4343                 results.push.apply( results, array );\r
4344                 return results;\r
4345         }\r
4346         \r
4347         return array;\r
4348 };\r
4349 \r
4350 // Perform a simple check to determine if the browser is capable of\r
4351 // converting a NodeList to an array using builtin methods.\r
4352 // Also verifies that the returned array holds DOM nodes\r
4353 // (which is not the case in the Blackberry browser)\r
4354 try {\r
4355         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
4356 \r
4357 // Provide a fallback method if it does not work\r
4358 } catch(e){\r
4359         makeArray = function(array, results) {\r
4360                 var ret = results || [], i = 0;\r
4361 \r
4362                 if ( toString.call(array) === "[object Array]" ) {\r
4363                         Array.prototype.push.apply( ret, array );\r
4364                 } else {\r
4365                         if ( typeof array.length === "number" ) {\r
4366                                 for ( var l = array.length; i < l; i++ ) {\r
4367                                         ret.push( array[i] );\r
4368                                 }\r
4369                         } else {\r
4370                                 for ( ; array[i]; i++ ) {\r
4371                                         ret.push( array[i] );\r
4372                                 }\r
4373                         }\r
4374                 }\r
4375 \r
4376                 return ret;\r
4377         };\r
4378 }\r
4379 \r
4380 var sortOrder;\r
4381 \r
4382 if ( document.documentElement.compareDocumentPosition ) {\r
4383         sortOrder = function( a, b ) {\r
4384                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
4385                         if ( a == b ) {\r
4386                                 hasDuplicate = true;\r
4387                         }\r
4388                         return a.compareDocumentPosition ? -1 : 1;\r
4389                 }\r
4390 \r
4391                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
4392                 if ( ret === 0 ) {\r
4393                         hasDuplicate = true;\r
4394                 }\r
4395                 return ret;\r
4396         };\r
4397 } else if ( "sourceIndex" in document.documentElement ) {\r
4398         sortOrder = function( a, b ) {\r
4399                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
4400                         if ( a == b ) {\r
4401                                 hasDuplicate = true;\r
4402                         }\r
4403                         return a.sourceIndex ? -1 : 1;\r
4404                 }\r
4405 \r
4406                 var ret = a.sourceIndex - b.sourceIndex;\r
4407                 if ( ret === 0 ) {\r
4408                         hasDuplicate = true;\r
4409                 }\r
4410                 return ret;\r
4411         };\r
4412 } else if ( document.createRange ) {\r
4413         sortOrder = function( a, b ) {\r
4414                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
4415                         if ( a == b ) {\r
4416                                 hasDuplicate = true;\r
4417                         }\r
4418                         return a.ownerDocument ? -1 : 1;\r
4419                 }\r
4420 \r
4421                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
4422                 aRange.setStart(a, 0);\r
4423                 aRange.setEnd(a, 0);\r
4424                 bRange.setStart(b, 0);\r
4425                 bRange.setEnd(b, 0);\r
4426                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
4427                 if ( ret === 0 ) {\r
4428                         hasDuplicate = true;\r
4429                 }\r
4430                 return ret;\r
4431         };\r
4432 }\r
4433 \r
4434 // Utility function for retreiving the text value of an array of DOM nodes\r
4435 Sizzle.getText = function( elems ) {\r
4436         var ret = "", elem;\r
4437 \r
4438         for ( var i = 0; elems[i]; i++ ) {\r
4439                 elem = elems[i];\r
4440 \r
4441                 // Get the text from text nodes and CDATA nodes\r
4442                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
4443                         ret += elem.nodeValue;\r
4444 \r
4445                 // Traverse everything else, except comment nodes\r
4446                 } else if ( elem.nodeType !== 8 ) {\r
4447                         ret += Sizzle.getText( elem.childNodes );\r
4448                 }\r
4449         }\r
4450 \r
4451         return ret;\r
4452 };\r
4453 \r
4454 // Check to see if the browser returns elements by name when\r
4455 // querying by getElementById (and provide a workaround)\r
4456 (function(){\r
4457         // We're going to inject a fake input element with a specified name\r
4458         var form = document.createElement("div"),\r
4459                 id = "script" + (new Date()).getTime();\r
4460         form.innerHTML = "<a name='" + id + "'/>";\r
4461 \r
4462         // Inject it into the root element, check its status, and remove it quickly\r
4463         var root = document.documentElement;\r
4464         root.insertBefore( form, root.firstChild );\r
4465 \r
4466         // The workaround has to do additional checks after a getElementById\r
4467         // Which slows things down for other browsers (hence the branching)\r
4468         if ( document.getElementById( id ) ) {\r
4469                 Expr.find.ID = function(match, context, isXML){\r
4470                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4471                                 var m = context.getElementById(match[1]);\r
4472                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
4473                         }\r
4474                 };\r
4475 \r
4476                 Expr.filter.ID = function(elem, match){\r
4477                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
4478                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
4479                 };\r
4480         }\r
4481 \r
4482         root.removeChild( form );\r
4483         root = form = null; // release memory in IE\r
4484 })();\r
4485 \r
4486 (function(){\r
4487         // Check to see if the browser returns only elements\r
4488         // when doing getElementsByTagName("*")\r
4489 \r
4490         // Create a fake element\r
4491         var div = document.createElement("div");\r
4492         div.appendChild( document.createComment("") );\r
4493 \r
4494         // Make sure no comments are found\r
4495         if ( div.getElementsByTagName("*").length > 0 ) {\r
4496                 Expr.find.TAG = function(match, context){\r
4497                         var results = context.getElementsByTagName(match[1]);\r
4498 \r
4499                         // Filter out possible comments\r
4500                         if ( match[1] === "*" ) {\r
4501                                 var tmp = [];\r
4502 \r
4503                                 for ( var i = 0; results[i]; i++ ) {\r
4504                                         if ( results[i].nodeType === 1 ) {\r
4505                                                 tmp.push( results[i] );\r
4506                                         }\r
4507                                 }\r
4508 \r
4509                                 results = tmp;\r
4510                         }\r
4511 \r
4512                         return results;\r
4513                 };\r
4514         }\r
4515 \r
4516         // Check to see if an attribute returns normalized href attributes\r
4517         div.innerHTML = "<a href='#'></a>";\r
4518         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
4519                         div.firstChild.getAttribute("href") !== "#" ) {\r
4520                 Expr.attrHandle.href = function(elem){\r
4521                         return elem.getAttribute("href", 2);\r
4522                 };\r
4523         }\r
4524 \r
4525         div = null; // release memory in IE\r
4526 })();\r
4527 \r
4528 if ( document.querySelectorAll ) {\r
4529         (function(){\r
4530                 var oldSizzle = Sizzle, div = document.createElement("div");\r
4531                 div.innerHTML = "<p class='TEST'></p>";\r
4532 \r
4533                 // Safari can't handle uppercase or unicode characters when\r
4534                 // in quirks mode.\r
4535                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
4536                         return;\r
4537                 }\r
4538         \r
4539                 Sizzle = function(query, context, extra, seed){\r
4540                         context = context || document;\r
4541 \r
4542                         // Only use querySelectorAll on non-XML documents\r
4543                         // (ID selectors don't work in non-HTML documents)\r
4544                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
4545                                 try {\r
4546                                         return makeArray( context.querySelectorAll(query), extra );\r
4547                                 } catch(e){}\r
4548                         }\r
4549                 \r
4550                         return oldSizzle(query, context, extra, seed);\r
4551                 };\r
4552 \r
4553                 for ( var prop in oldSizzle ) {\r
4554                         Sizzle[ prop ] = oldSizzle[ prop ];\r
4555                 }\r
4556 \r
4557                 div = null; // release memory in IE\r
4558         })();\r
4559 }\r
4560 \r
4561 (function(){\r
4562         var div = document.createElement("div");\r
4563 \r
4564         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
4565 \r
4566         // Opera can't find a second classname (in 9.6)\r
4567         // Also, make sure that getElementsByClassName actually exists\r
4568         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
4569                 return;\r
4570         }\r
4571 \r
4572         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
4573         div.lastChild.className = "e";\r
4574 \r
4575         if ( div.getElementsByClassName("e").length === 1 ) {\r
4576                 return;\r
4577         }\r
4578         \r
4579         Expr.order.splice(1, 0, "CLASS");\r
4580         Expr.find.CLASS = function(match, context, isXML) {\r
4581                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
4582                         return context.getElementsByClassName(match[1]);\r
4583                 }\r
4584         };\r
4585 \r
4586         div = null; // release memory in IE\r
4587 })();\r
4588 \r
4589 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4590         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4591                 var elem = checkSet[i];\r
4592                 if ( elem ) {\r
4593                         elem = elem[dir];\r
4594                         var match = false;\r
4595 \r
4596                         while ( elem ) {\r
4597                                 if ( elem.sizcache === doneName ) {\r
4598                                         match = checkSet[elem.sizset];\r
4599                                         break;\r
4600                                 }\r
4601 \r
4602                                 if ( elem.nodeType === 1 && !isXML ){\r
4603                                         elem.sizcache = doneName;\r
4604                                         elem.sizset = i;\r
4605                                 }\r
4606 \r
4607                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
4608                                         match = elem;\r
4609                                         break;\r
4610                                 }\r
4611 \r
4612                                 elem = elem[dir];\r
4613                         }\r
4614 \r
4615                         checkSet[i] = match;\r
4616                 }\r
4617         }\r
4618 }\r
4619 \r
4620 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4621         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4622                 var elem = checkSet[i];\r
4623                 if ( elem ) {\r
4624                         elem = elem[dir];\r
4625                         var match = false;\r
4626 \r
4627                         while ( elem ) {\r
4628                                 if ( elem.sizcache === doneName ) {\r
4629                                         match = checkSet[elem.sizset];\r
4630                                         break;\r
4631                                 }\r
4632 \r
4633                                 if ( elem.nodeType === 1 ) {\r
4634                                         if ( !isXML ) {\r
4635                                                 elem.sizcache = doneName;\r
4636                                                 elem.sizset = i;\r
4637                                         }\r
4638                                         if ( typeof cur !== "string" ) {\r
4639                                                 if ( elem === cur ) {\r
4640                                                         match = true;\r
4641                                                         break;\r
4642                                                 }\r
4643 \r
4644                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
4645                                                 match = elem;\r
4646                                                 break;\r
4647                                         }\r
4648                                 }\r
4649 \r
4650                                 elem = elem[dir];\r
4651                         }\r
4652 \r
4653                         checkSet[i] = match;\r
4654                 }\r
4655         }\r
4656 }\r
4657 \r
4658 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
4659         return !!(a.compareDocumentPosition(b) & 16);\r
4660 } : function(a, b){\r
4661         return a !== b && (a.contains ? a.contains(b) : true);\r
4662 };\r
4663 \r
4664 Sizzle.isXML = function(elem){\r
4665         // documentElement is verified for cases where it doesn't yet exist\r
4666         // (such as loading iframes in IE - #4833) \r
4667         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
4668         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
4669 };\r
4670 \r
4671 var posProcess = function(selector, context){\r
4672         var tmpSet = [], later = "", match,\r
4673                 root = context.nodeType ? [context] : context;\r
4674 \r
4675         // Position selectors must be done after the filter\r
4676         // And so must :not(positional) so we move all PSEUDOs to the end\r
4677         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
4678                 later += match[0];\r
4679                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
4680         }\r
4681 \r
4682         selector = Expr.relative[selector] ? selector + "*" : selector;\r
4683 \r
4684         for ( var i = 0, l = root.length; i < l; i++ ) {\r
4685                 Sizzle( selector, root[i], tmpSet );\r
4686         }\r
4687 \r
4688         return Sizzle.filter( later, tmpSet );\r
4689 };\r
4690 \r
4691 // EXPOSE\r
4692 \r
4693 window.tinymce.dom.Sizzle = Sizzle;\r
4694 \r
4695 })();\r
4696 \r
4697 \r
4698 (function(tinymce) {\r
4699         // Shorten names\r
4700         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
4701 \r
4702         tinymce.create('tinymce.dom.EventUtils', {\r
4703                 EventUtils : function() {\r
4704                         this.inits = [];\r
4705                         this.events = [];\r
4706                 },\r
4707 \r
4708                 add : function(o, n, f, s) {\r
4709                         var cb, t = this, el = t.events, r;\r
4710 \r
4711                         if (n instanceof Array) {\r
4712                                 r = [];\r
4713 \r
4714                                 each(n, function(n) {\r
4715                                         r.push(t.add(o, n, f, s));\r
4716                                 });\r
4717 \r
4718                                 return r;\r
4719                         }\r
4720 \r
4721                         // Handle array\r
4722                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4723                                 r = [];\r
4724 \r
4725                                 each(o, function(o) {\r
4726                                         o = DOM.get(o);\r
4727                                         r.push(t.add(o, n, f, s));\r
4728                                 });\r
4729 \r
4730                                 return r;\r
4731                         }\r
4732 \r
4733                         o = DOM.get(o);\r
4734 \r
4735                         if (!o)\r
4736                                 return;\r
4737 \r
4738                         // Setup event callback\r
4739                         cb = function(e) {\r
4740                                 // Is all events disabled\r
4741                                 if (t.disabled)\r
4742                                         return;\r
4743 \r
4744                                 e = e || window.event;\r
4745 \r
4746                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
4747                                 if (e && isIE) {\r
4748                                         if (!e.target)\r
4749                                                 e.target = e.srcElement;\r
4750 \r
4751                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
4752                                         tinymce.extend(e, t._stoppers);\r
4753                                 }\r
4754 \r
4755                                 if (!s)\r
4756                                         return f(e);\r
4757 \r
4758                                 return f.call(s, e);\r
4759                         };\r
4760 \r
4761                         if (n == 'unload') {\r
4762                                 tinymce.unloads.unshift({func : cb});\r
4763                                 return cb;\r
4764                         }\r
4765 \r
4766                         if (n == 'init') {\r
4767                                 if (t.domLoaded)\r
4768                                         cb();\r
4769                                 else\r
4770                                         t.inits.push(cb);\r
4771 \r
4772                                 return cb;\r
4773                         }\r
4774 \r
4775                         // Store away listener reference\r
4776                         el.push({\r
4777                                 obj : o,\r
4778                                 name : n,\r
4779                                 func : f,\r
4780                                 cfunc : cb,\r
4781                                 scope : s\r
4782                         });\r
4783 \r
4784                         t._add(o, n, cb);\r
4785 \r
4786                         return f;\r
4787                 },\r
4788 \r
4789                 remove : function(o, n, f) {\r
4790                         var t = this, a = t.events, s = false, r;\r
4791 \r
4792                         // Handle array\r
4793                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4794                                 r = [];\r
4795 \r
4796                                 each(o, function(o) {\r
4797                                         o = DOM.get(o);\r
4798                                         r.push(t.remove(o, n, f));\r
4799                                 });\r
4800 \r
4801                                 return r;\r
4802                         }\r
4803 \r
4804                         o = DOM.get(o);\r
4805 \r
4806                         each(a, function(e, i) {\r
4807                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
4808                                         a.splice(i, 1);\r
4809                                         t._remove(o, n, e.cfunc);\r
4810                                         s = true;\r
4811                                         return false;\r
4812                                 }\r
4813                         });\r
4814 \r
4815                         return s;\r
4816                 },\r
4817 \r
4818                 clear : function(o) {\r
4819                         var t = this, a = t.events, i, e;\r
4820 \r
4821                         if (o) {\r
4822                                 o = DOM.get(o);\r
4823 \r
4824                                 for (i = a.length - 1; i >= 0; i--) {\r
4825                                         e = a[i];\r
4826 \r
4827                                         if (e.obj === o) {\r
4828                                                 t._remove(e.obj, e.name, e.cfunc);\r
4829                                                 e.obj = e.cfunc = null;\r
4830                                                 a.splice(i, 1);\r
4831                                         }\r
4832                                 }\r
4833                         }\r
4834                 },\r
4835 \r
4836                 cancel : function(e) {\r
4837                         if (!e)\r
4838                                 return false;\r
4839 \r
4840                         this.stop(e);\r
4841 \r
4842                         return this.prevent(e);\r
4843                 },\r
4844 \r
4845                 stop : function(e) {\r
4846                         if (e.stopPropagation)\r
4847                                 e.stopPropagation();\r
4848                         else\r
4849                                 e.cancelBubble = true;\r
4850 \r
4851                         return false;\r
4852                 },\r
4853 \r
4854                 prevent : function(e) {\r
4855                         if (e.preventDefault)\r
4856                                 e.preventDefault();\r
4857                         else\r
4858                                 e.returnValue = false;\r
4859 \r
4860                         return false;\r
4861                 },\r
4862 \r
4863                 destroy : function() {\r
4864                         var t = this;\r
4865 \r
4866                         each(t.events, function(e, i) {\r
4867                                 t._remove(e.obj, e.name, e.cfunc);\r
4868                                 e.obj = e.cfunc = null;\r
4869                         });\r
4870 \r
4871                         t.events = [];\r
4872                         t = null;\r
4873                 },\r
4874 \r
4875                 _add : function(o, n, f) {\r
4876                         if (o.attachEvent)\r
4877                                 o.attachEvent('on' + n, f);\r
4878                         else if (o.addEventListener)\r
4879                                 o.addEventListener(n, f, false);\r
4880                         else\r
4881                                 o['on' + n] = f;\r
4882                 },\r
4883 \r
4884                 _remove : function(o, n, f) {\r
4885                         if (o) {\r
4886                                 try {\r
4887                                         if (o.detachEvent)\r
4888                                                 o.detachEvent('on' + n, f);\r
4889                                         else if (o.removeEventListener)\r
4890                                                 o.removeEventListener(n, f, false);\r
4891                                         else\r
4892                                                 o['on' + n] = null;\r
4893                                 } catch (ex) {\r
4894                                         // Might fail with permission denined on IE so we just ignore that\r
4895                                 }\r
4896                         }\r
4897                 },\r
4898 \r
4899                 _pageInit : function(win) {\r
4900                         var t = this;\r
4901 \r
4902                         // Keep it from running more than once\r
4903                         if (t.domLoaded)\r
4904                                 return;\r
4905 \r
4906                         t.domLoaded = true;\r
4907 \r
4908                         each(t.inits, function(c) {\r
4909                                 c();\r
4910                         });\r
4911 \r
4912                         t.inits = [];\r
4913                 },\r
4914 \r
4915                 _wait : function(win) {\r
4916                         var t = this, doc = win.document;\r
4917 \r
4918                         // No need since the document is already loaded\r
4919                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4920                                 t.domLoaded = 1;\r
4921                                 return;\r
4922                         }\r
4923 \r
4924                         // Use IE method\r
4925                         if (doc.attachEvent) {\r
4926                                 doc.attachEvent("onreadystatechange", function() {\r
4927                                         if (doc.readyState === "complete") {\r
4928                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4929                                                 t._pageInit(win);\r
4930                                         }\r
4931                                 });\r
4932 \r
4933                                 if (doc.documentElement.doScroll && win == win.top) {\r
4934                                         (function() {\r
4935                                                 if (t.domLoaded)\r
4936                                                         return;\r
4937 \r
4938                                                 try {\r
4939                                                         // If IE is used, use the trick by Diego Perini\r
4940                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4941                                                         doc.documentElement.doScroll("left");\r
4942                                                 } catch (ex) {\r
4943                                                         setTimeout(arguments.callee, 0);\r
4944                                                         return;\r
4945                                                 }\r
4946 \r
4947                                                 t._pageInit(win);\r
4948                                         })();\r
4949                                 }\r
4950                         } else if (doc.addEventListener) {\r
4951                                 t._add(win, 'DOMContentLoaded', function() {\r
4952                                         t._pageInit(win);\r
4953                                 });\r
4954                         }\r
4955 \r
4956                         t._add(win, 'load', function() {\r
4957                                 t._pageInit(win);\r
4958                         });\r
4959                 },\r
4960 \r
4961                 _stoppers : {\r
4962                         preventDefault :  function() {\r
4963                                 this.returnValue = false;\r
4964                         },\r
4965 \r
4966                         stopPropagation : function() {\r
4967                                 this.cancelBubble = true;\r
4968                         }\r
4969                 }\r
4970         });\r
4971 \r
4972         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4973 \r
4974         // Dispatch DOM content loaded event for IE and Safari\r
4975         Event._wait(window);\r
4976 \r
4977         tinymce.addUnload(function() {\r
4978                 Event.destroy();\r
4979         });\r
4980 })(tinymce);\r
4981 \r
4982 (function(tinymce) {\r
4983         tinymce.dom.Element = function(id, settings) {\r
4984                 var t = this, dom, el;\r
4985 \r
4986                 t.settings = settings = settings || {};\r
4987                 t.id = id;\r
4988                 t.dom = dom = settings.dom || tinymce.DOM;\r
4989 \r
4990                 // Only IE leaks DOM references, this is a lot faster\r
4991                 if (!tinymce.isIE)\r
4992                         el = dom.get(t.id);\r
4993 \r
4994                 tinymce.each(\r
4995                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4996                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4997                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4998                                 'isHidden,setHTML,get').split(/,/)\r
4999                         , function(k) {\r
5000                                 t[k] = function() {\r
5001                                         var a = [id], i;\r
5002 \r
5003                                         for (i = 0; i < arguments.length; i++)\r
5004                                                 a.push(arguments[i]);\r
5005 \r
5006                                         a = dom[k].apply(dom, a);\r
5007                                         t.update(k);\r
5008 \r
5009                                         return a;\r
5010                                 };\r
5011                 });\r
5012 \r
5013                 tinymce.extend(t, {\r
5014                         on : function(n, f, s) {\r
5015                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
5016                         },\r
5017 \r
5018                         getXY : function() {\r
5019                                 return {\r
5020                                         x : parseInt(t.getStyle('left')),\r
5021                                         y : parseInt(t.getStyle('top'))\r
5022                                 };\r
5023                         },\r
5024 \r
5025                         getSize : function() {\r
5026                                 var n = dom.get(t.id);\r
5027 \r
5028                                 return {\r
5029                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
5030                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
5031                                 };\r
5032                         },\r
5033 \r
5034                         moveTo : function(x, y) {\r
5035                                 t.setStyles({left : x, top : y});\r
5036                         },\r
5037 \r
5038                         moveBy : function(x, y) {\r
5039                                 var p = t.getXY();\r
5040 \r
5041                                 t.moveTo(p.x + x, p.y + y);\r
5042                         },\r
5043 \r
5044                         resizeTo : function(w, h) {\r
5045                                 t.setStyles({width : w, height : h});\r
5046                         },\r
5047 \r
5048                         resizeBy : function(w, h) {\r
5049                                 var s = t.getSize();\r
5050 \r
5051                                 t.resizeTo(s.w + w, s.h + h);\r
5052                         },\r
5053 \r
5054                         update : function(k) {\r
5055                                 var b;\r
5056 \r
5057                                 if (tinymce.isIE6 && settings.blocker) {\r
5058                                         k = k || '';\r
5059 \r
5060                                         // Ignore getters\r
5061                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
5062                                                 return;\r
5063 \r
5064                                         // Remove blocker on remove\r
5065                                         if (k == 'remove') {\r
5066                                                 dom.remove(t.blocker);\r
5067                                                 return;\r
5068                                         }\r
5069 \r
5070                                         if (!t.blocker) {\r
5071                                                 t.blocker = dom.uniqueId();\r
5072                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
5073                                                 dom.setStyle(b, 'opacity', 0);\r
5074                                         } else\r
5075                                                 b = dom.get(t.blocker);\r
5076 \r
5077                                         dom.setStyles(b, {\r
5078                                                 left : t.getStyle('left', 1),\r
5079                                                 top : t.getStyle('top', 1),\r
5080                                                 width : t.getStyle('width', 1),\r
5081                                                 height : t.getStyle('height', 1),\r
5082                                                 display : t.getStyle('display', 1),\r
5083                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
5084                                         });\r
5085                                 }\r
5086                         }\r
5087                 });\r
5088         };\r
5089 })(tinymce);\r
5090 \r
5091 (function(tinymce) {\r
5092         function trimNl(s) {\r
5093                 return s.replace(/[\n\r]+/g, '');\r
5094         };\r
5095 \r
5096         // Shorten names\r
5097         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
5098 \r
5099         tinymce.create('tinymce.dom.Selection', {\r
5100                 Selection : function(dom, win, serializer) {\r
5101                         var t = this;\r
5102 \r
5103                         t.dom = dom;\r
5104                         t.win = win;\r
5105                         t.serializer = serializer;\r
5106 \r
5107                         // Add events\r
5108                         each([\r
5109                                 'onBeforeSetContent',\r
5110                                 'onBeforeGetContent',\r
5111                                 'onSetContent',\r
5112                                 'onGetContent'\r
5113                         ], function(e) {\r
5114                                 t[e] = new tinymce.util.Dispatcher(t);\r
5115                         });\r
5116 \r
5117                         // No W3C Range support\r
5118                         if (!t.win.getSelection)\r
5119                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
5120 \r
5121                         // Prevent leaks\r
5122                         tinymce.addUnload(t.destroy, t);\r
5123                 },\r
5124 \r
5125                 getContent : function(s) {\r
5126                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
5127 \r
5128                         s = s || {};\r
5129                         wb = wa = '';\r
5130                         s.get = true;\r
5131                         s.format = s.format || 'html';\r
5132                         t.onBeforeGetContent.dispatch(t, s);\r
5133 \r
5134                         if (s.format == 'text')\r
5135                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
5136 \r
5137                         if (r.cloneContents) {\r
5138                                 n = r.cloneContents();\r
5139 \r
5140                                 if (n)\r
5141                                         e.appendChild(n);\r
5142                         } else if (is(r.item) || is(r.htmlText))\r
5143                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
5144                         else\r
5145                                 e.innerHTML = r.toString();\r
5146 \r
5147                         // Keep whitespace before and after\r
5148                         if (/^\s/.test(e.innerHTML))\r
5149                                 wb = ' ';\r
5150 \r
5151                         if (/\s+$/.test(e.innerHTML))\r
5152                                 wa = ' ';\r
5153 \r
5154                         s.getInner = true;\r
5155 \r
5156                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
5157                         t.onGetContent.dispatch(t, s);\r
5158 \r
5159                         return s.content;\r
5160                 },\r
5161 \r
5162                 setContent : function(h, s) {\r
5163                         var t = this, r = t.getRng(), c, d = t.win.document;\r
5164 \r
5165                         s = s || {format : 'html'};\r
5166                         s.set = true;\r
5167                         h = s.content = t.dom.processHTML(h);\r
5168 \r
5169                         // Dispatch before set content event\r
5170                         t.onBeforeSetContent.dispatch(t, s);\r
5171                         h = s.content;\r
5172 \r
5173                         if (r.insertNode) {\r
5174                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
5175                                 h += '<span id="__caret">_</span>';\r
5176 \r
5177                                 // Delete and insert new node\r
5178                                 \r
5179                                 if (r.startContainer == d && r.endContainer ==  d) {\r
5180                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
5181                                         d.body.innerHTML = h;\r
5182                                 } else {\r
5183                                         r.deleteContents();\r
5184                                         if (d.body.childNodes.length == 0) {\r
5185                                                 d.body.innerHTML = h;\r
5186                                         } else {\r
5187                                                 r.insertNode(r.createContextualFragment(h));\r
5188                                         }\r
5189                                 }\r
5190 \r
5191                                 // Move to caret marker\r
5192                                 c = t.dom.get('__caret');\r
5193                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
5194                                 r = d.createRange();\r
5195                                 r.setStartBefore(c);\r
5196                                 r.setEndBefore(c);\r
5197                                 t.setRng(r);\r
5198 \r
5199                                 // Remove the caret position\r
5200                                 t.dom.remove('__caret');\r
5201                         } else {\r
5202                                 if (r.item) {\r
5203                                         // Delete content and get caret text selection\r
5204                                         d.execCommand('Delete', false, null);\r
5205                                         r = t.getRng();\r
5206                                 }\r
5207 \r
5208                                 r.pasteHTML(h);\r
5209                         }\r
5210 \r
5211                         // Dispatch set content event\r
5212                         t.onSetContent.dispatch(t, s);\r
5213                 },\r
5214 \r
5215                 getStart : function() {\r
5216                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
5217 \r
5218                         if (rng.duplicate || rng.item) {\r
5219                                 // Control selection, return first item\r
5220                                 if (rng.item)\r
5221                                         return rng.item(0);\r
5222 \r
5223                                 // Get start element\r
5224                                 checkRng = rng.duplicate();\r
5225                                 checkRng.collapse(1);\r
5226                                 startElement = checkRng.parentElement();\r
5227 \r
5228                                 // Check if range parent is inside the start element, then return the inner parent element\r
5229                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
5230                                 parentElement = node = rng.parentElement();\r
5231                                 while (node = node.parentNode) {\r
5232                                         if (node == startElement) {\r
5233                                                 startElement = parentElement;\r
5234                                                 break;\r
5235                                         }\r
5236                                 }\r
5237 \r
5238                                 // If start element is body element try to move to the first child if it exists\r
5239                                 if (startElement && startElement.nodeName == 'BODY')\r
5240                                         return startElement.firstChild || startElement;\r
5241 \r
5242                                 return startElement;\r
5243                         } else {\r
5244                                 startElement = rng.startContainer;\r
5245 \r
5246                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
5247                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
5248 \r
5249                                 if (startElement && startElement.nodeType == 3)\r
5250                                         return startElement.parentNode;\r
5251 \r
5252                                 return startElement;\r
5253                         }\r
5254                 },\r
5255 \r
5256                 getEnd : function() {\r
5257                         var t = this, r = t.getRng(), e, eo;\r
5258 \r
5259                         if (r.duplicate || r.item) {\r
5260                                 if (r.item)\r
5261                                         return r.item(0);\r
5262 \r
5263                                 r = r.duplicate();\r
5264                                 r.collapse(0);\r
5265                                 e = r.parentElement();\r
5266 \r
5267                                 if (e && e.nodeName == 'BODY')\r
5268                                         return e.lastChild || e;\r
5269 \r
5270                                 return e;\r
5271                         } else {\r
5272                                 e = r.endContainer;\r
5273                                 eo = r.endOffset;\r
5274 \r
5275                                 if (e.nodeType == 1 && e.hasChildNodes())\r
5276                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
5277 \r
5278                                 if (e && e.nodeType == 3)\r
5279                                         return e.parentNode;\r
5280 \r
5281                                 return e;\r
5282                         }\r
5283                 },\r
5284 \r
5285                 getBookmark : function(type, normalized) {\r
5286                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
5287 \r
5288                         function findIndex(name, element) {\r
5289                                 var index = 0;\r
5290 \r
5291                                 each(dom.select(name), function(node, i) {\r
5292                                         if (node == element)\r
5293                                                 index = i;\r
5294                                 });\r
5295 \r
5296                                 return index;\r
5297                         };\r
5298 \r
5299                         if (type == 2) {\r
5300                                 function getLocation() {\r
5301                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
5302 \r
5303                                         function getPoint(rng, start) {\r
5304                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
5305                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
5306 \r
5307                                                 if (container.nodeType == 3) {\r
5308                                                         if (normalized) {\r
5309                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
5310                                                                         offset += node.nodeValue.length;\r
5311                                                         }\r
5312 \r
5313                                                         point.push(offset);\r
5314                                                 } else {\r
5315                                                         childNodes = container.childNodes;\r
5316 \r
5317                                                         if (offset >= childNodes.length && childNodes.length) {\r
5318                                                                 after = 1;\r
5319                                                                 offset = Math.max(0, childNodes.length - 1);\r
5320                                                         }\r
5321 \r
5322                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
5323                                                 }\r
5324 \r
5325                                                 for (; container && container != root; container = container.parentNode)\r
5326                                                         point.push(t.dom.nodeIndex(container, normalized));\r
5327 \r
5328                                                 return point;\r
5329                                         };\r
5330 \r
5331                                         bookmark.start = getPoint(rng, true);\r
5332 \r
5333                                         if (!t.isCollapsed())\r
5334                                                 bookmark.end = getPoint(rng);\r
5335 \r
5336                                         return bookmark;\r
5337                                 };\r
5338 \r
5339                                 return getLocation();\r
5340                         }\r
5341 \r
5342                         // Handle simple range\r
5343                         if (type)\r
5344                                 return {rng : t.getRng()};\r
5345 \r
5346                         rng = t.getRng();\r
5347                         id = dom.uniqueId();\r
5348                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
5349                         styles = 'overflow:hidden;line-height:0px';\r
5350 \r
5351                         // Explorer method\r
5352                         if (rng.duplicate || rng.item) {\r
5353                                 // Text selection\r
5354                                 if (!rng.item) {\r
5355                                         rng2 = rng.duplicate();\r
5356 \r
5357                                         // Insert start marker\r
5358                                         rng.collapse();\r
5359                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
5360 \r
5361                                         // Insert end marker\r
5362                                         if (!collapsed) {\r
5363                                                 rng2.collapse(false);\r
5364                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
5365                                         }\r
5366                                 } else {\r
5367                                         // Control selection\r
5368                                         element = rng.item(0);\r
5369                                         name = element.nodeName;\r
5370 \r
5371                                         return {name : name, index : findIndex(name, element)};\r
5372                                 }\r
5373                         } else {\r
5374                                 element = t.getNode();\r
5375                                 name = element.nodeName;\r
5376                                 if (name == 'IMG')\r
5377                                         return {name : name, index : findIndex(name, element)};\r
5378 \r
5379                                 // W3C method\r
5380                                 rng2 = rng.cloneRange();\r
5381 \r
5382                                 // Insert end marker\r
5383                                 if (!collapsed) {\r
5384                                         rng2.collapse(false);\r
5385                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
5386                                 }\r
5387 \r
5388                                 rng.collapse(true);\r
5389                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
5390                         }\r
5391 \r
5392                         t.moveToBookmark({id : id, keep : 1});\r
5393 \r
5394                         return {id : id};\r
5395                 },\r
5396 \r
5397                 moveToBookmark : function(bookmark) {\r
5398                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
5399 \r
5400                         // Clear selection cache\r
5401                         if (t.tridentSel)\r
5402                                 t.tridentSel.destroy();\r
5403 \r
5404                         if (bookmark) {\r
5405                                 if (bookmark.start) {\r
5406                                         rng = dom.createRng();\r
5407                                         root = dom.getRoot();\r
5408 \r
5409                                         function setEndPoint(start) {\r
5410                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
5411 \r
5412                                                 if (point) {\r
5413                                                         // Find container node\r
5414                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
5415                                                                 children = node.childNodes;\r
5416 \r
5417                                                                 if (children.length)\r
5418                                                                         node = children[point[i]];\r
5419                                                         }\r
5420 \r
5421                                                         // Set offset within container node\r
5422                                                         if (start)\r
5423                                                                 rng.setStart(node, point[0]);\r
5424                                                         else\r
5425                                                                 rng.setEnd(node, point[0]);\r
5426                                                 }\r
5427                                         };\r
5428 \r
5429                                         setEndPoint(true);\r
5430                                         setEndPoint();\r
5431 \r
5432                                         t.setRng(rng);\r
5433                                 } else if (bookmark.id) {\r
5434                                         function restoreEndPoint(suffix) {\r
5435                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
5436 \r
5437                                                 if (marker) {\r
5438                                                         node = marker.parentNode;\r
5439 \r
5440                                                         if (suffix == 'start') {\r
5441                                                                 if (!keep) {\r
5442                                                                         idx = dom.nodeIndex(marker);\r
5443                                                                 } else {\r
5444                                                                         node = marker.firstChild;\r
5445                                                                         idx = 1;\r
5446                                                                 }\r
5447 \r
5448                                                                 startContainer = endContainer = node;\r
5449                                                                 startOffset = endOffset = idx;\r
5450                                                         } else {\r
5451                                                                 if (!keep) {\r
5452                                                                         idx = dom.nodeIndex(marker);\r
5453                                                                 } else {\r
5454                                                                         node = marker.firstChild;\r
5455                                                                         idx = 1;\r
5456                                                                 }\r
5457 \r
5458                                                                 endContainer = node;\r
5459                                                                 endOffset = idx;\r
5460                                                         }\r
5461 \r
5462                                                         if (!keep) {\r
5463                                                                 prev = marker.previousSibling;\r
5464                                                                 next = marker.nextSibling;\r
5465 \r
5466                                                                 // Remove all marker text nodes\r
5467                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
5468                                                                         if (node.nodeType == 3)\r
5469                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
5470                                                                 });\r
5471 \r
5472                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
5473                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
5474                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
5475                                                                         dom.remove(marker, 1);\r
5476 \r
5477                                                                 // If siblings are text nodes then merge them\r
5478                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) {\r
5479                                                                         idx = prev.nodeValue.length;\r
5480                                                                         prev.appendData(next.nodeValue);\r
5481                                                                         dom.remove(next);\r
5482 \r
5483                                                                         if (suffix == 'start') {\r
5484                                                                                 startContainer = endContainer = prev;\r
5485                                                                                 startOffset = endOffset = idx;\r
5486                                                                         } else {\r
5487                                                                                 endContainer = prev;\r
5488                                                                                 endOffset = idx;\r
5489                                                                         }\r
5490                                                                 }\r
5491                                                         }\r
5492                                                 }\r
5493                                         };\r
5494 \r
5495                                         function addBogus(node) {\r
5496                                                 // Adds a bogus BR element for empty block elements\r
5497                                                 // on non IE browsers just to have a place to put the caret\r
5498                                                 if (!isIE && dom.isBlock(node) && !node.innerHTML)\r
5499                                                         node.innerHTML = '<br _mce_bogus="1" />';\r
5500 \r
5501                                                 return node;\r
5502                                         };\r
5503 \r
5504                                         // Restore start/end points\r
5505                                         restoreEndPoint('start');\r
5506                                         restoreEndPoint('end');\r
5507 \r
5508                                         rng = dom.createRng();\r
5509                                         rng.setStart(addBogus(startContainer), startOffset);\r
5510                                         rng.setEnd(addBogus(endContainer), endOffset);\r
5511                                         t.setRng(rng);\r
5512                                 } else if (bookmark.name) {\r
5513                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
5514                                 } else if (bookmark.rng)\r
5515                                         t.setRng(bookmark.rng);\r
5516                         }\r
5517                 },\r
5518 \r
5519                 select : function(node, content) {\r
5520                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
5521 \r
5522                         idx = dom.nodeIndex(node);\r
5523                         rng.setStart(node.parentNode, idx);\r
5524                         rng.setEnd(node.parentNode, idx + 1);\r
5525 \r
5526                         // Find first/last text node or BR element\r
5527                         if (content) {\r
5528                                 function setPoint(node, start) {\r
5529                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
5530 \r
5531                                         do {\r
5532                                                 // Text node\r
5533                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
5534                                                         if (start)\r
5535                                                                 rng.setStart(node, 0);\r
5536                                                         else\r
5537                                                                 rng.setEnd(node, node.nodeValue.length);\r
5538 \r
5539                                                         return;\r
5540                                                 }\r
5541 \r
5542                                                 // BR element\r
5543                                                 if (node.nodeName == 'BR') {\r
5544                                                         if (start)\r
5545                                                                 rng.setStartBefore(node);\r
5546                                                         else\r
5547                                                                 rng.setEndBefore(node);\r
5548 \r
5549                                                         return;\r
5550                                                 }\r
5551                                         } while (node = (start ? walker.next() : walker.prev()));\r
5552                                 };\r
5553 \r
5554                                 setPoint(node, 1);\r
5555                                 setPoint(node);\r
5556                         }\r
5557 \r
5558                         t.setRng(rng);\r
5559 \r
5560                         return node;\r
5561                 },\r
5562 \r
5563                 isCollapsed : function() {\r
5564                         var t = this, r = t.getRng(), s = t.getSel();\r
5565 \r
5566                         if (!r || r.item)\r
5567                                 return false;\r
5568 \r
5569                         if (r.compareEndPoints)\r
5570                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
5571 \r
5572                         return !s || r.collapsed;\r
5573                 },\r
5574 \r
5575                 collapse : function(b) {\r
5576                         var t = this, r = t.getRng(), n;\r
5577 \r
5578                         // Control range on IE\r
5579                         if (r.item) {\r
5580                                 n = r.item(0);\r
5581                                 r = this.win.document.body.createTextRange();\r
5582                                 r.moveToElementText(n);\r
5583                         }\r
5584 \r
5585                         r.collapse(!!b);\r
5586                         t.setRng(r);\r
5587                 },\r
5588 \r
5589                 getSel : function() {\r
5590                         var t = this, w = this.win;\r
5591 \r
5592                         return w.getSelection ? w.getSelection() : w.document.selection;\r
5593                 },\r
5594 \r
5595                 getRng : function(w3c) {\r
5596                         var t = this, s, r;\r
5597 \r
5598                         // Found tridentSel object then we need to use that one\r
5599                         if (w3c && t.tridentSel)\r
5600                                 return t.tridentSel.getRangeAt(0);\r
5601 \r
5602                         try {\r
5603                                 if (s = t.getSel())\r
5604                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
5605                         } catch (ex) {\r
5606                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
5607                         }\r
5608 \r
5609                         // No range found then create an empty one\r
5610                         // This can occur when the editor is placed in a hidden container element on Gecko\r
5611                         // Or on IE when there was an exception\r
5612                         if (!r)\r
5613                                 r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();\r
5614 \r
5615                         if (t.selectedRange && t.explicitRange) {\r
5616                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
5617                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
5618                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
5619                                         r = t.explicitRange;\r
5620                                 } else {\r
5621                                         t.selectedRange = null;\r
5622                                         t.explicitRange = null;\r
5623                                 }\r
5624                         }\r
5625                         return r;\r
5626                 },\r
5627 \r
5628                 setRng : function(r) {\r
5629                         var s, t = this;\r
5630                         \r
5631                         if (!t.tridentSel) {\r
5632                                 s = t.getSel();\r
5633 \r
5634                                 if (s) {\r
5635                                         t.explicitRange = r;\r
5636                                         s.removeAllRanges();\r
5637                                         s.addRange(r);\r
5638                                         t.selectedRange = s.getRangeAt(0);\r
5639                                 }\r
5640                         } else {\r
5641                                 // Is W3C Range\r
5642                                 if (r.cloneRange) {\r
5643                                         t.tridentSel.addRange(r);\r
5644                                         return;\r
5645                                 }\r
5646 \r
5647                                 // Is IE specific range\r
5648                                 try {\r
5649                                         r.select();\r
5650                                 } catch (ex) {\r
5651                                         // Needed for some odd IE bug #1843306\r
5652                                 }\r
5653                         }\r
5654                 },\r
5655 \r
5656                 setNode : function(n) {\r
5657                         var t = this;\r
5658 \r
5659                         t.setContent(t.dom.getOuterHTML(n));\r
5660 \r
5661                         return n;\r
5662                 },\r
5663 \r
5664                 getNode : function() {\r
5665                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
5666 \r
5667                         if (rng.setStart) {\r
5668                                 // Range maybe lost after the editor is made visible again\r
5669                                 if (!rng)\r
5670                                         return t.dom.getRoot();\r
5671 \r
5672                                 elm = rng.commonAncestorContainer;\r
5673 \r
5674                                 // Handle selection a image or other control like element such as anchors\r
5675                                 if (!rng.collapsed) {\r
5676                                         if (rng.startContainer == rng.endContainer) {\r
5677                                                 if (rng.startOffset - rng.endOffset < 2) {\r
5678                                                         if (rng.startContainer.hasChildNodes())\r
5679                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
5680                                                 }\r
5681                                         }\r
5682 \r
5683                                         // If the anchor node is a element instead of a text node then return this element\r
5684                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
5685                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
5686                                 }\r
5687 \r
5688                                 if (elm && elm.nodeType == 3)\r
5689                                         return elm.parentNode;\r
5690 \r
5691                                 return elm;\r
5692                         }\r
5693 \r
5694                         return rng.item ? rng.item(0) : rng.parentElement();\r
5695                 },\r
5696 \r
5697                 getSelectedBlocks : function(st, en) {\r
5698                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
5699 \r
5700                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
5701                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
5702 \r
5703                         if (sb)\r
5704                                 bl.push(sb);\r
5705 \r
5706                         if (sb && eb && sb != eb) {\r
5707                                 n = sb;\r
5708 \r
5709                                 while ((n = n.nextSibling) && n != eb) {\r
5710                                         if (dom.isBlock(n))\r
5711                                                 bl.push(n);\r
5712                                 }\r
5713                         }\r
5714 \r
5715                         if (eb && sb != eb)\r
5716                                 bl.push(eb);\r
5717 \r
5718                         return bl;\r
5719                 },\r
5720 \r
5721                 destroy : function(s) {\r
5722                         var t = this;\r
5723 \r
5724                         t.win = null;\r
5725 \r
5726                         if (t.tridentSel)\r
5727                                 t.tridentSel.destroy();\r
5728 \r
5729                         // Manual destroy then remove unload handler\r
5730                         if (!s)\r
5731                                 tinymce.removeUnload(t.destroy);\r
5732                 }\r
5733         });\r
5734 })(tinymce);\r
5735 \r
5736 (function(tinymce) {\r
5737         tinymce.create('tinymce.dom.XMLWriter', {\r
5738                 node : null,\r
5739 \r
5740                 XMLWriter : function(s) {\r
5741                         // Get XML document\r
5742                         function getXML() {\r
5743                                 var i = document.implementation;\r
5744 \r
5745                                 if (!i || !i.createDocument) {\r
5746                                         // Try IE objects\r
5747                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
5748                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
5749                                 } else\r
5750                                         return i.createDocument('', '', null);\r
5751                         };\r
5752 \r
5753                         this.doc = getXML();\r
5754                         \r
5755                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
5756                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
5757 \r
5758                         this.reset();\r
5759                 },\r
5760 \r
5761                 reset : function() {\r
5762                         var t = this, d = t.doc;\r
5763 \r
5764                         if (d.firstChild)\r
5765                                 d.removeChild(d.firstChild);\r
5766 \r
5767                         t.node = d.appendChild(d.createElement("html"));\r
5768                 },\r
5769 \r
5770                 writeStartElement : function(n) {\r
5771                         var t = this;\r
5772 \r
5773                         t.node = t.node.appendChild(t.doc.createElement(n));\r
5774                 },\r
5775 \r
5776                 writeAttribute : function(n, v) {\r
5777                         if (this.valid)\r
5778                                 v = v.replace(/>/g, '%MCGT%');\r
5779 \r
5780                         this.node.setAttribute(n, v);\r
5781                 },\r
5782 \r
5783                 writeEndElement : function() {\r
5784                         this.node = this.node.parentNode;\r
5785                 },\r
5786 \r
5787                 writeFullEndElement : function() {\r
5788                         var t = this, n = t.node;\r
5789 \r
5790                         n.appendChild(t.doc.createTextNode(""));\r
5791                         t.node = n.parentNode;\r
5792                 },\r
5793 \r
5794                 writeText : function(v) {\r
5795                         if (this.valid)\r
5796                                 v = v.replace(/>/g, '%MCGT%');\r
5797 \r
5798                         this.node.appendChild(this.doc.createTextNode(v));\r
5799                 },\r
5800 \r
5801                 writeCDATA : function(v) {\r
5802                         this.node.appendChild(this.doc.createCDATASection(v));\r
5803                 },\r
5804 \r
5805                 writeComment : function(v) {\r
5806                         // Fix for bug #2035694\r
5807                         if (tinymce.isIE)\r
5808                                 v = v.replace(/^\-|\-$/g, ' ');\r
5809 \r
5810                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
5811                 },\r
5812 \r
5813                 getContent : function() {\r
5814                         var h;\r
5815 \r
5816                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
5817                         h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
5818                         h = h.replace(/ ?\/>/g, ' />');\r
5819 \r
5820                         if (this.valid)\r
5821                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
5822 \r
5823                         return h;\r
5824                 }\r
5825         });\r
5826 })(tinymce);\r
5827 \r
5828 (function(tinymce) {\r
5829         tinymce.create('tinymce.dom.StringWriter', {\r
5830                 str : null,\r
5831                 tags : null,\r
5832                 count : 0,\r
5833                 settings : null,\r
5834                 indent : null,\r
5835 \r
5836                 StringWriter : function(s) {\r
5837                         this.settings = tinymce.extend({\r
5838                                 indent_char : ' ',\r
5839                                 indentation : 0\r
5840                         }, s);\r
5841 \r
5842                         this.reset();\r
5843                 },\r
5844 \r
5845                 reset : function() {\r
5846                         this.indent = '';\r
5847                         this.str = "";\r
5848                         this.tags = [];\r
5849                         this.count = 0;\r
5850                 },\r
5851 \r
5852                 writeStartElement : function(n) {\r
5853                         this._writeAttributesEnd();\r
5854                         this.writeRaw('<' + n);\r
5855                         this.tags.push(n);\r
5856                         this.inAttr = true;\r
5857                         this.count++;\r
5858                         this.elementCount = this.count;\r
5859                 },\r
5860 \r
5861                 writeAttribute : function(n, v) {\r
5862                         var t = this;\r
5863 \r
5864                         t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
5865                 },\r
5866 \r
5867                 writeEndElement : function() {\r
5868                         var n;\r
5869 \r
5870                         if (this.tags.length > 0) {\r
5871                                 n = this.tags.pop();\r
5872 \r
5873                                 if (this._writeAttributesEnd(1))\r
5874                                         this.writeRaw('</' + n + '>');\r
5875 \r
5876                                 if (this.settings.indentation > 0)\r
5877                                         this.writeRaw('\n');\r
5878                         }\r
5879                 },\r
5880 \r
5881                 writeFullEndElement : function() {\r
5882                         if (this.tags.length > 0) {\r
5883                                 this._writeAttributesEnd();\r
5884                                 this.writeRaw('</' + this.tags.pop() + '>');\r
5885 \r
5886                                 if (this.settings.indentation > 0)\r
5887                                         this.writeRaw('\n');\r
5888                         }\r
5889                 },\r
5890 \r
5891                 writeText : function(v) {\r
5892                         this._writeAttributesEnd();\r
5893                         this.writeRaw(this.encode(v));\r
5894                         this.count++;\r
5895                 },\r
5896 \r
5897                 writeCDATA : function(v) {\r
5898                         this._writeAttributesEnd();\r
5899                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5900                         this.count++;\r
5901                 },\r
5902 \r
5903                 writeComment : function(v) {\r
5904                         this._writeAttributesEnd();\r
5905                         this.writeRaw('<!-- ' + v + '-->');\r
5906                         this.count++;\r
5907                 },\r
5908 \r
5909                 writeRaw : function(v) {\r
5910                         this.str += v;\r
5911                 },\r
5912 \r
5913                 encode : function(s) {\r
5914                         return s.replace(/[<>&"]/g, function(v) {\r
5915                                 switch (v) {\r
5916                                         case '<':\r
5917                                                 return '&lt;';\r
5918 \r
5919                                         case '>':\r
5920                                                 return '&gt;';\r
5921 \r
5922                                         case '&':\r
5923                                                 return '&amp;';\r
5924 \r
5925                                         case '"':\r
5926                                                 return '&quot;';\r
5927                                 }\r
5928 \r
5929                                 return v;\r
5930                         });\r
5931                 },\r
5932 \r
5933                 getContent : function() {\r
5934                         return this.str;\r
5935                 },\r
5936 \r
5937                 _writeAttributesEnd : function(s) {\r
5938                         if (!this.inAttr)\r
5939                                 return;\r
5940 \r
5941                         this.inAttr = false;\r
5942 \r
5943                         if (s && this.elementCount == this.count) {\r
5944                                 this.writeRaw(' />');\r
5945                                 return false;\r
5946                         }\r
5947 \r
5948                         this.writeRaw('>');\r
5949 \r
5950                         return true;\r
5951                 }\r
5952         });\r
5953 })(tinymce);\r
5954 \r
5955 (function(tinymce) {\r
5956         // Shorten names\r
5957         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5958 \r
5959         function wildcardToRE(s) {\r
5960                 return s.replace(/([?+*])/g, '.$1');\r
5961         };\r
5962 \r
5963         tinymce.create('tinymce.dom.Serializer', {\r
5964                 Serializer : function(s) {\r
5965                         var t = this;\r
5966 \r
5967                         t.key = 0;\r
5968                         t.onPreProcess = new Dispatcher(t);\r
5969                         t.onPostProcess = new Dispatcher(t);\r
5970 \r
5971                         try {\r
5972                                 t.writer = new tinymce.dom.XMLWriter();\r
5973                         } catch (ex) {\r
5974                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5975                                 t.writer = new tinymce.dom.StringWriter();\r
5976                         }\r
5977 \r
5978                         // Default settings\r
5979                         t.settings = s = extend({\r
5980                                 dom : tinymce.DOM,\r
5981                                 valid_nodes : 0,\r
5982                                 node_filter : 0,\r
5983                                 attr_filter : 0,\r
5984                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5985                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5986                                 entity_encoding : 'named',\r
5987                                 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
5988                                 valid_elements : '*[*]',\r
5989                                 extended_valid_elements : 0,\r
5990                                 invalid_elements : 0,\r
5991                                 fix_table_elements : 1,\r
5992                                 fix_list_elements : true,\r
5993                                 fix_content_duplication : true,\r
5994                                 convert_fonts_to_spans : false,\r
5995                                 font_size_classes : 0,\r
5996                                 apply_source_formatting : 0,\r
5997                                 indent_mode : 'simple',\r
5998                                 indent_char : '\t',\r
5999                                 indent_levels : 1,\r
6000                                 remove_linebreaks : 1,\r
6001                                 remove_redundant_brs : 1,\r
6002                                 element_format : 'xhtml'\r
6003                         }, s);\r
6004 \r
6005                         t.dom = s.dom;\r
6006                         t.schema = s.schema;\r
6007 \r
6008                         // Use raw entities if no entities are defined\r
6009                         if (s.entity_encoding == 'named' && !s.entities)\r
6010                                 s.entity_encoding = 'raw';\r
6011 \r
6012                         if (s.remove_redundant_brs) {\r
6013                                 t.onPostProcess.add(function(se, o) {\r
6014                                         // Remove single BR at end of block elements since they get rendered\r
6015                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
6016                                                 // Check if it's a single element\r
6017                                                 if (/^<br \/>\s*<\//.test(a))\r
6018                                                         return '</' + c + '>';\r
6019 \r
6020                                                 return a;\r
6021                                         });\r
6022                                 });\r
6023                         }\r
6024 \r
6025                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
6026                         if (s.element_format == 'html') {\r
6027                                 t.onPostProcess.add(function(se, o) {\r
6028                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
6029                                 });\r
6030                         }\r
6031 \r
6032                         if (s.fix_list_elements) {\r
6033                                 t.onPreProcess.add(function(se, o) {\r
6034                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
6035 \r
6036                                         function prevNode(e, n) {\r
6037                                                 var a = n.split(','), i;\r
6038 \r
6039                                                 while ((e = e.previousSibling) != null) {\r
6040                                                         for (i=0; i<a.length; i++) {\r
6041                                                                 if (e.nodeName == a[i])\r
6042                                                                         return e;\r
6043                                                         }\r
6044                                                 }\r
6045 \r
6046                                                 return null;\r
6047                                         };\r
6048 \r
6049                                         for (x=0; x<a.length; x++) {\r
6050                                                 nl = t.dom.select(a[x], o.node);\r
6051 \r
6052                                                 for (i=0; i<nl.length; i++) {\r
6053                                                         n = nl[i];\r
6054                                                         p = n.parentNode;\r
6055 \r
6056                                                         if (r.test(p.nodeName)) {\r
6057                                                                 np = prevNode(n, 'LI');\r
6058 \r
6059                                                                 if (!np) {\r
6060                                                                         np = t.dom.create('li');\r
6061                                                                         np.innerHTML = '&nbsp;';\r
6062                                                                         np.appendChild(n);\r
6063                                                                         p.insertBefore(np, p.firstChild);\r
6064                                                                 } else\r
6065                                                                         np.appendChild(n);\r
6066                                                         }\r
6067                                                 }\r
6068                                         }\r
6069                                 });\r
6070                         }\r
6071 \r
6072                         if (s.fix_table_elements) {\r
6073                                 t.onPreProcess.add(function(se, o) {\r
6074                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
6075                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
6076                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
6077                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
6078                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
6079 \r
6080                                                         if (parent.nodeName != 'TABLE') {\r
6081                                                                 try {\r
6082                                                                         t.dom.split(parent, n);\r
6083                                                                 } catch (ex) {\r
6084                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
6085                                                                 }\r
6086                                                         }\r
6087                                                 });\r
6088                                         }\r
6089                                 });\r
6090                         }\r
6091                 },\r
6092 \r
6093                 setEntities : function(s) {\r
6094                         var t = this, a, i, l = {}, v;\r
6095 \r
6096                         // No need to setup more than once\r
6097                         if (t.entityLookup)\r
6098                                 return;\r
6099 \r
6100                         // Build regex and lookup array\r
6101                         a = s.split(',');\r
6102                         for (i = 0; i < a.length; i += 2) {\r
6103                                 v = a[i];\r
6104 \r
6105                                 // Don't add default &amp; &quot; etc.\r
6106                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
6107                                         continue;\r
6108 \r
6109                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
6110 \r
6111                                 v = parseInt(a[i]).toString(16);\r
6112                         }\r
6113 \r
6114                         t.entityLookup = l;\r
6115                 },\r
6116 \r
6117                 setRules : function(s) {\r
6118                         var t = this;\r
6119 \r
6120                         t._setup();\r
6121                         t.rules = {};\r
6122                         t.wildRules = [];\r
6123                         t.validElements = {};\r
6124 \r
6125                         return t.addRules(s);\r
6126                 },\r
6127 \r
6128                 addRules : function(s) {\r
6129                         var t = this, dr;\r
6130 \r
6131                         if (!s)\r
6132                                 return;\r
6133 \r
6134                         t._setup();\r
6135 \r
6136                         each(s.split(','), function(s) {\r
6137                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
6138 \r
6139                                 // Extend with default rules\r
6140                                 if (dr)\r
6141                                         at = tinymce.extend([], dr.attribs);\r
6142 \r
6143                                 // Parse attributes\r
6144                                 if (p.length > 1) {\r
6145                                         each(p[1].split('|'), function(s) {\r
6146                                                 var ar = {}, i;\r
6147 \r
6148                                                 at = at || [];\r
6149 \r
6150                                                 // Parse attribute rule\r
6151                                                 s = s.replace(/::/g, '~');\r
6152                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
6153                                                 s[2] = s[2].replace(/~/g, ':');\r
6154 \r
6155                                                 // Add required attributes\r
6156                                                 if (s[1] == '!') {\r
6157                                                         ra = ra || [];\r
6158                                                         ra.push(s[2]);\r
6159                                                 }\r
6160 \r
6161                                                 // Remove inherited attributes\r
6162                                                 if (s[1] == '-') {\r
6163                                                         for (i = 0; i <at.length; i++) {\r
6164                                                                 if (at[i].name == s[2]) {\r
6165                                                                         at.splice(i, 1);\r
6166                                                                         return;\r
6167                                                                 }\r
6168                                                         }\r
6169                                                 }\r
6170 \r
6171                                                 switch (s[3]) {\r
6172                                                         // Add default attrib values\r
6173                                                         case '=':\r
6174                                                                 ar.defaultVal = s[4] || '';\r
6175                                                                 break;\r
6176 \r
6177                                                         // Add forced attrib values\r
6178                                                         case ':':\r
6179                                                                 ar.forcedVal = s[4];\r
6180                                                                 break;\r
6181 \r
6182                                                         // Add validation values\r
6183                                                         case '<':\r
6184                                                                 ar.validVals = s[4].split('?');\r
6185                                                                 break;\r
6186                                                 }\r
6187 \r
6188                                                 if (/[*.?]/.test(s[2])) {\r
6189                                                         wat = wat || [];\r
6190                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
6191                                                         wat.push(ar);\r
6192                                                 } else {\r
6193                                                         ar.name = s[2];\r
6194                                                         at.push(ar);\r
6195                                                 }\r
6196 \r
6197                                                 va.push(s[2]);\r
6198                                         });\r
6199                                 }\r
6200 \r
6201                                 // Handle element names\r
6202                                 each(tn, function(s, i) {\r
6203                                         var pr = s.charAt(0), x = 1, ru = {};\r
6204 \r
6205                                         // Extend with default rule data\r
6206                                         if (dr) {\r
6207                                                 if (dr.noEmpty)\r
6208                                                         ru.noEmpty = dr.noEmpty;\r
6209 \r
6210                                                 if (dr.fullEnd)\r
6211                                                         ru.fullEnd = dr.fullEnd;\r
6212 \r
6213                                                 if (dr.padd)\r
6214                                                         ru.padd = dr.padd;\r
6215                                         }\r
6216 \r
6217                                         // Handle prefixes\r
6218                                         switch (pr) {\r
6219                                                 case '-':\r
6220                                                         ru.noEmpty = true;\r
6221                                                         break;\r
6222 \r
6223                                                 case '+':\r
6224                                                         ru.fullEnd = true;\r
6225                                                         break;\r
6226 \r
6227                                                 case '#':\r
6228                                                         ru.padd = true;\r
6229                                                         break;\r
6230 \r
6231                                                 default:\r
6232                                                         x = 0;\r
6233                                         }\r
6234 \r
6235                                         tn[i] = s = s.substring(x);\r
6236                                         t.validElements[s] = 1;\r
6237 \r
6238                                         // Add element name or element regex\r
6239                                         if (/[*.?]/.test(tn[0])) {\r
6240                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
6241                                                 t.wildRules = t.wildRules || {};\r
6242                                                 t.wildRules.push(ru);\r
6243                                         } else {\r
6244                                                 ru.name = tn[0];\r
6245 \r
6246                                                 // Store away default rule\r
6247                                                 if (tn[0] == '@')\r
6248                                                         dr = ru;\r
6249 \r
6250                                                 t.rules[s] = ru;\r
6251                                         }\r
6252 \r
6253                                         ru.attribs = at;\r
6254 \r
6255                                         if (ra)\r
6256                                                 ru.requiredAttribs = ra;\r
6257 \r
6258                                         if (wat) {\r
6259                                                 // Build valid attributes regexp\r
6260                                                 s = '';\r
6261                                                 each(va, function(v) {\r
6262                                                         if (s)\r
6263                                                                 s += '|';\r
6264 \r
6265                                                         s += '(' + wildcardToRE(v) + ')';\r
6266                                                 });\r
6267                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
6268                                                 ru.wildAttribs = wat;\r
6269                                         }\r
6270                                 });\r
6271                         });\r
6272 \r
6273                         // Build valid elements regexp\r
6274                         s = '';\r
6275                         each(t.validElements, function(v, k) {\r
6276                                 if (s)\r
6277                                         s += '|';\r
6278 \r
6279                                 if (k != '@')\r
6280                                         s += k;\r
6281                         });\r
6282                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
6283 \r
6284                         //console.debug(t.validElementsRE.toString());\r
6285                         //console.dir(t.rules);\r
6286                         //console.dir(t.wildRules);\r
6287                 },\r
6288 \r
6289                 findRule : function(n) {\r
6290                         var t = this, rl = t.rules, i, r;\r
6291 \r
6292                         t._setup();\r
6293 \r
6294                         // Exact match\r
6295                         r = rl[n];\r
6296                         if (r)\r
6297                                 return r;\r
6298 \r
6299                         // Try wildcards\r
6300                         rl = t.wildRules;\r
6301                         for (i = 0; i < rl.length; i++) {\r
6302                                 if (rl[i].nameRE.test(n))\r
6303                                         return rl[i];\r
6304                         }\r
6305 \r
6306                         return null;\r
6307                 },\r
6308 \r
6309                 findAttribRule : function(ru, n) {\r
6310                         var i, wa = ru.wildAttribs;\r
6311 \r
6312                         for (i = 0; i < wa.length; i++) {\r
6313                                 if (wa[i].nameRE.test(n))\r
6314                                         return wa[i];\r
6315                         }\r
6316 \r
6317                         return null;\r
6318                 },\r
6319 \r
6320                 serialize : function(n, o) {\r
6321                         var h, t = this, doc, oldDoc, impl, selected;\r
6322 \r
6323                         t._setup();\r
6324                         o = o || {};\r
6325                         o.format = o.format || 'html';\r
6326                         t.processObj = o;\r
6327 \r
6328                         // IE looses the selected attribute on option elements so we need to store it\r
6329                         // See: http://support.microsoft.com/kb/829907\r
6330                         if (isIE) {\r
6331                                 selected = [];\r
6332                                 each(n.getElementsByTagName('option'), function(n) {\r
6333                                         var v = t.dom.getAttrib(n, 'selected');\r
6334 \r
6335                                         selected.push(v ? v : null);\r
6336                                 });\r
6337                         }\r
6338 \r
6339                         n = n.cloneNode(true);\r
6340 \r
6341                         // IE looses the selected attribute on option elements so we need to restore it\r
6342                         if (isIE) {\r
6343                                 each(n.getElementsByTagName('option'), function(n, i) {\r
6344                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
6345                                 });\r
6346                         }\r
6347 \r
6348                         // Nodes needs to be attached to something in WebKit/Opera\r
6349                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
6350                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
6351                         // This fix will make DOM ranges and make Sizzle happy!\r
6352                         impl = n.ownerDocument.implementation;\r
6353                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
6354                                 // Create an empty HTML document\r
6355                                 doc = impl.createHTMLDocument("");\r
6356 \r
6357                                 // Add the element or it's children if it's a body element to the new document\r
6358                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
6359                                         doc.body.appendChild(doc.importNode(node, true));\r
6360                                 });\r
6361 \r
6362                                 // Grab first child or body element for serialization\r
6363                                 if (n.nodeName != 'BODY')\r
6364                                         n = doc.body.firstChild;\r
6365                                 else\r
6366                                         n = doc.body;\r
6367 \r
6368                                 // set the new document in DOMUtils so createElement etc works\r
6369                                 oldDoc = t.dom.doc;\r
6370                                 t.dom.doc = doc;\r
6371                         }\r
6372 \r
6373                         t.key = '' + (parseInt(t.key) + 1);\r
6374 \r
6375                         // Pre process\r
6376                         if (!o.no_events) {\r
6377                                 o.node = n;\r
6378                                 t.onPreProcess.dispatch(t, o);\r
6379                         }\r
6380 \r
6381                         // Serialize HTML DOM into a string\r
6382                         t.writer.reset();\r
6383                         t._info = o;\r
6384                         t._serializeNode(n, o.getInner);\r
6385 \r
6386                         // Post process\r
6387                         o.content = t.writer.getContent();\r
6388 \r
6389                         // Restore the old document if it was changed\r
6390                         if (oldDoc)\r
6391                                 t.dom.doc = oldDoc;\r
6392 \r
6393                         if (!o.no_events)\r
6394                                 t.onPostProcess.dispatch(t, o);\r
6395 \r
6396                         t._postProcess(o);\r
6397                         o.node = null;\r
6398 \r
6399                         return tinymce.trim(o.content);\r
6400                 },\r
6401 \r
6402                 // Internal functions\r
6403 \r
6404                 _postProcess : function(o) {\r
6405                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
6406 \r
6407                         if (o.format == 'html') {\r
6408                                 // Protect some elements\r
6409                                 p = t._protect({\r
6410                                         content : h,\r
6411                                         patterns : [\r
6412                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
6413                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
6414                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
6415                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
6416                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
6417                                         ]\r
6418                                 });\r
6419 \r
6420                                 h = p.content;\r
6421 \r
6422                                 // Entity encode\r
6423                                 if (s.entity_encoding !== 'raw')\r
6424                                         h = t._encode(h);\r
6425 \r
6426                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
6427 /*                              if (o.set)\r
6428                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
6429                                 else\r
6430                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
6431 \r
6432                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
6433                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
6434                                 // This process is only done when getting contents out from the editor.\r
6435                                 if (!o.set) {\r
6436                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
6437                                         h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
6438 \r
6439                                         if (s.remove_linebreaks) {\r
6440                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
6441                                                 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
6442                                                 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
6443                                                 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
6444                                                 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
6445                                                 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
6446                                         }\r
6447 \r
6448                                         // Simple indentation\r
6449                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
6450                                                 // Add line breaks before and after block elements\r
6451                                                 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
6452                                                 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
6453                                                 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
6454                                                 h = h.replace(/\n\n/g, '\n');\r
6455                                         }\r
6456                                 }\r
6457 \r
6458                                 h = t._unprotect(h, p);\r
6459 \r
6460                                 // Restore CDATA sections\r
6461                                 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
6462 \r
6463                                 // Restore the \u00a0 character if raw mode is enabled\r
6464                                 if (s.entity_encoding == 'raw')\r
6465                                         h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
6466 \r
6467                                 // Restore noscript elements\r
6468                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
6469                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
6470                                 });\r
6471                         }\r
6472 \r
6473                         o.content = h;\r
6474                 },\r
6475 \r
6476                 _serializeNode : function(n, inner) {\r
6477                         var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type;\r
6478 \r
6479                         if (!s.node_filter || s.node_filter(n)) {\r
6480                                 switch (n.nodeType) {\r
6481                                         case 1: // Element\r
6482                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
6483                                                         return;\r
6484 \r
6485                                                 iv = keep = false;\r
6486                                                 hc = n.hasChildNodes();\r
6487                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
6488 \r
6489                                                 // Get internal type\r
6490                                                 type = n.getAttribute('_mce_type');\r
6491                                                 if (type) {\r
6492                                                         if (!t._info.cleanup) {\r
6493                                                                 iv = true;\r
6494                                                                 return;\r
6495                                                         } else\r
6496                                                                 keep = 1;\r
6497                                                 }\r
6498 \r
6499                                                 // Add correct prefix on IE\r
6500                                                 if (isIE) {\r
6501                                                         if (n.scopeName !== 'HTML' && n.scopeName !== 'html')\r
6502                                                                 nn = n.scopeName + ':' + nn;\r
6503                                                 }\r
6504 \r
6505                                                 // Remove mce prefix on IE needed for the abbr element\r
6506                                                 if (nn.indexOf('mce:') === 0)\r
6507                                                         nn = nn.substring(4);\r
6508 \r
6509                                                 // Check if valid\r
6510                                                 if (!keep) {\r
6511                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
6512                                                                 iv = true;\r
6513                                                                 break;\r
6514                                                         }\r
6515                                                 }\r
6516 \r
6517                                                 if (isIE) {\r
6518                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
6519                                                         if (s.fix_content_duplication) {\r
6520                                                                 if (n._mce_serialized == t.key)\r
6521                                                                         return;\r
6522 \r
6523                                                                 n._mce_serialized = t.key;\r
6524                                                         }\r
6525 \r
6526                                                         // IE sometimes adds a / infront of the node name\r
6527                                                         if (nn.charAt(0) == '/')\r
6528                                                                 nn = nn.substring(1);\r
6529                                                 } else if (isGecko) {\r
6530                                                         // Ignore br elements\r
6531                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
6532                                                                 return;\r
6533                                                 }\r
6534 \r
6535                                                 // Check if valid child\r
6536                                                 if (s.validate_children) {\r
6537                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
6538                                                                 iv = true;\r
6539                                                                 break;\r
6540                                                         }\r
6541 \r
6542                                                         t.elementName = nn;\r
6543                                                 }\r
6544 \r
6545                                                 ru = t.findRule(nn);\r
6546                                                 \r
6547                                                 // No valid rule for this element could be found then skip it\r
6548                                                 if (!ru) {\r
6549                                                         iv = true;\r
6550                                                         break;\r
6551                                                 }\r
6552 \r
6553                                                 nn = ru.name || nn;\r
6554                                                 closed = s.closed.test(nn);\r
6555 \r
6556                                                 // Skip empty nodes or empty node name in IE\r
6557                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
6558                                                         iv = true;\r
6559                                                         break;\r
6560                                                 }\r
6561 \r
6562                                                 // Check required\r
6563                                                 if (ru.requiredAttribs) {\r
6564                                                         a = ru.requiredAttribs;\r
6565 \r
6566                                                         for (i = a.length - 1; i >= 0; i--) {\r
6567                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
6568                                                                         break;\r
6569                                                         }\r
6570 \r
6571                                                         // None of the required was there\r
6572                                                         if (i == -1) {\r
6573                                                                 iv = true;\r
6574                                                                 break;\r
6575                                                         }\r
6576                                                 }\r
6577 \r
6578                                                 w.writeStartElement(nn);\r
6579 \r
6580                                                 // Add ordered attributes\r
6581                                                 if (ru.attribs) {\r
6582                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
6583                                                                 a = at[i];\r
6584                                                                 v = t._getAttrib(n, a);\r
6585 \r
6586                                                                 if (v !== null)\r
6587                                                                         w.writeAttribute(a.name, v);\r
6588                                                         }\r
6589                                                 }\r
6590 \r
6591                                                 // Add wild attributes\r
6592                                                 if (ru.validAttribsRE) {\r
6593                                                         at = t.dom.getAttribs(n);\r
6594                                                         for (i=at.length-1; i>-1; i--) {\r
6595                                                                 no = at[i];\r
6596 \r
6597                                                                 if (no.specified) {\r
6598                                                                         a = no.nodeName.toLowerCase();\r
6599 \r
6600                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
6601                                                                                 continue;\r
6602 \r
6603                                                                         ar = t.findAttribRule(ru, a);\r
6604                                                                         v = t._getAttrib(n, ar, a);\r
6605 \r
6606                                                                         if (v !== null)\r
6607                                                                                 w.writeAttribute(a, v);\r
6608                                                                 }\r
6609                                                         }\r
6610                                                 }\r
6611 \r
6612                                                 // Keep type attribute\r
6613                                                 if (type && keep)\r
6614                                                         w.writeAttribute('_mce_type', type);\r
6615 \r
6616                                                 // Write text from script\r
6617                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
6618                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
6619                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
6620                                                         hc = false;\r
6621                                                         break;\r
6622                                                 }\r
6623 \r
6624                                                 // Padd empty nodes with a &nbsp;\r
6625                                                 if (ru.padd) {\r
6626                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
6627                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
6628                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
6629                                                                         w.writeText('\u00a0');\r
6630                                                         } else if (!hc)\r
6631                                                                 w.writeText('\u00a0'); // No children then padd it\r
6632                                                 }\r
6633 \r
6634                                                 break;\r
6635 \r
6636                                         case 3: // Text\r
6637                                                 // Check if valid child\r
6638                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
6639                                                         return;\r
6640 \r
6641                                                 return w.writeText(n.nodeValue);\r
6642 \r
6643                                         case 4: // CDATA\r
6644                                                 return w.writeCDATA(n.nodeValue);\r
6645 \r
6646                                         case 8: // Comment\r
6647                                                 return w.writeComment(n.nodeValue);\r
6648                                 }\r
6649                         } else if (n.nodeType == 1)\r
6650                                 hc = n.hasChildNodes();\r
6651 \r
6652                         if (hc && !closed) {\r
6653                                 cn = n.firstChild;\r
6654 \r
6655                                 while (cn) {\r
6656                                         t._serializeNode(cn);\r
6657                                         t.elementName = nn;\r
6658                                         cn = cn.nextSibling;\r
6659                                 }\r
6660                         }\r
6661 \r
6662                         // Write element end\r
6663                         if (!iv) {\r
6664                                 if (!closed)\r
6665                                         w.writeFullEndElement();\r
6666                                 else\r
6667                                         w.writeEndElement();\r
6668                         }\r
6669                 },\r
6670 \r
6671                 _protect : function(o) {\r
6672                         var t = this;\r
6673 \r
6674                         o.items = o.items || [];\r
6675 \r
6676                         function enc(s) {\r
6677                                 return s.replace(/[\r\n\\]/g, function(c) {\r
6678                                         if (c === '\n')\r
6679                                                 return '\\n';\r
6680                                         else if (c === '\\')\r
6681                                                 return '\\\\';\r
6682 \r
6683                                         return '\\r';\r
6684                                 });\r
6685                         };\r
6686 \r
6687                         function dec(s) {\r
6688                                 return s.replace(/\\[\\rn]/g, function(c) {\r
6689                                         if (c === '\\n')\r
6690                                                 return '\n';\r
6691                                         else if (c === '\\\\')\r
6692                                                 return '\\';\r
6693 \r
6694                                         return '\r';\r
6695                                 });\r
6696                         };\r
6697 \r
6698                         each(o.patterns, function(p) {\r
6699                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
6700                                         b = dec(b);\r
6701 \r
6702                                         if (p.encode)\r
6703                                                 b = t._encode(b);\r
6704 \r
6705                                         o.items.push(b);\r
6706                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
6707                                 }));\r
6708                         });\r
6709 \r
6710                         return o;\r
6711                 },\r
6712 \r
6713                 _unprotect : function(h, o) {\r
6714                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
6715                                 return o.items[parseInt(b)];\r
6716                         });\r
6717 \r
6718                         o.items = [];\r
6719 \r
6720                         return h;\r
6721                 },\r
6722 \r
6723                 _encode : function(h) {\r
6724                         var t = this, s = t.settings, l;\r
6725 \r
6726                         // Entity encode\r
6727                         if (s.entity_encoding !== 'raw') {\r
6728                                 if (s.entity_encoding.indexOf('named') != -1) {\r
6729                                         t.setEntities(s.entities);\r
6730                                         l = t.entityLookup;\r
6731 \r
6732                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6733                                                 var v;\r
6734 \r
6735                                                 if (v = l[a])\r
6736                                                         a = '&' + v + ';';\r
6737 \r
6738                                                 return a;\r
6739                                         });\r
6740                                 }\r
6741 \r
6742                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
6743                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6744                                                 return '&#' + a.charCodeAt(0) + ';';\r
6745                                         });\r
6746                                 }\r
6747                         }\r
6748 \r
6749                         return h;\r
6750                 },\r
6751 \r
6752                 _setup : function() {\r
6753                         var t = this, s = this.settings;\r
6754 \r
6755                         if (t.done)\r
6756                                 return;\r
6757 \r
6758                         t.done = 1;\r
6759 \r
6760                         t.setRules(s.valid_elements);\r
6761                         t.addRules(s.extended_valid_elements);\r
6762 \r
6763                         if (s.invalid_elements)\r
6764                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
6765 \r
6766                         if (s.attrib_value_filter)\r
6767                                 t.attribValueFilter = s.attribValueFilter;\r
6768                 },\r
6769 \r
6770                 _getAttrib : function(n, a, na) {\r
6771                         var i, v;\r
6772 \r
6773                         na = na || a.name;\r
6774 \r
6775                         if (a.forcedVal && (v = a.forcedVal)) {\r
6776                                 if (v === '{$uid}')\r
6777                                         return this.dom.uniqueId();\r
6778 \r
6779                                 return v;\r
6780                         }\r
6781 \r
6782                         v = this.dom.getAttrib(n, na);\r
6783 \r
6784                         switch (na) {\r
6785                                 case 'rowspan':\r
6786                                 case 'colspan':\r
6787                                         // Whats the point? Remove usless attribute value\r
6788                                         if (v == '1')\r
6789                                                 v = '';\r
6790 \r
6791                                         break;\r
6792                         }\r
6793 \r
6794                         if (this.attribValueFilter)\r
6795                                 v = this.attribValueFilter(na, v, n);\r
6796 \r
6797                         if (a.validVals) {\r
6798                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
6799                                         if (v == a.validVals[i])\r
6800                                                 break;\r
6801                                 }\r
6802 \r
6803                                 if (i == -1)\r
6804                                         return null;\r
6805                         }\r
6806 \r
6807                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
6808                                 v = a.defaultVal;\r
6809 \r
6810                                 if (v === '{$uid}')\r
6811                                         return this.dom.uniqueId();\r
6812 \r
6813                                 return v;\r
6814                         } else {\r
6815                                 // Remove internal mceItemXX classes when content is extracted from editor\r
6816                                 if (na == 'class' && this.processObj.get)\r
6817                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
6818                         }\r
6819 \r
6820                         if (v === '')\r
6821                                 return null;\r
6822 \r
6823 \r
6824                         return v;\r
6825                 }\r
6826         });\r
6827 })(tinymce);\r
6828 \r
6829 (function(tinymce) {\r
6830         tinymce.dom.ScriptLoader = function(settings) {\r
6831                 var QUEUED = 0,\r
6832                         LOADING = 1,\r
6833                         LOADED = 2,\r
6834                         states = {},\r
6835                         queue = [],\r
6836                         scriptLoadedCallbacks = {},\r
6837                         queueLoadedCallbacks = [],\r
6838                         loading = 0,\r
6839                         undefined;\r
6840 \r
6841                 function loadScript(url, callback) {\r
6842                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
6843 \r
6844                         // Execute callback when script is loaded\r
6845                         function done() {\r
6846                                 dom.remove(id);\r
6847 \r
6848                                 if (elm)\r
6849                                         elm.onreadystatechange = elm.onload = elm = null;\r
6850 \r
6851                                 callback();\r
6852                         };\r
6853 \r
6854                         id = dom.uniqueId();\r
6855 \r
6856                         if (tinymce.isIE6) {\r
6857                                 uri = new tinymce.util.URI(url);\r
6858                                 loc = location;\r
6859 \r
6860                                 // If script is from same domain and we\r
6861                                 // use IE 6 then use XHR since it's more reliable\r
6862                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
6863                                         tinymce.util.XHR.send({\r
6864                                                 url : tinymce._addVer(uri.getURI()),\r
6865                                                 success : function(content) {\r
6866                                                         // Create new temp script element\r
6867                                                         var script = dom.create('script', {\r
6868                                                                 type : 'text/javascript'\r
6869                                                         });\r
6870 \r
6871                                                         // Evaluate script in global scope\r
6872                                                         script.text = content;\r
6873                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
6874                                                         dom.remove(script);\r
6875 \r
6876                                                         done();\r
6877                                                 }\r
6878                                         });\r
6879 \r
6880                                         return;\r
6881                                 }\r
6882                         }\r
6883 \r
6884                         // Create new script element\r
6885                         elm = dom.create('script', {\r
6886                                 id : id,\r
6887                                 type : 'text/javascript',\r
6888                                 src : tinymce._addVer(url)\r
6889                         });\r
6890 \r
6891                         // Add onload and readystate listeners\r
6892                         elm.onload = done;\r
6893                         elm.onreadystatechange = function() {\r
6894                                 var state = elm.readyState;\r
6895 \r
6896                                 // Loaded state is passed on IE 6 however there\r
6897                                 // are known issues with this method but we can't use\r
6898                                 // XHR in a cross domain loading\r
6899                                 if (state == 'complete' || state == 'loaded')\r
6900                                         done();\r
6901                         };\r
6902 \r
6903                         // Most browsers support this feature so we report errors\r
6904                         // for those at least to help users track their missing plugins etc\r
6905                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6906                         /*elm.onerror = function() {\r
6907                                 alert('Failed to load: ' + url);\r
6908                         };*/\r
6909 \r
6910                         // Add script to document\r
6911                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6912                 };\r
6913 \r
6914                 this.isDone = function(url) {\r
6915                         return states[url] == LOADED;\r
6916                 };\r
6917 \r
6918                 this.markDone = function(url) {\r
6919                         states[url] = LOADED;\r
6920                 };\r
6921 \r
6922                 this.add = this.load = function(url, callback, scope) {\r
6923                         var item, state = states[url];\r
6924 \r
6925                         // Add url to load queue\r
6926                         if (state == undefined) {\r
6927                                 queue.push(url);\r
6928                                 states[url] = QUEUED;\r
6929                         }\r
6930 \r
6931                         if (callback) {\r
6932                                 // Store away callback for later execution\r
6933                                 if (!scriptLoadedCallbacks[url])\r
6934                                         scriptLoadedCallbacks[url] = [];\r
6935 \r
6936                                 scriptLoadedCallbacks[url].push({\r
6937                                         func : callback,\r
6938                                         scope : scope || this\r
6939                                 });\r
6940                         }\r
6941                 };\r
6942 \r
6943                 this.loadQueue = function(callback, scope) {\r
6944                         this.loadScripts(queue, callback, scope);\r
6945                 };\r
6946 \r
6947                 this.loadScripts = function(scripts, callback, scope) {\r
6948                         var loadScripts;\r
6949 \r
6950                         function execScriptLoadedCallbacks(url) {\r
6951                                 // Execute URL callback functions\r
6952                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6953                                         callback.func.call(callback.scope);\r
6954                                 });\r
6955 \r
6956                                 scriptLoadedCallbacks[url] = undefined;\r
6957                         };\r
6958 \r
6959                         queueLoadedCallbacks.push({\r
6960                                 func : callback,\r
6961                                 scope : scope || this\r
6962                         });\r
6963 \r
6964                         loadScripts = function() {\r
6965                                 var loadingScripts = tinymce.grep(scripts);\r
6966 \r
6967                                 // Current scripts has been handled\r
6968                                 scripts.length = 0;\r
6969 \r
6970                                 // Load scripts that needs to be loaded\r
6971                                 tinymce.each(loadingScripts, function(url) {\r
6972                                         // Script is already loaded then execute script callbacks directly\r
6973                                         if (states[url] == LOADED) {\r
6974                                                 execScriptLoadedCallbacks(url);\r
6975                                                 return;\r
6976                                         }\r
6977 \r
6978                                         // Is script not loading then start loading it\r
6979                                         if (states[url] != LOADING) {\r
6980                                                 states[url] = LOADING;\r
6981                                                 loading++;\r
6982 \r
6983                                                 loadScript(url, function() {\r
6984                                                         states[url] = LOADED;\r
6985                                                         loading--;\r
6986 \r
6987                                                         execScriptLoadedCallbacks(url);\r
6988 \r
6989                                                         // Load more scripts if they where added by the recently loaded script\r
6990                                                         loadScripts();\r
6991                                                 });\r
6992                                         }\r
6993                                 });\r
6994 \r
6995                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6996                                 if (!loading) {\r
6997                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6998                                                 callback.func.call(callback.scope);\r
6999                                         });\r
7000 \r
7001                                         queueLoadedCallbacks.length = 0;\r
7002                                 }\r
7003                         };\r
7004 \r
7005                         loadScripts();\r
7006                 };\r
7007         };\r
7008 \r
7009         // Global script loader\r
7010         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
7011 })(tinymce);\r
7012 \r
7013 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
7014         var node = start_node;\r
7015 \r
7016         function findSibling(node, start_name, sibling_name, shallow) {\r
7017                 var sibling, parent;\r
7018 \r
7019                 if (node) {\r
7020                         // Walk into nodes if it has a start\r
7021                         if (!shallow && node[start_name])\r
7022                                 return node[start_name];\r
7023 \r
7024                         // Return the sibling if it has one\r
7025                         if (node != root_node) {\r
7026                                 sibling = node[sibling_name];\r
7027                                 if (sibling)\r
7028                                         return sibling;\r
7029 \r
7030                                 // Walk up the parents to look for siblings\r
7031                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
7032                                         sibling = parent[sibling_name];\r
7033                                         if (sibling)\r
7034                                                 return sibling;\r
7035                                 }\r
7036                         }\r
7037                 }\r
7038         };\r
7039 \r
7040         this.current = function() {\r
7041                 return node;\r
7042         };\r
7043 \r
7044         this.next = function(shallow) {\r
7045                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
7046         };\r
7047 \r
7048         this.prev = function(shallow) {\r
7049                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
7050         };\r
7051 };\r
7052 \r
7053 (function() {\r
7054         var transitional = {};\r
7055 \r
7056         function unpack(lookup, data) {\r
7057                 var key;\r
7058 \r
7059                 function replace(value) {\r
7060                         return value.replace(/[A-Z]+/g, function(key) {\r
7061                                 return replace(lookup[key]);\r
7062                         });\r
7063                 };\r
7064 \r
7065                 // Unpack lookup\r
7066                 for (key in lookup) {\r
7067                         if (lookup.hasOwnProperty(key))\r
7068                                 lookup[key] = replace(lookup[key]);\r
7069                 }\r
7070 \r
7071                 // Unpack and parse data into object map\r
7072                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
7073                         var i, map = {};\r
7074 \r
7075                         children = children.split(/\|/);\r
7076 \r
7077                         for (i = children.length - 1; i >= 0; i--)\r
7078                                 map[children[i]] = 1;\r
7079 \r
7080                         transitional[name] = map;\r
7081                 });\r
7082         };\r
7083 \r
7084         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
7085         // we will later include the attributes here and use it as a default for valid elements but it\r
7086         // requires us to rewrite the serializer engine\r
7087         unpack({\r
7088                 Z : '#|H|K|N|O|P',\r
7089                 Y : '#|X|form|R|Q',\r
7090                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
7091                 W : 'pre|hr|blockquote|address|center|noframes',\r
7092                 U : 'ul|ol|dl|menu|dir',\r
7093                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
7094                 T : 'h1|h2|h3|h4|h5|h6',\r
7095                 ZB : '#|X|S|Q',\r
7096                 S : 'R|P',\r
7097                 ZA : '#|a|G|J|M|O|P',\r
7098                 R : '#|a|H|K|N|O',\r
7099                 Q : 'noscript|P',\r
7100                 P : 'ins|del|script',\r
7101                 O : 'input|select|textarea|label|button',\r
7102                 N : 'M|L',\r
7103                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
7104                 L : 'sub|sup',\r
7105                 K : 'J|I',\r
7106                 J : 'tt|i|b|u|s|strike',\r
7107                 I : 'big|small|font|basefont',\r
7108                 H : 'G|F',\r
7109                 G : 'br|span|bdo',\r
7110                 F : 'object|applet|img|map|iframe'\r
7111         }, 'script[]' + \r
7112                 'style[]' + \r
7113                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
7114                 'param[]' + \r
7115                 'p[S]' + \r
7116                 'a[Z]' + \r
7117                 'br[]' + \r
7118                 'span[S]' + \r
7119                 'bdo[S]' + \r
7120                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
7121                 'h1[S]' + \r
7122                 'img[]' + \r
7123                 'map[X|form|Q|area]' + \r
7124                 'h2[S]' + \r
7125                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
7126                 'h3[S]' + \r
7127                 'tt[S]' + \r
7128                 'i[S]' + \r
7129                 'b[S]' + \r
7130                 'u[S]' + \r
7131                 's[S]' + \r
7132                 'strike[S]' + \r
7133                 'big[S]' + \r
7134                 'small[S]' + \r
7135                 'font[S]' + \r
7136                 'basefont[]' + \r
7137                 'em[S]' + \r
7138                 'strong[S]' + \r
7139                 'dfn[S]' + \r
7140                 'code[S]' + \r
7141                 'q[S]' + \r
7142                 'samp[S]' + \r
7143                 'kbd[S]' + \r
7144                 'var[S]' + \r
7145                 'cite[S]' + \r
7146                 'abbr[S]' + \r
7147                 'acronym[S]' + \r
7148                 'sub[S]' + \r
7149                 'sup[S]' + \r
7150                 'input[]' + \r
7151                 'select[optgroup|option]' + \r
7152                 'optgroup[option]' + \r
7153                 'option[]' + \r
7154                 'textarea[]' + \r
7155                 'label[S]' + \r
7156                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
7157                 'h4[S]' + \r
7158                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
7159                 'h5[S]' + \r
7160                 'del[#|X|form|a|H|K|N|O|Q]' + \r
7161                 'h6[S]' + \r
7162                 'div[#|X|form|a|H|K|N|O|Q]' + \r
7163                 'ul[li]' + \r
7164                 'li[#|X|form|a|H|K|N|O|Q]' + \r
7165                 'ol[li]' + \r
7166                 'dl[dt|dd]' + \r
7167                 'dt[S]' + \r
7168                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
7169                 'menu[li]' + \r
7170                 'dir[li]' + \r
7171                 'pre[ZA]' + \r
7172                 'hr[]' + \r
7173                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
7174                 'address[S|p]' + \r
7175                 'center[#|X|form|a|H|K|N|O|Q]' + \r
7176                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
7177                 'isindex[]' + \r
7178                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
7179                 'legend[S]' + \r
7180                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
7181                 'caption[S]' + \r
7182                 'col[]' + \r
7183                 'colgroup[col]' + \r
7184                 'thead[tr]' + \r
7185                 'tr[th|td]' + \r
7186                 'th[#|X|form|a|H|K|N|O|Q]' + \r
7187                 'form[#|X|a|H|K|N|O|Q]' + \r
7188                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
7189                 'td[#|X|form|a|H|K|N|O|Q]' + \r
7190                 'tfoot[tr]' + \r
7191                 'tbody[tr]' + \r
7192                 'area[]' + \r
7193                 'base[]' + \r
7194                 'body[#|X|form|a|H|K|N|O|Q]'\r
7195         );\r
7196 \r
7197         tinymce.dom.Schema = function() {\r
7198                 var t = this, elements = transitional;\r
7199 \r
7200                 t.isValid = function(name, child_name) {\r
7201                         var element = elements[name];\r
7202 \r
7203                         return !!(element && (!child_name || element[child_name]));\r
7204                 };\r
7205         };\r
7206 })();\r
7207 (function(tinymce) {\r
7208         tinymce.dom.RangeUtils = function(dom) {\r
7209                 var INVISIBLE_CHAR = '\uFEFF';\r
7210 \r
7211                 this.walk = function(rng, callback) {\r
7212                         var startContainer = rng.startContainer,\r
7213                                 startOffset = rng.startOffset,\r
7214                                 endContainer = rng.endContainer,\r
7215                                 endOffset = rng.endOffset,\r
7216                                 ancestor, startPoint,\r
7217                                 endPoint, node, parent, siblings, nodes;\r
7218 \r
7219                         // Handle table cell selection the table plugin enables\r
7220                         // you to fake select table cells and perform formatting actions on them\r
7221                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
7222                         if (nodes.length > 0) {\r
7223                                 tinymce.each(nodes, function(node) {\r
7224                                         callback([node]);\r
7225                                 });\r
7226 \r
7227                                 return;\r
7228                         }\r
7229 \r
7230                         function collectSiblings(node, name, end_node) {\r
7231                                 var siblings = [];\r
7232 \r
7233                                 for (; node && node != end_node; node = node[name])\r
7234                                         siblings.push(node);\r
7235 \r
7236                                 return siblings;\r
7237                         };\r
7238 \r
7239                         function findEndPoint(node, root) {\r
7240                                 do {\r
7241                                         if (node.parentNode == root)\r
7242                                                 return node;\r
7243 \r
7244                                         node = node.parentNode;\r
7245                                 } while(node);\r
7246                         };\r
7247 \r
7248                         function walkBoundary(start_node, end_node, next) {\r
7249                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
7250 \r
7251                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
7252                                         parent = node.parentNode;\r
7253                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
7254 \r
7255                                         if (siblings.length) {\r
7256                                                 if (!next)\r
7257                                                         siblings.reverse();\r
7258 \r
7259                                                 callback(siblings);\r
7260                                         }\r
7261                                 }\r
7262                         };\r
7263 \r
7264                         // If index based start position then resolve it\r
7265                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
7266                                 startContainer = startContainer.childNodes[startOffset];\r
7267 \r
7268                         // If index based end position then resolve it\r
7269                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
7270                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
7271 \r
7272                         // Find common ancestor and end points\r
7273                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
7274 \r
7275                         // Same container\r
7276                         if (startContainer == endContainer)\r
7277                                 return callback([startContainer]);\r
7278 \r
7279                         // Process left side\r
7280                         for (node = startContainer; node; node = node.parentNode) {\r
7281                                 if (node == endContainer)\r
7282                                         return walkBoundary(startContainer, ancestor, true);\r
7283 \r
7284                                 if (node == ancestor)\r
7285                                         break;\r
7286                         }\r
7287 \r
7288                         // Process right side\r
7289                         for (node = endContainer; node; node = node.parentNode) {\r
7290                                 if (node == startContainer)\r
7291                                         return walkBoundary(endContainer, ancestor);\r
7292 \r
7293                                 if (node == ancestor)\r
7294                                         break;\r
7295                         }\r
7296 \r
7297                         // Find start/end point\r
7298                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
7299                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
7300 \r
7301                         // Walk left leaf\r
7302                         walkBoundary(startContainer, startPoint, true);\r
7303 \r
7304                         // Walk the middle from start to end point\r
7305                         siblings = collectSiblings(\r
7306                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
7307                                 'nextSibling',\r
7308                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
7309                         );\r
7310 \r
7311                         if (siblings.length)\r
7312                                 callback(siblings);\r
7313 \r
7314                         // Walk right leaf\r
7315                         walkBoundary(endContainer, endPoint);\r
7316                 };\r
7317 \r
7318                 /*              this.split = function(rng) {\r
7319                         var startContainer = rng.startContainer,\r
7320                                 startOffset = rng.startOffset,\r
7321                                 endContainer = rng.endContainer,\r
7322                                 endOffset = rng.endOffset;\r
7323 \r
7324                         function splitText(node, offset) {\r
7325                                 if (offset == node.nodeValue.length)\r
7326                                         node.appendData(INVISIBLE_CHAR);\r
7327 \r
7328                                 node = node.splitText(offset);\r
7329 \r
7330                                 if (node.nodeValue === INVISIBLE_CHAR)\r
7331                                         node.nodeValue = '';\r
7332 \r
7333                                 return node;\r
7334                         };\r
7335 \r
7336                         // Handle single text node\r
7337                         if (startContainer == endContainer) {\r
7338                                 if (startContainer.nodeType == 3) {\r
7339                                         if (startOffset != 0)\r
7340                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
7341 \r
7342                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
7343                                                 splitText(startContainer, endOffset - startOffset);\r
7344                                 }\r
7345                         } else {\r
7346                                 // Split startContainer text node if needed\r
7347                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
7348                                         startContainer = splitText(startContainer, startOffset);\r
7349                                         startOffset = 0;\r
7350                                 }\r
7351 \r
7352                                 // Split endContainer text node if needed\r
7353                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
7354                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
7355                                         endOffset = endContainer.nodeValue.length;\r
7356                                 }\r
7357                         }\r
7358 \r
7359                         return {\r
7360                                 startContainer : startContainer,\r
7361                                 startOffset : startOffset,\r
7362                                 endContainer : endContainer,\r
7363                                 endOffset : endOffset\r
7364                         };\r
7365                 };\r
7366 */\r
7367         };\r
7368 \r
7369         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
7370                 if (rng1 && rng2) {\r
7371                         // Compare native IE ranges\r
7372                         if (rng1.item || rng1.duplicate) {\r
7373                                 // Both are control ranges and the selected element matches\r
7374                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
7375                                         return true;\r
7376 \r
7377                                 // Both are text ranges and the range matches\r
7378                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
7379                                         return true;\r
7380                         } else {\r
7381                                 // Compare w3c ranges\r
7382                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
7383                         }\r
7384                 }\r
7385 \r
7386                 return false;\r
7387         };\r
7388 })(tinymce);\r
7389 \r
7390 (function(tinymce) {\r
7391         // Shorten class names\r
7392         var DOM = tinymce.DOM, is = tinymce.is;\r
7393 \r
7394         tinymce.create('tinymce.ui.Control', {\r
7395                 Control : function(id, s) {\r
7396                         this.id = id;\r
7397                         this.settings = s = s || {};\r
7398                         this.rendered = false;\r
7399                         this.onRender = new tinymce.util.Dispatcher(this);\r
7400                         this.classPrefix = '';\r
7401                         this.scope = s.scope || this;\r
7402                         this.disabled = 0;\r
7403                         this.active = 0;\r
7404                 },\r
7405 \r
7406                 setDisabled : function(s) {\r
7407                         var e;\r
7408 \r
7409                         if (s != this.disabled) {\r
7410                                 e = DOM.get(this.id);\r
7411 \r
7412                                 // Add accessibility title for unavailable actions\r
7413                                 if (e && this.settings.unavailable_prefix) {\r
7414                                         if (s) {\r
7415                                                 this.prevTitle = e.title;\r
7416                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
7417                                         } else\r
7418                                                 e.title = this.prevTitle;\r
7419                                 }\r
7420 \r
7421                                 this.setState('Disabled', s);\r
7422                                 this.setState('Enabled', !s);\r
7423                                 this.disabled = s;\r
7424                         }\r
7425                 },\r
7426 \r
7427                 isDisabled : function() {\r
7428                         return this.disabled;\r
7429                 },\r
7430 \r
7431                 setActive : function(s) {\r
7432                         if (s != this.active) {\r
7433                                 this.setState('Active', s);\r
7434                                 this.active = s;\r
7435                         }\r
7436                 },\r
7437 \r
7438                 isActive : function() {\r
7439                         return this.active;\r
7440                 },\r
7441 \r
7442                 setState : function(c, s) {\r
7443                         var n = DOM.get(this.id);\r
7444 \r
7445                         c = this.classPrefix + c;\r
7446 \r
7447                         if (s)\r
7448                                 DOM.addClass(n, c);\r
7449                         else\r
7450                                 DOM.removeClass(n, c);\r
7451                 },\r
7452 \r
7453                 isRendered : function() {\r
7454                         return this.rendered;\r
7455                 },\r
7456 \r
7457                 renderHTML : function() {\r
7458                 },\r
7459 \r
7460                 renderTo : function(n) {\r
7461                         DOM.setHTML(n, this.renderHTML());\r
7462                 },\r
7463 \r
7464                 postRender : function() {\r
7465                         var t = this, b;\r
7466 \r
7467                         // Set pending states\r
7468                         if (is(t.disabled)) {\r
7469                                 b = t.disabled;\r
7470                                 t.disabled = -1;\r
7471                                 t.setDisabled(b);\r
7472                         }\r
7473 \r
7474                         if (is(t.active)) {\r
7475                                 b = t.active;\r
7476                                 t.active = -1;\r
7477                                 t.setActive(b);\r
7478                         }\r
7479                 },\r
7480 \r
7481                 remove : function() {\r
7482                         DOM.remove(this.id);\r
7483                         this.destroy();\r
7484                 },\r
7485 \r
7486                 destroy : function() {\r
7487                         tinymce.dom.Event.clear(this.id);\r
7488                 }\r
7489         });\r
7490 })(tinymce);\r
7491 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
7492         Container : function(id, s) {\r
7493                 this.parent(id, s);\r
7494 \r
7495                 this.controls = [];\r
7496 \r
7497                 this.lookup = {};\r
7498         },\r
7499 \r
7500         add : function(c) {\r
7501                 this.lookup[c.id] = c;\r
7502                 this.controls.push(c);\r
7503 \r
7504                 return c;\r
7505         },\r
7506 \r
7507         get : function(n) {\r
7508                 return this.lookup[n];\r
7509         }\r
7510 });\r
7511 \r
7512 \r
7513 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
7514         Separator : function(id, s) {\r
7515                 this.parent(id, s);\r
7516                 this.classPrefix = 'mceSeparator';\r
7517         },\r
7518 \r
7519         renderHTML : function() {\r
7520                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
7521         }\r
7522 });\r
7523 \r
7524 (function(tinymce) {\r
7525         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7526 \r
7527         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
7528                 MenuItem : function(id, s) {\r
7529                         this.parent(id, s);\r
7530                         this.classPrefix = 'mceMenuItem';\r
7531                 },\r
7532 \r
7533                 setSelected : function(s) {\r
7534                         this.setState('Selected', s);\r
7535                         this.selected = s;\r
7536                 },\r
7537 \r
7538                 isSelected : function() {\r
7539                         return this.selected;\r
7540                 },\r
7541 \r
7542                 postRender : function() {\r
7543                         var t = this;\r
7544                         \r
7545                         t.parent();\r
7546 \r
7547                         // Set pending state\r
7548                         if (is(t.selected))\r
7549                                 t.setSelected(t.selected);\r
7550                 }\r
7551         });\r
7552 })(tinymce);\r
7553 \r
7554 (function(tinymce) {\r
7555         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7556 \r
7557         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
7558                 Menu : function(id, s) {\r
7559                         var t = this;\r
7560 \r
7561                         t.parent(id, s);\r
7562                         t.items = {};\r
7563                         t.collapsed = false;\r
7564                         t.menuCount = 0;\r
7565                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
7566                 },\r
7567 \r
7568                 expand : function(d) {\r
7569                         var t = this;\r
7570 \r
7571                         if (d) {\r
7572                                 walk(t, function(o) {\r
7573                                         if (o.expand)\r
7574                                                 o.expand();\r
7575                                 }, 'items', t);\r
7576                         }\r
7577 \r
7578                         t.collapsed = false;\r
7579                 },\r
7580 \r
7581                 collapse : function(d) {\r
7582                         var t = this;\r
7583 \r
7584                         if (d) {\r
7585                                 walk(t, function(o) {\r
7586                                         if (o.collapse)\r
7587                                                 o.collapse();\r
7588                                 }, 'items', t);\r
7589                         }\r
7590 \r
7591                         t.collapsed = true;\r
7592                 },\r
7593 \r
7594                 isCollapsed : function() {\r
7595                         return this.collapsed;\r
7596                 },\r
7597 \r
7598                 add : function(o) {\r
7599                         if (!o.settings)\r
7600                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
7601 \r
7602                         this.onAddItem.dispatch(this, o);\r
7603 \r
7604                         return this.items[o.id] = o;\r
7605                 },\r
7606 \r
7607                 addSeparator : function() {\r
7608                         return this.add({separator : true});\r
7609                 },\r
7610 \r
7611                 addMenu : function(o) {\r
7612                         if (!o.collapse)\r
7613                                 o = this.createMenu(o);\r
7614 \r
7615                         this.menuCount++;\r
7616 \r
7617                         return this.add(o);\r
7618                 },\r
7619 \r
7620                 hasMenus : function() {\r
7621                         return this.menuCount !== 0;\r
7622                 },\r
7623 \r
7624                 remove : function(o) {\r
7625                         delete this.items[o.id];\r
7626                 },\r
7627 \r
7628                 removeAll : function() {\r
7629                         var t = this;\r
7630 \r
7631                         walk(t, function(o) {\r
7632                                 if (o.removeAll)\r
7633                                         o.removeAll();\r
7634                                 else\r
7635                                         o.remove();\r
7636 \r
7637                                 o.destroy();\r
7638                         }, 'items', t);\r
7639 \r
7640                         t.items = {};\r
7641                 },\r
7642 \r
7643                 createMenu : function(o) {\r
7644                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
7645 \r
7646                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
7647 \r
7648                         return m;\r
7649                 }\r
7650         });\r
7651 })(tinymce);\r
7652 (function(tinymce) {\r
7653         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
7654 \r
7655         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
7656                 DropMenu : function(id, s) {\r
7657                         s = s || {};\r
7658                         s.container = s.container || DOM.doc.body;\r
7659                         s.offset_x = s.offset_x || 0;\r
7660                         s.offset_y = s.offset_y || 0;\r
7661                         s.vp_offset_x = s.vp_offset_x || 0;\r
7662                         s.vp_offset_y = s.vp_offset_y || 0;\r
7663 \r
7664                         if (is(s.icons) && !s.icons)\r
7665                                 s['class'] += ' mceNoIcons';\r
7666 \r
7667                         this.parent(id, s);\r
7668                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
7669                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
7670                         this.classPrefix = 'mceMenu';\r
7671                 },\r
7672 \r
7673                 createMenu : function(s) {\r
7674                         var t = this, cs = t.settings, m;\r
7675 \r
7676                         s.container = s.container || cs.container;\r
7677                         s.parent = t;\r
7678                         s.constrain = s.constrain || cs.constrain;\r
7679                         s['class'] = s['class'] || cs['class'];\r
7680                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
7681                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
7682                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
7683 \r
7684                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
7685 \r
7686                         return m;\r
7687                 },\r
7688 \r
7689                 update : function() {\r
7690                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
7691 \r
7692                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
7693                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
7694 \r
7695                         if (!DOM.boxModel)\r
7696                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
7697                         else\r
7698                                 t.element.setStyles({width : tw, height : th});\r
7699 \r
7700                         if (s.max_width)\r
7701                                 DOM.setStyle(co, 'width', tw);\r
7702 \r
7703                         if (s.max_height) {\r
7704                                 DOM.setStyle(co, 'height', th);\r
7705 \r
7706                                 if (tb.clientHeight < s.max_height)\r
7707                                         DOM.setStyle(co, 'overflow', 'hidden');\r
7708                         }\r
7709                 },\r
7710 \r
7711                 showMenu : function(x, y, px) {\r
7712                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
7713 \r
7714                         t.collapse(1);\r
7715 \r
7716                         if (t.isMenuVisible)\r
7717                                 return;\r
7718 \r
7719                         if (!t.rendered) {\r
7720                                 co = DOM.add(t.settings.container, t.renderNode());\r
7721 \r
7722                                 each(t.items, function(o) {\r
7723                                         o.postRender();\r
7724                                 });\r
7725 \r
7726                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7727                         } else\r
7728                                 co = DOM.get('menu_' + t.id);\r
7729 \r
7730                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
7731                         if (!tinymce.isOpera)\r
7732                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
7733 \r
7734                         DOM.show(co);\r
7735                         t.update();\r
7736 \r
7737                         x += s.offset_x || 0;\r
7738                         y += s.offset_y || 0;\r
7739                         vp.w -= 4;\r
7740                         vp.h -= 4;\r
7741 \r
7742                         // Move inside viewport if not submenu\r
7743                         if (s.constrain) {\r
7744                                 w = co.clientWidth - ot;\r
7745                                 h = co.clientHeight - ot;\r
7746                                 mx = vp.x + vp.w;\r
7747                                 my = vp.y + vp.h;\r
7748 \r
7749                                 if ((x + s.vp_offset_x + w) > mx)\r
7750                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
7751 \r
7752                                 if ((y + s.vp_offset_y + h) > my)\r
7753                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
7754                         }\r
7755 \r
7756                         DOM.setStyles(co, {left : x , top : y});\r
7757                         t.element.update();\r
7758 \r
7759                         t.isMenuVisible = 1;\r
7760                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
7761                                 var m;\r
7762 \r
7763                                 e = e.target;\r
7764 \r
7765                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
7766                                         m = t.items[e.id];\r
7767 \r
7768                                         if (m.isDisabled())\r
7769                                                 return;\r
7770 \r
7771                                         dm = t;\r
7772 \r
7773                                         while (dm) {\r
7774                                                 if (dm.hideMenu)\r
7775                                                         dm.hideMenu();\r
7776 \r
7777                                                 dm = dm.settings.parent;\r
7778                                         }\r
7779 \r
7780                                         if (m.settings.onclick)\r
7781                                                 m.settings.onclick(e);\r
7782 \r
7783                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7784                                 }\r
7785                         });\r
7786 \r
7787                         if (t.hasMenus()) {\r
7788                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
7789                                         var m, r, mi;\r
7790 \r
7791                                         e = e.target;\r
7792                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
7793                                                 m = t.items[e.id];\r
7794 \r
7795                                                 if (t.lastMenu)\r
7796                                                         t.lastMenu.collapse(1);\r
7797 \r
7798                                                 if (m.isDisabled())\r
7799                                                         return;\r
7800 \r
7801                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
7802                                                         //p = DOM.getPos(s.container);\r
7803                                                         r = DOM.getRect(e);\r
7804                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
7805                                                         t.lastMenu = m;\r
7806                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
7807                                                 }\r
7808                                         }\r
7809                                 });\r
7810                         }\r
7811 \r
7812                         t.onShowMenu.dispatch(t);\r
7813 \r
7814                         if (s.keyboard_focus) {\r
7815                                 Event.add(co, 'keydown', t._keyHandler, t);\r
7816                                 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
7817                                 t._focusIdx = 0;\r
7818                         }\r
7819                 },\r
7820 \r
7821                 hideMenu : function(c) {\r
7822                         var t = this, co = DOM.get('menu_' + t.id), e;\r
7823 \r
7824                         if (!t.isMenuVisible)\r
7825                                 return;\r
7826 \r
7827                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7828                         Event.remove(co, 'click', t.mouseClickFunc);\r
7829                         Event.remove(co, 'keydown', t._keyHandler);\r
7830                         DOM.hide(co);\r
7831                         t.isMenuVisible = 0;\r
7832 \r
7833                         if (!c)\r
7834                                 t.collapse(1);\r
7835 \r
7836                         if (t.element)\r
7837                                 t.element.hide();\r
7838 \r
7839                         if (e = DOM.get(t.id))\r
7840                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
7841 \r
7842                         t.onHideMenu.dispatch(t);\r
7843                 },\r
7844 \r
7845                 add : function(o) {\r
7846                         var t = this, co;\r
7847 \r
7848                         o = t.parent(o);\r
7849 \r
7850                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
7851                                 t._add(DOM.select('tbody', co)[0], o);\r
7852 \r
7853                         return o;\r
7854                 },\r
7855 \r
7856                 collapse : function(d) {\r
7857                         this.parent(d);\r
7858                         this.hideMenu(1);\r
7859                 },\r
7860 \r
7861                 remove : function(o) {\r
7862                         DOM.remove(o.id);\r
7863                         this.destroy();\r
7864 \r
7865                         return this.parent(o);\r
7866                 },\r
7867 \r
7868                 destroy : function() {\r
7869                         var t = this, co = DOM.get('menu_' + t.id);\r
7870 \r
7871                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7872                         Event.remove(co, 'click', t.mouseClickFunc);\r
7873 \r
7874                         if (t.element)\r
7875                                 t.element.remove();\r
7876 \r
7877                         DOM.remove(co);\r
7878                 },\r
7879 \r
7880                 renderNode : function() {\r
7881                         var t = this, s = t.settings, n, tb, co, w;\r
7882 \r
7883                         w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
7884                         co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
7885                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7886 \r
7887                         if (s.menu_line)\r
7888                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
7889 \r
7890 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
7891                         n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
7892                         tb = DOM.add(n, 'tbody');\r
7893 \r
7894                         each(t.items, function(o) {\r
7895                                 t._add(tb, o);\r
7896                         });\r
7897 \r
7898                         t.rendered = true;\r
7899 \r
7900                         return w;\r
7901                 },\r
7902 \r
7903                 // Internal functions\r
7904 \r
7905                 _keyHandler : function(e) {\r
7906                         var t = this, kc = e.keyCode;\r
7907 \r
7908                         function focus(d) {\r
7909                                 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7910 \r
7911                                 if (e) {\r
7912                                         t._focusIdx = i;\r
7913                                         e.focus();\r
7914                                 }\r
7915                         };\r
7916 \r
7917                         switch (kc) {\r
7918                                 case 38:\r
7919                                         focus(-1); // Select first link\r
7920                                         return;\r
7921 \r
7922                                 case 40:\r
7923                                         focus(1);\r
7924                                         return;\r
7925 \r
7926                                 case 13:\r
7927                                         return;\r
7928 \r
7929                                 case 27:\r
7930                                         return this.hideMenu();\r
7931                         }\r
7932                 },\r
7933 \r
7934                 _add : function(tb, o) {\r
7935                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7936 \r
7937                         if (s.separator) {\r
7938                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7939                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7940 \r
7941                                 if (n = ro.previousSibling)\r
7942                                         DOM.addClass(n, 'mceLast');\r
7943 \r
7944                                 return;\r
7945                         }\r
7946 \r
7947                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7948                         n = it = DOM.add(n, 'td');\r
7949                         n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7950 \r
7951                         DOM.addClass(it, s['class']);\r
7952 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
7953 \r
7954                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7955 \r
7956                         if (s.icon_src)\r
7957                                 DOM.add(ic, 'img', {src : s.icon_src});\r
7958 \r
7959                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7960 \r
7961                         if (o.settings.style)\r
7962                                 DOM.setAttrib(n, 'style', o.settings.style);\r
7963 \r
7964                         if (tb.childNodes.length == 1)\r
7965                                 DOM.addClass(ro, 'mceFirst');\r
7966 \r
7967                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7968                                 DOM.addClass(ro, 'mceFirst');\r
7969 \r
7970                         if (o.collapse)\r
7971                                 DOM.addClass(ro, cp + 'ItemSub');\r
7972 \r
7973                         if (n = ro.previousSibling)\r
7974                                 DOM.removeClass(n, 'mceLast');\r
7975 \r
7976                         DOM.addClass(ro, 'mceLast');\r
7977                 }\r
7978         });\r
7979 })(tinymce);\r
7980 (function(tinymce) {\r
7981         var DOM = tinymce.DOM;\r
7982 \r
7983         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7984                 Button : function(id, s) {\r
7985                         this.parent(id, s);\r
7986                         this.classPrefix = 'mceButton';\r
7987                 },\r
7988 \r
7989                 renderHTML : function() {\r
7990                         var cp = this.classPrefix, s = this.settings, h, l;\r
7991 \r
7992                         l = DOM.encode(s.label || '');\r
7993                         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
7994 \r
7995                         if (s.image)\r
7996                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7997                         else\r
7998                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7999 \r
8000                         return h;\r
8001                 },\r
8002 \r
8003                 postRender : function() {\r
8004                         var t = this, s = t.settings;\r
8005 \r
8006                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
8007                                 if (!t.isDisabled())\r
8008                                         return s.onclick.call(s.scope, e);\r
8009                         });\r
8010                 }\r
8011         });\r
8012 })(tinymce);\r
8013 \r
8014 (function(tinymce) {\r
8015         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8016 \r
8017         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
8018                 ListBox : function(id, s) {\r
8019                         var t = this;\r
8020 \r
8021                         t.parent(id, s);\r
8022 \r
8023                         t.items = [];\r
8024 \r
8025                         t.onChange = new Dispatcher(t);\r
8026 \r
8027                         t.onPostRender = new Dispatcher(t);\r
8028 \r
8029                         t.onAdd = new Dispatcher(t);\r
8030 \r
8031                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8032 \r
8033                         t.classPrefix = 'mceListBox';\r
8034                 },\r
8035 \r
8036                 select : function(va) {\r
8037                         var t = this, fv, f;\r
8038 \r
8039                         if (va == undefined)\r
8040                                 return t.selectByIndex(-1);\r
8041 \r
8042                         // Is string or number make function selector\r
8043                         if (va && va.call)\r
8044                                 f = va;\r
8045                         else {\r
8046                                 f = function(v) {\r
8047                                         return v == va;\r
8048                                 };\r
8049                         }\r
8050 \r
8051                         // Do we need to do something?\r
8052                         if (va != t.selectedValue) {\r
8053                                 // Find item\r
8054                                 each(t.items, function(o, i) {\r
8055                                         if (f(o.value)) {\r
8056                                                 fv = 1;\r
8057                                                 t.selectByIndex(i);\r
8058                                                 return false;\r
8059                                         }\r
8060                                 });\r
8061 \r
8062                                 if (!fv)\r
8063                                         t.selectByIndex(-1);\r
8064                         }\r
8065                 },\r
8066 \r
8067                 selectByIndex : function(idx) {\r
8068                         var t = this, e, o;\r
8069 \r
8070                         if (idx != t.selectedIndex) {\r
8071                                 e = DOM.get(t.id + '_text');\r
8072                                 o = t.items[idx];\r
8073 \r
8074                                 if (o) {\r
8075                                         t.selectedValue = o.value;\r
8076                                         t.selectedIndex = idx;\r
8077                                         DOM.setHTML(e, DOM.encode(o.title));\r
8078                                         DOM.removeClass(e, 'mceTitle');\r
8079                                 } else {\r
8080                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
8081                                         DOM.addClass(e, 'mceTitle');\r
8082                                         t.selectedValue = t.selectedIndex = null;\r
8083                                 }\r
8084 \r
8085                                 e = 0;\r
8086                         }\r
8087                 },\r
8088 \r
8089                 add : function(n, v, o) {\r
8090                         var t = this;\r
8091 \r
8092                         o = o || {};\r
8093                         o = tinymce.extend(o, {\r
8094                                 title : n,\r
8095                                 value : v\r
8096                         });\r
8097 \r
8098                         t.items.push(o);\r
8099                         t.onAdd.dispatch(t, o);\r
8100                 },\r
8101 \r
8102                 getLength : function() {\r
8103                         return this.items.length;\r
8104                 },\r
8105 \r
8106                 renderHTML : function() {\r
8107                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
8108 \r
8109                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
8110                         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
8111                         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
8112                         h += '</tr></tbody></table>';\r
8113 \r
8114                         return h;\r
8115                 },\r
8116 \r
8117                 showMenu : function() {\r
8118                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
8119 \r
8120                         if (t.isDisabled() || t.items.length == 0)\r
8121                                 return;\r
8122 \r
8123                         if (t.menu && t.menu.isMenuVisible)\r
8124                                 return t.hideMenu();\r
8125 \r
8126                         if (!t.isMenuRendered) {\r
8127                                 t.renderMenu();\r
8128                                 t.isMenuRendered = true;\r
8129                         }\r
8130 \r
8131                         p1 = DOM.getPos(this.settings.menu_container);\r
8132                         p2 = DOM.getPos(e);\r
8133 \r
8134                         m = t.menu;\r
8135                         m.settings.offset_x = p2.x;\r
8136                         m.settings.offset_y = p2.y;\r
8137                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
8138 \r
8139                         // Select in menu\r
8140                         if (t.oldID)\r
8141                                 m.items[t.oldID].setSelected(0);\r
8142 \r
8143                         each(t.items, function(o) {\r
8144                                 if (o.value === t.selectedValue) {\r
8145                                         m.items[o.id].setSelected(1);\r
8146                                         t.oldID = o.id;\r
8147                                 }\r
8148                         });\r
8149 \r
8150                         m.showMenu(0, e.clientHeight);\r
8151 \r
8152                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8153                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
8154 \r
8155                         //DOM.get(t.id + '_text').focus();\r
8156                 },\r
8157 \r
8158                 hideMenu : function(e) {\r
8159                         var t = this;\r
8160 \r
8161                         if (t.menu && t.menu.isMenuVisible) {\r
8162                                 // Prevent double toogles by canceling the mouse click event to the button\r
8163                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
8164                                         return;\r
8165 \r
8166                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8167                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
8168                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8169                                         t.menu.hideMenu();\r
8170                                 }\r
8171                         }\r
8172                 },\r
8173 \r
8174                 renderMenu : function() {\r
8175                         var t = this, m;\r
8176 \r
8177                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8178                                 menu_line : 1,\r
8179                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
8180                                 max_width : 150,\r
8181                                 max_height : 150\r
8182                         });\r
8183 \r
8184                         m.onHideMenu.add(t.hideMenu, t);\r
8185 \r
8186                         m.add({\r
8187                                 title : t.settings.title,\r
8188                                 'class' : 'mceMenuItemTitle',\r
8189                                 onclick : function() {\r
8190                                         if (t.settings.onselect('') !== false)\r
8191                                                 t.select(''); // Must be runned after\r
8192                                 }\r
8193                         });\r
8194 \r
8195                         each(t.items, function(o) {\r
8196                                 // No value then treat it as a title\r
8197                                 if (o.value === undefined) {\r
8198                                         m.add({\r
8199                                                 title : o.title,\r
8200                                                 'class' : 'mceMenuItemTitle',\r
8201                                                 onclick : function() {\r
8202                                                         if (t.settings.onselect('') !== false)\r
8203                                                                 t.select(''); // Must be runned after\r
8204                                                 }\r
8205                                         });\r
8206                                 } else {\r
8207                                         o.id = DOM.uniqueId();\r
8208                                         o.onclick = function() {\r
8209                                                 if (t.settings.onselect(o.value) !== false)\r
8210                                                         t.select(o.value); // Must be runned after\r
8211                                         };\r
8212 \r
8213                                         m.add(o);\r
8214                                 }\r
8215                         });\r
8216 \r
8217                         t.onRenderMenu.dispatch(t, m);\r
8218                         t.menu = m;\r
8219                 },\r
8220 \r
8221                 postRender : function() {\r
8222                         var t = this, cp = t.classPrefix;\r
8223 \r
8224                         Event.add(t.id, 'click', t.showMenu, t);\r
8225                         Event.add(t.id + '_text', 'focus', function() {\r
8226                                 if (!t._focused) {\r
8227                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
8228                                                 var idx = -1, v, kc = e.keyCode;\r
8229 \r
8230                                                 // Find current index\r
8231                                                 each(t.items, function(v, i) {\r
8232                                                         if (t.selectedValue == v.value)\r
8233                                                                 idx = i;\r
8234                                                 });\r
8235 \r
8236                                                 // Move up/down\r
8237                                                 if (kc == 38)\r
8238                                                         v = t.items[idx - 1];\r
8239                                                 else if (kc == 40)\r
8240                                                         v = t.items[idx + 1];\r
8241                                                 else if (kc == 13) {\r
8242                                                         // Fake select on enter\r
8243                                                         v = t.selectedValue;\r
8244                                                         t.selectedValue = null; // Needs to be null to fake change\r
8245                                                         t.settings.onselect(v);\r
8246                                                         return Event.cancel(e);\r
8247                                                 }\r
8248 \r
8249                                                 if (v) {\r
8250                                                         t.hideMenu();\r
8251                                                         t.select(v.value);\r
8252                                                 }\r
8253                                         });\r
8254                                 }\r
8255 \r
8256                                 t._focused = 1;\r
8257                         });\r
8258                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
8259 \r
8260                         // Old IE doesn't have hover on all elements\r
8261                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8262                                 Event.add(t.id, 'mouseover', function() {\r
8263                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8264                                                 DOM.addClass(t.id, cp + 'Hover');\r
8265                                 });\r
8266 \r
8267                                 Event.add(t.id, 'mouseout', function() {\r
8268                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8269                                                 DOM.removeClass(t.id, cp + 'Hover');\r
8270                                 });\r
8271                         }\r
8272 \r
8273                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8274                 },\r
8275 \r
8276                 destroy : function() {\r
8277                         this.parent();\r
8278 \r
8279                         Event.clear(this.id + '_text');\r
8280                         Event.clear(this.id + '_open');\r
8281                 }\r
8282         });\r
8283 })(tinymce);\r
8284 (function(tinymce) {\r
8285         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8286 \r
8287         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
8288                 NativeListBox : function(id, s) {\r
8289                         this.parent(id, s);\r
8290                         this.classPrefix = 'mceNativeListBox';\r
8291                 },\r
8292 \r
8293                 setDisabled : function(s) {\r
8294                         DOM.get(this.id).disabled = s;\r
8295                 },\r
8296 \r
8297                 isDisabled : function() {\r
8298                         return DOM.get(this.id).disabled;\r
8299                 },\r
8300 \r
8301                 select : function(va) {\r
8302                         var t = this, fv, f;\r
8303 \r
8304                         if (va == undefined)\r
8305                                 return t.selectByIndex(-1);\r
8306 \r
8307                         // Is string or number make function selector\r
8308                         if (va && va.call)\r
8309                                 f = va;\r
8310                         else {\r
8311                                 f = function(v) {\r
8312                                         return v == va;\r
8313                                 };\r
8314                         }\r
8315 \r
8316                         // Do we need to do something?\r
8317                         if (va != t.selectedValue) {\r
8318                                 // Find item\r
8319                                 each(t.items, function(o, i) {\r
8320                                         if (f(o.value)) {\r
8321                                                 fv = 1;\r
8322                                                 t.selectByIndex(i);\r
8323                                                 return false;\r
8324                                         }\r
8325                                 });\r
8326 \r
8327                                 if (!fv)\r
8328                                         t.selectByIndex(-1);\r
8329                         }\r
8330                 },\r
8331 \r
8332                 selectByIndex : function(idx) {\r
8333                         DOM.get(this.id).selectedIndex = idx + 1;\r
8334                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
8335                 },\r
8336 \r
8337                 add : function(n, v, a) {\r
8338                         var o, t = this;\r
8339 \r
8340                         a = a || {};\r
8341                         a.value = v;\r
8342 \r
8343                         if (t.isRendered())\r
8344                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
8345 \r
8346                         o = {\r
8347                                 title : n,\r
8348                                 value : v,\r
8349                                 attribs : a\r
8350                         };\r
8351 \r
8352                         t.items.push(o);\r
8353                         t.onAdd.dispatch(t, o);\r
8354                 },\r
8355 \r
8356                 getLength : function() {\r
8357                         return this.items.length;\r
8358                 },\r
8359 \r
8360                 renderHTML : function() {\r
8361                         var h, t = this;\r
8362 \r
8363                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
8364 \r
8365                         each(t.items, function(it) {\r
8366                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
8367                         });\r
8368 \r
8369                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
8370 \r
8371                         return h;\r
8372                 },\r
8373 \r
8374                 postRender : function() {\r
8375                         var t = this, ch;\r
8376 \r
8377                         t.rendered = true;\r
8378 \r
8379                         function onChange(e) {\r
8380                                 var v = t.items[e.target.selectedIndex - 1];\r
8381 \r
8382                                 if (v && (v = v.value)) {\r
8383                                         t.onChange.dispatch(t, v);\r
8384 \r
8385                                         if (t.settings.onselect)\r
8386                                                 t.settings.onselect(v);\r
8387                                 }\r
8388                         };\r
8389 \r
8390                         Event.add(t.id, 'change', onChange);\r
8391 \r
8392                         // Accessibility keyhandler\r
8393                         Event.add(t.id, 'keydown', function(e) {\r
8394                                 var bf;\r
8395 \r
8396                                 Event.remove(t.id, 'change', ch);\r
8397 \r
8398                                 bf = Event.add(t.id, 'blur', function() {\r
8399                                         Event.add(t.id, 'change', onChange);\r
8400                                         Event.remove(t.id, 'blur', bf);\r
8401                                 });\r
8402 \r
8403                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
8404                                         onChange(e);\r
8405                                         return Event.cancel(e);\r
8406                                 }\r
8407                         });\r
8408 \r
8409                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8410                 }\r
8411         });\r
8412 })(tinymce);\r
8413 (function(tinymce) {\r
8414         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8415 \r
8416         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
8417                 MenuButton : function(id, s) {\r
8418                         this.parent(id, s);\r
8419 \r
8420                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8421 \r
8422                         s.menu_container = s.menu_container || DOM.doc.body;\r
8423                 },\r
8424 \r
8425                 showMenu : function() {\r
8426                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
8427 \r
8428                         if (t.isDisabled())\r
8429                                 return;\r
8430 \r
8431                         if (!t.isMenuRendered) {\r
8432                                 t.renderMenu();\r
8433                                 t.isMenuRendered = true;\r
8434                         }\r
8435 \r
8436                         if (t.isMenuVisible)\r
8437                                 return t.hideMenu();\r
8438 \r
8439                         p1 = DOM.getPos(t.settings.menu_container);\r
8440                         p2 = DOM.getPos(e);\r
8441 \r
8442                         m = t.menu;\r
8443                         m.settings.offset_x = p2.x;\r
8444                         m.settings.offset_y = p2.y;\r
8445                         m.settings.vp_offset_x = p2.x;\r
8446                         m.settings.vp_offset_y = p2.y;\r
8447                         m.settings.keyboard_focus = t._focused;\r
8448                         m.showMenu(0, e.clientHeight);\r
8449 \r
8450                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8451                         t.setState('Selected', 1);\r
8452 \r
8453                         t.isMenuVisible = 1;\r
8454                 },\r
8455 \r
8456                 renderMenu : function() {\r
8457                         var t = this, m;\r
8458 \r
8459                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8460                                 menu_line : 1,\r
8461                                 'class' : this.classPrefix + 'Menu',\r
8462                                 icons : t.settings.icons\r
8463                         });\r
8464 \r
8465                         m.onHideMenu.add(t.hideMenu, t);\r
8466 \r
8467                         t.onRenderMenu.dispatch(t, m);\r
8468                         t.menu = m;\r
8469                 },\r
8470 \r
8471                 hideMenu : function(e) {\r
8472                         var t = this;\r
8473 \r
8474                         // Prevent double toogles by canceling the mouse click event to the button\r
8475                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
8476                                 return;\r
8477 \r
8478                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8479                                 t.setState('Selected', 0);\r
8480                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8481                                 if (t.menu)\r
8482                                         t.menu.hideMenu();\r
8483                         }\r
8484 \r
8485                         t.isMenuVisible = 0;\r
8486                 },\r
8487 \r
8488                 postRender : function() {\r
8489                         var t = this, s = t.settings;\r
8490 \r
8491                         Event.add(t.id, 'click', function() {\r
8492                                 if (!t.isDisabled()) {\r
8493                                         if (s.onclick)\r
8494                                                 s.onclick(t.value);\r
8495 \r
8496                                         t.showMenu();\r
8497                                 }\r
8498                         });\r
8499                 }\r
8500         });\r
8501 })(tinymce);\r
8502 \r
8503 (function(tinymce) {\r
8504         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8505 \r
8506         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
8507                 SplitButton : function(id, s) {\r
8508                         this.parent(id, s);\r
8509                         this.classPrefix = 'mceSplitButton';\r
8510                 },\r
8511 \r
8512                 renderHTML : function() {\r
8513                         var h, t = this, s = t.settings, h1;\r
8514 \r
8515                         h = '<tbody><tr>';\r
8516 \r
8517                         if (s.image)\r
8518                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
8519                         else\r
8520                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
8521 \r
8522                         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
8523         \r
8524                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
8525                         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
8526 \r
8527                         h += '</tr></tbody>';\r
8528 \r
8529                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
8530                 },\r
8531 \r
8532                 postRender : function() {\r
8533                         var t = this, s = t.settings;\r
8534 \r
8535                         if (s.onclick) {\r
8536                                 Event.add(t.id + '_action', 'click', function() {\r
8537                                         if (!t.isDisabled())\r
8538                                                 s.onclick(t.value);\r
8539                                 });\r
8540                         }\r
8541 \r
8542                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
8543                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
8544                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
8545 \r
8546                         // Old IE doesn't have hover on all elements\r
8547                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8548                                 Event.add(t.id, 'mouseover', function() {\r
8549                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8550                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
8551                                 });\r
8552 \r
8553                                 Event.add(t.id, 'mouseout', function() {\r
8554                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8555                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
8556                                 });\r
8557                         }\r
8558                 },\r
8559 \r
8560                 destroy : function() {\r
8561                         this.parent();\r
8562 \r
8563                         Event.clear(this.id + '_action');\r
8564                         Event.clear(this.id + '_open');\r
8565                 }\r
8566         });\r
8567 })(tinymce);\r
8568 \r
8569 (function(tinymce) {\r
8570         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
8571 \r
8572         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
8573                 ColorSplitButton : function(id, s) {\r
8574                         var t = this;\r
8575 \r
8576                         t.parent(id, s);\r
8577 \r
8578                         t.settings = s = tinymce.extend({\r
8579                                 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
8580                                 grid_width : 8,\r
8581                                 default_color : '#888888'\r
8582                         }, t.settings);\r
8583 \r
8584                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
8585 \r
8586                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
8587 \r
8588                         t.value = s.default_color;\r
8589                 },\r
8590 \r
8591                 showMenu : function() {\r
8592                         var t = this, r, p, e, p2;\r
8593 \r
8594                         if (t.isDisabled())\r
8595                                 return;\r
8596 \r
8597                         if (!t.isMenuRendered) {\r
8598                                 t.renderMenu();\r
8599                                 t.isMenuRendered = true;\r
8600                         }\r
8601 \r
8602                         if (t.isMenuVisible)\r
8603                                 return t.hideMenu();\r
8604 \r
8605                         e = DOM.get(t.id);\r
8606                         DOM.show(t.id + '_menu');\r
8607                         DOM.addClass(e, 'mceSplitButtonSelected');\r
8608                         p2 = DOM.getPos(e);\r
8609                         DOM.setStyles(t.id + '_menu', {\r
8610                                 left : p2.x,\r
8611                                 top : p2.y + e.clientHeight,\r
8612                                 zIndex : 200000\r
8613                         });\r
8614                         e = 0;\r
8615 \r
8616                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8617                         t.onShowMenu.dispatch(t);\r
8618 \r
8619                         if (t._focused) {\r
8620                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
8621                                         if (e.keyCode == 27)\r
8622                                                 t.hideMenu();\r
8623                                 });\r
8624 \r
8625                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
8626                         }\r
8627 \r
8628                         t.isMenuVisible = 1;\r
8629                 },\r
8630 \r
8631                 hideMenu : function(e) {\r
8632                         var t = this;\r
8633 \r
8634                         // Prevent double toogles by canceling the mouse click event to the button\r
8635                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
8636                                 return;\r
8637 \r
8638                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
8639                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
8640                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8641                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
8642                                 DOM.hide(t.id + '_menu');\r
8643                         }\r
8644 \r
8645                         t.onHideMenu.dispatch(t);\r
8646 \r
8647                         t.isMenuVisible = 0;\r
8648                 },\r
8649 \r
8650                 renderMenu : function() {\r
8651                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
8652 \r
8653                         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
8654                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
8655                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
8656 \r
8657                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
8658                         tb = DOM.add(n, 'tbody');\r
8659 \r
8660                         // Generate color grid\r
8661                         i = 0;\r
8662                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
8663                                 c = c.replace(/^#/, '');\r
8664 \r
8665                                 if (!i--) {\r
8666                                         tr = DOM.add(tb, 'tr');\r
8667                                         i = s.grid_width - 1;\r
8668                                 }\r
8669 \r
8670                                 n = DOM.add(tr, 'td');\r
8671 \r
8672                                 n = DOM.add(n, 'a', {\r
8673                                         href : 'javascript:;',\r
8674                                         style : {\r
8675                                                 backgroundColor : '#' + c\r
8676                                         },\r
8677                                         _mce_color : '#' + c\r
8678                                 });\r
8679                         });\r
8680 \r
8681                         if (s.more_colors_func) {\r
8682                                 n = DOM.add(tb, 'tr');\r
8683                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
8684                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
8685 \r
8686                                 Event.add(n, 'click', function(e) {\r
8687                                         s.more_colors_func.call(s.more_colors_scope || this);\r
8688                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8689                                 });\r
8690                         }\r
8691 \r
8692                         DOM.addClass(m, 'mceColorSplitMenu');\r
8693 \r
8694                         Event.add(t.id + '_menu', 'click', function(e) {\r
8695                                 var c;\r
8696 \r
8697                                 e = e.target;\r
8698 \r
8699                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
8700                                         t.setColor(c);\r
8701 \r
8702                                 return Event.cancel(e); // Prevent IE auto save warning\r
8703                         });\r
8704 \r
8705                         return w;\r
8706                 },\r
8707 \r
8708                 setColor : function(c) {\r
8709                         var t = this;\r
8710 \r
8711                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
8712 \r
8713                         t.value = c;\r
8714                         t.hideMenu();\r
8715                         t.settings.onselect(c);\r
8716                 },\r
8717 \r
8718                 postRender : function() {\r
8719                         var t = this, id = t.id;\r
8720 \r
8721                         t.parent();\r
8722                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
8723                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
8724                 },\r
8725 \r
8726                 destroy : function() {\r
8727                         this.parent();\r
8728 \r
8729                         Event.clear(this.id + '_menu');\r
8730                         Event.clear(this.id + '_more');\r
8731                         DOM.remove(this.id + '_menu');\r
8732                 }\r
8733         });\r
8734 })(tinymce);\r
8735 \r
8736 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
8737         renderHTML : function() {\r
8738                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
8739 \r
8740                 cl = t.controls;\r
8741                 for (i=0; i<cl.length; i++) {\r
8742                         // Get current control, prev control, next control and if the control is a list box or not\r
8743                         co = cl[i];\r
8744                         pr = cl[i - 1];\r
8745                         nx = cl[i + 1];\r
8746 \r
8747                         // Add toolbar start\r
8748                         if (i === 0) {\r
8749                                 c = 'mceToolbarStart';\r
8750 \r
8751                                 if (co.Button)\r
8752                                         c += ' mceToolbarStartButton';\r
8753                                 else if (co.SplitButton)\r
8754                                         c += ' mceToolbarStartSplitButton';\r
8755                                 else if (co.ListBox)\r
8756                                         c += ' mceToolbarStartListBox';\r
8757 \r
8758                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8759                         }\r
8760 \r
8761                         // Add toolbar end before list box and after the previous button\r
8762                         // This is to fix the o2k7 editor skins\r
8763                         if (pr && co.ListBox) {\r
8764                                 if (pr.Button || pr.SplitButton)\r
8765                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8766                         }\r
8767 \r
8768                         // Render control HTML\r
8769 \r
8770                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
8771                         if (dom.stdMode)\r
8772                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
8773                         else\r
8774                                 h += '<td>' + co.renderHTML() + '</td>';\r
8775 \r
8776                         // Add toolbar start after list box and before the next button\r
8777                         // This is to fix the o2k7 editor skins\r
8778                         if (nx && co.ListBox) {\r
8779                                 if (nx.Button || nx.SplitButton)\r
8780                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8781                         }\r
8782                 }\r
8783 \r
8784                 c = 'mceToolbarEnd';\r
8785 \r
8786                 if (co.Button)\r
8787                         c += ' mceToolbarEndButton';\r
8788                 else if (co.SplitButton)\r
8789                         c += ' mceToolbarEndSplitButton';\r
8790                 else if (co.ListBox)\r
8791                         c += ' mceToolbarEndListBox';\r
8792 \r
8793                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8794 \r
8795                 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
8796         }\r
8797 });\r
8798 \r
8799 (function(tinymce) {\r
8800         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
8801 \r
8802         tinymce.create('tinymce.AddOnManager', {\r
8803                 items : [],\r
8804                 urls : {},\r
8805                 lookup : {},\r
8806 \r
8807                 onAdd : new Dispatcher(this),\r
8808 \r
8809                 get : function(n) {\r
8810                         return this.lookup[n];\r
8811                 },\r
8812 \r
8813                 requireLangPack : function(n) {\r
8814                         var s = tinymce.settings;\r
8815 \r
8816                         if (s && s.language)\r
8817                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
8818                 },\r
8819 \r
8820                 add : function(id, o) {\r
8821                         this.items.push(o);\r
8822                         this.lookup[id] = o;\r
8823                         this.onAdd.dispatch(this, id, o);\r
8824 \r
8825                         return o;\r
8826                 },\r
8827 \r
8828                 load : function(n, u, cb, s) {\r
8829                         var t = this;\r
8830 \r
8831                         if (t.urls[n])\r
8832                                 return;\r
8833 \r
8834                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
8835                                 u = tinymce.baseURL + '/' +  u;\r
8836 \r
8837                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
8838                         tinymce.ScriptLoader.add(u, cb, s);\r
8839                 }\r
8840         });\r
8841 \r
8842         // Create plugin and theme managers\r
8843         tinymce.PluginManager = new tinymce.AddOnManager();\r
8844         tinymce.ThemeManager = new tinymce.AddOnManager();\r
8845 }(tinymce));\r
8846 \r
8847 (function(tinymce) {\r
8848         // Shorten names\r
8849         var each = tinymce.each, extend = tinymce.extend,\r
8850                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
8851                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8852                 explode = tinymce.explode,\r
8853                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
8854 \r
8855         // Setup some URLs where the editor API is located and where the document is\r
8856         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
8857         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
8858                 tinymce.documentBaseURL += '/';\r
8859 \r
8860         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
8861 \r
8862         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
8863 \r
8864         // Add before unload listener\r
8865         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
8866         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
8867         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
8868 \r
8869         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
8870         Event.add(window, 'beforeunload', function(e) {\r
8871                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
8872         });\r
8873 \r
8874         tinymce.onAddEditor = new Dispatcher(tinymce);\r
8875 \r
8876         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
8877 \r
8878         tinymce.EditorManager = extend(tinymce, {\r
8879                 editors : [],\r
8880 \r
8881                 i18n : {},\r
8882 \r
8883                 activeEditor : null,\r
8884 \r
8885                 init : function(s) {\r
8886                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
8887 \r
8888                         function execCallback(se, n, s) {\r
8889                                 var f = se[n];\r
8890 \r
8891                                 if (!f)\r
8892                                         return;\r
8893 \r
8894                                 if (tinymce.is(f, 'string')) {\r
8895                                         s = f.replace(/\.\w+$/, '');\r
8896                                         s = s ? tinymce.resolve(s) : 0;\r
8897                                         f = tinymce.resolve(f);\r
8898                                 }\r
8899 \r
8900                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8901                         };\r
8902 \r
8903                         s = extend({\r
8904                                 theme : "simple",\r
8905                                 language : "en"\r
8906                         }, s);\r
8907 \r
8908                         t.settings = s;\r
8909 \r
8910                         // Legacy call\r
8911                         Event.add(document, 'init', function() {\r
8912                                 var l, co;\r
8913 \r
8914                                 execCallback(s, 'onpageload');\r
8915 \r
8916                                 switch (s.mode) {\r
8917                                         case "exact":\r
8918                                                 l = s.elements || '';\r
8919 \r
8920                                                 if(l.length > 0) {\r
8921                                                         each(explode(l), function(v) {\r
8922                                                                 if (DOM.get(v)) {\r
8923                                                                         ed = new tinymce.Editor(v, s);\r
8924                                                                         el.push(ed);\r
8925                                                                         ed.render(1);\r
8926                                                                 } else {\r
8927                                                                         each(document.forms, function(f) {\r
8928                                                                                 each(f.elements, function(e) {\r
8929                                                                                         if (e.name === v) {\r
8930                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8931                                                                                                 DOM.setAttrib(e, 'id', v);\r
8932 \r
8933                                                                                                 ed = new tinymce.Editor(v, s);\r
8934                                                                                                 el.push(ed);\r
8935                                                                                                 ed.render(1);\r
8936                                                                                         }\r
8937                                                                                 });\r
8938                                                                         });\r
8939                                                                 }\r
8940                                                         });\r
8941                                                 }\r
8942                                                 break;\r
8943 \r
8944                                         case "textareas":\r
8945                                         case "specific_textareas":\r
8946                                                 function hasClass(n, c) {\r
8947                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8948                                                 };\r
8949 \r
8950                                                 each(DOM.select('textarea'), function(v) {\r
8951                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8952                                                                 return;\r
8953 \r
8954                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8955                                                                 // Can we use the name\r
8956                                                                 e = DOM.get(v.name);\r
8957                                                                 if (!v.id && !e)\r
8958                                                                         v.id = v.name;\r
8959 \r
8960                                                                 // Generate unique name if missing or already exists\r
8961                                                                 if (!v.id || t.get(v.id))\r
8962                                                                         v.id = DOM.uniqueId();\r
8963 \r
8964                                                                 ed = new tinymce.Editor(v.id, s);\r
8965                                                                 el.push(ed);\r
8966                                                                 ed.render(1);\r
8967                                                         }\r
8968                                                 });\r
8969                                                 break;\r
8970                                 }\r
8971 \r
8972                                 // Call onInit when all editors are initialized\r
8973                                 if (s.oninit) {\r
8974                                         l = co = 0;\r
8975 \r
8976                                         each(el, function(ed) {\r
8977                                                 co++;\r
8978 \r
8979                                                 if (!ed.initialized) {\r
8980                                                         // Wait for it\r
8981                                                         ed.onInit.add(function() {\r
8982                                                                 l++;\r
8983 \r
8984                                                                 // All done\r
8985                                                                 if (l == co)\r
8986                                                                         execCallback(s, 'oninit');\r
8987                                                         });\r
8988                                                 } else\r
8989                                                         l++;\r
8990 \r
8991                                                 // All done\r
8992                                                 if (l == co)\r
8993                                                         execCallback(s, 'oninit');                                      \r
8994                                         });\r
8995                                 }\r
8996                         });\r
8997                 },\r
8998 \r
8999                 get : function(id) {\r
9000                         if (id === undefined)\r
9001                                 return this.editors;\r
9002 \r
9003                         return this.editors[id];\r
9004                 },\r
9005 \r
9006                 getInstanceById : function(id) {\r
9007                         return this.get(id);\r
9008                 },\r
9009 \r
9010                 add : function(editor) {\r
9011                         var self = this, editors = self.editors;\r
9012 \r
9013                         // Add named and index editor instance\r
9014                         editors[editor.id] = editor;\r
9015                         editors.push(editor);\r
9016 \r
9017                         self._setActive(editor);\r
9018                         self.onAddEditor.dispatch(self, editor);\r
9019 \r
9020 \r
9021                         return editor;\r
9022                 },\r
9023 \r
9024                 remove : function(editor) {\r
9025                         var t = this, i, editors = t.editors;\r
9026 \r
9027                         // Not in the collection\r
9028                         if (!editors[editor.id])\r
9029                                 return null;\r
9030 \r
9031                         delete editors[editor.id];\r
9032 \r
9033                         for (i = 0; i < editors.length; i++) {\r
9034                                 if (editors[i] == editor) {\r
9035                                         editors.splice(i, 1);\r
9036                                         break;\r
9037                                 }\r
9038                         }\r
9039 \r
9040                         // Select another editor since the active one was removed\r
9041                         if (t.activeEditor == editor)\r
9042                                 t._setActive(editors[0]);\r
9043 \r
9044                         editor.destroy();\r
9045                         t.onRemoveEditor.dispatch(t, editor);\r
9046 \r
9047                         return editor;\r
9048                 },\r
9049 \r
9050                 execCommand : function(c, u, v) {\r
9051                         var t = this, ed = t.get(v), w;\r
9052 \r
9053                         // Manager commands\r
9054                         switch (c) {\r
9055                                 case "mceFocus":\r
9056                                         ed.focus();\r
9057                                         return true;\r
9058 \r
9059                                 case "mceAddEditor":\r
9060                                 case "mceAddControl":\r
9061                                         if (!t.get(v))\r
9062                                                 new tinymce.Editor(v, t.settings).render();\r
9063 \r
9064                                         return true;\r
9065 \r
9066                                 case "mceAddFrameControl":\r
9067                                         w = v.window;\r
9068 \r
9069                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
9070                                         w.tinyMCE = tinyMCE;\r
9071                                         w.tinymce = tinymce;\r
9072 \r
9073                                         tinymce.DOM.doc = w.document;\r
9074                                         tinymce.DOM.win = w;\r
9075 \r
9076                                         ed = new tinymce.Editor(v.element_id, v);\r
9077                                         ed.render();\r
9078 \r
9079                                         // Fix IE memory leaks\r
9080                                         if (tinymce.isIE) {\r
9081                                                 function clr() {\r
9082                                                         ed.destroy();\r
9083                                                         w.detachEvent('onunload', clr);\r
9084                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
9085                                                 };\r
9086 \r
9087                                                 w.attachEvent('onunload', clr);\r
9088                                         }\r
9089 \r
9090                                         v.page_window = null;\r
9091 \r
9092                                         return true;\r
9093 \r
9094                                 case "mceRemoveEditor":\r
9095                                 case "mceRemoveControl":\r
9096                                         if (ed)\r
9097                                                 ed.remove();\r
9098 \r
9099                                         return true;\r
9100 \r
9101                                 case 'mceToggleEditor':\r
9102                                         if (!ed) {\r
9103                                                 t.execCommand('mceAddControl', 0, v);\r
9104                                                 return true;\r
9105                                         }\r
9106 \r
9107                                         if (ed.isHidden())\r
9108                                                 ed.show();\r
9109                                         else\r
9110                                                 ed.hide();\r
9111 \r
9112                                         return true;\r
9113                         }\r
9114 \r
9115                         // Run command on active editor\r
9116                         if (t.activeEditor)\r
9117                                 return t.activeEditor.execCommand(c, u, v);\r
9118 \r
9119                         return false;\r
9120                 },\r
9121 \r
9122                 execInstanceCommand : function(id, c, u, v) {\r
9123                         var ed = this.get(id);\r
9124 \r
9125                         if (ed)\r
9126                                 return ed.execCommand(c, u, v);\r
9127 \r
9128                         return false;\r
9129                 },\r
9130 \r
9131                 triggerSave : function() {\r
9132                         each(this.editors, function(e) {\r
9133                                 e.save();\r
9134                         });\r
9135                 },\r
9136 \r
9137                 addI18n : function(p, o) {\r
9138                         var lo, i18n = this.i18n;\r
9139 \r
9140                         if (!tinymce.is(p, 'string')) {\r
9141                                 each(p, function(o, lc) {\r
9142                                         each(o, function(o, g) {\r
9143                                                 each(o, function(o, k) {\r
9144                                                         if (g === 'common')\r
9145                                                                 i18n[lc + '.' + k] = o;\r
9146                                                         else\r
9147                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
9148                                                 });\r
9149                                         });\r
9150                                 });\r
9151                         } else {\r
9152                                 each(o, function(o, k) {\r
9153                                         i18n[p + '.' + k] = o;\r
9154                                 });\r
9155                         }\r
9156                 },\r
9157 \r
9158                 // Private methods\r
9159 \r
9160                 _setActive : function(editor) {\r
9161                         this.selectedInstance = this.activeEditor = editor;\r
9162                 }\r
9163         });\r
9164 })(tinymce);\r
9165 \r
9166 (function(tinymce) {\r
9167         // Shorten these names\r
9168         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
9169                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
9170                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
9171                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
9172                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
9173 \r
9174         tinymce.create('tinymce.Editor', {\r
9175                 Editor : function(id, s) {\r
9176                         var t = this;\r
9177 \r
9178                         t.id = t.editorId = id;\r
9179 \r
9180                         t.execCommands = {};\r
9181                         t.queryStateCommands = {};\r
9182                         t.queryValueCommands = {};\r
9183 \r
9184                         t.isNotDirty = false;\r
9185 \r
9186                         t.plugins = {};\r
9187 \r
9188                         // Add events to the editor\r
9189                         each([\r
9190                                 'onPreInit',\r
9191 \r
9192                                 'onBeforeRenderUI',\r
9193 \r
9194                                 'onPostRender',\r
9195 \r
9196                                 'onInit',\r
9197 \r
9198                                 'onRemove',\r
9199 \r
9200                                 'onActivate',\r
9201 \r
9202                                 'onDeactivate',\r
9203 \r
9204                                 'onClick',\r
9205 \r
9206                                 'onEvent',\r
9207 \r
9208                                 'onMouseUp',\r
9209 \r
9210                                 'onMouseDown',\r
9211 \r
9212                                 'onDblClick',\r
9213 \r
9214                                 'onKeyDown',\r
9215 \r
9216                                 'onKeyUp',\r
9217 \r
9218                                 'onKeyPress',\r
9219 \r
9220                                 'onContextMenu',\r
9221 \r
9222                                 'onSubmit',\r
9223 \r
9224                                 'onReset',\r
9225 \r
9226                                 'onPaste',\r
9227 \r
9228                                 'onPreProcess',\r
9229 \r
9230                                 'onPostProcess',\r
9231 \r
9232                                 'onBeforeSetContent',\r
9233 \r
9234                                 'onBeforeGetContent',\r
9235 \r
9236                                 'onSetContent',\r
9237 \r
9238                                 'onGetContent',\r
9239 \r
9240                                 'onLoadContent',\r
9241 \r
9242                                 'onSaveContent',\r
9243 \r
9244                                 'onNodeChange',\r
9245 \r
9246                                 'onChange',\r
9247 \r
9248                                 'onBeforeExecCommand',\r
9249 \r
9250                                 'onExecCommand',\r
9251 \r
9252                                 'onUndo',\r
9253 \r
9254                                 'onRedo',\r
9255 \r
9256                                 'onVisualAid',\r
9257 \r
9258                                 'onSetProgressState'\r
9259                         ], function(e) {\r
9260                                 t[e] = new Dispatcher(t);\r
9261                         });\r
9262 \r
9263                         t.settings = s = extend({\r
9264                                 id : id,\r
9265                                 language : 'en',\r
9266                                 docs_language : 'en',\r
9267                                 theme : 'simple',\r
9268                                 skin : 'default',\r
9269                                 delta_width : 0,\r
9270                                 delta_height : 0,\r
9271                                 popup_css : '',\r
9272                                 plugins : '',\r
9273                                 document_base_url : tinymce.documentBaseURL,\r
9274                                 add_form_submit_trigger : 1,\r
9275                                 submit_patch : 1,\r
9276                                 add_unload_trigger : 1,\r
9277                                 convert_urls : 1,\r
9278                                 relative_urls : 1,\r
9279                                 remove_script_host : 1,\r
9280                                 table_inline_editing : 0,\r
9281                                 object_resizing : 1,\r
9282                                 cleanup : 1,\r
9283                                 accessibility_focus : 1,\r
9284                                 custom_shortcuts : 1,\r
9285                                 custom_undo_redo_keyboard_shortcuts : 1,\r
9286                                 custom_undo_redo_restore_selection : 1,\r
9287                                 custom_undo_redo : 1,\r
9288                                 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
9289                                 visual_table_class : 'mceItemTable',\r
9290                                 visual : 1,\r
9291                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
9292                                 apply_source_formatting : 1,\r
9293                                 directionality : 'ltr',\r
9294                                 forced_root_block : 'p',\r
9295                                 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
9296                                 hidden_input : 1,\r
9297                                 padd_empty_editor : 1,\r
9298                                 render_ui : 1,\r
9299                                 init_theme : 1,\r
9300                                 force_p_newlines : 1,\r
9301                                 indentation : '30px',\r
9302                                 keep_styles : 1,\r
9303                                 fix_table_elements : 1,\r
9304                                 inline_styles : 1,\r
9305                                 convert_fonts_to_spans : true\r
9306                         }, s);\r
9307 \r
9308                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
9309                                 base_uri : tinyMCE.baseURI\r
9310                         });\r
9311 \r
9312                         t.baseURI = tinymce.baseURI;\r
9313 \r
9314                         // Call setup\r
9315                         t.execCallback('setup', t);\r
9316                 },\r
9317 \r
9318                 render : function(nst) {\r
9319                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
9320 \r
9321                         // Page is not loaded yet, wait for it\r
9322                         if (!Event.domLoaded) {\r
9323                                 Event.add(document, 'init', function() {\r
9324                                         t.render();\r
9325                                 });\r
9326                                 return;\r
9327                         }\r
9328 \r
9329                         tinyMCE.settings = s;\r
9330 \r
9331                         // Element not found, then skip initialization\r
9332                         if (!t.getElement())\r
9333                                 return;\r
9334 \r
9335                         // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
9336                         // browser says it has contentEditable support but there is no visible caret\r
9337                         // We will remove this check ones Apple implements full contentEditable support\r
9338                         if (tinymce.isIDevice)\r
9339                                 return;\r
9340 \r
9341                         // Add hidden input for non input elements inside form elements\r
9342                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
9343                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
9344 \r
9345                         if (tinymce.WindowManager)\r
9346                                 t.windowManager = new tinymce.WindowManager(t);\r
9347 \r
9348                         if (s.encoding == 'xml') {\r
9349                                 t.onGetContent.add(function(ed, o) {\r
9350                                         if (o.save)\r
9351                                                 o.content = DOM.encode(o.content);\r
9352                                 });\r
9353                         }\r
9354 \r
9355                         if (s.add_form_submit_trigger) {\r
9356                                 t.onSubmit.addToTop(function() {\r
9357                                         if (t.initialized) {\r
9358                                                 t.save();\r
9359                                                 t.isNotDirty = 1;\r
9360                                         }\r
9361                                 });\r
9362                         }\r
9363 \r
9364                         if (s.add_unload_trigger) {\r
9365                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
9366                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
9367                                                 t.save({format : 'raw', no_events : true});\r
9368                                 });\r
9369                         }\r
9370 \r
9371                         tinymce.addUnload(t.destroy, t);\r
9372 \r
9373                         if (s.submit_patch) {\r
9374                                 t.onBeforeRenderUI.add(function() {\r
9375                                         var n = t.getElement().form;\r
9376 \r
9377                                         if (!n)\r
9378                                                 return;\r
9379 \r
9380                                         // Already patched\r
9381                                         if (n._mceOldSubmit)\r
9382                                                 return;\r
9383 \r
9384                                         // Check page uses id="submit" or name="submit" for it's submit button\r
9385                                         if (!n.submit.nodeType && !n.submit.length) {\r
9386                                                 t.formElement = n;\r
9387                                                 n._mceOldSubmit = n.submit;\r
9388                                                 n.submit = function() {\r
9389                                                         // Save all instances\r
9390                                                         tinymce.triggerSave();\r
9391                                                         t.isNotDirty = 1;\r
9392 \r
9393                                                         return t.formElement._mceOldSubmit(t.formElement);\r
9394                                                 };\r
9395                                         }\r
9396 \r
9397                                         n = null;\r
9398                                 });\r
9399                         }\r
9400 \r
9401                         // Load scripts\r
9402                         function loadScripts() {\r
9403                                 if (s.language)\r
9404                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
9405 \r
9406                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
9407                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
9408 \r
9409                                 each(explode(s.plugins), function(p) {\r
9410                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
9411                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
9412                                                 if (p == 'safari')\r
9413                                                         return;\r
9414 \r
9415                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
9416                                         }\r
9417                                 });\r
9418 \r
9419                                 // Init when que is loaded\r
9420                                 sl.loadQueue(function() {\r
9421                                         if (!t.removed)\r
9422                                                 t.init();\r
9423                                 });\r
9424                         };\r
9425 \r
9426                         loadScripts();\r
9427                 },\r
9428 \r
9429                 init : function() {\r
9430                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
9431 \r
9432                         tinymce.add(t);\r
9433 \r
9434                         if (s.theme) {\r
9435                                 s.theme = s.theme.replace(/-/, '');\r
9436                                 o = ThemeManager.get(s.theme);\r
9437                                 t.theme = new o();\r
9438 \r
9439                                 if (t.theme.init && s.init_theme)\r
9440                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
9441                         }\r
9442 \r
9443                         // Create all plugins\r
9444                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
9445                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
9446 \r
9447                                 if (c) {\r
9448                                         po = new c(t, u);\r
9449 \r
9450                                         t.plugins[p] = po;\r
9451 \r
9452                                         if (po.init)\r
9453                                                 po.init(t, u);\r
9454                                 }\r
9455                         });\r
9456 \r
9457                         // Setup popup CSS path(s)\r
9458                         if (s.popup_css !== false) {\r
9459                                 if (s.popup_css)\r
9460                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
9461                                 else\r
9462                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
9463                         }\r
9464 \r
9465                         if (s.popup_css_add)\r
9466                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
9467 \r
9468                         t.controlManager = new tinymce.ControlManager(t);\r
9469 \r
9470                         if (s.custom_undo_redo) {\r
9471                                 // Add initial undo level\r
9472                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
9473                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
9474                                                 if (!t.undoManager.hasUndo())\r
9475                                                         t.undoManager.add();\r
9476                                         }\r
9477                                 });\r
9478 \r
9479                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
9480                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
9481                                                 t.undoManager.add();\r
9482                                 });\r
9483                         }\r
9484 \r
9485                         t.onExecCommand.add(function(ed, c) {\r
9486                                 // Don't refresh the select lists until caret move\r
9487                                 if (!/^(FontName|FontSize)$/.test(c))\r
9488                                         t.nodeChanged();\r
9489                         });\r
9490 \r
9491                         // Remove ghost selections on images and tables in Gecko\r
9492                         if (isGecko) {\r
9493                                 function repaint(a, o) {\r
9494                                         if (!o || !o.initial)\r
9495                                                 t.execCommand('mceRepaint');\r
9496                                 };\r
9497 \r
9498                                 t.onUndo.add(repaint);\r
9499                                 t.onRedo.add(repaint);\r
9500                                 t.onSetContent.add(repaint);\r
9501                         }\r
9502 \r
9503                         // Enables users to override the control factory\r
9504                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
9505 \r
9506                         // Measure box\r
9507                         if (s.render_ui) {\r
9508                                 w = s.width || e.style.width || e.offsetWidth;\r
9509                                 h = s.height || e.style.height || e.offsetHeight;\r
9510                                 t.orgDisplay = e.style.display;\r
9511                                 re = /^[0-9\.]+(|px)$/i;\r
9512 \r
9513                                 if (re.test('' + w))\r
9514                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
9515 \r
9516                                 if (re.test('' + h))\r
9517                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
9518 \r
9519                                 // Render UI\r
9520                                 o = t.theme.renderUI({\r
9521                                         targetNode : e,\r
9522                                         width : w,\r
9523                                         height : h,\r
9524                                         deltaWidth : s.delta_width,\r
9525                                         deltaHeight : s.delta_height\r
9526                                 });\r
9527 \r
9528                                 t.editorContainer = o.editorContainer;\r
9529                         }\r
9530 \r
9531 \r
9532                         // User specified a document.domain value\r
9533                         if (document.domain && location.hostname != document.domain)\r
9534                                 tinymce.relaxedDomain = document.domain;\r
9535 \r
9536                         // Resize editor\r
9537                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
9538                                 width : w,\r
9539                                 height : h\r
9540                         });\r
9541 \r
9542                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
9543                         if (h < 100)\r
9544                                 h = 100;\r
9545 \r
9546                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
9547 \r
9548                         // We only need to override paths if we have to\r
9549                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
9550                         if (s.document_base_url != tinymce.documentBaseURL)\r
9551                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
9552 \r
9553                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
9554 \r
9555                         if (tinymce.relaxedDomain)\r
9556                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
9557 \r
9558                         bi = s.body_id || 'tinymce';\r
9559                         if (bi.indexOf('=') != -1) {\r
9560                                 bi = t.getParam('body_id', '', 'hash');\r
9561                                 bi = bi[t.id] || bi;\r
9562                         }\r
9563 \r
9564                         bc = s.body_class || '';\r
9565                         if (bc.indexOf('=') != -1) {\r
9566                                 bc = t.getParam('body_class', '', 'hash');\r
9567                                 bc = bc[t.id] || '';\r
9568                         }\r
9569 \r
9570                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
9571 \r
9572                         // Domain relaxing enabled, then set document domain\r
9573                         if (tinymce.relaxedDomain) {\r
9574                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
9575                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
9576                                         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
9577                                 else if (tinymce.isOpera)\r
9578                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
9579                         }\r
9580 \r
9581                         // Create iframe\r
9582                         n = DOM.add(o.iframeContainer, 'iframe', {\r
9583                                 id : t.id + "_ifr",\r
9584                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
9585                                 frameBorder : '0',\r
9586                                 style : {\r
9587                                         width : '100%',\r
9588                                         height : h\r
9589                                 }\r
9590                         });\r
9591 \r
9592                         t.contentAreaContainer = o.iframeContainer;\r
9593                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
9594                         DOM.get(t.id).style.display = 'none';\r
9595 \r
9596                         if (!isIE || !tinymce.relaxedDomain)\r
9597                                 t.setupIframe();\r
9598 \r
9599                         e = n = o = null; // Cleanup\r
9600                 },\r
9601 \r
9602                 setupIframe : function() {\r
9603                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
9604 \r
9605                         // Setup iframe body\r
9606                         if (!isIE || !tinymce.relaxedDomain) {\r
9607                                 d.open();\r
9608                                 d.write(t.iframeHTML);\r
9609                                 d.close();\r
9610                         }\r
9611 \r
9612                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
9613                         if (!isIE) {\r
9614                                 try {\r
9615                                         if (!s.readonly)\r
9616                                                 d.designMode = 'On';\r
9617                                 } catch (ex) {\r
9618                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
9619                                         // The design mode will be set ones the editor is focused\r
9620                                 }\r
9621                         }\r
9622 \r
9623                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
9624                         if (isIE) {\r
9625                                 // It will not steal focus if we hide it while setting contentEditable\r
9626                                 b = t.getBody();\r
9627                                 DOM.hide(b);\r
9628 \r
9629                                 if (!s.readonly)\r
9630                                         b.contentEditable = true;\r
9631 \r
9632                                 DOM.show(b);\r
9633                         }\r
9634 \r
9635                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
9636                                 keep_values : true,\r
9637                                 url_converter : t.convertURL,\r
9638                                 url_converter_scope : t,\r
9639                                 hex_colors : s.force_hex_style_colors,\r
9640                                 class_filter : s.class_filter,\r
9641                                 update_styles : 1,\r
9642                                 fix_ie_paragraphs : 1,\r
9643                                 valid_styles : s.valid_styles\r
9644                         });\r
9645 \r
9646                         t.schema = new tinymce.dom.Schema();\r
9647 \r
9648                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
9649                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
9650                                 dom : t.dom,\r
9651                                 schema : t.schema\r
9652                         }));\r
9653 \r
9654                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
9655 \r
9656                         t.formatter = new tinymce.Formatter(this);\r
9657 \r
9658                         // Register default formats\r
9659                         t.formatter.register({\r
9660                                 alignleft : [\r
9661                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
9662                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
9663                                 ],\r
9664 \r
9665                                 aligncenter : [\r
9666                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
9667                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
9668                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
9669                                 ],\r
9670 \r
9671                                 alignright : [\r
9672                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
9673                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
9674                                 ],\r
9675 \r
9676                                 alignfull : [\r
9677                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
9678                                 ],\r
9679 \r
9680                                 bold : [\r
9681                                         {inline : 'strong'},\r
9682                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
9683                                         {inline : 'b'}\r
9684                                 ],\r
9685 \r
9686                                 italic : [\r
9687                                         {inline : 'em'},\r
9688                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
9689                                         {inline : 'i'}\r
9690                                 ],\r
9691 \r
9692                                 underline : [\r
9693                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
9694                                         {inline : 'u'}\r
9695                                 ],\r
9696 \r
9697                                 strikethrough : [\r
9698                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
9699                                         {inline : 'u'}\r
9700                                 ],\r
9701 \r
9702                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
9703                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
9704                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
9705                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
9706                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
9707                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
9708 \r
9709                                 removeformat : [\r
9710                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
9711                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
9712                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
9713                                 ]\r
9714                         });\r
9715 \r
9716                         // Register default block formats\r
9717                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
9718                                 t.formatter.register(name, {block : name, remove : 'all'});\r
9719                         });\r
9720 \r
9721                         // Register user defined formats\r
9722                         t.formatter.register(t.settings.formats);\r
9723 \r
9724                         t.undoManager = new tinymce.UndoManager(t);\r
9725 \r
9726                         // Pass through\r
9727                         t.undoManager.onAdd.add(function(um, l) {\r
9728                                 if (!l.initial)\r
9729                                         return t.onChange.dispatch(t, l, um);\r
9730                         });\r
9731 \r
9732                         t.undoManager.onUndo.add(function(um, l) {\r
9733                                 return t.onUndo.dispatch(t, l, um);\r
9734                         });\r
9735 \r
9736                         t.undoManager.onRedo.add(function(um, l) {\r
9737                                 return t.onRedo.dispatch(t, l, um);\r
9738                         });\r
9739 \r
9740                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
9741                                 forced_root_block : s.forced_root_block\r
9742                         });\r
9743 \r
9744                         t.editorCommands = new tinymce.EditorCommands(t);\r
9745 \r
9746                         // Pass through\r
9747                         t.serializer.onPreProcess.add(function(se, o) {\r
9748                                 return t.onPreProcess.dispatch(t, o, se);\r
9749                         });\r
9750 \r
9751                         t.serializer.onPostProcess.add(function(se, o) {\r
9752                                 return t.onPostProcess.dispatch(t, o, se);\r
9753                         });\r
9754 \r
9755                         t.onPreInit.dispatch(t);\r
9756 \r
9757                         if (!s.gecko_spellcheck)\r
9758                                 t.getBody().spellcheck = 0;\r
9759 \r
9760                         if (!s.readonly)\r
9761                                 t._addEvents();\r
9762 \r
9763                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
9764                         t.onPostRender.dispatch(t);\r
9765 \r
9766                         if (s.directionality)\r
9767                                 t.getBody().dir = s.directionality;\r
9768 \r
9769                         if (s.nowrap)\r
9770                                 t.getBody().style.whiteSpace = "nowrap";\r
9771 \r
9772                         if (s.custom_elements) {\r
9773                                 function handleCustom(ed, o) {\r
9774                                         each(explode(s.custom_elements), function(v) {\r
9775                                                 var n;\r
9776 \r
9777                                                 if (v.indexOf('~') === 0) {\r
9778                                                         v = v.substring(1);\r
9779                                                         n = 'span';\r
9780                                                 } else\r
9781                                                         n = 'div';\r
9782 \r
9783                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
9784                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
9785                                         });\r
9786                                 };\r
9787 \r
9788                                 t.onBeforeSetContent.add(handleCustom);\r
9789                                 t.onPostProcess.add(function(ed, o) {\r
9790                                         if (o.set)\r
9791                                                 handleCustom(ed, o);\r
9792                                 });\r
9793                         }\r
9794 \r
9795                         if (s.handle_node_change_callback) {\r
9796                                 t.onNodeChange.add(function(ed, cm, n) {\r
9797                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
9798                                 });\r
9799                         }\r
9800 \r
9801                         if (s.save_callback) {\r
9802                                 t.onSaveContent.add(function(ed, o) {\r
9803                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9804 \r
9805                                         if (h)\r
9806                                                 o.content = h;\r
9807                                 });\r
9808                         }\r
9809 \r
9810                         if (s.onchange_callback) {\r
9811                                 t.onChange.add(function(ed, l) {\r
9812                                         t.execCallback('onchange_callback', t, l);\r
9813                                 });\r
9814                         }\r
9815 \r
9816                         if (s.convert_newlines_to_brs) {\r
9817                                 t.onBeforeSetContent.add(function(ed, o) {\r
9818                                         if (o.initial)\r
9819                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
9820                                 });\r
9821                         }\r
9822 \r
9823                         if (s.fix_nesting && isIE) {\r
9824                                 t.onBeforeSetContent.add(function(ed, o) {\r
9825                                         o.content = t._fixNesting(o.content);\r
9826                                 });\r
9827                         }\r
9828 \r
9829                         if (s.preformatted) {\r
9830                                 t.onPostProcess.add(function(ed, o) {\r
9831                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
9832                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
9833 \r
9834                                         if (o.set)\r
9835                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
9836                                 });\r
9837                         }\r
9838 \r
9839                         if (s.verify_css_classes) {\r
9840                                 t.serializer.attribValueFilter = function(n, v) {\r
9841                                         var s, cl;\r
9842 \r
9843                                         if (n == 'class') {\r
9844                                                 // Build regexp for classes\r
9845                                                 if (!t.classesRE) {\r
9846                                                         cl = t.dom.getClasses();\r
9847 \r
9848                                                         if (cl.length > 0) {\r
9849                                                                 s = '';\r
9850 \r
9851                                                                 each (cl, function(o) {\r
9852                                                                         s += (s ? '|' : '') + o['class'];\r
9853                                                                 });\r
9854 \r
9855                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
9856                                                         }\r
9857                                                 }\r
9858 \r
9859                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
9860                                         }\r
9861 \r
9862                                         return v;\r
9863                                 };\r
9864                         }\r
9865 \r
9866                         if (s.cleanup_callback) {\r
9867                                 t.onBeforeSetContent.add(function(ed, o) {\r
9868                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9869                                 });\r
9870 \r
9871                                 t.onPreProcess.add(function(ed, o) {\r
9872                                         if (o.set)\r
9873                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
9874 \r
9875                                         if (o.get)\r
9876                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
9877                                 });\r
9878 \r
9879                                 t.onPostProcess.add(function(ed, o) {\r
9880                                         if (o.set)\r
9881                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9882 \r
9883                                         if (o.get)                                              \r
9884                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
9885                                 });\r
9886                         }\r
9887 \r
9888                         if (s.save_callback) {\r
9889                                 t.onGetContent.add(function(ed, o) {\r
9890                                         if (o.save)\r
9891                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9892                                 });\r
9893                         }\r
9894 \r
9895                         if (s.handle_event_callback) {\r
9896                                 t.onEvent.add(function(ed, e, o) {\r
9897                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9898                                                 Event.cancel(e);\r
9899                                 });\r
9900                         }\r
9901 \r
9902                         // Add visual aids when new contents is added\r
9903                         t.onSetContent.add(function() {\r
9904                                 t.addVisual(t.getBody());\r
9905                         });\r
9906 \r
9907                         // Remove empty contents\r
9908                         if (s.padd_empty_editor) {\r
9909                                 t.onPostProcess.add(function(ed, o) {\r
9910                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9911                                 });\r
9912                         }\r
9913 \r
9914                         if (isGecko) {\r
9915                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9916                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9917                                 function fixLinks(ed, o) {\r
9918                                         each(ed.dom.select('a'), function(n) {\r
9919                                                 var pn = n.parentNode;\r
9920 \r
9921                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9922                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9923                                         });\r
9924                                 };\r
9925 \r
9926                                 t.onExecCommand.add(function(ed, cmd) {\r
9927                                         if (cmd === 'CreateLink')\r
9928                                                 fixLinks(ed);\r
9929                                 });\r
9930 \r
9931                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9932 \r
9933                                 if (!s.readonly) {\r
9934                                         try {\r
9935                                                 // Design mode must be set here once again to fix a bug where\r
9936                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9937                                                 d.designMode = 'Off';\r
9938                                                 d.designMode = 'On';\r
9939                                         } catch (ex) {\r
9940                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9941                                                 // The design mode will be set ones the editor is focused\r
9942                                         }\r
9943                                 }\r
9944                         }\r
9945 \r
9946                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9947                         setTimeout(function () {\r
9948                                 if (t.removed)\r
9949                                         return;\r
9950 \r
9951                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9952                                 t.startContent = t.getContent({format : 'raw'});\r
9953                                 t.initialized = true;\r
9954 \r
9955                                 t.onInit.dispatch(t);\r
9956                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9957                                 t.execCallback('init_instance_callback', t);\r
9958                                 t.focus(true);\r
9959                                 t.nodeChanged({initial : 1});\r
9960 \r
9961                                 // Load specified content CSS last\r
9962                                 if (s.content_css) {\r
9963                                         tinymce.each(explode(s.content_css), function(u) {\r
9964                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9965                                         });\r
9966                                 }\r
9967 \r
9968                                 // Handle auto focus\r
9969                                 if (s.auto_focus) {\r
9970                                         setTimeout(function () {\r
9971                                                 var ed = tinymce.get(s.auto_focus);\r
9972 \r
9973                                                 ed.selection.select(ed.getBody(), 1);\r
9974                                                 ed.selection.collapse(1);\r
9975                                                 ed.getWin().focus();\r
9976                                         }, 100);\r
9977                                 }\r
9978                         }, 1);\r
9979         \r
9980                         e = null;\r
9981                 },\r
9982 \r
9983 \r
9984                 focus : function(sf) {\r
9985                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
9986 \r
9987                         if (!sf) {\r
9988                                 // Get selected control element\r
9989                                 ieRng = t.selection.getRng();\r
9990                                 if (ieRng.item) {\r
9991                                         controlElm = ieRng.item(0);\r
9992                                 }\r
9993 \r
9994                                 // Is not content editable\r
9995                                 if (!ce)\r
9996                                         t.getWin().focus();\r
9997 \r
9998                                 // Restore selected control element\r
9999                                 // This is needed when for example an image is selected within a\r
10000                                 // layer a call to focus will then remove the control selection\r
10001                                 if (controlElm && controlElm.ownerDocument == doc) {\r
10002                                         ieRng = doc.body.createControlRange();\r
10003                                         ieRng.addElement(controlElm);\r
10004                                         ieRng.select();\r
10005                                 }\r
10006 \r
10007                         }\r
10008 \r
10009                         if (tinymce.activeEditor != t) {\r
10010                                 if ((oed = tinymce.activeEditor) != null)\r
10011                                         oed.onDeactivate.dispatch(oed, t);\r
10012 \r
10013                                 t.onActivate.dispatch(t, oed);\r
10014                         }\r
10015 \r
10016                         tinymce._setActive(t);\r
10017                 },\r
10018 \r
10019                 execCallback : function(n) {\r
10020                         var t = this, f = t.settings[n], s;\r
10021 \r
10022                         if (!f)\r
10023                                 return;\r
10024 \r
10025                         // Look through lookup\r
10026                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
10027                                 f = s.func;\r
10028                                 s = s.scope;\r
10029                         }\r
10030 \r
10031                         if (is(f, 'string')) {\r
10032                                 s = f.replace(/\.\w+$/, '');\r
10033                                 s = s ? tinymce.resolve(s) : 0;\r
10034                                 f = tinymce.resolve(f);\r
10035                                 t.callbackLookup = t.callbackLookup || {};\r
10036                                 t.callbackLookup[n] = {func : f, scope : s};\r
10037                         }\r
10038 \r
10039                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
10040                 },\r
10041 \r
10042                 translate : function(s) {\r
10043                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
10044 \r
10045                         if (!s)\r
10046                                 return '';\r
10047 \r
10048                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
10049                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
10050                         });\r
10051                 },\r
10052 \r
10053                 getLang : function(n, dv) {\r
10054                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
10055                 },\r
10056 \r
10057                 getParam : function(n, dv, ty) {\r
10058                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
10059 \r
10060                         if (ty === 'hash') {\r
10061                                 o = {};\r
10062 \r
10063                                 if (is(v, 'string')) {\r
10064                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
10065                                                 v = v.split('=');\r
10066 \r
10067                                                 if (v.length > 1)\r
10068                                                         o[tr(v[0])] = tr(v[1]);\r
10069                                                 else\r
10070                                                         o[tr(v[0])] = tr(v);\r
10071                                         });\r
10072                                 } else\r
10073                                         o = v;\r
10074 \r
10075                                 return o;\r
10076                         }\r
10077 \r
10078                         return v;\r
10079                 },\r
10080 \r
10081                 nodeChanged : function(o) {\r
10082                         var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody();\r
10083 \r
10084                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
10085                         if (t.initialized) {\r
10086                                 o = o || {};\r
10087                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
10088 \r
10089                                 // Get parents and add them to object\r
10090                                 o.parents = [];\r
10091                                 t.dom.getParent(n, function(node) {\r
10092                                         if (node.nodeName == 'BODY')\r
10093                                                 return true;\r
10094 \r
10095                                         o.parents.push(node);\r
10096                                 });\r
10097 \r
10098                                 t.onNodeChange.dispatch(\r
10099                                         t,\r
10100                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
10101                                         n,\r
10102                                         s.isCollapsed(),\r
10103                                         o\r
10104                                 );\r
10105                         }\r
10106                 },\r
10107 \r
10108                 addButton : function(n, s) {\r
10109                         var t = this;\r
10110 \r
10111                         t.buttons = t.buttons || {};\r
10112                         t.buttons[n] = s;\r
10113                 },\r
10114 \r
10115                 addCommand : function(n, f, s) {\r
10116                         this.execCommands[n] = {func : f, scope : s || this};\r
10117                 },\r
10118 \r
10119                 addQueryStateHandler : function(n, f, s) {\r
10120                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
10121                 },\r
10122 \r
10123                 addQueryValueHandler : function(n, f, s) {\r
10124                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
10125                 },\r
10126 \r
10127                 addShortcut : function(pa, desc, cmd_func, sc) {\r
10128                         var t = this, c;\r
10129 \r
10130                         if (!t.settings.custom_shortcuts)\r
10131                                 return false;\r
10132 \r
10133                         t.shortcuts = t.shortcuts || {};\r
10134 \r
10135                         if (is(cmd_func, 'string')) {\r
10136                                 c = cmd_func;\r
10137 \r
10138                                 cmd_func = function() {\r
10139                                         t.execCommand(c, false, null);\r
10140                                 };\r
10141                         }\r
10142 \r
10143                         if (is(cmd_func, 'object')) {\r
10144                                 c = cmd_func;\r
10145 \r
10146                                 cmd_func = function() {\r
10147                                         t.execCommand(c[0], c[1], c[2]);\r
10148                                 };\r
10149                         }\r
10150 \r
10151                         each(explode(pa), function(pa) {\r
10152                                 var o = {\r
10153                                         func : cmd_func,\r
10154                                         scope : sc || this,\r
10155                                         desc : desc,\r
10156                                         alt : false,\r
10157                                         ctrl : false,\r
10158                                         shift : false\r
10159                                 };\r
10160 \r
10161                                 each(explode(pa, '+'), function(v) {\r
10162                                         switch (v) {\r
10163                                                 case 'alt':\r
10164                                                 case 'ctrl':\r
10165                                                 case 'shift':\r
10166                                                         o[v] = true;\r
10167                                                         break;\r
10168 \r
10169                                                 default:\r
10170                                                         o.charCode = v.charCodeAt(0);\r
10171                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
10172                                         }\r
10173                                 });\r
10174 \r
10175                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
10176                         });\r
10177 \r
10178                         return true;\r
10179                 },\r
10180 \r
10181                 execCommand : function(cmd, ui, val, a) {\r
10182                         var t = this, s = 0, o, st;\r
10183 \r
10184                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
10185                                 t.focus();\r
10186 \r
10187                         o = {};\r
10188                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
10189                         if (o.terminate)\r
10190                                 return false;\r
10191 \r
10192                         // Command callback\r
10193                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
10194                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10195                                 return true;\r
10196                         }\r
10197 \r
10198                         // Registred commands\r
10199                         if (o = t.execCommands[cmd]) {\r
10200                                 st = o.func.call(o.scope, ui, val);\r
10201 \r
10202                                 // Fall through on true\r
10203                                 if (st !== true) {\r
10204                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10205                                         return st;\r
10206                                 }\r
10207                         }\r
10208 \r
10209                         // Plugin commands\r
10210                         each(t.plugins, function(p) {\r
10211                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
10212                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10213                                         s = 1;\r
10214                                         return false;\r
10215                                 }\r
10216                         });\r
10217 \r
10218                         if (s)\r
10219                                 return true;\r
10220 \r
10221                         // Theme commands\r
10222                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
10223                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10224                                 return true;\r
10225                         }\r
10226 \r
10227                         // Execute global commands\r
10228                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
10229                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10230                                 return true;\r
10231                         }\r
10232 \r
10233                         // Editor commands\r
10234                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
10235                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10236                                 return true;\r
10237                         }\r
10238 \r
10239                         // Browser commands\r
10240                         t.getDoc().execCommand(cmd, ui, val);\r
10241                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10242                 },\r
10243 \r
10244                 queryCommandState : function(cmd) {\r
10245                         var t = this, o, s;\r
10246 \r
10247                         // Is hidden then return undefined\r
10248                         if (t._isHidden())\r
10249                                 return;\r
10250 \r
10251                         // Registred commands\r
10252                         if (o = t.queryStateCommands[cmd]) {\r
10253                                 s = o.func.call(o.scope);\r
10254 \r
10255                                 // Fall though on true\r
10256                                 if (s !== true)\r
10257                                         return s;\r
10258                         }\r
10259 \r
10260                         // Registred commands\r
10261                         o = t.editorCommands.queryCommandState(cmd);\r
10262                         if (o !== -1)\r
10263                                 return o;\r
10264 \r
10265                         // Browser commands\r
10266                         try {\r
10267                                 return this.getDoc().queryCommandState(cmd);\r
10268                         } catch (ex) {\r
10269                                 // Fails sometimes see bug: 1896577\r
10270                         }\r
10271                 },\r
10272 \r
10273                 queryCommandValue : function(c) {\r
10274                         var t = this, o, s;\r
10275 \r
10276                         // Is hidden then return undefined\r
10277                         if (t._isHidden())\r
10278                                 return;\r
10279 \r
10280                         // Registred commands\r
10281                         if (o = t.queryValueCommands[c]) {\r
10282                                 s = o.func.call(o.scope);\r
10283 \r
10284                                 // Fall though on true\r
10285                                 if (s !== true)\r
10286                                         return s;\r
10287                         }\r
10288 \r
10289                         // Registred commands\r
10290                         o = t.editorCommands.queryCommandValue(c);\r
10291                         if (is(o))\r
10292                                 return o;\r
10293 \r
10294                         // Browser commands\r
10295                         try {\r
10296                                 return this.getDoc().queryCommandValue(c);\r
10297                         } catch (ex) {\r
10298                                 // Fails sometimes see bug: 1896577\r
10299                         }\r
10300                 },\r
10301 \r
10302                 show : function() {\r
10303                         var t = this;\r
10304 \r
10305                         DOM.show(t.getContainer());\r
10306                         DOM.hide(t.id);\r
10307                         t.load();\r
10308                 },\r
10309 \r
10310                 hide : function() {\r
10311                         var t = this, d = t.getDoc();\r
10312 \r
10313                         // Fixed bug where IE has a blinking cursor left from the editor\r
10314                         if (isIE && d)\r
10315                                 d.execCommand('SelectAll');\r
10316 \r
10317                         // We must save before we hide so Safari doesn't crash\r
10318                         t.save();\r
10319                         DOM.hide(t.getContainer());\r
10320                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
10321                 },\r
10322 \r
10323                 isHidden : function() {\r
10324                         return !DOM.isHidden(this.id);\r
10325                 },\r
10326 \r
10327                 setProgressState : function(b, ti, o) {\r
10328                         this.onSetProgressState.dispatch(this, b, ti, o);\r
10329 \r
10330                         return b;\r
10331                 },\r
10332 \r
10333                 load : function(o) {\r
10334                         var t = this, e = t.getElement(), h;\r
10335 \r
10336                         if (e) {\r
10337                                 o = o || {};\r
10338                                 o.load = true;\r
10339 \r
10340                                 // Double encode existing entities in the value\r
10341                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
10342                                 o.element = e;\r
10343 \r
10344                                 if (!o.no_events)\r
10345                                         t.onLoadContent.dispatch(t, o);\r
10346 \r
10347                                 o.element = e = null;\r
10348 \r
10349                                 return h;\r
10350                         }\r
10351                 },\r
10352 \r
10353                 save : function(o) {\r
10354                         var t = this, e = t.getElement(), h, f;\r
10355 \r
10356                         if (!e || !t.initialized)\r
10357                                 return;\r
10358 \r
10359                         o = o || {};\r
10360                         o.save = true;\r
10361 \r
10362                         // Add undo level will trigger onchange event\r
10363                         if (!o.no_events) {\r
10364                                 t.undoManager.typing = 0;\r
10365                                 t.undoManager.add();\r
10366                         }\r
10367 \r
10368                         o.element = e;\r
10369                         h = o.content = t.getContent(o);\r
10370 \r
10371                         if (!o.no_events)\r
10372                                 t.onSaveContent.dispatch(t, o);\r
10373 \r
10374                         h = o.content;\r
10375 \r
10376                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
10377                                 e.innerHTML = h;\r
10378 \r
10379                                 // Update hidden form element\r
10380                                 if (f = DOM.getParent(t.id, 'form')) {\r
10381                                         each(f.elements, function(e) {\r
10382                                                 if (e.name == t.id) {\r
10383                                                         e.value = h;\r
10384                                                         return false;\r
10385                                                 }\r
10386                                         });\r
10387                                 }\r
10388                         } else\r
10389                                 e.value = h;\r
10390 \r
10391                         o.element = e = null;\r
10392 \r
10393                         return h;\r
10394                 },\r
10395 \r
10396                 setContent : function(h, o) {\r
10397                         var t = this;\r
10398 \r
10399                         o = o || {};\r
10400                         o.format = o.format || 'html';\r
10401                         o.set = true;\r
10402                         o.content = h;\r
10403 \r
10404                         if (!o.no_events)\r
10405                                 t.onBeforeSetContent.dispatch(t, o);\r
10406 \r
10407                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
10408                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
10409                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
10410                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
10411                                 o.format = 'raw';\r
10412                         }\r
10413 \r
10414                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
10415 \r
10416                         if (o.format != 'raw' && t.settings.cleanup) {\r
10417                                 o.getInner = true;\r
10418                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
10419                         }\r
10420 \r
10421                         if (!o.no_events)\r
10422                                 t.onSetContent.dispatch(t, o);\r
10423 \r
10424                         return o.content;\r
10425                 },\r
10426 \r
10427                 getContent : function(o) {\r
10428                         var t = this, h;\r
10429 \r
10430                         o = o || {};\r
10431                         o.format = o.format || 'html';\r
10432                         o.get = true;\r
10433 \r
10434                         if (!o.no_events)\r
10435                                 t.onBeforeGetContent.dispatch(t, o);\r
10436 \r
10437                         if (o.format != 'raw' && t.settings.cleanup) {\r
10438                                 o.getInner = true;\r
10439                                 h = t.serializer.serialize(t.getBody(), o);\r
10440                         } else\r
10441                                 h = t.getBody().innerHTML;\r
10442 \r
10443                         h = h.replace(/^\s*|\s*$/g, '');\r
10444                         o.content = h;\r
10445 \r
10446                         if (!o.no_events)\r
10447                                 t.onGetContent.dispatch(t, o);\r
10448 \r
10449                         return o.content;\r
10450                 },\r
10451 \r
10452                 isDirty : function() {\r
10453                         var t = this;\r
10454 \r
10455                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
10456                 },\r
10457 \r
10458                 getContainer : function() {\r
10459                         var t = this;\r
10460 \r
10461                         if (!t.container)\r
10462                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
10463 \r
10464                         return t.container;\r
10465                 },\r
10466 \r
10467                 getContentAreaContainer : function() {\r
10468                         return this.contentAreaContainer;\r
10469                 },\r
10470 \r
10471                 getElement : function() {\r
10472                         return DOM.get(this.settings.content_element || this.id);\r
10473                 },\r
10474 \r
10475                 getWin : function() {\r
10476                         var t = this, e;\r
10477 \r
10478                         if (!t.contentWindow) {\r
10479                                 e = DOM.get(t.id + "_ifr");\r
10480 \r
10481                                 if (e)\r
10482                                         t.contentWindow = e.contentWindow;\r
10483                         }\r
10484 \r
10485                         return t.contentWindow;\r
10486                 },\r
10487 \r
10488                 getDoc : function() {\r
10489                         var t = this, w;\r
10490 \r
10491                         if (!t.contentDocument) {\r
10492                                 w = t.getWin();\r
10493 \r
10494                                 if (w)\r
10495                                         t.contentDocument = w.document;\r
10496                         }\r
10497 \r
10498                         return t.contentDocument;\r
10499                 },\r
10500 \r
10501                 getBody : function() {\r
10502                         return this.bodyElement || this.getDoc().body;\r
10503                 },\r
10504 \r
10505                 convertURL : function(u, n, e) {\r
10506                         var t = this, s = t.settings;\r
10507 \r
10508                         // Use callback instead\r
10509                         if (s.urlconverter_callback)\r
10510                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
10511 \r
10512                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
10513                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
10514                                 return u;\r
10515 \r
10516                         // Convert to relative\r
10517                         if (s.relative_urls)\r
10518                                 return t.documentBaseURI.toRelative(u);\r
10519 \r
10520                         // Convert to absolute\r
10521                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
10522 \r
10523                         return u;\r
10524                 },\r
10525 \r
10526                 addVisual : function(e) {\r
10527                         var t = this, s = t.settings;\r
10528 \r
10529                         e = e || t.getBody();\r
10530 \r
10531                         if (!is(t.hasVisual))\r
10532                                 t.hasVisual = s.visual;\r
10533 \r
10534                         each(t.dom.select('table,a', e), function(e) {\r
10535                                 var v;\r
10536 \r
10537                                 switch (e.nodeName) {\r
10538                                         case 'TABLE':\r
10539                                                 v = t.dom.getAttrib(e, 'border');\r
10540 \r
10541                                                 if (!v || v == '0') {\r
10542                                                         if (t.hasVisual)\r
10543                                                                 t.dom.addClass(e, s.visual_table_class);\r
10544                                                         else\r
10545                                                                 t.dom.removeClass(e, s.visual_table_class);\r
10546                                                 }\r
10547 \r
10548                                                 return;\r
10549 \r
10550                                         case 'A':\r
10551                                                 v = t.dom.getAttrib(e, 'name');\r
10552 \r
10553                                                 if (v) {\r
10554                                                         if (t.hasVisual)\r
10555                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
10556                                                         else\r
10557                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
10558                                                 }\r
10559 \r
10560                                                 return;\r
10561                                 }\r
10562                         });\r
10563 \r
10564                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
10565                 },\r
10566 \r
10567                 remove : function() {\r
10568                         var t = this, e = t.getContainer();\r
10569 \r
10570                         t.removed = 1; // Cancels post remove event execution\r
10571                         t.hide();\r
10572 \r
10573                         t.execCallback('remove_instance_callback', t);\r
10574                         t.onRemove.dispatch(t);\r
10575 \r
10576                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
10577                         t.onExecCommand.listeners = [];\r
10578 \r
10579                         tinymce.remove(t);\r
10580                         DOM.remove(e);\r
10581                 },\r
10582 \r
10583                 destroy : function(s) {\r
10584                         var t = this;\r
10585 \r
10586                         // One time is enough\r
10587                         if (t.destroyed)\r
10588                                 return;\r
10589 \r
10590                         if (!s) {\r
10591                                 tinymce.removeUnload(t.destroy);\r
10592                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
10593 \r
10594                                 // Manual destroy\r
10595                                 if (t.theme && t.theme.destroy)\r
10596                                         t.theme.destroy();\r
10597 \r
10598                                 // Destroy controls, selection and dom\r
10599                                 t.controlManager.destroy();\r
10600                                 t.selection.destroy();\r
10601                                 t.dom.destroy();\r
10602 \r
10603                                 // Remove all events\r
10604 \r
10605                                 // Don't clear the window or document if content editable\r
10606                                 // is enabled since other instances might still be present\r
10607                                 if (!t.settings.content_editable) {\r
10608                                         Event.clear(t.getWin());\r
10609                                         Event.clear(t.getDoc());\r
10610                                 }\r
10611 \r
10612                                 Event.clear(t.getBody());\r
10613                                 Event.clear(t.formElement);\r
10614                         }\r
10615 \r
10616                         if (t.formElement) {\r
10617                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
10618                                 t.formElement._mceOldSubmit = null;\r
10619                         }\r
10620 \r
10621                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
10622 \r
10623                         if (t.selection)\r
10624                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
10625 \r
10626                         t.destroyed = 1;\r
10627                 },\r
10628 \r
10629                 // Internal functions\r
10630 \r
10631                 _addEvents : function() {\r
10632                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
10633                         var t = this, i, s = t.settings, lo = {\r
10634                                 mouseup : 'onMouseUp',\r
10635                                 mousedown : 'onMouseDown',\r
10636                                 click : 'onClick',\r
10637                                 keyup : 'onKeyUp',\r
10638                                 keydown : 'onKeyDown',\r
10639                                 keypress : 'onKeyPress',\r
10640                                 submit : 'onSubmit',\r
10641                                 reset : 'onReset',\r
10642                                 contextmenu : 'onContextMenu',\r
10643                                 dblclick : 'onDblClick',\r
10644                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
10645                         };\r
10646 \r
10647                         function eventHandler(e, o) {\r
10648                                 var ty = e.type;\r
10649 \r
10650                                 // Don't fire events when it's removed\r
10651                                 if (t.removed)\r
10652                                         return;\r
10653 \r
10654                                 // Generic event handler\r
10655                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
10656                                         // Specific event handler\r
10657                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
10658                                 }\r
10659                         };\r
10660 \r
10661                         // Add DOM events\r
10662                         each(lo, function(v, k) {\r
10663                                 switch (k) {\r
10664                                         case 'contextmenu':\r
10665                                                 if (tinymce.isOpera) {\r
10666                                                         // Fake contextmenu on Opera\r
10667                                                         t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
10668                                                                 if (e.ctrlKey) {\r
10669                                                                         e.fakeType = 'contextmenu';\r
10670                                                                         eventHandler(e);\r
10671                                                                 }\r
10672                                                         });\r
10673                                                 } else\r
10674                                                         t.dom.bind(t.getBody(), k, eventHandler);\r
10675                                                 break;\r
10676 \r
10677                                         case 'paste':\r
10678                                                 t.dom.bind(t.getBody(), k, function(e) {\r
10679                                                         eventHandler(e);\r
10680                                                 });\r
10681                                                 break;\r
10682 \r
10683                                         case 'submit':\r
10684                                         case 'reset':\r
10685                                                 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
10686                                                 break;\r
10687 \r
10688                                         default:\r
10689                                                 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
10690                                 }\r
10691                         });\r
10692 \r
10693                         t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
10694                                 t.focus(true);\r
10695                         });\r
10696 \r
10697 \r
10698                         // Fixes bug where a specified document_base_uri could result in broken images\r
10699                         // This will also fix drag drop of images in Gecko\r
10700                         if (tinymce.isGecko) {\r
10701                                 // Convert all images to absolute URLs\r
10702 /*                              t.onSetContent.add(function(ed, o) {\r
10703                                         each(ed.dom.select('img'), function(e) {\r
10704                                                 var v;\r
10705 \r
10706                                                 if (v = e.getAttribute('_mce_src'))\r
10707                                                         e.src = t.documentBaseURI.toAbsolute(v);\r
10708                                         })\r
10709                                 });*/\r
10710 \r
10711                                 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
10712                                         var v;\r
10713 \r
10714                                         e = e.target;\r
10715 \r
10716                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
10717                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
10718                                 });\r
10719                         }\r
10720 \r
10721                         // Set various midas options in Gecko\r
10722                         if (isGecko) {\r
10723                                 function setOpts() {\r
10724                                         var t = this, d = t.getDoc(), s = t.settings;\r
10725 \r
10726                                         if (isGecko && !s.readonly) {\r
10727                                                 if (t._isHidden()) {\r
10728                                                         try {\r
10729                                                                 if (!s.content_editable)\r
10730                                                                         d.designMode = 'On';\r
10731                                                         } catch (ex) {\r
10732                                                                 // Fails if it's hidden\r
10733                                                         }\r
10734                                                 }\r
10735 \r
10736                                                 try {\r
10737                                                         // Try new Gecko method\r
10738                                                         d.execCommand("styleWithCSS", 0, false);\r
10739                                                 } catch (ex) {\r
10740                                                         // Use old method\r
10741                                                         if (!t._isHidden())\r
10742                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
10743                                                 }\r
10744 \r
10745                                                 if (!s.table_inline_editing)\r
10746                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
10747 \r
10748                                                 if (!s.object_resizing)\r
10749                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
10750                                         }\r
10751                                 };\r
10752 \r
10753                                 t.onBeforeExecCommand.add(setOpts);\r
10754                                 t.onMouseDown.add(setOpts);\r
10755                         }\r
10756 \r
10757                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
10758                         // WebKit can't even do simple things like selecting an image\r
10759                         // This also fixes so it's possible to select mceItemAnchors\r
10760                         if (tinymce.isWebKit) {\r
10761                                 t.onClick.add(function(ed, e) {\r
10762                                         e = e.target;\r
10763 \r
10764                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
10765                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor')))\r
10766                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
10767                                 });\r
10768                         }\r
10769 \r
10770                         // Add node change handlers\r
10771                         t.onMouseUp.add(t.nodeChanged);\r
10772                         //t.onClick.add(t.nodeChanged);\r
10773                         t.onKeyUp.add(function(ed, e) {\r
10774                                 var c = e.keyCode;\r
10775 \r
10776                                 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
10777                                         t.nodeChanged();\r
10778                         });\r
10779 \r
10780                         // Add reset handler\r
10781                         t.onReset.add(function() {\r
10782                                 t.setContent(t.startContent, {format : 'raw'});\r
10783                         });\r
10784 \r
10785                         // Add shortcuts\r
10786                         if (s.custom_shortcuts) {\r
10787                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
10788                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
10789                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
10790                                 }\r
10791 \r
10792                                 // Add default shortcuts for gecko\r
10793                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
10794                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
10795                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
10796 \r
10797                                 // BlockFormat shortcuts keys\r
10798                                 for (i=1; i<=6; i++)\r
10799                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
10800 \r
10801                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
10802                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
10803                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
10804 \r
10805                                 function find(e) {\r
10806                                         var v = null;\r
10807 \r
10808                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
10809                                                 return v;\r
10810 \r
10811                                         each(t.shortcuts, function(o) {\r
10812                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
10813                                                         return;\r
10814                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
10815                                                         return;\r
10816 \r
10817                                                 if (o.alt != e.altKey)\r
10818                                                         return;\r
10819 \r
10820                                                 if (o.shift != e.shiftKey)\r
10821                                                         return;\r
10822 \r
10823                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
10824                                                         v = o;\r
10825                                                         return false;\r
10826                                                 }\r
10827                                         });\r
10828 \r
10829                                         return v;\r
10830                                 };\r
10831 \r
10832                                 t.onKeyUp.add(function(ed, e) {\r
10833                                         var o = find(e);\r
10834 \r
10835                                         if (o)\r
10836                                                 return Event.cancel(e);\r
10837                                 });\r
10838 \r
10839                                 t.onKeyPress.add(function(ed, e) {\r
10840                                         var o = find(e);\r
10841 \r
10842                                         if (o)\r
10843                                                 return Event.cancel(e);\r
10844                                 });\r
10845 \r
10846                                 t.onKeyDown.add(function(ed, e) {\r
10847                                         var o = find(e);\r
10848 \r
10849                                         if (o) {\r
10850                                                 o.func.call(o.scope);\r
10851                                                 return Event.cancel(e);\r
10852                                         }\r
10853                                 });\r
10854                         }\r
10855 \r
10856                         if (tinymce.isIE) {\r
10857                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
10858                                 // It will also block mceItemNoResize items\r
10859                                 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
10860                                         var re = t.resizeInfo, cb;\r
10861 \r
10862                                         e = e.target;\r
10863 \r
10864                                         // Don't do this action for non image elements\r
10865                                         if (e.nodeName !== 'IMG')\r
10866                                                 return;\r
10867 \r
10868                                         if (re)\r
10869                                                 t.dom.unbind(re.node, re.ev, re.cb);\r
10870 \r
10871                                         if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
10872                                                 ev = 'resizeend';\r
10873                                                 cb = t.dom.bind(e, ev, function(e) {\r
10874                                                         var v;\r
10875 \r
10876                                                         e = e.target;\r
10877 \r
10878                                                         if (v = t.dom.getStyle(e, 'width')) {\r
10879                                                                 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
10880                                                                 t.dom.setStyle(e, 'width', '');\r
10881                                                         }\r
10882 \r
10883                                                         if (v = t.dom.getStyle(e, 'height')) {\r
10884                                                                 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
10885                                                                 t.dom.setStyle(e, 'height', '');\r
10886                                                         }\r
10887                                                 });\r
10888                                         } else {\r
10889                                                 ev = 'resizestart';\r
10890                                                 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
10891                                         }\r
10892 \r
10893                                         re = t.resizeInfo = {\r
10894                                                 node : e,\r
10895                                                 ev : ev,\r
10896                                                 cb : cb\r
10897                                         };\r
10898                                 });\r
10899 \r
10900                                 t.onKeyDown.add(function(ed, e) {\r
10901                                         switch (e.keyCode) {\r
10902                                                 case 8:\r
10903                                                         // Fix IE control + backspace browser bug\r
10904                                                         if (t.selection.getRng().item) {\r
10905                                                                 ed.dom.remove(t.selection.getRng().item(0));\r
10906                                                                 return Event.cancel(e);\r
10907                                                         }\r
10908                                         }\r
10909                                 });\r
10910 \r
10911                                 /*if (t.dom.boxModel) {\r
10912                                         t.getBody().style.height = '100%';\r
10913 \r
10914                                         Event.add(t.getWin(), 'resize', function(e) {\r
10915                                                 var docElm = t.getDoc().documentElement;\r
10916 \r
10917                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10918                                         });\r
10919                                 }*/\r
10920                         }\r
10921 \r
10922                         if (tinymce.isOpera) {\r
10923                                 t.onClick.add(function(ed, e) {\r
10924                                         Event.prevent(e);\r
10925                                 });\r
10926                         }\r
10927 \r
10928                         // Add custom undo/redo handlers\r
10929                         if (s.custom_undo_redo) {\r
10930                                 function addUndo() {\r
10931                                         t.undoManager.typing = 0;\r
10932                                         t.undoManager.add();\r
10933                                 };\r
10934 \r
10935                                 t.dom.bind(t.getDoc(), 'focusout', function(e) {\r
10936                                         if (!t.removed && t.undoManager.typing)\r
10937                                                 addUndo();\r
10938                                 });\r
10939 \r
10940                                 t.onKeyUp.add(function(ed, e) {\r
10941                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10942                                                 addUndo();\r
10943                                 });\r
10944 \r
10945                                 t.onKeyDown.add(function(ed, e) {\r
10946                                         var rng, tmpRng, parent, offset;\r
10947 \r
10948                                         // IE has a really odd bug where the DOM might include an node that doesn't have\r
10949                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
10950                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
10951                                         // after you delete contents from it. See: #3008923\r
10952                                         if (isIE && e.keyCode == 46) {\r
10953                                                 rng = t.selection.getRng();\r
10954 \r
10955                                                 if (rng.parentElement) {\r
10956                                                         parent = rng.parentElement();\r
10957 \r
10958                                                         // Get the current caret position within the element\r
10959                                                         tmpRng = rng.duplicate();\r
10960                                                         tmpRng.moveToElementText(parent);\r
10961                                                         tmpRng.setEndPoint('EndToEnd', rng);\r
10962                                                         offset = tmpRng.text.length;\r
10963 \r
10964                                                         // Select next word when ctrl key is used in combo with delete\r
10965                                                         if (e.ctrlKey) {\r
10966                                                                 rng.moveEnd('word', 1);\r
10967                                                                 rng.select();\r
10968                                                         }\r
10969 \r
10970                                                         // Delete contents\r
10971                                                         t.selection.getSel().clear();\r
10972 \r
10973                                                         // Check if we are within the same parent\r
10974                                                         if (rng.parentElement() == parent) {\r
10975                                                                 try {\r
10976                                                                         // Update the HTML and hopefully it will remove the artifacts\r
10977                                                                         parent.innerHTML = parent.innerHTML;\r
10978                                                                 } catch (ex) {\r
10979                                                                         // And since it's IE it can sometimes produce an unknown runtime error\r
10980                                                                 }\r
10981 \r
10982                                                                 // Restore the caret position\r
10983                                                                 tmpRng.moveToElementText(parent);\r
10984                                                                 tmpRng.collapse();\r
10985                                                                 tmpRng.move('character', offset);\r
10986                                                                 tmpRng.select();\r
10987                                                         }\r
10988 \r
10989                                                         // Block the default delete behavior since it might be broken\r
10990                                                         e.preventDefault();\r
10991                                                         return;\r
10992                                                 }\r
10993                                         }\r
10994 \r
10995                                         // Is caracter positon keys\r
10996                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10997                                                 if (t.undoManager.typing)\r
10998                                                         addUndo();\r
10999 \r
11000                                                 return;\r
11001                                         }\r
11002 \r
11003                                         if (!t.undoManager.typing) {\r
11004                                                 t.undoManager.add();\r
11005                                                 t.undoManager.typing = 1;\r
11006                                         }\r
11007                                 });\r
11008 \r
11009                                 t.onMouseDown.add(function() {\r
11010                                         if (t.undoManager.typing)\r
11011                                                 addUndo();\r
11012                                 });\r
11013                         }\r
11014                 },\r
11015 \r
11016                 _isHidden : function() {\r
11017                         var s;\r
11018 \r
11019                         if (!isGecko)\r
11020                                 return 0;\r
11021 \r
11022                         // Weird, wheres that cursor selection?\r
11023                         s = this.selection.getSel();\r
11024                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
11025                 },\r
11026 \r
11027                 // Fix for bug #1867292\r
11028                 _fixNesting : function(s) {\r
11029                         var d = [], i;\r
11030 \r
11031                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
11032                                 var e;\r
11033 \r
11034                                 // Handle end element\r
11035                                 if (b === '/') {\r
11036                                         if (!d.length)\r
11037                                                 return '';\r
11038 \r
11039                                         if (c !== d[d.length - 1].tag) {\r
11040                                                 for (i=d.length - 1; i>=0; i--) {\r
11041                                                         if (d[i].tag === c) {\r
11042                                                                 d[i].close = 1;\r
11043                                                                 break;\r
11044                                                         }\r
11045                                                 }\r
11046 \r
11047                                                 return '';\r
11048                                         } else {\r
11049                                                 d.pop();\r
11050 \r
11051                                                 if (d.length && d[d.length - 1].close) {\r
11052                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
11053                                                         d.pop();\r
11054                                                 }\r
11055                                         }\r
11056                                 } else {\r
11057                                         // Ignore these\r
11058                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
11059                                                 return a;\r
11060 \r
11061                                         // Ignore closed ones\r
11062                                         if (/\/>$/.test(a))\r
11063                                                 return a;\r
11064 \r
11065                                         d.push({tag : c}); // Push start element\r
11066                                 }\r
11067 \r
11068                                 return a;\r
11069                         });\r
11070 \r
11071                         // End all open tags\r
11072                         for (i=d.length - 1; i>=0; i--)\r
11073                                 s += '</' + d[i].tag + '>';\r
11074 \r
11075                         return s;\r
11076                 }\r
11077         });\r
11078 })(tinymce);\r
11079 \r
11080 (function(tinymce) {\r
11081         // Added for compression purposes\r
11082         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
11083 \r
11084         tinymce.EditorCommands = function(editor) {\r
11085                 var dom = editor.dom,\r
11086                         selection = editor.selection,\r
11087                         commands = {state: {}, exec : {}, value : {}},\r
11088                         settings = editor.settings,\r
11089                         bookmark;\r
11090 \r
11091                 function execCommand(command, ui, value) {\r
11092                         var func;\r
11093 \r
11094                         command = command.toLowerCase();\r
11095                         if (func = commands.exec[command]) {\r
11096                                 func(command, ui, value);\r
11097                                 return TRUE;\r
11098                         }\r
11099 \r
11100                         return FALSE;\r
11101                 };\r
11102 \r
11103                 function queryCommandState(command) {\r
11104                         var func;\r
11105 \r
11106                         command = command.toLowerCase();\r
11107                         if (func = commands.state[command])\r
11108                                 return func(command);\r
11109 \r
11110                         return -1;\r
11111                 };\r
11112 \r
11113                 function queryCommandValue(command) {\r
11114                         var func;\r
11115 \r
11116                         command = command.toLowerCase();\r
11117                         if (func = commands.value[command])\r
11118                                 return func(command);\r
11119 \r
11120                         return FALSE;\r
11121                 };\r
11122 \r
11123                 function addCommands(command_list, type) {\r
11124                         type = type || 'exec';\r
11125 \r
11126                         each(command_list, function(callback, command) {\r
11127                                 each(command.toLowerCase().split(','), function(command) {\r
11128                                         commands[type][command] = callback;\r
11129                                 });\r
11130                         });\r
11131                 };\r
11132 \r
11133                 // Expose public methods\r
11134                 tinymce.extend(this, {\r
11135                         execCommand : execCommand,\r
11136                         queryCommandState : queryCommandState,\r
11137                         queryCommandValue : queryCommandValue,\r
11138                         addCommands : addCommands\r
11139                 });\r
11140 \r
11141                 // Private methods\r
11142 \r
11143                 function execNativeCommand(command, ui, value) {\r
11144                         if (ui === undefined)\r
11145                                 ui = FALSE;\r
11146 \r
11147                         if (value === undefined)\r
11148                                 value = null;\r
11149 \r
11150                         return editor.getDoc().execCommand(command, ui, value);\r
11151                 };\r
11152 \r
11153                 function isFormatMatch(name) {\r
11154                         return editor.formatter.match(name);\r
11155                 };\r
11156 \r
11157                 function toggleFormat(name, value) {\r
11158                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
11159                 };\r
11160 \r
11161                 function storeSelection(type) {\r
11162                         bookmark = selection.getBookmark(type);\r
11163                 };\r
11164 \r
11165                 function restoreSelection() {\r
11166                         selection.moveToBookmark(bookmark);\r
11167                 };\r
11168 \r
11169                 // Add execCommand overrides\r
11170                 addCommands({\r
11171                         // Ignore these, added for compatibility\r
11172                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
11173 \r
11174                         // Add undo manager logic\r
11175                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
11176                                 editor.undoManager.add();\r
11177                         },\r
11178 \r
11179                         'Cut,Copy,Paste' : function(command) {\r
11180                                 var doc = editor.getDoc(), failed;\r
11181 \r
11182                                 // Try executing the native command\r
11183                                 try {\r
11184                                         execNativeCommand(command);\r
11185                                 } catch (ex) {\r
11186                                         // Command failed\r
11187                                         failed = TRUE;\r
11188                                 }\r
11189 \r
11190                                 // Present alert message about clipboard access not being available\r
11191                                 if (failed || !doc.queryCommandSupported(command)) {\r
11192                                         if (tinymce.isGecko) {\r
11193                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
11194                                                         if (state)\r
11195                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
11196                                                 });\r
11197                                         } else\r
11198                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
11199                                 }\r
11200                         },\r
11201 \r
11202                         // Override unlink command\r
11203                         unlink : function(command) {\r
11204                                 if (selection.isCollapsed())\r
11205                                         selection.select(selection.getNode());\r
11206 \r
11207                                 execNativeCommand(command);\r
11208                                 selection.collapse(FALSE);\r
11209                         },\r
11210 \r
11211                         // Override justify commands to use the text formatter engine\r
11212                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11213                                 var align = command.substring(7);\r
11214 \r
11215                                 // Remove all other alignments first\r
11216                                 each('left,center,right,full'.split(','), function(name) {\r
11217                                         if (align != name)\r
11218                                                 editor.formatter.remove('align' + name);\r
11219                                 });\r
11220 \r
11221                                 toggleFormat('align' + align);\r
11222                         },\r
11223 \r
11224                         // Override list commands to fix WebKit bug\r
11225                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11226                                 var listElm, listParent;\r
11227 \r
11228                                 execNativeCommand(command);\r
11229 \r
11230                                 // WebKit produces lists within block elements so we need to split them\r
11231                                 // we will replace the native list creation logic to custom logic later on\r
11232                                 // TODO: Remove this when the list creation logic is removed\r
11233                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
11234                                 if (listElm) {\r
11235                                         listParent = listElm.parentNode;\r
11236 \r
11237                                         // If list is within a text block then split that block\r
11238                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
11239                                                 storeSelection();\r
11240                                                 dom.split(listParent, listElm);\r
11241                                                 restoreSelection();\r
11242                                         }\r
11243                                 }\r
11244                         },\r
11245 \r
11246                         // Override commands to use the text formatter engine\r
11247                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11248                                 toggleFormat(command);\r
11249                         },\r
11250 \r
11251                         // Override commands to use the text formatter engine\r
11252                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
11253                                 toggleFormat(command, value);\r
11254                         },\r
11255 \r
11256                         FontSize : function(command, ui, value) {\r
11257                                 var fontClasses, fontSizes;\r
11258 \r
11259                                 // Convert font size 1-7 to styles\r
11260                                 if (value >= 1 && value <= 7) {\r
11261                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
11262                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
11263 \r
11264                                         if (fontClasses)\r
11265                                                 value = fontClasses[value - 1] || value;\r
11266                                         else\r
11267                                                 value = fontSizes[value - 1] || value;\r
11268                                 }\r
11269 \r
11270                                 toggleFormat(command, value);\r
11271                         },\r
11272 \r
11273                         RemoveFormat : function(command) {\r
11274                                 editor.formatter.remove(command);\r
11275                         },\r
11276 \r
11277                         mceBlockQuote : function(command) {\r
11278                                 toggleFormat('blockquote');\r
11279                         },\r
11280 \r
11281                         FormatBlock : function(command, ui, value) {\r
11282                                 return toggleFormat(value);\r
11283                         },\r
11284 \r
11285                         mceCleanup : function() {\r
11286                                 var bookmark = selection.getBookmark();\r
11287 \r
11288                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
11289 \r
11290                                 selection.moveToBookmark(bookmark);\r
11291                         },\r
11292 \r
11293                         mceRemoveNode : function(command, ui, value) {\r
11294                                 var node = value || selection.getNode();\r
11295 \r
11296                                 // Make sure that the body node isn't removed\r
11297                                 if (node != editor.getBody()) {\r
11298                                         storeSelection();\r
11299                                         editor.dom.remove(node, TRUE);\r
11300                                         restoreSelection();\r
11301                                 }\r
11302                         },\r
11303 \r
11304                         mceSelectNodeDepth : function(command, ui, value) {\r
11305                                 var counter = 0;\r
11306 \r
11307                                 dom.getParent(selection.getNode(), function(node) {\r
11308                                         if (node.nodeType == 1 && counter++ == value) {\r
11309                                                 selection.select(node);\r
11310                                                 return FALSE;\r
11311                                         }\r
11312                                 }, editor.getBody());\r
11313                         },\r
11314 \r
11315                         mceSelectNode : function(command, ui, value) {\r
11316                                 selection.select(value);\r
11317                         },\r
11318 \r
11319                         mceInsertContent : function(command, ui, value) {\r
11320                                 selection.setContent(value);\r
11321                         },\r
11322 \r
11323                         mceInsertRawHTML : function(command, ui, value) {\r
11324                                 selection.setContent('tiny_mce_marker');\r
11325                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value));\r
11326                         },\r
11327 \r
11328                         mceSetContent : function(command, ui, value) {\r
11329                                 editor.setContent(value);\r
11330                         },\r
11331 \r
11332                         'Indent,Outdent' : function(command) {\r
11333                                 var intentValue, indentUnit, value;\r
11334 \r
11335                                 // Setup indent level\r
11336                                 intentValue = settings.indentation;\r
11337                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
11338                                 intentValue = parseInt(intentValue);\r
11339 \r
11340                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
11341                                         each(selection.getSelectedBlocks(), function(element) {\r
11342                                                 if (command == 'outdent') {\r
11343                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
11344                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
11345                                                 } else\r
11346                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
11347                                         });\r
11348                                 } else\r
11349                                         execNativeCommand(command);\r
11350                         },\r
11351 \r
11352                         mceRepaint : function() {\r
11353                                 var bookmark;\r
11354 \r
11355                                 if (tinymce.isGecko) {\r
11356                                         try {\r
11357                                                 storeSelection(TRUE);\r
11358 \r
11359                                                 if (selection.getSel())\r
11360                                                         selection.getSel().selectAllChildren(editor.getBody());\r
11361 \r
11362                                                 selection.collapse(TRUE);\r
11363                                                 restoreSelection();\r
11364                                         } catch (ex) {\r
11365                                                 // Ignore\r
11366                                         }\r
11367                                 }\r
11368                         },\r
11369 \r
11370                         mceToggleFormat : function(command, ui, value) {\r
11371                                 editor.formatter.toggle(value);\r
11372                         },\r
11373 \r
11374                         InsertHorizontalRule : function() {\r
11375                                 selection.setContent('<hr />');\r
11376                         },\r
11377 \r
11378                         mceToggleVisualAid : function() {\r
11379                                 editor.hasVisual = !editor.hasVisual;\r
11380                                 editor.addVisual();\r
11381                         },\r
11382 \r
11383                         mceReplaceContent : function(command, ui, value) {\r
11384                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
11385                         },\r
11386 \r
11387                         mceInsertLink : function(command, ui, value) {\r
11388                                 var link = dom.getParent(selection.getNode(), 'a');\r
11389 \r
11390                                 if (tinymce.is(value, 'string'))\r
11391                                         value = {href : value};\r
11392 \r
11393                                 if (!link) {\r
11394                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
11395                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
11396                                                 dom.setAttribs(link, value);\r
11397                                         });\r
11398                                 } else {\r
11399                                         if (value.href)\r
11400                                                 dom.setAttribs(link, value);\r
11401                                         else\r
11402                                                 editor.dom.remove(link, TRUE);\r
11403                                 }\r
11404                         },\r
11405                         \r
11406                         selectAll : function() {\r
11407                                 var root = dom.getRoot();\r
11408                                 var rng = dom.createRng();\r
11409                                 rng.setStart(root, 0);\r
11410                                 rng.setEnd(root, root.childNodes.length);\r
11411                                 editor.selection.setRng(rng);\r
11412                         }\r
11413                 });\r
11414 \r
11415                 // Add queryCommandState overrides\r
11416                 addCommands({\r
11417                         // Override justify commands\r
11418                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11419                                 return isFormatMatch('align' + command.substring(7));\r
11420                         },\r
11421 \r
11422                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11423                                 return isFormatMatch(command);\r
11424                         },\r
11425 \r
11426                         mceBlockQuote : function() {\r
11427                                 return isFormatMatch('blockquote');\r
11428                         },\r
11429 \r
11430                         Outdent : function() {\r
11431                                 var node;\r
11432 \r
11433                                 if (settings.inline_styles) {\r
11434                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11435                                                 return TRUE;\r
11436 \r
11437                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11438                                                 return TRUE;\r
11439                                 }\r
11440 \r
11441                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
11442                         },\r
11443 \r
11444                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11445                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
11446                         }\r
11447                 }, 'state');\r
11448 \r
11449                 // Add queryCommandValue overrides\r
11450                 addCommands({\r
11451                         'FontSize,FontName' : function(command) {\r
11452                                 var value = 0, parent;\r
11453 \r
11454                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
11455                                         if (command == 'fontsize')\r
11456                                                 value = parent.style.fontSize;\r
11457                                         else\r
11458                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
11459                                 }\r
11460 \r
11461                                 return value;\r
11462                         }\r
11463                 }, 'value');\r
11464 \r
11465                 // Add undo manager logic\r
11466                 if (settings.custom_undo_redo) {\r
11467                         addCommands({\r
11468                                 Undo : function() {\r
11469                                         editor.undoManager.undo();\r
11470                                 },\r
11471 \r
11472                                 Redo : function() {\r
11473                                         editor.undoManager.redo();\r
11474                                 }\r
11475                         });\r
11476                 }\r
11477         };\r
11478 })(tinymce);\r
11479 (function(tinymce) {\r
11480         var Dispatcher = tinymce.util.Dispatcher;\r
11481 \r
11482         tinymce.UndoManager = function(editor) {\r
11483                 var self, index = 0, data = [];\r
11484 \r
11485                 function getContent() {\r
11486                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
11487                 };\r
11488 \r
11489                 return self = {\r
11490                         typing : 0,\r
11491 \r
11492                         onAdd : new Dispatcher(self),\r
11493                         onUndo : new Dispatcher(self),\r
11494                         onRedo : new Dispatcher(self),\r
11495 \r
11496                         add : function(level) {\r
11497                                 var i, settings = editor.settings, lastLevel;\r
11498 \r
11499                                 level = level || {};\r
11500                                 level.content = getContent();\r
11501 \r
11502                                 // Add undo level if needed\r
11503                                 lastLevel = data[index];\r
11504                                 if (lastLevel && lastLevel.content == level.content) {\r
11505                                         if (index > 0 || data.length == 1)\r
11506                                                 return null;\r
11507                                 }\r
11508 \r
11509                                 // Time to compress\r
11510                                 if (settings.custom_undo_redo_levels) {\r
11511                                         if (data.length > settings.custom_undo_redo_levels) {\r
11512                                                 for (i = 0; i < data.length - 1; i++)\r
11513                                                         data[i] = data[i + 1];\r
11514 \r
11515                                                 data.length--;\r
11516                                                 index = data.length;\r
11517                                         }\r
11518                                 }\r
11519 \r
11520                                 // Get a non intrusive normalized bookmark\r
11521                                 level.bookmark = editor.selection.getBookmark(2, true);\r
11522 \r
11523                                 // Crop array if needed\r
11524                                 if (index < data.length - 1) {\r
11525                                         // Treat first level as initial\r
11526                                         if (index == 0)\r
11527                                                 data = [];\r
11528                                         else\r
11529                                                 data.length = index + 1;\r
11530                                 }\r
11531 \r
11532                                 data.push(level);\r
11533                                 index = data.length - 1;\r
11534 \r
11535                                 self.onAdd.dispatch(self, level);\r
11536                                 editor.isNotDirty = 0;\r
11537 \r
11538                                 return level;\r
11539                         },\r
11540 \r
11541                         undo : function() {\r
11542                                 var level, i;\r
11543 \r
11544                                 if (self.typing) {\r
11545                                         self.add();\r
11546                                         self.typing = 0;\r
11547                                 }\r
11548 \r
11549                                 if (index > 0) {\r
11550                                         level = data[--index];\r
11551 \r
11552                                         editor.setContent(level.content, {format : 'raw'});\r
11553                                         editor.selection.moveToBookmark(level.bookmark);\r
11554 \r
11555                                         self.onUndo.dispatch(self, level);\r
11556                                 }\r
11557 \r
11558                                 return level;\r
11559                         },\r
11560 \r
11561                         redo : function() {\r
11562                                 var level;\r
11563 \r
11564                                 if (index < data.length - 1) {\r
11565                                         level = data[++index];\r
11566 \r
11567                                         editor.setContent(level.content, {format : 'raw'});\r
11568                                         editor.selection.moveToBookmark(level.bookmark);\r
11569 \r
11570                                         self.onRedo.dispatch(self, level);\r
11571                                 }\r
11572 \r
11573                                 return level;\r
11574                         },\r
11575 \r
11576                         clear : function() {\r
11577                                 data = [];\r
11578                                 index = self.typing = 0;\r
11579                         },\r
11580 \r
11581                         hasUndo : function() {\r
11582                                 return index > 0 || self.typing;\r
11583                         },\r
11584 \r
11585                         hasRedo : function() {\r
11586                                 return index < data.length - 1;\r
11587                         }\r
11588                 };\r
11589         };\r
11590 })(tinymce);\r
11591 \r
11592 (function(tinymce) {\r
11593         // Shorten names\r
11594         var Event = tinymce.dom.Event,\r
11595                 isIE = tinymce.isIE,\r
11596                 isGecko = tinymce.isGecko,\r
11597                 isOpera = tinymce.isOpera,\r
11598                 each = tinymce.each,\r
11599                 extend = tinymce.extend,\r
11600                 TRUE = true,\r
11601                 FALSE = false;\r
11602 \r
11603         function cloneFormats(node) {\r
11604                 var clone, temp, inner;\r
11605 \r
11606                 do {\r
11607                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
11608                                 if (clone) {\r
11609                                         temp = node.cloneNode(false);\r
11610                                         temp.appendChild(clone);\r
11611                                         clone = temp;\r
11612                                 } else {\r
11613                                         clone = inner = node.cloneNode(false);\r
11614                                 }\r
11615 \r
11616                                 clone.removeAttribute('id');\r
11617                         }\r
11618                 } while (node = node.parentNode);\r
11619 \r
11620                 if (clone)\r
11621                         return {wrapper : clone, inner : inner};\r
11622         };\r
11623 \r
11624         // Checks if the selection/caret is at the end of the specified block element\r
11625         function isAtEnd(rng, par) {\r
11626                 var rng2 = par.ownerDocument.createRange();\r
11627 \r
11628                 rng2.setStart(rng.endContainer, rng.endOffset);\r
11629                 rng2.setEndAfter(par);\r
11630 \r
11631                 // 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
11632                 return rng2.cloneContents().textContent.length == 0;\r
11633         };\r
11634 \r
11635         function isEmpty(n) {\r
11636                 n = n.innerHTML;\r
11637 \r
11638                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
11639                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
11640 \r
11641                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
11642         };\r
11643 \r
11644         function splitList(selection, dom, li) {\r
11645                 var listBlock, block;\r
11646 \r
11647                 if (isEmpty(li)) {\r
11648                         listBlock = dom.getParent(li, 'ul,ol');\r
11649 \r
11650                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
11651                                 dom.split(listBlock, li);\r
11652                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
11653                                 dom.replace(block, li);\r
11654                                 selection.select(block, 1);\r
11655                         }\r
11656 \r
11657                         return FALSE;\r
11658                 }\r
11659 \r
11660                 return TRUE;\r
11661         };\r
11662 \r
11663         tinymce.create('tinymce.ForceBlocks', {\r
11664                 ForceBlocks : function(ed) {\r
11665                         var t = this, s = ed.settings, elm;\r
11666 \r
11667                         t.editor = ed;\r
11668                         t.dom = ed.dom;\r
11669                         elm = (s.forced_root_block || 'p').toLowerCase();\r
11670                         s.element = elm.toUpperCase();\r
11671 \r
11672                         ed.onPreInit.add(t.setup, t);\r
11673 \r
11674                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
11675                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11676                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11677                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
11678                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
11679 \r
11680                         function padd(ed, o) {\r
11681                                 if (isOpera)\r
11682                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
11683 \r
11684                                 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
11685 \r
11686                                 if (!isIE && !isOpera && o.set) {\r
11687                                         // Use &nbsp; instead of BR in padded paragraphs\r
11688                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
11689                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
11690                                 } else\r
11691                                         o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
11692                         };\r
11693 \r
11694                         ed.onBeforeSetContent.add(padd);\r
11695                         ed.onPostProcess.add(padd);\r
11696 \r
11697                         if (s.forced_root_block) {\r
11698                                 ed.onInit.add(t.forceRoots, t);\r
11699                                 ed.onSetContent.add(t.forceRoots, t);\r
11700                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
11701                         }\r
11702                 },\r
11703 \r
11704                 setup : function() {\r
11705                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
11706 \r
11707                         // Force root blocks when typing and when getting output\r
11708                         if (s.forced_root_block) {\r
11709                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
11710                                 ed.onKeyUp.add(t.forceRoots, t);\r
11711                                 ed.onPreProcess.add(t.forceRoots, t);\r
11712                         }\r
11713 \r
11714                         if (s.force_br_newlines) {\r
11715                                 // Force IE to produce BRs on enter\r
11716                                 if (isIE) {\r
11717                                         ed.onKeyPress.add(function(ed, e) {\r
11718                                                 var n;\r
11719 \r
11720                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
11721                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
11722                                                         n = dom.get('__');\r
11723                                                         n.removeAttribute('id');\r
11724                                                         selection.select(n);\r
11725                                                         selection.collapse();\r
11726                                                         return Event.cancel(e);\r
11727                                                 }\r
11728                                         });\r
11729                                 }\r
11730                         }\r
11731 \r
11732                         if (s.force_p_newlines) {\r
11733                                 if (!isIE) {\r
11734                                         ed.onKeyPress.add(function(ed, e) {\r
11735                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
11736                                                         Event.cancel(e);\r
11737                                         });\r
11738                                 } else {\r
11739                                         // Ungly hack to for IE to preserve the formatting when you press\r
11740                                         // enter at the end of a block element with formatted contents\r
11741                                         // This logic overrides the browsers default logic with\r
11742                                         // custom logic that enables us to control the output\r
11743                                         tinymce.addUnload(function() {\r
11744                                                 t._previousFormats = 0; // Fix IE leak\r
11745                                         });\r
11746 \r
11747                                         ed.onKeyPress.add(function(ed, e) {\r
11748                                                 t._previousFormats = 0;\r
11749 \r
11750                                                 // Clone the current formats, this will later be applied to the new block contents\r
11751                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
11752                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
11753                                         });\r
11754 \r
11755                                         ed.onKeyUp.add(function(ed, e) {\r
11756                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
11757                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
11758                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
11759 \r
11760                                                         // Parent is an empty block\r
11761                                                         if (!parent.hasChildNodes()) {\r
11762                                                                 parent = dom.getParent(parent, dom.isBlock);\r
11763 \r
11764                                                                 if (parent) {\r
11765                                                                         parent.innerHTML = '';\r
11766         \r
11767                                                                         if (t._previousFormats) {\r
11768                                                                                 parent.appendChild(fmt.wrapper);\r
11769                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
11770                                                                         } else\r
11771                                                                                 parent.innerHTML = '\uFEFF';\r
11772 \r
11773                                                                         selection.select(parent, 1);\r
11774                                                                         ed.getDoc().execCommand('Delete', false, null);\r
11775                                                                 }\r
11776                                                         }\r
11777                                                 }\r
11778                                         });\r
11779                                 }\r
11780 \r
11781                                 if (isGecko) {\r
11782                                         ed.onKeyDown.add(function(ed, e) {\r
11783                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
11784                                                         t.backspaceDelete(e, e.keyCode == 8);\r
11785                                         });\r
11786                                 }\r
11787                         }\r
11788 \r
11789                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
11790                         if (tinymce.isWebKit) {\r
11791                                 function insertBr(ed) {\r
11792                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
11793 \r
11794                                         // Insert BR element\r
11795                                         rng.insertNode(br = dom.create('br'));\r
11796 \r
11797                                         // Place caret after BR\r
11798                                         rng.setStartAfter(br);\r
11799                                         rng.setEndAfter(br);\r
11800                                         selection.setRng(rng);\r
11801 \r
11802                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
11803                                         if (selection.getSel().focusNode == br.previousSibling) {\r
11804                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
11805                                                 selection.collapse(TRUE);\r
11806                                         }\r
11807 \r
11808                                         // Create a temporary DIV after the BR and get the position as it\r
11809                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
11810                                         dom.insertAfter(div, br);\r
11811                                         divYPos = dom.getPos(div).y;\r
11812                                         dom.remove(div);\r
11813 \r
11814                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
11815                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
11816                                                 ed.getWin().scrollTo(0, divYPos);\r
11817                                 };\r
11818 \r
11819                                 ed.onKeyPress.add(function(ed, e) {\r
11820                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
11821                                                 insertBr(ed);\r
11822                                                 Event.cancel(e);\r
11823                                         }\r
11824                                 });\r
11825                         }\r
11826 \r
11827                         // Padd empty inline elements within block elements\r
11828                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
11829                         ed.onPreProcess.add(function(ed, o) {\r
11830                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
11831                                         if (isEmpty(p)) {\r
11832                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
11833                                                         if (!n.hasChildNodes()) {\r
11834                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
11835                                                                 return FALSE; // Break the loop one padding is enough\r
11836                                                         }\r
11837                                                 });\r
11838                                         }\r
11839                                 });\r
11840                         });\r
11841 \r
11842                         // IE specific fixes\r
11843                         if (isIE) {\r
11844                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
11845                                 if (s.element != 'P') {\r
11846                                         ed.onKeyPress.add(function(ed, e) {\r
11847                                                 t.lastElm = selection.getNode().nodeName;\r
11848                                         });\r
11849 \r
11850                                         ed.onKeyUp.add(function(ed, e) {\r
11851                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
11852 \r
11853                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
11854                                                         n = dom.rename(n, s.element);\r
11855                                                         selection.select(n);\r
11856                                                         selection.collapse();\r
11857                                                         ed.nodeChanged();\r
11858                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
11859                                                         bl = dom.getParent(n, 'p');\r
11860 \r
11861                                                         if (bl) {\r
11862                                                                 dom.rename(bl, s.element);\r
11863                                                                 ed.nodeChanged();\r
11864                                                         }\r
11865                                                 }\r
11866                                         });\r
11867                                 }\r
11868                         }\r
11869                 },\r
11870 \r
11871                 find : function(n, t, s) {\r
11872                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
11873 \r
11874                         while (n = w.nextNode()) {\r
11875                                 c++;\r
11876 \r
11877                                 // Index by node\r
11878                                 if (t == 0 && n == s)\r
11879                                         return c;\r
11880 \r
11881                                 // Node by index\r
11882                                 if (t == 1 && c == s)\r
11883                                         return n;\r
11884                         }\r
11885 \r
11886                         return -1;\r
11887                 },\r
11888 \r
11889                 forceRoots : function(ed, e) {\r
11890                         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
11891                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
11892 \r
11893                         // Fix for bug #1863847\r
11894                         //if (e && e.keyCode == 13)\r
11895                         //      return TRUE;\r
11896 \r
11897                         // Wrap non blocks into blocks\r
11898                         for (i = nl.length - 1; i >= 0; i--) {\r
11899                                 nx = nl[i];\r
11900 \r
11901                                 // Ignore internal elements\r
11902                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
11903                                         bl = null;\r
11904                                         continue;\r
11905                                 }\r
11906 \r
11907                                 // Is text or non block element\r
11908                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11909                                         if (!bl) {\r
11910                                                 // Create new block but ignore whitespace\r
11911                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11912                                                         // Store selection\r
11913                                                         if (si == -2 && r) {\r
11914                                                                 if (!isIE) {\r
11915                                                                         // If selection is element then mark it\r
11916                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11917                                                                                 // Save the id of the selected element\r
11918                                                                                 eid = n.getAttribute("id");\r
11919                                                                                 n.setAttribute("id", "__mce");\r
11920                                                                         } else {\r
11921                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
11922                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11923                                                                                         so = r.startOffset;\r
11924                                                                                         eo = r.endOffset;\r
11925                                                                                         si = t.find(b, 0, r.startContainer);\r
11926                                                                                         ei = t.find(b, 0, r.endContainer);\r
11927                                                                                 }\r
11928                                                                         }\r
11929                                                                 } else {\r
11930                                                                         // Force control range into text range\r
11931                                                                         if (r.item) {\r
11932                                                                                 tr = d.body.createTextRange();\r
11933                                                                                 tr.moveToElementText(r.item(0));\r
11934                                                                                 r = tr;\r
11935                                                                         }\r
11936 \r
11937                                                                         tr = d.body.createTextRange();\r
11938                                                                         tr.moveToElementText(b);\r
11939                                                                         tr.collapse(1);\r
11940                                                                         bp = tr.move('character', c) * -1;\r
11941 \r
11942                                                                         tr = r.duplicate();\r
11943                                                                         tr.collapse(1);\r
11944                                                                         sp = tr.move('character', c) * -1;\r
11945 \r
11946                                                                         tr = r.duplicate();\r
11947                                                                         tr.collapse(0);\r
11948                                                                         le = (tr.move('character', c) * -1) - sp;\r
11949 \r
11950                                                                         si = sp - bp;\r
11951                                                                         ei = le;\r
11952                                                                 }\r
11953                                                         }\r
11954 \r
11955                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11956                                                         // See: http://support.microsoft.com/kb/829907\r
11957                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
11958                                                         nx.parentNode.replaceChild(bl, nx);\r
11959                                                         bl.appendChild(nx);\r
11960                                                 }\r
11961                                         } else {\r
11962                                                 if (bl.hasChildNodes())\r
11963                                                         bl.insertBefore(nx, bl.firstChild);\r
11964                                                 else\r
11965                                                         bl.appendChild(nx);\r
11966                                         }\r
11967                                 } else\r
11968                                         bl = null; // Time to create new block\r
11969                         }\r
11970 \r
11971                         // Restore selection\r
11972                         if (si != -2) {\r
11973                                 if (!isIE) {\r
11974                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
11975                                         r = d.createRange();\r
11976 \r
11977                                         // Select last location or generated block\r
11978                                         if (si != -1)\r
11979                                                 r.setStart(t.find(b, 1, si), so);\r
11980                                         else\r
11981                                                 r.setStart(bl, 0);\r
11982 \r
11983                                         // Select last location or generated block\r
11984                                         if (ei != -1)\r
11985                                                 r.setEnd(t.find(b, 1, ei), eo);\r
11986                                         else\r
11987                                                 r.setEnd(bl, 0);\r
11988 \r
11989                                         if (s) {\r
11990                                                 s.removeAllRanges();\r
11991                                                 s.addRange(r);\r
11992                                         }\r
11993                                 } else {\r
11994                                         try {\r
11995                                                 r = s.createRange();\r
11996                                                 r.moveToElementText(b);\r
11997                                                 r.collapse(1);\r
11998                                                 r.moveStart('character', si);\r
11999                                                 r.moveEnd('character', ei);\r
12000                                                 r.select();\r
12001                                         } catch (ex) {\r
12002                                                 // Ignore\r
12003                                         }\r
12004                                 }\r
12005                         } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
12006                                 // Restore the id of the selected element\r
12007                                 if (eid)\r
12008                                         n.setAttribute('id', eid);\r
12009                                 else\r
12010                                         n.removeAttribute('id');\r
12011 \r
12012                                 // Move caret before selected element\r
12013                                 r = d.createRange();\r
12014                                 r.setStartBefore(n);\r
12015                                 r.setEndBefore(n);\r
12016                                 se.setRng(r);\r
12017                         }\r
12018                 },\r
12019 \r
12020                 getParentBlock : function(n) {\r
12021                         var d = this.dom;\r
12022 \r
12023                         return d.getParent(n, d.isBlock);\r
12024                 },\r
12025 \r
12026                 insertPara : function(e) {\r
12027                         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
12028                         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
12029 \r
12030                         // If root blocks are forced then use Operas default behavior since it's really good\r
12031 // Removed due to bug: #1853816\r
12032 //                      if (se.forced_root_block && isOpera)\r
12033 //                              return TRUE;\r
12034 \r
12035                         // Setup before range\r
12036                         rb = d.createRange();\r
12037 \r
12038                         // If is before the first block element and in body, then move it into first block element\r
12039                         rb.setStart(s.anchorNode, s.anchorOffset);\r
12040                         rb.collapse(TRUE);\r
12041 \r
12042                         // Setup after range\r
12043                         ra = d.createRange();\r
12044 \r
12045                         // If is before the first block element and in body, then move it into first block element\r
12046                         ra.setStart(s.focusNode, s.focusOffset);\r
12047                         ra.collapse(TRUE);\r
12048 \r
12049                         // Setup start/end points\r
12050                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
12051                         sn = dir ? s.anchorNode : s.focusNode;\r
12052                         so = dir ? s.anchorOffset : s.focusOffset;\r
12053                         en = dir ? s.focusNode : s.anchorNode;\r
12054                         eo = dir ? s.focusOffset : s.anchorOffset;\r
12055 \r
12056                         // If selection is in empty table cell\r
12057                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
12058                                 if (sn.firstChild.nodeName == 'BR')\r
12059                                         dom.remove(sn.firstChild); // Remove BR\r
12060 \r
12061                                 // Create two new block elements\r
12062                                 if (sn.childNodes.length == 0) {\r
12063                                         ed.dom.add(sn, se.element, null, '<br />');\r
12064                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
12065                                 } else {\r
12066                                         n = sn.innerHTML;\r
12067                                         sn.innerHTML = '';\r
12068                                         ed.dom.add(sn, se.element, null, n);\r
12069                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
12070                                 }\r
12071 \r
12072                                 // Move caret into the last one\r
12073                                 r = d.createRange();\r
12074                                 r.selectNodeContents(aft);\r
12075                                 r.collapse(1);\r
12076                                 ed.selection.setRng(r);\r
12077 \r
12078                                 return FALSE;\r
12079                         }\r
12080 \r
12081                         // If the caret is in an invalid location in FF we need to move it into the first block\r
12082                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
12083                                 sn = en = sn.firstChild;\r
12084                                 so = eo = 0;\r
12085                                 rb = d.createRange();\r
12086                                 rb.setStart(sn, 0);\r
12087                                 ra = d.createRange();\r
12088                                 ra.setStart(en, 0);\r
12089                         }\r
12090 \r
12091                         // Never use body as start or end node\r
12092                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12093                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
12094                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12095                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
12096 \r
12097                         // Get start and end blocks\r
12098                         sb = t.getParentBlock(sn);\r
12099                         eb = t.getParentBlock(en);\r
12100                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
12101 \r
12102                         // Return inside list use default browser behavior\r
12103                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
12104                                 if (n.nodeName == 'LI')\r
12105                                         return splitList(ed.selection, t.dom, n);\r
12106 \r
12107                                 return TRUE;\r
12108                         }\r
12109 \r
12110                         // If caption or absolute layers then always generate new blocks within\r
12111                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12112                                 bn = se.element;\r
12113                                 sb = null;\r
12114                         }\r
12115 \r
12116                         // If caption or absolute layers then always generate new blocks within\r
12117                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12118                                 bn = se.element;\r
12119                                 eb = null;\r
12120                         }\r
12121 \r
12122                         // Use P instead\r
12123                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
12124                                 bn = se.element;\r
12125                                 sb = eb = null;\r
12126                         }\r
12127 \r
12128                         // Setup new before and after blocks\r
12129                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
12130                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
12131 \r
12132                         // Remove id from after clone\r
12133                         aft.removeAttribute('id');\r
12134 \r
12135                         // Is header and cursor is at the end, then force paragraph under\r
12136                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
12137                                 aft = ed.dom.create(se.element);\r
12138 \r
12139                         // Find start chop node\r
12140                         n = sc = sn;\r
12141                         do {\r
12142                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12143                                         break;\r
12144 \r
12145                                 sc = n;\r
12146                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
12147 \r
12148                         // Find end chop node\r
12149                         n = ec = en;\r
12150                         do {\r
12151                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12152                                         break;\r
12153 \r
12154                                 ec = n;\r
12155                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
12156 \r
12157                         // Place first chop part into before block element\r
12158                         if (sc.nodeName == bn)\r
12159                                 rb.setStart(sc, 0);\r
12160                         else\r
12161                                 rb.setStartBefore(sc);\r
12162 \r
12163                         rb.setEnd(sn, so);\r
12164                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12165 \r
12166                         // Place secnd chop part within new block element\r
12167                         try {\r
12168                                 ra.setEndAfter(ec);\r
12169                         } catch(ex) {\r
12170                                 //console.debug(s.focusNode, s.focusOffset);\r
12171                         }\r
12172 \r
12173                         ra.setStart(en, eo);\r
12174                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12175 \r
12176                         // Create range around everything\r
12177                         r = d.createRange();\r
12178                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
12179                                 r.setStartBefore(sc.parentNode);\r
12180                         } else {\r
12181                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
12182                                         r.setStartBefore(rb.startContainer);\r
12183                                 else\r
12184                                         r.setStart(rb.startContainer, rb.startOffset);\r
12185                         }\r
12186 \r
12187                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
12188                                 r.setEndAfter(ec.parentNode);\r
12189                         else\r
12190                                 r.setEnd(ra.endContainer, ra.endOffset);\r
12191 \r
12192                         // Delete and replace it with new block elements\r
12193                         r.deleteContents();\r
12194 \r
12195                         if (isOpera)\r
12196                                 ed.getWin().scrollTo(0, vp.y);\r
12197 \r
12198                         // Never wrap blocks in blocks\r
12199                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
12200                                 bef.innerHTML = bef.firstChild.innerHTML;\r
12201 \r
12202                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
12203                                 aft.innerHTML = aft.firstChild.innerHTML;\r
12204 \r
12205                         // Padd empty blocks\r
12206                         if (isEmpty(bef))\r
12207                                 bef.innerHTML = '<br />';\r
12208 \r
12209                         function appendStyles(e, en) {\r
12210                                 var nl = [], nn, n, i;\r
12211 \r
12212                                 e.innerHTML = '';\r
12213 \r
12214                                 // Make clones of style elements\r
12215                                 if (se.keep_styles) {\r
12216                                         n = en;\r
12217                                         do {\r
12218                                                 // We only want style specific elements\r
12219                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
12220                                                         nn = n.cloneNode(FALSE);\r
12221                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
12222                                                         nl.push(nn);\r
12223                                                 }\r
12224                                         } while (n = n.parentNode);\r
12225                                 }\r
12226 \r
12227                                 // Append style elements to aft\r
12228                                 if (nl.length > 0) {\r
12229                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
12230                                                 nn = nn.appendChild(nl[i]);\r
12231 \r
12232                                         // Padd most inner style element\r
12233                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12234                                         return nl[0]; // Move caret to most inner element\r
12235                                 } else\r
12236                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12237                         };\r
12238 \r
12239                         // Fill empty afterblook with current style\r
12240                         if (isEmpty(aft))\r
12241                                 car = appendStyles(aft, en);\r
12242 \r
12243                         // Opera needs this one backwards for older versions\r
12244                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
12245                                 r.insertNode(bef);\r
12246                                 r.insertNode(aft);\r
12247                         } else {\r
12248                                 r.insertNode(aft);\r
12249                                 r.insertNode(bef);\r
12250                         }\r
12251 \r
12252                         // Normalize\r
12253                         aft.normalize();\r
12254                         bef.normalize();\r
12255 \r
12256                         function first(n) {\r
12257                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
12258                         };\r
12259 \r
12260                         // Move cursor and scroll into view\r
12261                         r = d.createRange();\r
12262                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
12263                         r.collapse(1);\r
12264                         s.removeAllRanges();\r
12265                         s.addRange(r);\r
12266 \r
12267                         // 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
12268                         y = ed.dom.getPos(aft).y;\r
12269                         ch = aft.clientHeight;\r
12270 \r
12271                         // Is element within viewport\r
12272                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
12273                                 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
12274                                 //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
12275                         }\r
12276 \r
12277                         return FALSE;\r
12278                 },\r
12279 \r
12280                 backspaceDelete : function(e, bs) {\r
12281                         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
12282 \r
12283                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
12284                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
12285                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
12286 \r
12287                                 // Walk the dom backwards until we find a text node\r
12288                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
12289                                         if (n.nodeType == 3) {\r
12290                                                 r.setStart(n, n.nodeValue.length);\r
12291                                                 r.collapse(true);\r
12292                                                 se.setRng(r);\r
12293                                                 return;\r
12294                                         }\r
12295                                 }\r
12296                         }\r
12297 \r
12298                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
12299                         // This workaround removes the element by hand and moves the caret to the previous element\r
12300                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
12301                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
12302                                         // Find previous block element\r
12303                                         n = sc;\r
12304                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
12305 \r
12306                                         if (n) {\r
12307                                                 if (sc != b.firstChild) {\r
12308                                                         // Find last text node\r
12309                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
12310                                                         while (tn = w.nextNode())\r
12311                                                                 n = tn;\r
12312 \r
12313                                                         // Place caret at the end of last text node\r
12314                                                         r = ed.getDoc().createRange();\r
12315                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
12316                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
12317                                                         se.setRng(r);\r
12318 \r
12319                                                         // Remove the target container\r
12320                                                         ed.dom.remove(sc);\r
12321                                                 }\r
12322 \r
12323                                                 return Event.cancel(e);\r
12324                                         }\r
12325                                 }\r
12326                         }\r
12327                 }\r
12328         });\r
12329 })(tinymce);\r
12330 \r
12331 (function(tinymce) {\r
12332         // Shorten names\r
12333         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
12334 \r
12335         tinymce.create('tinymce.ControlManager', {\r
12336                 ControlManager : function(ed, s) {\r
12337                         var t = this, i;\r
12338 \r
12339                         s = s || {};\r
12340                         t.editor = ed;\r
12341                         t.controls = {};\r
12342                         t.onAdd = new tinymce.util.Dispatcher(t);\r
12343                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
12344                         t.prefix = s.prefix || ed.id + '_';\r
12345                         t._cls = {};\r
12346 \r
12347                         t.onPostRender.add(function() {\r
12348                                 each(t.controls, function(c) {\r
12349                                         c.postRender();\r
12350                                 });\r
12351                         });\r
12352                 },\r
12353 \r
12354                 get : function(id) {\r
12355                         return this.controls[this.prefix + id] || this.controls[id];\r
12356                 },\r
12357 \r
12358                 setActive : function(id, s) {\r
12359                         var c = null;\r
12360 \r
12361                         if (c = this.get(id))\r
12362                                 c.setActive(s);\r
12363 \r
12364                         return c;\r
12365                 },\r
12366 \r
12367                 setDisabled : function(id, s) {\r
12368                         var c = null;\r
12369 \r
12370                         if (c = this.get(id))\r
12371                                 c.setDisabled(s);\r
12372 \r
12373                         return c;\r
12374                 },\r
12375 \r
12376                 add : function(c) {\r
12377                         var t = this;\r
12378 \r
12379                         if (c) {\r
12380                                 t.controls[c.id] = c;\r
12381                                 t.onAdd.dispatch(c, t);\r
12382                         }\r
12383 \r
12384                         return c;\r
12385                 },\r
12386 \r
12387                 createControl : function(n) {\r
12388                         var c, t = this, ed = t.editor;\r
12389 \r
12390                         each(ed.plugins, function(p) {\r
12391                                 if (p.createControl) {\r
12392                                         c = p.createControl(n, t);\r
12393 \r
12394                                         if (c)\r
12395                                                 return false;\r
12396                                 }\r
12397                         });\r
12398 \r
12399                         switch (n) {\r
12400                                 case "|":\r
12401                                 case "separator":\r
12402                                         return t.createSeparator();\r
12403                         }\r
12404 \r
12405                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
12406                                 return t.createButton(n, c);\r
12407 \r
12408                         return t.add(c);\r
12409                 },\r
12410 \r
12411                 createDropMenu : function(id, s, cc) {\r
12412                         var t = this, ed = t.editor, c, bm, v, cls;\r
12413 \r
12414                         s = extend({\r
12415                                 'class' : 'mceDropDown',\r
12416                                 constrain : ed.settings.constrain_menus\r
12417                         }, s);\r
12418 \r
12419                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
12420                         if (v = ed.getParam('skin_variant'))\r
12421                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
12422 \r
12423                         id = t.prefix + id;\r
12424                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
12425                         c = t.controls[id] = new cls(id, s);\r
12426                         c.onAddItem.add(function(c, o) {\r
12427                                 var s = o.settings;\r
12428 \r
12429                                 s.title = ed.getLang(s.title, s.title);\r
12430 \r
12431                                 if (!s.onclick) {\r
12432                                         s.onclick = function(v) {\r
12433                                                 if (s.cmd)\r
12434                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12435                                         };\r
12436                                 }\r
12437                         });\r
12438 \r
12439                         ed.onRemove.add(function() {\r
12440                                 c.destroy();\r
12441                         });\r
12442 \r
12443                         // Fix for bug #1897785, #1898007\r
12444                         if (tinymce.isIE) {\r
12445                                 c.onShowMenu.add(function() {\r
12446                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12447                                         ed.focus();\r
12448 \r
12449                                         bm = ed.selection.getBookmark(1);\r
12450                                 });\r
12451 \r
12452                                 c.onHideMenu.add(function() {\r
12453                                         if (bm) {\r
12454                                                 ed.selection.moveToBookmark(bm);\r
12455                                                 bm = 0;\r
12456                                         }\r
12457                                 });\r
12458                         }\r
12459 \r
12460                         return t.add(c);\r
12461                 },\r
12462 \r
12463                 createListBox : function(id, s, cc) {\r
12464                         var t = this, ed = t.editor, cmd, c, cls;\r
12465 \r
12466                         if (t.get(id))\r
12467                                 return null;\r
12468 \r
12469                         s.title = ed.translate(s.title);\r
12470                         s.scope = s.scope || ed;\r
12471 \r
12472                         if (!s.onselect) {\r
12473                                 s.onselect = function(v) {\r
12474                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12475                                 };\r
12476                         }\r
12477 \r
12478                         s = extend({\r
12479                                 title : s.title,\r
12480                                 'class' : 'mce_' + id,\r
12481                                 scope : s.scope,\r
12482                                 control_manager : t\r
12483                         }, s);\r
12484 \r
12485                         id = t.prefix + id;\r
12486 \r
12487                         if (ed.settings.use_native_selects)\r
12488                                 c = new tinymce.ui.NativeListBox(id, s);\r
12489                         else {\r
12490                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
12491                                 c = new cls(id, s);\r
12492                         }\r
12493 \r
12494                         t.controls[id] = c;\r
12495 \r
12496                         // Fix focus problem in Safari\r
12497                         if (tinymce.isWebKit) {\r
12498                                 c.onPostRender.add(function(c, n) {\r
12499                                         // Store bookmark on mousedown\r
12500                                         Event.add(n, 'mousedown', function() {\r
12501                                                 ed.bookmark = ed.selection.getBookmark(1);\r
12502                                         });\r
12503 \r
12504                                         // Restore on focus, since it might be lost\r
12505                                         Event.add(n, 'focus', function() {\r
12506                                                 ed.selection.moveToBookmark(ed.bookmark);\r
12507                                                 ed.bookmark = null;\r
12508                                         });\r
12509                                 });\r
12510                         }\r
12511 \r
12512                         if (c.hideMenu)\r
12513                                 ed.onMouseDown.add(c.hideMenu, c);\r
12514 \r
12515                         return t.add(c);\r
12516                 },\r
12517 \r
12518                 createButton : function(id, s, cc) {\r
12519                         var t = this, ed = t.editor, o, c, cls;\r
12520 \r
12521                         if (t.get(id))\r
12522                                 return null;\r
12523 \r
12524                         s.title = ed.translate(s.title);\r
12525                         s.label = ed.translate(s.label);\r
12526                         s.scope = s.scope || ed;\r
12527 \r
12528                         if (!s.onclick && !s.menu_button) {\r
12529                                 s.onclick = function() {\r
12530                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12531                                 };\r
12532                         }\r
12533 \r
12534                         s = extend({\r
12535                                 title : s.title,\r
12536                                 'class' : 'mce_' + id,\r
12537                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
12538                                 scope : s.scope,\r
12539                                 control_manager : t\r
12540                         }, s);\r
12541 \r
12542                         id = t.prefix + id;\r
12543 \r
12544                         if (s.menu_button) {\r
12545                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
12546                                 c = new cls(id, s);\r
12547                                 ed.onMouseDown.add(c.hideMenu, c);\r
12548                         } else {\r
12549                                 cls = t._cls.button || tinymce.ui.Button;\r
12550                                 c = new cls(id, s);\r
12551                         }\r
12552 \r
12553                         return t.add(c);\r
12554                 },\r
12555 \r
12556                 createMenuButton : function(id, s, cc) {\r
12557                         s = s || {};\r
12558                         s.menu_button = 1;\r
12559 \r
12560                         return this.createButton(id, s, cc);\r
12561                 },\r
12562 \r
12563                 createSplitButton : function(id, s, cc) {\r
12564                         var t = this, ed = t.editor, cmd, c, cls;\r
12565 \r
12566                         if (t.get(id))\r
12567                                 return null;\r
12568 \r
12569                         s.title = ed.translate(s.title);\r
12570                         s.scope = s.scope || ed;\r
12571 \r
12572                         if (!s.onclick) {\r
12573                                 s.onclick = function(v) {\r
12574                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12575                                 };\r
12576                         }\r
12577 \r
12578                         if (!s.onselect) {\r
12579                                 s.onselect = function(v) {\r
12580                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12581                                 };\r
12582                         }\r
12583 \r
12584                         s = extend({\r
12585                                 title : s.title,\r
12586                                 'class' : 'mce_' + id,\r
12587                                 scope : s.scope,\r
12588                                 control_manager : t\r
12589                         }, s);\r
12590 \r
12591                         id = t.prefix + id;\r
12592                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
12593                         c = t.add(new cls(id, s));\r
12594                         ed.onMouseDown.add(c.hideMenu, c);\r
12595 \r
12596                         return c;\r
12597                 },\r
12598 \r
12599                 createColorSplitButton : function(id, s, cc) {\r
12600                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
12601 \r
12602                         if (t.get(id))\r
12603                                 return null;\r
12604 \r
12605                         s.title = ed.translate(s.title);\r
12606                         s.scope = s.scope || ed;\r
12607 \r
12608                         if (!s.onclick) {\r
12609                                 s.onclick = function(v) {\r
12610                                         if (tinymce.isIE)\r
12611                                                 bm = ed.selection.getBookmark(1);\r
12612 \r
12613                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12614                                 };\r
12615                         }\r
12616 \r
12617                         if (!s.onselect) {\r
12618                                 s.onselect = function(v) {\r
12619                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12620                                 };\r
12621                         }\r
12622 \r
12623                         s = extend({\r
12624                                 title : s.title,\r
12625                                 'class' : 'mce_' + id,\r
12626                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
12627                                 scope : s.scope,\r
12628                                 more_colors_title : ed.getLang('more_colors')\r
12629                         }, s);\r
12630 \r
12631                         id = t.prefix + id;\r
12632                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
12633                         c = new cls(id, s);\r
12634                         ed.onMouseDown.add(c.hideMenu, c);\r
12635 \r
12636                         // Remove the menu element when the editor is removed\r
12637                         ed.onRemove.add(function() {\r
12638                                 c.destroy();\r
12639                         });\r
12640 \r
12641                         // Fix for bug #1897785, #1898007\r
12642                         if (tinymce.isIE) {\r
12643                                 c.onShowMenu.add(function() {\r
12644                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12645                                         ed.focus();\r
12646                                         bm = ed.selection.getBookmark(1);\r
12647                                 });\r
12648 \r
12649                                 c.onHideMenu.add(function() {\r
12650                                         if (bm) {\r
12651                                                 ed.selection.moveToBookmark(bm);\r
12652                                                 bm = 0;\r
12653                                         }\r
12654                                 });\r
12655                         }\r
12656 \r
12657                         return t.add(c);\r
12658                 },\r
12659 \r
12660                 createToolbar : function(id, s, cc) {\r
12661                         var c, t = this, cls;\r
12662 \r
12663                         id = t.prefix + id;\r
12664                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
12665                         c = new cls(id, s);\r
12666 \r
12667                         if (t.get(id))\r
12668                                 return null;\r
12669 \r
12670                         return t.add(c);\r
12671                 },\r
12672 \r
12673                 createSeparator : function(cc) {\r
12674                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
12675 \r
12676                         return new cls();\r
12677                 },\r
12678 \r
12679                 setControlType : function(n, c) {\r
12680                         return this._cls[n.toLowerCase()] = c;\r
12681                 },\r
12682         \r
12683                 destroy : function() {\r
12684                         each(this.controls, function(c) {\r
12685                                 c.destroy();\r
12686                         });\r
12687 \r
12688                         this.controls = null;\r
12689                 }\r
12690         });\r
12691 })(tinymce);\r
12692 \r
12693 (function(tinymce) {\r
12694         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
12695 \r
12696         tinymce.create('tinymce.WindowManager', {\r
12697                 WindowManager : function(ed) {\r
12698                         var t = this;\r
12699 \r
12700                         t.editor = ed;\r
12701                         t.onOpen = new Dispatcher(t);\r
12702                         t.onClose = new Dispatcher(t);\r
12703                         t.params = {};\r
12704                         t.features = {};\r
12705                 },\r
12706 \r
12707                 open : function(s, p) {\r
12708                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
12709 \r
12710                         // Default some options\r
12711                         s = s || {};\r
12712                         p = p || {};\r
12713                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
12714                         sh = isOpera ? vp.h : screen.height;\r
12715                         s.name = s.name || 'mc_' + new Date().getTime();\r
12716                         s.width = parseInt(s.width || 320);\r
12717                         s.height = parseInt(s.height || 240);\r
12718                         s.resizable = true;\r
12719                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
12720                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
12721                         p.inline = false;\r
12722                         p.mce_width = s.width;\r
12723                         p.mce_height = s.height;\r
12724                         p.mce_auto_focus = s.auto_focus;\r
12725 \r
12726                         if (mo) {\r
12727                                 if (isIE) {\r
12728                                         s.center = true;\r
12729                                         s.help = false;\r
12730                                         s.dialogWidth = s.width + 'px';\r
12731                                         s.dialogHeight = s.height + 'px';\r
12732                                         s.scroll = s.scrollbars || false;\r
12733                                 }\r
12734                         }\r
12735 \r
12736                         // Build features string\r
12737                         each(s, function(v, k) {\r
12738                                 if (tinymce.is(v, 'boolean'))\r
12739                                         v = v ? 'yes' : 'no';\r
12740 \r
12741                                 if (!/^(name|url)$/.test(k)) {\r
12742                                         if (isIE && mo)\r
12743                                                 f += (f ? ';' : '') + k + ':' + v;\r
12744                                         else\r
12745                                                 f += (f ? ',' : '') + k + '=' + v;\r
12746                                 }\r
12747                         });\r
12748 \r
12749                         t.features = s;\r
12750                         t.params = p;\r
12751                         t.onOpen.dispatch(t, s, p);\r
12752 \r
12753                         u = s.url || s.file;\r
12754                         u = tinymce._addVer(u);\r
12755 \r
12756                         try {\r
12757                                 if (isIE && mo) {\r
12758                                         w = 1;\r
12759                                         window.showModalDialog(u, window, f);\r
12760                                 } else\r
12761                                         w = window.open(u, s.name, f);\r
12762                         } catch (ex) {\r
12763                                 // Ignore\r
12764                         }\r
12765 \r
12766                         if (!w)\r
12767                                 alert(t.editor.getLang('popup_blocked'));\r
12768                 },\r
12769 \r
12770                 close : function(w) {\r
12771                         w.close();\r
12772                         this.onClose.dispatch(this);\r
12773                 },\r
12774 \r
12775                 createInstance : function(cl, a, b, c, d, e) {\r
12776                         var f = tinymce.resolve(cl);\r
12777 \r
12778                         return new f(a, b, c, d, e);\r
12779                 },\r
12780 \r
12781                 confirm : function(t, cb, s, w) {\r
12782                         w = w || window;\r
12783 \r
12784                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
12785                 },\r
12786 \r
12787                 alert : function(tx, cb, s, w) {\r
12788                         var t = this;\r
12789 \r
12790                         w = w || window;\r
12791                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
12792 \r
12793                         if (cb)\r
12794                                 cb.call(s || t);\r
12795                 },\r
12796 \r
12797                 resizeBy : function(dw, dh, win) {\r
12798                         win.resizeBy(dw, dh);\r
12799                 },\r
12800 \r
12801                 // Internal functions\r
12802 \r
12803                 _decode : function(s) {\r
12804                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
12805                 }\r
12806         });\r
12807 }(tinymce));\r
12808 (function(tinymce) {\r
12809         function CommandManager() {\r
12810                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
12811 \r
12812                 function add(collection, cmd, func, scope) {\r
12813                         if (typeof(cmd) == 'string')\r
12814                                 cmd = [cmd];\r
12815 \r
12816                         tinymce.each(cmd, function(cmd) {\r
12817                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
12818                         });\r
12819                 };\r
12820 \r
12821                 tinymce.extend(this, {\r
12822                         add : function(cmd, func, scope) {\r
12823                                 add(execCommands, cmd, func, scope);\r
12824                         },\r
12825 \r
12826                         addQueryStateHandler : function(cmd, func, scope) {\r
12827                                 add(queryStateCommands, cmd, func, scope);\r
12828                         },\r
12829 \r
12830                         addQueryValueHandler : function(cmd, func, scope) {\r
12831                                 add(queryValueCommands, cmd, func, scope);\r
12832                         },\r
12833 \r
12834                         execCommand : function(scope, cmd, ui, value, args) {\r
12835                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
12836                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
12837                                                 return true;\r
12838                                 }\r
12839                         },\r
12840 \r
12841                         queryCommandValue : function() {\r
12842                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
12843                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12844                         },\r
12845 \r
12846                         queryCommandState : function() {\r
12847                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
12848                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12849                         }\r
12850                 });\r
12851         };\r
12852 \r
12853         tinymce.GlobalCommands = new CommandManager();\r
12854 })(tinymce);\r
12855 (function(tinymce) {\r
12856         tinymce.Formatter = function(ed) {\r
12857                 var formats = {},\r
12858                         each = tinymce.each,\r
12859                         dom = ed.dom,\r
12860                         selection = ed.selection,\r
12861                         TreeWalker = tinymce.dom.TreeWalker,\r
12862                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
12863                         isValid = ed.schema.isValid,\r
12864                         isBlock = dom.isBlock,\r
12865                         forcedRootBlock = ed.settings.forced_root_block,\r
12866                         nodeIndex = dom.nodeIndex,\r
12867                         INVISIBLE_CHAR = '\uFEFF',\r
12868                         MCE_ATTR_RE = /^(src|href|style)$/,\r
12869                         FALSE = false,\r
12870                         TRUE = true,\r
12871                         undefined,\r
12872                         pendingFormats = {apply : [], remove : []};\r
12873 \r
12874                 function isArray(obj) {\r
12875                         return obj instanceof Array;\r
12876                 };\r
12877 \r
12878                 function getParents(node, selector) {\r
12879                         return dom.getParents(node, selector, dom.getRoot());\r
12880                 };\r
12881 \r
12882                 function isCaretNode(node) {\r
12883                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
12884                 };\r
12885 \r
12886                 // Public functions\r
12887 \r
12888                 function get(name) {\r
12889                         return name ? formats[name] : formats;\r
12890                 };\r
12891 \r
12892                 function register(name, format) {\r
12893                         if (name) {\r
12894                                 if (typeof(name) !== 'string') {\r
12895                                         each(name, function(format, name) {\r
12896                                                 register(name, format);\r
12897                                         });\r
12898                                 } else {\r
12899                                         // Force format into array and add it to internal collection\r
12900                                         format = format.length ? format : [format];\r
12901 \r
12902                                         each(format, function(format) {\r
12903                                                 // Set deep to false by default on selector formats this to avoid removing\r
12904                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
12905                                                 if (format.deep === undefined)\r
12906                                                         format.deep = !format.selector;\r
12907 \r
12908                                                 // Default to true\r
12909                                                 if (format.split === undefined)\r
12910                                                         format.split = !format.selector || format.inline;\r
12911 \r
12912                                                 // Default to true\r
12913                                                 if (format.remove === undefined && format.selector && !format.inline)\r
12914                                                         format.remove = 'none';\r
12915 \r
12916                                                 // Mark format as a mixed format inline + block level\r
12917                                                 if (format.selector && format.inline) {\r
12918                                                         format.mixed = true;\r
12919                                                         format.block_expand = true;\r
12920                                                 }\r
12921 \r
12922                                                 // Split classes if needed\r
12923                                                 if (typeof(format.classes) === 'string')\r
12924                                                         format.classes = format.classes.split(/\s+/);\r
12925                                         });\r
12926 \r
12927                                         formats[name] = format;\r
12928                                 }\r
12929                         }\r
12930                 };\r
12931 \r
12932                 function apply(name, vars, node) {\r
12933                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
12934 \r
12935                         function moveStart(rng) {\r
12936                                 var container = rng.startContainer,\r
12937                                         offset = rng.startOffset,\r
12938                                         walker, node;\r
12939 \r
12940                                 // Move startContainer/startOffset in to a suitable node\r
12941                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
12942                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
12943 \r
12944                                         // Might fail if the offset is behind the last element in it's container\r
12945                                         if (container) {\r
12946                                                 walker = new TreeWalker(container, container.parentNode);\r
12947                                                 for (node = walker.current(); node; node = walker.next()) {\r
12948                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
12949                                                                 rng.setStart(node, 0);\r
12950                                                                 break;\r
12951                                                         }\r
12952                                                 }\r
12953                                         }\r
12954                                 }\r
12955 \r
12956                                 return rng;\r
12957                         };\r
12958 \r
12959                         function setElementFormat(elm, fmt) {\r
12960                                 fmt = fmt || format;\r
12961 \r
12962                                 if (elm) {\r
12963                                         each(fmt.styles, function(value, name) {\r
12964                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
12965                                         });\r
12966 \r
12967                                         each(fmt.attributes, function(value, name) {\r
12968                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
12969                                         });\r
12970 \r
12971                                         each(fmt.classes, function(value) {\r
12972                                                 value = replaceVars(value, vars);\r
12973 \r
12974                                                 if (!dom.hasClass(elm, value))\r
12975                                                         dom.addClass(elm, value);\r
12976                                         });\r
12977                                 }\r
12978                         };\r
12979 \r
12980                         function applyRngStyle(rng) {\r
12981                                 var newWrappers = [], wrapName, wrapElm;\r
12982 \r
12983                                 // Setup wrapper element\r
12984                                 wrapName = format.inline || format.block;\r
12985                                 wrapElm = dom.create(wrapName);\r
12986                                 setElementFormat(wrapElm);\r
12987 \r
12988                                 rangeUtils.walk(rng, function(nodes) {\r
12989                                         var currentWrapElm;\r
12990 \r
12991                                         function process(node) {\r
12992                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
12993 \r
12994                                                 // Stop wrapping on br elements\r
12995                                                 if (isEq(nodeName, 'br')) {\r
12996                                                         currentWrapElm = 0;\r
12997 \r
12998                                                         // Remove any br elements when we wrap things\r
12999                                                         if (format.block)\r
13000                                                                 dom.remove(node);\r
13001 \r
13002                                                         return;\r
13003                                                 }\r
13004 \r
13005                                                 // If node is wrapper type\r
13006                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
13007                                                         currentWrapElm = 0;\r
13008                                                         return;\r
13009                                                 }\r
13010 \r
13011                                                 // Can we rename the block\r
13012                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
13013                                                         node = dom.rename(node, wrapName);\r
13014                                                         setElementFormat(node);\r
13015                                                         newWrappers.push(node);\r
13016                                                         currentWrapElm = 0;\r
13017                                                         return;\r
13018                                                 }\r
13019 \r
13020                                                 // Handle selector patterns\r
13021                                                 if (format.selector) {\r
13022                                                         // Look for matching formats\r
13023                                                         each(formatList, function(format) {\r
13024                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
13025                                                                         setElementFormat(node, format);\r
13026                                                                         found = true;\r
13027                                                                 }\r
13028                                                         });\r
13029 \r
13030                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
13031                                                         if (!format.inline || found) {\r
13032                                                                 currentWrapElm = 0;\r
13033                                                                 return;\r
13034                                                         }\r
13035                                                 }\r
13036 \r
13037                                                 // Is it valid to wrap this item\r
13038                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
13039                                                         // Start wrapping\r
13040                                                         if (!currentWrapElm) {\r
13041                                                                 // Wrap the node\r
13042                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
13043                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
13044                                                                 newWrappers.push(currentWrapElm);\r
13045                                                         }\r
13046 \r
13047                                                         currentWrapElm.appendChild(node);\r
13048                                                 } else {\r
13049                                                         // Start a new wrapper for possible children\r
13050                                                         currentWrapElm = 0;\r
13051 \r
13052                                                         each(tinymce.grep(node.childNodes), process);\r
13053 \r
13054                                                         // End the last wrapper\r
13055                                                         currentWrapElm = 0;\r
13056                                                 }\r
13057                                         };\r
13058 \r
13059                                         // Process siblings from range\r
13060                                         each(nodes, process);\r
13061                                 });\r
13062 \r
13063                                 // Cleanup\r
13064                                 each(newWrappers, function(node) {\r
13065                                         var childCount;\r
13066 \r
13067                                         function getChildCount(node) {\r
13068                                                 var count = 0;\r
13069 \r
13070                                                 each(node.childNodes, function(node) {\r
13071                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
13072                                                                 count++;\r
13073                                                 });\r
13074 \r
13075                                                 return count;\r
13076                                         };\r
13077 \r
13078                                         function mergeStyles(node) {\r
13079                                                 var child, clone;\r
13080 \r
13081                                                 each(node.childNodes, function(node) {\r
13082                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
13083                                                                 child = node;\r
13084                                                                 return FALSE; // break loop\r
13085                                                         }\r
13086                                                 });\r
13087 \r
13088                                                 // If child was found and of the same type as the current node\r
13089                                                 if (child && matchName(child, format)) {\r
13090                                                         clone = child.cloneNode(FALSE);\r
13091                                                         setElementFormat(clone);\r
13092 \r
13093                                                         dom.replace(clone, node, TRUE);\r
13094                                                         dom.remove(child, 1);\r
13095                                                 }\r
13096 \r
13097                                                 return clone || node;\r
13098                                         };\r
13099 \r
13100                                         childCount = getChildCount(node);\r
13101 \r
13102                                         // Remove empty nodes\r
13103                                         if (childCount === 0) {\r
13104                                                 dom.remove(node, 1);\r
13105                                                 return;\r
13106                                         }\r
13107 \r
13108                                         if (format.inline || format.wrapper) {\r
13109                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
13110                                                 if (!format.exact && childCount === 1)\r
13111                                                         node = mergeStyles(node);\r
13112 \r
13113                                                 // Remove/merge children\r
13114                                                 each(formatList, function(format) {\r
13115                                                         // Merge all children of similar type will move styles from child to parent\r
13116                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
13117                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
13118                                                         each(dom.select(format.inline, node), function(child) {\r
13119                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
13120                                                         });\r
13121                                                 });\r
13122 \r
13123                                                 // Remove child if direct parent is of same type\r
13124                                                 if (matchNode(node.parentNode, name, vars)) {\r
13125                                                         dom.remove(node, 1);\r
13126                                                         node = 0;\r
13127                                                         return TRUE;\r
13128                                                 }\r
13129 \r
13130                                                 // Look for parent with similar style format\r
13131                                                 if (format.merge_with_parents) {\r
13132                                                         dom.getParent(node.parentNode, function(parent) {\r
13133                                                                 if (matchNode(parent, name, vars)) {\r
13134                                                                         dom.remove(node, 1);\r
13135                                                                         node = 0;\r
13136                                                                         return TRUE;\r
13137                                                                 }\r
13138                                                         });\r
13139                                                 }\r
13140 \r
13141                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
13142                                                 if (node) {\r
13143                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
13144                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
13145                                                 }\r
13146                                         }\r
13147                                 });\r
13148                         };\r
13149 \r
13150                         if (format) {\r
13151                                 if (node) {\r
13152                                         rng = dom.createRng();\r
13153 \r
13154                                         rng.setStartBefore(node);\r
13155                                         rng.setEndAfter(node);\r
13156 \r
13157                                         applyRngStyle(expandRng(rng, formatList));\r
13158                                 } else {\r
13159                                         if (!selection.isCollapsed() || !format.inline) {\r
13160                                                 // Apply formatting to selection\r
13161                                                 bookmark = selection.getBookmark();\r
13162                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
13163 \r
13164                                                 selection.moveToBookmark(bookmark);\r
13165                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
13166                                                 ed.nodeChanged();\r
13167                                         } else\r
13168                                                 performCaretAction('apply', name, vars);\r
13169                                 }\r
13170                         }\r
13171                 };\r
13172 \r
13173                 function remove(name, vars, node) {\r
13174                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
13175 \r
13176                         // Merges the styles for each node\r
13177                         function process(node) {\r
13178                                 var children, i, l;\r
13179 \r
13180                                 // Grab the children first since the nodelist might be changed\r
13181                                 children = tinymce.grep(node.childNodes);\r
13182 \r
13183                                 // Process current node\r
13184                                 for (i = 0, l = formatList.length; i < l; i++) {\r
13185                                         if (removeFormat(formatList[i], vars, node, node))\r
13186                                                 break;\r
13187                                 }\r
13188 \r
13189                                 // Process the children\r
13190                                 if (format.deep) {\r
13191                                         for (i = 0, l = children.length; i < l; i++)\r
13192                                                 process(children[i]);\r
13193                                 }\r
13194                         };\r
13195 \r
13196                         function findFormatRoot(container) {\r
13197                                 var formatRoot;\r
13198 \r
13199                                 // Find format root\r
13200                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
13201                                         var format;\r
13202 \r
13203                                         // Find format root element\r
13204                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
13205                                                 // Is the node matching the format we are looking for\r
13206                                                 format = matchNode(parent, name, vars);\r
13207                                                 if (format && format.split !== false)\r
13208                                                         formatRoot = parent;\r
13209                                         }\r
13210                                 });\r
13211 \r
13212                                 return formatRoot;\r
13213                         };\r
13214 \r
13215                         function wrapAndSplit(format_root, container, target, split) {\r
13216                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
13217 \r
13218                                 // Format root found then clone formats and split it\r
13219                                 if (format_root) {\r
13220                                         formatRootParent = format_root.parentNode;\r
13221 \r
13222                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
13223                                                 clone = parent.cloneNode(FALSE);\r
13224 \r
13225                                                 for (i = 0; i < formatList.length; i++) {\r
13226                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
13227                                                                 clone = 0;\r
13228                                                                 break;\r
13229                                                         }\r
13230                                                 }\r
13231 \r
13232                                                 // Build wrapper node\r
13233                                                 if (clone) {\r
13234                                                         if (lastClone)\r
13235                                                                 clone.appendChild(lastClone);\r
13236 \r
13237                                                         if (!firstClone)\r
13238                                                                 firstClone = clone;\r
13239 \r
13240                                                         lastClone = clone;\r
13241                                                 }\r
13242                                         }\r
13243 \r
13244                                         // Never split block elements if the format is mixed\r
13245                                         if (split && (!format.mixed || !isBlock(format_root)))\r
13246                                                 container = dom.split(format_root, container);\r
13247 \r
13248                                         // Wrap container in cloned formats\r
13249                                         if (lastClone) {\r
13250                                                 target.parentNode.insertBefore(lastClone, target);\r
13251                                                 firstClone.appendChild(target);\r
13252                                         }\r
13253                                 }\r
13254 \r
13255                                 return container;\r
13256                         };\r
13257 \r
13258                         function splitToFormatRoot(container) {\r
13259                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
13260                         };\r
13261 \r
13262                         function unwrap(start) {\r
13263                                 var node = dom.get(start ? '_start' : '_end'),\r
13264                                         out = node[start ? 'firstChild' : 'lastChild'];\r
13265 \r
13266                                 // If the end is placed within the start the result will be removed\r
13267                                 // So this checks if the out node is a bookmark node if it is it\r
13268                                 // checks for another more suitable node\r
13269                                 if (isBookmarkNode(out))\r
13270                                         out = out[start ? 'firstChild' : 'lastChild'];\r
13271 \r
13272                                 dom.remove(node, true);\r
13273 \r
13274                                 return out;\r
13275                         };\r
13276 \r
13277                         function removeRngStyle(rng) {\r
13278                                 var startContainer, endContainer;\r
13279 \r
13280                                 rng = expandRng(rng, formatList, TRUE);\r
13281 \r
13282                                 if (format.split) {\r
13283                                         startContainer = getContainer(rng, TRUE);\r
13284                                         endContainer = getContainer(rng);\r
13285 \r
13286                                         if (startContainer != endContainer) {\r
13287                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
13288                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
13289                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
13290 \r
13291                                                 // Split start/end\r
13292                                                 splitToFormatRoot(startContainer);\r
13293                                                 splitToFormatRoot(endContainer);\r
13294 \r
13295                                                 // Unwrap start/end to get real elements again\r
13296                                                 startContainer = unwrap(TRUE);\r
13297                                                 endContainer = unwrap();\r
13298                                         } else\r
13299                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
13300 \r
13301                                         // Update range positions since they might have changed after the split operations\r
13302                                         rng.startContainer = startContainer.parentNode;\r
13303                                         rng.startOffset = nodeIndex(startContainer);\r
13304                                         rng.endContainer = endContainer.parentNode;\r
13305                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
13306                                 }\r
13307 \r
13308                                 // Remove items between start/end\r
13309                                 rangeUtils.walk(rng, function(nodes) {\r
13310                                         each(nodes, function(node) {\r
13311                                                 process(node);\r
13312                                         });\r
13313                                 });\r
13314                         };\r
13315 \r
13316                         // Handle node\r
13317                         if (node) {\r
13318                                 rng = dom.createRng();\r
13319                                 rng.setStartBefore(node);\r
13320                                 rng.setEndAfter(node);\r
13321                                 removeRngStyle(rng);\r
13322                                 return;\r
13323                         }\r
13324 \r
13325                         if (!selection.isCollapsed() || !format.inline) {\r
13326                                 bookmark = selection.getBookmark();\r
13327                                 removeRngStyle(selection.getRng(TRUE));\r
13328                                 selection.moveToBookmark(bookmark);\r
13329                                 ed.nodeChanged();\r
13330                         } else\r
13331                                 performCaretAction('remove', name, vars);\r
13332                 };\r
13333 \r
13334                 function toggle(name, vars, node) {\r
13335                         if (match(name, vars, node))\r
13336                                 remove(name, vars, node);\r
13337                         else\r
13338                                 apply(name, vars, node);\r
13339                 };\r
13340 \r
13341                 function matchNode(node, name, vars, similar) {\r
13342                         var formatList = get(name), format, i, classes;\r
13343 \r
13344                         function matchItems(node, format, item_name) {\r
13345                                 var key, value, items = format[item_name], i;\r
13346 \r
13347                                 // Check all items\r
13348                                 if (items) {\r
13349                                         // Non indexed object\r
13350                                         if (items.length === undefined) {\r
13351                                                 for (key in items) {\r
13352                                                         if (items.hasOwnProperty(key)) {\r
13353                                                                 if (item_name === 'attributes')\r
13354                                                                         value = dom.getAttrib(node, key);\r
13355                                                                 else\r
13356                                                                         value = getStyle(node, key);\r
13357 \r
13358                                                                 if (similar && !value && !format.exact)\r
13359                                                                         return;\r
13360 \r
13361                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
13362                                                                         return;\r
13363                                                         }\r
13364                                                 }\r
13365                                         } else {\r
13366                                                 // Only one match needed for indexed arrays\r
13367                                                 for (i = 0; i < items.length; i++) {\r
13368                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
13369                                                                 return format;\r
13370                                                 }\r
13371                                         }\r
13372                                 }\r
13373 \r
13374                                 return format;\r
13375                         };\r
13376 \r
13377                         if (formatList && node) {\r
13378                                 // Check each format in list\r
13379                                 for (i = 0; i < formatList.length; i++) {\r
13380                                         format = formatList[i];\r
13381 \r
13382                                         // Name name, attributes, styles and classes\r
13383                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
13384                                                 // Match classes\r
13385                                                 if (classes = format.classes) {\r
13386                                                         for (i = 0; i < classes.length; i++) {\r
13387                                                                 if (!dom.hasClass(node, classes[i]))\r
13388                                                                         return;\r
13389                                                         }\r
13390                                                 }\r
13391 \r
13392                                                 return format;\r
13393                                         }\r
13394                                 }\r
13395                         }\r
13396                 };\r
13397 \r
13398                 function match(name, vars, node) {\r
13399                         var startNode, i;\r
13400 \r
13401                         function matchParents(node) {\r
13402                                 // Find first node with similar format settings\r
13403                                 node = dom.getParent(node, function(node) {\r
13404                                         return !!matchNode(node, name, vars, true);\r
13405                                 });\r
13406 \r
13407                                 // Do an exact check on the similar format element\r
13408                                 return matchNode(node, name, vars);\r
13409                         };\r
13410 \r
13411                         // Check specified node\r
13412                         if (node)\r
13413                                 return matchParents(node);\r
13414 \r
13415                         // Check pending formats\r
13416                         if (selection.isCollapsed()) {\r
13417                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13418                                         if (pendingFormats.apply[i].name == name)\r
13419                                                 return true;\r
13420                                 }\r
13421 \r
13422                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13423                                         if (pendingFormats.remove[i].name == name)\r
13424                                                 return false;\r
13425                                 }\r
13426 \r
13427                                 return matchParents(selection.getNode());\r
13428                         }\r
13429 \r
13430                         // Check selected node\r
13431                         node = selection.getNode();\r
13432                         if (matchParents(node))\r
13433                                 return TRUE;\r
13434 \r
13435                         // Check start node if it's different\r
13436                         startNode = selection.getStart();\r
13437                         if (startNode != node) {\r
13438                                 if (matchParents(startNode))\r
13439                                         return TRUE;\r
13440                         }\r
13441 \r
13442                         return FALSE;\r
13443                 };\r
13444 \r
13445                 function matchAll(names, vars) {\r
13446                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
13447 \r
13448                         // If the selection is collapsed then check pending formats\r
13449                         if (selection.isCollapsed()) {\r
13450                                 for (ni = 0; ni < names.length; ni++) {\r
13451                                         // If the name is to be removed, then stop it from being added\r
13452                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13453                                                 name = names[ni];\r
13454 \r
13455                                                 if (pendingFormats.remove[i].name == name) {\r
13456                                                         checkedMap[name] = true;\r
13457                                                         break;\r
13458                                                 }\r
13459                                         }\r
13460                                 }\r
13461 \r
13462                                 // If the format is to be applied\r
13463                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13464                                         for (ni = 0; ni < names.length; ni++) {\r
13465                                                 name = names[ni];\r
13466 \r
13467                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
13468                                                         checkedMap[name] = true;\r
13469                                                         matchedFormatNames.push(name);\r
13470                                                 }\r
13471                                         }\r
13472                                 }\r
13473                         }\r
13474 \r
13475                         // Check start of selection for formats\r
13476                         startElement = selection.getStart();\r
13477                         dom.getParent(startElement, function(node) {\r
13478                                 var i, name;\r
13479 \r
13480                                 for (i = 0; i < names.length; i++) {\r
13481                                         name = names[i];\r
13482 \r
13483                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
13484                                                 checkedMap[name] = true;\r
13485                                                 matchedFormatNames.push(name);\r
13486                                         }\r
13487                                 }\r
13488                         });\r
13489 \r
13490                         return matchedFormatNames;\r
13491                 };\r
13492 \r
13493                 function canApply(name) {\r
13494                         var formatList = get(name), startNode, parents, i, x, selector;\r
13495 \r
13496                         if (formatList) {\r
13497                                 startNode = selection.getStart();\r
13498                                 parents = getParents(startNode);\r
13499 \r
13500                                 for (x = formatList.length - 1; x >= 0; x--) {\r
13501                                         selector = formatList[x].selector;\r
13502 \r
13503                                         // Format is not selector based, then always return TRUE\r
13504                                         if (!selector)\r
13505                                                 return TRUE;\r
13506 \r
13507                                         for (i = parents.length - 1; i >= 0; i--) {\r
13508                                                 if (dom.is(parents[i], selector))\r
13509                                                         return TRUE;\r
13510                                         }\r
13511                                 }\r
13512                         }\r
13513 \r
13514                         return FALSE;\r
13515                 };\r
13516 \r
13517                 // Expose to public\r
13518                 tinymce.extend(this, {\r
13519                         get : get,\r
13520                         register : register,\r
13521                         apply : apply,\r
13522                         remove : remove,\r
13523                         toggle : toggle,\r
13524                         match : match,\r
13525                         matchAll : matchAll,\r
13526                         matchNode : matchNode,\r
13527                         canApply : canApply\r
13528                 });\r
13529 \r
13530                 // Private functions\r
13531 \r
13532                 function matchName(node, format) {\r
13533                         // Check for inline match\r
13534                         if (isEq(node, format.inline))\r
13535                                 return TRUE;\r
13536 \r
13537                         // Check for block match\r
13538                         if (isEq(node, format.block))\r
13539                                 return TRUE;\r
13540 \r
13541                         // Check for selector match\r
13542                         if (format.selector)\r
13543                                 return dom.is(node, format.selector);\r
13544                 };\r
13545 \r
13546                 function isEq(str1, str2) {\r
13547                         str1 = str1 || '';\r
13548                         str2 = str2 || '';\r
13549 \r
13550                         str1 = '' + (str1.nodeName || str1);\r
13551                         str2 = '' + (str2.nodeName || str2);\r
13552 \r
13553                         return str1.toLowerCase() == str2.toLowerCase();\r
13554                 };\r
13555 \r
13556                 function getStyle(node, name) {\r
13557                         var styleVal = dom.getStyle(node, name);\r
13558 \r
13559                         // Force the format to hex\r
13560                         if (name == 'color' || name == 'backgroundColor')\r
13561                                 styleVal = dom.toHex(styleVal);\r
13562 \r
13563                         // Opera will return bold as 700\r
13564                         if (name == 'fontWeight' && styleVal == 700)\r
13565                                 styleVal = 'bold';\r
13566 \r
13567                         return '' + styleVal;\r
13568                 };\r
13569 \r
13570                 function replaceVars(value, vars) {\r
13571                         if (typeof(value) != "string")\r
13572                                 value = value(vars);\r
13573                         else if (vars) {\r
13574                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
13575                                         return vars[name] || str;\r
13576                                 });\r
13577                         }\r
13578 \r
13579                         return value;\r
13580                 };\r
13581 \r
13582                 function isWhiteSpaceNode(node) {\r
13583                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
13584                 };\r
13585 \r
13586                 function wrap(node, name, attrs) {\r
13587                         var wrapper = dom.create(name, attrs);\r
13588 \r
13589                         node.parentNode.insertBefore(wrapper, node);\r
13590                         wrapper.appendChild(node);\r
13591 \r
13592                         return wrapper;\r
13593                 };\r
13594 \r
13595                 function expandRng(rng, format, remove) {\r
13596                         var startContainer = rng.startContainer,\r
13597                                 startOffset = rng.startOffset,\r
13598                                 endContainer = rng.endContainer,\r
13599                                 endOffset = rng.endOffset, sibling, lastIdx;\r
13600 \r
13601                         // This function walks up the tree if there is no siblings before/after the node\r
13602                         function findParentContainer(container, child_name, sibling_name, root) {\r
13603                                 var parent, child;\r
13604 \r
13605                                 root = root || dom.getRoot();\r
13606 \r
13607                                 for (;;) {\r
13608                                         // Check if we can move up are we at root level or body level\r
13609                                         parent = container.parentNode;\r
13610 \r
13611                                         // Stop expanding on block elements or root depending on format\r
13612                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
13613                                                 return container;\r
13614 \r
13615                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
13616                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13617                                                         return container;\r
13618 \r
13619                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13620                                                         return container;\r
13621                                         }\r
13622 \r
13623                                         container = container.parentNode;\r
13624                                 }\r
13625 \r
13626                                 return container;\r
13627                         };\r
13628 \r
13629                         // If index based start position then resolve it\r
13630                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
13631                                 lastIdx = startContainer.childNodes.length - 1;\r
13632                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
13633 \r
13634                                 if (startContainer.nodeType == 3)\r
13635                                         startOffset = 0;\r
13636                         }\r
13637 \r
13638                         // If index based end position then resolve it\r
13639                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
13640                                 lastIdx = endContainer.childNodes.length - 1;\r
13641                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
13642 \r
13643                                 if (endContainer.nodeType == 3)\r
13644                                         endOffset = endContainer.nodeValue.length;\r
13645                         }\r
13646 \r
13647                         // Exclude bookmark nodes if possible\r
13648                         if (isBookmarkNode(startContainer.parentNode))\r
13649                                 startContainer = startContainer.parentNode;\r
13650 \r
13651                         if (isBookmarkNode(startContainer))\r
13652                                 startContainer = startContainer.nextSibling || startContainer;\r
13653 \r
13654                         if (isBookmarkNode(endContainer.parentNode))\r
13655                                 endContainer = endContainer.parentNode;\r
13656 \r
13657                         if (isBookmarkNode(endContainer))\r
13658                                 endContainer = endContainer.previousSibling || endContainer;\r
13659 \r
13660                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
13661                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
13662                         // This will reduce the number of wrapper elements that needs to be created\r
13663                         // Move start point up the tree\r
13664                         if (format[0].inline || format[0].block_expand) {\r
13665                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13666                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13667                         }\r
13668 \r
13669                         // Expand start/end container to matching selector\r
13670                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
13671                                 function findSelectorEndPoint(container, sibling_name) {\r
13672                                         var parents, i, y;\r
13673 \r
13674                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
13675                                                 container = container[sibling_name];\r
13676 \r
13677                                         parents = getParents(container);\r
13678                                         for (i = 0; i < parents.length; i++) {\r
13679                                                 for (y = 0; y < format.length; y++) {\r
13680                                                         if (dom.is(parents[i], format[y].selector))\r
13681                                                                 return parents[i];\r
13682                                                 }\r
13683                                         }\r
13684 \r
13685                                         return container;\r
13686                                 };\r
13687 \r
13688                                 // Find new startContainer/endContainer if there is better one\r
13689                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
13690                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
13691                         }\r
13692 \r
13693                         // Expand start/end container to matching block element or text node\r
13694                         if (format[0].block || format[0].selector) {\r
13695                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
13696                                         var node;\r
13697 \r
13698                                         // Expand to block of similar type\r
13699                                         if (!format[0].wrapper)\r
13700                                                 node = dom.getParent(container, format[0].block);\r
13701 \r
13702                                         // Expand to first wrappable block element or any block element\r
13703                                         if (!node)\r
13704                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
13705 \r
13706                                         // Exclude inner lists from wrapping\r
13707                                         if (node && format[0].wrapper)\r
13708                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
13709 \r
13710                                         // Didn't find a block element look for first/last wrappable element\r
13711                                         if (!node) {\r
13712                                                 node = container;\r
13713 \r
13714                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
13715                                                         node = node[sibling_name];\r
13716 \r
13717                                                         // Break on BR but include it will be removed later on\r
13718                                                         // we can't remove it now since we need to check if it can be wrapped\r
13719                                                         if (isEq(node, 'br'))\r
13720                                                                 break;\r
13721                                                 }\r
13722                                         }\r
13723 \r
13724                                         return node || container;\r
13725                                 };\r
13726 \r
13727                                 // Find new startContainer/endContainer if there is better one\r
13728                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
13729                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
13730 \r
13731                                 // Non block element then try to expand up the leaf\r
13732                                 if (format[0].block) {\r
13733                                         if (!isBlock(startContainer))\r
13734                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13735 \r
13736                                         if (!isBlock(endContainer))\r
13737                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13738                                 }\r
13739                         }\r
13740 \r
13741                         // Setup index for startContainer\r
13742                         if (startContainer.nodeType == 1) {\r
13743                                 startOffset = nodeIndex(startContainer);\r
13744                                 startContainer = startContainer.parentNode;\r
13745                         }\r
13746 \r
13747                         // Setup index for endContainer\r
13748                         if (endContainer.nodeType == 1) {\r
13749                                 endOffset = nodeIndex(endContainer) + 1;\r
13750                                 endContainer = endContainer.parentNode;\r
13751                         }\r
13752 \r
13753                         // Return new range like object\r
13754                         return {\r
13755                                 startContainer : startContainer,\r
13756                                 startOffset : startOffset,\r
13757                                 endContainer : endContainer,\r
13758                                 endOffset : endOffset\r
13759                         };\r
13760                 }\r
13761 \r
13762                 function removeFormat(format, vars, node, compare_node) {\r
13763                         var i, attrs, stylesModified;\r
13764 \r
13765                         // Check if node matches format\r
13766                         if (!matchName(node, format))\r
13767                                 return FALSE;\r
13768 \r
13769                         // Should we compare with format attribs and styles\r
13770                         if (format.remove != 'all') {\r
13771                                 // Remove styles\r
13772                                 each(format.styles, function(value, name) {\r
13773                                         value = replaceVars(value, vars);\r
13774 \r
13775                                         // Indexed array\r
13776                                         if (typeof(name) === 'number') {\r
13777                                                 name = value;\r
13778                                                 compare_node = 0;\r
13779                                         }\r
13780 \r
13781                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
13782                                                 dom.setStyle(node, name, '');\r
13783 \r
13784                                         stylesModified = 1;\r
13785                                 });\r
13786 \r
13787                                 // Remove style attribute if it's empty\r
13788                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
13789                                         node.removeAttribute('style');\r
13790                                         node.removeAttribute('_mce_style');\r
13791                                 }\r
13792 \r
13793                                 // Remove attributes\r
13794                                 each(format.attributes, function(value, name) {\r
13795                                         var valueOut;\r
13796 \r
13797                                         value = replaceVars(value, vars);\r
13798 \r
13799                                         // Indexed array\r
13800                                         if (typeof(name) === 'number') {\r
13801                                                 name = value;\r
13802                                                 compare_node = 0;\r
13803                                         }\r
13804 \r
13805                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
13806                                                 // Keep internal classes\r
13807                                                 if (name == 'class') {\r
13808                                                         value = dom.getAttrib(node, name);\r
13809                                                         if (value) {\r
13810                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
13811                                                                 valueOut = '';\r
13812                                                                 each(value.split(/\s+/), function(cls) {\r
13813                                                                         if (/mce\w+/.test(cls))\r
13814                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
13815                                                                 });\r
13816 \r
13817                                                                 // We got some internal classes left\r
13818                                                                 if (valueOut) {\r
13819                                                                         dom.setAttrib(node, name, valueOut);\r
13820                                                                         return;\r
13821                                                                 }\r
13822                                                         }\r
13823                                                 }\r
13824 \r
13825                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
13826                                                 if (name == "class")\r
13827                                                         node.removeAttribute('className');\r
13828 \r
13829                                                 // Remove mce prefixed attributes\r
13830                                                 if (MCE_ATTR_RE.test(name))\r
13831                                                         node.removeAttribute('_mce_' + name);\r
13832 \r
13833                                                 node.removeAttribute(name);\r
13834                                         }\r
13835                                 });\r
13836 \r
13837                                 // Remove classes\r
13838                                 each(format.classes, function(value) {\r
13839                                         value = replaceVars(value, vars);\r
13840 \r
13841                                         if (!compare_node || dom.hasClass(compare_node, value))\r
13842                                                 dom.removeClass(node, value);\r
13843                                 });\r
13844 \r
13845                                 // Check for non internal attributes\r
13846                                 attrs = dom.getAttribs(node);\r
13847                                 for (i = 0; i < attrs.length; i++) {\r
13848                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
13849                                                 return FALSE;\r
13850                                 }\r
13851                         }\r
13852 \r
13853                         // Remove the inline child if it's empty for example <b> or <span>\r
13854                         if (format.remove != 'none') {\r
13855                                 removeNode(node, format);\r
13856                                 return TRUE;\r
13857                         }\r
13858                 };\r
13859 \r
13860                 function removeNode(node, format) {\r
13861                         var parentNode = node.parentNode, rootBlockElm;\r
13862 \r
13863                         if (format.block) {\r
13864                                 if (!forcedRootBlock) {\r
13865                                         function find(node, next, inc) {\r
13866                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
13867 \r
13868                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
13869                                         };\r
13870 \r
13871                                         // Append BR elements if needed before we remove the block\r
13872                                         if (isBlock(node) && !isBlock(parentNode)) {\r
13873                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
13874                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
13875 \r
13876                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
13877                                                         node.appendChild(dom.create('br'));\r
13878                                         }\r
13879                                 } else {\r
13880                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
13881                                         if (parentNode == dom.getRoot()) {\r
13882                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
13883                                                         each(tinymce.grep(node.childNodes), function(node) {\r
13884                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
13885                                                                         if (!rootBlockElm)\r
13886                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
13887                                                                         else\r
13888                                                                                 rootBlockElm.appendChild(node);\r
13889                                                                 } else\r
13890                                                                         rootBlockElm = 0;\r
13891                                                         });\r
13892                                                 }\r
13893                                         }\r
13894                                 }\r
13895                         }\r
13896 \r
13897                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
13898                         if (format.selector && format.inline && !isEq(format.inline, node))\r
13899                                 return;\r
13900 \r
13901                         dom.remove(node, 1);\r
13902                 };\r
13903 \r
13904                 function getNonWhiteSpaceSibling(node, next, inc) {\r
13905                         if (node) {\r
13906                                 next = next ? 'nextSibling' : 'previousSibling';\r
13907 \r
13908                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
13909                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
13910                                                 return node;\r
13911                                 }\r
13912                         }\r
13913                 };\r
13914 \r
13915                 function isBookmarkNode(node) {\r
13916                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
13917                 };\r
13918 \r
13919                 function mergeSiblings(prev, next) {\r
13920                         var marker, sibling, tmpSibling;\r
13921 \r
13922                         function compareElements(node1, node2) {\r
13923                                 // Not the same name\r
13924                                 if (node1.nodeName != node2.nodeName)\r
13925                                         return FALSE;\r
13926 \r
13927                                 function getAttribs(node) {\r
13928                                         var attribs = {};\r
13929 \r
13930                                         each(dom.getAttribs(node), function(attr) {\r
13931                                                 var name = attr.nodeName.toLowerCase();\r
13932 \r
13933                                                 // Don't compare internal attributes or style\r
13934                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
13935                                                         attribs[name] = dom.getAttrib(node, name);\r
13936                                         });\r
13937 \r
13938                                         return attribs;\r
13939                                 };\r
13940 \r
13941                                 function compareObjects(obj1, obj2) {\r
13942                                         var value, name;\r
13943 \r
13944                                         for (name in obj1) {\r
13945                                                 // Obj1 has item obj2 doesn't have\r
13946                                                 if (obj1.hasOwnProperty(name)) {\r
13947                                                         value = obj2[name];\r
13948 \r
13949                                                         // Obj2 doesn't have obj1 item\r
13950                                                         if (value === undefined)\r
13951                                                                 return FALSE;\r
13952 \r
13953                                                         // Obj2 item has a different value\r
13954                                                         if (obj1[name] != value)\r
13955                                                                 return FALSE;\r
13956 \r
13957                                                         // Delete similar value\r
13958                                                         delete obj2[name];\r
13959                                                 }\r
13960                                         }\r
13961 \r
13962                                         // Check if obj 2 has something obj 1 doesn't have\r
13963                                         for (name in obj2) {\r
13964                                                 // Obj2 has item obj1 doesn't have\r
13965                                                 if (obj2.hasOwnProperty(name))\r
13966                                                         return FALSE;\r
13967                                         }\r
13968 \r
13969                                         return TRUE;\r
13970                                 };\r
13971 \r
13972                                 // Attribs are not the same\r
13973                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
13974                                         return FALSE;\r
13975 \r
13976                                 // Styles are not the same\r
13977                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
13978                                         return FALSE;\r
13979 \r
13980                                 return TRUE;\r
13981                         };\r
13982 \r
13983                         // Check if next/prev exists and that they are elements\r
13984                         if (prev && next) {\r
13985                                 function findElementSibling(node, sibling_name) {\r
13986                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
13987                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13988                                                         return node;\r
13989 \r
13990                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13991                                                         return sibling;\r
13992                                         }\r
13993 \r
13994                                         return node;\r
13995                                 };\r
13996 \r
13997                                 // If previous sibling is empty then jump over it\r
13998                                 prev = findElementSibling(prev, 'previousSibling');\r
13999                                 next = findElementSibling(next, 'nextSibling');\r
14000 \r
14001                                 // Compare next and previous nodes\r
14002                                 if (compareElements(prev, next)) {\r
14003                                         // Append nodes between\r
14004                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
14005                                                 tmpSibling = sibling;\r
14006                                                 sibling = sibling.nextSibling;\r
14007                                                 prev.appendChild(tmpSibling);\r
14008                                         }\r
14009 \r
14010                                         // Remove next node\r
14011                                         dom.remove(next);\r
14012 \r
14013                                         // Move children into prev node\r
14014                                         each(tinymce.grep(next.childNodes), function(node) {\r
14015                                                 prev.appendChild(node);\r
14016                                         });\r
14017 \r
14018                                         return prev;\r
14019                                 }\r
14020                         }\r
14021 \r
14022                         return next;\r
14023                 };\r
14024 \r
14025                 function isTextBlock(name) {\r
14026                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
14027                 };\r
14028 \r
14029                 function getContainer(rng, start) {\r
14030                         var container, offset, lastIdx;\r
14031 \r
14032                         container = rng[start ? 'startContainer' : 'endContainer'];\r
14033                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
14034 \r
14035                         if (container.nodeType == 1) {\r
14036                                 lastIdx = container.childNodes.length - 1;\r
14037 \r
14038                                 if (!start && offset)\r
14039                                         offset--;\r
14040 \r
14041                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
14042                         }\r
14043 \r
14044                         return container;\r
14045                 };\r
14046 \r
14047                 function performCaretAction(type, name, vars) {\r
14048                         var i, currentPendingFormats = pendingFormats[type],\r
14049                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
14050 \r
14051                         function hasPending() {\r
14052                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
14053                         };\r
14054 \r
14055                         function resetPending() {\r
14056                                 pendingFormats.apply = [];\r
14057                                 pendingFormats.remove = [];\r
14058                         };\r
14059 \r
14060                         function perform(caret_node) {\r
14061                                 // Apply pending formats\r
14062                                 each(pendingFormats.apply.reverse(), function(item) {\r
14063                                         apply(item.name, item.vars, caret_node);\r
14064                                 });\r
14065 \r
14066                                 // Remove pending formats\r
14067                                 each(pendingFormats.remove.reverse(), function(item) {\r
14068                                         remove(item.name, item.vars, caret_node);\r
14069                                 });\r
14070 \r
14071                                 dom.remove(caret_node, 1);\r
14072                                 resetPending();\r
14073                         };\r
14074 \r
14075                         // Check if it already exists then ignore it\r
14076                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
14077                                 if (currentPendingFormats[i].name == name)\r
14078                                         return;\r
14079                         }\r
14080 \r
14081                         currentPendingFormats.push({name : name, vars : vars});\r
14082 \r
14083                         // Check if it's in the other type, then remove it\r
14084                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
14085                                 if (otherPendingFormats[i].name == name)\r
14086                                         otherPendingFormats.splice(i, 1);\r
14087                         }\r
14088 \r
14089                         // Pending apply or remove formats\r
14090                         if (hasPending()) {\r
14091                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
14092                                 pendingFormats.lastRng = selection.getRng();\r
14093 \r
14094                                 // IE will convert the current word\r
14095                                 each(dom.select('font,span'), function(node) {\r
14096                                         var bookmark;\r
14097 \r
14098                                         if (isCaretNode(node)) {\r
14099                                                 bookmark = selection.getBookmark();\r
14100                                                 perform(node);\r
14101                                                 selection.moveToBookmark(bookmark);\r
14102                                                 ed.nodeChanged();\r
14103                                         }\r
14104                                 });\r
14105 \r
14106                                 // Only register listeners once if we need to\r
14107                                 if (!pendingFormats.isListening && hasPending()) {\r
14108                                         pendingFormats.isListening = true;\r
14109 \r
14110                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
14111                                                 ed[event].addToTop(function(ed, e) {\r
14112                                                         // Do we have pending formats and is the selection moved has moved\r
14113                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
14114                                                                 each(dom.select('font,span'), function(node) {\r
14115                                                                         var textNode, rng;\r
14116 \r
14117                                                                         // Look for marker\r
14118                                                                         if (isCaretNode(node)) {\r
14119                                                                                 textNode = node.firstChild;\r
14120 \r
14121                                                                                 if (textNode) {\r
14122                                                                                         perform(node);\r
14123 \r
14124                                                                                         rng = dom.createRng();\r
14125                                                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
14126                                                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
14127                                                                                         selection.setRng(rng);\r
14128                                                                                         ed.nodeChanged();\r
14129                                                                                 } else\r
14130                                                                                         dom.remove(node);\r
14131                                                                         }\r
14132                                                                 });\r
14133 \r
14134                                                                 // Always unbind and clear pending styles on keyup\r
14135                                                                 if (e.type == 'keyup' || e.type == 'mouseup')\r
14136                                                                         resetPending();\r
14137                                                         }\r
14138                                                 });\r
14139                                         });\r
14140                                 }\r
14141                         }\r
14142                 };\r
14143         };\r
14144 })(tinymce);\r
14145 \r
14146 tinymce.onAddEditor.add(function(tinymce, ed) {\r
14147         var filters, fontSizes, dom, settings = ed.settings;\r
14148 \r
14149         if (settings.inline_styles) {\r
14150                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
14151 \r
14152                 function replaceWithSpan(node, styles) {\r
14153                         dom.replace(dom.create('span', {\r
14154                                 style : styles\r
14155                         }), node, 1);\r
14156                 };\r
14157 \r
14158                 filters = {\r
14159                         font : function(dom, node) {\r
14160                                 replaceWithSpan(node, {\r
14161                                         backgroundColor : node.style.backgroundColor,\r
14162                                         color : node.color,\r
14163                                         fontFamily : node.face,\r
14164                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
14165                                 });\r
14166                         },\r
14167 \r
14168                         u : function(dom, node) {\r
14169                                 replaceWithSpan(node, {\r
14170                                         textDecoration : 'underline'\r
14171                                 });\r
14172                         },\r
14173 \r
14174                         strike : function(dom, node) {\r
14175                                 replaceWithSpan(node, {\r
14176                                         textDecoration : 'line-through'\r
14177                                 });\r
14178                         }\r
14179                 };\r
14180 \r
14181                 function convert(editor, params) {\r
14182                         dom = editor.dom;\r
14183 \r
14184                         if (settings.convert_fonts_to_spans) {\r
14185                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
14186                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
14187                                 });\r
14188                         }\r
14189                 };\r
14190 \r
14191                 ed.onPreProcess.add(convert);\r
14192 \r
14193                 ed.onInit.add(function() {\r
14194                         ed.selection.onSetContent.add(convert);\r
14195                 });\r
14196         }\r
14197 });\r
14198 \r