]> git.mxchange.org Git - friendica.git/blob - library/tinymce/jscripts/tiny_mce/tiny_mce_src.js
updated tinymce to 3.5.11
[friendica.git] / library / tinymce / jscripts / tiny_mce / tiny_mce_src.js
1 // FILE IS GENERATED BY COMBINING THE SOURCES IN THE "classes" DIRECTORY SO DON'T MODIFY THIS FILE DIRECTLY\r
2 (function(win) {\r
3         var whiteSpaceRe = /^\s*|\s*$/g,\r
4                 undef, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';\r
5 \r
6         var tinymce = {\r
7                 majorVersion : '3',\r
8 \r
9                 minorVersion : '5.11',\r
10 \r
11                 releaseDate : '2014-05-08',\r
12 \r
13                 _init : function() {\r
14                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
15 \r
16                         t.isIE11 = ua.indexOf('Trident/') != -1 && (ua.indexOf('rv:') != -1 || na.appName.indexOf('Netscape') != -1);\r
17 \r
18                         t.isOpera = win.opera && opera.buildNumber;\r
19 \r
20                         t.isWebKit = /WebKit/.test(ua);\r
21 \r
22                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName) || t.isIE11;\r
23 \r
24                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
25 \r
26                         t.isIE7 = t.isIE && /MSIE [7]/.test(ua);\r
27 \r
28                         t.isIE8 = t.isIE && /MSIE [8]/.test(ua);\r
29 \r
30                         t.isIE9 = t.isIE && /MSIE [9]/.test(ua);\r
31 \r
32                         t.isGecko = !t.isWebKit && !t.isIE11 && /Gecko/.test(ua);\r
33 \r
34                         t.isMac = ua.indexOf('Mac') != -1;\r
35 \r
36                         t.isAir = /adobeair/i.test(ua);\r
37 \r
38                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
39                         \r
40                         t.isIOS5 = t.isIDevice && ua.match(/AppleWebKit\/(\d*)/)[1]>=534;\r
41 \r
42                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
43                         if (win.tinyMCEPreInit) {\r
44                                 t.suffix = tinyMCEPreInit.suffix;\r
45                                 t.baseURL = tinyMCEPreInit.base;\r
46                                 t.query = tinyMCEPreInit.query;\r
47                                 return;\r
48                         }\r
49 \r
50                         // Get suffix and base\r
51                         t.suffix = '';\r
52 \r
53                         // If base element found, add that infront of baseURL\r
54                         nl = d.getElementsByTagName('base');\r
55                         for (i=0; i<nl.length; i++) {\r
56                                 v = nl[i].href;\r
57                                 if (v) {\r
58                                         // Host only value like http://site.com or http://site.com:8008\r
59                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
60                                                 v += '/';\r
61 \r
62                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
63                                 }\r
64                         }\r
65 \r
66                         function getBase(n) {\r
67                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {\r
68                                         if (/_(src|dev)\.js/g.test(n.src))\r
69                                                 t.suffix = '_src';\r
70 \r
71                                         if ((p = n.src.indexOf('?')) != -1)\r
72                                                 t.query = n.src.substring(p + 1);\r
73 \r
74                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
75 \r
76                                         // If path to script is relative and a base href was found add that one infront\r
77                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
78                                         // so this logic will basically only be executed on older IE versions\r
79                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
80                                                 t.baseURL = base + t.baseURL;\r
81 \r
82                                         return t.baseURL;\r
83                                 }\r
84 \r
85                                 return null;\r
86                         };\r
87 \r
88                         // Check document\r
89                         nl = d.getElementsByTagName('script');\r
90                         for (i=0; i<nl.length; i++) {\r
91                                 if (getBase(nl[i]))\r
92                                         return;\r
93                         }\r
94 \r
95                         // Check head\r
96                         n = d.getElementsByTagName('head')[0];\r
97                         if (n) {\r
98                                 nl = n.getElementsByTagName('script');\r
99                                 for (i=0; i<nl.length; i++) {\r
100                                         if (getBase(nl[i]))\r
101                                                 return;\r
102                                 }\r
103                         }\r
104 \r
105                         return;\r
106                 },\r
107 \r
108                 is : function(o, t) {\r
109                         if (!t)\r
110                                 return o !== undef;\r
111 \r
112                         if (t == 'array' && tinymce.isArray(o))\r
113                                 return true;\r
114 \r
115                         return typeof(o) == t;\r
116                 },\r
117 \r
118                 isArray: Array.isArray || function(obj) {\r
119                         return Object.prototype.toString.call(obj) === "[object Array]";\r
120                 },\r
121 \r
122                 makeMap : function(items, delim, map) {\r
123                         var i;\r
124 \r
125                         items = items || [];\r
126                         delim = delim || ',';\r
127 \r
128                         if (typeof(items) == "string")\r
129                                 items = items.split(delim);\r
130 \r
131                         map = map || {};\r
132 \r
133                         i = items.length;\r
134                         while (i--)\r
135                                 map[items[i]] = {};\r
136 \r
137                         return map;\r
138                 },\r
139 \r
140                 each : function(o, cb, s) {\r
141                         var n, l;\r
142 \r
143                         if (!o)\r
144                                 return 0;\r
145 \r
146                         s = s || o;\r
147 \r
148                         if (o.length !== undef) {\r
149                                 // Indexed arrays, needed for Safari\r
150                                 for (n=0, l = o.length; n < l; n++) {\r
151                                         if (cb.call(s, o[n], n, o) === false)\r
152                                                 return 0;\r
153                                 }\r
154                         } else {\r
155                                 // Hashtables\r
156                                 for (n in o) {\r
157                                         if (o.hasOwnProperty(n)) {\r
158                                                 if (cb.call(s, o[n], n, o) === false)\r
159                                                         return 0;\r
160                                         }\r
161                                 }\r
162                         }\r
163 \r
164                         return 1;\r
165                 },\r
166 \r
167 \r
168                 map : function(a, f) {\r
169                         var o = [];\r
170 \r
171                         tinymce.each(a, function(v) {\r
172                                 o.push(f(v));\r
173                         });\r
174 \r
175                         return o;\r
176                 },\r
177 \r
178                 grep : function(a, f) {\r
179                         var o = [];\r
180 \r
181                         tinymce.each(a, function(v) {\r
182                                 if (!f || f(v))\r
183                                         o.push(v);\r
184                         });\r
185 \r
186                         return o;\r
187                 },\r
188 \r
189                 inArray : function(a, v) {\r
190                         var i, l;\r
191 \r
192                         if (a) {\r
193                                 for (i = 0, l = a.length; i < l; i++) {\r
194                                         if (a[i] === v)\r
195                                                 return i;\r
196                                 }\r
197                         }\r
198 \r
199                         return -1;\r
200                 },\r
201 \r
202                 extend : function(obj, ext) {\r
203                         var i, l, name, args = arguments, value;\r
204 \r
205                         for (i = 1, l = args.length; i < l; i++) {\r
206                                 ext = args[i];\r
207                                 for (name in ext) {\r
208                                         if (ext.hasOwnProperty(name)) {\r
209                                                 value = ext[name];\r
210 \r
211                                                 if (value !== undef) {\r
212                                                         obj[name] = value;\r
213                                                 }\r
214                                         }\r
215                                 }\r
216                         }\r
217 \r
218                         return obj;\r
219                 },\r
220 \r
221 \r
222                 trim : function(s) {\r
223                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
224                 },\r
225 \r
226                 create : function(s, p, root) {\r
227                         var t = this, sp, ns, cn, scn, c, de = 0;\r
228 \r
229                         // Parse : <prefix> <class>:<super class>\r
230                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
231                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
232 \r
233                         // Create namespace for new class\r
234                         ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);\r
235 \r
236                         // Class already exists\r
237                         if (ns[cn])\r
238                                 return;\r
239 \r
240                         // Make pure static class\r
241                         if (s[2] == 'static') {\r
242                                 ns[cn] = p;\r
243 \r
244                                 if (this.onCreate)\r
245                                         this.onCreate(s[2], s[3], ns[cn]);\r
246 \r
247                                 return;\r
248                         }\r
249 \r
250                         // Create default constructor\r
251                         if (!p[cn]) {\r
252                                 p[cn] = function() {};\r
253                                 de = 1;\r
254                         }\r
255 \r
256                         // Add constructor and methods\r
257                         ns[cn] = p[cn];\r
258                         t.extend(ns[cn].prototype, p);\r
259 \r
260                         // Extend\r
261                         if (s[5]) {\r
262                                 sp = t.resolve(s[5]).prototype;\r
263                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
264 \r
265                                 // Extend constructor\r
266                                 c = ns[cn];\r
267                                 if (de) {\r
268                                         // Add passthrough constructor\r
269                                         ns[cn] = function() {\r
270                                                 return sp[scn].apply(this, arguments);\r
271                                         };\r
272                                 } else {\r
273                                         // Add inherit constructor\r
274                                         ns[cn] = function() {\r
275                                                 this.parent = sp[scn];\r
276                                                 return c.apply(this, arguments);\r
277                                         };\r
278                                 }\r
279                                 ns[cn].prototype[cn] = ns[cn];\r
280 \r
281                                 // Add super methods\r
282                                 t.each(sp, function(f, n) {\r
283                                         ns[cn].prototype[n] = sp[n];\r
284                                 });\r
285 \r
286                                 // Add overridden methods\r
287                                 t.each(p, function(f, n) {\r
288                                         // Extend methods if needed\r
289                                         if (sp[n]) {\r
290                                                 ns[cn].prototype[n] = function() {\r
291                                                         this.parent = sp[n];\r
292                                                         return f.apply(this, arguments);\r
293                                                 };\r
294                                         } else {\r
295                                                 if (n != cn)\r
296                                                         ns[cn].prototype[n] = f;\r
297                                         }\r
298                                 });\r
299                         }\r
300 \r
301                         // Add static methods\r
302                         t.each(p['static'], function(f, n) {\r
303                                 ns[cn][n] = f;\r
304                         });\r
305 \r
306                         if (this.onCreate)\r
307                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
308                 },\r
309 \r
310                 walk : function(o, f, n, s) {\r
311                         s = s || this;\r
312 \r
313                         if (o) {\r
314                                 if (n)\r
315                                         o = o[n];\r
316 \r
317                                 tinymce.each(o, function(o, i) {\r
318                                         if (f.call(s, o, i, n) === false)\r
319                                                 return false;\r
320 \r
321                                         tinymce.walk(o, f, n, s);\r
322                                 });\r
323                         }\r
324                 },\r
325 \r
326                 createNS : function(n, o) {\r
327                         var i, v;\r
328 \r
329                         o = o || win;\r
330 \r
331                         n = n.split('.');\r
332                         for (i=0; i<n.length; i++) {\r
333                                 v = n[i];\r
334 \r
335                                 if (!o[v])\r
336                                         o[v] = {};\r
337 \r
338                                 o = o[v];\r
339                         }\r
340 \r
341                         return o;\r
342                 },\r
343 \r
344                 resolve : function(n, o) {\r
345                         var i, l;\r
346 \r
347                         o = o || win;\r
348 \r
349                         n = n.split('.');\r
350                         for (i = 0, l = n.length; i < l; i++) {\r
351                                 o = o[n[i]];\r
352 \r
353                                 if (!o)\r
354                                         break;\r
355                         }\r
356 \r
357                         return o;\r
358                 },\r
359 \r
360                 addUnload : function(f, s) {\r
361                         var t = this, unload;\r
362 \r
363                         unload = function() {\r
364                                 var li = t.unloads, o, n;\r
365 \r
366                                 if (li) {\r
367                                         // Call unload handlers\r
368                                         for (n in li) {\r
369                                                 o = li[n];\r
370 \r
371                                                 if (o && o.func)\r
372                                                         o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
373                                         }\r
374 \r
375                                         // Detach unload function\r
376                                         if (win.detachEvent) {\r
377                                                 win.detachEvent('onbeforeunload', fakeUnload);\r
378                                                 win.detachEvent('onunload', unload);\r
379                                         } else if (win.removeEventListener)\r
380                                                 win.removeEventListener('unload', unload, false);\r
381 \r
382                                         // Destroy references\r
383                                         t.unloads = o = li = w = unload = 0;\r
384 \r
385                                         // Run garbarge collector on IE\r
386                                         if (win.CollectGarbage)\r
387                                                 CollectGarbage();\r
388                                 }\r
389                         };\r
390 \r
391                         function fakeUnload() {\r
392                                 var d = document;\r
393 \r
394                                 function stop() {\r
395                                         // Prevent memory leak\r
396                                         d.detachEvent('onstop', stop);\r
397 \r
398                                         // Call unload handler\r
399                                         if (unload)\r
400                                                 unload();\r
401 \r
402                                         d = 0;\r
403                                 };\r
404 \r
405                                 // Is there things still loading, then do some magic\r
406                                 if (d.readyState == 'interactive') {\r
407                                         // Fire unload when the currently loading page is stopped\r
408                                         if (d)\r
409                                                 d.attachEvent('onstop', stop);\r
410 \r
411                                         // Remove onstop listener after a while to prevent the unload function\r
412                                         // to execute if the user presses cancel in an onbeforeunload\r
413                                         // confirm dialog and then presses the browser stop button\r
414                                         win.setTimeout(function() {\r
415                                                 if (d)\r
416                                                         d.detachEvent('onstop', stop);\r
417                                         }, 0);\r
418                                 }\r
419                         };\r
420 \r
421                         f = {func : f, scope : s || this};\r
422 \r
423                         if (!t.unloads) {\r
424                                 // Attach unload handler\r
425                                 if (win.attachEvent) {\r
426                                         win.attachEvent('onunload', unload);\r
427                                         win.attachEvent('onbeforeunload', fakeUnload);\r
428                                 } else if (win.addEventListener)\r
429                                         win.addEventListener('unload', unload, false);\r
430 \r
431                                 // Setup initial unload handler array\r
432                                 t.unloads = [f];\r
433                         } else\r
434                                 t.unloads.push(f);\r
435 \r
436                         return f;\r
437                 },\r
438 \r
439                 removeUnload : function(f) {\r
440                         var u = this.unloads, r = null;\r
441 \r
442                         tinymce.each(u, function(o, i) {\r
443                                 if (o && o.func == f) {\r
444                                         u.splice(i, 1);\r
445                                         r = f;\r
446                                         return false;\r
447                                 }\r
448                         });\r
449 \r
450                         return r;\r
451                 },\r
452 \r
453                 explode : function(s, d) {\r
454                         if (!s || tinymce.is(s, 'array')) {\r
455                                 return s;\r
456                         }\r
457 \r
458                         return tinymce.map(s.split(d || ','), tinymce.trim);\r
459                 },\r
460 \r
461                 _addVer : function(u) {\r
462                         var v;\r
463 \r
464                         if (!this.query)\r
465                                 return u;\r
466 \r
467                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
468 \r
469                         if (u.indexOf('#') == -1)\r
470                                 return u + v;\r
471 \r
472                         return u.replace('#', v + '#');\r
473                 },\r
474 \r
475                 // Fix function for IE 9 where regexps isn't working correctly\r
476                 // Todo: remove me once MS fixes the bug\r
477                 _replace : function(find, replace, str) {\r
478                         // On IE9 we have to fake $x replacement\r
479                         if (isRegExpBroken) {\r
480                                 return str.replace(find, function() {\r
481                                         var val = replace, args = arguments, i;\r
482 \r
483                                         for (i = 0; i < args.length - 2; i++) {\r
484                                                 if (args[i] === undef) {\r
485                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');\r
486                                                 } else {\r
487                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);\r
488                                                 }\r
489                                         }\r
490 \r
491                                         return val;\r
492                                 });\r
493                         }\r
494 \r
495                         return str.replace(find, replace);\r
496                 }\r
497 \r
498                 };\r
499 \r
500         // Initialize the API\r
501         tinymce._init();\r
502 \r
503         // Expose tinymce namespace to the global namespace (window)\r
504         win.tinymce = win.tinyMCE = tinymce;\r
505 \r
506         // Describe the different namespaces\r
507 \r
508         })(window);\r
509 \r
510 \r
511 \r
512 tinymce.create('tinymce.util.Dispatcher', {\r
513         scope : null,\r
514         listeners : null,\r
515         inDispatch: false,\r
516 \r
517         Dispatcher : function(scope) {\r
518                 this.scope = scope || this;\r
519                 this.listeners = [];\r
520         },\r
521 \r
522         add : function(callback, scope) {\r
523                 this.listeners.push({cb : callback, scope : scope || this.scope});\r
524 \r
525                 return callback;\r
526         },\r
527 \r
528         addToTop : function(callback, scope) {\r
529                 var self = this, listener = {cb : callback, scope : scope || self.scope};\r
530 \r
531                 // Create new listeners if addToTop is executed in a dispatch loop\r
532                 if (self.inDispatch) {\r
533                         self.listeners = [listener].concat(self.listeners);\r
534                 } else {\r
535                         self.listeners.unshift(listener);\r
536                 }\r
537 \r
538                 return callback;\r
539         },\r
540 \r
541         remove : function(callback) {\r
542                 var listeners = this.listeners, output = null;\r
543 \r
544                 tinymce.each(listeners, function(listener, i) {\r
545                         if (callback == listener.cb) {\r
546                                 output = listener;\r
547                                 listeners.splice(i, 1);\r
548                                 return false;\r
549                         }\r
550                 });\r
551 \r
552                 return output;\r
553         },\r
554 \r
555         dispatch : function() {\r
556                 var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener;\r
557 \r
558                 self.inDispatch = true;\r
559                 \r
560                 // Needs to be a real loop since the listener count might change while looping\r
561                 // And this is also more efficient\r
562                 for (i = 0; i < listeners.length; i++) {\r
563                         listener = listeners[i];\r
564                         returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]);\r
565 \r
566                         if (returnValue === false)\r
567                                 break;\r
568                 }\r
569 \r
570                 self.inDispatch = false;\r
571 \r
572                 return returnValue;\r
573         }\r
574 \r
575         });\r
576 \r
577 (function() {\r
578         var each = tinymce.each;\r
579 \r
580         tinymce.create('tinymce.util.URI', {\r
581                 URI : function(u, s) {\r
582                         var t = this, o, a, b, base_url;\r
583 \r
584                         // Trim whitespace\r
585                         u = tinymce.trim(u);\r
586 \r
587                         // Default settings\r
588                         s = t.settings = s || {};\r
589 \r
590                         // Strange app protocol that isn't http/https or local anchor\r
591                         // For example: mailto,skype,tel etc.\r
592                         if (/^([\w\-]+):([^\/]{2})/i.test(u) || /^\s*#/.test(u)) {\r
593                                 t.source = u;\r
594                                 return;\r
595                         }\r
596 \r
597                         // Absolute path with no host, fake host and protocol\r
598                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
599                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
600 \r
601                         // Relative path http:// or protocol relative //path\r
602                         if (!/^[\w\-]*:?\/\//.test(u)) {\r
603                                 base_url = s.base_uri ? s.base_uri.path : new tinymce.util.URI(location.href).directory;\r
604                                 u = ((s.base_uri && s.base_uri.protocol) || 'http') + '://mce_host' + t.toAbsPath(base_url, u);\r
605                         }\r
606 \r
607                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
608                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
609                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
610                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
611                                 var s = u[i];\r
612 \r
613                                 // Zope 3 workaround, they use @@something\r
614                                 if (s)\r
615                                         s = s.replace(/\(mce_at\)/g, '@@');\r
616 \r
617                                 t[v] = s;\r
618                         });\r
619 \r
620                         b = s.base_uri;\r
621                         if (b) {\r
622                                 if (!t.protocol)\r
623                                         t.protocol = b.protocol;\r
624 \r
625                                 if (!t.userInfo)\r
626                                         t.userInfo = b.userInfo;\r
627 \r
628                                 if (!t.port && t.host === 'mce_host')\r
629                                         t.port = b.port;\r
630 \r
631                                 if (!t.host || t.host === 'mce_host')\r
632                                         t.host = b.host;\r
633 \r
634                                 t.source = '';\r
635                         }\r
636 \r
637                         //t.path = t.path || '/';\r
638                 },\r
639 \r
640                 setPath : function(p) {\r
641                         var t = this;\r
642 \r
643                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
644 \r
645                         // Update path parts\r
646                         t.path = p[0];\r
647                         t.directory = p[1];\r
648                         t.file = p[2];\r
649 \r
650                         // Rebuild source\r
651                         t.source = '';\r
652                         t.getURI();\r
653                 },\r
654 \r
655                 toRelative : function(u) {\r
656                         var t = this, o;\r
657 \r
658                         if (u === "./")\r
659                                 return u;\r
660 \r
661                         u = new tinymce.util.URI(u, {base_uri : t});\r
662 \r
663                         // Not on same domain/port or protocol\r
664                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
665                                 return u.getURI();\r
666 \r
667                         var tu = t.getURI(), uu = u.getURI();\r
668                         \r
669                         // Allow usage of the base_uri when relative_urls = true\r
670                         if(tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu))\r
671                                 return tu;\r
672 \r
673                         o = t.toRelPath(t.path, u.path);\r
674 \r
675                         // Add query\r
676                         if (u.query)\r
677                                 o += '?' + u.query;\r
678 \r
679                         // Add anchor\r
680                         if (u.anchor)\r
681                                 o += '#' + u.anchor;\r
682 \r
683                         return o;\r
684                 },\r
685         \r
686                 toAbsolute : function(u, nh) {\r
687                         u = new tinymce.util.URI(u, {base_uri : this});\r
688 \r
689                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
690                 },\r
691 \r
692                 toRelPath : function(base, path) {\r
693                         var items, bp = 0, out = '', i, l;\r
694 \r
695                         // Split the paths\r
696                         base = base.substring(0, base.lastIndexOf('/'));\r
697                         base = base.split('/');\r
698                         items = path.split('/');\r
699 \r
700                         if (base.length >= items.length) {\r
701                                 for (i = 0, l = base.length; i < l; i++) {\r
702                                         if (i >= items.length || base[i] != items[i]) {\r
703                                                 bp = i + 1;\r
704                                                 break;\r
705                                         }\r
706                                 }\r
707                         }\r
708 \r
709                         if (base.length < items.length) {\r
710                                 for (i = 0, l = items.length; i < l; i++) {\r
711                                         if (i >= base.length || base[i] != items[i]) {\r
712                                                 bp = i + 1;\r
713                                                 break;\r
714                                         }\r
715                                 }\r
716                         }\r
717 \r
718                         if (bp === 1)\r
719                                 return path;\r
720 \r
721                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
722                                 out += "../";\r
723 \r
724                         for (i = bp - 1, l = items.length; i < l; i++) {\r
725                                 if (i != bp - 1)\r
726                                         out += "/" + items[i];\r
727                                 else\r
728                                         out += items[i];\r
729                         }\r
730 \r
731                         return out;\r
732                 },\r
733 \r
734                 toAbsPath : function(base, path) {\r
735                         var i, nb = 0, o = [], tr, outPath;\r
736 \r
737                         // Split paths\r
738                         tr = /\/$/.test(path) ? '/' : '';\r
739                         base = base.split('/');\r
740                         path = path.split('/');\r
741 \r
742                         // Remove empty chunks\r
743                         each(base, function(k) {\r
744                                 if (k)\r
745                                         o.push(k);\r
746                         });\r
747 \r
748                         base = o;\r
749 \r
750                         // Merge relURLParts chunks\r
751                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
752                                 // Ignore empty or .\r
753                                 if (path[i].length === 0 || path[i] === ".")\r
754                                         continue;\r
755 \r
756                                 // Is parent\r
757                                 if (path[i] === '..') {\r
758                                         nb++;\r
759                                         continue;\r
760                                 }\r
761 \r
762                                 // Move up\r
763                                 if (nb > 0) {\r
764                                         nb--;\r
765                                         continue;\r
766                                 }\r
767 \r
768                                 o.push(path[i]);\r
769                         }\r
770 \r
771                         i = base.length - nb;\r
772 \r
773                         // If /a/b/c or /\r
774                         if (i <= 0)\r
775                                 outPath = o.reverse().join('/');\r
776                         else\r
777                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
778 \r
779                         // Add front / if it's needed\r
780                         if (outPath.indexOf('/') !== 0)\r
781                                 outPath = '/' + outPath;\r
782 \r
783                         // Add traling / if it's needed\r
784                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
785                                 outPath += tr;\r
786 \r
787                         return outPath;\r
788                 },\r
789 \r
790                 getURI : function(nh) {\r
791                         var s, t = this;\r
792 \r
793                         // Rebuild source\r
794                         if (!t.source || nh) {\r
795                                 s = '';\r
796 \r
797                                 if (!nh) {\r
798                                         if (t.protocol)\r
799                                                 s += t.protocol + '://';\r
800 \r
801                                         if (t.userInfo)\r
802                                                 s += t.userInfo + '@';\r
803 \r
804                                         if (t.host)\r
805                                                 s += t.host;\r
806 \r
807                                         if (t.port)\r
808                                                 s += ':' + t.port;\r
809                                 }\r
810 \r
811                                 if (t.path)\r
812                                         s += t.path;\r
813 \r
814                                 if (t.query)\r
815                                         s += '?' + t.query;\r
816 \r
817                                 if (t.anchor)\r
818                                         s += '#' + t.anchor;\r
819 \r
820                                 t.source = s;\r
821                         }\r
822 \r
823                         return t.source;\r
824                 }\r
825         });\r
826 })();\r
827 \r
828 (function() {\r
829         var each = tinymce.each;\r
830 \r
831         tinymce.create('static tinymce.util.Cookie', {\r
832                 getHash : function(n) {\r
833                         var v = this.get(n), h;\r
834 \r
835                         if (v) {\r
836                                 each(v.split('&'), function(v) {\r
837                                         v = v.split('=');\r
838                                         h = h || {};\r
839                                         h[unescape(v[0])] = unescape(v[1]);\r
840                                 });\r
841                         }\r
842 \r
843                         return h;\r
844                 },\r
845 \r
846                 setHash : function(n, v, e, p, d, s) {\r
847                         var o = '';\r
848 \r
849                         each(v, function(v, k) {\r
850                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
851                         });\r
852 \r
853                         this.set(n, o, e, p, d, s);\r
854                 },\r
855 \r
856                 get : function(n) {\r
857                         var c = document.cookie, e, p = n + "=", b;\r
858 \r
859                         // Strict mode\r
860                         if (!c)\r
861                                 return;\r
862 \r
863                         b = c.indexOf("; " + p);\r
864 \r
865                         if (b == -1) {\r
866                                 b = c.indexOf(p);\r
867 \r
868                                 if (b !== 0)\r
869                                         return null;\r
870                         } else\r
871                                 b += 2;\r
872 \r
873                         e = c.indexOf(";", b);\r
874 \r
875                         if (e == -1)\r
876                                 e = c.length;\r
877 \r
878                         return unescape(c.substring(b + p.length, e));\r
879                 },\r
880 \r
881                 set : function(n, v, e, p, d, s) {\r
882                         document.cookie = n + "=" + escape(v) +\r
883                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
884                                 ((p) ? "; path=" + escape(p) : "") +\r
885                                 ((d) ? "; domain=" + d : "") +\r
886                                 ((s) ? "; secure" : "");\r
887                 },\r
888 \r
889                 remove : function(name, path, domain) {\r
890                         var date = new Date();\r
891 \r
892                         date.setTime(date.getTime() - 1000);\r
893 \r
894                         this.set(name, '', date, path, domain);\r
895                 }\r
896         });\r
897 })();\r
898 \r
899 (function() {\r
900         function serialize(o, quote) {\r
901                 var i, v, t, name;\r
902 \r
903                 quote = quote || '"';\r
904 \r
905                 if (o == null)\r
906                         return 'null';\r
907 \r
908                 t = typeof o;\r
909 \r
910                 if (t == 'string') {\r
911                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
912 \r
913                         return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {\r
914                                 // Make sure single quotes never get encoded inside double quotes for JSON compatibility\r
915                                 if (quote === '"' && a === "'")\r
916                                         return a;\r
917 \r
918                                 i = v.indexOf(b);\r
919 \r
920                                 if (i + 1)\r
921                                         return '\\' + v.charAt(i + 1);\r
922 \r
923                                 a = b.charCodeAt().toString(16);\r
924 \r
925                                 return '\\u' + '0000'.substring(a.length) + a;\r
926                         }) + quote;\r
927                 }\r
928 \r
929                 if (t == 'object') {\r
930                         if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {\r
931                                         for (i=0, v = '['; i<o.length; i++)\r
932                                                 v += (i > 0 ? ',' : '') + serialize(o[i], quote);\r
933 \r
934                                         return v + ']';\r
935                                 }\r
936 \r
937                                 v = '{';\r
938 \r
939                                 for (name in o) {\r
940                                         if (o.hasOwnProperty(name)) {\r
941                                                 v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + quote +':' + serialize(o[name], quote) : '';\r
942                                         }\r
943                                 }\r
944 \r
945                                 return v + '}';\r
946                 }\r
947 \r
948                 return '' + o;\r
949         };\r
950 \r
951         tinymce.util.JSON = {\r
952                 serialize: serialize,\r
953 \r
954                 parse: function(s) {\r
955                         try {\r
956                                 return eval('(' + s + ')');\r
957                         } catch (ex) {\r
958                                 // Ignore\r
959                         }\r
960                 }\r
961 \r
962                 };\r
963 })();\r
964 \r
965 tinymce.create('static tinymce.util.XHR', {\r
966         send : function(o) {\r
967                 var x, t, w = window, c = 0;\r
968 \r
969                 function ready() {\r
970                         if (!o.async || x.readyState == 4 || c++ > 10000) {\r
971                                 if (o.success && c < 10000 && x.status == 200)\r
972                                         o.success.call(o.success_scope, '' + x.responseText, x, o);\r
973                                 else if (o.error)\r
974                                         o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
975 \r
976                                 x = null;\r
977                         } else\r
978                                 w.setTimeout(ready, 10);\r
979                 };\r
980 \r
981                 // Default settings\r
982                 o.scope = o.scope || this;\r
983                 o.success_scope = o.success_scope || o.scope;\r
984                 o.error_scope = o.error_scope || o.scope;\r
985                 o.async = o.async === false ? false : true;\r
986                 o.data = o.data || '';\r
987 \r
988                 function get(s) {\r
989                         x = 0;\r
990 \r
991                         try {\r
992                                 x = new ActiveXObject(s);\r
993                         } catch (ex) {\r
994                         }\r
995 \r
996                         return x;\r
997                 };\r
998 \r
999                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
1000 \r
1001                 if (x) {\r
1002                         if (x.overrideMimeType)\r
1003                                 x.overrideMimeType(o.content_type);\r
1004 \r
1005                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
1006 \r
1007                         if (o.content_type)\r
1008                                 x.setRequestHeader('Content-Type', o.content_type);\r
1009 \r
1010                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
1011 \r
1012                         x.send(o.data);\r
1013 \r
1014                         // Syncronous request\r
1015                         if (!o.async)\r
1016                                 return ready();\r
1017 \r
1018                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
1019                         t = w.setTimeout(ready, 10);\r
1020                 }\r
1021         }\r
1022 });\r
1023 \r
1024 (function() {\r
1025         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
1026 \r
1027         tinymce.create('tinymce.util.JSONRequest', {\r
1028                 JSONRequest : function(s) {\r
1029                         this.settings = extend({\r
1030                         }, s);\r
1031                         this.count = 0;\r
1032                 },\r
1033 \r
1034                 send : function(o) {\r
1035                         var ecb = o.error, scb = o.success;\r
1036 \r
1037                         o = extend(this.settings, o);\r
1038 \r
1039                         o.success = function(c, x) {\r
1040                                 c = JSON.parse(c);\r
1041 \r
1042                                 if (typeof(c) == 'undefined') {\r
1043                                         c = {\r
1044                                                 error : 'JSON Parse error.'\r
1045                                         };\r
1046                                 }\r
1047 \r
1048                                 if (c.error)\r
1049                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
1050                                 else\r
1051                                         scb.call(o.success_scope || o.scope, c.result);\r
1052                         };\r
1053 \r
1054                         o.error = function(ty, x) {\r
1055                                 if (ecb)\r
1056                                         ecb.call(o.error_scope || o.scope, ty, x);\r
1057                         };\r
1058 \r
1059                         o.data = JSON.serialize({\r
1060                                 id : o.id || 'c' + (this.count++),\r
1061                                 method : o.method,\r
1062                                 params : o.params\r
1063                         });\r
1064 \r
1065                         // JSON content type for Ruby on rails. Bug: #1883287\r
1066                         o.content_type = 'application/json';\r
1067 \r
1068                         XHR.send(o);\r
1069                 },\r
1070 \r
1071                 'static' : {\r
1072                         sendRPC : function(o) {\r
1073                                 return new tinymce.util.JSONRequest().send(o);\r
1074                         }\r
1075                 }\r
1076         });\r
1077 }());\r
1078 (function(tinymce){\r
1079         tinymce.VK = {\r
1080                 BACKSPACE: 8,\r
1081                 DELETE: 46,\r
1082                 DOWN: 40,\r
1083                 ENTER: 13,\r
1084                 LEFT: 37,\r
1085                 RIGHT: 39,\r
1086                 SPACEBAR: 32,\r
1087                 TAB: 9,\r
1088                 UP: 38,\r
1089 \r
1090                 modifierPressed: function (e) {\r
1091                         return e.shiftKey || e.ctrlKey || e.altKey;\r
1092                 },\r
1093 \r
1094                 metaKeyPressed: function(e) {\r
1095                         // Check if ctrl or meta key is pressed also check if alt is false for Polish users\r
1096                         return tinymce.isMac ? e.metaKey : e.ctrlKey && !e.altKey;\r
1097                 }\r
1098         };\r
1099 })(tinymce);\r
1100 \r
1101 tinymce.util.Quirks = function(editor) {\r
1102         var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,\r
1103                 settings = editor.settings, parser = editor.parser, serializer = editor.serializer, each = tinymce.each;\r
1104 \r
1105         function setEditorCommandState(cmd, state) {\r
1106                 try {\r
1107                         editor.getDoc().execCommand(cmd, false, state);\r
1108                 } catch (ex) {\r
1109                         // Ignore\r
1110                 }\r
1111         }\r
1112 \r
1113         function getDocumentMode() {\r
1114                 var documentMode = editor.getDoc().documentMode;\r
1115 \r
1116                 return documentMode ? documentMode : 6;\r
1117         };\r
1118 \r
1119         function isDefaultPrevented(e) {\r
1120                 return e.isDefaultPrevented();\r
1121         };\r
1122 \r
1123         function cleanupStylesWhenDeleting() {\r
1124                 function removeMergedFormatSpans(isDelete) {\r
1125                         var rng, blockElm, wrapperElm, bookmark, container, offset, elm;\r
1126 \r
1127                         function isAtStartOrEndOfElm() {\r
1128                                 if (container.nodeType == 3) {\r
1129                                         if (isDelete && offset == container.length) {\r
1130                                                 return true;\r
1131                                         }\r
1132 \r
1133                                         if (!isDelete && offset === 0) {\r
1134                                                 return true;\r
1135                                         }\r
1136                                 }\r
1137                         }\r
1138 \r
1139                         rng = selection.getRng();\r
1140                         var tmpRng = [rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset];\r
1141 \r
1142                         if (!rng.collapsed) {\r
1143                                 isDelete = true;\r
1144                         }\r
1145 \r
1146                         container = rng[(isDelete ? 'start' : 'end') + 'Container'];\r
1147                         offset = rng[(isDelete ? 'start' : 'end') + 'Offset'];\r
1148 \r
1149                         if (container.nodeType == 3) {\r
1150                                 blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
1151 \r
1152                                 // On delete clone the root span of the next block element\r
1153                                 if (isDelete) {\r
1154                                         blockElm = dom.getNext(blockElm, dom.isBlock);\r
1155                                 }\r
1156 \r
1157                                 if (blockElm && (isAtStartOrEndOfElm() || !rng.collapsed)) {\r
1158                                         // Wrap children of block in a EM and let WebKit stick is\r
1159                                         // runtime styles junk into that EM\r
1160                                         wrapperElm = dom.create('em', {'id': '__mceDel'});\r
1161 \r
1162                                         each(tinymce.grep(blockElm.childNodes), function(node) {\r
1163                                                 wrapperElm.appendChild(node);\r
1164                                         });\r
1165 \r
1166                                         blockElm.appendChild(wrapperElm);\r
1167                                 }\r
1168                         }\r
1169 \r
1170                         // Do the backspace/delete action\r
1171                         rng = dom.createRng();\r
1172                         rng.setStart(tmpRng[0], tmpRng[1]);\r
1173                         rng.setEnd(tmpRng[2], tmpRng[3]);\r
1174                         selection.setRng(rng);\r
1175                         editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);\r
1176 \r
1177                         // Remove temp wrapper element\r
1178                         if (wrapperElm) {\r
1179                                 bookmark = selection.getBookmark();\r
1180 \r
1181                                 while (elm = dom.get('__mceDel')) {\r
1182                                         dom.remove(elm, true);\r
1183                                 }\r
1184 \r
1185                                 selection.moveToBookmark(bookmark);\r
1186                         }\r
1187                 }\r
1188 \r
1189                 editor.onKeyDown.add(function(editor, e) {\r
1190                         var isDelete;\r
1191 \r
1192                         isDelete = e.keyCode == DELETE;\r
1193                         if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {\r
1194                                 e.preventDefault();\r
1195                                 removeMergedFormatSpans(isDelete);\r
1196                         }\r
1197                 });\r
1198 \r
1199                 editor.addCommand('Delete', function() {removeMergedFormatSpans();});\r
1200         };\r
1201         \r
1202         function emptyEditorWhenDeleting() {\r
1203                 function serializeRng(rng) {\r
1204                         var body = dom.create("body");\r
1205                         var contents = rng.cloneContents();\r
1206                         body.appendChild(contents);\r
1207                         return selection.serializer.serialize(body, {format: 'html'});\r
1208                 }\r
1209 \r
1210                 function allContentsSelected(rng) {\r
1211                         var selection = serializeRng(rng);\r
1212 \r
1213                         var allRng = dom.createRng();\r
1214                         allRng.selectNode(editor.getBody());\r
1215 \r
1216                         var allSelection = serializeRng(allRng);\r
1217                         return selection === allSelection;\r
1218                 }\r
1219 \r
1220                 editor.onKeyDown.add(function(editor, e) {\r
1221                         var keyCode = e.keyCode, isCollapsed;\r
1222 \r
1223                         // Empty the editor if it's needed for example backspace at <p><b>|</b></p>\r
1224                         if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {\r
1225                                 isCollapsed = editor.selection.isCollapsed();\r
1226 \r
1227                                 // Selection is collapsed but the editor isn't empty\r
1228                                 if (isCollapsed && !dom.isEmpty(editor.getBody())) {\r
1229                                         return;\r
1230                                 }\r
1231 \r
1232                                 // IE deletes all contents correctly when everything is selected\r
1233                                 if (tinymce.isIE && !isCollapsed) {\r
1234                                         return;\r
1235                                 }\r
1236 \r
1237                                 // Selection isn't collapsed but not all the contents is selected\r
1238                                 if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {\r
1239                                         return;\r
1240                                 }\r
1241 \r
1242                                 // Manually empty the editor\r
1243                                 editor.setContent('');\r
1244                                 editor.selection.setCursorLocation(editor.getBody(), 0);\r
1245                                 editor.nodeChanged();\r
1246                         }\r
1247                 });\r
1248         };\r
1249 \r
1250         function selectAll() {\r
1251                 editor.onKeyDown.add(function(editor, e) {\r
1252                         if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {\r
1253                                 e.preventDefault();\r
1254                                 editor.execCommand('SelectAll');\r
1255                         }\r
1256                 });\r
1257         };\r
1258 \r
1259         function inputMethodFocus() {\r
1260                 if (!editor.settings.content_editable) {\r
1261                         // Case 1 IME doesn't initialize if you focus the document\r
1262                         dom.bind(editor.getDoc(), 'focusin', function(e) {\r
1263                                 selection.setRng(selection.getRng());\r
1264                         });\r
1265 \r
1266                         // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event\r
1267                         dom.bind(editor.getDoc(), 'mousedown', function(e) {\r
1268                                 if (e.target == editor.getDoc().documentElement) {\r
1269                                         editor.getWin().focus();\r
1270                                         selection.setRng(selection.getRng());\r
1271                                 }\r
1272                         });\r
1273                 }\r
1274         };\r
1275 \r
1276         function removeHrOnBackspace() {\r
1277                 editor.onKeyDown.add(function(editor, e) {\r
1278                         if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {\r
1279                                 if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {\r
1280                                         var node = selection.getNode();\r
1281                                         var previousSibling = node.previousSibling;\r
1282 \r
1283                                         if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {\r
1284                                                 dom.remove(previousSibling);\r
1285                                                 tinymce.dom.Event.cancel(e);\r
1286                                         }\r
1287                                 }\r
1288                         }\r
1289                 })\r
1290         }\r
1291 \r
1292         function focusBody() {\r
1293                 // Fix for a focus bug in FF 3.x where the body element\r
1294                 // wouldn't get proper focus if the user clicked on the HTML element\r
1295                 if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4\r
1296                         editor.onMouseDown.add(function(editor, e) {\r
1297                                 if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {\r
1298                                         var body = editor.getBody();\r
1299 \r
1300                                         // Blur the body it's focused but not correctly focused\r
1301                                         body.blur();\r
1302 \r
1303                                         // Refocus the body after a little while\r
1304                                         setTimeout(function() {\r
1305                                                 body.focus();\r
1306                                         }, 0);\r
1307                                 }\r
1308                         });\r
1309                 }\r
1310         };\r
1311 \r
1312         function selectControlElements() {\r
1313                 editor.onClick.add(function(editor, e) {\r
1314                         e = e.target;\r
1315 \r
1316                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
1317                         // WebKit can't even do simple things like selecting an image\r
1318                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
1319                         if (/^(IMG|HR)$/.test(e.nodeName)) {\r
1320                                 selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
1321                         }\r
1322 \r
1323                         if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) {\r
1324                                 selection.select(e);\r
1325                         }\r
1326 \r
1327                         editor.nodeChanged();\r
1328                 });\r
1329         };\r
1330 \r
1331         function removeStylesWhenDeletingAccrossBlockElements() {\r
1332                 function getAttributeApplyFunction() {\r
1333                         var template = dom.getAttribs(selection.getStart().cloneNode(false));\r
1334 \r
1335                         return function() {\r
1336                                 var target = selection.getStart();\r
1337 \r
1338                                 if (target !== editor.getBody()) {\r
1339                                         dom.setAttrib(target, "style", null);\r
1340 \r
1341                                         each(template, function(attr) {\r
1342                                                 target.setAttributeNode(attr.cloneNode(true));\r
1343                                         });\r
1344                                 }\r
1345                         };\r
1346                 }\r
1347 \r
1348                 function isSelectionAcrossElements() {\r
1349                         return !selection.isCollapsed() && dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);\r
1350                 }\r
1351 \r
1352                 function blockEvent(editor, e) {\r
1353                         e.preventDefault();\r
1354                         return false;\r
1355                 }\r
1356 \r
1357                 editor.onKeyPress.add(function(editor, e) {\r
1358                         var applyAttributes;\r
1359 \r
1360                         if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {\r
1361                                 applyAttributes = getAttributeApplyFunction();\r
1362                                 editor.getDoc().execCommand('delete', false, null);\r
1363                                 applyAttributes();\r
1364                                 e.preventDefault();\r
1365                                 return false;\r
1366                         }\r
1367                 });\r
1368 \r
1369                 dom.bind(editor.getDoc(), 'cut', function(e) {\r
1370                         var applyAttributes;\r
1371 \r
1372                         if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {\r
1373                                 applyAttributes = getAttributeApplyFunction();\r
1374                                 editor.onKeyUp.addToTop(blockEvent);\r
1375 \r
1376                                 setTimeout(function() {\r
1377                                         applyAttributes();\r
1378                                         editor.onKeyUp.remove(blockEvent);\r
1379                                 }, 0);\r
1380                         }\r
1381                 });\r
1382         }\r
1383 \r
1384         function selectionChangeNodeChanged() {\r
1385                 var lastRng, selectionTimer;\r
1386 \r
1387                 dom.bind(editor.getDoc(), 'selectionchange', function() {\r
1388                         if (selectionTimer) {\r
1389                                 clearTimeout(selectionTimer);\r
1390                                 selectionTimer = 0;\r
1391                         }\r
1392 \r
1393                         selectionTimer = window.setTimeout(function() {\r
1394                                 var rng = selection.getRng();\r
1395 \r
1396                                 // Compare the ranges to see if it was a real change or not\r
1397                                 if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {\r
1398                                         editor.nodeChanged();\r
1399                                         lastRng = rng;\r
1400                                 }\r
1401                         }, 50);\r
1402                 });\r
1403         }\r
1404 \r
1405         function ensureBodyHasRoleApplication() {\r
1406                 document.body.setAttribute("role", "application");\r
1407         }\r
1408 \r
1409         function disableBackspaceIntoATable() {\r
1410                 editor.onKeyDown.add(function(editor, e) {\r
1411                         if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {\r
1412                                 if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {\r
1413                                         var previousSibling = selection.getNode().previousSibling;\r
1414                                         if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {\r
1415                                                 return tinymce.dom.Event.cancel(e);\r
1416                                         }\r
1417                                 }\r
1418                         }\r
1419                 })\r
1420         }\r
1421 \r
1422         function addNewLinesBeforeBrInPre() {\r
1423                 // IE8+ rendering mode does the right thing with BR in PRE\r
1424                 if (getDocumentMode() > 7) {\r
1425                         return;\r
1426                 }\r
1427 \r
1428                  // Enable display: none in area and add a specific class that hides all BR elements in PRE to\r
1429                  // avoid the caret from getting stuck at the BR elements while pressing the right arrow key\r
1430                 setEditorCommandState('RespectVisibilityInDesign', true);\r
1431                 editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');\r
1432                 dom.addClass(editor.getBody(), 'mceHideBrInPre');\r
1433 \r
1434                 // Adds a \n before all BR elements in PRE to get them visual\r
1435                 parser.addNodeFilter('pre', function(nodes, name) {\r
1436                         var i = nodes.length, brNodes, j, brElm, sibling;\r
1437 \r
1438                         while (i--) {\r
1439                                 brNodes = nodes[i].getAll('br');\r
1440                                 j = brNodes.length;\r
1441                                 while (j--) {\r
1442                                         brElm = brNodes[j];\r
1443 \r
1444                                         // Add \n before BR in PRE elements on older IE:s so the new lines get rendered\r
1445                                         sibling = brElm.prev;\r
1446                                         if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {\r
1447                                                 sibling.value += '\n';\r
1448                                         } else {\r
1449                                                 brElm.parent.insert(new tinymce.html.Node('#text', 3), brElm, true).value = '\n';\r
1450                                         }\r
1451                                 }\r
1452                         }\r
1453                 });\r
1454 \r
1455                 // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible\r
1456                 serializer.addNodeFilter('pre', function(nodes, name) {\r
1457                         var i = nodes.length, brNodes, j, brElm, sibling;\r
1458 \r
1459                         while (i--) {\r
1460                                 brNodes = nodes[i].getAll('br');\r
1461                                 j = brNodes.length;\r
1462                                 while (j--) {\r
1463                                         brElm = brNodes[j];\r
1464                                         sibling = brElm.prev;\r
1465                                         if (sibling && sibling.type == 3) {\r
1466                                                 sibling.value = sibling.value.replace(/\r?\n$/, '');\r
1467                                         }\r
1468                                 }\r
1469                         }\r
1470                 });\r
1471         }\r
1472 \r
1473         function removePreSerializedStylesWhenSelectingControls() {\r
1474                 dom.bind(editor.getBody(), 'mouseup', function(e) {\r
1475                         var value, node = selection.getNode();\r
1476 \r
1477                         // Moved styles to attributes on IMG eements\r
1478                         if (node.nodeName == 'IMG') {\r
1479                                 // Convert style width to width attribute\r
1480                                 if (value = dom.getStyle(node, 'width')) {\r
1481                                         dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));\r
1482                                         dom.setStyle(node, 'width', '');\r
1483                                 }\r
1484 \r
1485                                 // Convert style height to height attribute\r
1486                                 if (value = dom.getStyle(node, 'height')) {\r
1487                                         dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));\r
1488                                         dom.setStyle(node, 'height', '');\r
1489                                 }\r
1490                         }\r
1491                 });\r
1492         }\r
1493 \r
1494         function keepInlineElementOnDeleteBackspace() {\r
1495                 editor.onKeyDown.add(function(editor, e) {\r
1496                         var isDelete, rng, container, offset, brElm, sibling, collapsed;\r
1497 \r
1498                         isDelete = e.keyCode == DELETE;\r
1499                         if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {\r
1500                                 rng = selection.getRng();\r
1501                                 container = rng.startContainer;\r
1502                                 offset = rng.startOffset;\r
1503                                 collapsed = rng.collapsed;\r
1504 \r
1505                                 // Override delete if the start container is a text node and is at the beginning of text or\r
1506                                 // just before/after the last character to be deleted in collapsed mode\r
1507                                 if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) || (collapsed && offset === (isDelete ? 0 : 1)))) {\r
1508                                         // Edge case when deleting <p><b><img> |x</b></p>\r
1509                                         sibling = container.previousSibling;\r
1510                                         if (sibling && sibling.nodeName == "IMG") {\r
1511                                                 return;\r
1512                                         }\r
1513 \r
1514                                         nonEmptyElements = editor.schema.getNonEmptyElements();\r
1515 \r
1516                                         // Prevent default logic since it's broken\r
1517                                         e.preventDefault();\r
1518 \r
1519                                         // Insert a BR before the text node this will prevent the containing element from being deleted/converted\r
1520                                         brElm = dom.create('br', {id: '__tmp'});\r
1521                                         container.parentNode.insertBefore(brElm, container);\r
1522 \r
1523                                         // Do the browser delete\r
1524                                         editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);\r
1525 \r
1526                                         // Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>\r
1527                                         container = selection.getRng().startContainer;\r
1528                                         sibling = container.previousSibling;\r
1529                                         if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) && !nonEmptyElements[sibling.nodeName.toLowerCase()]) {\r
1530                                                 dom.remove(sibling);\r
1531                                         }\r
1532 \r
1533                                         // Remove the temp element we inserted\r
1534                                         dom.remove('__tmp');\r
1535                                 }\r
1536                         }\r
1537                 });\r
1538         }\r
1539 \r
1540         function removeBlockQuoteOnBackSpace() {\r
1541                 // Add block quote deletion handler\r
1542                 editor.onKeyDown.add(function(editor, e) {\r
1543                         var rng, container, offset, root, parent;\r
1544 \r
1545                         if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {\r
1546                                 return;\r
1547                         }\r
1548 \r
1549                         rng = selection.getRng();\r
1550                         container = rng.startContainer;\r
1551                         offset = rng.startOffset;\r
1552                         root = dom.getRoot();\r
1553                         parent = container;\r
1554 \r
1555                         if (!rng.collapsed || offset !== 0) {\r
1556                                 return;\r
1557                         }\r
1558 \r
1559                         while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {\r
1560                                 parent = parent.parentNode;\r
1561                         }\r
1562 \r
1563                         // Is the cursor at the beginning of a blockquote?\r
1564                         if (parent.tagName === 'BLOCKQUOTE') {\r
1565                                 // Remove the blockquote\r
1566                                 editor.formatter.toggle('blockquote', null, parent);\r
1567 \r
1568                                 // Move the caret to the beginning of container\r
1569                                 rng = dom.createRng();\r
1570                                 rng.setStart(container, 0);\r
1571                                 rng.setEnd(container, 0);\r
1572                                 selection.setRng(rng);\r
1573                         }\r
1574                 });\r
1575         };\r
1576 \r
1577         function setGeckoEditingOptions() {\r
1578                 function setOpts() {\r
1579                         editor._refreshContentEditable();\r
1580 \r
1581                         setEditorCommandState("StyleWithCSS", false);\r
1582                         setEditorCommandState("enableInlineTableEditing", false);\r
1583 \r
1584                         if (!settings.object_resizing) {\r
1585                                 setEditorCommandState("enableObjectResizing", false);\r
1586                         }\r
1587                 };\r
1588 \r
1589                 if (!settings.readonly) {\r
1590                         editor.onBeforeExecCommand.add(setOpts);\r
1591                         editor.onMouseDown.add(setOpts);\r
1592                 }\r
1593         };\r
1594 \r
1595         function addBrAfterLastLinks() {\r
1596                 function fixLinks(editor, o) {\r
1597                         each(dom.select('a'), function(node) {\r
1598                                 var parentNode = node.parentNode, root = dom.getRoot();\r
1599 \r
1600                                 if (parentNode.lastChild === node) {\r
1601                                         while (parentNode && !dom.isBlock(parentNode)) {\r
1602                                                 if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {\r
1603                                                         return;\r
1604                                                 }\r
1605 \r
1606                                                 parentNode = parentNode.parentNode;\r
1607                                         }\r
1608 \r
1609                                         dom.add(parentNode, 'br', {'data-mce-bogus' : 1});\r
1610                                 }\r
1611                         });\r
1612                 };\r
1613 \r
1614                 editor.onExecCommand.add(function(editor, cmd) {\r
1615                         if (cmd === 'CreateLink') {\r
1616                                 fixLinks(editor);\r
1617                         }\r
1618                 });\r
1619 \r
1620                 editor.onSetContent.add(selection.onSetContent.add(fixLinks));\r
1621         };\r
1622 \r
1623         function setDefaultBlockType() {\r
1624                 if (settings.forced_root_block) {\r
1625                         editor.onInit.add(function() {\r
1626                                 setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);\r
1627                         });\r
1628                 }\r
1629         }\r
1630 \r
1631         function removeGhostSelection() {\r
1632                 function repaint(sender, args) {\r
1633                         if (!sender || !args.initial) {\r
1634                                 editor.execCommand('mceRepaint');\r
1635                         }\r
1636                 };\r
1637 \r
1638                 editor.onUndo.add(repaint);\r
1639                 editor.onRedo.add(repaint);\r
1640                 editor.onSetContent.add(repaint);\r
1641         };\r
1642 \r
1643         function deleteControlItemOnBackSpace() {\r
1644                 editor.onKeyDown.add(function(editor, e) {\r
1645                         var rng;\r
1646 \r
1647                         if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {\r
1648                                 rng = editor.getDoc().selection.createRange();\r
1649                                 if (rng && rng.item) {\r
1650                                         e.preventDefault();\r
1651                                         editor.undoManager.beforeChange();\r
1652                                         dom.remove(rng.item(0));\r
1653                                         editor.undoManager.add();\r
1654                                 }\r
1655                         }\r
1656                 });\r
1657         };\r
1658 \r
1659         function renderEmptyBlocksFix() {\r
1660                 var emptyBlocksCSS;\r
1661 \r
1662                 // IE10+\r
1663                 if (getDocumentMode() >= 10) {\r
1664                         emptyBlocksCSS = '';\r
1665                         each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {\r
1666                                 emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';\r
1667                         });\r
1668 \r
1669                         editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');\r
1670                 }\r
1671         };\r
1672 \r
1673         function fakeImageResize() {\r
1674                 var selectedElmX, selectedElmY, selectedElm, selectedElmGhost, selectedHandle, startX, startY, startW, startH, ratio,\r
1675                         resizeHandles, width, height, rootDocument = document, editableDoc = editor.getDoc();\r
1676 \r
1677                 if (!settings.object_resizing || settings.webkit_fake_resize === false) {\r
1678                         return;\r
1679                 }\r
1680 \r
1681                 // Try disabling object resizing if WebKit implements resizing in the future\r
1682                 setEditorCommandState("enableObjectResizing", false);\r
1683 \r
1684                 // Details about each resize handle how to scale etc\r
1685                 resizeHandles = {\r
1686                         // Name: x multiplier, y multiplier, delta size x, delta size y\r
1687                         n: [.5, 0, 0, -1],\r
1688                         e: [1, .5, 1, 0],\r
1689                         s: [.5, 1, 0, 1],\r
1690                         w: [0, .5, -1, 0],\r
1691                         nw: [0, 0, -1, -1],\r
1692                         ne: [1, 0, 1, -1],\r
1693                         se: [1, 1, 1, 1],\r
1694                         sw : [0, 1, -1, 1]\r
1695                 };\r
1696 \r
1697                 function resizeElement(e) {\r
1698                         var deltaX, deltaY;\r
1699 \r
1700                         // Calc new width/height\r
1701                         deltaX = e.screenX - startX;\r
1702                         deltaY = e.screenY - startY;\r
1703 \r
1704                         // Calc new size\r
1705                         width = deltaX * selectedHandle[2] + startW;\r
1706                         height = deltaY * selectedHandle[3] + startH;\r
1707 \r
1708                         // Never scale down lower than 5 pixels\r
1709                         width = width < 5 ? 5 : width;\r
1710                         height = height < 5 ? 5 : height;\r
1711 \r
1712                         // Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image\r
1713                         if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {\r
1714                                 width = Math.round(height / ratio);\r
1715                                 height = Math.round(width * ratio);\r
1716                         }\r
1717 \r
1718                         // Update ghost size\r
1719                         dom.setStyles(selectedElmGhost, {\r
1720                                 width: width,\r
1721                                 height: height\r
1722                         });\r
1723 \r
1724                         // Update ghost X position if needed\r
1725                         if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {\r
1726                                 dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));\r
1727                         }\r
1728 \r
1729                         // Update ghost Y position if needed\r
1730                         if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {\r
1731                                 dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));\r
1732                         }\r
1733                 }\r
1734 \r
1735                 function endResize() {\r
1736                         function setSizeProp(name, value) {\r
1737                                 if (value) {\r
1738                                         // Resize by using style or attribute\r
1739                                         if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {\r
1740                                                 dom.setStyle(selectedElm, name, value);\r
1741                                         } else {\r
1742                                                 dom.setAttrib(selectedElm, name, value);\r
1743                                         }\r
1744                                 }\r
1745                         }\r
1746 \r
1747                         // Set width/height properties\r
1748                         setSizeProp('width', width);\r
1749                         setSizeProp('height', height);\r
1750 \r
1751                         dom.unbind(editableDoc, 'mousemove', resizeElement);\r
1752                         dom.unbind(editableDoc, 'mouseup', endResize);\r
1753 \r
1754                         if (rootDocument != editableDoc) {\r
1755                                 dom.unbind(rootDocument, 'mousemove', resizeElement);\r
1756                                 dom.unbind(rootDocument, 'mouseup', endResize);\r
1757                         }\r
1758 \r
1759                         // Remove ghost and update resize handle positions\r
1760                         dom.remove(selectedElmGhost);\r
1761                         showResizeRect(selectedElm);\r
1762                 }\r
1763 \r
1764                 function showResizeRect(targetElm) {\r
1765                         var position, targetWidth, targetHeight;\r
1766 \r
1767                         hideResizeRect();\r
1768 \r
1769                         // Get position and size of target\r
1770                         position = dom.getPos(targetElm);\r
1771                         selectedElmX = position.x;\r
1772                         selectedElmY = position.y;\r
1773                         targetWidth = targetElm.offsetWidth;\r
1774                         targetHeight = targetElm.offsetHeight;\r
1775 \r
1776                         // Reset width/height if user selects a new image/table\r
1777                         if (selectedElm != targetElm) {\r
1778                                 selectedElm = targetElm;\r
1779                                 width = height = 0;\r
1780                         }\r
1781 \r
1782                         each(resizeHandles, function(handle, name) {\r
1783                                 var handleElm;\r
1784 \r
1785                                 // Get existing or render resize handle\r
1786                                 handleElm = dom.get('mceResizeHandle' + name);\r
1787                                 if (!handleElm) {\r
1788                                         handleElm = dom.add(editableDoc.documentElement, 'div', {\r
1789                                                 id: 'mceResizeHandle' + name,\r
1790                                                 'class': 'mceResizeHandle',\r
1791                                                 style: 'cursor:' + name + '-resize; margin:0; padding:0'\r
1792                                         });\r
1793 \r
1794                                         dom.bind(handleElm, 'mousedown', function(e) {\r
1795                                                 e.preventDefault();\r
1796 \r
1797                                                 endResize();\r
1798 \r
1799                                                 startX = e.screenX;\r
1800                                                 startY = e.screenY;\r
1801                                                 startW = selectedElm.clientWidth;\r
1802                                                 startH = selectedElm.clientHeight;\r
1803                                                 ratio = startH / startW;\r
1804                                                 selectedHandle = handle;\r
1805 \r
1806                                                 selectedElmGhost = selectedElm.cloneNode(true);\r
1807                                                 dom.addClass(selectedElmGhost, 'mceClonedResizable');\r
1808                                                 dom.setStyles(selectedElmGhost, {\r
1809                                                         left: selectedElmX,\r
1810                                                         top: selectedElmY,\r
1811                                                         margin: 0\r
1812                                                 });\r
1813 \r
1814                                                 editableDoc.documentElement.appendChild(selectedElmGhost);\r
1815 \r
1816                                                 dom.bind(editableDoc, 'mousemove', resizeElement);\r
1817                                                 dom.bind(editableDoc, 'mouseup', endResize);\r
1818 \r
1819                                                 if (rootDocument != editableDoc) {\r
1820                                                         dom.bind(rootDocument, 'mousemove', resizeElement);\r
1821                                                         dom.bind(rootDocument, 'mouseup', endResize);\r
1822                                                 }\r
1823                                         });\r
1824                                 } else {\r
1825                                         dom.show(handleElm);\r
1826                                 }\r
1827 \r
1828                                 // Position element\r
1829                                 dom.setStyles(handleElm, {\r
1830                                         left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),\r
1831                                         top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)\r
1832                                 });\r
1833                         });\r
1834 \r
1835                         // Only add resize rectangle on WebKit and only on images\r
1836                         if (!tinymce.isOpera && selectedElm.nodeName == "IMG") {\r
1837                                 selectedElm.setAttribute('data-mce-selected', '1');\r
1838                         }\r
1839                 }\r
1840 \r
1841                 function hideResizeRect() {\r
1842                         if (selectedElm) {\r
1843                                 selectedElm.removeAttribute('data-mce-selected');\r
1844                         }\r
1845 \r
1846                         for (var name in resizeHandles) {\r
1847                                 dom.hide('mceResizeHandle' + name);\r
1848                         }\r
1849                 }\r
1850 \r
1851                 // Add CSS for resize handles, cloned element and selected\r
1852                 editor.contentStyles.push(\r
1853                         '.mceResizeHandle {' +\r
1854                                 'position: absolute;' +\r
1855                                 'border: 1px solid black;' +\r
1856                                 'background: #FFF;' +\r
1857                                 'width: 5px;' +\r
1858                                 'height: 5px;' +\r
1859                                 'z-index: 10000' +\r
1860                         '}' +\r
1861                         '.mceResizeHandle:hover {' +\r
1862                                 'background: #000' +\r
1863                         '}' +\r
1864                         'img[data-mce-selected] {' +\r
1865                                 'outline: 1px solid black' +\r
1866                         '}' +\r
1867                         'img.mceClonedResizable, table.mceClonedResizable {' +\r
1868                                 'position: absolute;' +\r
1869                                 'outline: 1px dashed black;' +\r
1870                                 'opacity: .5;' +\r
1871                                 'z-index: 10000' +\r
1872                         '}'\r
1873                 );\r
1874 \r
1875                 function updateResizeRect() {\r
1876                         var controlElm = dom.getParent(selection.getNode(), 'table,img');\r
1877 \r
1878                         // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v\r
1879                         each(dom.select('img[data-mce-selected]'), function(img) {\r
1880                                 img.removeAttribute('data-mce-selected');\r
1881                         });\r
1882 \r
1883                         if (controlElm) {\r
1884                                 showResizeRect(controlElm);\r
1885                         } else {\r
1886                                 hideResizeRect();\r
1887                         }\r
1888                 }\r
1889 \r
1890                 // Show/hide resize rect when image is selected\r
1891                 editor.onNodeChange.add(updateResizeRect);\r
1892 \r
1893                 // Fixes WebKit quirk where it returns IMG on getNode if caret is after last image in container\r
1894                 dom.bind(editableDoc, 'selectionchange', updateResizeRect);\r
1895 \r
1896                 // Remove the internal attribute when serializing the DOM\r
1897                 editor.serializer.addAttributeFilter('data-mce-selected', function(nodes, name) {\r
1898                         var i = nodes.length;\r
1899 \r
1900                         while (i--) {\r
1901                                 nodes[i].attr(name, null);\r
1902                         }\r
1903                 });\r
1904         }\r
1905 \r
1906         function keepNoScriptContents() {\r
1907                 if (getDocumentMode() < 9) {\r
1908                         parser.addNodeFilter('noscript', function(nodes) {\r
1909                                 var i = nodes.length, node, textNode;\r
1910 \r
1911                                 while (i--) {\r
1912                                         node = nodes[i];\r
1913                                         textNode = node.firstChild;\r
1914 \r
1915                                         if (textNode) {\r
1916                                                 node.attr('data-mce-innertext', textNode.value);\r
1917                                         }\r
1918                                 }\r
1919                         });\r
1920 \r
1921                         serializer.addNodeFilter('noscript', function(nodes) {\r
1922                                 var i = nodes.length, node, textNode, value;\r
1923 \r
1924                                 while (i--) {\r
1925                                         node = nodes[i];\r
1926                                         textNode = nodes[i].firstChild;\r
1927 \r
1928                                         if (textNode) {\r
1929                                                 textNode.value = tinymce.html.Entities.decode(textNode.value);\r
1930                                         } else {\r
1931                                                 // Old IE can't retain noscript value so an attribute is used to store it\r
1932                                                 value = node.attributes.map['data-mce-innertext'];\r
1933                                                 if (value) {\r
1934                                                         node.attr('data-mce-innertext', null);\r
1935                                                         textNode = new tinymce.html.Node('#text', 3);\r
1936                                                         textNode.value = value;\r
1937                                                         textNode.raw = true;\r
1938                                                         node.append(textNode);\r
1939                                                 }\r
1940                                         }\r
1941                                 }\r
1942                         });\r
1943                 }\r
1944         }\r
1945 \r
1946         function bodyHeight() {\r
1947                 editor.contentStyles.push('body {min-height: 100px}');\r
1948                 editor.onClick.add(function(ed, e) {\r
1949                         if (e.target.nodeName == 'HTML') {\r
1950                                 editor.execCommand('SelectAll');\r
1951                                 editor.selection.collapse(true);\r
1952                                 editor.nodeChanged();\r
1953                         }\r
1954                 });\r
1955         }\r
1956 \r
1957         function fixControlSelection() {\r
1958                 editor.onInit.add(function() {\r
1959                         var selectedRng;\r
1960 \r
1961                         editor.getBody().addEventListener('mscontrolselect', function(e) {\r
1962                                 setTimeout(function() {\r
1963                                         if (editor.selection.getNode() != e.target) {\r
1964                                                 selectedRng = editor.selection.getRng();\r
1965                                                 selection.fakeRng = editor.dom.createRng();\r
1966                                                 selection.fakeRng.setStartBefore(e.target);\r
1967                                                 selection.fakeRng.setEndAfter(e.target);\r
1968                                         }\r
1969                                 }, 0);\r
1970                         }, false);\r
1971 \r
1972                         editor.getDoc().addEventListener('selectionchange', function(e) {\r
1973                                 if (selectedRng && !tinymce.dom.RangeUtils.compareRanges(editor.selection.getRng(), selectedRng)) {\r
1974                                         selection.fakeRng = selectedRng = null;\r
1975                                 }\r
1976                         }, false);\r
1977                 });\r
1978         }\r
1979 \r
1980         // All browsers\r
1981         disableBackspaceIntoATable();\r
1982         removeBlockQuoteOnBackSpace();\r
1983         emptyEditorWhenDeleting();\r
1984 \r
1985         // WebKit\r
1986         if (tinymce.isWebKit) {\r
1987                 keepInlineElementOnDeleteBackspace();\r
1988                 cleanupStylesWhenDeleting();\r
1989                 inputMethodFocus();\r
1990                 selectControlElements();\r
1991                 setDefaultBlockType();\r
1992 \r
1993                 // iOS\r
1994                 if (tinymce.isIDevice) {\r
1995                         selectionChangeNodeChanged();\r
1996                 } else {\r
1997                         fakeImageResize();\r
1998                         selectAll();\r
1999                 }\r
2000         }\r
2001 \r
2002         // IE\r
2003         if (tinymce.isIE && !tinymce.isIE11) {\r
2004                 removeHrOnBackspace();\r
2005                 ensureBodyHasRoleApplication();\r
2006                 addNewLinesBeforeBrInPre();\r
2007                 removePreSerializedStylesWhenSelectingControls();\r
2008                 deleteControlItemOnBackSpace();\r
2009                 renderEmptyBlocksFix();\r
2010                 keepNoScriptContents();\r
2011         }\r
2012 \r
2013         // IE 11+\r
2014         if (tinymce.isIE11) {\r
2015                 bodyHeight();\r
2016                 fixControlSelection();\r
2017         }\r
2018 \r
2019         // Gecko\r
2020         if (tinymce.isGecko && !tinymce.isIE11) {\r
2021                 removeHrOnBackspace();\r
2022                 focusBody();\r
2023                 removeStylesWhenDeletingAccrossBlockElements();\r
2024                 setGeckoEditingOptions();\r
2025                 addBrAfterLastLinks();\r
2026                 removeGhostSelection();\r
2027         }\r
2028 \r
2029         // Opera\r
2030         if (tinymce.isOpera) {\r
2031                 fakeImageResize();\r
2032         }\r
2033 };\r
2034 (function(tinymce) {\r
2035         var namedEntities, baseEntities, reverseEntities,\r
2036                 attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
2037                 textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
2038                 rawCharsRegExp = /[<>&\"\']/g,\r
2039                 entityRegExp = /&(#x|#)?([\w]+);/g,\r
2040                 asciiMap = {\r
2041                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",\r
2042                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",\r
2043                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",\r
2044                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",\r
2045                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"\r
2046                 };\r
2047 \r
2048         // Raw entities\r
2049         baseEntities = {\r
2050                 '\"' : '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code\r
2051                 "'" : '&#39;',\r
2052                 '<' : '&lt;',\r
2053                 '>' : '&gt;',\r
2054                 '&' : '&amp;'\r
2055         };\r
2056 \r
2057         // Reverse lookup table for raw entities\r
2058         reverseEntities = {\r
2059                 '&lt;' : '<',\r
2060                 '&gt;' : '>',\r
2061                 '&amp;' : '&',\r
2062                 '&quot;' : '"',\r
2063                 '&apos;' : "'"\r
2064         };\r
2065 \r
2066         // Decodes text by using the browser\r
2067         function nativeDecode(text) {\r
2068                 var elm;\r
2069 \r
2070                 elm = document.createElement("div");\r
2071                 elm.innerHTML = text;\r
2072 \r
2073                 return elm.textContent || elm.innerText || text;\r
2074         };\r
2075 \r
2076         // Build a two way lookup table for the entities\r
2077         function buildEntitiesLookup(items, radix) {\r
2078                 var i, chr, entity, lookup = {};\r
2079 \r
2080                 if (items) {\r
2081                         items = items.split(',');\r
2082                         radix = radix || 10;\r
2083 \r
2084                         // Build entities lookup table\r
2085                         for (i = 0; i < items.length; i += 2) {\r
2086                                 chr = String.fromCharCode(parseInt(items[i], radix));\r
2087 \r
2088                                 // Only add non base entities\r
2089                                 if (!baseEntities[chr]) {\r
2090                                         entity = '&' + items[i + 1] + ';';\r
2091                                         lookup[chr] = entity;\r
2092                                         lookup[entity] = chr;\r
2093                                 }\r
2094                         }\r
2095 \r
2096                         return lookup;\r
2097                 }\r
2098         };\r
2099 \r
2100         // Unpack entities lookup where the numbers are in radix 32 to reduce the size\r
2101         namedEntities = buildEntitiesLookup(\r
2102                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +\r
2103                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +\r
2104                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +\r
2105                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +\r
2106                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +\r
2107                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +\r
2108                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +\r
2109                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +\r
2110                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +\r
2111                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +\r
2112                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +\r
2113                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +\r
2114                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +\r
2115                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +\r
2116                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +\r
2117                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +\r
2118                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +\r
2119                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +\r
2120                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +\r
2121                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +\r
2122                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +\r
2123                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +\r
2124                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +\r
2125                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +\r
2126                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);\r
2127 \r
2128         tinymce.html = tinymce.html || {};\r
2129 \r
2130         tinymce.html.Entities = {\r
2131                 encodeRaw : function(text, attr) {\r
2132                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
2133                                 return baseEntities[chr] || chr;\r
2134                         });\r
2135                 },\r
2136 \r
2137                 encodeAllRaw : function(text) {\r
2138                         return ('' + text).replace(rawCharsRegExp, function(chr) {\r
2139                                 return baseEntities[chr] || chr;\r
2140                         });\r
2141                 },\r
2142 \r
2143                 encodeNumeric : function(text, attr) {\r
2144                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
2145                                 // Multi byte sequence convert it to a single entity\r
2146                                 if (chr.length > 1)\r
2147                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';\r
2148 \r
2149                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';\r
2150                         });\r
2151                 },\r
2152 \r
2153                 encodeNamed : function(text, attr, entities) {\r
2154                         entities = entities || namedEntities;\r
2155 \r
2156                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
2157                                 return baseEntities[chr] || entities[chr] || chr;\r
2158                         });\r
2159                 },\r
2160 \r
2161                 getEncodeFunc : function(name, entities) {\r
2162                         var Entities = tinymce.html.Entities;\r
2163 \r
2164                         entities = buildEntitiesLookup(entities) || namedEntities;\r
2165 \r
2166                         function encodeNamedAndNumeric(text, attr) {\r
2167                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
2168                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;\r
2169                                 });\r
2170                         };\r
2171 \r
2172                         function encodeCustomNamed(text, attr) {\r
2173                                 return Entities.encodeNamed(text, attr, entities);\r
2174                         };\r
2175 \r
2176                         // Replace + with , to be compatible with previous TinyMCE versions\r
2177                         name = tinymce.makeMap(name.replace(/\+/g, ','));\r
2178 \r
2179                         // Named and numeric encoder\r
2180                         if (name.named && name.numeric)\r
2181                                 return encodeNamedAndNumeric;\r
2182 \r
2183                         // Named encoder\r
2184                         if (name.named) {\r
2185                                 // Custom names\r
2186                                 if (entities)\r
2187                                         return encodeCustomNamed;\r
2188 \r
2189                                 return Entities.encodeNamed;\r
2190                         }\r
2191 \r
2192                         // Numeric\r
2193                         if (name.numeric)\r
2194                                 return Entities.encodeNumeric;\r
2195 \r
2196                         // Raw encoder\r
2197                         return Entities.encodeRaw;\r
2198                 },\r
2199 \r
2200                 decode : function(text) {\r
2201                         return text.replace(entityRegExp, function(all, numeric, value) {\r
2202                                 if (numeric) {\r
2203                                         value = parseInt(value, numeric.length === 2 ? 16 : 10);\r
2204 \r
2205                                         // Support upper UTF\r
2206                                         if (value > 0xFFFF) {\r
2207                                                 value -= 0x10000;\r
2208 \r
2209                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));\r
2210                                         } else\r
2211                                                 return asciiMap[value] || String.fromCharCode(value);\r
2212                                 }\r
2213 \r
2214                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);\r
2215                         });\r
2216                 }\r
2217         };\r
2218 })(tinymce);\r
2219 \r
2220 tinymce.html.Styles = function(settings, schema) {\r
2221         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,\r
2222                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,\r
2223                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,\r
2224                 trimRightRegExp = /\s+$/,\r
2225                 urlColorRegExp = /rgb/,\r
2226                 undef, i, encodingLookup = {}, encodingItems;\r
2227 \r
2228         settings = settings || {};\r
2229 \r
2230         encodingItems = '\\" \\\' \\; \\: ; : \uFEFF'.split(' ');\r
2231         for (i = 0; i < encodingItems.length; i++) {\r
2232                 encodingLookup[encodingItems[i]] = '\uFEFF' + i;\r
2233                 encodingLookup['\uFEFF' + i] = encodingItems[i];\r
2234         }\r
2235 \r
2236         function toHex(match, r, g, b) {\r
2237                 function hex(val) {\r
2238                         val = parseInt(val).toString(16);\r
2239 \r
2240                         return val.length > 1 ? val : '0' + val; // 0 -> 00\r
2241                 };\r
2242 \r
2243                 return '#' + hex(r) + hex(g) + hex(b);\r
2244         };\r
2245 \r
2246         return {\r
2247                 toHex : function(color) {\r
2248                         return color.replace(rgbRegExp, toHex);\r
2249                 },\r
2250 \r
2251                 parse : function(css) {\r
2252                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;\r
2253 \r
2254                         function compress(prefix, suffix) {\r
2255                                 var top, right, bottom, left;\r
2256 \r
2257                                 // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>\r
2258                                 // So lets asume it shouldn't be there\r
2259                                 if (styles['border-image'] === 'none') {\r
2260                                         delete styles['border-image'];\r
2261                                 }\r
2262 \r
2263                                 // Get values and check it it needs compressing\r
2264                                 top = styles[prefix + '-top' + suffix];\r
2265                                 if (!top)\r
2266                                         return;\r
2267 \r
2268                                 right = styles[prefix + '-right' + suffix];\r
2269                                 if (top != right)\r
2270                                         return;\r
2271 \r
2272                                 bottom = styles[prefix + '-bottom' + suffix];\r
2273                                 if (right != bottom)\r
2274                                         return;\r
2275 \r
2276                                 left = styles[prefix + '-left' + suffix];\r
2277                                 if (bottom != left)\r
2278                                         return;\r
2279 \r
2280                                 // Compress\r
2281                                 styles[prefix + suffix] = left;\r
2282                                 delete styles[prefix + '-top' + suffix];\r
2283                                 delete styles[prefix + '-right' + suffix];\r
2284                                 delete styles[prefix + '-bottom' + suffix];\r
2285                                 delete styles[prefix + '-left' + suffix];\r
2286                         };\r
2287 \r
2288                         function canCompress(key) {\r
2289                                 var value = styles[key], i;\r
2290 \r
2291                                 if (!value || value.indexOf(' ') < 0)\r
2292                                         return;\r
2293 \r
2294                                 value = value.split(' ');\r
2295                                 i = value.length;\r
2296                                 while (i--) {\r
2297                                         if (value[i] !== value[0])\r
2298                                                 return false;\r
2299                                 }\r
2300 \r
2301                                 styles[key] = value[0];\r
2302 \r
2303                                 return true;\r
2304                         };\r
2305 \r
2306                         function compress2(target, a, b, c) {\r
2307                                 if (!canCompress(a))\r
2308                                         return;\r
2309 \r
2310                                 if (!canCompress(b))\r
2311                                         return;\r
2312 \r
2313                                 if (!canCompress(c))\r
2314                                         return;\r
2315 \r
2316                                 // Compress\r
2317                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];\r
2318                                 delete styles[a];\r
2319                                 delete styles[b];\r
2320                                 delete styles[c];\r
2321                         };\r
2322 \r
2323                         // Encodes the specified string by replacing all \" \' ; : with _<num>\r
2324                         function encode(str) {\r
2325                                 isEncoded = true;\r
2326 \r
2327                                 return encodingLookup[str];\r
2328                         };\r
2329 \r
2330                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc\r
2331                         // It will also decode the \" \' if keep_slashes is set to fale or omitted\r
2332                         function decode(str, keep_slashes) {\r
2333                                 if (isEncoded) {\r
2334                                         str = str.replace(/\uFEFF[0-9]/g, function(str) {\r
2335                                                 return encodingLookup[str];\r
2336                                         });\r
2337                                 }\r
2338 \r
2339                                 if (!keep_slashes)\r
2340                                         str = str.replace(/\\([\'\";:])/g, "$1");\r
2341 \r
2342                                 return str;\r
2343                         };\r
2344 \r
2345                         function processUrl(match, url, url2, url3, str, str2) {\r
2346                                 str = str || str2;\r
2347 \r
2348                                 if (str) {\r
2349                                         str = decode(str);\r
2350 \r
2351                                         // Force strings into single quote format\r
2352                                         return "'" + str.replace(/\'/g, "\\'") + "'";\r
2353                                 }\r
2354 \r
2355                                 url = decode(url || url2 || url3);\r
2356 \r
2357                                 // Convert the URL to relative/absolute depending on config\r
2358                                 if (urlConverter)\r
2359                                         url = urlConverter.call(urlConverterScope, url, 'style');\r
2360 \r
2361                                 // Output new URL format\r
2362                                 return "url('" + url.replace(/\'/g, "\\'") + "')";\r
2363                         };\r
2364 \r
2365                         if (css) {\r
2366                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing\r
2367                                 css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {\r
2368                                         return str.replace(/[;:]/g, encode);\r
2369                                 });\r
2370 \r
2371                                 // Parse styles\r
2372                                 while (matches = styleRegExp.exec(css)) {\r
2373                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();\r
2374                                         value = matches[2].replace(trimRightRegExp, '');\r
2375 \r
2376                                         if (name && value.length > 0) {\r
2377                                                 // Opera will produce 700 instead of bold in their style values\r
2378                                                 if (name === 'font-weight' && value === '700')\r
2379                                                         value = 'bold';\r
2380                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED\r
2381                                                         value = value.toLowerCase();            \r
2382 \r
2383                                                 // Convert RGB colors to HEX\r
2384                                                 value = value.replace(rgbRegExp, toHex);\r
2385 \r
2386                                                 // Convert URLs and force them into url('value') format\r
2387                                                 value = value.replace(urlOrStrRegExp, processUrl);\r
2388                                                 styles[name] = isEncoded ? decode(value, true) : value;\r
2389                                         }\r
2390 \r
2391                                         styleRegExp.lastIndex = matches.index + matches[0].length;\r
2392                                 }\r
2393 \r
2394                                 // Compress the styles to reduce it's size for example IE will expand styles\r
2395                                 compress("border", "");\r
2396                                 compress("border", "-width");\r
2397                                 compress("border", "-color");\r
2398                                 compress("border", "-style");\r
2399                                 compress("padding", "");\r
2400                                 compress("margin", "");\r
2401                                 compress2('border', 'border-width', 'border-style', 'border-color');\r
2402 \r
2403                                 // Remove pointless border, IE produces these\r
2404                                 if (styles.border === 'medium none')\r
2405                                         delete styles.border;\r
2406                         }\r
2407 \r
2408                         return styles;\r
2409                 },\r
2410 \r
2411                 serialize : function(styles, element_name) {\r
2412                         var css = '', name, value;\r
2413 \r
2414                         function serializeStyles(name) {\r
2415                                 var styleList, i, l, value;\r
2416 \r
2417                                 styleList = schema.styles[name];\r
2418                                 if (styleList) {\r
2419                                         for (i = 0, l = styleList.length; i < l; i++) {\r
2420                                                 name = styleList[i];\r
2421                                                 value = styles[name];\r
2422 \r
2423                                                 if (value !== undef && value.length > 0)\r
2424                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
2425                                         }\r
2426                                 }\r
2427                         };\r
2428 \r
2429                         // Serialize styles according to schema\r
2430                         if (element_name && schema && schema.styles) {\r
2431                                 // Serialize global styles and element specific styles\r
2432                                 serializeStyles('*');\r
2433                                 serializeStyles(element_name);\r
2434                         } else {\r
2435                                 // Output the styles in the order they are inside the object\r
2436                                 for (name in styles) {\r
2437                                         value = styles[name];\r
2438 \r
2439                                         if (value !== undef && value.length > 0)\r
2440                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
2441                                 }\r
2442                         }\r
2443 \r
2444                         return css;\r
2445                 }\r
2446         };\r
2447 };\r
2448 \r
2449 (function(tinymce) {\r
2450         var mapCache = {}, makeMap = tinymce.makeMap, each = tinymce.each;\r
2451 \r
2452         function split(str, delim) {\r
2453                 return str.split(delim || ',');\r
2454         };\r
2455 \r
2456         function unpack(lookup, data) {\r
2457                 var key, elements = {};\r
2458 \r
2459                 function replace(value) {\r
2460                         return value.replace(/[A-Z]+/g, function(key) {\r
2461                                 return replace(lookup[key]);\r
2462                         });\r
2463                 };\r
2464 \r
2465                 // Unpack lookup\r
2466                 for (key in lookup) {\r
2467                         if (lookup.hasOwnProperty(key))\r
2468                                 lookup[key] = replace(lookup[key]);\r
2469                 }\r
2470 \r
2471                 // Unpack and parse data into object map\r
2472                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {\r
2473                         attributes = split(attributes, '|');\r
2474 \r
2475                         elements[name] = {\r
2476                                 attributes : makeMap(attributes),\r
2477                                 attributesOrder : attributes,\r
2478                                 children : makeMap(children, '|', {'#comment' : {}})\r
2479                         }\r
2480                 });\r
2481 \r
2482                 return elements;\r
2483         };\r
2484 \r
2485         function getHTML5() {\r
2486                 var html5 = mapCache.html5;\r
2487 \r
2488                 if (!html5) {\r
2489                         html5 = mapCache.html5 = unpack({\r
2490                                         A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',\r
2491                                         B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|' +\r
2492                                                 'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr',\r
2493                                         C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|' +\r
2494                                                 'figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|' +\r
2495                                                 'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'\r
2496                                 }, 'html[A|manifest][body|head]' +\r
2497                                         'head[A][base|command|link|meta|noscript|script|style|title]' +\r
2498                                         'title[A][#]' +\r
2499                                         'base[A|href|target][]' +\r
2500                                         'link[A|href|rel|media|type|sizes][]' +\r
2501                                         'meta[A|http-equiv|name|content|charset][]' +\r
2502                                         'style[A|type|media|scoped][#]' +\r
2503                                         'script[A|charset|type|src|defer|async][#]' +\r
2504                                         'noscript[A][C]' +\r
2505                                         'body[A][C]' +\r
2506                                         'section[A][C]' +\r
2507                                         'nav[A][C]' +\r
2508                                         'article[A][C]' +\r
2509                                         'aside[A][C]' +\r
2510                                         'h1[A][B]' +\r
2511                                         'h2[A][B]' +\r
2512                                         'h3[A][B]' +\r
2513                                         'h4[A][B]' +\r
2514                                         'h5[A][B]' +\r
2515                                         'h6[A][B]' +\r
2516                                         'hgroup[A][h1|h2|h3|h4|h5|h6]' +\r
2517                                         'header[A][C]' +\r
2518                                         'footer[A][C]' +\r
2519                                         'address[A][C]' +\r
2520                                         'p[A][B]' +\r
2521                                         'br[A][]' +\r
2522                                         'pre[A][B]' +\r
2523                                         'dialog[A][dd|dt]' +\r
2524                                         'blockquote[A|cite][C]' +\r
2525                                         'ol[A|start|reversed][li]' +\r
2526                                         'ul[A][li]' +\r
2527                                         'li[A|value][C]' +\r
2528                                         'dl[A][dd|dt]' +\r
2529                                         'dt[A][B]' +\r
2530                                         'dd[A][C]' +\r
2531                                         'a[A|href|target|ping|rel|media|type][B]' +\r
2532                                         'em[A][B]' +\r
2533                                         'strong[A][B]' +\r
2534                                         'small[A][B]' +\r
2535                                         'cite[A][B]' +\r
2536                                         'q[A|cite][B]' +\r
2537                                         'dfn[A][B]' +\r
2538                                         'abbr[A][B]' +\r
2539                                         'code[A][B]' +\r
2540                                         'var[A][B]' +\r
2541                                         'samp[A][B]' +\r
2542                                         'kbd[A][B]' +\r
2543                                         'sub[A][B]' +\r
2544                                         'sup[A][B]' +\r
2545                                         'i[A][B]' +\r
2546                                         'b[A][B]' +\r
2547                                         'mark[A][B]' +\r
2548                                         'progress[A|value|max][B]' +\r
2549                                         'meter[A|value|min|max|low|high|optimum][B]' +\r
2550                                         'time[A|datetime][B]' +\r
2551                                         'ruby[A][B|rt|rp]' +\r
2552                                         'rt[A][B]' +\r
2553                                         'rp[A][B]' +\r
2554                                         'bdo[A][B]' +\r
2555                                         'span[A][B]' +\r
2556                                         'ins[A|cite|datetime][B]' +\r
2557                                         'del[A|cite|datetime][B]' +\r
2558                                         'figure[A][C|legend|figcaption]' +\r
2559                                         'figcaption[A][C]' +\r
2560                                         'img[A|alt|src|height|width|usemap|ismap][]' +\r
2561                                         'iframe[A|name|src|height|width|sandbox|seamless][]' +\r
2562                                         'embed[A|src|height|width|type][]' +\r
2563                                         'object[A|data|type|height|width|usemap|name|form|classid][param]' +\r
2564                                         'param[A|name|value][]' +\r
2565                                         'details[A|open][C|legend]' +\r
2566                                         'command[A|type|label|icon|disabled|checked|radiogroup][]' +\r
2567                                         'menu[A|type|label][C|li]' +\r
2568                                         'legend[A][C|B]' +\r
2569                                         'div[A][C]' +\r
2570                                         'source[A|src|type|media][]' +\r
2571                                         'audio[A|src|autobuffer|autoplay|loop|controls][source]' +\r
2572                                         'video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]' +\r
2573                                         'hr[A][]' +\r
2574                                         'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +\r
2575                                         'fieldset[A|disabled|form|name][C|legend]' +\r
2576                                         'label[A|form|for][B]' +\r
2577                                         'input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|' +\r
2578                                                 'multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]' +\r
2579                                         'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +\r
2580                                         'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +\r
2581                                         'datalist[A][B|option]' +\r
2582                                         'optgroup[A|disabled|label][option]' +\r
2583                                         'option[A|disabled|selected|label|value][]' +\r
2584                                         'textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]' +\r
2585                                         'keygen[A|autofocus|challenge|disabled|form|keytype|name][]' +\r
2586                                         'output[A|for|form|name][B]' +\r
2587                                         'canvas[A|width|height][]' +\r
2588                                         'map[A|name][B|C]' +\r
2589                                         'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +\r
2590                                         'mathml[A][]' +\r
2591                                         'svg[A][]' +\r
2592                                         'table[A|border][caption|colgroup|thead|tfoot|tbody|tr]' +\r
2593                                         'caption[A][C]' +\r
2594                                         'colgroup[A|span][col]' +\r
2595                                         'col[A|span][]' +\r
2596                                         'thead[A][tr]' +\r
2597                                         'tfoot[A][tr]' +\r
2598                                         'tbody[A][tr]' +\r
2599                                         'tr[A][th|td]' +\r
2600                                         'th[A|headers|rowspan|colspan|scope][B]' +\r
2601                                         'td[A|headers|rowspan|colspan][C]' +\r
2602                                         'wbr[A][]'\r
2603                         );\r
2604                 }\r
2605 \r
2606                 return html5;\r
2607         };\r
2608 \r
2609         function getHTML4() {\r
2610                 var html4 = mapCache.html4;\r
2611 \r
2612                 if (!html4) {\r
2613                         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size\r
2614                         html4 = mapCache.html4 = unpack({\r
2615                                 Z : 'H|K|N|O|P',\r
2616                                 Y : 'X|form|R|Q',\r
2617                                 ZG : 'E|span|width|align|char|charoff|valign',\r
2618                                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
2619                                 ZF : 'E|align|char|charoff|valign',\r
2620                                 W : 'pre|hr|blockquote|address|center|noframes',\r
2621                                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',\r
2622                                 ZD : '[E][S]',\r
2623                                 U : 'ul|ol|dl|menu|dir',\r
2624                                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
2625                                 T : 'h1|h2|h3|h4|h5|h6',\r
2626                                 ZB : 'X|S|Q',\r
2627                                 S : 'R|P',\r
2628                                 ZA : 'a|G|J|M|O|P',\r
2629                                 R : 'a|H|K|N|O',\r
2630                                 Q : 'noscript|P',\r
2631                                 P : 'ins|del|script',\r
2632                                 O : 'input|select|textarea|label|button',\r
2633                                 N : 'M|L',\r
2634                                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
2635                                 L : 'sub|sup',\r
2636                                 K : 'J|I',\r
2637                                 J : 'tt|i|b|u|s|strike',\r
2638                                 I : 'big|small|font|basefont',\r
2639                                 H : 'G|F',\r
2640                                 G : 'br|span|bdo',\r
2641                                 F : 'object|applet|img|map|iframe',\r
2642                                 E : 'A|B|C',\r
2643                                 D : 'accesskey|tabindex|onfocus|onblur',\r
2644                                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',\r
2645                                 B : 'lang|xml:lang|dir',\r
2646                                 A : 'id|class|style|title'\r
2647                         }, 'script[id|charset|type|language|src|defer|xml:space][]' + \r
2648                                 'style[B|id|type|media|title|xml:space][]' + \r
2649                                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + \r
2650                                 'param[id|name|value|valuetype|type][]' + \r
2651                                 'p[E|align][#|S]' + \r
2652                                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + \r
2653                                 'br[A|clear][]' + \r
2654                                 'span[E][#|S]' + \r
2655                                 'bdo[A|C|B][#|S]' + \r
2656                                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + \r
2657                                 'h1[E|align][#|S]' + \r
2658                                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + \r
2659                                 'map[B|C|A|name][X|form|Q|area]' + \r
2660                                 'h2[E|align][#|S]' + \r
2661                                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + \r
2662                                 'h3[E|align][#|S]' + \r
2663                                 'tt[E][#|S]' + \r
2664                                 'i[E][#|S]' + \r
2665                                 'b[E][#|S]' + \r
2666                                 'u[E][#|S]' + \r
2667                                 's[E][#|S]' + \r
2668                                 'strike[E][#|S]' + \r
2669                                 'big[E][#|S]' + \r
2670                                 'small[E][#|S]' + \r
2671                                 'font[A|B|size|color|face][#|S]' + \r
2672                                 'basefont[id|size|color|face][]' + \r
2673                                 'em[E][#|S]' + \r
2674                                 'strong[E][#|S]' + \r
2675                                 'dfn[E][#|S]' + \r
2676                                 'code[E][#|S]' + \r
2677                                 'q[E|cite][#|S]' + \r
2678                                 'samp[E][#|S]' + \r
2679                                 'kbd[E][#|S]' + \r
2680                                 'var[E][#|S]' + \r
2681                                 'cite[E][#|S]' + \r
2682                                 'abbr[E][#|S]' + \r
2683                                 'acronym[E][#|S]' + \r
2684                                 'sub[E][#|S]' + \r
2685                                 'sup[E][#|S]' + \r
2686                                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + \r
2687                                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + \r
2688                                 'optgroup[E|disabled|label][option]' + \r
2689                                 'option[E|selected|disabled|label|value][]' + \r
2690                                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + \r
2691                                 'label[E|for|accesskey|onfocus|onblur][#|S]' + \r
2692                                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
2693                                 'h4[E|align][#|S]' + \r
2694                                 'ins[E|cite|datetime][#|Y]' + \r
2695                                 'h5[E|align][#|S]' + \r
2696                                 'del[E|cite|datetime][#|Y]' + \r
2697                                 'h6[E|align][#|S]' + \r
2698                                 'div[E|align][#|Y]' + \r
2699                                 'ul[E|type|compact][li]' + \r
2700                                 'li[E|type|value][#|Y]' + \r
2701                                 'ol[E|type|compact|start][li]' + \r
2702                                 'dl[E|compact][dt|dd]' + \r
2703                                 'dt[E][#|S]' + \r
2704                                 'dd[E][#|Y]' + \r
2705                                 'menu[E|compact][li]' + \r
2706                                 'dir[E|compact][li]' + \r
2707                                 'pre[E|width|xml:space][#|ZA]' + \r
2708                                 'hr[E|align|noshade|size|width][]' + \r
2709                                 'blockquote[E|cite][#|Y]' + \r
2710                                 'address[E][#|S|p]' + \r
2711                                 'center[E][#|Y]' + \r
2712                                 'noframes[E][#|Y]' + \r
2713                                 'isindex[A|B|prompt][]' + \r
2714                                 'fieldset[E][#|legend|Y]' + \r
2715                                 'legend[E|accesskey|align][#|S]' + \r
2716                                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
2717                                 'caption[E|align][#|S]' + \r
2718                                 'col[ZG][]' + \r
2719                                 'colgroup[ZG][col]' + \r
2720                                 'thead[ZF][tr]' + \r
2721                                 'tr[ZF|bgcolor][th|td]' + \r
2722                                 'th[E|ZE][#|Y]' + \r
2723                                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + \r
2724                                 'noscript[E][#|Y]' + \r
2725                                 'td[E|ZE][#|Y]' + \r
2726                                 'tfoot[ZF][tr]' + \r
2727                                 'tbody[ZF][tr]' + \r
2728                                 'area[E|D|shape|coords|href|nohref|alt|target][]' + \r
2729                                 'base[id|href|target][]' + \r
2730                                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'\r
2731                         );\r
2732                 }\r
2733 \r
2734                 return html4;\r
2735         };\r
2736 \r
2737         tinymce.html.Schema = function(settings) {\r
2738                 var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;\r
2739                 var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, blockElementsMap, nonEmptyElementsMap, customElementsMap = {};\r
2740 \r
2741                 // Creates an lookup table map object for the specified option or the default value\r
2742                 function createLookupTable(option, default_value, extend) {\r
2743                         var value = settings[option];\r
2744 \r
2745                         if (!value) {\r
2746                                 // Get cached default map or make it if needed\r
2747                                 value = mapCache[option];\r
2748 \r
2749                                 if (!value) {\r
2750                                         value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));\r
2751                                         value = tinymce.extend(value, extend);\r
2752 \r
2753                                         mapCache[option] = value;\r
2754                                 }\r
2755                         } else {\r
2756                                 // Create custom map\r
2757                                 value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));\r
2758                         }\r
2759 \r
2760                         return value;\r
2761                 };\r
2762 \r
2763                 settings = settings || {};\r
2764                 schemaItems = settings.schema == "html5" ? getHTML5() : getHTML4();\r
2765 \r
2766                 // Allow all elements and attributes if verify_html is set to false\r
2767                 if (settings.verify_html === false)\r
2768                         settings.valid_elements = '*[*]';\r
2769 \r
2770                 // Build styles list\r
2771                 if (settings.valid_styles) {\r
2772                         validStyles = {};\r
2773 \r
2774                         // Convert styles into a rule list\r
2775                         each(settings.valid_styles, function(value, key) {\r
2776                                 validStyles[key] = tinymce.explode(value);\r
2777                         });\r
2778                 }\r
2779 \r
2780                 // Setup map objects\r
2781                 whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea');\r
2782                 selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');\r
2783                 shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source wbr');\r
2784                 boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');\r
2785                 nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);\r
2786                 textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + \r
2787                                                 'blockquote center dir fieldset header footer article section hgroup aside nav figure');\r
2788                 blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + \r
2789                                                 'th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup', textBlockElementsMap);\r
2790 \r
2791                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.\r
2792                 function patternToRegExp(str) {\r
2793                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');\r
2794                 };\r
2795 \r
2796                 // Parses the specified valid_elements string and adds to the current rules\r
2797                 // This function is a bit hard to read since it's heavily optimized for speed\r
2798                 function addValidElements(valid_elements) {\r
2799                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,\r
2800                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,\r
2801                                 elementRuleRegExp = /^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,\r
2802                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,\r
2803                                 hasPatternsRegExp = /[*?+]/;\r
2804 \r
2805                         if (valid_elements) {\r
2806                                 // Split valid elements into an array with rules\r
2807                                 valid_elements = split(valid_elements);\r
2808 \r
2809                                 if (elements['@']) {\r
2810                                         globalAttributes = elements['@'].attributes;\r
2811                                         globalAttributesOrder = elements['@'].attributesOrder;\r
2812                                 }\r
2813 \r
2814                                 // Loop all rules\r
2815                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {\r
2816                                         // Parse element rule\r
2817                                         matches = elementRuleRegExp.exec(valid_elements[ei]);\r
2818                                         if (matches) {\r
2819                                                 // Setup local names for matches\r
2820                                                 prefix = matches[1];\r
2821                                                 elementName = matches[2];\r
2822                                                 outputName = matches[3];\r
2823                                                 attrData = matches[4];\r
2824 \r
2825                                                 // Create new attributes and attributesOrder\r
2826                                                 attributes = {};\r
2827                                                 attributesOrder = [];\r
2828 \r
2829                                                 // Create the new element\r
2830                                                 element = {\r
2831                                                         attributes : attributes,\r
2832                                                         attributesOrder : attributesOrder\r
2833                                                 };\r
2834 \r
2835                                                 // Padd empty elements prefix\r
2836                                                 if (prefix === '#')\r
2837                                                         element.paddEmpty = true;\r
2838 \r
2839                                                 // Remove empty elements prefix\r
2840                                                 if (prefix === '-')\r
2841                                                         element.removeEmpty = true;\r
2842 \r
2843                                                 // Copy attributes from global rule into current rule\r
2844                                                 if (globalAttributes) {\r
2845                                                         for (key in globalAttributes)\r
2846                                                                 attributes[key] = globalAttributes[key];\r
2847 \r
2848                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);\r
2849                                                 }\r
2850 \r
2851                                                 // Attributes defined\r
2852                                                 if (attrData) {\r
2853                                                         attrData = split(attrData, '|');\r
2854                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {\r
2855                                                                 matches = attrRuleRegExp.exec(attrData[ai]);\r
2856                                                                 if (matches) {\r
2857                                                                         attr = {};\r
2858                                                                         attrType = matches[1];\r
2859                                                                         attrName = matches[2].replace(/::/g, ':');\r
2860                                                                         prefix = matches[3];\r
2861                                                                         value = matches[4];\r
2862 \r
2863                                                                         // Required\r
2864                                                                         if (attrType === '!') {\r
2865                                                                                 element.attributesRequired = element.attributesRequired || [];\r
2866                                                                                 element.attributesRequired.push(attrName);\r
2867                                                                                 attr.required = true;\r
2868                                                                         }\r
2869 \r
2870                                                                         // Denied from global\r
2871                                                                         if (attrType === '-') {\r
2872                                                                                 delete attributes[attrName];\r
2873                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);\r
2874                                                                                 continue;\r
2875                                                                         }\r
2876 \r
2877                                                                         // Default value\r
2878                                                                         if (prefix) {\r
2879                                                                                 // Default value\r
2880                                                                                 if (prefix === '=') {\r
2881                                                                                         element.attributesDefault = element.attributesDefault || [];\r
2882                                                                                         element.attributesDefault.push({name: attrName, value: value});\r
2883                                                                                         attr.defaultValue = value;\r
2884                                                                                 }\r
2885 \r
2886                                                                                 // Forced value\r
2887                                                                                 if (prefix === ':') {\r
2888                                                                                         element.attributesForced = element.attributesForced || [];\r
2889                                                                                         element.attributesForced.push({name: attrName, value: value});\r
2890                                                                                         attr.forcedValue = value;\r
2891                                                                                 }\r
2892 \r
2893                                                                                 // Required values\r
2894                                                                                 if (prefix === '<')\r
2895                                                                                         attr.validValues = makeMap(value, '?');\r
2896                                                                         }\r
2897 \r
2898                                                                         // Check for attribute patterns\r
2899                                                                         if (hasPatternsRegExp.test(attrName)) {\r
2900                                                                                 element.attributePatterns = element.attributePatterns || [];\r
2901                                                                                 attr.pattern = patternToRegExp(attrName);\r
2902                                                                                 element.attributePatterns.push(attr);\r
2903                                                                         } else {\r
2904                                                                                 // Add attribute to order list if it doesn't already exist\r
2905                                                                                 if (!attributes[attrName])\r
2906                                                                                         attributesOrder.push(attrName);\r
2907 \r
2908                                                                                 attributes[attrName] = attr;\r
2909                                                                         }\r
2910                                                                 }\r
2911                                                         }\r
2912                                                 }\r
2913 \r
2914                                                 // Global rule, store away these for later usage\r
2915                                                 if (!globalAttributes && elementName == '@') {\r
2916                                                         globalAttributes = attributes;\r
2917                                                         globalAttributesOrder = attributesOrder;\r
2918                                                 }\r
2919 \r
2920                                                 // Handle substitute elements such as b/strong\r
2921                                                 if (outputName) {\r
2922                                                         element.outputName = elementName;\r
2923                                                         elements[outputName] = element;\r
2924                                                 }\r
2925 \r
2926                                                 // Add pattern or exact element\r
2927                                                 if (hasPatternsRegExp.test(elementName)) {\r
2928                                                         element.pattern = patternToRegExp(elementName);\r
2929                                                         patternElements.push(element);\r
2930                                                 } else\r
2931                                                         elements[elementName] = element;\r
2932                                         }\r
2933                                 }\r
2934                         }\r
2935                 };\r
2936 \r
2937                 function setValidElements(valid_elements) {\r
2938                         elements = {};\r
2939                         patternElements = [];\r
2940 \r
2941                         addValidElements(valid_elements);\r
2942 \r
2943                         each(schemaItems, function(element, name) {\r
2944                                 children[name] = element.children;\r
2945                         });\r
2946                 };\r
2947 \r
2948                 // Adds custom non HTML elements to the schema\r
2949                 function addCustomElements(custom_elements) {\r
2950                         var customElementRegExp = /^(~)?(.+)$/;\r
2951 \r
2952                         if (custom_elements) {\r
2953                                 each(split(custom_elements), function(rule) {\r
2954                                         var matches = customElementRegExp.exec(rule),\r
2955                                                 inline = matches[1] === '~',\r
2956                                                 cloneName = inline ? 'span' : 'div',\r
2957                                                 name = matches[2];\r
2958 \r
2959                                         children[name] = children[cloneName];\r
2960                                         customElementsMap[name] = cloneName;\r
2961 \r
2962                                         // If it's not marked as inline then add it to valid block elements\r
2963                                         if (!inline) {\r
2964                                                 blockElementsMap[name.toUpperCase()] = {};\r
2965                                                 blockElementsMap[name] = {};\r
2966                                         }\r
2967 \r
2968                                         // Add elements clone if needed\r
2969                                         if (!elements[name]) {\r
2970                                                 elements[name] = elements[cloneName];\r
2971                                         }\r
2972 \r
2973                                         // Add custom elements at span/div positions\r
2974                                         each(children, function(element, child) {\r
2975                                                 if (element[cloneName])\r
2976                                                         element[name] = element[cloneName];\r
2977                                         });\r
2978                                 });\r
2979                         }\r
2980                 };\r
2981 \r
2982                 // Adds valid children to the schema object\r
2983                 function addValidChildren(valid_children) {\r
2984                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;\r
2985 \r
2986                         if (valid_children) {\r
2987                                 each(split(valid_children), function(rule) {\r
2988                                         var matches = childRuleRegExp.exec(rule), parent, prefix;\r
2989 \r
2990                                         if (matches) {\r
2991                                                 prefix = matches[1];\r
2992 \r
2993                                                 // Add/remove items from default\r
2994                                                 if (prefix)\r
2995                                                         parent = children[matches[2]];\r
2996                                                 else\r
2997                                                         parent = children[matches[2]] = {'#comment' : {}};\r
2998 \r
2999                                                 parent = children[matches[2]];\r
3000 \r
3001                                                 each(split(matches[3], '|'), function(child) {\r
3002                                                         if (prefix === '-')\r
3003                                                                 delete parent[child];\r
3004                                                         else\r
3005                                                                 parent[child] = {};\r
3006                                                 });\r
3007                                         }\r
3008                                 });\r
3009                         }\r
3010                 };\r
3011 \r
3012                 function getElementRule(name) {\r
3013                         var element = elements[name], i;\r
3014 \r
3015                         // Exact match found\r
3016                         if (element)\r
3017                                 return element;\r
3018 \r
3019                         // No exact match then try the patterns\r
3020                         i = patternElements.length;\r
3021                         while (i--) {\r
3022                                 element = patternElements[i];\r
3023 \r
3024                                 if (element.pattern.test(name))\r
3025                                         return element;\r
3026                         }\r
3027                 };\r
3028 \r
3029                 if (!settings.valid_elements) {\r
3030                         // No valid elements defined then clone the elements from the schema spec\r
3031                         each(schemaItems, function(element, name) {\r
3032                                 elements[name] = {\r
3033                                         attributes : element.attributes,\r
3034                                         attributesOrder : element.attributesOrder\r
3035                                 };\r
3036 \r
3037                                 children[name] = element.children;\r
3038                         });\r
3039 \r
3040                         // Switch these on HTML4\r
3041                         if (settings.schema != "html5") {\r
3042                                 each(split('strong/b,em/i'), function(item) {\r
3043                                         item = split(item, '/');\r
3044                                         elements[item[1]].outputName = item[0];\r
3045                                 });\r
3046                         }\r
3047 \r
3048                         // Add default alt attribute for images\r
3049                         elements.img.attributesDefault = [{name: 'alt', value: ''}];\r
3050 \r
3051                         // Remove these if they are empty by default\r
3052                         each(split('ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i'), function(name) {\r
3053                                 if (elements[name]) {\r
3054                                         elements[name].removeEmpty = true;\r
3055                                 }\r
3056                         });\r
3057 \r
3058                         // Padd these by default\r
3059                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {\r
3060                                 elements[name].paddEmpty = true;\r
3061                         });\r
3062                 } else\r
3063                         setValidElements(settings.valid_elements);\r
3064 \r
3065                 addCustomElements(settings.custom_elements);\r
3066                 addValidChildren(settings.valid_children);\r
3067                 addValidElements(settings.extended_valid_elements);\r
3068 \r
3069                 // Todo: Remove this when we fix list handling to be valid\r
3070                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');\r
3071 \r
3072                 // Delete invalid elements\r
3073                 if (settings.invalid_elements) {\r
3074                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {\r
3075                                 if (elements[item])\r
3076                                         delete elements[item];\r
3077                         });\r
3078                 }\r
3079 \r
3080                 // If the user didn't allow span only allow internal spans\r
3081                 if (!getElementRule('span'))\r
3082                         addValidElements('span[!data-mce-type|*]');\r
3083 \r
3084                 self.children = children;\r
3085 \r
3086                 self.styles = validStyles;\r
3087 \r
3088                 self.getBoolAttrs = function() {\r
3089                         return boolAttrMap;\r
3090                 };\r
3091 \r
3092                 self.getBlockElements = function() {\r
3093                         return blockElementsMap;\r
3094                 };\r
3095 \r
3096                 self.getTextBlockElements = function() {\r
3097                         return textBlockElementsMap;\r
3098                 };\r
3099 \r
3100                 self.getShortEndedElements = function() {\r
3101                         return shortEndedElementsMap;\r
3102                 };\r
3103 \r
3104                 self.getSelfClosingElements = function() {\r
3105                         return selfClosingElementsMap;\r
3106                 };\r
3107 \r
3108                 self.getNonEmptyElements = function() {\r
3109                         return nonEmptyElementsMap;\r
3110                 };\r
3111 \r
3112                 self.getWhiteSpaceElements = function() {\r
3113                         return whiteSpaceElementsMap;\r
3114                 };\r
3115 \r
3116                 self.isValidChild = function(name, child) {\r
3117                         var parent = children[name];\r
3118 \r
3119                         return !!(parent && parent[child]);\r
3120                 };\r
3121 \r
3122                 self.isValid = function(name, attr) {\r
3123                         var attrPatterns, i, rule = getElementRule(name);\r
3124 \r
3125                         // Check if it's a valid element\r
3126                         if (rule) {\r
3127                                 if (attr) {\r
3128                                         // Check if attribute name exists\r
3129                                         if (rule.attributes[attr]) {\r
3130                                                 return true;\r
3131                                         }\r
3132 \r
3133                                         // Check if attribute matches a regexp pattern\r
3134                                         attrPatterns = rule.attributePatterns;\r
3135                                         if (attrPatterns) {\r
3136                                                 i = attrPatterns.length;\r
3137                                                 while (i--) {\r
3138                                                         if (attrPatterns[i].pattern.test(name)) {\r
3139                                                                 return true;\r
3140                                                         }\r
3141                                                 }\r
3142                                         }\r
3143                                 } else {\r
3144                                         return true;\r
3145                                 }\r
3146                         }\r
3147 \r
3148                         // No match\r
3149                         return false;\r
3150                 };\r
3151                 \r
3152                 self.getElementRule = getElementRule;\r
3153 \r
3154                 self.getCustomElements = function() {\r
3155                         return customElementsMap;\r
3156                 };\r
3157 \r
3158                 self.addValidElements = addValidElements;\r
3159 \r
3160                 self.setValidElements = setValidElements;\r
3161 \r
3162                 self.addCustomElements = addCustomElements;\r
3163 \r
3164                 self.addValidChildren = addValidChildren;\r
3165 \r
3166                 self.elements = elements;\r
3167         };\r
3168 })(tinymce);\r
3169 \r
3170 (function(tinymce) {\r
3171         tinymce.html.SaxParser = function(settings, schema) {\r
3172                 var self = this, noop = function() {};\r
3173 \r
3174                 settings = settings || {};\r
3175                 self.schema = schema = schema || new tinymce.html.Schema();\r
3176 \r
3177                 if (settings.fix_self_closing !== false)\r
3178                         settings.fix_self_closing = true;\r
3179 \r
3180                 // Add handler functions from settings and setup default handlers\r
3181                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {\r
3182                         if (name)\r
3183                                 self[name] = settings[name] || noop;\r
3184                 });\r
3185 \r
3186                 self.parse = function(html) {\r
3187                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,\r
3188                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,\r
3189                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,\r
3190                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;\r
3191 \r
3192                         function processEndTag(name) {\r
3193                                 var pos, i;\r
3194 \r
3195                                 // Find position of parent of the same type\r
3196                                 pos = stack.length;\r
3197                                 while (pos--) {\r
3198                                         if (stack[pos].name === name)\r
3199                                                 break;                                          \r
3200                                 }\r
3201 \r
3202                                 // Found parent\r
3203                                 if (pos >= 0) {\r
3204                                         // Close all the open elements\r
3205                                         for (i = stack.length - 1; i >= pos; i--) {\r
3206                                                 name = stack[i];\r
3207 \r
3208                                                 if (name.valid)\r
3209                                                         self.end(name.name);\r
3210                                         }\r
3211 \r
3212                                         // Remove the open elements from the stack\r
3213                                         stack.length = pos;\r
3214                                 }\r
3215                         };\r
3216 \r
3217                         function parseAttribute(match, name, value, val2, val3) {\r
3218                                 var attrRule, i;\r
3219 \r
3220                                 name = name.toLowerCase();\r
3221                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute\r
3222 \r
3223                                 // Validate name and value\r
3224                                 if (validate && !isInternalElement && name.indexOf('data-') !== 0) {\r
3225                                         attrRule = validAttributesMap[name];\r
3226 \r
3227                                         // Find rule by pattern matching\r
3228                                         if (!attrRule && validAttributePatterns) {\r
3229                                                 i = validAttributePatterns.length;\r
3230                                                 while (i--) {\r
3231                                                         attrRule = validAttributePatterns[i];\r
3232                                                         if (attrRule.pattern.test(name))\r
3233                                                                 break;\r
3234                                                 }\r
3235 \r
3236                                                 // No rule matched\r
3237                                                 if (i === -1)\r
3238                                                         attrRule = null;\r
3239                                         }\r
3240 \r
3241                                         // No attribute rule found\r
3242                                         if (!attrRule)\r
3243                                                 return;\r
3244 \r
3245                                         // Validate value\r
3246                                         if (attrRule.validValues && !(value in attrRule.validValues))\r
3247                                                 return;\r
3248                                 }\r
3249 \r
3250                                 // Add attribute to list and map\r
3251                                 attrList.map[name] = value;\r
3252                                 attrList.push({\r
3253                                         name: name,\r
3254                                         value: value\r
3255                                 });\r
3256                         };\r
3257 \r
3258                         // Precompile RegExps and map objects\r
3259                         tokenRegExp = new RegExp('<(?:' +\r
3260                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment\r
3261                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA\r
3262                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE\r
3263                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI\r
3264                                 '(?:\\/([^>]+)>)|' + // End element\r
3265                                 '(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element\r
3266                         ')', 'g');\r
3267 \r
3268                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;\r
3269                         specialElements = {\r
3270                                 'script' : /<\/script[^>]*>/gi,\r
3271                                 'style' : /<\/style[^>]*>/gi,\r
3272                                 'noscript' : /<\/noscript[^>]*>/gi\r
3273                         };\r
3274 \r
3275                         // Setup lookup tables for empty elements and boolean attributes\r
3276                         shortEndedElements = schema.getShortEndedElements();\r
3277                         selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();\r
3278                         fillAttrsMap = schema.getBoolAttrs();\r
3279                         validate = settings.validate;\r
3280                         removeInternalElements = settings.remove_internals;\r
3281                         fixSelfClosing = settings.fix_self_closing;\r
3282                         isIE = tinymce.isIE;\r
3283                         invalidPrefixRegExp = /^:/;\r
3284 \r
3285                         while (matches = tokenRegExp.exec(html)) {\r
3286                                 // Text\r
3287                                 if (index < matches.index)\r
3288                                         self.text(decode(html.substr(index, matches.index - index)));\r
3289 \r
3290                                 if (value = matches[6]) { // End element\r
3291                                         value = value.toLowerCase();\r
3292 \r
3293                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
3294                                         if (isIE && invalidPrefixRegExp.test(value))\r
3295                                                 value = value.substr(1);\r
3296 \r
3297                                         processEndTag(value);\r
3298                                 } else if (value = matches[7]) { // Start element\r
3299                                         value = value.toLowerCase();\r
3300 \r
3301                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
3302                                         if (isIE && invalidPrefixRegExp.test(value))\r
3303                                                 value = value.substr(1);\r
3304 \r
3305                                         isShortEnded = value in shortEndedElements;\r
3306 \r
3307                                         // Is self closing tag for example an <li> after an open <li>\r
3308                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)\r
3309                                                 processEndTag(value);\r
3310 \r
3311                                         // Validate element\r
3312                                         if (!validate || (elementRule = schema.getElementRule(value))) {\r
3313                                                 isValidElement = true;\r
3314 \r
3315                                                 // Grab attributes map and patters when validation is enabled\r
3316                                                 if (validate) {\r
3317                                                         validAttributesMap = elementRule.attributes;\r
3318                                                         validAttributePatterns = elementRule.attributePatterns;\r
3319                                                 }\r
3320 \r
3321                                                 // Parse attributes\r
3322                                                 if (attribsValue = matches[8]) {\r
3323                                                         isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element\r
3324 \r
3325                                                         // If the element has internal attributes then remove it if we are told to do so\r
3326                                                         if (isInternalElement && removeInternalElements)\r
3327                                                                 isValidElement = false;\r
3328 \r
3329                                                         attrList = [];\r
3330                                                         attrList.map = {};\r
3331 \r
3332                                                         attribsValue.replace(attrRegExp, parseAttribute);\r
3333                                                 } else {\r
3334                                                         attrList = [];\r
3335                                                         attrList.map = {};\r
3336                                                 }\r
3337 \r
3338                                                 // Process attributes if validation is enabled\r
3339                                                 if (validate && !isInternalElement) {\r
3340                                                         attributesRequired = elementRule.attributesRequired;\r
3341                                                         attributesDefault = elementRule.attributesDefault;\r
3342                                                         attributesForced = elementRule.attributesForced;\r
3343 \r
3344                                                         // Handle forced attributes\r
3345                                                         if (attributesForced) {\r
3346                                                                 i = attributesForced.length;\r
3347                                                                 while (i--) {\r
3348                                                                         attr = attributesForced[i];\r
3349                                                                         name = attr.name;\r
3350                                                                         attrValue = attr.value;\r
3351 \r
3352                                                                         if (attrValue === '{$uid}')\r
3353                                                                                 attrValue = 'mce_' + idCount++;\r
3354 \r
3355                                                                         attrList.map[name] = attrValue;\r
3356                                                                         attrList.push({name: name, value: attrValue});\r
3357                                                                 }\r
3358                                                         }\r
3359 \r
3360                                                         // Handle default attributes\r
3361                                                         if (attributesDefault) {\r
3362                                                                 i = attributesDefault.length;\r
3363                                                                 while (i--) {\r
3364                                                                         attr = attributesDefault[i];\r
3365                                                                         name = attr.name;\r
3366 \r
3367                                                                         if (!(name in attrList.map)) {\r
3368                                                                                 attrValue = attr.value;\r
3369 \r
3370                                                                                 if (attrValue === '{$uid}')\r
3371                                                                                         attrValue = 'mce_' + idCount++;\r
3372 \r
3373                                                                                 attrList.map[name] = attrValue;\r
3374                                                                                 attrList.push({name: name, value: attrValue});\r
3375                                                                         }\r
3376                                                                 }\r
3377                                                         }\r
3378 \r
3379                                                         // Handle required attributes\r
3380                                                         if (attributesRequired) {\r
3381                                                                 i = attributesRequired.length;\r
3382                                                                 while (i--) {\r
3383                                                                         if (attributesRequired[i] in attrList.map)\r
3384                                                                                 break;\r
3385                                                                 }\r
3386 \r
3387                                                                 // None of the required attributes where found\r
3388                                                                 if (i === -1)\r
3389                                                                         isValidElement = false;\r
3390                                                         }\r
3391 \r
3392                                                         // Invalidate element if it's marked as bogus\r
3393                                                         if (attrList.map['data-mce-bogus'])\r
3394                                                                 isValidElement = false;\r
3395                                                 }\r
3396 \r
3397                                                 if (isValidElement)\r
3398                                                         self.start(value, attrList, isShortEnded);\r
3399                                         } else\r
3400                                                 isValidElement = false;\r
3401 \r
3402                                         // Treat script, noscript and style a bit different since they may include code that looks like elements\r
3403                                         if (endRegExp = specialElements[value]) {\r
3404                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;\r
3405 \r
3406                                                 if (matches = endRegExp.exec(html)) {\r
3407                                                         if (isValidElement)\r
3408                                                                 text = html.substr(index, matches.index - index);\r
3409 \r
3410                                                         index = matches.index + matches[0].length;\r
3411                                                 } else {\r
3412                                                         text = html.substr(index);\r
3413                                                         index = html.length;\r
3414                                                 }\r
3415 \r
3416                                                 if (isValidElement && text.length > 0)\r
3417                                                         self.text(text, true);\r
3418 \r
3419                                                 if (isValidElement)\r
3420                                                         self.end(value);\r
3421 \r
3422                                                 tokenRegExp.lastIndex = index;\r
3423                                                 continue;\r
3424                                         }\r
3425 \r
3426                                         // Push value on to stack\r
3427                                         if (!isShortEnded) {\r
3428                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)\r
3429                                                         stack.push({name: value, valid: isValidElement});\r
3430                                                 else if (isValidElement)\r
3431                                                         self.end(value);\r
3432                                         }\r
3433                                 } else if (value = matches[1]) { // Comment\r
3434                                         self.comment(value);\r
3435                                 } else if (value = matches[2]) { // CDATA\r
3436                                         self.cdata(value);\r
3437                                 } else if (value = matches[3]) { // DOCTYPE\r
3438                                         self.doctype(value);\r
3439                                 } else if (value = matches[4]) { // PI\r
3440                                         self.pi(value, matches[5]);\r
3441                                 }\r
3442 \r
3443                                 index = matches.index + matches[0].length;\r
3444                         }\r
3445 \r
3446                         // Text\r
3447                         if (index < html.length)\r
3448                                 self.text(decode(html.substr(index)));\r
3449 \r
3450                         // Close any open elements\r
3451                         for (i = stack.length - 1; i >= 0; i--) {\r
3452                                 value = stack[i];\r
3453 \r
3454                                 if (value.valid)\r
3455                                         self.end(value.name);\r
3456                         }\r
3457                 };\r
3458         }\r
3459 })(tinymce);\r
3460 \r
3461 (function(tinymce) {\r
3462         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {\r
3463                 '#text' : 3,\r
3464                 '#comment' : 8,\r
3465                 '#cdata' : 4,\r
3466                 '#pi' : 7,\r
3467                 '#doctype' : 10,\r
3468                 '#document-fragment' : 11\r
3469         };\r
3470 \r
3471         // Walks the tree left/right\r
3472         function walk(node, root_node, prev) {\r
3473                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';\r
3474 \r
3475                 // Walk into nodes if it has a start\r
3476                 if (node[startName])\r
3477                         return node[startName];\r
3478 \r
3479                 // Return the sibling if it has one\r
3480                 if (node !== root_node) {\r
3481                         sibling = node[siblingName];\r
3482 \r
3483                         if (sibling)\r
3484                                 return sibling;\r
3485 \r
3486                         // Walk up the parents to look for siblings\r
3487                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {\r
3488                                 sibling = parent[siblingName];\r
3489 \r
3490                                 if (sibling)\r
3491                                         return sibling;\r
3492                         }\r
3493                 }\r
3494         };\r
3495 \r
3496         function Node(name, type) {\r
3497                 this.name = name;\r
3498                 this.type = type;\r
3499 \r
3500                 if (type === 1) {\r
3501                         this.attributes = [];\r
3502                         this.attributes.map = {};\r
3503                 }\r
3504         }\r
3505 \r
3506         tinymce.extend(Node.prototype, {\r
3507                 replace : function(node) {\r
3508                         var self = this;\r
3509 \r
3510                         if (node.parent)\r
3511                                 node.remove();\r
3512 \r
3513                         self.insert(node, self);\r
3514                         self.remove();\r
3515 \r
3516                         return self;\r
3517                 },\r
3518 \r
3519                 attr : function(name, value) {\r
3520                         var self = this, attrs, i, undef;\r
3521 \r
3522                         if (typeof name !== "string") {\r
3523                                 for (i in name)\r
3524                                         self.attr(i, name[i]);\r
3525 \r
3526                                 return self;\r
3527                         }\r
3528 \r
3529                         if (attrs = self.attributes) {\r
3530                                 if (value !== undef) {\r
3531                                         // Remove attribute\r
3532                                         if (value === null) {\r
3533                                                 if (name in attrs.map) {\r
3534                                                         delete attrs.map[name];\r
3535 \r
3536                                                         i = attrs.length;\r
3537                                                         while (i--) {\r
3538                                                                 if (attrs[i].name === name) {\r
3539                                                                         attrs = attrs.splice(i, 1);\r
3540                                                                         return self;\r
3541                                                                 }\r
3542                                                         }\r
3543                                                 }\r
3544 \r
3545                                                 return self;\r
3546                                         }\r
3547 \r
3548                                         // Set attribute\r
3549                                         if (name in attrs.map) {\r
3550                                                 // Set attribute\r
3551                                                 i = attrs.length;\r
3552                                                 while (i--) {\r
3553                                                         if (attrs[i].name === name) {\r
3554                                                                 attrs[i].value = value;\r
3555                                                                 break;\r
3556                                                         }\r
3557                                                 }\r
3558                                         } else\r
3559                                                 attrs.push({name: name, value: value});\r
3560 \r
3561                                         attrs.map[name] = value;\r
3562 \r
3563                                         return self;\r
3564                                 } else {\r
3565                                         return attrs.map[name];\r
3566                                 }\r
3567                         }\r
3568                 },\r
3569 \r
3570                 clone : function() {\r
3571                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;\r
3572 \r
3573                         // Clone element attributes\r
3574                         if (selfAttrs = self.attributes) {\r
3575                                 cloneAttrs = [];\r
3576                                 cloneAttrs.map = {};\r
3577 \r
3578                                 for (i = 0, l = selfAttrs.length; i < l; i++) {\r
3579                                         selfAttr = selfAttrs[i];\r
3580 \r
3581                                         // Clone everything except id\r
3582                                         if (selfAttr.name !== 'id') {\r
3583                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};\r
3584                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;\r
3585                                         }\r
3586                                 }\r
3587 \r
3588                                 clone.attributes = cloneAttrs;\r
3589                         }\r
3590 \r
3591                         clone.value = self.value;\r
3592                         clone.shortEnded = self.shortEnded;\r
3593 \r
3594                         return clone;\r
3595                 },\r
3596 \r
3597                 wrap : function(wrapper) {\r
3598                         var self = this;\r
3599 \r
3600                         self.parent.insert(wrapper, self);\r
3601                         wrapper.append(self);\r
3602 \r
3603                         return self;\r
3604                 },\r
3605 \r
3606                 unwrap : function() {\r
3607                         var self = this, node, next;\r
3608 \r
3609                         for (node = self.firstChild; node; ) {\r
3610                                 next = node.next;\r
3611                                 self.insert(node, self, true);\r
3612                                 node = next;\r
3613                         }\r
3614 \r
3615                         self.remove();\r
3616                 },\r
3617 \r
3618                 remove : function() {\r
3619                         var self = this, parent = self.parent, next = self.next, prev = self.prev;\r
3620 \r
3621                         if (parent) {\r
3622                                 if (parent.firstChild === self) {\r
3623                                         parent.firstChild = next;\r
3624 \r
3625                                         if (next)\r
3626                                                 next.prev = null;\r
3627                                 } else {\r
3628                                         prev.next = next;\r
3629                                 }\r
3630 \r
3631                                 if (parent.lastChild === self) {\r
3632                                         parent.lastChild = prev;\r
3633 \r
3634                                         if (prev)\r
3635                                                 prev.next = null;\r
3636                                 } else {\r
3637                                         next.prev = prev;\r
3638                                 }\r
3639 \r
3640                                 self.parent = self.next = self.prev = null;\r
3641                         }\r
3642 \r
3643                         return self;\r
3644                 },\r
3645 \r
3646                 append : function(node) {\r
3647                         var self = this, last;\r
3648 \r
3649                         if (node.parent)\r
3650                                 node.remove();\r
3651 \r
3652                         last = self.lastChild;\r
3653                         if (last) {\r
3654                                 last.next = node;\r
3655                                 node.prev = last;\r
3656                                 self.lastChild = node;\r
3657                         } else\r
3658                                 self.lastChild = self.firstChild = node;\r
3659 \r
3660                         node.parent = self;\r
3661 \r
3662                         return node;\r
3663                 },\r
3664 \r
3665                 insert : function(node, ref_node, before) {\r
3666                         var parent;\r
3667 \r
3668                         if (node.parent)\r
3669                                 node.remove();\r
3670 \r
3671                         parent = ref_node.parent || this;\r
3672 \r
3673                         if (before) {\r
3674                                 if (ref_node === parent.firstChild)\r
3675                                         parent.firstChild = node;\r
3676                                 else\r
3677                                         ref_node.prev.next = node;\r
3678 \r
3679                                 node.prev = ref_node.prev;\r
3680                                 node.next = ref_node;\r
3681                                 ref_node.prev = node;\r
3682                         } else {\r
3683                                 if (ref_node === parent.lastChild)\r
3684                                         parent.lastChild = node;\r
3685                                 else\r
3686                                         ref_node.next.prev = node;\r
3687 \r
3688                                 node.next = ref_node.next;\r
3689                                 node.prev = ref_node;\r
3690                                 ref_node.next = node;\r
3691                         }\r
3692 \r
3693                         node.parent = parent;\r
3694 \r
3695                         return node;\r
3696                 },\r
3697 \r
3698                 getAll : function(name) {\r
3699                         var self = this, node, collection = [];\r
3700 \r
3701                         for (node = self.firstChild; node; node = walk(node, self)) {\r
3702                                 if (node.name === name)\r
3703                                         collection.push(node);\r
3704                         }\r
3705 \r
3706                         return collection;\r
3707                 },\r
3708 \r
3709                 empty : function() {\r
3710                         var self = this, nodes, i, node;\r
3711 \r
3712                         // Remove all children\r
3713                         if (self.firstChild) {\r
3714                                 nodes = [];\r
3715 \r
3716                                 // Collect the children\r
3717                                 for (node = self.firstChild; node; node = walk(node, self))\r
3718                                         nodes.push(node);\r
3719 \r
3720                                 // Remove the children\r
3721                                 i = nodes.length;\r
3722                                 while (i--) {\r
3723                                         node = nodes[i];\r
3724                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;\r
3725                                 }\r
3726                         }\r
3727 \r
3728                         self.firstChild = self.lastChild = null;\r
3729 \r
3730                         return self;\r
3731                 },\r
3732 \r
3733                 isEmpty : function(elements) {\r
3734                         var self = this, node = self.firstChild, i, name;\r
3735 \r
3736                         if (node) {\r
3737                                 do {\r
3738                                         if (node.type === 1) {\r
3739                                                 // Ignore bogus elements\r
3740                                                 if (node.attributes.map['data-mce-bogus'])\r
3741                                                         continue;\r
3742 \r
3743                                                 // Keep empty elements like <img />\r
3744                                                 if (elements[node.name])\r
3745                                                         return false;\r
3746 \r
3747                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>\r
3748                                                 i = node.attributes.length;\r
3749                                                 while (i--) {\r
3750                                                         name = node.attributes[i].name;\r
3751                                                         if (name === "name" || name.indexOf('data-mce-') === 0)\r
3752                                                                 return false;\r
3753                                                 }\r
3754                                         }\r
3755 \r
3756                                         // Keep comments\r
3757                                         if (node.type === 8)\r
3758                                                 return false;\r
3759                                         \r
3760                                         // Keep non whitespace text nodes\r
3761                                         if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))\r
3762                                                 return false;\r
3763                                 } while (node = walk(node, self));\r
3764                         }\r
3765 \r
3766                         return true;\r
3767                 },\r
3768 \r
3769                 walk : function(prev) {\r
3770                         return walk(this, null, prev);\r
3771                 }\r
3772         });\r
3773 \r
3774         tinymce.extend(Node, {\r
3775                 create : function(name, attrs) {\r
3776                         var node, attrName;\r
3777 \r
3778                         // Create node\r
3779                         node = new Node(name, typeLookup[name] || 1);\r
3780 \r
3781                         // Add attributes if needed\r
3782                         if (attrs) {\r
3783                                 for (attrName in attrs)\r
3784                                         node.attr(attrName, attrs[attrName]);\r
3785                         }\r
3786 \r
3787                         return node;\r
3788                 }\r
3789         });\r
3790 \r
3791         tinymce.html.Node = Node;\r
3792 })(tinymce);\r
3793 \r
3794 (function(tinymce) {\r
3795         var Node = tinymce.html.Node;\r
3796 \r
3797         tinymce.html.DomParser = function(settings, schema) {\r
3798                 var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};\r
3799 \r
3800                 settings = settings || {};\r
3801                 settings.validate = "validate" in settings ? settings.validate : true;\r
3802                 settings.root_name = settings.root_name || 'body';\r
3803                 self.schema = schema = schema || new tinymce.html.Schema();\r
3804 \r
3805                 function fixInvalidChildren(nodes) {\r
3806                         var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,\r
3807                                 childClone, nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;\r
3808 \r
3809                         nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');\r
3810                         nonEmptyElements = schema.getNonEmptyElements();\r
3811                         textBlockElements = schema.getTextBlockElements();\r
3812 \r
3813                         for (ni = 0; ni < nodes.length; ni++) {\r
3814                                 node = nodes[ni];\r
3815 \r
3816                                 // Already removed or fixed\r
3817                                 if (!node.parent || node.fixed)\r
3818                                         continue;\r
3819 \r
3820                                 // If the invalid element is a text block and the text block is within a parent LI element\r
3821                                 // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office\r
3822                                 if (textBlockElements[node.name] && node.parent.name == 'li') {\r
3823                                         // Move sibling text blocks after LI element\r
3824                                         sibling = node.next;\r
3825                                         while (sibling) {\r
3826                                                 if (textBlockElements[sibling.name]) {\r
3827                                                         sibling.name = 'li';\r
3828                                                         sibling.fixed = true;\r
3829                                                         node.parent.insert(sibling, node.parent);\r
3830                                                 } else {\r
3831                                                         break;\r
3832                                                 }\r
3833 \r
3834                                                 sibling = sibling.next;\r
3835                                         }\r
3836 \r
3837                                         // Unwrap current text block\r
3838                                         node.unwrap(node);\r
3839                                         continue;\r
3840                                 }\r
3841 \r
3842                                 // Get list of all parent nodes until we find a valid parent to stick the child into\r
3843                                 parents = [node];\r
3844                                 for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplitableElements[parent.name]; parent = parent.parent)\r
3845                                         parents.push(parent);\r
3846 \r
3847                                 // Found a suitable parent\r
3848                                 if (parent && parents.length > 1) {\r
3849                                         // Reverse the array since it makes looping easier\r
3850                                         parents.reverse();\r
3851 \r
3852                                         // Clone the related parent and insert that after the moved node\r
3853                                         newParent = currentNode = self.filterNode(parents[0].clone());\r
3854 \r
3855                                         // Start cloning and moving children on the left side of the target node\r
3856                                         for (i = 0; i < parents.length - 1; i++) {\r
3857                                                 if (schema.isValidChild(currentNode.name, parents[i].name)) {\r
3858                                                         tempNode = self.filterNode(parents[i].clone());\r
3859                                                         currentNode.append(tempNode);\r
3860                                                 } else\r
3861                                                         tempNode = currentNode;\r
3862 \r
3863                                                 for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {\r
3864                                                         nextNode = childNode.next;\r
3865                                                         tempNode.append(childNode);\r
3866                                                         childNode = nextNode;\r
3867                                                 }\r
3868 \r
3869                                                 currentNode = tempNode;\r
3870                                         }\r
3871 \r
3872                                         if (!newParent.isEmpty(nonEmptyElements)) {\r
3873                                                 parent.insert(newParent, parents[0], true);\r
3874                                                 parent.insert(node, newParent);\r
3875                                         } else {\r
3876                                                 parent.insert(node, parents[0], true);\r
3877                                         }\r
3878 \r
3879                                         // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>\r
3880                                         parent = parents[0];\r
3881                                         if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {\r
3882                                                 parent.empty().remove();\r
3883                                         }\r
3884                                 } else if (node.parent) {\r
3885                                         // If it's an LI try to find a UL/OL for it or wrap it\r
3886                                         if (node.name === 'li') {\r
3887                                                 sibling = node.prev;\r
3888                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
3889                                                         sibling.append(node);\r
3890                                                         continue;\r
3891                                                 }\r
3892 \r
3893                                                 sibling = node.next;\r
3894                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
3895                                                         sibling.insert(node, sibling.firstChild, true);\r
3896                                                         continue;\r
3897                                                 }\r
3898 \r
3899                                                 node.wrap(self.filterNode(new Node('ul', 1)));\r
3900                                                 continue;\r
3901                                         }\r
3902 \r
3903                                         // Try wrapping the element in a DIV\r
3904                                         if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {\r
3905                                                 node.wrap(self.filterNode(new Node('div', 1)));\r
3906                                         } else {\r
3907                                                 // We failed wrapping it, then remove or unwrap it\r
3908                                                 if (node.name === 'style' || node.name === 'script')\r
3909                                                         node.empty().remove();\r
3910                                                 else\r
3911                                                         node.unwrap();\r
3912                                         }\r
3913                                 }\r
3914                         }\r
3915                 };\r
3916 \r
3917                 self.filterNode = function(node) {\r
3918                         var i, name, list;\r
3919 \r
3920                         // Run element filters\r
3921                         if (name in nodeFilters) {\r
3922                                 list = matchedNodes[name];\r
3923 \r
3924                                 if (list)\r
3925                                         list.push(node);\r
3926                                 else\r
3927                                         matchedNodes[name] = [node];\r
3928                         }\r
3929 \r
3930                         // Run attribute filters\r
3931                         i = attributeFilters.length;\r
3932                         while (i--) {\r
3933                                 name = attributeFilters[i].name;\r
3934 \r
3935                                 if (name in node.attributes.map) {\r
3936                                         list = matchedAttributes[name];\r
3937 \r
3938                                         if (list)\r
3939                                                 list.push(node);\r
3940                                         else\r
3941                                                 matchedAttributes[name] = [node];\r
3942                                 }\r
3943                         }\r
3944 \r
3945                         return node;\r
3946                 };\r
3947 \r
3948                 self.addNodeFilter = function(name, callback) {\r
3949                         tinymce.each(tinymce.explode(name), function(name) {\r
3950                                 var list = nodeFilters[name];\r
3951 \r
3952                                 if (!list)\r
3953                                         nodeFilters[name] = list = [];\r
3954 \r
3955                                 list.push(callback);\r
3956                         });\r
3957                 };\r
3958 \r
3959                 self.addAttributeFilter = function(name, callback) {\r
3960                         tinymce.each(tinymce.explode(name), function(name) {\r
3961                                 var i;\r
3962 \r
3963                                 for (i = 0; i < attributeFilters.length; i++) {\r
3964                                         if (attributeFilters[i].name === name) {\r
3965                                                 attributeFilters[i].callbacks.push(callback);\r
3966                                                 return;\r
3967                                         }\r
3968                                 }\r
3969 \r
3970                                 attributeFilters.push({name: name, callbacks: [callback]});\r
3971                         });\r
3972                 };\r
3973 \r
3974                 self.parse = function(html, args) {\r
3975                         var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,\r
3976                                 blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement,\r
3977                                 endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;\r
3978 \r
3979                         args = args || {};\r
3980                         matchedNodes = {};\r
3981                         matchedAttributes = {};\r
3982                         blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\r
3983                         nonEmptyElements = schema.getNonEmptyElements();\r
3984                         children = schema.children;\r
3985                         validate = settings.validate;\r
3986                         rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;\r
3987 \r
3988                         whiteSpaceElements = schema.getWhiteSpaceElements();\r
3989                         startWhiteSpaceRegExp = /^[ \t\r\n]+/;\r
3990                         endWhiteSpaceRegExp = /[ \t\r\n]+$/;\r
3991                         allWhiteSpaceRegExp = /[ \t\r\n]+/g;\r
3992                         isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;\r
3993 \r
3994                         function addRootBlocks() {\r
3995                                 var node = rootNode.firstChild, next, rootBlockNode;\r
3996 \r
3997                                 while (node) {\r
3998                                         next = node.next;\r
3999 \r
4000                                         if (node.type == 3 || (node.type == 1 && node.name !== 'p' && !blockElements[node.name] && !node.attr('data-mce-type'))) {\r
4001                                                 if (!rootBlockNode) {\r
4002                                                         // Create a new root block element\r
4003                                                         rootBlockNode = createNode(rootBlockName, 1);\r
4004                                                         rootNode.insert(rootBlockNode, node);\r
4005                                                         rootBlockNode.append(node);\r
4006                                                 } else\r
4007                                                         rootBlockNode.append(node);\r
4008                                         } else {\r
4009                                                 rootBlockNode = null;\r
4010                                         }\r
4011 \r
4012                                         node = next;\r
4013                                 };\r
4014                         };\r
4015 \r
4016                         function createNode(name, type) {\r
4017                                 var node = new Node(name, type), list;\r
4018 \r
4019                                 if (name in nodeFilters) {\r
4020                                         list = matchedNodes[name];\r
4021 \r
4022                                         if (list)\r
4023                                                 list.push(node);\r
4024                                         else\r
4025                                                 matchedNodes[name] = [node];\r
4026                                 }\r
4027 \r
4028                                 return node;\r
4029                         };\r
4030 \r
4031                         function removeWhitespaceBefore(node) {\r
4032                                 var textNode, textVal, sibling;\r
4033 \r
4034                                 for (textNode = node.prev; textNode && textNode.type === 3; ) {\r
4035                                         textVal = textNode.value.replace(endWhiteSpaceRegExp, '');\r
4036 \r
4037                                         if (textVal.length > 0) {\r
4038                                                 textNode.value = textVal;\r
4039                                                 textNode = textNode.prev;\r
4040                                         } else {\r
4041                                                 sibling = textNode.prev;\r
4042                                                 textNode.remove();\r
4043                                                 textNode = sibling;\r
4044                                         }\r
4045                                 }\r
4046                         };\r
4047 \r
4048                         function cloneAndExcludeBlocks(input) {\r
4049                                 var name, output = {};\r
4050 \r
4051                                 for (name in input) {\r
4052                                         if (name !== 'li' && name != 'p') {\r
4053                                                 output[name] = input[name];\r
4054                                         }\r
4055                                 }\r
4056 \r
4057                                 return output;\r
4058                         };\r
4059 \r
4060                         parser = new tinymce.html.SaxParser({\r
4061                                 validate : validate,\r
4062 \r
4063                                 // Exclude P and LI from DOM parsing since it's treated better by the DOM parser\r
4064                                 self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),\r
4065 \r
4066                                 cdata: function(text) {\r
4067                                         node.append(createNode('#cdata', 4)).value = text;\r
4068                                 },\r
4069 \r
4070                                 text: function(text, raw) {\r
4071                                         var textNode;\r
4072 \r
4073                                         // Trim all redundant whitespace on non white space elements\r
4074                                         if (!isInWhiteSpacePreservedElement) {\r
4075                                                 text = text.replace(allWhiteSpaceRegExp, ' ');\r
4076 \r
4077                                                 if (node.lastChild && blockElements[node.lastChild.name])\r
4078                                                         text = text.replace(startWhiteSpaceRegExp, '');\r
4079                                         }\r
4080 \r
4081                                         // Do we need to create the node\r
4082                                         if (text.length !== 0) {\r
4083                                                 textNode = createNode('#text', 3);\r
4084                                                 textNode.raw = !!raw;\r
4085                                                 node.append(textNode).value = text;\r
4086                                         }\r
4087                                 },\r
4088 \r
4089                                 comment: function(text) {\r
4090                                         node.append(createNode('#comment', 8)).value = text;\r
4091                                 },\r
4092 \r
4093                                 pi: function(name, text) {\r
4094                                         node.append(createNode(name, 7)).value = text;\r
4095                                         removeWhitespaceBefore(node);\r
4096                                 },\r
4097 \r
4098                                 doctype: function(text) {\r
4099                                         var newNode;\r
4100                 \r
4101                                         newNode = node.append(createNode('#doctype', 10));\r
4102                                         newNode.value = text;\r
4103                                         removeWhitespaceBefore(node);\r
4104                                 },\r
4105 \r
4106                                 start: function(name, attrs, empty) {\r
4107                                         var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;\r
4108 \r
4109                                         elementRule = validate ? schema.getElementRule(name) : {};\r
4110                                         if (elementRule) {\r
4111                                                 newNode = createNode(elementRule.outputName || name, 1);\r
4112                                                 newNode.attributes = attrs;\r
4113                                                 newNode.shortEnded = empty;\r
4114 \r
4115                                                 node.append(newNode);\r
4116 \r
4117                                                 // Check if node is valid child of the parent node is the child is\r
4118                                                 // unknown we don't collect it since it's probably a custom element\r
4119                                                 parent = children[node.name];\r
4120                                                 if (parent && children[newNode.name] && !parent[newNode.name])\r
4121                                                         invalidChildren.push(newNode);\r
4122 \r
4123                                                 attrFiltersLen = attributeFilters.length;\r
4124                                                 while (attrFiltersLen--) {\r
4125                                                         attrName = attributeFilters[attrFiltersLen].name;\r
4126 \r
4127                                                         if (attrName in attrs.map) {\r
4128                                                                 list = matchedAttributes[attrName];\r
4129 \r
4130                                                                 if (list)\r
4131                                                                         list.push(newNode);\r
4132                                                                 else\r
4133                                                                         matchedAttributes[attrName] = [newNode];\r
4134                                                         }\r
4135                                                 }\r
4136 \r
4137                                                 // Trim whitespace before block\r
4138                                                 if (blockElements[name])\r
4139                                                         removeWhitespaceBefore(newNode);\r
4140 \r
4141                                                 // Change current node if the element wasn't empty i.e not <br /> or <img />\r
4142                                                 if (!empty)\r
4143                                                         node = newNode;\r
4144 \r
4145                                                 // Check if we are inside a whitespace preserved element\r
4146                                                 if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {\r
4147                                                         isInWhiteSpacePreservedElement = true;\r
4148                                                 }\r
4149                                         }\r
4150                                 },\r
4151 \r
4152                                 end: function(name) {\r
4153                                         var textNode, elementRule, text, sibling, tempNode;\r
4154 \r
4155                                         elementRule = validate ? schema.getElementRule(name) : {};\r
4156                                         if (elementRule) {\r
4157                                                 if (blockElements[name]) {\r
4158                                                         if (!isInWhiteSpacePreservedElement) {\r
4159                                                                 // Trim whitespace of the first node in a block\r
4160                                                                 textNode = node.firstChild;\r
4161                                                                 if (textNode && textNode.type === 3) {\r
4162                                                                         text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
4163 \r
4164                                                                         // Any characters left after trim or should we remove it\r
4165                                                                         if (text.length > 0) {\r
4166                                                                                 textNode.value = text;\r
4167                                                                                 textNode = textNode.next;\r
4168                                                                         } else {\r
4169                                                                                 sibling = textNode.next;\r
4170                                                                                 textNode.remove();\r
4171                                                                                 textNode = sibling;\r
4172                                                                         }\r
4173 \r
4174                                                                         // Remove any pure whitespace siblings\r
4175                                                                         while (textNode && textNode.type === 3) {\r
4176                                                                                 text = textNode.value;\r
4177                                                                                 sibling = textNode.next;\r
4178 \r
4179                                                                                 if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {\r
4180                                                                                         textNode.remove();\r
4181                                                                                         textNode = sibling;\r
4182                                                                                 }\r
4183 \r
4184                                                                                 textNode = sibling;\r
4185                                                                         }\r
4186                                                                 }\r
4187 \r
4188                                                                 // Trim whitespace of the last node in a block\r
4189                                                                 textNode = node.lastChild;\r
4190                                                                 if (textNode && textNode.type === 3) {\r
4191                                                                         text = textNode.value.replace(endWhiteSpaceRegExp, '');\r
4192 \r
4193                                                                         // Any characters left after trim or should we remove it\r
4194                                                                         if (text.length > 0) {\r
4195                                                                                 textNode.value = text;\r
4196                                                                                 textNode = textNode.prev;\r
4197                                                                         } else {\r
4198                                                                                 sibling = textNode.prev;\r
4199                                                                                 textNode.remove();\r
4200                                                                                 textNode = sibling;\r
4201                                                                         }\r
4202 \r
4203                                                                         // Remove any pure whitespace siblings\r
4204                                                                         while (textNode && textNode.type === 3) {\r
4205                                                                                 text = textNode.value;\r
4206                                                                                 sibling = textNode.prev;\r
4207 \r
4208                                                                                 if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {\r
4209                                                                                         textNode.remove();\r
4210                                                                                         textNode = sibling;\r
4211                                                                                 }\r
4212 \r
4213                                                                                 textNode = sibling;\r
4214                                                                         }\r
4215                                                                 }\r
4216                                                         }\r
4217 \r
4218                                                         // Trim start white space\r
4219                                                         // Removed due to: #5424\r
4220                                                         /*textNode = node.prev;\r
4221                                                         if (textNode && textNode.type === 3) {\r
4222                                                                 text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
4223 \r
4224                                                                 if (text.length > 0)\r
4225                                                                         textNode.value = text;\r
4226                                                                 else\r
4227                                                                         textNode.remove();\r
4228                                                         }*/\r
4229                                                 }\r
4230 \r
4231                                                 // Check if we exited a whitespace preserved element\r
4232                                                 if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {\r
4233                                                         isInWhiteSpacePreservedElement = false;\r
4234                                                 }\r
4235 \r
4236                                                 // Handle empty nodes\r
4237                                                 if (elementRule.removeEmpty || elementRule.paddEmpty) {\r
4238                                                         if (node.isEmpty(nonEmptyElements)) {\r
4239                                                                 if (elementRule.paddEmpty)\r
4240                                                                         node.empty().append(new Node('#text', '3')).value = '\u00a0';\r
4241                                                                 else {\r
4242                                                                         // Leave nodes that have a name like <a name="name">\r
4243                                                                         if (!node.attributes.map.name && !node.attributes.map.id) {\r
4244                                                                                 tempNode = node.parent;\r
4245                                                                                 node.empty().remove();\r
4246                                                                                 node = tempNode;\r
4247                                                                                 return;\r
4248                                                                         }\r
4249                                                                 }\r
4250                                                         }\r
4251                                                 }\r
4252 \r
4253                                                 node = node.parent;\r
4254                                         }\r
4255                                 }\r
4256                         }, schema);\r
4257 \r
4258                         rootNode = node = new Node(args.context || settings.root_name, 11);\r
4259 \r
4260                         parser.parse(html);\r
4261 \r
4262                         // Fix invalid children or report invalid children in a contextual parsing\r
4263                         if (validate && invalidChildren.length) {\r
4264                                 if (!args.context)\r
4265                                         fixInvalidChildren(invalidChildren);\r
4266                                 else\r
4267                                         args.invalid = true;\r
4268                         }\r
4269 \r
4270                         // Wrap nodes in the root into block elements if the root is body\r
4271                         if (rootBlockName && rootNode.name == 'body')\r
4272                                 addRootBlocks();\r
4273 \r
4274                         // Run filters only when the contents is valid\r
4275                         if (!args.invalid) {\r
4276                                 // Run node filters\r
4277                                 for (name in matchedNodes) {\r
4278                                         list = nodeFilters[name];\r
4279                                         nodes = matchedNodes[name];\r
4280 \r
4281                                         // Remove already removed children\r
4282                                         fi = nodes.length;\r
4283                                         while (fi--) {\r
4284                                                 if (!nodes[fi].parent)\r
4285                                                         nodes.splice(fi, 1);\r
4286                                         }\r
4287 \r
4288                                         for (i = 0, l = list.length; i < l; i++)\r
4289                                                 list[i](nodes, name, args);\r
4290                                 }\r
4291 \r
4292                                 // Run attribute filters\r
4293                                 for (i = 0, l = attributeFilters.length; i < l; i++) {\r
4294                                         list = attributeFilters[i];\r
4295 \r
4296                                         if (list.name in matchedAttributes) {\r
4297                                                 nodes = matchedAttributes[list.name];\r
4298 \r
4299                                                 // Remove already removed children\r
4300                                                 fi = nodes.length;\r
4301                                                 while (fi--) {\r
4302                                                         if (!nodes[fi].parent)\r
4303                                                                 nodes.splice(fi, 1);\r
4304                                                 }\r
4305 \r
4306                                                 for (fi = 0, fl = list.callbacks.length; fi < fl; fi++)\r
4307                                                         list.callbacks[fi](nodes, list.name, args);\r
4308                                         }\r
4309                                 }\r
4310                         }\r
4311 \r
4312                         return rootNode;\r
4313                 };\r
4314 \r
4315                 // Remove <br> at end of block elements Gecko and WebKit injects BR elements to\r
4316                 // make it possible to place the caret inside empty blocks. This logic tries to remove\r
4317                 // these elements and keep br elements that where intended to be there intact\r
4318                 if (settings.remove_trailing_brs) {\r
4319                         self.addNodeFilter('br', function(nodes, name) {\r
4320                                 var i, l = nodes.length, node, blockElements = tinymce.extend({}, schema.getBlockElements()),\r
4321                                         nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;\r
4322 \r
4323                                 // Remove brs from body element as well\r
4324                                 blockElements.body = 1;\r
4325 \r
4326                                 // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>\r
4327                                 for (i = 0; i < l; i++) {\r
4328                                         node = nodes[i];\r
4329                                         parent = node.parent;\r
4330 \r
4331                                         if (blockElements[node.parent.name] && node === parent.lastChild) {\r
4332                                                 // Loop all nodes to the left of the current node and check for other BR elements\r
4333                                                 // excluding bookmarks since they are invisible\r
4334                                                 prev = node.prev;\r
4335                                                 while (prev) {\r
4336                                                         prevName = prev.name;\r
4337 \r
4338                                                         // Ignore bookmarks\r
4339                                                         if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {\r
4340                                                                 // Found a non BR element\r
4341                                                                 if (prevName !== "br")\r
4342                                                                         break;\r
4343         \r
4344                                                                 // Found another br it's a <br><br> structure then don't remove anything\r
4345                                                                 if (prevName === 'br') {\r
4346                                                                         node = null;\r
4347                                                                         break;\r
4348                                                                 }\r
4349                                                         }\r
4350 \r
4351                                                         prev = prev.prev;\r
4352                                                 }\r
4353 \r
4354                                                 if (node) {\r
4355                                                         node.remove();\r
4356 \r
4357                                                         // Is the parent to be considered empty after we removed the BR\r
4358                                                         if (parent.isEmpty(nonEmptyElements)) {\r
4359                                                                 elementRule = schema.getElementRule(parent.name);\r
4360 \r
4361                                                                 // Remove or padd the element depending on schema rule\r
4362                                                                 if (elementRule) {\r
4363                                                                         if (elementRule.removeEmpty)\r
4364                                                                                 parent.remove();\r
4365                                                                         else if (elementRule.paddEmpty)\r
4366                                                                                 parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';\r
4367                                                                 }\r
4368                                                         }\r
4369                                                 }\r
4370                                         } else {\r
4371                                                 // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> so they become <p><b><i>&nbsp;</i></b></p> \r
4372                                                 lastParent = node;\r
4373                                                 while (parent.firstChild === lastParent && parent.lastChild === lastParent) {\r
4374                                                         lastParent = parent;\r
4375 \r
4376                                                         if (blockElements[parent.name]) {\r
4377                                                                 break;\r
4378                                                         }\r
4379 \r
4380                                                         parent = parent.parent;\r
4381                                                 }\r
4382 \r
4383                                                 if (lastParent === parent) {\r
4384                                                         textNode = new tinymce.html.Node('#text', 3);\r
4385                                                         textNode.value = '\u00a0';\r
4386                                                         node.replace(textNode);\r
4387                                                 }\r
4388                                         }\r
4389                                 }\r
4390                         });\r
4391                 }\r
4392 \r
4393                 // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.\r
4394                 if (!settings.allow_html_in_named_anchor) {\r
4395                         self.addAttributeFilter('id,name', function(nodes, name) {\r
4396                                 var i = nodes.length, sibling, prevSibling, parent, node;\r
4397 \r
4398                                 while (i--) {\r
4399                                         node = nodes[i];\r
4400                                         if (node.name === 'a' && node.firstChild && !node.attr('href')) {\r
4401                                                 parent = node.parent;\r
4402 \r
4403                                                 // Move children after current node\r
4404                                                 sibling = node.lastChild;\r
4405                                                 do {\r
4406                                                         prevSibling = sibling.prev;\r
4407                                                         parent.insert(sibling, node);\r
4408                                                         sibling = prevSibling;\r
4409                                                 } while (sibling);\r
4410                                         }\r
4411                                 }\r
4412                         });\r
4413                 }\r
4414         }\r
4415 })(tinymce);\r
4416 \r
4417 tinymce.html.Writer = function(settings) {\r
4418         var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;\r
4419 \r
4420         settings = settings || {};\r
4421         indent = settings.indent;\r
4422         indentBefore = tinymce.makeMap(settings.indent_before || '');\r
4423         indentAfter = tinymce.makeMap(settings.indent_after || '');\r
4424         encode = tinymce.html.Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);\r
4425         htmlOutput = settings.element_format == "html";\r
4426 \r
4427         return {\r
4428                 start: function(name, attrs, empty) {\r
4429                         var i, l, attr, value;\r
4430 \r
4431                         if (indent && indentBefore[name] && html.length > 0) {\r
4432                                 value = html[html.length - 1];\r
4433 \r
4434                                 if (value.length > 0 && value !== '\n')\r
4435                                         html.push('\n');\r
4436                         }\r
4437 \r
4438                         html.push('<', name);\r
4439 \r
4440                         if (attrs) {\r
4441                                 for (i = 0, l = attrs.length; i < l; i++) {\r
4442                                         attr = attrs[i];\r
4443                                         html.push(' ', attr.name, '="', encode(attr.value, true), '"');\r
4444                                 }\r
4445                         }\r
4446 \r
4447                         if (!empty || htmlOutput)\r
4448                                 html[html.length] = '>';\r
4449                         else\r
4450                                 html[html.length] = ' />';\r
4451 \r
4452                         if (empty && indent && indentAfter[name] && html.length > 0) {\r
4453                                 value = html[html.length - 1];\r
4454 \r
4455                                 if (value.length > 0 && value !== '\n')\r
4456                                         html.push('\n');\r
4457                         }\r
4458                 },\r
4459 \r
4460                 end: function(name) {\r
4461                         var value;\r
4462 \r
4463                         /*if (indent && indentBefore[name] && html.length > 0) {\r
4464                                 value = html[html.length - 1];\r
4465 \r
4466                                 if (value.length > 0 && value !== '\n')\r
4467                                         html.push('\n');\r
4468                         }*/\r
4469 \r
4470                         html.push('</', name, '>');\r
4471 \r
4472                         if (indent && indentAfter[name] && html.length > 0) {\r
4473                                 value = html[html.length - 1];\r
4474 \r
4475                                 if (value.length > 0 && value !== '\n')\r
4476                                         html.push('\n');\r
4477                         }\r
4478                 },\r
4479 \r
4480                 text: function(text, raw) {\r
4481                         if (text.length > 0)\r
4482                                 html[html.length] = raw ? text : encode(text);\r
4483                 },\r
4484 \r
4485                 cdata: function(text) {\r
4486                         html.push('<![CDATA[', text, ']]>');\r
4487                 },\r
4488 \r
4489                 comment: function(text) {\r
4490                         html.push('<!--', text, '-->');\r
4491                 },\r
4492 \r
4493                 pi: function(name, text) {\r
4494                         if (text)\r
4495                                 html.push('<?', name, ' ', text, '?>');\r
4496                         else\r
4497                                 html.push('<?', name, '?>');\r
4498 \r
4499                         if (indent)\r
4500                                 html.push('\n');\r
4501                 },\r
4502 \r
4503                 doctype: function(text) {\r
4504                         html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');\r
4505                 },\r
4506 \r
4507                 reset: function() {\r
4508                         html.length = 0;\r
4509                 },\r
4510 \r
4511                 getContent: function() {\r
4512                         return html.join('').replace(/\n$/, '');\r
4513                 }\r
4514         };\r
4515 };\r
4516 \r
4517 (function(tinymce) {\r
4518         tinymce.html.Serializer = function(settings, schema) {\r
4519                 var self = this, writer = new tinymce.html.Writer(settings);\r
4520 \r
4521                 settings = settings || {};\r
4522                 settings.validate = "validate" in settings ? settings.validate : true;\r
4523 \r
4524                 self.schema = schema = schema || new tinymce.html.Schema();\r
4525                 self.writer = writer;\r
4526 \r
4527                 self.serialize = function(node) {\r
4528                         var handlers, validate;\r
4529 \r
4530                         validate = settings.validate;\r
4531 \r
4532                         handlers = {\r
4533                                 // #text\r
4534                                 3: function(node, raw) {\r
4535                                         writer.text(node.value, node.raw);\r
4536                                 },\r
4537 \r
4538                                 // #comment\r
4539                                 8: function(node) {\r
4540                                         writer.comment(node.value);\r
4541                                 },\r
4542 \r
4543                                 // Processing instruction\r
4544                                 7: function(node) {\r
4545                                         writer.pi(node.name, node.value);\r
4546                                 },\r
4547 \r
4548                                 // Doctype\r
4549                                 10: function(node) {\r
4550                                         writer.doctype(node.value);\r
4551                                 },\r
4552 \r
4553                                 // CDATA\r
4554                                 4: function(node) {\r
4555                                         writer.cdata(node.value);\r
4556                                 },\r
4557 \r
4558                                 // Document fragment\r
4559                                 11: function(node) {\r
4560                                         if ((node = node.firstChild)) {\r
4561                                                 do {\r
4562                                                         walk(node);\r
4563                                                 } while (node = node.next);\r
4564                                         }\r
4565                                 }\r
4566                         };\r
4567 \r
4568                         writer.reset();\r
4569 \r
4570                         function walk(node) {\r
4571                                 var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;\r
4572 \r
4573                                 if (!handler) {\r
4574                                         name = node.name;\r
4575                                         isEmpty = node.shortEnded;\r
4576                                         attrs = node.attributes;\r
4577 \r
4578                                         // Sort attributes\r
4579                                         if (validate && attrs && attrs.length > 1) {\r
4580                                                 sortedAttrs = [];\r
4581                                                 sortedAttrs.map = {};\r
4582 \r
4583                                                 elementRule = schema.getElementRule(node.name);\r
4584                                                 for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {\r
4585                                                         attrName = elementRule.attributesOrder[i];\r
4586 \r
4587                                                         if (attrName in attrs.map) {\r
4588                                                                 attrValue = attrs.map[attrName];\r
4589                                                                 sortedAttrs.map[attrName] = attrValue;\r
4590                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
4591                                                         }\r
4592                                                 }\r
4593 \r
4594                                                 for (i = 0, l = attrs.length; i < l; i++) {\r
4595                                                         attrName = attrs[i].name;\r
4596 \r
4597                                                         if (!(attrName in sortedAttrs.map)) {\r
4598                                                                 attrValue = attrs.map[attrName];\r
4599                                                                 sortedAttrs.map[attrName] = attrValue;\r
4600                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
4601                                                         }\r
4602                                                 }\r
4603 \r
4604                                                 attrs = sortedAttrs;\r
4605                                         }\r
4606 \r
4607                                         writer.start(node.name, attrs, isEmpty);\r
4608 \r
4609                                         if (!isEmpty) {\r
4610                                                 if ((node = node.firstChild)) {\r
4611                                                         do {\r
4612                                                                 walk(node);\r
4613                                                         } while (node = node.next);\r
4614                                                 }\r
4615 \r
4616                                                 writer.end(name);\r
4617                                         }\r
4618                                 } else\r
4619                                         handler(node);\r
4620                         }\r
4621 \r
4622                         // Serialize element and treat all non elements as fragments\r
4623                         if (node.type == 1 && !settings.inner)\r
4624                                 walk(node);\r
4625                         else\r
4626                                 handlers[11](node);\r
4627 \r
4628                         return writer.getContent();\r
4629                 };\r
4630         }\r
4631 })(tinymce);\r
4632 \r
4633 // JSLint defined globals\r
4634 /*global tinymce:false, window:false */\r
4635 \r
4636 tinymce.dom = {};\r
4637 \r
4638 (function(namespace, expando) {\r
4639         var w3cEventModel = !!document.addEventListener;\r
4640 \r
4641         function addEvent(target, name, callback, capture) {\r
4642                 if (target.addEventListener) {\r
4643                         target.addEventListener(name, callback, capture || false);\r
4644                 } else if (target.attachEvent) {\r
4645                         target.attachEvent('on' + name, callback);\r
4646                 }\r
4647         }\r
4648 \r
4649         function removeEvent(target, name, callback, capture) {\r
4650                 if (target.removeEventListener) {\r
4651                         target.removeEventListener(name, callback, capture || false);\r
4652                 } else if (target.detachEvent) {\r
4653                         target.detachEvent('on' + name, callback);\r
4654                 }\r
4655         }\r
4656 \r
4657         function fix(original_event, data) {\r
4658                 var name, event = data || {};\r
4659 \r
4660                 // Dummy function that gets replaced on the delegation state functions\r
4661                 function returnFalse() {\r
4662                         return false;\r
4663                 }\r
4664 \r
4665                 // Dummy function that gets replaced on the delegation state functions\r
4666                 function returnTrue() {\r
4667                         return true;\r
4668                 }\r
4669 \r
4670                 // Copy all properties from the original event\r
4671                 for (name in original_event) {\r
4672                         // layerX/layerY is deprecated in Chrome and produces a warning\r
4673                         if (name !== "layerX" && name !== "layerY") {\r
4674                                 event[name] = original_event[name];\r
4675                         }\r
4676                 }\r
4677 \r
4678                 // Normalize target IE uses srcElement\r
4679                 if (!event.target) {\r
4680                         event.target = event.srcElement || document;\r
4681                 }\r
4682 \r
4683                 // Add preventDefault method\r
4684                 event.preventDefault = function() {\r
4685                         event.isDefaultPrevented = returnTrue;\r
4686 \r
4687                         // Execute preventDefault on the original event object\r
4688                         if (original_event) {\r
4689                                 if (original_event.preventDefault) {\r
4690                                         original_event.preventDefault();\r
4691                                 } else {\r
4692                                         original_event.returnValue = false; // IE\r
4693                                 }\r
4694                         }\r
4695                 };\r
4696 \r
4697                 // Add stopPropagation\r
4698                 event.stopPropagation = function() {\r
4699                         event.isPropagationStopped = returnTrue;\r
4700 \r
4701                         // Execute stopPropagation on the original event object\r
4702                         if (original_event) {\r
4703                                 if (original_event.stopPropagation) {\r
4704                                         original_event.stopPropagation();\r
4705                                 } else {\r
4706                                         original_event.cancelBubble = true; // IE\r
4707                                 }\r
4708                          }\r
4709                 };\r
4710 \r
4711                 // Add stopImmediatePropagation\r
4712                 event.stopImmediatePropagation = function() {\r
4713                         event.isImmediatePropagationStopped = returnTrue;\r
4714                         event.stopPropagation();\r
4715                 };\r
4716 \r
4717                 // Add event delegation states\r
4718                 if (!event.isDefaultPrevented) {\r
4719                         event.isDefaultPrevented = returnFalse;\r
4720                         event.isPropagationStopped = returnFalse;\r
4721                         event.isImmediatePropagationStopped = returnFalse;\r
4722                 }\r
4723 \r
4724                 return event;\r
4725         }\r
4726 \r
4727         function bindOnReady(win, callback, event_utils) {\r
4728                 var doc = win.document, event = {type: 'ready'};\r
4729 \r
4730                 // Gets called when the DOM is ready\r
4731                 function readyHandler() {\r
4732                         if (!event_utils.domLoaded) {\r
4733                                 event_utils.domLoaded = true;\r
4734                                 callback(event);\r
4735                         }\r
4736                 }\r
4737 \r
4738                 // Page already loaded then fire it directly\r
4739                 if (doc.readyState == "complete") {\r
4740                         readyHandler();\r
4741                         return;\r
4742                 }\r
4743 \r
4744                 // Use W3C method\r
4745                 if (w3cEventModel) {\r
4746                         addEvent(win, 'DOMContentLoaded', readyHandler);\r
4747                 } else {\r
4748                         // Use IE method\r
4749                         addEvent(doc, "readystatechange", function() {\r
4750                                 if (doc.readyState === "complete") {\r
4751                                         removeEvent(doc, "readystatechange", arguments.callee);\r
4752                                         readyHandler();\r
4753                                 }\r
4754                         });\r
4755 \r
4756                         // Wait until we can scroll, when we can the DOM is initialized\r
4757                         if (doc.documentElement.doScroll && win === win.top) {\r
4758                                 (function() {\r
4759                                         try {\r
4760                                                 // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.\r
4761                                                 // http://javascript.nwbox.com/IEContentLoaded/\r
4762                                                 doc.documentElement.doScroll("left");\r
4763                                         } catch (ex) {\r
4764                                                 setTimeout(arguments.callee, 0);\r
4765                                                 return;\r
4766                                         }\r
4767 \r
4768                                         readyHandler();\r
4769                                 })();\r
4770                         }\r
4771                 }\r
4772 \r
4773                 // Fallback if any of the above methods should fail for some odd reason\r
4774                 addEvent(win, 'load', readyHandler);\r
4775         }\r
4776 \r
4777         function EventUtils(proxy) {\r
4778                 var self = this, events = {}, count, isFocusBlurBound, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;\r
4779 \r
4780                 hasMouseEnterLeave = "onmouseenter" in document.documentElement;\r
4781                 hasFocusIn = "onfocusin" in document.documentElement;\r
4782                 mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};\r
4783                 count = 1;\r
4784 \r
4785                 // State if the DOMContentLoaded was executed or not\r
4786                 self.domLoaded = false;\r
4787                 self.events = events;\r
4788 \r
4789                 function executeHandlers(evt, id) {\r
4790                         var callbackList, i, l, callback;\r
4791 \r
4792                         callbackList = events[id][evt.type];\r
4793                         if (callbackList) {\r
4794                                 for (i = 0, l = callbackList.length; i < l; i++) {\r
4795                                         callback = callbackList[i];\r
4796                                         \r
4797                                         // Check if callback exists might be removed if a unbind is called inside the callback\r
4798                                         if (callback && callback.func.call(callback.scope, evt) === false) {\r
4799                                                 evt.preventDefault();\r
4800                                         }\r
4801 \r
4802                                         // Should we stop propagation to immediate listeners\r
4803                                         if (evt.isImmediatePropagationStopped()) {\r
4804                                                 return;\r
4805                                         }\r
4806                                 }\r
4807                         }\r
4808                 }\r
4809 \r
4810                 self.bind = function(target, names, callback, scope) {\r
4811                         var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;\r
4812 \r
4813                         // Native event handler function patches the event and executes the callbacks for the expando\r
4814                         function defaultNativeHandler(evt) {\r
4815                                 executeHandlers(fix(evt || win.event), id);\r
4816                         }\r
4817 \r
4818                         // Don't bind to text nodes or comments\r
4819                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4820                                 return;\r
4821                         }\r
4822 \r
4823                         // Create or get events id for the target\r
4824                         if (!target[expando]) {\r
4825                                 id = count++;\r
4826                                 target[expando] = id;\r
4827                                 events[id] = {};\r
4828                         } else {\r
4829                                 id = target[expando];\r
4830 \r
4831                                 if (!events[id]) {\r
4832                                         events[id] = {};\r
4833                                 }\r
4834                         }\r
4835 \r
4836                         // Setup the specified scope or use the target as a default\r
4837                         scope = scope || target;\r
4838 \r
4839                         // Split names and bind each event, enables you to bind multiple events with one call\r
4840                         names = names.split(' ');\r
4841                         i = names.length;\r
4842                         while (i--) {\r
4843                                 name = names[i];\r
4844                                 nativeHandler = defaultNativeHandler;\r
4845                                 fakeName = capture = false;\r
4846 \r
4847                                 // Use ready instead of DOMContentLoaded\r
4848                                 if (name === "DOMContentLoaded") {\r
4849                                         name = "ready";\r
4850                                 }\r
4851 \r
4852                                 // DOM is already ready\r
4853                                 if ((self.domLoaded || target.readyState == 'complete') && name === "ready") {\r
4854                                         self.domLoaded = true;\r
4855                                         callback.call(scope, fix({type: name}));\r
4856                                         continue;\r
4857                                 }\r
4858 \r
4859                                 // Handle mouseenter/mouseleaver\r
4860                                 if (!hasMouseEnterLeave) {\r
4861                                         fakeName = mouseEnterLeave[name];\r
4862 \r
4863                                         if (fakeName) {\r
4864                                                 nativeHandler = function(evt) {\r
4865                                                         var current, related;\r
4866 \r
4867                                                         current = evt.currentTarget;\r
4868                                                         related = evt.relatedTarget;\r
4869 \r
4870                                                         // Check if related is inside the current target if it's not then the event should be ignored since it's a mouseover/mouseout inside the element\r
4871                                                         if (related && current.contains) {\r
4872                                                                 // Use contains for performance\r
4873                                                                 related = current.contains(related);\r
4874                                                         } else {\r
4875                                                                 while (related && related !== current) {\r
4876                                                                         related = related.parentNode;\r
4877                                                                 }\r
4878                                                         }\r
4879 \r
4880                                                         // Fire fake event\r
4881                                                         if (!related) {\r
4882                                                                 evt = fix(evt || win.event);\r
4883                                                                 evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';\r
4884                                                                 evt.target = current;\r
4885                                                                 executeHandlers(evt, id);\r
4886                                                         }\r
4887                                                 };\r
4888                                         }\r
4889                                 }\r
4890 \r
4891                                 // Fake bubbeling of focusin/focusout\r
4892                                 if (!hasFocusIn && (name === "focusin" || name === "focusout")) {\r
4893                                         capture = true;\r
4894                                         fakeName = name === "focusin" ? "focus" : "blur";\r
4895                                         nativeHandler = function(evt) {\r
4896                                                 evt = fix(evt || win.event);\r
4897                                                 evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';\r
4898                                                 executeHandlers(evt, id);\r
4899                                         };\r
4900                                 }\r
4901 \r
4902                                 // Setup callback list and bind native event\r
4903                                 callbackList = events[id][name];\r
4904                                 if (!callbackList) {\r
4905                                         events[id][name] = callbackList = [{func: callback, scope: scope}];\r
4906                                         callbackList.fakeName = fakeName;\r
4907                                         callbackList.capture = capture;\r
4908 \r
4909                                         // Add the nativeHandler to the callback list so that we can later unbind it\r
4910                                         callbackList.nativeHandler = nativeHandler;\r
4911                                         if (!w3cEventModel) {\r
4912                                                 callbackList.proxyHandler = proxy(id);\r
4913                                         }\r
4914 \r
4915                                         // Check if the target has native events support\r
4916                                         if (name === "ready") {\r
4917                                                 bindOnReady(target, nativeHandler, self);\r
4918                                         } else {\r
4919                                                 addEvent(target, fakeName || name, w3cEventModel ? nativeHandler : callbackList.proxyHandler, capture);\r
4920                                         }\r
4921                                 } else {\r
4922                                         // If it already has an native handler then just push the callback\r
4923                                         callbackList.push({func: callback, scope: scope});\r
4924                                 }\r
4925                         }\r
4926 \r
4927                         target = callbackList = 0; // Clean memory for IE\r
4928 \r
4929                         return callback;\r
4930                 };\r
4931 \r
4932                 self.unbind = function(target, names, callback) {\r
4933                         var id, callbackList, i, ci, name, eventMap;\r
4934 \r
4935                         // Don't bind to text nodes or comments\r
4936                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4937                                 return self;\r
4938                         }\r
4939 \r
4940                         // Unbind event or events if the target has the expando\r
4941                         id = target[expando];\r
4942                         if (id) {\r
4943                                 eventMap = events[id];\r
4944 \r
4945                                 // Specific callback\r
4946                                 if (names) {\r
4947                                         names = names.split(' ');\r
4948                                         i = names.length;\r
4949                                         while (i--) {\r
4950                                                 name = names[i];\r
4951                                                 callbackList = eventMap[name];\r
4952 \r
4953                                                 // Unbind the event if it exists in the map\r
4954                                                 if (callbackList) {\r
4955                                                         // Remove specified callback\r
4956                                                         if (callback) {\r
4957                                                                 ci = callbackList.length;\r
4958                                                                 while (ci--) {\r
4959                                                                         if (callbackList[ci].func === callback) {\r
4960                                                                                 callbackList.splice(ci, 1);\r
4961                                                                         }\r
4962                                                                 }\r
4963                                                         }\r
4964 \r
4965                                                         // Remove all callbacks if there isn't a specified callback or there is no callbacks left\r
4966                                                         if (!callback || callbackList.length === 0) {\r
4967                                                                 delete eventMap[name];\r
4968                                                                 removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);\r
4969                                                         }\r
4970                                                 }\r
4971                                         }\r
4972                                 } else {\r
4973                                         // All events for a specific element\r
4974                                         for (name in eventMap) {\r
4975                                                 callbackList = eventMap[name];\r
4976                                                 removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);\r
4977                                         }\r
4978 \r
4979                                         eventMap = {};\r
4980                                 }\r
4981 \r
4982                                 // Check if object is empty, if it isn't then we won't remove the expando map\r
4983                                 for (name in eventMap) {\r
4984                                         return self;\r
4985                                 }\r
4986 \r
4987                                 // Delete event object\r
4988                                 delete events[id];\r
4989 \r
4990                                 // Remove expando from target\r
4991                                 try {\r
4992                                         // IE will fail here since it can't delete properties from window\r
4993                                         delete target[expando];\r
4994                                 } catch (ex) {\r
4995                                         // IE will set it to null\r
4996                                         target[expando] = null;\r
4997                                 }\r
4998                         }\r
4999 \r
5000                         return self;\r
5001                 };\r
5002 \r
5003                 self.fire = function(target, name, args) {\r
5004                         var id, event;\r
5005 \r
5006                         // Don't bind to text nodes or comments\r
5007                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
5008                                 return self;\r
5009                         }\r
5010 \r
5011                         // Build event object by patching the args\r
5012                         event = fix(null, args);\r
5013                         event.type = name;\r
5014 \r
5015                         do {\r
5016                                 // Found an expando that means there is listeners to execute\r
5017                                 id = target[expando];\r
5018                                 if (id) {\r
5019                                         executeHandlers(event, id);\r
5020                                 }\r
5021 \r
5022                                 // Walk up the DOM\r
5023                                 target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;\r
5024                         } while (target && !event.isPropagationStopped());\r
5025 \r
5026                         return self;\r
5027                 };\r
5028 \r
5029                 self.clean = function(target) {\r
5030                         var i, children, unbind = self.unbind;\r
5031         \r
5032                         // Don't bind to text nodes or comments\r
5033                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
5034                                 return self;\r
5035                         }\r
5036 \r
5037                         // Unbind any element on the specificed target\r
5038                         if (target[expando]) {\r
5039                                 unbind(target);\r
5040                         }\r
5041 \r
5042                         // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children\r
5043                         if (!target.getElementsByTagName) {\r
5044                                 target = target.document;\r
5045                         }\r
5046 \r
5047                         // Remove events from each child element\r
5048                         if (target && target.getElementsByTagName) {\r
5049                                 unbind(target);\r
5050 \r
5051                                 children = target.getElementsByTagName('*');\r
5052                                 i = children.length;\r
5053                                 while (i--) {\r
5054                                         target = children[i];\r
5055 \r
5056                                         if (target[expando]) {\r
5057                                                 unbind(target);\r
5058                                         }\r
5059                                 }\r
5060                         }\r
5061 \r
5062                         return self;\r
5063                 };\r
5064 \r
5065                 self.callNativeHandler = function(id, evt) {\r
5066                         if (events) {\r
5067                                 events[id][evt.type].nativeHandler(evt);\r
5068                         }\r
5069                 };\r
5070 \r
5071                 self.destory = function() {\r
5072                         events = {};\r
5073                 };\r
5074 \r
5075                 // Legacy function calls\r
5076 \r
5077                 self.add = function(target, events, func, scope) {\r
5078                         // Old API supported direct ID assignment\r
5079                         if (typeof(target) === "string") {\r
5080                                 target = document.getElementById(target);\r
5081                         }\r
5082 \r
5083                         // Old API supported multiple targets\r
5084                         if (target && target instanceof Array) {\r
5085                                 var i = target.length;\r
5086 \r
5087                                 while (i--) {\r
5088                                         self.add(target[i], events, func, scope);\r
5089                                 }\r
5090 \r
5091                                 return;\r
5092                         }\r
5093 \r
5094                         // Old API called ready init\r
5095                         if (events === "init") {\r
5096                                 events = "ready";\r
5097                         }\r
5098 \r
5099                         return self.bind(target, events instanceof Array ? events.join(' ') : events, func, scope);\r
5100                 };\r
5101 \r
5102                 self.remove = function(target, events, func, scope) {\r
5103                         if (!target) {\r
5104                                 return self;\r
5105                         }\r
5106 \r
5107                         // Old API supported direct ID assignment\r
5108                         if (typeof(target) === "string") {\r
5109                                 target = document.getElementById(target);\r
5110                         }\r
5111 \r
5112                         // Old API supported multiple targets\r
5113                         if (target instanceof Array) {\r
5114                                 var i = target.length;\r
5115 \r
5116                                 while (i--) {\r
5117                                         self.remove(target[i], events, func, scope);\r
5118                                 }\r
5119 \r
5120                                 return self;\r
5121                         }\r
5122 \r
5123                         return self.unbind(target, events instanceof Array ? events.join(' ') : events, func);\r
5124                 };\r
5125 \r
5126                 self.clear = function(target) {\r
5127                         // Old API supported direct ID assignment\r
5128                         if (typeof(target) === "string") {\r
5129                                 target = document.getElementById(target);\r
5130                         }\r
5131 \r
5132                         return self.clean(target);\r
5133                 };\r
5134 \r
5135                 self.cancel = function(e) {\r
5136                         if (e) {\r
5137                                 self.prevent(e);\r
5138                                 self.stop(e);\r
5139                         }\r
5140 \r
5141                         return false;\r
5142                 };\r
5143 \r
5144                 self.prevent = function(e) {\r
5145                         if (!e.preventDefault) {\r
5146                                 e = fix(e);\r
5147                         }\r
5148 \r
5149                         e.preventDefault();\r
5150 \r
5151                         return false;\r
5152                 };\r
5153 \r
5154                 self.stop = function(e) {\r
5155                         if (!e.stopPropagation) {\r
5156                                 e = fix(e);\r
5157                         }\r
5158 \r
5159                         e.stopPropagation();\r
5160 \r
5161                         return false;\r
5162                 };\r
5163         }\r
5164 \r
5165         namespace.EventUtils = EventUtils;\r
5166 \r
5167         namespace.Event = new EventUtils(function(id) {\r
5168                 return function(evt) {\r
5169                         tinymce.dom.Event.callNativeHandler(id, evt);\r
5170                 };\r
5171         });\r
5172 \r
5173         // Bind ready event when tinymce script is loaded\r
5174         namespace.Event.bind(window, 'ready', function() {});\r
5175 \r
5176         namespace = 0;\r
5177 })(tinymce.dom, 'data-mce-expando'); // Namespace and expando\r
5178 \r
5179 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
5180         var node = start_node;\r
5181 \r
5182         function findSibling(node, start_name, sibling_name, shallow) {\r
5183                 var sibling, parent;\r
5184 \r
5185                 if (node) {\r
5186                         // Walk into nodes if it has a start\r
5187                         if (!shallow && node[start_name])\r
5188                                 return node[start_name];\r
5189 \r
5190                         // Return the sibling if it has one\r
5191                         if (node != root_node) {\r
5192                                 sibling = node[sibling_name];\r
5193                                 if (sibling)\r
5194                                         return sibling;\r
5195 \r
5196                                 // Walk up the parents to look for siblings\r
5197                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
5198                                         sibling = parent[sibling_name];\r
5199                                         if (sibling)\r
5200                                                 return sibling;\r
5201                                 }\r
5202                         }\r
5203                 }\r
5204         };\r
5205 \r
5206         this.current = function() {\r
5207                 return node;\r
5208         };\r
5209 \r
5210         this.next = function(shallow) {\r
5211                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
5212         };\r
5213 \r
5214         this.prev = function(shallow) {\r
5215                 return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));\r
5216         };\r
5217 };\r
5218 \r
5219 (function(tinymce) {\r
5220         // Shorten names\r
5221         var each = tinymce.each,\r
5222                 is = tinymce.is,\r
5223                 isWebKit = tinymce.isWebKit,\r
5224                 isIE = tinymce.isIE,\r
5225                 Entities = tinymce.html.Entities,\r
5226                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
5227                 whiteSpaceRegExp = /^[ \t\r\n]*$/;\r
5228 \r
5229         tinymce.create('tinymce.dom.DOMUtils', {\r
5230                 doc : null,\r
5231                 root : null,\r
5232                 files : null,\r
5233                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
5234                 props : {\r
5235                         "for" : "htmlFor",\r
5236                         "class" : "className",\r
5237                         className : "className",\r
5238                         checked : "checked",\r
5239                         disabled : "disabled",\r
5240                         maxlength : "maxLength",\r
5241                         readonly : "readOnly",\r
5242                         selected : "selected",\r
5243                         value : "value",\r
5244                         id : "id",\r
5245                         name : "name",\r
5246                         type : "type"\r
5247                 },\r
5248 \r
5249                 DOMUtils : function(d, s) {\r
5250                         var t = this, globalStyle, name, blockElementsMap;\r
5251 \r
5252                         t.doc = d;\r
5253                         t.win = window;\r
5254                         t.files = {};\r
5255                         t.cssFlicker = false;\r
5256                         t.counter = 0;\r
5257                         t.stdMode = !tinymce.isIE || d.documentMode >= 8;\r
5258                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
5259                         t.hasOuterHTML = "outerHTML" in d.createElement("a");\r
5260 \r
5261                         t.settings = s = tinymce.extend({\r
5262                                 keep_values : false,\r
5263                                 hex_colors : 1\r
5264                         }, s);\r
5265                         \r
5266                         t.schema = s.schema;\r
5267                         t.styles = new tinymce.html.Styles({\r
5268                                 url_converter : s.url_converter,\r
5269                                 url_converter_scope : s.url_converter_scope\r
5270                         }, s.schema);\r
5271 \r
5272                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
5273                         if (tinymce.isIE6) {\r
5274                                 try {\r
5275                                         d.execCommand('BackgroundImageCache', false, true);\r
5276                                 } catch (e) {\r
5277                                         t.cssFlicker = true;\r
5278                                 }\r
5279                         }\r
5280 \r
5281                         t.fixDoc(d);\r
5282                         t.events = s.ownEvents ? new tinymce.dom.EventUtils(s.proxy) : tinymce.dom.Event;\r
5283                         tinymce.addUnload(t.destroy, t);\r
5284                         blockElementsMap = s.schema ? s.schema.getBlockElements() : {};\r
5285 \r
5286                         t.isBlock = function(node) {\r
5287                                 // Fix for #5446\r
5288                                 if (!node) {\r
5289                                         return false;\r
5290                                 }\r
5291 \r
5292                                 // This function is called in module pattern style since it might be executed with the wrong this scope\r
5293                                 var type = node.nodeType;\r
5294 \r
5295                                 // If it's a node then check the type and use the nodeName\r
5296                                 if (type)\r
5297                                         return !!(type === 1 && blockElementsMap[node.nodeName]);\r
5298 \r
5299                                 return !!blockElementsMap[node];\r
5300                         };\r
5301                 },\r
5302 \r
5303                 fixDoc: function(doc) {\r
5304                         var settings = this.settings, name;\r
5305 \r
5306                         if (isIE && !tinymce.isIE11 && settings.schema) {\r
5307                                 // Add missing HTML 4/5 elements to IE\r
5308                                 ('abbr article aside audio canvas ' +\r
5309                                 'details figcaption figure footer ' +\r
5310                                 'header hgroup mark menu meter nav ' +\r
5311                                 'output progress section summary ' +\r
5312                                 'time video').replace(/\w+/g, function(name) {\r
5313                                         doc.createElement(name);\r
5314                                 });\r
5315 \r
5316                                 // Create all custom elements\r
5317                                 for (name in settings.schema.getCustomElements()) {\r
5318                                         doc.createElement(name);\r
5319                                 }\r
5320                         }\r
5321                 },\r
5322 \r
5323                 clone: function(node, deep) {\r
5324                         var self = this, clone, doc;\r
5325 \r
5326                         // TODO: Add feature detection here in the future\r
5327                         if (!isIE || tinymce.isIE11 || node.nodeType !== 1 || deep) {\r
5328                                 return node.cloneNode(deep);\r
5329                         }\r
5330 \r
5331                         doc = self.doc;\r
5332 \r
5333                         // Make a HTML5 safe shallow copy\r
5334                         if (!deep) {\r
5335                                 clone = doc.createElement(node.nodeName);\r
5336 \r
5337                                 // Copy attribs\r
5338                                 each(self.getAttribs(node), function(attr) {\r
5339                                         self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));\r
5340                                 });\r
5341 \r
5342                                 return clone;\r
5343                         }\r
5344 /*\r
5345                         // Setup HTML5 patched document fragment\r
5346                         if (!self.frag) {\r
5347                                 self.frag = doc.createDocumentFragment();\r
5348                                 self.fixDoc(self.frag);\r
5349                         }\r
5350 \r
5351                         // Make a deep copy by adding it to the document fragment then removing it this removed the :section\r
5352                         clone = doc.createElement('div');\r
5353                         self.frag.appendChild(clone);\r
5354                         clone.innerHTML = node.outerHTML;\r
5355                         self.frag.removeChild(clone);\r
5356 */\r
5357                         return clone.firstChild;\r
5358                 },\r
5359 \r
5360                 getRoot : function() {\r
5361                         var t = this, s = t.settings;\r
5362 \r
5363                         return (s && t.get(s.root_element)) || t.doc.body;\r
5364                 },\r
5365 \r
5366                 getViewPort : function(w) {\r
5367                         var d, b;\r
5368 \r
5369                         w = !w ? this.win : w;\r
5370                         d = w.document;\r
5371                         b = this.boxModel ? d.documentElement : d.body;\r
5372 \r
5373                         // Returns viewport size excluding scrollbars\r
5374                         return {\r
5375                                 x : w.pageXOffset || b.scrollLeft,\r
5376                                 y : w.pageYOffset || b.scrollTop,\r
5377                                 w : w.innerWidth || b.clientWidth,\r
5378                                 h : w.innerHeight || b.clientHeight\r
5379                         };\r
5380                 },\r
5381 \r
5382                 getRect : function(e) {\r
5383                         var p, t = this, sr;\r
5384 \r
5385                         e = t.get(e);\r
5386                         p = t.getPos(e);\r
5387                         sr = t.getSize(e);\r
5388 \r
5389                         return {\r
5390                                 x : p.x,\r
5391                                 y : p.y,\r
5392                                 w : sr.w,\r
5393                                 h : sr.h\r
5394                         };\r
5395                 },\r
5396 \r
5397                 getSize : function(e) {\r
5398                         var t = this, w, h;\r
5399 \r
5400                         e = t.get(e);\r
5401                         w = t.getStyle(e, 'width');\r
5402                         h = t.getStyle(e, 'height');\r
5403 \r
5404                         // Non pixel value, then force offset/clientWidth\r
5405                         if (w.indexOf('px') === -1)\r
5406                                 w = 0;\r
5407 \r
5408                         // Non pixel value, then force offset/clientWidth\r
5409                         if (h.indexOf('px') === -1)\r
5410                                 h = 0;\r
5411 \r
5412                         return {\r
5413                                 w : parseInt(w, 10) || e.offsetWidth || e.clientWidth,\r
5414                                 h : parseInt(h, 10) || e.offsetHeight || e.clientHeight\r
5415                         };\r
5416                 },\r
5417 \r
5418                 getParent : function(n, f, r) {\r
5419                         return this.getParents(n, f, r, false);\r
5420                 },\r
5421 \r
5422                 getParents : function(n, f, r, c) {\r
5423                         var t = this, na, se = t.settings, o = [];\r
5424 \r
5425                         n = t.get(n);\r
5426                         c = c === undefined;\r
5427 \r
5428                         if (se.strict_root)\r
5429                                 r = r || t.getRoot();\r
5430 \r
5431                         // Wrap node name as func\r
5432                         if (is(f, 'string')) {\r
5433                                 na = f;\r
5434 \r
5435                                 if (f === '*') {\r
5436                                         f = function(n) {return n.nodeType == 1;};\r
5437                                 } else {\r
5438                                         f = function(n) {\r
5439                                                 return t.is(n, na);\r
5440                                         };\r
5441                                 }\r
5442                         }\r
5443 \r
5444                         while (n) {\r
5445                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
5446                                         break;\r
5447 \r
5448                                 if (!f || f(n)) {\r
5449                                         if (c)\r
5450                                                 o.push(n);\r
5451                                         else\r
5452                                                 return n;\r
5453                                 }\r
5454 \r
5455                                 n = n.parentNode;\r
5456                         }\r
5457 \r
5458                         return c ? o : null;\r
5459                 },\r
5460 \r
5461                 get : function(e) {\r
5462                         var n;\r
5463 \r
5464                         if (e && this.doc && typeof(e) == 'string') {\r
5465                                 n = e;\r
5466                                 e = this.doc.getElementById(e);\r
5467 \r
5468                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
5469                                 if (e && e.id !== n)\r
5470                                         return this.doc.getElementsByName(n)[1];\r
5471                         }\r
5472 \r
5473                         return e;\r
5474                 },\r
5475 \r
5476                 getNext : function(node, selector) {\r
5477                         return this._findSib(node, selector, 'nextSibling');\r
5478                 },\r
5479 \r
5480                 getPrev : function(node, selector) {\r
5481                         return this._findSib(node, selector, 'previousSibling');\r
5482                 },\r
5483 \r
5484 \r
5485                 select : function(pa, s) {\r
5486                         var t = this;\r
5487 \r
5488                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
5489                 },\r
5490 \r
5491                 is : function(n, selector) {\r
5492                         var i;\r
5493 \r
5494                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
5495                         if (n.length === undefined) {\r
5496                                 // Simple all selector\r
5497                                 if (selector === '*')\r
5498                                         return n.nodeType == 1;\r
5499 \r
5500                                 // Simple selector just elements\r
5501                                 if (simpleSelectorRe.test(selector)) {\r
5502                                         selector = selector.toLowerCase().split(/,/);\r
5503                                         n = n.nodeName.toLowerCase();\r
5504 \r
5505                                         for (i = selector.length - 1; i >= 0; i--) {\r
5506                                                 if (selector[i] == n)\r
5507                                                         return true;\r
5508                                         }\r
5509 \r
5510                                         return false;\r
5511                                 }\r
5512                         }\r
5513 \r
5514                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
5515                 },\r
5516 \r
5517 \r
5518                 add : function(p, n, a, h, c) {\r
5519                         var t = this;\r
5520 \r
5521                         return this.run(p, function(p) {\r
5522                                 var e, k;\r
5523 \r
5524                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
5525                                 t.setAttribs(e, a);\r
5526 \r
5527                                 if (h) {\r
5528                                         if (h.nodeType)\r
5529                                                 e.appendChild(h);\r
5530                                         else\r
5531                                                 t.setHTML(e, h);\r
5532                                 }\r
5533 \r
5534                                 return !c ? p.appendChild(e) : e;\r
5535                         });\r
5536                 },\r
5537 \r
5538                 create : function(n, a, h) {\r
5539                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
5540                 },\r
5541 \r
5542                 createHTML : function(n, a, h) {\r
5543                         var o = '', t = this, k;\r
5544 \r
5545                         o += '<' + n;\r
5546 \r
5547                         for (k in a) {\r
5548                                 if (a.hasOwnProperty(k))\r
5549                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
5550                         }\r
5551 \r
5552                         // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
5553                         if (typeof(h) != "undefined")\r
5554                                 return o + '>' + h + '</' + n + '>';\r
5555 \r
5556                         return o + ' />';\r
5557                 },\r
5558 \r
5559                 remove : function(node, keep_children) {\r
5560                         return this.run(node, function(node) {\r
5561                                 var child, parent = node.parentNode;\r
5562 \r
5563                                 if (!parent)\r
5564                                         return null;\r
5565 \r
5566                                 if (keep_children) {\r
5567                                         while (child = node.firstChild) {\r
5568                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
5569                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
5570                                                         parent.insertBefore(child, node);\r
5571                                                 else\r
5572                                                         node.removeChild(child);\r
5573                                         }\r
5574                                 }\r
5575 \r
5576                                 return parent.removeChild(node);\r
5577                         });\r
5578                 },\r
5579 \r
5580                 setStyle : function(n, na, v) {\r
5581                         var t = this;\r
5582 \r
5583                         return t.run(n, function(e) {\r
5584                                 var s, i;\r
5585 \r
5586                                 s = e.style;\r
5587 \r
5588                                 // Camelcase it, if needed\r
5589                                 na = na.replace(/-(\D)/g, function(a, b){\r
5590                                         return b.toUpperCase();\r
5591                                 });\r
5592 \r
5593                                 // Default px suffix on these\r
5594                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
5595                                         v += 'px';\r
5596 \r
5597                                 switch (na) {\r
5598                                         case 'opacity':\r
5599                                                 // IE specific opacity\r
5600                                                 if (isIE && ! tinymce.isIE11) {\r
5601                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
5602 \r
5603                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
5604                                                                 s.display = 'inline-block';\r
5605                                                 }\r
5606 \r
5607                                                 // Fix for older browsers\r
5608                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
5609                                                 break;\r
5610 \r
5611                                         case 'float':\r
5612                                                 (isIE && ! tinymce.isIE11) ? s.styleFloat = v : s.cssFloat = v;\r
5613                                                 break;\r
5614                                         \r
5615                                         default:\r
5616                                                 s[na] = v || '';\r
5617                                 }\r
5618 \r
5619                                 // Force update of the style data\r
5620                                 if (t.settings.update_styles)\r
5621                                         t.setAttrib(e, 'data-mce-style');\r
5622                         });\r
5623                 },\r
5624 \r
5625                 getStyle : function(n, na, c) {\r
5626                         n = this.get(n);\r
5627 \r
5628                         if (!n)\r
5629                                 return;\r
5630 \r
5631                         // Gecko\r
5632                         if (this.doc.defaultView && c) {\r
5633                                 // Remove camelcase\r
5634                                 na = na.replace(/[A-Z]/g, function(a){\r
5635                                         return '-' + a;\r
5636                                 });\r
5637 \r
5638                                 try {\r
5639                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
5640                                 } catch (ex) {\r
5641                                         // Old safari might fail\r
5642                                         return null;\r
5643                                 }\r
5644                         }\r
5645 \r
5646                         // Camelcase it, if needed\r
5647                         na = na.replace(/-(\D)/g, function(a, b){\r
5648                                 return b.toUpperCase();\r
5649                         });\r
5650 \r
5651                         if (na == 'float')\r
5652                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
5653 \r
5654                         // IE & Opera\r
5655                         if (n.currentStyle && c)\r
5656                                 return n.currentStyle[na];\r
5657 \r
5658                         return n.style ? n.style[na] : undefined;\r
5659                 },\r
5660 \r
5661                 setStyles : function(e, o) {\r
5662                         var t = this, s = t.settings, ol;\r
5663 \r
5664                         ol = s.update_styles;\r
5665                         s.update_styles = 0;\r
5666 \r
5667                         each(o, function(v, n) {\r
5668                                 t.setStyle(e, n, v);\r
5669                         });\r
5670 \r
5671                         // Update style info\r
5672                         s.update_styles = ol;\r
5673                         if (s.update_styles)\r
5674                                 t.setAttrib(e, s.cssText);\r
5675                 },\r
5676 \r
5677                 removeAllAttribs: function(e) {\r
5678                         return this.run(e, function(e) {\r
5679                                 var i, attrs = e.attributes;\r
5680                                 for (i = attrs.length - 1; i >= 0; i--) {\r
5681                                         e.removeAttributeNode(attrs.item(i));\r
5682                                 }\r
5683                         });\r
5684                 },\r
5685 \r
5686                 setAttrib : function(e, n, v) {\r
5687                         var t = this;\r
5688 \r
5689                         // Whats the point\r
5690                         if (!e || !n)\r
5691                                 return;\r
5692 \r
5693                         // Strict XML mode\r
5694                         if (t.settings.strict)\r
5695                                 n = n.toLowerCase();\r
5696 \r
5697                         return this.run(e, function(e) {\r
5698                                 var s = t.settings;\r
5699                                 var originalValue = e.getAttribute(n);\r
5700                                 if (v !== null) {\r
5701                                         switch (n) {\r
5702                                                 case "style":\r
5703                                                         if (!is(v, 'string')) {\r
5704                                                                 each(v, function(v, n) {\r
5705                                                                         t.setStyle(e, n, v);\r
5706                                                                 });\r
5707 \r
5708                                                                 return;\r
5709                                                         }\r
5710 \r
5711                                                         // No mce_style for elements with these since they might get resized by the user\r
5712                                                         if (s.keep_values) {\r
5713                                                                 if (v && !t._isRes(v))\r
5714                                                                         e.setAttribute('data-mce-style', v, 2);\r
5715                                                                 else\r
5716                                                                         e.removeAttribute('data-mce-style', 2);\r
5717                                                         }\r
5718 \r
5719                                                         e.style.cssText = v;\r
5720                                                         break;\r
5721 \r
5722                                                 case "class":\r
5723                                                         e.className = v || ''; // Fix IE null bug\r
5724                                                         break;\r
5725 \r
5726                                                 case "src":\r
5727                                                 case "href":\r
5728                                                         if (s.keep_values) {\r
5729                                                                 if (s.url_converter)\r
5730                                                                         v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
5731 \r
5732                                                                 t.setAttrib(e, 'data-mce-' + n, v, 2);\r
5733                                                         }\r
5734 \r
5735                                                         break;\r
5736 \r
5737                                                 case "shape":\r
5738                                                         e.setAttribute('data-mce-style', v);\r
5739                                                         break;\r
5740                                         }\r
5741                                 }\r
5742                                 if (is(v) && v !== null && v.length !== 0)\r
5743                                         e.setAttribute(n, '' + v, 2);\r
5744                                 else\r
5745                                         e.removeAttribute(n, 2);\r
5746 \r
5747                                 // fire onChangeAttrib event for attributes that have changed\r
5748                                 if (tinyMCE.activeEditor && originalValue != v) {\r
5749                                         var ed = tinyMCE.activeEditor;\r
5750                                         ed.onSetAttrib.dispatch(ed, e, n, v);\r
5751                                 }\r
5752                         });\r
5753                 },\r
5754 \r
5755                 setAttribs : function(e, o) {\r
5756                         var t = this;\r
5757 \r
5758                         return this.run(e, function(e) {\r
5759                                 each(o, function(v, n) {\r
5760                                         t.setAttrib(e, n, v);\r
5761                                 });\r
5762                         });\r
5763                 },\r
5764 \r
5765                 getAttrib : function(e, n, dv) {\r
5766                         var v, t = this, undef;\r
5767 \r
5768                         e = t.get(e);\r
5769 \r
5770                         if (!e || e.nodeType !== 1)\r
5771                                 return dv === undef ? false : dv;\r
5772 \r
5773                         if (!is(dv))\r
5774                                 dv = '';\r
5775 \r
5776                         // Try the mce variant for these\r
5777                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
5778                                 v = e.getAttribute("data-mce-" + n);\r
5779 \r
5780                                 if (v)\r
5781                                         return v;\r
5782                         }\r
5783 \r
5784                         if (isIE && t.props[n]) {\r
5785                                 v = e[t.props[n]];\r
5786                                 v = v && v.nodeValue ? v.nodeValue : v;\r
5787                         }\r
5788 \r
5789                         if (!v)\r
5790                                 v = e.getAttribute(n, 2);\r
5791 \r
5792                         // Check boolean attribs\r
5793                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
5794                                 if (e[t.props[n]] === true && v === '')\r
5795                                         return n;\r
5796 \r
5797                                 return v ? n : '';\r
5798                         }\r
5799 \r
5800                         // Inner input elements will override attributes on form elements\r
5801                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
5802                                 return e.getAttributeNode(n).nodeValue;\r
5803 \r
5804                         if (n === 'style') {\r
5805                                 v = v || e.style.cssText;\r
5806 \r
5807                                 if (v) {\r
5808                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
5809 \r
5810                                         if (t.settings.keep_values && !t._isRes(v))\r
5811                                                 e.setAttribute('data-mce-style', v);\r
5812                                 }\r
5813                         }\r
5814 \r
5815                         // Remove Apple and WebKit stuff\r
5816                         if (isWebKit && n === "class" && v)\r
5817                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
5818 \r
5819                         // Handle IE issues\r
5820                         if (isIE) {\r
5821                                 switch (n) {\r
5822                                         case 'rowspan':\r
5823                                         case 'colspan':\r
5824                                                 // IE returns 1 as default value\r
5825                                                 if (v === 1)\r
5826                                                         v = '';\r
5827 \r
5828                                                 break;\r
5829 \r
5830                                         case 'size':\r
5831                                                 // IE returns +0 as default value for size\r
5832                                                 if (v === '+0' || v === 20 || v === 0)\r
5833                                                         v = '';\r
5834 \r
5835                                                 break;\r
5836 \r
5837                                         case 'width':\r
5838                                         case 'height':\r
5839                                         case 'vspace':\r
5840                                         case 'checked':\r
5841                                         case 'disabled':\r
5842                                         case 'readonly':\r
5843                                                 if (v === 0)\r
5844                                                         v = '';\r
5845 \r
5846                                                 break;\r
5847 \r
5848                                         case 'hspace':\r
5849                                                 // IE returns -1 as default value\r
5850                                                 if (v === -1)\r
5851                                                         v = '';\r
5852 \r
5853                                                 break;\r
5854 \r
5855                                         case 'maxlength':\r
5856                                         case 'tabindex':\r
5857                                                 // IE returns default value\r
5858                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
5859                                                         v = '';\r
5860 \r
5861                                                 break;\r
5862 \r
5863                                         case 'multiple':\r
5864                                         case 'compact':\r
5865                                         case 'noshade':\r
5866                                         case 'nowrap':\r
5867                                                 if (v === 65535)\r
5868                                                         return n;\r
5869 \r
5870                                                 return dv;\r
5871 \r
5872                                         case 'shape':\r
5873                                                 v = v.toLowerCase();\r
5874                                                 break;\r
5875 \r
5876                                         default:\r
5877                                                 // IE has odd anonymous function for event attributes\r
5878                                                 if (n.indexOf('on') === 0 && v)\r
5879                                                         v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
5880                                 }\r
5881                         }\r
5882 \r
5883                         return (v !== undef && v !== null && v !== '') ? '' + v : dv;\r
5884                 },\r
5885 \r
5886                 getPos : function(n, ro) {\r
5887                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
5888 \r
5889                         n = t.get(n);\r
5890                         ro = ro || d.body;\r
5891 \r
5892                         if (n) {\r
5893                                 // Use getBoundingClientRect if it exists since it's faster than looping offset nodes\r
5894                                 if (n.getBoundingClientRect) {\r
5895                                         n = n.getBoundingClientRect();\r
5896                                         e = t.boxModel ? d.documentElement : d.body;\r
5897 \r
5898                                         // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit\r
5899                                         // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position\r
5900                                         x = n.left + (d.documentElement.scrollLeft || d.body.scrollLeft) - e.clientTop;\r
5901                                         y = n.top + (d.documentElement.scrollTop || d.body.scrollTop) - e.clientLeft;\r
5902 \r
5903                                         return {x : x, y : y};\r
5904                                 }\r
5905 \r
5906                                 r = n;\r
5907                                 while (r && r != ro && r.nodeType) {\r
5908                                         x += r.offsetLeft || 0;\r
5909                                         y += r.offsetTop || 0;\r
5910                                         r = r.offsetParent;\r
5911                                 }\r
5912 \r
5913                                 r = n.parentNode;\r
5914                                 while (r && r != ro && r.nodeType) {\r
5915                                         x -= r.scrollLeft || 0;\r
5916                                         y -= r.scrollTop || 0;\r
5917                                         r = r.parentNode;\r
5918                                 }\r
5919                         }\r
5920 \r
5921                         return {x : x, y : y};\r
5922                 },\r
5923 \r
5924                 parseStyle : function(st) {\r
5925                         return this.styles.parse(st);\r
5926                 },\r
5927 \r
5928                 serializeStyle : function(o, name) {\r
5929                         return this.styles.serialize(o, name);\r
5930                 },\r
5931 \r
5932                 addStyle: function(cssText) {\r
5933                         var doc = this.doc, head;\r
5934 \r
5935                         // Create style element if needed\r
5936                         styleElm = doc.getElementById('mceDefaultStyles');\r
5937                         if (!styleElm) {\r
5938                                 styleElm = doc.createElement('style'),\r
5939                                 styleElm.id = 'mceDefaultStyles';\r
5940                                 styleElm.type = 'text/css';\r
5941 \r
5942                                 head = doc.getElementsByTagName('head')[0];\r
5943                                 if (head.firstChild) {\r
5944                                         head.insertBefore(styleElm, head.firstChild);\r
5945                                 } else {\r
5946                                         head.appendChild(styleElm);\r
5947                                 }\r
5948                         }\r
5949 \r
5950                         // Append style data to old or new style element\r
5951                         if (styleElm.styleSheet) {\r
5952                                 styleElm.styleSheet.cssText += cssText;\r
5953                         } else {\r
5954                                 styleElm.appendChild(doc.createTextNode(cssText));\r
5955                         }\r
5956                 },\r
5957 \r
5958                 loadCSS : function(u) {\r
5959                         var t = this, d = t.doc, head;\r
5960 \r
5961                         if (!u)\r
5962                                 u = '';\r
5963 \r
5964                         head = d.getElementsByTagName('head')[0];\r
5965 \r
5966                         each(u.split(','), function(u) {\r
5967                                 var link;\r
5968 \r
5969                                 if (t.files[u])\r
5970                                         return;\r
5971 \r
5972                                 t.files[u] = true;\r
5973                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
5974 \r
5975                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
5976                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
5977                                 // It's ugly but it seems to work fine.\r
5978                                 if (isIE && !tinymce.isIE11 && d.documentMode && d.recalc) {\r
5979                                         link.onload = function() {\r
5980                                                 if (d.recalc)\r
5981                                                         d.recalc();\r
5982 \r
5983                                                 link.onload = null;\r
5984                                         };\r
5985                                 }\r
5986 \r
5987                                 head.appendChild(link);\r
5988                         });\r
5989                 },\r
5990 \r
5991                 addClass : function(e, c) {\r
5992                         return this.run(e, function(e) {\r
5993                                 var o;\r
5994 \r
5995                                 if (!c)\r
5996                                         return 0;\r
5997 \r
5998                                 if (this.hasClass(e, c))\r
5999                                         return e.className;\r
6000 \r
6001                                 o = this.removeClass(e, c);\r
6002 \r
6003                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
6004                         });\r
6005                 },\r
6006 \r
6007                 removeClass : function(e, c) {\r
6008                         var t = this, re;\r
6009 \r
6010                         return t.run(e, function(e) {\r
6011                                 var v;\r
6012 \r
6013                                 if (t.hasClass(e, c)) {\r
6014                                         if (!re)\r
6015                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
6016 \r
6017                                         v = e.className.replace(re, ' ');\r
6018                                         v = tinymce.trim(v != ' ' ? v : '');\r
6019 \r
6020                                         e.className = v;\r
6021 \r
6022                                         // Empty class attr\r
6023                                         if (!v) {\r
6024                                                 e.removeAttribute('class');\r
6025                                                 e.removeAttribute('className');\r
6026                                         }\r
6027 \r
6028                                         return v;\r
6029                                 }\r
6030 \r
6031                                 return e.className;\r
6032                         });\r
6033                 },\r
6034 \r
6035                 hasClass : function(n, c) {\r
6036                         n = this.get(n);\r
6037 \r
6038                         if (!n || !c)\r
6039                                 return false;\r
6040 \r
6041                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
6042                 },\r
6043 \r
6044                 show : function(e) {\r
6045                         return this.setStyle(e, 'display', 'block');\r
6046                 },\r
6047 \r
6048                 hide : function(e) {\r
6049                         return this.setStyle(e, 'display', 'none');\r
6050                 },\r
6051 \r
6052                 isHidden : function(e) {\r
6053                         e = this.get(e);\r
6054 \r
6055                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
6056                 },\r
6057 \r
6058                 uniqueId : function(p) {\r
6059                         return (!p ? 'mce_' : p) + (this.counter++);\r
6060                 },\r
6061 \r
6062                 setHTML : function(element, html) {\r
6063                         var self = this;\r
6064 \r
6065                         return self.run(element, function(element) {\r
6066                                 if (isIE) {\r
6067                                         // Remove all child nodes, IE keeps empty text nodes in DOM\r
6068                                         while (element.firstChild)\r
6069                                                 element.removeChild(element.firstChild);\r
6070 \r
6071                                         try {\r
6072                                                 // IE will remove comments from the beginning\r
6073                                                 // unless you padd the contents with something\r
6074                                                 element.innerHTML = '<br />' + html;\r
6075                                                 element.removeChild(element.firstChild);\r
6076                                         } catch (ex) {\r
6077                                                 // 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
6078                                                 // This seems to fix this problem\r
6079 \r
6080                                                 // Create new div with HTML contents and a BR infront to keep comments\r
6081                                                 var newElement = self.create('div');\r
6082                                                 newElement.innerHTML = '<br />' + html;\r
6083 \r
6084                                                 // Add all children from div to target\r
6085                                                 each (tinymce.grep(newElement.childNodes), function(node, i) {\r
6086                                                         // Skip br element\r
6087                                                         if (i && element.canHaveHTML)\r
6088                                                                 element.appendChild(node);\r
6089                                                 });\r
6090                                         }\r
6091                                 } else\r
6092                                         element.innerHTML = html;\r
6093 \r
6094                                 return html;\r
6095                         });\r
6096                 },\r
6097 \r
6098                 getOuterHTML : function(elm) {\r
6099                         var doc, self = this;\r
6100 \r
6101                         elm = self.get(elm);\r
6102 \r
6103                         if (!elm)\r
6104                                 return null;\r
6105 \r
6106                         if (elm.nodeType === 1 && self.hasOuterHTML)\r
6107                                 return elm.outerHTML;\r
6108 \r
6109                         doc = (elm.ownerDocument || self.doc).createElement("body");\r
6110                         doc.appendChild(elm.cloneNode(true));\r
6111 \r
6112                         return doc.innerHTML;\r
6113                 },\r
6114 \r
6115                 setOuterHTML : function(e, h, d) {\r
6116                         var t = this;\r
6117 \r
6118                         function setHTML(e, h, d) {\r
6119                                 var n, tp;\r
6120 \r
6121                                 tp = d.createElement("body");\r
6122                                 tp.innerHTML = h;\r
6123 \r
6124                                 n = tp.lastChild;\r
6125                                 while (n) {\r
6126                                         t.insertAfter(n.cloneNode(true), e);\r
6127                                         n = n.previousSibling;\r
6128                                 }\r
6129 \r
6130                                 t.remove(e);\r
6131                         };\r
6132 \r
6133                         return this.run(e, function(e) {\r
6134                                 e = t.get(e);\r
6135 \r
6136                                 // Only set HTML on elements\r
6137                                 if (e.nodeType == 1) {\r
6138                                         d = d || e.ownerDocument || t.doc;\r
6139 \r
6140                                         if (isIE) {\r
6141                                                 try {\r
6142                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
6143                                                         if (isIE && e.nodeType == 1)\r
6144                                                                 e.outerHTML = h;\r
6145                                                         else\r
6146                                                                 setHTML(e, h, d);\r
6147                                                 } catch (ex) {\r
6148                                                         // Fix for unknown runtime error\r
6149                                                         setHTML(e, h, d);\r
6150                                                 }\r
6151                                         } else\r
6152                                                 setHTML(e, h, d);\r
6153                                 }\r
6154                         });\r
6155                 },\r
6156 \r
6157                 decode : Entities.decode,\r
6158 \r
6159                 encode : Entities.encodeAllRaw,\r
6160 \r
6161                 insertAfter : function(node, reference_node) {\r
6162                         reference_node = this.get(reference_node);\r
6163 \r
6164                         return this.run(node, function(node) {\r
6165                                 var parent, nextSibling;\r
6166 \r
6167                                 parent = reference_node.parentNode;\r
6168                                 nextSibling = reference_node.nextSibling;\r
6169 \r
6170                                 if (nextSibling)\r
6171                                         parent.insertBefore(node, nextSibling);\r
6172                                 else\r
6173                                         parent.appendChild(node);\r
6174 \r
6175                                 return node;\r
6176                         });\r
6177                 },\r
6178 \r
6179                 replace : function(n, o, k) {\r
6180                         var t = this;\r
6181 \r
6182                         if (is(o, 'array'))\r
6183                                 n = n.cloneNode(true);\r
6184 \r
6185                         return t.run(o, function(o) {\r
6186                                 if (k) {\r
6187                                         each(tinymce.grep(o.childNodes), function(c) {\r
6188                                                 n.appendChild(c);\r
6189                                         });\r
6190                                 }\r
6191 \r
6192                                 return o.parentNode.replaceChild(n, o);\r
6193                         });\r
6194                 },\r
6195 \r
6196                 rename : function(elm, name) {\r
6197                         var t = this, newElm;\r
6198 \r
6199                         if (elm.nodeName != name.toUpperCase()) {\r
6200                                 // Rename block element\r
6201                                 newElm = t.create(name);\r
6202 \r
6203                                 // Copy attribs to new block\r
6204                                 each(t.getAttribs(elm), function(attr_node) {\r
6205                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
6206                                 });\r
6207 \r
6208                                 // Replace block\r
6209                                 t.replace(newElm, elm, 1);\r
6210                         }\r
6211 \r
6212                         return newElm || elm;\r
6213                 },\r
6214 \r
6215                 findCommonAncestor : function(a, b) {\r
6216                         var ps = a, pe;\r
6217 \r
6218                         while (ps) {\r
6219                                 pe = b;\r
6220 \r
6221                                 while (pe && ps != pe)\r
6222                                         pe = pe.parentNode;\r
6223 \r
6224                                 if (ps == pe)\r
6225                                         break;\r
6226 \r
6227                                 ps = ps.parentNode;\r
6228                         }\r
6229 \r
6230                         if (!ps && a.ownerDocument)\r
6231                                 return a.ownerDocument.documentElement;\r
6232 \r
6233                         return ps;\r
6234                 },\r
6235 \r
6236                 toHex : function(s) {\r
6237                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
6238 \r
6239                         function hex(s) {\r
6240                                 s = parseInt(s, 10).toString(16);\r
6241 \r
6242                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
6243                         };\r
6244 \r
6245                         if (c) {\r
6246                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
6247 \r
6248                                 return s;\r
6249                         }\r
6250 \r
6251                         return s;\r
6252                 },\r
6253 \r
6254                 getClasses : function() {\r
6255                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
6256 \r
6257                         if (t.classes)\r
6258                                 return t.classes;\r
6259 \r
6260                         function addClasses(s) {\r
6261                                 // IE style imports\r
6262                                 each(s.imports, function(r) {\r
6263                                         addClasses(r);\r
6264                                 });\r
6265 \r
6266                                 each(s.cssRules || s.rules, function(r) {\r
6267                                         // Real type or fake it on IE\r
6268                                         switch (r.type || 1) {\r
6269                                                 // Rule\r
6270                                                 case 1:\r
6271                                                         if (r.selectorText) {\r
6272                                                                 each(r.selectorText.split(','), function(v) {\r
6273                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
6274 \r
6275                                                                         // Is internal or it doesn't contain a class\r
6276                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
6277                                                                                 return;\r
6278 \r
6279                                                                         // Remove everything but class name\r
6280                                                                         ov = v;\r
6281                                                                         v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
6282 \r
6283                                                                         // Filter classes\r
6284                                                                         if (f && !(v = f(v, ov)))\r
6285                                                                                 return;\r
6286 \r
6287                                                                         if (!lo[v]) {\r
6288                                                                                 cl.push({'class' : v});\r
6289                                                                                 lo[v] = 1;\r
6290                                                                         }\r
6291                                                                 });\r
6292                                                         }\r
6293                                                         break;\r
6294 \r
6295                                                 // Import\r
6296                                                 case 3:\r
6297                                                         try {\r
6298                                                                 addClasses(r.styleSheet);\r
6299                                                         } catch (ex) {\r
6300                                                                 // Ignore\r
6301                                                         }\r
6302 \r
6303                                                         break;\r
6304                                         }\r
6305                                 });\r
6306                         };\r
6307 \r
6308                         try {\r
6309                                 each(t.doc.styleSheets, addClasses);\r
6310                         } catch (ex) {\r
6311                                 // Ignore\r
6312                         }\r
6313 \r
6314                         if (cl.length > 0)\r
6315                                 t.classes = cl;\r
6316 \r
6317                         return cl;\r
6318                 },\r
6319 \r
6320                 run : function(e, f, s) {\r
6321                         var t = this, o;\r
6322 \r
6323                         if (t.doc && typeof(e) === 'string')\r
6324                                 e = t.get(e);\r
6325 \r
6326                         if (!e)\r
6327                                 return false;\r
6328 \r
6329                         s = s || this;\r
6330                         if (!e.nodeType && (e.length || e.length === 0)) {\r
6331                                 o = [];\r
6332 \r
6333                                 each(e, function(e, i) {\r
6334                                         if (e) {\r
6335                                                 if (typeof(e) == 'string')\r
6336                                                         e = t.doc.getElementById(e);\r
6337 \r
6338                                                 o.push(f.call(s, e, i));\r
6339                                         }\r
6340                                 });\r
6341 \r
6342                                 return o;\r
6343                         }\r
6344 \r
6345                         return f.call(s, e);\r
6346                 },\r
6347 \r
6348                 getAttribs : function(n) {\r
6349                         var o;\r
6350 \r
6351                         n = this.get(n);\r
6352 \r
6353                         if (!n)\r
6354                                 return [];\r
6355 \r
6356                         if (isIE) {\r
6357                                 o = [];\r
6358 \r
6359                                 // Object will throw exception in IE\r
6360                                 if (n.nodeName == 'OBJECT')\r
6361                                         return n.attributes;\r
6362 \r
6363                                 // IE doesn't keep the selected attribute if you clone option elements\r
6364                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
6365                                         o.push({specified : 1, nodeName : 'selected'});\r
6366 \r
6367                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
6368                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
6369                                         o.push({specified : 1, nodeName : a});\r
6370                                 });\r
6371 \r
6372                                 return o;\r
6373                         }\r
6374 \r
6375                         return n.attributes;\r
6376                 },\r
6377 \r
6378                 isEmpty : function(node, elements) {\r
6379                         var self = this, i, attributes, type, walker, name, brCount = 0;\r
6380 \r
6381                         node = node.firstChild;\r
6382                         if (node) {\r
6383                                 walker = new tinymce.dom.TreeWalker(node, node.parentNode);\r
6384                                 elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;\r
6385 \r
6386                                 do {\r
6387                                         type = node.nodeType;\r
6388 \r
6389                                         if (type === 1) {\r
6390                                                 // Ignore bogus elements\r
6391                                                 if (node.getAttribute('data-mce-bogus'))\r
6392                                                         continue;\r
6393 \r
6394                                                 // Keep empty elements like <img />\r
6395                                                 name = node.nodeName.toLowerCase();\r
6396                                                 if (elements && elements[name]) {\r
6397                                                         // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>\r
6398                                                         if (name === 'br') {\r
6399                                                                 brCount++;\r
6400                                                                 continue;\r
6401                                                         }\r
6402 \r
6403                                                         return false;\r
6404                                                 }\r
6405 \r
6406                                                 // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>\r
6407                                                 attributes = self.getAttribs(node);\r
6408                                                 i = node.attributes.length;\r
6409                                                 while (i--) {\r
6410                                                         name = node.attributes[i].nodeName;\r
6411                                                         if (name === "name" || name === 'data-mce-bookmark')\r
6412                                                                 return false;\r
6413                                                 }\r
6414                                         }\r
6415 \r
6416                                         // Keep comment nodes\r
6417                                         if (type == 8)\r
6418                                                 return false;\r
6419 \r
6420                                         // Keep non whitespace text nodes\r
6421                                         if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))\r
6422                                                 return false;\r
6423                                 } while (node = walker.next());\r
6424                         }\r
6425 \r
6426                         return brCount <= 1;\r
6427                 },\r
6428 \r
6429                 destroy : function(s) {\r
6430                         var t = this;\r
6431 \r
6432                         t.win = t.doc = t.root = t.events = t.frag = null;\r
6433 \r
6434                         // Manual destroy then remove unload handler\r
6435                         if (!s)\r
6436                                 tinymce.removeUnload(t.destroy);\r
6437                 },\r
6438 \r
6439                 createRng : function() {\r
6440                         var d = this.doc;\r
6441 \r
6442                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
6443                 },\r
6444 \r
6445                 nodeIndex : function(node, normalized) {\r
6446                         var idx = 0, lastNodeType, lastNode, nodeType;\r
6447 \r
6448                         if (node) {\r
6449                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
6450                                         nodeType = node.nodeType;\r
6451 \r
6452                                         // Normalize text nodes\r
6453                                         if (normalized && nodeType == 3) {\r
6454                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
6455                                                         continue;\r
6456                                         }\r
6457                                         idx++;\r
6458                                         lastNodeType = nodeType;\r
6459                                 }\r
6460                         }\r
6461 \r
6462                         return idx;\r
6463                 },\r
6464 \r
6465                 split : function(pe, e, re) {\r
6466                         var t = this, r = t.createRng(), bef, aft, pa;\r
6467 \r
6468                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
6469                         // but we don't want that in our code since it serves no purpose for the end user\r
6470                         // For example if this is chopped:\r
6471                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
6472                         // would produce:\r
6473                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
6474                         // this function will then trim of empty edges and produce:\r
6475                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
6476                         function trim(node) {\r
6477                                 var i, children = node.childNodes, type = node.nodeType;\r
6478 \r
6479                                 function surroundedBySpans(node) {\r
6480                                         var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';\r
6481                                         var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';\r
6482                                         return previousIsSpan && nextIsSpan;\r
6483                                 }\r
6484 \r
6485                                 if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')\r
6486                                         return;\r
6487 \r
6488                                 for (i = children.length - 1; i >= 0; i--)\r
6489                                         trim(children[i]);\r
6490 \r
6491                                 if (type != 9) {\r
6492                                         // Keep non whitespace text nodes\r
6493                                         if (type == 3 && node.nodeValue.length > 0) {\r
6494                                                 // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
6495                                                 // Also keep text nodes with only spaces if surrounded by spans.\r
6496                                                 // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b\r
6497                                                 var trimmedLength = tinymce.trim(node.nodeValue).length;\r
6498                                                 if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node))\r
6499                                                         return;\r
6500                                         } else if (type == 1) {\r
6501                                                 // If the only child is a bookmark then move it up\r
6502                                                 children = node.childNodes;\r
6503                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')\r
6504                                                         node.parentNode.insertBefore(children[0], node);\r
6505 \r
6506                                                 // Keep non empty elements or img, hr etc\r
6507                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
6508                                                         return;\r
6509                                         }\r
6510 \r
6511                                         t.remove(node);\r
6512                                 }\r
6513 \r
6514                                 return node;\r
6515                         };\r
6516 \r
6517                         if (pe && e) {\r
6518                                 // Get before chunk\r
6519                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
6520                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
6521                                 bef = r.extractContents();\r
6522 \r
6523                                 // Get after chunk\r
6524                                 r = t.createRng();\r
6525                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
6526                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
6527                                 aft = r.extractContents();\r
6528 \r
6529                                 // Insert before chunk\r
6530                                 pa = pe.parentNode;\r
6531                                 pa.insertBefore(trim(bef), pe);\r
6532 \r
6533                                 // Insert middle chunk\r
6534                                 if (re)\r
6535                                 pa.replaceChild(re, e);\r
6536                         else\r
6537                                 pa.insertBefore(e, pe);\r
6538 \r
6539                                 // Insert after chunk\r
6540                                 pa.insertBefore(trim(aft), pe);\r
6541                                 t.remove(pe);\r
6542 \r
6543                                 return re || e;\r
6544                         }\r
6545                 },\r
6546 \r
6547                 bind : function(target, name, func, scope) {\r
6548                         return this.events.add(target, name, func, scope || this);\r
6549                 },\r
6550 \r
6551                 unbind : function(target, name, func) {\r
6552                         return this.events.remove(target, name, func);\r
6553                 },\r
6554 \r
6555                 fire : function(target, name, evt) {\r
6556                         return this.events.fire(target, name, evt);\r
6557                 },\r
6558 \r
6559                 // Returns the content editable state of a node\r
6560                 getContentEditable: function(node) {\r
6561                         var contentEditable;\r
6562 \r
6563                         // Check type\r
6564                         if (node.nodeType != 1) {\r
6565                                 return null;\r
6566                         }\r
6567 \r
6568                         // Check for fake content editable\r
6569                         contentEditable = node.getAttribute("data-mce-contenteditable");\r
6570                         if (contentEditable && contentEditable !== "inherit") {\r
6571                                 return contentEditable;\r
6572                         }\r
6573 \r
6574                         // Check for real content editable\r
6575                         return node.contentEditable !== "inherit" ? node.contentEditable : null;\r
6576                 },\r
6577 \r
6578 \r
6579                 _findSib : function(node, selector, name) {\r
6580                         var t = this, f = selector;\r
6581 \r
6582                         if (node) {\r
6583                                 // If expression make a function of it using is\r
6584                                 if (is(f, 'string')) {\r
6585                                         f = function(node) {\r
6586                                                 return t.is(node, selector);\r
6587                                         };\r
6588                                 }\r
6589 \r
6590                                 // Loop all siblings\r
6591                                 for (node = node[name]; node; node = node[name]) {\r
6592                                         if (f(node))\r
6593                                                 return node;\r
6594                                 }\r
6595                         }\r
6596 \r
6597                         return null;\r
6598                 },\r
6599 \r
6600                 _isRes : function(c) {\r
6601                         // Is live resizble element\r
6602                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
6603                 }\r
6604 \r
6605                 /*\r
6606                 walk : function(n, f, s) {\r
6607                         var d = this.doc, w;\r
6608 \r
6609                         if (d.createTreeWalker) {\r
6610                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
6611 \r
6612                                 while ((n = w.nextNode()) != null)\r
6613                                         f.call(s || this, n);\r
6614                         } else\r
6615                                 tinymce.walk(n, f, 'childNodes', s);\r
6616                 }\r
6617                 */\r
6618 \r
6619                 /*\r
6620                 toRGB : function(s) {\r
6621                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
6622 \r
6623                         if (c) {\r
6624                                 // #FFF -> #FFFFFF\r
6625                                 if (!is(c[3]))\r
6626                                         c[3] = c[2] = c[1];\r
6627 \r
6628                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
6629                         }\r
6630 \r
6631                         return s;\r
6632                 }\r
6633                 */\r
6634         });\r
6635 \r
6636         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
6637 })(tinymce);\r
6638 \r
6639 (function(ns) {\r
6640         // Range constructor\r
6641         function Range(dom) {\r
6642                 var t = this,\r
6643                         doc = dom.doc,\r
6644                         EXTRACT = 0,\r
6645                         CLONE = 1,\r
6646                         DELETE = 2,\r
6647                         TRUE = true,\r
6648                         FALSE = false,\r
6649                         START_OFFSET = 'startOffset',\r
6650                         START_CONTAINER = 'startContainer',\r
6651                         END_CONTAINER = 'endContainer',\r
6652                         END_OFFSET = 'endOffset',\r
6653                         extend = tinymce.extend,\r
6654                         nodeIndex = dom.nodeIndex;\r
6655 \r
6656                 extend(t, {\r
6657                         // Inital states\r
6658                         startContainer : doc,\r
6659                         startOffset : 0,\r
6660                         endContainer : doc,\r
6661                         endOffset : 0,\r
6662                         collapsed : TRUE,\r
6663                         commonAncestorContainer : doc,\r
6664 \r
6665                         // Range constants\r
6666                         START_TO_START : 0,\r
6667                         START_TO_END : 1,\r
6668                         END_TO_END : 2,\r
6669                         END_TO_START : 3,\r
6670 \r
6671                         // Public methods\r
6672                         setStart : setStart,\r
6673                         setEnd : setEnd,\r
6674                         setStartBefore : setStartBefore,\r
6675                         setStartAfter : setStartAfter,\r
6676                         setEndBefore : setEndBefore,\r
6677                         setEndAfter : setEndAfter,\r
6678                         collapse : collapse,\r
6679                         selectNode : selectNode,\r
6680                         selectNodeContents : selectNodeContents,\r
6681                         compareBoundaryPoints : compareBoundaryPoints,\r
6682                         deleteContents : deleteContents,\r
6683                         extractContents : extractContents,\r
6684                         cloneContents : cloneContents,\r
6685                         insertNode : insertNode,\r
6686                         surroundContents : surroundContents,\r
6687                         cloneRange : cloneRange,\r
6688                         toStringIE : toStringIE\r
6689                 });\r
6690 \r
6691                 function createDocumentFragment() {\r
6692                         return doc.createDocumentFragment();\r
6693                 };\r
6694 \r
6695                 function setStart(n, o) {\r
6696                         _setEndPoint(TRUE, n, o);\r
6697                 };\r
6698 \r
6699                 function setEnd(n, o) {\r
6700                         _setEndPoint(FALSE, n, o);\r
6701                 };\r
6702 \r
6703                 function setStartBefore(n) {\r
6704                         setStart(n.parentNode, nodeIndex(n));\r
6705                 };\r
6706 \r
6707                 function setStartAfter(n) {\r
6708                         setStart(n.parentNode, nodeIndex(n) + 1);\r
6709                 };\r
6710 \r
6711                 function setEndBefore(n) {\r
6712                         setEnd(n.parentNode, nodeIndex(n));\r
6713                 };\r
6714 \r
6715                 function setEndAfter(n) {\r
6716                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
6717                 };\r
6718 \r
6719                 function collapse(ts) {\r
6720                         if (ts) {\r
6721                                 t[END_CONTAINER] = t[START_CONTAINER];\r
6722                                 t[END_OFFSET] = t[START_OFFSET];\r
6723                         } else {\r
6724                                 t[START_CONTAINER] = t[END_CONTAINER];\r
6725                                 t[START_OFFSET] = t[END_OFFSET];\r
6726                         }\r
6727 \r
6728                         t.collapsed = TRUE;\r
6729                 };\r
6730 \r
6731                 function selectNode(n) {\r
6732                         setStartBefore(n);\r
6733                         setEndAfter(n);\r
6734                 };\r
6735 \r
6736                 function selectNodeContents(n) {\r
6737                         setStart(n, 0);\r
6738                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
6739                 };\r
6740 \r
6741                 function compareBoundaryPoints(h, r) {\r
6742                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],\r
6743                         rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;\r
6744 \r
6745                         // Check START_TO_START\r
6746                         if (h === 0)\r
6747                                 return _compareBoundaryPoints(sc, so, rsc, rso);\r
6748         \r
6749                         // Check START_TO_END\r
6750                         if (h === 1)\r
6751                                 return _compareBoundaryPoints(ec, eo, rsc, rso);\r
6752         \r
6753                         // Check END_TO_END\r
6754                         if (h === 2)\r
6755                                 return _compareBoundaryPoints(ec, eo, rec, reo);\r
6756         \r
6757                         // Check END_TO_START\r
6758                         if (h === 3) \r
6759                                 return _compareBoundaryPoints(sc, so, rec, reo);\r
6760                 };\r
6761 \r
6762                 function deleteContents() {\r
6763                         _traverse(DELETE);\r
6764                 };\r
6765 \r
6766                 function extractContents() {\r
6767                         return _traverse(EXTRACT);\r
6768                 };\r
6769 \r
6770                 function cloneContents() {\r
6771                         return _traverse(CLONE);\r
6772                 };\r
6773 \r
6774                 function insertNode(n) {\r
6775                         var startContainer = this[START_CONTAINER],\r
6776                                 startOffset = this[START_OFFSET], nn, o;\r
6777 \r
6778                         // Node is TEXT_NODE or CDATA\r
6779                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
6780                                 if (!startOffset) {\r
6781                                         // At the start of text\r
6782                                         startContainer.parentNode.insertBefore(n, startContainer);\r
6783                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
6784                                         // At the end of text\r
6785                                         dom.insertAfter(n, startContainer);\r
6786                                 } else {\r
6787                                         // Middle, need to split\r
6788                                         nn = startContainer.splitText(startOffset);\r
6789                                         startContainer.parentNode.insertBefore(n, nn);\r
6790                                 }\r
6791                         } else {\r
6792                                 // Insert element node\r
6793                                 if (startContainer.childNodes.length > 0)\r
6794                                         o = startContainer.childNodes[startOffset];\r
6795 \r
6796                                 if (o)\r
6797                                         startContainer.insertBefore(n, o);\r
6798                                 else\r
6799                                         startContainer.appendChild(n);\r
6800                         }\r
6801                 };\r
6802 \r
6803                 function surroundContents(n) {\r
6804                         var f = t.extractContents();\r
6805 \r
6806                         t.insertNode(n);\r
6807                         n.appendChild(f);\r
6808                         t.selectNode(n);\r
6809                 };\r
6810 \r
6811                 function cloneRange() {\r
6812                         return extend(new Range(dom), {\r
6813                                 startContainer : t[START_CONTAINER],\r
6814                                 startOffset : t[START_OFFSET],\r
6815                                 endContainer : t[END_CONTAINER],\r
6816                                 endOffset : t[END_OFFSET],\r
6817                                 collapsed : t.collapsed,\r
6818                                 commonAncestorContainer : t.commonAncestorContainer\r
6819                         });\r
6820                 };\r
6821 \r
6822                 // Private methods\r
6823 \r
6824                 function _getSelectedNode(container, offset) {\r
6825                         var child;\r
6826 \r
6827                         if (container.nodeType == 3 /* TEXT_NODE */)\r
6828                                 return container;\r
6829 \r
6830                         if (offset < 0)\r
6831                                 return container;\r
6832 \r
6833                         child = container.firstChild;\r
6834                         while (child && offset > 0) {\r
6835                                 --offset;\r
6836                                 child = child.nextSibling;\r
6837                         }\r
6838 \r
6839                         if (child)\r
6840                                 return child;\r
6841 \r
6842                         return container;\r
6843                 };\r
6844 \r
6845                 function _isCollapsed() {\r
6846                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
6847                 };\r
6848 \r
6849                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
6850                         var c, offsetC, n, cmnRoot, childA, childB;\r
6851                         \r
6852                         // In the first case the boundary-points have the same container. A is before B\r
6853                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
6854                         // equal to the offset of B, and A is after B if its offset is greater than the\r
6855                         // offset of B.\r
6856                         if (containerA == containerB) {\r
6857                                 if (offsetA == offsetB)\r
6858                                         return 0; // equal\r
6859 \r
6860                                 if (offsetA < offsetB)\r
6861                                         return -1; // before\r
6862 \r
6863                                 return 1; // after\r
6864                         }\r
6865 \r
6866                         // In the second case a child node C of the container of A is an ancestor\r
6867                         // container of B. In this case, A is before B if the offset of A is less than or\r
6868                         // equal to the index of the child node C and A is after B otherwise.\r
6869                         c = containerB;\r
6870                         while (c && c.parentNode != containerA)\r
6871                                 c = c.parentNode;\r
6872 \r
6873                         if (c) {\r
6874                                 offsetC = 0;\r
6875                                 n = containerA.firstChild;\r
6876 \r
6877                                 while (n != c && offsetC < offsetA) {\r
6878                                         offsetC++;\r
6879                                         n = n.nextSibling;\r
6880                                 }\r
6881 \r
6882                                 if (offsetA <= offsetC)\r
6883                                         return -1; // before\r
6884 \r
6885                                 return 1; // after\r
6886                         }\r
6887 \r
6888                         // In the third case a child node C of the container of B is an ancestor container\r
6889                         // of A. In this case, A is before B if the index of the child node C is less than\r
6890                         // the offset of B and A is after B otherwise.\r
6891                         c = containerA;\r
6892                         while (c && c.parentNode != containerB) {\r
6893                                 c = c.parentNode;\r
6894                         }\r
6895 \r
6896                         if (c) {\r
6897                                 offsetC = 0;\r
6898                                 n = containerB.firstChild;\r
6899 \r
6900                                 while (n != c && offsetC < offsetB) {\r
6901                                         offsetC++;\r
6902                                         n = n.nextSibling;\r
6903                                 }\r
6904 \r
6905                                 if (offsetC < offsetB)\r
6906                                         return -1; // before\r
6907 \r
6908                                 return 1; // after\r
6909                         }\r
6910 \r
6911                         // In the fourth case, none of three other cases hold: the containers of A and B\r
6912                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
6913                         // the container of A is before the container of B in a pre-order traversal of the\r
6914                         // Ranges' context tree and A is after B otherwise.\r
6915                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
6916                         childA = containerA;\r
6917 \r
6918                         while (childA && childA.parentNode != cmnRoot)\r
6919                                 childA = childA.parentNode;\r
6920 \r
6921                         if (!childA)\r
6922                                 childA = cmnRoot;\r
6923 \r
6924                         childB = containerB;\r
6925                         while (childB && childB.parentNode != cmnRoot)\r
6926                                 childB = childB.parentNode;\r
6927 \r
6928                         if (!childB)\r
6929                                 childB = cmnRoot;\r
6930 \r
6931                         if (childA == childB)\r
6932                                 return 0; // equal\r
6933 \r
6934                         n = cmnRoot.firstChild;\r
6935                         while (n) {\r
6936                                 if (n == childA)\r
6937                                         return -1; // before\r
6938 \r
6939                                 if (n == childB)\r
6940                                         return 1; // after\r
6941 \r
6942                                 n = n.nextSibling;\r
6943                         }\r
6944                 };\r
6945 \r
6946                 function _setEndPoint(st, n, o) {\r
6947                         var ec, sc;\r
6948 \r
6949                         if (st) {\r
6950                                 t[START_CONTAINER] = n;\r
6951                                 t[START_OFFSET] = o;\r
6952                         } else {\r
6953                                 t[END_CONTAINER] = n;\r
6954                                 t[END_OFFSET] = o;\r
6955                         }\r
6956 \r
6957                         // If one boundary-point of a Range is set to have a root container\r
6958                         // other than the current one for the Range, the Range is collapsed to\r
6959                         // the new position. This enforces the restriction that both boundary-\r
6960                         // points of a Range must have the same root container.\r
6961                         ec = t[END_CONTAINER];\r
6962                         while (ec.parentNode)\r
6963                                 ec = ec.parentNode;\r
6964 \r
6965                         sc = t[START_CONTAINER];\r
6966                         while (sc.parentNode)\r
6967                                 sc = sc.parentNode;\r
6968 \r
6969                         if (sc == ec) {\r
6970                                 // The start position of a Range is guaranteed to never be after the\r
6971                                 // end position. To enforce this restriction, if the start is set to\r
6972                                 // be at a position after the end, the Range is collapsed to that\r
6973                                 // position.\r
6974                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
6975                                         t.collapse(st);\r
6976                         } else\r
6977                                 t.collapse(st);\r
6978 \r
6979                         t.collapsed = _isCollapsed();\r
6980                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
6981                 };\r
6982 \r
6983                 function _traverse(how) {\r
6984                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
6985 \r
6986                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
6987                                 return _traverseSameContainer(how);\r
6988 \r
6989                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
6990                                 if (p == t[START_CONTAINER])\r
6991                                         return _traverseCommonStartContainer(c, how);\r
6992 \r
6993                                 ++endContainerDepth;\r
6994                         }\r
6995 \r
6996                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
6997                                 if (p == t[END_CONTAINER])\r
6998                                         return _traverseCommonEndContainer(c, how);\r
6999 \r
7000                                 ++startContainerDepth;\r
7001                         }\r
7002 \r
7003                         depthDiff = startContainerDepth - endContainerDepth;\r
7004 \r
7005                         startNode = t[START_CONTAINER];\r
7006                         while (depthDiff > 0) {\r
7007                                 startNode = startNode.parentNode;\r
7008                                 depthDiff--;\r
7009                         }\r
7010 \r
7011                         endNode = t[END_CONTAINER];\r
7012                         while (depthDiff < 0) {\r
7013                                 endNode = endNode.parentNode;\r
7014                                 depthDiff++;\r
7015                         }\r
7016 \r
7017                         // ascend the ancestor hierarchy until we have a common parent.\r
7018                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
7019                                 startNode = sp;\r
7020                                 endNode = ep;\r
7021                         }\r
7022 \r
7023                         return _traverseCommonAncestors(startNode, endNode, how);\r
7024                 };\r
7025 \r
7026                  function _traverseSameContainer(how) {\r
7027                         var frag, s, sub, n, cnt, sibling, xferNode, start, len;\r
7028 \r
7029                         if (how != DELETE)\r
7030                                 frag = createDocumentFragment();\r
7031 \r
7032                         // If selection is empty, just return the fragment\r
7033                         if (t[START_OFFSET] == t[END_OFFSET])\r
7034                                 return frag;\r
7035 \r
7036                         // Text node needs special case handling\r
7037                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
7038                                 // get the substring\r
7039                                 s = t[START_CONTAINER].nodeValue;\r
7040                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
7041 \r
7042                                 // set the original text node to its new value\r
7043                                 if (how != CLONE) {\r
7044                                         n = t[START_CONTAINER];\r
7045                                         start = t[START_OFFSET];\r
7046                                         len = t[END_OFFSET] - t[START_OFFSET];\r
7047 \r
7048                                         if (start === 0 && len >= n.nodeValue.length - 1) {\r
7049                                                 n.parentNode.removeChild(n);\r
7050                                         } else {\r
7051                                                 n.deleteData(start, len);\r
7052                                         }\r
7053 \r
7054                                         // Nothing is partially selected, so collapse to start point\r
7055                                         t.collapse(TRUE);\r
7056                                 }\r
7057 \r
7058                                 if (how == DELETE)\r
7059                                         return;\r
7060 \r
7061                                 if (sub.length > 0) {\r
7062                                         frag.appendChild(doc.createTextNode(sub));\r
7063                                 }\r
7064 \r
7065                                 return frag;\r
7066                         }\r
7067 \r
7068                         // Copy nodes between the start/end offsets.\r
7069                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
7070                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
7071 \r
7072                         while (n && cnt > 0) {\r
7073                                 sibling = n.nextSibling;\r
7074                                 xferNode = _traverseFullySelected(n, how);\r
7075 \r
7076                                 if (frag)\r
7077                                         frag.appendChild( xferNode );\r
7078 \r
7079                                 --cnt;\r
7080                                 n = sibling;\r
7081                         }\r
7082 \r
7083                         // Nothing is partially selected, so collapse to start point\r
7084                         if (how != CLONE)\r
7085                                 t.collapse(TRUE);\r
7086 \r
7087                         return frag;\r
7088                 };\r
7089 \r
7090                 function _traverseCommonStartContainer(endAncestor, how) {\r
7091                         var frag, n, endIdx, cnt, sibling, xferNode;\r
7092 \r
7093                         if (how != DELETE)\r
7094                                 frag = createDocumentFragment();\r
7095 \r
7096                         n = _traverseRightBoundary(endAncestor, how);\r
7097 \r
7098                         if (frag)\r
7099                                 frag.appendChild(n);\r
7100 \r
7101                         endIdx = nodeIndex(endAncestor);\r
7102                         cnt = endIdx - t[START_OFFSET];\r
7103 \r
7104                         if (cnt <= 0) {\r
7105                                 // Collapse to just before the endAncestor, which\r
7106                                 // is partially selected.\r
7107                                 if (how != CLONE) {\r
7108                                         t.setEndBefore(endAncestor);\r
7109                                         t.collapse(FALSE);\r
7110                                 }\r
7111 \r
7112                                 return frag;\r
7113                         }\r
7114 \r
7115                         n = endAncestor.previousSibling;\r
7116                         while (cnt > 0) {\r
7117                                 sibling = n.previousSibling;\r
7118                                 xferNode = _traverseFullySelected(n, how);\r
7119 \r
7120                                 if (frag)\r
7121                                         frag.insertBefore(xferNode, frag.firstChild);\r
7122 \r
7123                                 --cnt;\r
7124                                 n = sibling;\r
7125                         }\r
7126 \r
7127                         // Collapse to just before the endAncestor, which\r
7128                         // is partially selected.\r
7129                         if (how != CLONE) {\r
7130                                 t.setEndBefore(endAncestor);\r
7131                                 t.collapse(FALSE);\r
7132                         }\r
7133 \r
7134                         return frag;\r
7135                 };\r
7136 \r
7137                 function _traverseCommonEndContainer(startAncestor, how) {\r
7138                         var frag, startIdx, n, cnt, sibling, xferNode;\r
7139 \r
7140                         if (how != DELETE)\r
7141                                 frag = createDocumentFragment();\r
7142 \r
7143                         n = _traverseLeftBoundary(startAncestor, how);\r
7144                         if (frag)\r
7145                                 frag.appendChild(n);\r
7146 \r
7147                         startIdx = nodeIndex(startAncestor);\r
7148                         ++startIdx; // Because we already traversed it\r
7149 \r
7150                         cnt = t[END_OFFSET] - startIdx;\r
7151                         n = startAncestor.nextSibling;\r
7152                         while (n && cnt > 0) {\r
7153                                 sibling = n.nextSibling;\r
7154                                 xferNode = _traverseFullySelected(n, how);\r
7155 \r
7156                                 if (frag)\r
7157                                         frag.appendChild(xferNode);\r
7158 \r
7159                                 --cnt;\r
7160                                 n = sibling;\r
7161                         }\r
7162 \r
7163                         if (how != CLONE) {\r
7164                                 t.setStartAfter(startAncestor);\r
7165                                 t.collapse(TRUE);\r
7166                         }\r
7167 \r
7168                         return frag;\r
7169                 };\r
7170 \r
7171                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
7172                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
7173 \r
7174                         if (how != DELETE)\r
7175                                 frag = createDocumentFragment();\r
7176 \r
7177                         n = _traverseLeftBoundary(startAncestor, how);\r
7178                         if (frag)\r
7179                                 frag.appendChild(n);\r
7180 \r
7181                         commonParent = startAncestor.parentNode;\r
7182                         startOffset = nodeIndex(startAncestor);\r
7183                         endOffset = nodeIndex(endAncestor);\r
7184                         ++startOffset;\r
7185 \r
7186                         cnt = endOffset - startOffset;\r
7187                         sibling = startAncestor.nextSibling;\r
7188 \r
7189                         while (cnt > 0) {\r
7190                                 nextSibling = sibling.nextSibling;\r
7191                                 n = _traverseFullySelected(sibling, how);\r
7192 \r
7193                                 if (frag)\r
7194                                         frag.appendChild(n);\r
7195 \r
7196                                 sibling = nextSibling;\r
7197                                 --cnt;\r
7198                         }\r
7199 \r
7200                         n = _traverseRightBoundary(endAncestor, how);\r
7201 \r
7202                         if (frag)\r
7203                                 frag.appendChild(n);\r
7204 \r
7205                         if (how != CLONE) {\r
7206                                 t.setStartAfter(startAncestor);\r
7207                                 t.collapse(TRUE);\r
7208                         }\r
7209 \r
7210                         return frag;\r
7211                 };\r
7212 \r
7213                 function _traverseRightBoundary(root, how) {\r
7214                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
7215 \r
7216                         if (next == root)\r
7217                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
7218 \r
7219                         parent = next.parentNode;\r
7220                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
7221 \r
7222                         while (parent) {\r
7223                                 while (next) {\r
7224                                         prevSibling = next.previousSibling;\r
7225                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
7226 \r
7227                                         if (how != DELETE)\r
7228                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
7229 \r
7230                                         isFullySelected = TRUE;\r
7231                                         next = prevSibling;\r
7232                                 }\r
7233 \r
7234                                 if (parent == root)\r
7235                                         return clonedParent;\r
7236 \r
7237                                 next = parent.previousSibling;\r
7238                                 parent = parent.parentNode;\r
7239 \r
7240                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
7241 \r
7242                                 if (how != DELETE)\r
7243                                         clonedGrandParent.appendChild(clonedParent);\r
7244 \r
7245                                 clonedParent = clonedGrandParent;\r
7246                         }\r
7247                 };\r
7248 \r
7249                 function _traverseLeftBoundary(root, how) {\r
7250                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
7251 \r
7252                         if (next == root)\r
7253                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
7254 \r
7255                         parent = next.parentNode;\r
7256                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
7257 \r
7258                         while (parent) {\r
7259                                 while (next) {\r
7260                                         nextSibling = next.nextSibling;\r
7261                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
7262 \r
7263                                         if (how != DELETE)\r
7264                                                 clonedParent.appendChild(clonedChild);\r
7265 \r
7266                                         isFullySelected = TRUE;\r
7267                                         next = nextSibling;\r
7268                                 }\r
7269 \r
7270                                 if (parent == root)\r
7271                                         return clonedParent;\r
7272 \r
7273                                 next = parent.nextSibling;\r
7274                                 parent = parent.parentNode;\r
7275 \r
7276                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
7277 \r
7278                                 if (how != DELETE)\r
7279                                         clonedGrandParent.appendChild(clonedParent);\r
7280 \r
7281                                 clonedParent = clonedGrandParent;\r
7282                         }\r
7283                 };\r
7284 \r
7285                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
7286                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
7287 \r
7288                         if (isFullySelected)\r
7289                                 return _traverseFullySelected(n, how);\r
7290 \r
7291                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
7292                                 txtValue = n.nodeValue;\r
7293 \r
7294                                 if (isLeft) {\r
7295                                         offset = t[START_OFFSET];\r
7296                                         newNodeValue = txtValue.substring(offset);\r
7297                                         oldNodeValue = txtValue.substring(0, offset);\r
7298                                 } else {\r
7299                                         offset = t[END_OFFSET];\r
7300                                         newNodeValue = txtValue.substring(0, offset);\r
7301                                         oldNodeValue = txtValue.substring(offset);\r
7302                                 }\r
7303 \r
7304                                 if (how != CLONE)\r
7305                                         n.nodeValue = oldNodeValue;\r
7306 \r
7307                                 if (how == DELETE)\r
7308                                         return;\r
7309 \r
7310                                 newNode = dom.clone(n, FALSE);\r
7311                                 newNode.nodeValue = newNodeValue;\r
7312 \r
7313                                 return newNode;\r
7314                         }\r
7315 \r
7316                         if (how == DELETE)\r
7317                                 return;\r
7318 \r
7319                         return dom.clone(n, FALSE);\r
7320                 };\r
7321 \r
7322                 function _traverseFullySelected(n, how) {\r
7323                         if (how != DELETE)\r
7324                                 return how == CLONE ? dom.clone(n, TRUE) : n;\r
7325 \r
7326                         n.parentNode.removeChild(n);\r
7327                 };\r
7328 \r
7329                 function toStringIE() {\r
7330                         return dom.create('body', null, cloneContents()).outerText;\r
7331                 }\r
7332                 \r
7333                 return t;\r
7334         };\r
7335 \r
7336         ns.Range = Range;\r
7337 \r
7338         // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype\r
7339         Range.prototype.toString = function() {\r
7340                 return this.toStringIE();\r
7341         };\r
7342 })(tinymce.dom);\r
7343 \r
7344 (function() {\r
7345         function Selection(selection) {\r
7346                 var self = this, dom = selection.dom, TRUE = true, FALSE = false;\r
7347 \r
7348                 function getPosition(rng, start) {\r
7349                         var checkRng, startIndex = 0, endIndex, inside,\r
7350                                 children, child, offset, index, position = -1, parent;\r
7351 \r
7352                         // Setup test range, collapse it and get the parent\r
7353                         checkRng = rng.duplicate();\r
7354                         checkRng.collapse(start);\r
7355                         parent = checkRng.parentElement();\r
7356 \r
7357                         // Check if the selection is within the right document\r
7358                         if (parent.ownerDocument !== selection.dom.doc)\r
7359                                 return;\r
7360 \r
7361                         // IE will report non editable elements as it's parent so look for an editable one\r
7362                         while (parent.contentEditable === "false") {\r
7363                                 parent = parent.parentNode;\r
7364                         }\r
7365 \r
7366                         // If parent doesn't have any children then return that we are inside the element\r
7367                         if (!parent.hasChildNodes()) {\r
7368                                 return {node : parent, inside : 1};\r
7369                         }\r
7370 \r
7371                         // Setup node list and endIndex\r
7372                         children = parent.children;\r
7373                         endIndex = children.length - 1;\r
7374 \r
7375                         // Perform a binary search for the position\r
7376                         while (startIndex <= endIndex) {\r
7377                                 index = Math.floor((startIndex + endIndex) / 2);\r
7378 \r
7379                                 // Move selection to node and compare the ranges\r
7380                                 child = children[index];\r
7381                                 checkRng.moveToElementText(child);\r
7382                                 position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);\r
7383 \r
7384                                 // Before/after or an exact match\r
7385                                 if (position > 0) {\r
7386                                         endIndex = index - 1;\r
7387                                 } else if (position < 0) {\r
7388                                         startIndex = index + 1;\r
7389                                 } else {\r
7390                                         return {node : child};\r
7391                                 }\r
7392                         }\r
7393 \r
7394                         // Check if child position is before or we didn't find a position\r
7395                         if (position < 0) {\r
7396                                 // No element child was found use the parent element and the offset inside that\r
7397                                 if (!child) {\r
7398                                         checkRng.moveToElementText(parent);\r
7399                                         checkRng.collapse(true);\r
7400                                         child = parent;\r
7401                                         inside = true;\r
7402                                 } else\r
7403                                         checkRng.collapse(false);\r
7404 \r
7405                                 // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one\r
7406                                 // We need to walk char by char since rng.text or rng.htmlText will trim line endings\r
7407                                 offset = 0;\r
7408                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {\r
7409                                         if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {\r
7410                                                 break;\r
7411                                         }\r
7412 \r
7413                                         offset++;\r
7414                                 }\r
7415                         } else {\r
7416                                 // Child position is after the selection endpoint\r
7417                                 checkRng.collapse(true);\r
7418 \r
7419                                 // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one\r
7420                                 offset = 0;\r
7421                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {\r
7422                                         if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {\r
7423                                                 break;\r
7424                                         }\r
7425 \r
7426                                         offset++;\r
7427                                 }\r
7428                         }\r
7429 \r
7430                         return {node : child, position : position, offset : offset, inside : inside};\r
7431                 };\r
7432 \r
7433                 // Returns a W3C DOM compatible range object by using the IE Range API\r
7434                 function getRange() {\r
7435                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark, fail;\r
7436 \r
7437                         // If selection is outside the current document just return an empty range\r
7438                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
7439                         if (element.ownerDocument != dom.doc)\r
7440                                 return domRange;\r
7441 \r
7442                         collapsed = selection.isCollapsed();\r
7443 \r
7444                         // Handle control selection\r
7445                         if (ieRange.item) {\r
7446                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
7447                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
7448 \r
7449                                 return domRange;\r
7450                         }\r
7451 \r
7452                         function findEndPoint(start) {\r
7453                                 var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;\r
7454 \r
7455                                 container = endPoint.node;\r
7456                                 offset = endPoint.offset;\r
7457 \r
7458                                 if (endPoint.inside && !container.hasChildNodes()) {\r
7459                                         domRange[start ? 'setStart' : 'setEnd'](container, 0);\r
7460                                         return;\r
7461                                 }\r
7462 \r
7463                                 if (offset === undef) {\r
7464                                         domRange[start ? 'setStartBefore' : 'setEndAfter'](container);\r
7465                                         return;\r
7466                                 }\r
7467 \r
7468                                 if (endPoint.position < 0) {\r
7469                                         sibling = endPoint.inside ? container.firstChild : container.nextSibling;\r
7470 \r
7471                                         if (!sibling) {\r
7472                                                 domRange[start ? 'setStartAfter' : 'setEndAfter'](container);\r
7473                                                 return;\r
7474                                         }\r
7475 \r
7476                                         if (!offset) {\r
7477                                                 if (sibling.nodeType == 3)\r
7478                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, 0);\r
7479                                                 else\r
7480                                                         domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);\r
7481 \r
7482                                                 return;\r
7483                                         }\r
7484 \r
7485                                         // Find the text node and offset\r
7486                                         while (sibling) {\r
7487                                                 nodeValue = sibling.nodeValue;\r
7488                                                 textNodeOffset += nodeValue.length;\r
7489 \r
7490                                                 // We are at or passed the position we where looking for\r
7491                                                 if (textNodeOffset >= offset) {\r
7492                                                         container = sibling;\r
7493                                                         textNodeOffset -= offset;\r
7494                                                         textNodeOffset = nodeValue.length - textNodeOffset;\r
7495                                                         break;\r
7496                                                 }\r
7497 \r
7498                                                 sibling = sibling.nextSibling;\r
7499                                         }\r
7500                                 } else {\r
7501                                         // Find the text node and offset\r
7502                                         sibling = container.previousSibling;\r
7503 \r
7504                                         if (!sibling)\r
7505                                                 return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);\r
7506 \r
7507                                         // If there isn't any text to loop then use the first position\r
7508                                         if (!offset) {\r
7509                                                 if (container.nodeType == 3)\r
7510                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);\r
7511                                                 else\r
7512                                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);\r
7513 \r
7514                                                 return;\r
7515                                         }\r
7516 \r
7517                                         while (sibling) {\r
7518                                                 textNodeOffset += sibling.nodeValue.length;\r
7519 \r
7520                                                 // We are at or passed the position we where looking for\r
7521                                                 if (textNodeOffset >= offset) {\r
7522                                                         container = sibling;\r
7523                                                         textNodeOffset -= offset;\r
7524                                                         break;\r
7525                                                 }\r
7526 \r
7527                                                 sibling = sibling.previousSibling;\r
7528                                         }\r
7529                                 }\r
7530 \r
7531                                 domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);\r
7532                         };\r
7533 \r
7534                         try {\r
7535                                 // Find start point\r
7536                                 findEndPoint(true);\r
7537 \r
7538                                 // Find end point if needed\r
7539                                 if (!collapsed)\r
7540                                         findEndPoint();\r
7541                         } catch (ex) {\r
7542                                 // IE has a nasty bug where text nodes might throw "invalid argument" when you\r
7543                                 // access the nodeValue or other properties of text nodes. This seems to happend when\r
7544                                 // text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.\r
7545                                 if (ex.number == -2147024809) {\r
7546                                         // Get the current selection\r
7547                                         bookmark = self.getBookmark(2);\r
7548 \r
7549                                         // Get start element\r
7550                                         tmpRange = ieRange.duplicate();\r
7551                                         tmpRange.collapse(true);\r
7552                                         element = tmpRange.parentElement();\r
7553 \r
7554                                         // Get end element\r
7555                                         if (!collapsed) {\r
7556                                                 tmpRange = ieRange.duplicate();\r
7557                                                 tmpRange.collapse(false);\r
7558                                                 element2 = tmpRange.parentElement();\r
7559                                                 element2.innerHTML = element2.innerHTML;\r
7560                                         }\r
7561 \r
7562                                         // Remove the broken elements\r
7563                                         element.innerHTML = element.innerHTML;\r
7564 \r
7565                                         // Restore the selection\r
7566                                         self.moveToBookmark(bookmark);\r
7567 \r
7568                                         // Since the range has moved we need to re-get it\r
7569                                         ieRange = selection.getRng();\r
7570 \r
7571                                         // Find start point\r
7572                                         findEndPoint(true);\r
7573 \r
7574                                         // Find end point if needed\r
7575                                         if (!collapsed)\r
7576                                                 findEndPoint();\r
7577                                 } else\r
7578                                         throw ex; // Throw other errors\r
7579                         }\r
7580 \r
7581                         return domRange;\r
7582                 };\r
7583 \r
7584                 this.getBookmark = function(type) {\r
7585                         var rng = selection.getRng(), start, end, bookmark = {};\r
7586 \r
7587                         function getIndexes(node) {\r
7588                                 var parent, root, children, i, indexes = [];\r
7589 \r
7590                                 parent = node.parentNode;\r
7591                                 root = dom.getRoot().parentNode;\r
7592 \r
7593                                 while (parent != root && parent.nodeType !== 9) {\r
7594                                         children = parent.children;\r
7595 \r
7596                                         i = children.length;\r
7597                                         while (i--) {\r
7598                                                 if (node === children[i]) {\r
7599                                                         indexes.push(i);\r
7600                                                         break;\r
7601                                                 }\r
7602                                         }\r
7603 \r
7604                                         node = parent;\r
7605                                         parent = parent.parentNode;\r
7606                                 }\r
7607 \r
7608                                 return indexes;\r
7609                         };\r
7610 \r
7611                         function getBookmarkEndPoint(start) {\r
7612                                 var position;\r
7613 \r
7614                                 position = getPosition(rng, start);\r
7615                                 if (position) {\r
7616                                         return {\r
7617                                                 position : position.position,\r
7618                                                 offset : position.offset,\r
7619                                                 indexes : getIndexes(position.node),\r
7620                                                 inside : position.inside\r
7621                                         };\r
7622                                 }\r
7623                         };\r
7624 \r
7625                         // Non ubstructive bookmark\r
7626                         if (type === 2) {\r
7627                                 // Handle text selection\r
7628                                 if (!rng.item) {\r
7629                                         bookmark.start = getBookmarkEndPoint(true);\r
7630 \r
7631                                         if (!selection.isCollapsed())\r
7632                                                 bookmark.end = getBookmarkEndPoint();\r
7633                                 } else\r
7634                                         bookmark.start = {ctrl : true, indexes : getIndexes(rng.item(0))};\r
7635                         }\r
7636 \r
7637                         return bookmark;\r
7638                 };\r
7639 \r
7640                 this.moveToBookmark = function(bookmark) {\r
7641                         var rng, body = dom.doc.body;\r
7642 \r
7643                         function resolveIndexes(indexes) {\r
7644                                 var node, i, idx, children;\r
7645 \r
7646                                 node = dom.getRoot();\r
7647                                 for (i = indexes.length - 1; i >= 0; i--) {\r
7648                                         children = node.children;\r
7649                                         idx = indexes[i];\r
7650 \r
7651                                         if (idx <= children.length - 1) {\r
7652                                                 node = children[idx];\r
7653                                         }\r
7654                                 }\r
7655 \r
7656                                 return node;\r
7657                         };\r
7658                         \r
7659                         function setBookmarkEndPoint(start) {\r
7660                                 var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef;\r
7661 \r
7662                                 if (endPoint) {\r
7663                                         moveLeft = endPoint.position > 0;\r
7664 \r
7665                                         moveRng = body.createTextRange();\r
7666                                         moveRng.moveToElementText(resolveIndexes(endPoint.indexes));\r
7667 \r
7668                                         offset = endPoint.offset;\r
7669                                         if (offset !== undef) {\r
7670                                                 moveRng.collapse(endPoint.inside || moveLeft);\r
7671                                                 moveRng.moveStart('character', moveLeft ? -offset : offset);\r
7672                                         } else\r
7673                                                 moveRng.collapse(start);\r
7674 \r
7675                                         rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);\r
7676 \r
7677                                         if (start)\r
7678                                                 rng.collapse(true);\r
7679                                 }\r
7680                         };\r
7681 \r
7682                         if (bookmark.start) {\r
7683                                 if (bookmark.start.ctrl) {\r
7684                                         rng = body.createControlRange();\r
7685                                         rng.addElement(resolveIndexes(bookmark.start.indexes));\r
7686                                         rng.select();\r
7687                                 } else {\r
7688                                         rng = body.createTextRange();\r
7689                                         setBookmarkEndPoint(true);\r
7690                                         setBookmarkEndPoint();\r
7691                                         rng.select();\r
7692                                 }\r
7693                         }\r
7694                 };\r
7695 \r
7696                 this.addRange = function(rng) {\r
7697                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,\r
7698                                 doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;\r
7699 \r
7700                         function setEndPoint(start) {\r
7701                                 var container, offset, marker, tmpRng, nodes;\r
7702 \r
7703                                 marker = dom.create('a');\r
7704                                 container = start ? startContainer : endContainer;\r
7705                                 offset = start ? startOffset : endOffset;\r
7706                                 tmpRng = ieRng.duplicate();\r
7707 \r
7708                                 if (container == doc || container == doc.documentElement) {\r
7709                                         container = body;\r
7710                                         offset = 0;\r
7711                                 }\r
7712 \r
7713                                 if (container.nodeType == 3) {\r
7714                                         container.parentNode.insertBefore(marker, container);\r
7715                                         tmpRng.moveToElementText(marker);\r
7716                                         tmpRng.moveStart('character', offset);\r
7717                                         dom.remove(marker);\r
7718                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
7719                                 } else {\r
7720                                         nodes = container.childNodes;\r
7721 \r
7722                                         if (nodes.length) {\r
7723                                                 if (offset >= nodes.length) {\r
7724                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
7725                                                 } else {\r
7726                                                         container.insertBefore(marker, nodes[offset]);\r
7727                                                 }\r
7728 \r
7729                                                 tmpRng.moveToElementText(marker);\r
7730                                         } else if (container.canHaveHTML) {\r
7731                                                 // Empty node selection for example <div>|</div>\r
7732                                                 // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open\r
7733                                                 container.innerHTML = '<span>\uFEFF</span>';\r
7734                                                 marker = container.firstChild;\r
7735                                                 tmpRng.moveToElementText(marker);\r
7736                                                 tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason\r
7737                                         }\r
7738 \r
7739                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
7740                                         dom.remove(marker);\r
7741                                 }\r
7742                         }\r
7743 \r
7744                         // Setup some shorter versions\r
7745                         startContainer = rng.startContainer;\r
7746                         startOffset = rng.startOffset;\r
7747                         endContainer = rng.endContainer;\r
7748                         endOffset = rng.endOffset;\r
7749                         ieRng = body.createTextRange();\r
7750 \r
7751                         // If single element selection then try making a control selection out of it\r
7752                         if (startContainer == endContainer && startContainer.nodeType == 1) {\r
7753                                 // Trick to place the caret inside an empty block element like <p></p>\r
7754                                 if (startOffset == endOffset && !startContainer.hasChildNodes()) {\r
7755                                         if (startContainer.canHaveHTML) {\r
7756                                                 // Check if previous sibling is an empty block if it is then we need to render it\r
7757                                                 // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236\r
7758                                                 // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>\r
7759                                                 sibling = startContainer.previousSibling;\r
7760                                                 if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {\r
7761                                                         sibling.innerHTML = '\uFEFF';\r
7762                                                 } else {\r
7763                                                         sibling = null;\r
7764                                                 }\r
7765 \r
7766                                                 startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';\r
7767                                                 ieRng.moveToElementText(startContainer.lastChild);\r
7768                                                 ieRng.select();\r
7769                                                 dom.doc.selection.clear();\r
7770                                                 startContainer.innerHTML = '';\r
7771 \r
7772                                                 if (sibling) {\r
7773                                                         sibling.innerHTML = '';\r
7774                                                 }\r
7775                                                 return;\r
7776                                         } else {\r
7777                                                 startOffset = dom.nodeIndex(startContainer);\r
7778                                                 startContainer = startContainer.parentNode;\r
7779                                         }\r
7780                                 }\r
7781 \r
7782                                 if (startOffset == endOffset - 1) {\r
7783                                         try {\r
7784                                                 ctrlElm = startContainer.childNodes[startOffset];\r
7785                                                 ctrlRng = body.createControlRange();\r
7786                                                 ctrlRng.addElement(ctrlElm);\r
7787                                                 ctrlRng.select();\r
7788 \r
7789                                                 // Check if the range produced is on the correct element and is a control range\r
7790                                                 // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398\r
7791                                                 nativeRng = selection.getRng();\r
7792                                                 if (nativeRng.item && ctrlElm === nativeRng.item(0)) {\r
7793                                                         return;\r
7794                                                 }\r
7795                                         } catch (ex) {\r
7796                                                 // Ignore\r
7797                                         }\r
7798                                 }\r
7799                         }\r
7800 \r
7801                         // Set start/end point of selection\r
7802                         setEndPoint(true);\r
7803                         setEndPoint();\r
7804 \r
7805                         // Select the new range and scroll it into view\r
7806                         ieRng.select();\r
7807                 };\r
7808 \r
7809                 // Expose range method\r
7810                 this.getRangeAt = getRange;\r
7811         };\r
7812 \r
7813         // Expose the selection object\r
7814         tinymce.dom.TridentSelection = Selection;\r
7815 })();\r
7816 \r
7817 \r
7818 /*\r
7819  * Sizzle CSS Selector Engine\r
7820  *  Copyright, The Dojo Foundation\r
7821  *  Released under the MIT, BSD, and GPL Licenses.\r
7822  *  More information: http://sizzlejs.com/\r
7823  */\r
7824 (function(){\r
7825 \r
7826 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
7827         expando = "sizcache",\r
7828         done = 0,\r
7829         toString = Object.prototype.toString,\r
7830         hasDuplicate = false,\r
7831         baseHasDuplicate = true,\r
7832         rBackslash = /\\/g,\r
7833         rReturn = /\r\n/g,\r
7834         rNonWord = /\W/;\r
7835 \r
7836 // Here we check if the JavaScript engine is using some sort of\r
7837 // optimization where it does not always call our comparision\r
7838 // function. If that is the case, discard the hasDuplicate value.\r
7839 //   Thus far that includes Google Chrome.\r
7840 [0, 0].sort(function() {\r
7841         baseHasDuplicate = false;\r
7842         return 0;\r
7843 });\r
7844 \r
7845 var Sizzle = function( selector, context, results, seed ) {\r
7846         results = results || [];\r
7847         context = context || document;\r
7848 \r
7849         var origContext = context;\r
7850 \r
7851         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
7852                 return [];\r
7853         }\r
7854 \r
7855         if ( !selector || typeof selector !== "string" ) {\r
7856                 return results;\r
7857         }\r
7858 \r
7859         var m, set, checkSet, extra, ret, cur, pop, i,\r
7860                 prune = true,\r
7861                 contextXML = Sizzle.isXML( context ),\r
7862                 parts = [],\r
7863                 soFar = selector;\r
7864 \r
7865         // Reset the position of the chunker regexp (start from head)\r
7866         do {\r
7867                 chunker.exec( "" );\r
7868                 m = chunker.exec( soFar );\r
7869 \r
7870                 if ( m ) {\r
7871                         soFar = m[3];\r
7872 \r
7873                         parts.push( m[1] );\r
7874 \r
7875                         if ( m[2] ) {\r
7876                                 extra = m[3];\r
7877                                 break;\r
7878                         }\r
7879                 }\r
7880         } while ( m );\r
7881 \r
7882         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
7883 \r
7884                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
7885                         set = posProcess( parts[0] + parts[1], context, seed );\r
7886 \r
7887                 } else {\r
7888                         set = Expr.relative[ parts[0] ] ?\r
7889                                 [ context ] :\r
7890                                 Sizzle( parts.shift(), context );\r
7891 \r
7892                         while ( parts.length ) {\r
7893                                 selector = parts.shift();\r
7894 \r
7895                                 if ( Expr.relative[ selector ] ) {\r
7896                                         selector += parts.shift();\r
7897                                 }\r
7898 \r
7899                                 set = posProcess( selector, set, seed );\r
7900                         }\r
7901                 }\r
7902 \r
7903         } else {\r
7904                 // Take a shortcut and set the context if the root selector is an ID\r
7905                 // (but not if it'll be faster if the inner selector is an ID)\r
7906                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
7907                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
7908 \r
7909                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
7910                         context = ret.expr ?\r
7911                                 Sizzle.filter( ret.expr, ret.set )[0] :\r
7912                                 ret.set[0];\r
7913                 }\r
7914 \r
7915                 if ( context ) {\r
7916                         ret = seed ?\r
7917                                 { expr: parts.pop(), set: makeArray(seed) } :\r
7918                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
7919 \r
7920                         set = ret.expr ?\r
7921                                 Sizzle.filter( ret.expr, ret.set ) :\r
7922                                 ret.set;\r
7923 \r
7924                         if ( parts.length > 0 ) {\r
7925                                 checkSet = makeArray( set );\r
7926 \r
7927                         } else {\r
7928                                 prune = false;\r
7929                         }\r
7930 \r
7931                         while ( parts.length ) {\r
7932                                 cur = parts.pop();\r
7933                                 pop = cur;\r
7934 \r
7935                                 if ( !Expr.relative[ cur ] ) {\r
7936                                         cur = "";\r
7937                                 } else {\r
7938                                         pop = parts.pop();\r
7939                                 }\r
7940 \r
7941                                 if ( pop == null ) {\r
7942                                         pop = context;\r
7943                                 }\r
7944 \r
7945                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
7946                         }\r
7947 \r
7948                 } else {\r
7949                         checkSet = parts = [];\r
7950                 }\r
7951         }\r
7952 \r
7953         if ( !checkSet ) {\r
7954                 checkSet = set;\r
7955         }\r
7956 \r
7957         if ( !checkSet ) {\r
7958                 Sizzle.error( cur || selector );\r
7959         }\r
7960 \r
7961         if ( toString.call(checkSet) === "[object Array]" ) {\r
7962                 if ( !prune ) {\r
7963                         results.push.apply( results, checkSet );\r
7964 \r
7965                 } else if ( context && context.nodeType === 1 ) {\r
7966                         for ( i = 0; checkSet[i] != null; i++ ) {\r
7967                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
7968                                         results.push( set[i] );\r
7969                                 }\r
7970                         }\r
7971 \r
7972                 } else {\r
7973                         for ( i = 0; checkSet[i] != null; i++ ) {\r
7974                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
7975                                         results.push( set[i] );\r
7976                                 }\r
7977                         }\r
7978                 }\r
7979 \r
7980         } else {\r
7981                 makeArray( checkSet, results );\r
7982         }\r
7983 \r
7984         if ( extra ) {\r
7985                 Sizzle( extra, origContext, results, seed );\r
7986                 Sizzle.uniqueSort( results );\r
7987         }\r
7988 \r
7989         return results;\r
7990 };\r
7991 \r
7992 Sizzle.uniqueSort = function( results ) {\r
7993         if ( sortOrder ) {\r
7994                 hasDuplicate = baseHasDuplicate;\r
7995                 results.sort( sortOrder );\r
7996 \r
7997                 if ( hasDuplicate ) {\r
7998                         for ( var i = 1; i < results.length; i++ ) {\r
7999                                 if ( results[i] === results[ i - 1 ] ) {\r
8000                                         results.splice( i--, 1 );\r
8001                                 }\r
8002                         }\r
8003                 }\r
8004         }\r
8005 \r
8006         return results;\r
8007 };\r
8008 \r
8009 Sizzle.matches = function( expr, set ) {\r
8010         return Sizzle( expr, null, null, set );\r
8011 };\r
8012 \r
8013 Sizzle.matchesSelector = function( node, expr ) {\r
8014         return Sizzle( expr, null, null, [node] ).length > 0;\r
8015 };\r
8016 \r
8017 Sizzle.find = function( expr, context, isXML ) {\r
8018         var set, i, len, match, type, left;\r
8019 \r
8020         if ( !expr ) {\r
8021                 return [];\r
8022         }\r
8023 \r
8024         for ( i = 0, len = Expr.order.length; i < len; i++ ) {\r
8025                 type = Expr.order[i];\r
8026 \r
8027                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
8028                         left = match[1];\r
8029                         match.splice( 1, 1 );\r
8030 \r
8031                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
8032                                 match[1] = (match[1] || "").replace( rBackslash, "" );\r
8033                                 set = Expr.find[ type ]( match, context, isXML );\r
8034 \r
8035                                 if ( set != null ) {\r
8036                                         expr = expr.replace( Expr.match[ type ], "" );\r
8037                                         break;\r
8038                                 }\r
8039                         }\r
8040                 }\r
8041         }\r
8042 \r
8043         if ( !set ) {\r
8044                 set = typeof context.getElementsByTagName !== "undefined" ?\r
8045                         context.getElementsByTagName( "*" ) :\r
8046                         [];\r
8047         }\r
8048 \r
8049         return { set: set, expr: expr };\r
8050 };\r
8051 \r
8052 Sizzle.filter = function( expr, set, inplace, not ) {\r
8053         var match, anyFound,\r
8054                 type, found, item, filter, left,\r
8055                 i, pass,\r
8056                 old = expr,\r
8057                 result = [],\r
8058                 curLoop = set,\r
8059                 isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );\r
8060 \r
8061         while ( expr && set.length ) {\r
8062                 for ( type in Expr.filter ) {\r
8063                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
8064                                 filter = Expr.filter[ type ];\r
8065                                 left = match[1];\r
8066 \r
8067                                 anyFound = false;\r
8068 \r
8069                                 match.splice(1,1);\r
8070 \r
8071                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
8072                                         continue;\r
8073                                 }\r
8074 \r
8075                                 if ( curLoop === result ) {\r
8076                                         result = [];\r
8077                                 }\r
8078 \r
8079                                 if ( Expr.preFilter[ type ] ) {\r
8080                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
8081 \r
8082                                         if ( !match ) {\r
8083                                                 anyFound = found = true;\r
8084 \r
8085                                         } else if ( match === true ) {\r
8086                                                 continue;\r
8087                                         }\r
8088                                 }\r
8089 \r
8090                                 if ( match ) {\r
8091                                         for ( i = 0; (item = curLoop[i]) != null; i++ ) {\r
8092                                                 if ( item ) {\r
8093                                                         found = filter( item, match, i, curLoop );\r
8094                                                         pass = not ^ found;\r
8095 \r
8096                                                         if ( inplace && found != null ) {\r
8097                                                                 if ( pass ) {\r
8098                                                                         anyFound = true;\r
8099 \r
8100                                                                 } else {\r
8101                                                                         curLoop[i] = false;\r
8102                                                                 }\r
8103 \r
8104                                                         } else if ( pass ) {\r
8105                                                                 result.push( item );\r
8106                                                                 anyFound = true;\r
8107                                                         }\r
8108                                                 }\r
8109                                         }\r
8110                                 }\r
8111 \r
8112                                 if ( found !== undefined ) {\r
8113                                         if ( !inplace ) {\r
8114                                                 curLoop = result;\r
8115                                         }\r
8116 \r
8117                                         expr = expr.replace( Expr.match[ type ], "" );\r
8118 \r
8119                                         if ( !anyFound ) {\r
8120                                                 return [];\r
8121                                         }\r
8122 \r
8123                                         break;\r
8124                                 }\r
8125                         }\r
8126                 }\r
8127 \r
8128                 // Improper expression\r
8129                 if ( expr === old ) {\r
8130                         if ( anyFound == null ) {\r
8131                                 Sizzle.error( expr );\r
8132 \r
8133                         } else {\r
8134                                 break;\r
8135                         }\r
8136                 }\r
8137 \r
8138                 old = expr;\r
8139         }\r
8140 \r
8141         return curLoop;\r
8142 };\r
8143 \r
8144 Sizzle.error = function( msg ) {\r
8145         throw new Error( "Syntax error, unrecognized expression: " + msg );\r
8146 };\r
8147 \r
8148 var getText = Sizzle.getText = function( elem ) {\r
8149     var i, node,\r
8150                 nodeType = elem.nodeType,\r
8151                 ret = "";\r
8152 \r
8153         if ( nodeType ) {\r
8154                 if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\r
8155                         // Use textContent || innerText for elements\r
8156                         if ( typeof elem.textContent === 'string' ) {\r
8157                                 return elem.textContent;\r
8158                         } else if ( typeof elem.innerText === 'string' ) {\r
8159                                 // Replace IE's carriage returns\r
8160                                 return elem.innerText.replace( rReturn, '' );\r
8161                         } else {\r
8162                                 // Traverse it's children\r
8163                                 for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {\r
8164                                         ret += getText( elem );\r
8165                                 }\r
8166                         }\r
8167                 } else if ( nodeType === 3 || nodeType === 4 ) {\r
8168                         return elem.nodeValue;\r
8169                 }\r
8170         } else {\r
8171 \r
8172                 // If no nodeType, this is expected to be an array\r
8173                 for ( i = 0; (node = elem[i]); i++ ) {\r
8174                         // Do not traverse comment nodes\r
8175                         if ( node.nodeType !== 8 ) {\r
8176                                 ret += getText( node );\r
8177                         }\r
8178                 }\r
8179         }\r
8180         return ret;\r
8181 };\r
8182 \r
8183 var Expr = Sizzle.selectors = {\r
8184         order: [ "ID", "NAME", "TAG" ],\r
8185 \r
8186         match: {\r
8187                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
8188                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
8189                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
8190                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,\r
8191                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
8192                 CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,\r
8193                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
8194                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
8195         },\r
8196 \r
8197         leftMatch: {},\r
8198 \r
8199         attrMap: {\r
8200                 "class": "className",\r
8201                 "for": "htmlFor"\r
8202         },\r
8203 \r
8204         attrHandle: {\r
8205                 href: function( elem ) {\r
8206                         return elem.getAttribute( "href" );\r
8207                 },\r
8208                 type: function( elem ) {\r
8209                         return elem.getAttribute( "type" );\r
8210                 }\r
8211         },\r
8212 \r
8213         relative: {\r
8214                 "+": function(checkSet, part){\r
8215                         var isPartStr = typeof part === "string",\r
8216                                 isTag = isPartStr && !rNonWord.test( part ),\r
8217                                 isPartStrNotTag = isPartStr && !isTag;\r
8218 \r
8219                         if ( isTag ) {\r
8220                                 part = part.toLowerCase();\r
8221                         }\r
8222 \r
8223                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
8224                                 if ( (elem = checkSet[i]) ) {\r
8225                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
8226 \r
8227                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
8228                                                 elem || false :\r
8229                                                 elem === part;\r
8230                                 }\r
8231                         }\r
8232 \r
8233                         if ( isPartStrNotTag ) {\r
8234                                 Sizzle.filter( part, checkSet, true );\r
8235                         }\r
8236                 },\r
8237 \r
8238                 ">": function( checkSet, part ) {\r
8239                         var elem,\r
8240                                 isPartStr = typeof part === "string",\r
8241                                 i = 0,\r
8242                                 l = checkSet.length;\r
8243 \r
8244                         if ( isPartStr && !rNonWord.test( part ) ) {\r
8245                                 part = part.toLowerCase();\r
8246 \r
8247                                 for ( ; i < l; i++ ) {\r
8248                                         elem = checkSet[i];\r
8249 \r
8250                                         if ( elem ) {\r
8251                                                 var parent = elem.parentNode;\r
8252                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
8253                                         }\r
8254                                 }\r
8255 \r
8256                         } else {\r
8257                                 for ( ; i < l; i++ ) {\r
8258                                         elem = checkSet[i];\r
8259 \r
8260                                         if ( elem ) {\r
8261                                                 checkSet[i] = isPartStr ?\r
8262                                                         elem.parentNode :\r
8263                                                         elem.parentNode === part;\r
8264                                         }\r
8265                                 }\r
8266 \r
8267                                 if ( isPartStr ) {\r
8268                                         Sizzle.filter( part, checkSet, true );\r
8269                                 }\r
8270                         }\r
8271                 },\r
8272 \r
8273                 "": function(checkSet, part, isXML){\r
8274                         var nodeCheck,\r
8275                                 doneName = done++,\r
8276                                 checkFn = dirCheck;\r
8277 \r
8278                         if ( typeof part === "string" && !rNonWord.test( part ) ) {\r
8279                                 part = part.toLowerCase();\r
8280                                 nodeCheck = part;\r
8281                                 checkFn = dirNodeCheck;\r
8282                         }\r
8283 \r
8284                         checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );\r
8285                 },\r
8286 \r
8287                 "~": function( checkSet, part, isXML ) {\r
8288                         var nodeCheck,\r
8289                                 doneName = done++,\r
8290                                 checkFn = dirCheck;\r
8291 \r
8292                         if ( typeof part === "string" && !rNonWord.test( part ) ) {\r
8293                                 part = part.toLowerCase();\r
8294                                 nodeCheck = part;\r
8295                                 checkFn = dirNodeCheck;\r
8296                         }\r
8297 \r
8298                         checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );\r
8299                 }\r
8300         },\r
8301 \r
8302         find: {\r
8303                 ID: function( match, context, isXML ) {\r
8304                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
8305                                 var m = context.getElementById(match[1]);\r
8306                                 // Check parentNode to catch when Blackberry 4.6 returns\r
8307                                 // nodes that are no longer in the document #6963\r
8308                                 return m && m.parentNode ? [m] : [];\r
8309                         }\r
8310                 },\r
8311 \r
8312                 NAME: function( match, context ) {\r
8313                         if ( typeof context.getElementsByName !== "undefined" ) {\r
8314                                 var ret = [],\r
8315                                         results = context.getElementsByName( match[1] );\r
8316 \r
8317                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
8318                                         if ( results[i].getAttribute("name") === match[1] ) {\r
8319                                                 ret.push( results[i] );\r
8320                                         }\r
8321                                 }\r
8322 \r
8323                                 return ret.length === 0 ? null : ret;\r
8324                         }\r
8325                 },\r
8326 \r
8327                 TAG: function( match, context ) {\r
8328                         if ( typeof context.getElementsByTagName !== "undefined" ) {\r
8329                                 return context.getElementsByTagName( match[1] );\r
8330                         }\r
8331                 }\r
8332         },\r
8333         preFilter: {\r
8334                 CLASS: function( match, curLoop, inplace, result, not, isXML ) {\r
8335                         match = " " + match[1].replace( rBackslash, "" ) + " ";\r
8336 \r
8337                         if ( isXML ) {\r
8338                                 return match;\r
8339                         }\r
8340 \r
8341                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
8342                                 if ( elem ) {\r
8343                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {\r
8344                                                 if ( !inplace ) {\r
8345                                                         result.push( elem );\r
8346                                                 }\r
8347 \r
8348                                         } else if ( inplace ) {\r
8349                                                 curLoop[i] = false;\r
8350                                         }\r
8351                                 }\r
8352                         }\r
8353 \r
8354                         return false;\r
8355                 },\r
8356 \r
8357                 ID: function( match ) {\r
8358                         return match[1].replace( rBackslash, "" );\r
8359                 },\r
8360 \r
8361                 TAG: function( match, curLoop ) {\r
8362                         return match[1].replace( rBackslash, "" ).toLowerCase();\r
8363                 },\r
8364 \r
8365                 CHILD: function( match ) {\r
8366                         if ( match[1] === "nth" ) {\r
8367                                 if ( !match[2] ) {\r
8368                                         Sizzle.error( match[0] );\r
8369                                 }\r
8370 \r
8371                                 match[2] = match[2].replace(/^\+|\s*/g, '');\r
8372 \r
8373                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
8374                                 var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(\r
8375                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
8376                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
8377 \r
8378                                 // calculate the numbers (first)n+(last) including if they are negative\r
8379                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
8380                                 match[3] = test[3] - 0;\r
8381                         }\r
8382                         else if ( match[2] ) {\r
8383                                 Sizzle.error( match[0] );\r
8384                         }\r
8385 \r
8386                         // TODO: Move to normal caching system\r
8387                         match[0] = done++;\r
8388 \r
8389                         return match;\r
8390                 },\r
8391 \r
8392                 ATTR: function( match, curLoop, inplace, result, not, isXML ) {\r
8393                         var name = match[1] = match[1].replace( rBackslash, "" );\r
8394 \r
8395                         if ( !isXML && Expr.attrMap[name] ) {\r
8396                                 match[1] = Expr.attrMap[name];\r
8397                         }\r
8398 \r
8399                         // Handle if an un-quoted value was used\r
8400                         match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );\r
8401 \r
8402                         if ( match[2] === "~=" ) {\r
8403                                 match[4] = " " + match[4] + " ";\r
8404                         }\r
8405 \r
8406                         return match;\r
8407                 },\r
8408 \r
8409                 PSEUDO: function( match, curLoop, inplace, result, not ) {\r
8410                         if ( match[1] === "not" ) {\r
8411                                 // If we're dealing with a complex expression, or a simple one\r
8412                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
8413                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
8414 \r
8415                                 } else {\r
8416                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
8417 \r
8418                                         if ( !inplace ) {\r
8419                                                 result.push.apply( result, ret );\r
8420                                         }\r
8421 \r
8422                                         return false;\r
8423                                 }\r
8424 \r
8425                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
8426                                 return true;\r
8427                         }\r
8428 \r
8429                         return match;\r
8430                 },\r
8431 \r
8432                 POS: function( match ) {\r
8433                         match.unshift( true );\r
8434 \r
8435                         return match;\r
8436                 }\r
8437         },\r
8438 \r
8439         filters: {\r
8440                 enabled: function( elem ) {\r
8441                         return elem.disabled === false && elem.type !== "hidden";\r
8442                 },\r
8443 \r
8444                 disabled: function( elem ) {\r
8445                         return elem.disabled === true;\r
8446                 },\r
8447 \r
8448                 checked: function( elem ) {\r
8449                         return elem.checked === true;\r
8450                 },\r
8451 \r
8452                 selected: function( elem ) {\r
8453                         // Accessing this property makes selected-by-default\r
8454                         // options in Safari work properly\r
8455                         if ( elem.parentNode ) {\r
8456                                 elem.parentNode.selectedIndex;\r
8457                         }\r
8458 \r
8459                         return elem.selected === true;\r
8460                 },\r
8461 \r
8462                 parent: function( elem ) {\r
8463                         return !!elem.firstChild;\r
8464                 },\r
8465 \r
8466                 empty: function( elem ) {\r
8467                         return !elem.firstChild;\r
8468                 },\r
8469 \r
8470                 has: function( elem, i, match ) {\r
8471                         return !!Sizzle( match[3], elem ).length;\r
8472                 },\r
8473 \r
8474                 header: function( elem ) {\r
8475                         return (/h\d/i).test( elem.nodeName );\r
8476                 },\r
8477 \r
8478                 text: function( elem ) {\r
8479                         var attr = elem.getAttribute( "type" ), type = elem.type;\r
8480                         // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)\r
8481                         // use getAttribute instead to test this case\r
8482                         return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );\r
8483                 },\r
8484 \r
8485                 radio: function( elem ) {\r
8486                         return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;\r
8487                 },\r
8488 \r
8489                 checkbox: function( elem ) {\r
8490                         return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;\r
8491                 },\r
8492 \r
8493                 file: function( elem ) {\r
8494                         return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;\r
8495                 },\r
8496 \r
8497                 password: function( elem ) {\r
8498                         return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;\r
8499                 },\r
8500 \r
8501                 submit: function( elem ) {\r
8502                         var name = elem.nodeName.toLowerCase();\r
8503                         return (name === "input" || name === "button") && "submit" === elem.type;\r
8504                 },\r
8505 \r
8506                 image: function( elem ) {\r
8507                         return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;\r
8508                 },\r
8509 \r
8510                 reset: function( elem ) {\r
8511                         var name = elem.nodeName.toLowerCase();\r
8512                         return (name === "input" || name === "button") && "reset" === elem.type;\r
8513                 },\r
8514 \r
8515                 button: function( elem ) {\r
8516                         var name = elem.nodeName.toLowerCase();\r
8517                         return name === "input" && "button" === elem.type || name === "button";\r
8518                 },\r
8519 \r
8520                 input: function( elem ) {\r
8521                         return (/input|select|textarea|button/i).test( elem.nodeName );\r
8522                 },\r
8523 \r
8524                 focus: function( elem ) {\r
8525                         return elem === elem.ownerDocument.activeElement;\r
8526                 }\r
8527         },\r
8528         setFilters: {\r
8529                 first: function( elem, i ) {\r
8530                         return i === 0;\r
8531                 },\r
8532 \r
8533                 last: function( elem, i, match, array ) {\r
8534                         return i === array.length - 1;\r
8535                 },\r
8536 \r
8537                 even: function( elem, i ) {\r
8538                         return i % 2 === 0;\r
8539                 },\r
8540 \r
8541                 odd: function( elem, i ) {\r
8542                         return i % 2 === 1;\r
8543                 },\r
8544 \r
8545                 lt: function( elem, i, match ) {\r
8546                         return i < match[3] - 0;\r
8547                 },\r
8548 \r
8549                 gt: function( elem, i, match ) {\r
8550                         return i > match[3] - 0;\r
8551                 },\r
8552 \r
8553                 nth: function( elem, i, match ) {\r
8554                         return match[3] - 0 === i;\r
8555                 },\r
8556 \r
8557                 eq: function( elem, i, match ) {\r
8558                         return match[3] - 0 === i;\r
8559                 }\r
8560         },\r
8561         filter: {\r
8562                 PSEUDO: function( elem, match, i, array ) {\r
8563                         var name = match[1],\r
8564                                 filter = Expr.filters[ name ];\r
8565 \r
8566                         if ( filter ) {\r
8567                                 return filter( elem, i, match, array );\r
8568 \r
8569                         } else if ( name === "contains" ) {\r
8570                                 return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
8571 \r
8572                         } else if ( name === "not" ) {\r
8573                                 var not = match[3];\r
8574 \r
8575                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
8576                                         if ( not[j] === elem ) {\r
8577                                                 return false;\r
8578                                         }\r
8579                                 }\r
8580 \r
8581                                 return true;\r
8582 \r
8583                         } else {\r
8584                                 Sizzle.error( name );\r
8585                         }\r
8586                 },\r
8587 \r
8588                 CHILD: function( elem, match ) {\r
8589                         var first, last,\r
8590                                 doneName, parent, cache,\r
8591                                 count, diff,\r
8592                                 type = match[1],\r
8593                                 node = elem;\r
8594 \r
8595                         switch ( type ) {\r
8596                                 case "only":\r
8597                                 case "first":\r
8598                                         while ( (node = node.previousSibling) ) {\r
8599                                                 if ( node.nodeType === 1 ) {\r
8600                                                         return false;\r
8601                                                 }\r
8602                                         }\r
8603 \r
8604                                         if ( type === "first" ) {\r
8605                                                 return true;\r
8606                                         }\r
8607 \r
8608                                         node = elem;\r
8609 \r
8610                                         /* falls through */\r
8611                                 case "last":\r
8612                                         while ( (node = node.nextSibling) ) {\r
8613                                                 if ( node.nodeType === 1 ) {\r
8614                                                         return false;\r
8615                                                 }\r
8616                                         }\r
8617 \r
8618                                         return true;\r
8619 \r
8620                                 case "nth":\r
8621                                         first = match[2];\r
8622                                         last = match[3];\r
8623 \r
8624                                         if ( first === 1 && last === 0 ) {\r
8625                                                 return true;\r
8626                                         }\r
8627 \r
8628                                         doneName = match[0];\r
8629                                         parent = elem.parentNode;\r
8630 \r
8631                                         if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {\r
8632                                                 count = 0;\r
8633 \r
8634                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
8635                                                         if ( node.nodeType === 1 ) {\r
8636                                                                 node.nodeIndex = ++count;\r
8637                                                         }\r
8638                                                 }\r
8639 \r
8640                                                 parent[ expando ] = doneName;\r
8641                                         }\r
8642 \r
8643                                         diff = elem.nodeIndex - last;\r
8644 \r
8645                                         if ( first === 0 ) {\r
8646                                                 return diff === 0;\r
8647 \r
8648                                         } else {\r
8649                                                 return ( diff % first === 0 && diff / first >= 0 );\r
8650                                         }\r
8651                         }\r
8652                 },\r
8653 \r
8654                 ID: function( elem, match ) {\r
8655                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
8656                 },\r
8657 \r
8658                 TAG: function( elem, match ) {\r
8659                         return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;\r
8660                 },\r
8661 \r
8662                 CLASS: function( elem, match ) {\r
8663                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
8664                                 .indexOf( match ) > -1;\r
8665                 },\r
8666 \r
8667                 ATTR: function( elem, match ) {\r
8668                         var name = match[1],\r
8669                                 result = Sizzle.attr ?\r
8670                                         Sizzle.attr( elem, name ) :\r
8671                                         Expr.attrHandle[ name ] ?\r
8672                                         Expr.attrHandle[ name ]( elem ) :\r
8673                                         elem[ name ] != null ?\r
8674                                                 elem[ name ] :\r
8675                                                 elem.getAttribute( name ),\r
8676                                 value = result + "",\r
8677                                 type = match[2],\r
8678                                 check = match[4];\r
8679 \r
8680                         return result == null ?\r
8681                                 type === "!=" :\r
8682                                 !type && Sizzle.attr ?\r
8683                                 result != null :\r
8684                                 type === "=" ?\r
8685                                 value === check :\r
8686                                 type === "*=" ?\r
8687                                 value.indexOf(check) >= 0 :\r
8688                                 type === "~=" ?\r
8689                                 (" " + value + " ").indexOf(check) >= 0 :\r
8690                                 !check ?\r
8691                                 value && result !== false :\r
8692                                 type === "!=" ?\r
8693                                 value !== check :\r
8694                                 type === "^=" ?\r
8695                                 value.indexOf(check) === 0 :\r
8696                                 type === "$=" ?\r
8697                                 value.substr(value.length - check.length) === check :\r
8698                                 type === "|=" ?\r
8699                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
8700                                 false;\r
8701                 },\r
8702 \r
8703                 POS: function( elem, match, i, array ) {\r
8704                         var name = match[2],\r
8705                                 filter = Expr.setFilters[ name ];\r
8706 \r
8707                         if ( filter ) {\r
8708                                 return filter( elem, i, match, array );\r
8709                         }\r
8710                 }\r
8711         }\r
8712 };\r
8713 \r
8714 var origPOS = Expr.match.POS,\r
8715         fescape = function(all, num){\r
8716                 return "\\" + (num - 0 + 1);\r
8717         };\r
8718 \r
8719 for ( var type in Expr.match ) {\r
8720         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
8721         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
8722 }\r
8723 // Expose origPOS\r
8724 // "global" as in regardless of relation to brackets/parens\r
8725 Expr.match.globalPOS = origPOS;\r
8726 \r
8727 var makeArray = function( array, results ) {\r
8728         array = Array.prototype.slice.call( array, 0 );\r
8729 \r
8730         if ( results ) {\r
8731                 results.push.apply( results, array );\r
8732                 return results;\r
8733         }\r
8734 \r
8735         return array;\r
8736 };\r
8737 \r
8738 // Perform a simple check to determine if the browser is capable of\r
8739 // converting a NodeList to an array using builtin methods.\r
8740 // Also verifies that the returned array holds DOM nodes\r
8741 // (which is not the case in the Blackberry browser)\r
8742 try {\r
8743         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
8744 \r
8745 // Provide a fallback method if it does not work\r
8746 } catch( e ) {\r
8747         makeArray = function( array, results ) {\r
8748                 var i = 0,\r
8749                         ret = results || [];\r
8750 \r
8751                 if ( toString.call(array) === "[object Array]" ) {\r
8752                         Array.prototype.push.apply( ret, array );\r
8753 \r
8754                 } else {\r
8755                         if ( typeof array.length === "number" ) {\r
8756                                 for ( var l = array.length; i < l; i++ ) {\r
8757                                         ret.push( array[i] );\r
8758                                 }\r
8759 \r
8760                         } else {\r
8761                                 for ( ; array[i]; i++ ) {\r
8762                                         ret.push( array[i] );\r
8763                                 }\r
8764                         }\r
8765                 }\r
8766 \r
8767                 return ret;\r
8768         };\r
8769 }\r
8770 \r
8771 var sortOrder, siblingCheck;\r
8772 \r
8773 if ( document.documentElement.compareDocumentPosition ) {\r
8774         sortOrder = function( a, b ) {\r
8775                 if ( a === b ) {\r
8776                         hasDuplicate = true;\r
8777                         return 0;\r
8778                 }\r
8779 \r
8780                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
8781                         return a.compareDocumentPosition ? -1 : 1;\r
8782                 }\r
8783 \r
8784                 return a.compareDocumentPosition(b) & 4 ? -1 : 1;\r
8785         };\r
8786 \r
8787 } else {\r
8788         sortOrder = function( a, b ) {\r
8789                 // The nodes are identical, we can exit early\r
8790                 if ( a === b ) {\r
8791                         hasDuplicate = true;\r
8792                         return 0;\r
8793 \r
8794                 // Fallback to using sourceIndex (in IE) if it's available on both nodes\r
8795                 } else if ( a.sourceIndex && b.sourceIndex ) {\r
8796                         return a.sourceIndex - b.sourceIndex;\r
8797                 }\r
8798 \r
8799                 var al, bl,\r
8800                         ap = [],\r
8801                         bp = [],\r
8802                         aup = a.parentNode,\r
8803                         bup = b.parentNode,\r
8804                         cur = aup;\r
8805 \r
8806                 // If the nodes are siblings (or identical) we can do a quick check\r
8807                 if ( aup === bup ) {\r
8808                         return siblingCheck( a, b );\r
8809 \r
8810                 // If no parents were found then the nodes are disconnected\r
8811                 } else if ( !aup ) {\r
8812                         return -1;\r
8813 \r
8814                 } else if ( !bup ) {\r
8815                         return 1;\r
8816                 }\r
8817 \r
8818                 // Otherwise they're somewhere else in the tree so we need\r
8819                 // to build up a full list of the parentNodes for comparison\r
8820                 while ( cur ) {\r
8821                         ap.unshift( cur );\r
8822                         cur = cur.parentNode;\r
8823                 }\r
8824 \r
8825                 cur = bup;\r
8826 \r
8827                 while ( cur ) {\r
8828                         bp.unshift( cur );\r
8829                         cur = cur.parentNode;\r
8830                 }\r
8831 \r
8832                 al = ap.length;\r
8833                 bl = bp.length;\r
8834 \r
8835                 // Start walking down the tree looking for a discrepancy\r
8836                 for ( var i = 0; i < al && i < bl; i++ ) {\r
8837                         if ( ap[i] !== bp[i] ) {\r
8838                                 return siblingCheck( ap[i], bp[i] );\r
8839                         }\r
8840                 }\r
8841 \r
8842                 // We ended someplace up the tree so do a sibling check\r
8843                 return i === al ?\r
8844                         siblingCheck( a, bp[i], -1 ) :\r
8845                         siblingCheck( ap[i], b, 1 );\r
8846         };\r
8847 \r
8848         siblingCheck = function( a, b, ret ) {\r
8849                 if ( a === b ) {\r
8850                         return ret;\r
8851                 }\r
8852 \r
8853                 var cur = a.nextSibling;\r
8854 \r
8855                 while ( cur ) {\r
8856                         if ( cur === b ) {\r
8857                                 return -1;\r
8858                         }\r
8859 \r
8860                         cur = cur.nextSibling;\r
8861                 }\r
8862 \r
8863                 return 1;\r
8864         };\r
8865 }\r
8866 \r
8867 // Check to see if the browser returns elements by name when\r
8868 // querying by getElementById (and provide a workaround)\r
8869 (function(){\r
8870         // We're going to inject a fake input element with a specified name\r
8871         var form = document.createElement("div"),\r
8872                 id = "script" + (new Date()).getTime(),\r
8873                 root = document.documentElement;\r
8874 \r
8875         form.innerHTML = "<a name='" + id + "'/>";\r
8876 \r
8877         // Inject it into the root element, check its status, and remove it quickly\r
8878         root.insertBefore( form, root.firstChild );\r
8879 \r
8880         // The workaround has to do additional checks after a getElementById\r
8881         // Which slows things down for other browsers (hence the branching)\r
8882         if ( document.getElementById( id ) ) {\r
8883                 Expr.find.ID = function( match, context, isXML ) {\r
8884                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
8885                                 var m = context.getElementById(match[1]);\r
8886 \r
8887                                 return m ?\r
8888                                         m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?\r
8889                                                 [m] :\r
8890                                                 undefined :\r
8891                                         [];\r
8892                         }\r
8893                 };\r
8894 \r
8895                 Expr.filter.ID = function( elem, match ) {\r
8896                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
8897 \r
8898                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
8899                 };\r
8900         }\r
8901 \r
8902         root.removeChild( form );\r
8903 \r
8904         // release memory in IE\r
8905         root = form = null;\r
8906 })();\r
8907 \r
8908 (function(){\r
8909         // Check to see if the browser returns only elements\r
8910         // when doing getElementsByTagName("*")\r
8911 \r
8912         // Create a fake element\r
8913         var div = document.createElement("div");\r
8914         div.appendChild( document.createComment("") );\r
8915 \r
8916         // Make sure no comments are found\r
8917         if ( div.getElementsByTagName("*").length > 0 ) {\r
8918                 Expr.find.TAG = function( match, context ) {\r
8919                         var results = context.getElementsByTagName( match[1] );\r
8920 \r
8921                         // Filter out possible comments\r
8922                         if ( match[1] === "*" ) {\r
8923                                 var tmp = [];\r
8924 \r
8925                                 for ( var i = 0; results[i]; i++ ) {\r
8926                                         if ( results[i].nodeType === 1 ) {\r
8927                                                 tmp.push( results[i] );\r
8928                                         }\r
8929                                 }\r
8930 \r
8931                                 results = tmp;\r
8932                         }\r
8933 \r
8934                         return results;\r
8935                 };\r
8936         }\r
8937 \r
8938         // Check to see if an attribute returns normalized href attributes\r
8939         div.innerHTML = "<a href='#'></a>";\r
8940 \r
8941         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
8942                         div.firstChild.getAttribute("href") !== "#" ) {\r
8943 \r
8944                 Expr.attrHandle.href = function( elem ) {\r
8945                         return elem.getAttribute( "href", 2 );\r
8946                 };\r
8947         }\r
8948 \r
8949         // release memory in IE\r
8950         div = null;\r
8951 })();\r
8952 \r
8953 if ( document.querySelectorAll ) {\r
8954         (function(){\r
8955                 var oldSizzle = Sizzle,\r
8956                         div = document.createElement("div"),\r
8957                         id = "__sizzle__";\r
8958 \r
8959                 div.innerHTML = "<p class='TEST'></p>";\r
8960 \r
8961                 // Safari can't handle uppercase or unicode characters when\r
8962                 // in quirks mode.\r
8963                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
8964                         return;\r
8965                 }\r
8966 \r
8967                 Sizzle = function( query, context, extra, seed ) {\r
8968                         context = context || document;\r
8969 \r
8970                         // Only use querySelectorAll on non-XML documents\r
8971                         // (ID selectors don't work in non-HTML documents)\r
8972                         if ( !seed && !Sizzle.isXML(context) ) {\r
8973                                 // See if we find a selector to speed up\r
8974                                 var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );\r
8975 \r
8976                                 if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {\r
8977                                         // Speed-up: Sizzle("TAG")\r
8978                                         if ( match[1] ) {\r
8979                                                 return makeArray( context.getElementsByTagName( query ), extra );\r
8980 \r
8981                                         // Speed-up: Sizzle(".CLASS")\r
8982                                         } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {\r
8983                                                 return makeArray( context.getElementsByClassName( match[2] ), extra );\r
8984                                         }\r
8985                                 }\r
8986 \r
8987                                 if ( context.nodeType === 9 ) {\r
8988                                         // Speed-up: Sizzle("body")\r
8989                                         // The body element only exists once, optimize finding it\r
8990                                         if ( query === "body" && context.body ) {\r
8991                                                 return makeArray( [ context.body ], extra );\r
8992 \r
8993                                         // Speed-up: Sizzle("#ID")\r
8994                                         } else if ( match && match[3] ) {\r
8995                                                 var elem = context.getElementById( match[3] );\r
8996 \r
8997                                                 // Check parentNode to catch when Blackberry 4.6 returns\r
8998                                                 // nodes that are no longer in the document #6963\r
8999                                                 if ( elem && elem.parentNode ) {\r
9000                                                         // Handle the case where IE and Opera return items\r
9001                                                         // by name instead of ID\r
9002                                                         if ( elem.id === match[3] ) {\r
9003                                                                 return makeArray( [ elem ], extra );\r
9004                                                         }\r
9005 \r
9006                                                 } else {\r
9007                                                         return makeArray( [], extra );\r
9008                                                 }\r
9009                                         }\r
9010 \r
9011                                         try {\r
9012                                                 return makeArray( context.querySelectorAll(query), extra );\r
9013                                         } catch(qsaError) {}\r
9014 \r
9015                                 // qSA works strangely on Element-rooted queries\r
9016                                 // We can work around this by specifying an extra ID on the root\r
9017                                 // and working up from there (Thanks to Andrew Dupont for the technique)\r
9018                                 // IE 8 doesn't work on object elements\r
9019                                 } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {\r
9020                                         var oldContext = context,\r
9021                                                 old = context.getAttribute( "id" ),\r
9022                                                 nid = old || id,\r
9023                                                 hasParent = context.parentNode,\r
9024                                                 relativeHierarchySelector = /^\s*[+~]/.test( query );\r
9025 \r
9026                                         if ( !old ) {\r
9027                                                 context.setAttribute( "id", nid );\r
9028                                         } else {\r
9029                                                 nid = nid.replace( /'/g, "\\$&" );\r
9030                                         }\r
9031                                         if ( relativeHierarchySelector && hasParent ) {\r
9032                                                 context = context.parentNode;\r
9033                                         }\r
9034 \r
9035                                         try {\r
9036                                                 if ( !relativeHierarchySelector || hasParent ) {\r
9037                                                         return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );\r
9038                                                 }\r
9039 \r
9040                                         } catch(pseudoError) {\r
9041                                         } finally {\r
9042                                                 if ( !old ) {\r
9043                                                         oldContext.removeAttribute( "id" );\r
9044                                                 }\r
9045                                         }\r
9046                                 }\r
9047                         }\r
9048 \r
9049                         return oldSizzle(query, context, extra, seed);\r
9050                 };\r
9051 \r
9052                 for ( var prop in oldSizzle ) {\r
9053                         Sizzle[ prop ] = oldSizzle[ prop ];\r
9054                 }\r
9055 \r
9056                 // release memory in IE\r
9057                 div = null;\r
9058         })();\r
9059 }\r
9060 \r
9061 (function(){\r
9062         var html = document.documentElement,\r
9063                 matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;\r
9064 \r
9065         if ( matches ) {\r
9066                 // Check to see if it's possible to do matchesSelector\r
9067                 // on a disconnected node (IE 9 fails this)\r
9068                 var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),\r
9069                         pseudoWorks = false;\r
9070 \r
9071                 try {\r
9072                         // This should fail with an exception\r
9073                         // Gecko does not error, returns false instead\r
9074                         matches.call( document.documentElement, "[test!='']:sizzle" );\r
9075 \r
9076                 } catch( pseudoError ) {\r
9077                         pseudoWorks = true;\r
9078                 }\r
9079 \r
9080                 Sizzle.matchesSelector = function( node, expr ) {\r
9081                         // Make sure that attribute selectors are quoted\r
9082                         expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");\r
9083 \r
9084                         if ( !Sizzle.isXML( node ) ) {\r
9085                                 try {\r
9086                                         if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {\r
9087                                                 var ret = matches.call( node, expr );\r
9088 \r
9089                                                 // IE 9's matchesSelector returns false on disconnected nodes\r
9090                                                 if ( ret || !disconnectedMatch ||\r
9091                                                                 // As well, disconnected nodes are said to be in a document\r
9092                                                                 // fragment in IE 9, so check for that\r
9093                                                                 node.document && node.document.nodeType !== 11 ) {\r
9094                                                         return ret;\r
9095                                                 }\r
9096                                         }\r
9097                                 } catch(e) {}\r
9098                         }\r
9099 \r
9100                         return Sizzle(expr, null, null, [node]).length > 0;\r
9101                 };\r
9102         }\r
9103 })();\r
9104 \r
9105 (function(){\r
9106         var div = document.createElement("div");\r
9107 \r
9108         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
9109 \r
9110         // Opera can't find a second classname (in 9.6)\r
9111         // Also, make sure that getElementsByClassName actually exists\r
9112         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
9113                 return;\r
9114         }\r
9115 \r
9116         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
9117         div.lastChild.className = "e";\r
9118 \r
9119         if ( div.getElementsByClassName("e").length === 1 ) {\r
9120                 return;\r
9121         }\r
9122 \r
9123         Expr.order.splice(1, 0, "CLASS");\r
9124         Expr.find.CLASS = function( match, context, isXML ) {\r
9125                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
9126                         return context.getElementsByClassName(match[1]);\r
9127                 }\r
9128         };\r
9129 \r
9130         // release memory in IE\r
9131         div = null;\r
9132 })();\r
9133 \r
9134 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
9135         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
9136                 var elem = checkSet[i];\r
9137 \r
9138                 if ( elem ) {\r
9139                         var match = false;\r
9140 \r
9141                         elem = elem[dir];\r
9142 \r
9143                         while ( elem ) {\r
9144                                 if ( elem[ expando ] === doneName ) {\r
9145                                         match = checkSet[elem.sizset];\r
9146                                         break;\r
9147                                 }\r
9148 \r
9149                                 if ( elem.nodeType === 1 && !isXML ){\r
9150                                         elem[ expando ] = doneName;\r
9151                                         elem.sizset = i;\r
9152                                 }\r
9153 \r
9154                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
9155                                         match = elem;\r
9156                                         break;\r
9157                                 }\r
9158 \r
9159                                 elem = elem[dir];\r
9160                         }\r
9161 \r
9162                         checkSet[i] = match;\r
9163                 }\r
9164         }\r
9165 }\r
9166 \r
9167 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
9168         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
9169                 var elem = checkSet[i];\r
9170 \r
9171                 if ( elem ) {\r
9172                         var match = false;\r
9173 \r
9174                         elem = elem[dir];\r
9175 \r
9176                         while ( elem ) {\r
9177                                 if ( elem[ expando ] === doneName ) {\r
9178                                         match = checkSet[elem.sizset];\r
9179                                         break;\r
9180                                 }\r
9181 \r
9182                                 if ( elem.nodeType === 1 ) {\r
9183                                         if ( !isXML ) {\r
9184                                                 elem[ expando ] = doneName;\r
9185                                                 elem.sizset = i;\r
9186                                         }\r
9187 \r
9188                                         if ( typeof cur !== "string" ) {\r
9189                                                 if ( elem === cur ) {\r
9190                                                         match = true;\r
9191                                                         break;\r
9192                                                 }\r
9193 \r
9194                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
9195                                                 match = elem;\r
9196                                                 break;\r
9197                                         }\r
9198                                 }\r
9199 \r
9200                                 elem = elem[dir];\r
9201                         }\r
9202 \r
9203                         checkSet[i] = match;\r
9204                 }\r
9205         }\r
9206 }\r
9207 \r
9208 if ( document.documentElement.contains ) {\r
9209         Sizzle.contains = function( a, b ) {\r
9210                 return a !== b && (a.contains ? a.contains(b) : true);\r
9211         };\r
9212 \r
9213 } else if ( document.documentElement.compareDocumentPosition ) {\r
9214         Sizzle.contains = function( a, b ) {\r
9215                 return !!(a.compareDocumentPosition(b) & 16);\r
9216         };\r
9217 \r
9218 } else {\r
9219         Sizzle.contains = function() {\r
9220                 return false;\r
9221         };\r
9222 }\r
9223 \r
9224 Sizzle.isXML = function( elem ) {\r
9225         // documentElement is verified for cases where it doesn't yet exist\r
9226         // (such as loading iframes in IE - #4833)\r
9227         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
9228 \r
9229         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
9230 };\r
9231 \r
9232 var posProcess = function( selector, context, seed ) {\r
9233         var match,\r
9234                 tmpSet = [],\r
9235                 later = "",\r
9236                 root = context.nodeType ? [context] : context;\r
9237 \r
9238         // Position selectors must be done after the filter\r
9239         // And so must :not(positional) so we move all PSEUDOs to the end\r
9240         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
9241                 later += match[0];\r
9242                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
9243         }\r
9244 \r
9245         selector = Expr.relative[selector] ? selector + "*" : selector;\r
9246 \r
9247         for ( var i = 0, l = root.length; i < l; i++ ) {\r
9248                 Sizzle( selector, root[i], tmpSet, seed );\r
9249         }\r
9250 \r
9251         return Sizzle.filter( later, tmpSet );\r
9252 };\r
9253 \r
9254 // EXPOSE\r
9255 \r
9256 window.tinymce.dom.Sizzle = Sizzle;\r
9257 \r
9258 })();\r
9259 \r
9260 \r
9261 (function(tinymce) {\r
9262         tinymce.dom.Element = function(id, settings) {\r
9263                 var t = this, dom, el;\r
9264 \r
9265                 t.settings = settings = settings || {};\r
9266                 t.id = id;\r
9267                 t.dom = dom = settings.dom || tinymce.DOM;\r
9268 \r
9269                 // Only IE leaks DOM references, this is a lot faster\r
9270                 if (!tinymce.isIE)\r
9271                         el = dom.get(t.id);\r
9272 \r
9273                 tinymce.each(\r
9274                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
9275                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
9276                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
9277                                 'isHidden,setHTML,get').split(/,/), function(k) {\r
9278                                         t[k] = function() {\r
9279                                                 var a = [id], i;\r
9280 \r
9281                                                 for (i = 0; i < arguments.length; i++)\r
9282                                                         a.push(arguments[i]);\r
9283 \r
9284                                                 a = dom[k].apply(dom, a);\r
9285                                                 t.update(k);\r
9286 \r
9287                                                 return a;\r
9288                                         };\r
9289                         }\r
9290                 );\r
9291 \r
9292                 tinymce.extend(t, {\r
9293                         on : function(n, f, s) {\r
9294                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
9295                         },\r
9296 \r
9297                         getXY : function() {\r
9298                                 return {\r
9299                                         x : parseInt(t.getStyle('left')),\r
9300                                         y : parseInt(t.getStyle('top'))\r
9301                                 };\r
9302                         },\r
9303 \r
9304                         getSize : function() {\r
9305                                 var n = dom.get(t.id);\r
9306 \r
9307                                 return {\r
9308                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
9309                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
9310                                 };\r
9311                         },\r
9312 \r
9313                         moveTo : function(x, y) {\r
9314                                 t.setStyles({left : x, top : y});\r
9315                         },\r
9316 \r
9317                         moveBy : function(x, y) {\r
9318                                 var p = t.getXY();\r
9319 \r
9320                                 t.moveTo(p.x + x, p.y + y);\r
9321                         },\r
9322 \r
9323                         resizeTo : function(w, h) {\r
9324                                 t.setStyles({width : w, height : h});\r
9325                         },\r
9326 \r
9327                         resizeBy : function(w, h) {\r
9328                                 var s = t.getSize();\r
9329 \r
9330                                 t.resizeTo(s.w + w, s.h + h);\r
9331                         },\r
9332 \r
9333                         update : function(k) {\r
9334                                 var b;\r
9335 \r
9336                                 if (tinymce.isIE6 && settings.blocker) {\r
9337                                         k = k || '';\r
9338 \r
9339                                         // Ignore getters\r
9340                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
9341                                                 return;\r
9342 \r
9343                                         // Remove blocker on remove\r
9344                                         if (k == 'remove') {\r
9345                                                 dom.remove(t.blocker);\r
9346                                                 return;\r
9347                                         }\r
9348 \r
9349                                         if (!t.blocker) {\r
9350                                                 t.blocker = dom.uniqueId();\r
9351                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
9352                                                 dom.setStyle(b, 'opacity', 0);\r
9353                                         } else\r
9354                                                 b = dom.get(t.blocker);\r
9355 \r
9356                                         dom.setStyles(b, {\r
9357                                                 left : t.getStyle('left', 1),\r
9358                                                 top : t.getStyle('top', 1),\r
9359                                                 width : t.getStyle('width', 1),\r
9360                                                 height : t.getStyle('height', 1),\r
9361                                                 display : t.getStyle('display', 1),\r
9362                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
9363                                         });\r
9364                                 }\r
9365                         }\r
9366                 });\r
9367         };\r
9368 })(tinymce);\r
9369 \r
9370 (function(tinymce) {\r
9371         function trimNl(s) {\r
9372                 return s.replace(/[\n\r]+/g, '');\r
9373         };\r
9374 \r
9375         // Shorten names\r
9376         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;\r
9377 \r
9378         tinymce.create('tinymce.dom.Selection', {\r
9379                 Selection : function(dom, win, serializer, editor) {\r
9380                         var t = this;\r
9381 \r
9382                         t.dom = dom;\r
9383                         t.win = win;\r
9384                         t.serializer = serializer;\r
9385                         t.editor = editor;\r
9386 \r
9387                         // Add events\r
9388                         each([\r
9389                                 'onBeforeSetContent',\r
9390 \r
9391                                 'onBeforeGetContent',\r
9392 \r
9393                                 'onSetContent',\r
9394 \r
9395                                 'onGetContent'\r
9396                         ], function(e) {\r
9397                                 t[e] = new tinymce.util.Dispatcher(t);\r
9398                         });\r
9399 \r
9400                         // No W3C Range support\r
9401                         if (!t.win.getSelection)\r
9402                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
9403 \r
9404                         if (tinymce.isIE && ! tinymce.isIE11 && dom.boxModel)\r
9405                                 this._fixIESelection();\r
9406 \r
9407                         // Prevent leaks\r
9408                         tinymce.addUnload(t.destroy, t);\r
9409                 },\r
9410 \r
9411                 setCursorLocation: function(node, offset) {\r
9412                         var t = this; var r = t.dom.createRng();\r
9413                         r.setStart(node, offset);\r
9414                         r.setEnd(node, offset);\r
9415                         t.setRng(r);\r
9416                         t.collapse(false);\r
9417                 },\r
9418                 getContent : function(s) {\r
9419                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
9420 \r
9421                         s = s || {};\r
9422                         wb = wa = '';\r
9423                         s.get = true;\r
9424                         s.format = s.format || 'html';\r
9425                         s.forced_root_block = '';\r
9426                         t.onBeforeGetContent.dispatch(t, s);\r
9427 \r
9428                         if (s.format == 'text')\r
9429                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
9430 \r
9431                         if (r.cloneContents) {\r
9432                                 n = r.cloneContents();\r
9433 \r
9434                                 if (n)\r
9435                                         e.appendChild(n);\r
9436                         } else if (is(r.item) || is(r.htmlText)) {\r
9437                                 // IE will produce invalid markup if elements are present that\r
9438                                 // it doesn't understand like custom elements or HTML5 elements.\r
9439                                 // Adding a BR in front of the contents and then remoiving it seems to fix it though.\r
9440                                 e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);\r
9441                                 e.removeChild(e.firstChild);\r
9442                         } else\r
9443                                 e.innerHTML = r.toString();\r
9444 \r
9445                         // Keep whitespace before and after\r
9446                         if (/^\s/.test(e.innerHTML))\r
9447                                 wb = ' ';\r
9448 \r
9449                         if (/\s+$/.test(e.innerHTML))\r
9450                                 wa = ' ';\r
9451 \r
9452                         s.getInner = true;\r
9453 \r
9454                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
9455                         t.onGetContent.dispatch(t, s);\r
9456 \r
9457                         return s.content;\r
9458                 },\r
9459 \r
9460                 setContent : function(content, args) {\r
9461                         var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;\r
9462 \r
9463                         args = args || {format : 'html'};\r
9464                         args.set = true;\r
9465                         content = args.content = content;\r
9466 \r
9467                         // Dispatch before set content event\r
9468                         if (!args.no_events)\r
9469                                 self.onBeforeSetContent.dispatch(self, args);\r
9470 \r
9471                         content = args.content;\r
9472 \r
9473                         if (rng.insertNode) {\r
9474                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
9475                                 content += '<span id="__caret">_</span>';\r
9476 \r
9477                                 // Delete and insert new node\r
9478                                 if (rng.startContainer == doc && rng.endContainer == doc) {\r
9479                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
9480                                         doc.body.innerHTML = content;\r
9481                                 } else {\r
9482                                         rng.deleteContents();\r
9483 \r
9484                                         if (doc.body.childNodes.length === 0) {\r
9485                                                 doc.body.innerHTML = content;\r
9486                                         } else {\r
9487                                                 // createContextualFragment doesn't exists in IE 9 DOMRanges\r
9488                                                 if (rng.createContextualFragment) {\r
9489                                                         rng.insertNode(rng.createContextualFragment(content));\r
9490                                                 } else {\r
9491                                                         // Fake createContextualFragment call in IE 9\r
9492                                                         frag = doc.createDocumentFragment();\r
9493                                                         temp = doc.createElement('div');\r
9494 \r
9495                                                         frag.appendChild(temp);\r
9496                                                         temp.outerHTML = content;\r
9497 \r
9498                                                         rng.insertNode(frag);\r
9499                                                 }\r
9500                                         }\r
9501                                 }\r
9502 \r
9503                                 // Move to caret marker\r
9504                                 caretNode = self.dom.get('__caret');\r
9505 \r
9506                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
9507                                 rng = doc.createRange();\r
9508                                 rng.setStartBefore(caretNode);\r
9509                                 rng.setEndBefore(caretNode);\r
9510                                 self.setRng(rng);\r
9511 \r
9512                                 // Remove the caret position\r
9513                                 self.dom.remove('__caret');\r
9514 \r
9515                                 try {\r
9516                                         self.setRng(rng);\r
9517                                 } catch (ex) {\r
9518                                         // Might fail on Opera for some odd reason\r
9519                                 }\r
9520                         } else {\r
9521                                 if (rng.item) {\r
9522                                         // Delete content and get caret text selection\r
9523                                         doc.execCommand('Delete', false, null);\r
9524                                         rng = self.getRng();\r
9525                                 }\r
9526 \r
9527                                 // Explorer removes spaces from the beginning of pasted contents\r
9528                                 if (/^\s+/.test(content)) {\r
9529                                         rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);\r
9530                                         self.dom.remove('__mce_tmp');\r
9531                                 } else\r
9532                                         rng.pasteHTML(content);\r
9533                         }\r
9534 \r
9535                         // Dispatch set content event\r
9536                         if (!args.no_events)\r
9537                                 self.onSetContent.dispatch(self, args);\r
9538                 },\r
9539 \r
9540                 getStart : function() {\r
9541                         var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;\r
9542 \r
9543                         if (rng.duplicate || rng.item) {\r
9544                                 // Control selection, return first item\r
9545                                 if (rng.item)\r
9546                                         return rng.item(0);\r
9547 \r
9548                                 // Get start element\r
9549                                 checkRng = rng.duplicate();\r
9550                                 checkRng.collapse(1);\r
9551                                 startElement = checkRng.parentElement();\r
9552                                 if (startElement.ownerDocument !== self.dom.doc) {\r
9553                                         startElement = self.dom.getRoot();\r
9554                                 }\r
9555 \r
9556                                 // Check if range parent is inside the start element, then return the inner parent element\r
9557                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
9558                                 parentElement = node = rng.parentElement();\r
9559                                 while (node = node.parentNode) {\r
9560                                         if (node == startElement) {\r
9561                                                 startElement = parentElement;\r
9562                                                 break;\r
9563                                         }\r
9564                                 }\r
9565 \r
9566                                 return startElement;\r
9567                         } else {\r
9568                                 startElement = rng.startContainer;\r
9569 \r
9570                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
9571                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
9572 \r
9573                                 if (startElement && startElement.nodeType == 3)\r
9574                                         return startElement.parentNode;\r
9575 \r
9576                                 return startElement;\r
9577                         }\r
9578                 },\r
9579 \r
9580                 getEnd : function() {\r
9581                         var self = this, rng = self.getRng(), endElement, endOffset;\r
9582 \r
9583                         if (rng.duplicate || rng.item) {\r
9584                                 if (rng.item)\r
9585                                         return rng.item(0);\r
9586 \r
9587                                 rng = rng.duplicate();\r
9588                                 rng.collapse(0);\r
9589                                 endElement = rng.parentElement();\r
9590                                 if (endElement.ownerDocument !== self.dom.doc) {\r
9591                                         endElement = self.dom.getRoot();\r
9592                                 }\r
9593 \r
9594                                 if (endElement && endElement.nodeName == 'BODY')\r
9595                                         return endElement.lastChild || endElement;\r
9596 \r
9597                                 return endElement;\r
9598                         } else {\r
9599                                 endElement = rng.endContainer;\r
9600                                 endOffset = rng.endOffset;\r
9601 \r
9602                                 if (endElement.nodeType == 1 && endElement.hasChildNodes())\r
9603                                         endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];\r
9604 \r
9605                                 if (endElement && endElement.nodeType == 3)\r
9606                                         return endElement.parentNode;\r
9607 \r
9608                                 return endElement;\r
9609                         }\r
9610                 },\r
9611 \r
9612                 getBookmark : function(type, normalized) {\r
9613                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
9614 \r
9615                         function findIndex(name, element) {\r
9616                                 var index = 0;\r
9617 \r
9618                                 each(dom.select(name), function(node, i) {\r
9619                                         if (node == element)\r
9620                                                 index = i;\r
9621                                 });\r
9622 \r
9623                                 return index;\r
9624                         };\r
9625 \r
9626                         function normalizeTableCellSelection(rng) {\r
9627                                 function moveEndPoint(start) {\r
9628                                         var container, offset, childNodes, prefix = start ? 'start' : 'end';\r
9629 \r
9630                                         container = rng[prefix + 'Container'];\r
9631                                         offset = rng[prefix + 'Offset'];\r
9632 \r
9633                                         if (container.nodeType == 1 && container.nodeName == "TR") {\r
9634                                                 childNodes = container.childNodes;\r
9635                                                 container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];\r
9636                                                 if (container) {\r
9637                                                         offset = start ? 0 : container.childNodes.length;\r
9638                                                         rng['set' + (start ? 'Start' : 'End')](container, offset);\r
9639                                                 }\r
9640                                         }\r
9641                                 };\r
9642 \r
9643                                 moveEndPoint(true);\r
9644                                 moveEndPoint();\r
9645 \r
9646                                 return rng;\r
9647                         };\r
9648 \r
9649                         function getLocation() {\r
9650                                 var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
9651 \r
9652                                 function getPoint(rng, start) {\r
9653                                         var container = rng[start ? 'startContainer' : 'endContainer'],\r
9654                                                 offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
9655 \r
9656                                         if (container.nodeType == 3) {\r
9657                                                 if (normalized) {\r
9658                                                         for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
9659                                                                 offset += node.nodeValue.length;\r
9660                                                 }\r
9661 \r
9662                                                 point.push(offset);\r
9663                                         } else {\r
9664                                                 childNodes = container.childNodes;\r
9665 \r
9666                                                 if (offset >= childNodes.length && childNodes.length) {\r
9667                                                         after = 1;\r
9668                                                         offset = Math.max(0, childNodes.length - 1);\r
9669                                                 }\r
9670 \r
9671                                                 point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
9672                                         }\r
9673 \r
9674                                         for (; container && container != root; container = container.parentNode)\r
9675                                                 point.push(t.dom.nodeIndex(container, normalized));\r
9676 \r
9677                                         return point;\r
9678                                 };\r
9679 \r
9680                                 bookmark.start = getPoint(rng, true);\r
9681 \r
9682                                 if (!t.isCollapsed())\r
9683                                         bookmark.end = getPoint(rng);\r
9684 \r
9685                                 return bookmark;\r
9686                         };\r
9687 \r
9688                         if (type == 2) {\r
9689                                 if (t.tridentSel)\r
9690                                         return t.tridentSel.getBookmark(type);\r
9691 \r
9692                                 return getLocation();\r
9693                         }\r
9694 \r
9695                         // Handle simple range\r
9696                         if (type) {\r
9697                                 rng = t.getRng();\r
9698 \r
9699                                 if (rng.setStart) {\r
9700                                         rng = {\r
9701                                                 startContainer: rng.startContainer,\r
9702                                                 startOffset: rng.startOffset,\r
9703                                                 endContainer: rng.endContainer,\r
9704                                                 endOffset: rng.endOffset\r
9705                                         };\r
9706                                 }\r
9707 \r
9708                                 return {rng : rng};\r
9709                         }\r
9710 \r
9711                         rng = t.getRng();\r
9712                         id = dom.uniqueId();\r
9713                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
9714                         styles = 'overflow:hidden;line-height:0px';\r
9715 \r
9716                         // Explorer method\r
9717                         if (rng.duplicate || rng.item) {\r
9718                                 // Text selection\r
9719                                 if (!rng.item) {\r
9720                                         rng2 = rng.duplicate();\r
9721 \r
9722                                         try {\r
9723                                                 // Insert start marker\r
9724                                                 rng.collapse();\r
9725                                                 rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
9726 \r
9727                                                 // Insert end marker\r
9728                                                 if (!collapsed) {\r
9729                                                         rng2.collapse(false);\r
9730 \r
9731                                                         // Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>\r
9732                                                         rng.moveToElementText(rng2.parentElement());\r
9733                                                         if (rng.compareEndPoints('StartToEnd', rng2) === 0)\r
9734                                                                 rng2.move('character', -1);\r
9735 \r
9736                                                         rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
9737                                                 }\r
9738                                         } catch (ex) {\r
9739                                                 // IE might throw unspecified error so lets ignore it\r
9740                                                 return null;\r
9741                                         }\r
9742                                 } else {\r
9743                                         // Control selection\r
9744                                         element = rng.item(0);\r
9745                                         name = element.nodeName;\r
9746 \r
9747                                         return {name : name, index : findIndex(name, element)};\r
9748                                 }\r
9749                         } else {\r
9750                                 element = t.getNode();\r
9751                                 name = element.nodeName;\r
9752                                 if (name == 'IMG')\r
9753                                         return {name : name, index : findIndex(name, element)};\r
9754 \r
9755                                 // W3C method\r
9756                                 rng2 = normalizeTableCellSelection(rng.cloneRange());\r
9757 \r
9758                                 // Insert end marker\r
9759                                 if (!collapsed) {\r
9760                                         rng2.collapse(false);\r
9761                                         rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));\r
9762                                 }\r
9763 \r
9764                                 rng = normalizeTableCellSelection(rng);\r
9765                                 rng.collapse(true);\r
9766                                 rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));\r
9767                         }\r
9768 \r
9769                         t.moveToBookmark({id : id, keep : 1});\r
9770 \r
9771                         return {id : id};\r
9772                 },\r
9773 \r
9774                 moveToBookmark : function(bookmark) {\r
9775                         var t = this, dom = t.dom, marker1, marker2, rng, rng2, root, startContainer, endContainer, startOffset, endOffset;\r
9776 \r
9777                         function setEndPoint(start) {\r
9778                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
9779 \r
9780                                 if (point) {\r
9781                                         offset = point[0];\r
9782 \r
9783                                         // Find container node\r
9784                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
9785                                                 children = node.childNodes;\r
9786 \r
9787                                                 if (point[i] > children.length - 1)\r
9788                                                         return;\r
9789 \r
9790                                                 node = children[point[i]];\r
9791                                         }\r
9792 \r
9793                                         // Move text offset to best suitable location\r
9794                                         if (node.nodeType === 3)\r
9795                                                 offset = Math.min(point[0], node.nodeValue.length);\r
9796 \r
9797                                         // Move element offset to best suitable location\r
9798                                         if (node.nodeType === 1)\r
9799                                                 offset = Math.min(point[0], node.childNodes.length);\r
9800 \r
9801                                         // Set offset within container node\r
9802                                         if (start)\r
9803                                                 rng.setStart(node, offset);\r
9804                                         else\r
9805                                                 rng.setEnd(node, offset);\r
9806                                 }\r
9807 \r
9808                                 return true;\r
9809                         };\r
9810 \r
9811                         function restoreEndPoint(suffix) {\r
9812                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
9813 \r
9814                                 if (marker) {\r
9815                                         node = marker.parentNode;\r
9816 \r
9817                                         if (suffix == 'start') {\r
9818                                                 if (!keep) {\r
9819                                                         idx = dom.nodeIndex(marker);\r
9820                                                 } else {\r
9821                                                         node = marker.firstChild;\r
9822                                                         idx = 1;\r
9823                                                 }\r
9824 \r
9825                                                 startContainer = endContainer = node;\r
9826                                                 startOffset = endOffset = idx;\r
9827                                         } else {\r
9828                                                 if (!keep) {\r
9829                                                         idx = dom.nodeIndex(marker);\r
9830                                                 } else {\r
9831                                                         node = marker.firstChild;\r
9832                                                         idx = 1;\r
9833                                                 }\r
9834 \r
9835                                                 endContainer = node;\r
9836                                                 endOffset = idx;\r
9837                                         }\r
9838 \r
9839                                         if (!keep) {\r
9840                                                 prev = marker.previousSibling;\r
9841                                                 next = marker.nextSibling;\r
9842 \r
9843                                                 // Remove all marker text nodes\r
9844                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
9845                                                         if (node.nodeType == 3)\r
9846                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
9847                                                 });\r
9848 \r
9849                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
9850                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
9851                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
9852                                                         dom.remove(marker, 1);\r
9853 \r
9854                                                 // If siblings are text nodes then merge them unless it's Opera since it some how removes the node\r
9855                                                 // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact\r
9856                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {\r
9857                                                         idx = prev.nodeValue.length;\r
9858                                                         prev.appendData(next.nodeValue);\r
9859                                                         dom.remove(next);\r
9860 \r
9861                                                         if (suffix == 'start') {\r
9862                                                                 startContainer = endContainer = prev;\r
9863                                                                 startOffset = endOffset = idx;\r
9864                                                         } else {\r
9865                                                                 endContainer = prev;\r
9866                                                                 endOffset = idx;\r
9867                                                         }\r
9868                                                 }\r
9869                                         }\r
9870                                 }\r
9871                         };\r
9872 \r
9873                         function addBogus(node) {\r
9874                                 // Adds a bogus BR element for empty block elements\r
9875                                 if (dom.isBlock(node) && !node.innerHTML && !isIE)\r
9876                                         node.innerHTML = '<br data-mce-bogus="1" />';\r
9877 \r
9878                                 return node;\r
9879                         };\r
9880 \r
9881                         if (bookmark) {\r
9882                                 if (bookmark.start) {\r
9883                                         rng = dom.createRng();\r
9884                                         root = dom.getRoot();\r
9885 \r
9886                                         if (t.tridentSel)\r
9887                                                 return t.tridentSel.moveToBookmark(bookmark);\r
9888 \r
9889                                         if (setEndPoint(true) && setEndPoint()) {\r
9890                                                 t.setRng(rng);\r
9891                                         }\r
9892                                 } else if (bookmark.id) {\r
9893                                         // Restore start/end points\r
9894                                         restoreEndPoint('start');\r
9895                                         restoreEndPoint('end');\r
9896 \r
9897                                         if (startContainer) {\r
9898                                                 rng = dom.createRng();\r
9899                                                 rng.setStart(addBogus(startContainer), startOffset);\r
9900                                                 rng.setEnd(addBogus(endContainer), endOffset);\r
9901                                                 t.setRng(rng);\r
9902                                         }\r
9903                                 } else if (bookmark.name) {\r
9904                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
9905                                 } else if (bookmark.rng) {\r
9906                                         rng = bookmark.rng;\r
9907 \r
9908                                         if (rng.startContainer) {\r
9909                                                 rng2 = t.dom.createRng();\r
9910 \r
9911                                                 try {\r
9912                                                         rng2.setStart(rng.startContainer, rng.startOffset);\r
9913                                                         rng2.setEnd(rng.endContainer, rng.endOffset);\r
9914                                                 } catch (e) {\r
9915                                                         // Might fail with index error\r
9916                                                 }\r
9917 \r
9918                                                 rng = rng2;\r
9919                                         }\r
9920 \r
9921                                         t.setRng(rng);\r
9922                                 }\r
9923                         }\r
9924                 },\r
9925 \r
9926                 select : function(node, content) {\r
9927                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
9928 \r
9929                         function setPoint(node, start) {\r
9930                                 var walker = new TreeWalker(node, node);\r
9931 \r
9932                                 do {\r
9933                                         // Text node\r
9934                                         if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length !== 0) {\r
9935                                                 if (start)\r
9936                                                         rng.setStart(node, 0);\r
9937                                                 else\r
9938                                                         rng.setEnd(node, node.nodeValue.length);\r
9939 \r
9940                                                 return;\r
9941                                         }\r
9942 \r
9943                                         // BR element\r
9944                                         if (node.nodeName == 'BR') {\r
9945                                                 if (start)\r
9946                                                         rng.setStartBefore(node);\r
9947                                                 else\r
9948                                                         rng.setEndBefore(node);\r
9949 \r
9950                                                 return;\r
9951                                         }\r
9952                                 } while (node = (start ? walker.next() : walker.prev()));\r
9953                         };\r
9954 \r
9955                         if (node) {\r
9956                                 idx = dom.nodeIndex(node);\r
9957                                 rng.setStart(node.parentNode, idx);\r
9958                                 rng.setEnd(node.parentNode, idx + 1);\r
9959 \r
9960                                 // Find first/last text node or BR element\r
9961                                 if (content) {\r
9962                                         setPoint(node, 1);\r
9963                                         setPoint(node);\r
9964                                 }\r
9965 \r
9966                                 t.setRng(rng);\r
9967                         }\r
9968 \r
9969                         return node;\r
9970                 },\r
9971 \r
9972                 isCollapsed : function() {\r
9973                         var t = this, r = t.getRng(), s = t.getSel();\r
9974 \r
9975                         if (!r || r.item)\r
9976                                 return false;\r
9977 \r
9978                         if (r.compareEndPoints)\r
9979                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
9980 \r
9981                         return !s || r.collapsed;\r
9982                 },\r
9983 \r
9984                 collapse : function(to_start) {\r
9985                         var self = this, rng = self.getRng(), node;\r
9986 \r
9987                         // Control range on IE\r
9988                         if (rng.item) {\r
9989                                 node = rng.item(0);\r
9990                                 rng = self.win.document.body.createTextRange();\r
9991                                 rng.moveToElementText(node);\r
9992                         }\r
9993 \r
9994                         rng.collapse(!!to_start);\r
9995                         self.setRng(rng);\r
9996                 },\r
9997 \r
9998                 getSel : function() {\r
9999                         var t = this, w = this.win;\r
10000 \r
10001                         return w.getSelection ? w.getSelection() : w.document.selection;\r
10002                 },\r
10003 \r
10004                 getRng : function(w3c) {\r
10005                         var self = this, selection, rng, elm, doc = self.win.document;\r
10006 \r
10007                         // Workaround for IE 11 not being able to select images properly see #6613 see quirk fix\r
10008                         if (self.fakeRng) {\r
10009                                 return self.fakeRng;\r
10010                         }\r
10011 \r
10012                         // Found tridentSel object then we need to use that one\r
10013                         if (w3c && self.tridentSel) {\r
10014                                 return self.tridentSel.getRangeAt(0);\r
10015                         }\r
10016 \r
10017                         try {\r
10018                                 if (selection = self.getSel()) {\r
10019                                         rng = selection.rangeCount > 0 ? selection.getRangeAt(0) : (selection.createRange ? selection.createRange() : doc.createRange());\r
10020                                 }\r
10021                         } catch (ex) {\r
10022                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
10023                         }\r
10024 \r
10025                         // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet\r
10026                         if (tinymce.isIE && ! tinymce.isIE11 && rng && rng.setStart && doc.selection.createRange().item) {\r
10027                                 elm = doc.selection.createRange().item(0);\r
10028                                 rng = doc.createRange();\r
10029                                 rng.setStartBefore(elm);\r
10030                                 rng.setEndAfter(elm);\r
10031                         }\r
10032 \r
10033                         // No range found then create an empty one\r
10034                         // This can occur when the editor is placed in a hidden container element on Gecko\r
10035                         // Or on IE when there was an exception\r
10036                         if (!rng) {\r
10037                                 rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();\r
10038                         }\r
10039 \r
10040                         // If range is at start of document then move it to start of body\r
10041                         if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {\r
10042                                 elm = self.dom.getRoot();\r
10043                                 rng.setStart(elm, 0);\r
10044                                 rng.setEnd(elm, 0);\r
10045                         }\r
10046 \r
10047                         if (self.selectedRange && self.explicitRange) {\r
10048                                 if (rng.compareBoundaryPoints(rng.START_TO_START, self.selectedRange) === 0 && rng.compareBoundaryPoints(rng.END_TO_END, self.selectedRange) === 0) {\r
10049                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
10050                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
10051                                         rng = self.explicitRange;\r
10052                                 } else {\r
10053                                         self.selectedRange = null;\r
10054                                         self.explicitRange = null;\r
10055                                 }\r
10056                         }\r
10057 \r
10058                         return rng;\r
10059                 },\r
10060 \r
10061                 setRng : function(r, forward) {\r
10062                         var s, t = this;\r
10063 \r
10064                         if (!t.tridentSel) {\r
10065                                 s = t.getSel();\r
10066 \r
10067                                 if (s) {\r
10068                                         t.explicitRange = r;\r
10069 \r
10070                                         try {\r
10071                                                 s.removeAllRanges();\r
10072                                         } catch (ex) {\r
10073                                                 // IE9 might throw errors here don't know why\r
10074                                         }\r
10075 \r
10076                                         s.addRange(r);\r
10077 \r
10078                                         // Forward is set to false and we have an extend function\r
10079                                         if (forward === false && s.extend) {\r
10080                                                 s.collapse(r.endContainer, r.endOffset);\r
10081                                                 s.extend(r.startContainer, r.startOffset);\r
10082                                         }\r
10083 \r
10084                                         // adding range isn't always successful so we need to check range count otherwise an exception can occur\r
10085                                         t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;\r
10086                                 }\r
10087                         } else {\r
10088                                 // Is W3C Range\r
10089                                 if (r.cloneRange) {\r
10090                                         try {\r
10091                                                 t.tridentSel.addRange(r);\r
10092                                                 return;\r
10093                                         } catch (ex) {\r
10094                                                 //IE9 throws an error here if called before selection is placed in the editor\r
10095                                         }\r
10096                                 }\r
10097 \r
10098                                 // Is IE specific range\r
10099                                 try {\r
10100                                         r.select();\r
10101                                 } catch (ex) {\r
10102                                         // Needed for some odd IE bug #1843306\r
10103                                 }\r
10104                         }\r
10105                 },\r
10106 \r
10107                 setNode : function(n) {\r
10108                         var t = this;\r
10109 \r
10110                         t.setContent(t.dom.getOuterHTML(n));\r
10111 \r
10112                         return n;\r
10113                 },\r
10114 \r
10115                 getNode : function() {\r
10116                         var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;\r
10117 \r
10118                         function skipEmptyTextNodes(n, forwards) {\r
10119                                 var orig = n;\r
10120                                 while (n && n.nodeType === 3 && n.length === 0) {\r
10121                                         n = forwards ? n.nextSibling : n.previousSibling;\r
10122                                 }\r
10123                                 return n || orig;\r
10124                         };\r
10125 \r
10126                         // Range maybe lost after the editor is made visible again\r
10127                         if (!rng)\r
10128                                 return t.dom.getRoot();\r
10129 \r
10130                         if (rng.setStart) {\r
10131                                 elm = rng.commonAncestorContainer;\r
10132 \r
10133                                 // Handle selection a image or other control like element such as anchors\r
10134                                 if (!rng.collapsed) {\r
10135                                         if (rng.startContainer == rng.endContainer) {\r
10136                                                 if (rng.endOffset - rng.startOffset < 2) {\r
10137                                                         if (rng.startContainer.hasChildNodes())\r
10138                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
10139                                                 }\r
10140                                         }\r
10141 \r
10142                                         // If the anchor node is a element instead of a text node then return this element\r
10143                                         //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)\r
10144                                         //      return sel.anchorNode.childNodes[sel.anchorOffset];\r
10145 \r
10146                                         // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.\r
10147                                         // This happens when you double click an underlined word in FireFox.\r
10148                                         if (start.nodeType === 3 && end.nodeType === 3) {\r
10149                                                 if (start.length === rng.startOffset) {\r
10150                                                         start = skipEmptyTextNodes(start.nextSibling, true);\r
10151                                                 } else {\r
10152                                                         start = start.parentNode;\r
10153                                                 }\r
10154                                                 if (rng.endOffset === 0) {\r
10155                                                         end = skipEmptyTextNodes(end.previousSibling, false);\r
10156                                                 } else {\r
10157                                                         end = end.parentNode;\r
10158                                                 }\r
10159 \r
10160                                                 if (start && start === end)\r
10161                                                         return start;\r
10162                                         }\r
10163                                 }\r
10164 \r
10165                                 if (elm && elm.nodeType == 3)\r
10166                                         return elm.parentNode;\r
10167 \r
10168                                 return elm;\r
10169                         }\r
10170 \r
10171                         return rng.item ? rng.item(0) : rng.parentElement();\r
10172                 },\r
10173 \r
10174                 getSelectedBlocks : function(st, en) {\r
10175                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
10176 \r
10177                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
10178                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
10179 \r
10180                         if (sb)\r
10181                                 bl.push(sb);\r
10182 \r
10183                         if (sb && eb && sb != eb) {\r
10184                                 n = sb;\r
10185 \r
10186                                 var walker = new TreeWalker(sb, dom.getRoot());\r
10187                                 while ((n = walker.next()) && n != eb) {\r
10188                                         if (dom.isBlock(n))\r
10189                                                 bl.push(n);\r
10190                                 }\r
10191                         }\r
10192 \r
10193                         if (eb && sb != eb)\r
10194                                 bl.push(eb);\r
10195 \r
10196                         return bl;\r
10197                 },\r
10198 \r
10199                 isForward: function(){\r
10200                         var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;\r
10201 \r
10202                         // No support for selection direction then always return true\r
10203                         if (!sel || sel.anchorNode == null || sel.focusNode == null) {\r
10204                                 return true;\r
10205                         }\r
10206 \r
10207                         anchorRange = dom.createRng();\r
10208                         anchorRange.setStart(sel.anchorNode, sel.anchorOffset);\r
10209                         anchorRange.collapse(true);\r
10210 \r
10211                         focusRange = dom.createRng();\r
10212                         focusRange.setStart(sel.focusNode, sel.focusOffset);\r
10213                         focusRange.collapse(true);\r
10214 \r
10215                         return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;\r
10216                 },\r
10217 \r
10218                 normalize : function() {\r
10219                         var self = this, rng, normalized, collapsed, node, sibling;\r
10220 \r
10221                         function normalizeEndPoint(start) {\r
10222                                 var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, nodeName;\r
10223 \r
10224                                 function hasBrBeforeAfter(node, left) {\r
10225                                         var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);\r
10226 \r
10227                                         while (node = walker[left ? 'prev' : 'next']()) {\r
10228                                                 if (node.nodeName === "BR") {\r
10229                                                         return true;\r
10230                                                 }\r
10231                                         }\r
10232                                 };\r
10233 \r
10234                                 // Walks the dom left/right to find a suitable text node to move the endpoint into\r
10235                                 // It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG\r
10236                                 function findTextNodeRelative(left, startNode) {\r
10237                                         var walker, lastInlineElement;\r
10238 \r
10239                                         startNode = startNode || container;\r
10240                                         walker = new TreeWalker(startNode, dom.getParent(startNode.parentNode, dom.isBlock) || body);\r
10241 \r
10242                                         // Walk left until we hit a text node we can move to or a block/br/img\r
10243                                         while (node = walker[left ? 'prev' : 'next']()) {\r
10244                                                 // Found text node that has a length\r
10245                                                 if (node.nodeType === 3 && node.nodeValue.length > 0) {\r
10246                                                         container = node;\r
10247                                                         offset = left ? node.nodeValue.length : 0;\r
10248                                                         normalized = true;\r
10249                                                         return;\r
10250                                                 }\r
10251 \r
10252                                                 // Break if we find a block or a BR/IMG/INPUT etc\r
10253                                                 if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {\r
10254                                                         return;\r
10255                                                 }\r
10256 \r
10257                                                 lastInlineElement = node;\r
10258                                         }\r
10259 \r
10260                                         // Only fetch the last inline element when in caret mode for now\r
10261                                         if (collapsed && lastInlineElement) {\r
10262                                                 container = lastInlineElement;\r
10263                                                 normalized = true;\r
10264                                                 offset = 0;\r
10265                                         }\r
10266                                 };\r
10267 \r
10268                                 container = rng[(start ? 'start' : 'end') + 'Container'];\r
10269                                 offset = rng[(start ? 'start' : 'end') + 'Offset'];\r
10270                                 nonEmptyElementsMap = dom.schema.getNonEmptyElements();\r
10271 \r
10272                                 // If the container is a document move it to the body element\r
10273                                 if (container.nodeType === 9) {\r
10274                                         container = dom.getRoot();\r
10275                                         offset = 0;\r
10276                                 }\r
10277 \r
10278                                 // If the container is body try move it into the closest text node or position\r
10279                                 if (container === body) {\r
10280                                         // If start is before/after a image, table etc\r
10281                                         if (start) {\r
10282                                                 node = container.childNodes[offset > 0 ? offset - 1 : 0];\r
10283                                                 if (node) {\r
10284                                                         nodeName = node.nodeName.toLowerCase();\r
10285                                                         if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {\r
10286                                                                 return;\r
10287                                                         }\r
10288                                                 }\r
10289                                         }\r
10290 \r
10291                                         // Resolve the index\r
10292                                         if (container.hasChildNodes()) {\r
10293                                                 container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];\r
10294                                                 offset = 0;\r
10295 \r
10296                                                 // Don't walk into elements that doesn't have any child nodes like a IMG\r
10297                                                 if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {\r
10298                                                         // Walk the DOM to find a text node to place the caret at or a BR\r
10299                                                         node = container;\r
10300                                                         walker = new TreeWalker(container, body);\r
10301 \r
10302                                                         do {\r
10303                                                                 // Found a text node use that position\r
10304                                                                 if (node.nodeType === 3 && node.nodeValue.length > 0) {\r
10305                                                                         offset = start ? 0 : node.nodeValue.length;\r
10306                                                                         container = node;\r
10307                                                                         normalized = true;\r
10308                                                                         break;\r
10309                                                                 }\r
10310 \r
10311                                                                 // Found a BR/IMG element that we can place the caret before\r
10312                                                                 if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {\r
10313                                                                         offset = dom.nodeIndex(node);\r
10314                                                                         container = node.parentNode;\r
10315 \r
10316                                                                         // Put caret after image when moving the end point\r
10317                                                                         if (node.nodeName ==  "IMG" && !start) {\r
10318                                                                                 offset++;\r
10319                                                                         }\r
10320 \r
10321                                                                         normalized = true;\r
10322                                                                         break;\r
10323                                                                 }\r
10324                                                         } while (node = (start ? walker.next() : walker.prev()));\r
10325                                                 }\r
10326                                         }\r
10327                                 }\r
10328 \r
10329                                 // Lean the caret to the left if possible\r
10330                                 if (collapsed) {\r
10331                                         // So this: <b>x</b><i>|x</i>\r
10332                                         // Becomes: <b>x|</b><i>x</i>\r
10333                                         // Seems that only gecko has issues with this\r
10334                                         if (container.nodeType === 3 && offset === 0) {\r
10335                                                 findTextNodeRelative(true);\r
10336                                         }\r
10337 \r
10338                                         // Lean left into empty inline elements when the caret is before a BR\r
10339                                         // So this: <i><b></b><i>|<br></i>\r
10340                                         // Becomes: <i><b>|</b><i><br></i>\r
10341                                         // Seems that only gecko has issues with this\r
10342                                         if (container.nodeType === 1) {\r
10343                                                 node = container.childNodes[offset];\r
10344                                                 if(node && node.nodeName === 'BR' && !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {\r
10345                                                         findTextNodeRelative(true, container.childNodes[offset]);\r
10346                                                 }\r
10347                                         }\r
10348                                 }\r
10349 \r
10350                                 // Lean the start of the selection right if possible\r
10351                                 // So this: x[<b>x]</b>\r
10352                                 // Becomes: x<b>[x]</b>\r
10353                                 if (start && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {\r
10354                                         findTextNodeRelative(false);\r
10355                                 }\r
10356 \r
10357                                 // Set endpoint if it was normalized\r
10358                                 if (normalized)\r
10359                                         rng['set' + (start ? 'Start' : 'End')](container, offset);\r
10360                         };\r
10361 \r
10362                         // Normalize only on non IE browsers for now\r
10363                         if (tinymce.isIE)\r
10364                                 return;\r
10365                         \r
10366                         rng = self.getRng();\r
10367                         collapsed = rng.collapsed;\r
10368 \r
10369                         // Normalize the end points\r
10370                         normalizeEndPoint(true);\r
10371 \r
10372                         if (!collapsed)\r
10373                                 normalizeEndPoint();\r
10374 \r
10375                         // Set the selection if it was normalized\r
10376                         if (normalized) {\r
10377                                 // If it was collapsed then make sure it still is\r
10378                                 if (collapsed) {\r
10379                                         rng.collapse(true);\r
10380                                 }\r
10381 \r
10382                                 //console.log(self.dom.dumpRng(rng));\r
10383                                 self.setRng(rng, self.isForward());\r
10384                         }\r
10385                 },\r
10386 \r
10387                 selectorChanged: function(selector, callback) {\r
10388                         var self = this, currentSelectors;\r
10389 \r
10390                         if (!self.selectorChangedData) {\r
10391                                 self.selectorChangedData = {};\r
10392                                 currentSelectors = {};\r
10393 \r
10394                                 self.editor.onNodeChange.addToTop(function(ed, cm, node) {\r
10395                                         var dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};\r
10396 \r
10397                                         // Check for new matching selectors\r
10398                                         each(self.selectorChangedData, function(callbacks, selector) {\r
10399                                                 each(parents, function(node) {\r
10400                                                         if (dom.is(node, selector)) {\r
10401                                                                 if (!currentSelectors[selector]) {\r
10402                                                                         // Execute callbacks\r
10403                                                                         each(callbacks, function(callback) {\r
10404                                                                                 callback(true, {node: node, selector: selector, parents: parents});\r
10405                                                                         });\r
10406 \r
10407                                                                         currentSelectors[selector] = callbacks;\r
10408                                                                 }\r
10409 \r
10410                                                                 matchedSelectors[selector] = callbacks;\r
10411                                                                 return false;\r
10412                                                         }\r
10413                                                 });\r
10414                                         });\r
10415 \r
10416                                         // Check if current selectors still match\r
10417                                         each(currentSelectors, function(callbacks, selector) {\r
10418                                                 if (!matchedSelectors[selector]) {\r
10419                                                         delete currentSelectors[selector];\r
10420 \r
10421                                                         each(callbacks, function(callback) {\r
10422                                                                 callback(false, {node: node, selector: selector, parents: parents});\r
10423                                                         });\r
10424                                                 }\r
10425                                         });\r
10426                                 });\r
10427                         }\r
10428 \r
10429                         // Add selector listeners\r
10430                         if (!self.selectorChangedData[selector]) {\r
10431                                 self.selectorChangedData[selector] = [];\r
10432                         }\r
10433 \r
10434                         self.selectorChangedData[selector].push(callback);\r
10435 \r
10436                         return self;\r
10437                 },\r
10438 \r
10439                 scrollIntoView: function(elm) {\r
10440                         var y, viewPort, self = this, dom = self.dom;\r
10441 \r
10442                         viewPort = dom.getViewPort(self.editor.getWin());\r
10443                         y = dom.getPos(elm).y;\r
10444                         if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {\r
10445                                 self.editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25);\r
10446                         }\r
10447                 },\r
10448 \r
10449                 destroy : function(manual) {\r
10450                         var self = this;\r
10451 \r
10452                         self.win = null;\r
10453 \r
10454                         // Manual destroy then remove unload handler\r
10455                         if (!manual)\r
10456                                 tinymce.removeUnload(self.destroy);\r
10457                 },\r
10458 \r
10459                 // 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
10460                 _fixIESelection : function() {\r
10461                         var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;\r
10462 \r
10463                         // Return range from point or null if it failed\r
10464                         function rngFromPoint(x, y) {\r
10465                                 var rng = body.createTextRange();\r
10466 \r
10467                                 try {\r
10468                                         rng.moveToPoint(x, y);\r
10469                                 } catch (ex) {\r
10470                                         // IE sometimes throws and exception, so lets just ignore it\r
10471                                         rng = null;\r
10472                                 }\r
10473 \r
10474                                 return rng;\r
10475                         };\r
10476 \r
10477                         // Fires while the selection is changing\r
10478                         function selectionChange(e) {\r
10479                                 var pointRng;\r
10480 \r
10481                                 // Check if the button is down or not\r
10482                                 if (e.button) {\r
10483                                         // Create range from mouse position\r
10484                                         pointRng = rngFromPoint(e.x, e.y);\r
10485 \r
10486                                         if (pointRng) {\r
10487                                                 // Check if pointRange is before/after selection then change the endPoint\r
10488                                                 if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
10489                                                         pointRng.setEndPoint('StartToStart', startRng);\r
10490                                                 else\r
10491                                                         pointRng.setEndPoint('EndToEnd', startRng);\r
10492 \r
10493                                                 pointRng.select();\r
10494                                         }\r
10495                                 } else\r
10496                                         endSelection();\r
10497                         }\r
10498 \r
10499                         // Removes listeners\r
10500                         function endSelection() {\r
10501                                 var rng = doc.selection.createRange();\r
10502 \r
10503                                 // If the range is collapsed then use the last start range\r
10504                                 if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0)\r
10505                                         startRng.select();\r
10506 \r
10507                                 dom.unbind(doc, 'mouseup', endSelection);\r
10508                                 dom.unbind(doc, 'mousemove', selectionChange);\r
10509                                 startRng = started = 0;\r
10510                         };\r
10511 \r
10512                         // Make HTML element unselectable since we are going to handle selection by hand\r
10513                         doc.documentElement.unselectable = true;\r
10514                         \r
10515                         // Detect when user selects outside BODY\r
10516                         dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {\r
10517                                 if (e.target.nodeName === 'HTML') {\r
10518                                         if (started)\r
10519                                                 endSelection();\r
10520 \r
10521                                         // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML\r
10522                                         htmlElm = doc.documentElement;\r
10523                                         if (htmlElm.scrollHeight > htmlElm.clientHeight)\r
10524                                                 return;\r
10525 \r
10526                                         started = 1;\r
10527                                         // Setup start position\r
10528                                         startRng = rngFromPoint(e.x, e.y);\r
10529                                         if (startRng) {\r
10530                                                 // Listen for selection change events\r
10531                                                 dom.bind(doc, 'mouseup', endSelection);\r
10532                                                 dom.bind(doc, 'mousemove', selectionChange);\r
10533 \r
10534                                                 dom.win.focus();\r
10535                                                 startRng.select();\r
10536                                         }\r
10537                                 }\r
10538                         });\r
10539                 }\r
10540         });\r
10541 })(tinymce);\r
10542 \r
10543 (function(tinymce) {\r
10544         tinymce.dom.Serializer = function(settings, dom, schema) {\r
10545                 var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;\r
10546 \r
10547                 // Support the old apply_source_formatting option\r
10548                 if (!settings.apply_source_formatting)\r
10549                         settings.indent = false;\r
10550 \r
10551                 // Default DOM and Schema if they are undefined\r
10552                 dom = dom || tinymce.DOM;\r
10553                 schema = schema || new tinymce.html.Schema(settings);\r
10554                 settings.entity_encoding = settings.entity_encoding || 'named';\r
10555                 settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;\r
10556 \r
10557                 onPreProcess = new tinymce.util.Dispatcher(self);\r
10558 \r
10559                 onPostProcess = new tinymce.util.Dispatcher(self);\r
10560 \r
10561                 htmlParser = new tinymce.html.DomParser(settings, schema);\r
10562 \r
10563                 // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed\r
10564                 htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {\r
10565                         var i = nodes.length, node, value, internalName = 'data-mce-' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;\r
10566 \r
10567                         while (i--) {\r
10568                                 node = nodes[i];\r
10569 \r
10570                                 value = node.attributes.map[internalName];\r
10571                                 if (value !== undef) {\r
10572                                         // Set external name to internal value and remove internal\r
10573                                         node.attr(name, value.length > 0 ? value : null);\r
10574                                         node.attr(internalName, null);\r
10575                                 } else {\r
10576                                         // No internal attribute found then convert the value we have in the DOM\r
10577                                         value = node.attributes.map[name];\r
10578 \r
10579                                         if (name === "style")\r
10580                                                 value = dom.serializeStyle(dom.parseStyle(value), node.name);\r
10581                                         else if (urlConverter)\r
10582                                                 value = urlConverter.call(urlConverterScope, value, name, node.name);\r
10583 \r
10584                                         node.attr(name, value.length > 0 ? value : null);\r
10585                                 }\r
10586                         }\r
10587                 });\r
10588 \r
10589                 // Remove internal classes mceItem<..> or mceSelected\r
10590                 htmlParser.addAttributeFilter('class', function(nodes, name) {\r
10591                         var i = nodes.length, node, value;\r
10592 \r
10593                         while (i--) {\r
10594                                 node = nodes[i];\r
10595                                 value = node.attr('class').replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g, '');\r
10596                                 node.attr('class', value.length > 0 ? value : null);\r
10597                         }\r
10598                 });\r
10599 \r
10600                 // Remove bookmark elements\r
10601                 htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {\r
10602                         var i = nodes.length, node;\r
10603 \r
10604                         while (i--) {\r
10605                                 node = nodes[i];\r
10606 \r
10607                                 if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup)\r
10608                                         node.remove();\r
10609                         }\r
10610                 });\r
10611 \r
10612                 // Remove expando attributes\r
10613                 htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name, args) {\r
10614                         var i = nodes.length;\r
10615 \r
10616                         while (i--) {\r
10617                                 nodes[i].attr(name, null);\r
10618                         }\r
10619                 });\r
10620 \r
10621                 htmlParser.addNodeFilter('noscript', function(nodes) {\r
10622                         var i = nodes.length, node;\r
10623 \r
10624                         while (i--) {\r
10625                                 node = nodes[i].firstChild;\r
10626 \r
10627                                 if (node) {\r
10628                                         node.value = tinymce.html.Entities.decode(node.value);\r
10629                                 }\r
10630                         }\r
10631                 });\r
10632 \r
10633                 // Force script into CDATA sections and remove the mce- prefix also add comments around styles\r
10634                 htmlParser.addNodeFilter('script,style', function(nodes, name) {\r
10635                         var i = nodes.length, node, value;\r
10636 \r
10637                         function trim(value) {\r
10638                                 return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')\r
10639                                                 .replace(/^[\r\n]*|[\r\n]*$/g, '')\r
10640                                                 .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')\r
10641                                                 .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');\r
10642                         };\r
10643 \r
10644                         while (i--) {\r
10645                                 node = nodes[i];\r
10646                                 value = node.firstChild ? node.firstChild.value : '';\r
10647 \r
10648                                 if (name === "script") {\r
10649                                         // Remove mce- prefix from script elements\r
10650                                         node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));\r
10651 \r
10652                                         if (value.length > 0)\r
10653                                                 node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';\r
10654                                 } else {\r
10655                                         if (value.length > 0)\r
10656                                                 node.firstChild.value = '<!--\n' + trim(value) + '\n-->';\r
10657                                 }\r
10658                         }\r
10659                 });\r
10660 \r
10661                 // Convert comments to cdata and handle protected comments\r
10662                 htmlParser.addNodeFilter('#comment', function(nodes, name) {\r
10663                         var i = nodes.length, node;\r
10664 \r
10665                         while (i--) {\r
10666                                 node = nodes[i];\r
10667 \r
10668                                 if (node.value.indexOf('[CDATA[') === 0) {\r
10669                                         node.name = '#cdata';\r
10670                                         node.type = 4;\r
10671                                         node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');\r
10672                                 } else if (node.value.indexOf('mce:protected ') === 0) {\r
10673                                         node.name = "#text";\r
10674                                         node.type = 3;\r
10675                                         node.raw = true;\r
10676                                         node.value = unescape(node.value).substr(14);\r
10677                                 }\r
10678                         }\r
10679                 });\r
10680 \r
10681                 htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {\r
10682                         var i = nodes.length, node;\r
10683 \r
10684                         while (i--) {\r
10685                                 node = nodes[i];\r
10686                                 if (node.type === 7)\r
10687                                         node.remove();\r
10688                                 else if (node.type === 1) {\r
10689                                         if (name === "input" && !("type" in node.attributes.map))\r
10690                                                 node.attr('type', 'text');\r
10691                                 }\r
10692                         }\r
10693                 });\r
10694 \r
10695                 // Fix list elements, TODO: Replace this later\r
10696                 if (settings.fix_list_elements) {\r
10697                         htmlParser.addNodeFilter('ul,ol', function(nodes, name) {\r
10698                                 var i = nodes.length, node, parentNode;\r
10699 \r
10700                                 while (i--) {\r
10701                                         node = nodes[i];\r
10702                                         parentNode = node.parent;\r
10703 \r
10704                                         if (parentNode.name === 'ul' || parentNode.name === 'ol') {\r
10705                                                 if (node.prev && node.prev.name === 'li') {\r
10706                                                         node.prev.append(node);\r
10707                                                 }\r
10708                                         }\r
10709                                 }\r
10710                         });\r
10711                 }\r
10712 \r
10713                 // Remove internal data attributes\r
10714                 htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style', function(nodes, name) {\r
10715                         var i = nodes.length;\r
10716 \r
10717                         while (i--) {\r
10718                                 nodes[i].attr(name, null);\r
10719                         }\r
10720                 });\r
10721 \r
10722                 // Return public methods\r
10723                 return {\r
10724                         schema : schema,\r
10725 \r
10726                         addNodeFilter : htmlParser.addNodeFilter,\r
10727 \r
10728                         addAttributeFilter : htmlParser.addAttributeFilter,\r
10729 \r
10730                         onPreProcess : onPreProcess,\r
10731 \r
10732                         onPostProcess : onPostProcess,\r
10733 \r
10734                         serialize : function(node, args) {\r
10735                                 var impl, doc, oldDoc, htmlSerializer, content;\r
10736 \r
10737                                 // Explorer won't clone contents of script and style and the\r
10738                                 // selected index of select elements are cleared on a clone operation.\r
10739                                 if (isIE && dom.select('script,style,select,map').length > 0) {\r
10740                                         content = node.innerHTML;\r
10741                                         node = node.cloneNode(false);\r
10742                                         dom.setHTML(node, content);\r
10743                                 } else\r
10744                                         node = node.cloneNode(true);\r
10745 \r
10746                                 // Nodes needs to be attached to something in WebKit/Opera\r
10747                                 // Older builds of Opera crashes if you attach the node to an document created dynamically\r
10748                                 // and since we can't feature detect a crash we need to sniff the acutal build number\r
10749                                 // This fix will make DOM ranges and make Sizzle happy!\r
10750                                 impl = node.ownerDocument.implementation;\r
10751                                 if (impl.createHTMLDocument) {\r
10752                                         // Create an empty HTML document\r
10753                                         doc = impl.createHTMLDocument("");\r
10754 \r
10755                                         // Add the element or it's children if it's a body element to the new document\r
10756                                         each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {\r
10757                                                 doc.body.appendChild(doc.importNode(node, true));\r
10758                                         });\r
10759 \r
10760                                         // Grab first child or body element for serialization\r
10761                                         if (node.nodeName != 'BODY')\r
10762                                                 node = doc.body.firstChild;\r
10763                                         else\r
10764                                                 node = doc.body;\r
10765 \r
10766                                         // set the new document in DOMUtils so createElement etc works\r
10767                                         oldDoc = dom.doc;\r
10768                                         dom.doc = doc;\r
10769                                 }\r
10770 \r
10771                                 args = args || {};\r
10772                                 args.format = args.format || 'html';\r
10773 \r
10774                                 // Pre process\r
10775                                 if (!args.no_events) {\r
10776                                         args.node = node;\r
10777                                         onPreProcess.dispatch(self, args);\r
10778                                 }\r
10779 \r
10780                                 // Setup serializer\r
10781                                 htmlSerializer = new tinymce.html.Serializer(settings, schema);\r
10782 \r
10783                                 // Parse and serialize HTML\r
10784                                 args.content = htmlSerializer.serialize(\r
10785                                         htmlParser.parse(tinymce.trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)\r
10786                                 );\r
10787 \r
10788                                 // Replace all BOM characters for now until we can find a better solution\r
10789                                 if (!args.cleanup)\r
10790                                         args.content = args.content.replace(/\uFEFF/g, '');\r
10791 \r
10792                                 // Post process\r
10793                                 if (!args.no_events)\r
10794                                         onPostProcess.dispatch(self, args);\r
10795 \r
10796                                 // Restore the old document if it was changed\r
10797                                 if (oldDoc)\r
10798                                         dom.doc = oldDoc;\r
10799 \r
10800                                 args.node = null;\r
10801 \r
10802                                 return args.content;\r
10803                         },\r
10804 \r
10805                         addRules : function(rules) {\r
10806                                 schema.addValidElements(rules);\r
10807                         },\r
10808 \r
10809                         setRules : function(rules) {\r
10810                                 schema.setValidElements(rules);\r
10811                         }\r
10812                 };\r
10813         };\r
10814 })(tinymce);\r
10815 (function(tinymce) {\r
10816         tinymce.dom.ScriptLoader = function(settings) {\r
10817                 var QUEUED = 0,\r
10818                         LOADING = 1,\r
10819                         LOADED = 2,\r
10820                         states = {},\r
10821                         queue = [],\r
10822                         scriptLoadedCallbacks = {},\r
10823                         queueLoadedCallbacks = [],\r
10824                         loading = 0,\r
10825                         undef;\r
10826 \r
10827                 function loadScript(url, callback) {\r
10828                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
10829 \r
10830                         // Execute callback when script is loaded\r
10831                         function done() {\r
10832                                 dom.remove(id);\r
10833 \r
10834                                 if (elm)\r
10835                                         elm.onreadystatechange = elm.onload = elm = null;\r
10836 \r
10837                                 callback();\r
10838                         };\r
10839                         \r
10840                         function error() {\r
10841                                 // Report the error so it's easier for people to spot loading errors\r
10842                                 if (typeof(console) !== "undefined" && console.log)\r
10843                                         console.log("Failed to load: " + url);\r
10844 \r
10845                                 // We can't mark it as done if there is a load error since\r
10846                                 // A) We don't want to produce 404 errors on the server and\r
10847                                 // B) the onerror event won't fire on all browsers.\r
10848                                 // done();\r
10849                         };\r
10850 \r
10851                         id = dom.uniqueId();\r
10852 \r
10853                         if (tinymce.isIE6) {\r
10854                                 uri = new tinymce.util.URI(url);\r
10855                                 loc = location;\r
10856 \r
10857                                 // If script is from same domain and we\r
10858                                 // use IE 6 then use XHR since it's more reliable\r
10859                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {\r
10860                                         tinymce.util.XHR.send({\r
10861                                                 url : tinymce._addVer(uri.getURI()),\r
10862                                                 success : function(content) {\r
10863                                                         // Create new temp script element\r
10864                                                         var script = dom.create('script', {\r
10865                                                                 type : 'text/javascript'\r
10866                                                         });\r
10867 \r
10868                                                         // Evaluate script in global scope\r
10869                                                         script.text = content;\r
10870                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
10871                                                         dom.remove(script);\r
10872 \r
10873                                                         done();\r
10874                                                 },\r
10875                                                 \r
10876                                                 error : error\r
10877                                         });\r
10878 \r
10879                                         return;\r
10880                                 }\r
10881                         }\r
10882 \r
10883                         // Create new script element\r
10884                         elm = document.createElement('script');\r
10885                         elm.id = id;\r
10886                         elm.type = 'text/javascript';\r
10887                         elm.src = tinymce._addVer(url);\r
10888 \r
10889                         // Add onload listener for non IE browsers since IE9\r
10890                         // fires onload event before the script is parsed and executed\r
10891                         if (!tinymce.isIE || tinymce.isIE11)\r
10892                                 elm.onload = done;\r
10893 \r
10894                         // Add onerror event will get fired on some browsers but not all of them\r
10895                         elm.onerror = error;\r
10896 \r
10897                         // Opera 9.60 doesn't seem to fire the onreadystate event at correctly\r
10898                         if (!tinymce.isOpera) {\r
10899                                 elm.onreadystatechange = function() {\r
10900                                         var state = elm.readyState;\r
10901 \r
10902                                         // Loaded state is passed on IE 6 however there\r
10903                                         // are known issues with this method but we can't use\r
10904                                         // XHR in a cross domain loading\r
10905                                         if (state == 'complete' || state == 'loaded')\r
10906                                                 done();\r
10907                                 };\r
10908                         }\r
10909 \r
10910                         // Most browsers support this feature so we report errors\r
10911                         // for those at least to help users track their missing plugins etc\r
10912                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
10913                         /*elm.onerror = function() {\r
10914                                 alert('Failed to load: ' + url);\r
10915                         };*/\r
10916 \r
10917                         // Add script to document\r
10918                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
10919                 };\r
10920 \r
10921                 this.isDone = function(url) {\r
10922                         return states[url] == LOADED;\r
10923                 };\r
10924 \r
10925                 this.markDone = function(url) {\r
10926                         states[url] = LOADED;\r
10927                 };\r
10928 \r
10929                 this.add = this.load = function(url, callback, scope) {\r
10930                         var item, state = states[url];\r
10931 \r
10932                         // Add url to load queue\r
10933                         if (state == undef) {\r
10934                                 queue.push(url);\r
10935                                 states[url] = QUEUED;\r
10936                         }\r
10937 \r
10938                         if (callback) {\r
10939                                 // Store away callback for later execution\r
10940                                 if (!scriptLoadedCallbacks[url])\r
10941                                         scriptLoadedCallbacks[url] = [];\r
10942 \r
10943                                 scriptLoadedCallbacks[url].push({\r
10944                                         func : callback,\r
10945                                         scope : scope || this\r
10946                                 });\r
10947                         }\r
10948                 };\r
10949 \r
10950                 this.loadQueue = function(callback, scope) {\r
10951                         this.loadScripts(queue, callback, scope);\r
10952                 };\r
10953 \r
10954                 this.loadScripts = function(scripts, callback, scope) {\r
10955                         var loadScripts;\r
10956 \r
10957                         function execScriptLoadedCallbacks(url) {\r
10958                                 // Execute URL callback functions\r
10959                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
10960                                         callback.func.call(callback.scope);\r
10961                                 });\r
10962 \r
10963                                 scriptLoadedCallbacks[url] = undef;\r
10964                         };\r
10965 \r
10966                         queueLoadedCallbacks.push({\r
10967                                 func : callback,\r
10968                                 scope : scope || this\r
10969                         });\r
10970 \r
10971                         loadScripts = function() {\r
10972                                 var loadingScripts = tinymce.grep(scripts);\r
10973 \r
10974                                 // Current scripts has been handled\r
10975                                 scripts.length = 0;\r
10976 \r
10977                                 // Load scripts that needs to be loaded\r
10978                                 tinymce.each(loadingScripts, function(url) {\r
10979                                         // Script is already loaded then execute script callbacks directly\r
10980                                         if (states[url] == LOADED) {\r
10981                                                 execScriptLoadedCallbacks(url);\r
10982                                                 return;\r
10983                                         }\r
10984 \r
10985                                         // Is script not loading then start loading it\r
10986                                         if (states[url] != LOADING) {\r
10987                                                 states[url] = LOADING;\r
10988                                                 loading++;\r
10989 \r
10990                                                 loadScript(url, function() {\r
10991                                                         states[url] = LOADED;\r
10992                                                         loading--;\r
10993 \r
10994                                                         execScriptLoadedCallbacks(url);\r
10995 \r
10996                                                         // Load more scripts if they where added by the recently loaded script\r
10997                                                         loadScripts();\r
10998                                                 });\r
10999                                         }\r
11000                                 });\r
11001 \r
11002                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
11003                                 if (!loading) {\r
11004                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
11005                                                 callback.func.call(callback.scope);\r
11006                                         });\r
11007 \r
11008                                         queueLoadedCallbacks.length = 0;\r
11009                                 }\r
11010                         };\r
11011 \r
11012                         loadScripts();\r
11013                 };\r
11014         };\r
11015 \r
11016         // Global script loader\r
11017         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
11018 })(tinymce);\r
11019 \r
11020 (function(tinymce) {\r
11021         tinymce.dom.RangeUtils = function(dom) {\r
11022                 var INVISIBLE_CHAR = '\uFEFF';\r
11023 \r
11024                 this.walk = function(rng, callback) {\r
11025                         var startContainer = rng.startContainer,\r
11026                                 startOffset = rng.startOffset,\r
11027                                 endContainer = rng.endContainer,\r
11028                                 endOffset = rng.endOffset,\r
11029                                 ancestor, startPoint,\r
11030                                 endPoint, node, parent, siblings, nodes;\r
11031 \r
11032                         // Handle table cell selection the table plugin enables\r
11033                         // you to fake select table cells and perform formatting actions on them\r
11034                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
11035                         if (nodes.length > 0) {\r
11036                                 tinymce.each(nodes, function(node) {\r
11037                                         callback([node]);\r
11038                                 });\r
11039 \r
11040                                 return;\r
11041                         }\r
11042 \r
11043                         function exclude(nodes) {\r
11044                                 var node;\r
11045 \r
11046                                 // First node is excluded\r
11047                                 node = nodes[0];\r
11048                                 if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {\r
11049                                         nodes.splice(0, 1);\r
11050                                 }\r
11051 \r
11052                                 // Last node is excluded\r
11053                                 node = nodes[nodes.length - 1];\r
11054                                 if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {\r
11055                                         nodes.splice(nodes.length - 1, 1);\r
11056                                 }\r
11057 \r
11058                                 return nodes;\r
11059                         };\r
11060 \r
11061                         function collectSiblings(node, name, end_node) {\r
11062                                 var siblings = [];\r
11063 \r
11064                                 for (; node && node != end_node; node = node[name])\r
11065                                         siblings.push(node);\r
11066 \r
11067                                 return siblings;\r
11068                         };\r
11069 \r
11070                         function findEndPoint(node, root) {\r
11071                                 do {\r
11072                                         if (node.parentNode == root)\r
11073                                                 return node;\r
11074 \r
11075                                         node = node.parentNode;\r
11076                                 } while(node);\r
11077                         };\r
11078 \r
11079                         function walkBoundary(start_node, end_node, next) {\r
11080                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
11081 \r
11082                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
11083                                         parent = node.parentNode;\r
11084                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
11085 \r
11086                                         if (siblings.length) {\r
11087                                                 if (!next)\r
11088                                                         siblings.reverse();\r
11089 \r
11090                                                 callback(exclude(siblings));\r
11091                                         }\r
11092                                 }\r
11093                         };\r
11094 \r
11095                         // If index based start position then resolve it\r
11096                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
11097                                 startContainer = startContainer.childNodes[startOffset];\r
11098 \r
11099                         // If index based end position then resolve it\r
11100                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
11101                                 endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];\r
11102 \r
11103                         // Same container\r
11104                         if (startContainer == endContainer)\r
11105                                 return callback(exclude([startContainer]));\r
11106 \r
11107                         // Find common ancestor and end points\r
11108                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
11109                                 \r
11110                         // Process left side\r
11111                         for (node = startContainer; node; node = node.parentNode) {\r
11112                                 if (node === endContainer)\r
11113                                         return walkBoundary(startContainer, ancestor, true);\r
11114 \r
11115                                 if (node === ancestor)\r
11116                                         break;\r
11117                         }\r
11118 \r
11119                         // Process right side\r
11120                         for (node = endContainer; node; node = node.parentNode) {\r
11121                                 if (node === startContainer)\r
11122                                         return walkBoundary(endContainer, ancestor);\r
11123 \r
11124                                 if (node === ancestor)\r
11125                                         break;\r
11126                         }\r
11127 \r
11128                         // Find start/end point\r
11129                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
11130                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
11131 \r
11132                         // Walk left leaf\r
11133                         walkBoundary(startContainer, startPoint, true);\r
11134 \r
11135                         // Walk the middle from start to end point\r
11136                         siblings = collectSiblings(\r
11137                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
11138                                 'nextSibling',\r
11139                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
11140                         );\r
11141 \r
11142                         if (siblings.length)\r
11143                                 callback(exclude(siblings));\r
11144 \r
11145                         // Walk right leaf\r
11146                         walkBoundary(endContainer, endPoint);\r
11147                 };\r
11148 \r
11149                 this.split = function(rng) {\r
11150                         var startContainer = rng.startContainer,\r
11151                                 startOffset = rng.startOffset,\r
11152                                 endContainer = rng.endContainer,\r
11153                                 endOffset = rng.endOffset;\r
11154 \r
11155                         function splitText(node, offset) {\r
11156                                 return node.splitText(offset);\r
11157                         };\r
11158 \r
11159                         // Handle single text node\r
11160                         if (startContainer == endContainer && startContainer.nodeType == 3) {\r
11161                                 if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
11162                                         endContainer = splitText(startContainer, startOffset);\r
11163                                         startContainer = endContainer.previousSibling;\r
11164 \r
11165                                         if (endOffset > startOffset) {\r
11166                                                 endOffset = endOffset - startOffset;\r
11167                                                 startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;\r
11168                                                 endOffset = endContainer.nodeValue.length;\r
11169                                                 startOffset = 0;\r
11170                                         } else {\r
11171                                                 endOffset = 0;\r
11172                                         }\r
11173                                 }\r
11174                         } else {\r
11175                                 // Split startContainer text node if needed\r
11176                                 if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
11177                                         startContainer = splitText(startContainer, startOffset);\r
11178                                         startOffset = 0;\r
11179                                 }\r
11180 \r
11181                                 // Split endContainer text node if needed\r
11182                                 if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {\r
11183                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
11184                                         endOffset = endContainer.nodeValue.length;\r
11185                                 }\r
11186                         }\r
11187 \r
11188                         return {\r
11189                                 startContainer : startContainer,\r
11190                                 startOffset : startOffset,\r
11191                                 endContainer : endContainer,\r
11192                                 endOffset : endOffset\r
11193                         };\r
11194                 };\r
11195 \r
11196         };\r
11197 \r
11198         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
11199                 if (rng1 && rng2) {\r
11200                         // Compare native IE ranges\r
11201                         if (rng1.item || rng1.duplicate) {\r
11202                                 // Both are control ranges and the selected element matches\r
11203                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
11204                                         return true;\r
11205 \r
11206                                 // Both are text ranges and the range matches\r
11207                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
11208                                         return true;\r
11209                         } else {\r
11210                                 // Compare w3c ranges\r
11211                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
11212                         }\r
11213                 }\r
11214 \r
11215                 return false;\r
11216         };\r
11217 })(tinymce);\r
11218 \r
11219 (function(tinymce) {\r
11220         var Event = tinymce.dom.Event, each = tinymce.each;\r
11221 \r
11222         tinymce.create('tinymce.ui.KeyboardNavigation', {\r
11223                 KeyboardNavigation: function(settings, dom) {\r
11224                         var t = this, root = settings.root, items = settings.items,\r
11225                                         enableUpDown = settings.enableUpDown, enableLeftRight = settings.enableLeftRight || !settings.enableUpDown,\r
11226                                         excludeFromTabOrder = settings.excludeFromTabOrder,\r
11227                                         itemFocussed, itemBlurred, rootKeydown, rootFocussed, focussedId;\r
11228 \r
11229                         dom = dom || tinymce.DOM;\r
11230 \r
11231                         itemFocussed = function(evt) {\r
11232                                 focussedId = evt.target.id;\r
11233                         };\r
11234                         \r
11235                         itemBlurred = function(evt) {\r
11236                                 dom.setAttrib(evt.target.id, 'tabindex', '-1');\r
11237                         };\r
11238                         \r
11239                         rootFocussed = function(evt) {\r
11240                                 var item = dom.get(focussedId);\r
11241                                 dom.setAttrib(item, 'tabindex', '0');\r
11242                                 item.focus();\r
11243                         };\r
11244                         \r
11245                         t.focus = function() {\r
11246                                 dom.get(focussedId).focus();\r
11247                         };\r
11248 \r
11249                         t.destroy = function() {\r
11250                                 each(items, function(item) {\r
11251                                         var elm = dom.get(item.id);\r
11252 \r
11253                                         dom.unbind(elm, 'focus', itemFocussed);\r
11254                                         dom.unbind(elm, 'blur', itemBlurred);\r
11255                                 });\r
11256 \r
11257                                 var rootElm = dom.get(root);\r
11258                                 dom.unbind(rootElm, 'focus', rootFocussed);\r
11259                                 dom.unbind(rootElm, 'keydown', rootKeydown);\r
11260 \r
11261                                 items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;\r
11262                                 t.destroy = function() {};\r
11263                         };\r
11264                         \r
11265                         t.moveFocus = function(dir, evt) {\r
11266                                 var idx = -1, controls = t.controls, newFocus;\r
11267 \r
11268                                 if (!focussedId)\r
11269                                         return;\r
11270 \r
11271                                 each(items, function(item, index) {\r
11272                                         if (item.id === focussedId) {\r
11273                                                 idx = index;\r
11274                                                 return false;\r
11275                                         }\r
11276                                 });\r
11277 \r
11278                                 idx += dir;\r
11279                                 if (idx < 0) {\r
11280                                         idx = items.length - 1;\r
11281                                 } else if (idx >= items.length) {\r
11282                                         idx = 0;\r
11283                                 }\r
11284                                 \r
11285                                 newFocus = items[idx];\r
11286                                 dom.setAttrib(focussedId, 'tabindex', '-1');\r
11287                                 dom.setAttrib(newFocus.id, 'tabindex', '0');\r
11288                                 dom.get(newFocus.id).focus();\r
11289 \r
11290                                 if (settings.actOnFocus) {\r
11291                                         settings.onAction(newFocus.id);\r
11292                                 }\r
11293 \r
11294                                 if (evt)\r
11295                                         Event.cancel(evt);\r
11296                         };\r
11297                         \r
11298                         rootKeydown = function(evt) {\r
11299                                 var DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40, DOM_VK_ESCAPE = 27, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32;\r
11300                                 \r
11301                                 switch (evt.keyCode) {\r
11302                                         case DOM_VK_LEFT:\r
11303                                                 if (enableLeftRight) t.moveFocus(-1);\r
11304                                                 Event.cancel(evt);\r
11305                                                 break;\r
11306         \r
11307                                         case DOM_VK_RIGHT:\r
11308                                                 if (enableLeftRight) t.moveFocus(1);\r
11309                                                 Event.cancel(evt);\r
11310                                                 break;\r
11311         \r
11312                                         case DOM_VK_UP:\r
11313                                                 if (enableUpDown) t.moveFocus(-1);\r
11314                                                 Event.cancel(evt);\r
11315                                                 break;\r
11316 \r
11317                                         case DOM_VK_DOWN:\r
11318                                                 if (enableUpDown) t.moveFocus(1);\r
11319                                                 Event.cancel(evt);\r
11320                                                 break;\r
11321 \r
11322                                         case DOM_VK_ESCAPE:\r
11323                                                 if (settings.onCancel) {\r
11324                                                         settings.onCancel();\r
11325                                                         Event.cancel(evt);\r
11326                                                 }\r
11327                                                 break;\r
11328 \r
11329                                         case DOM_VK_ENTER:\r
11330                                         case DOM_VK_RETURN:\r
11331                                         case DOM_VK_SPACE:\r
11332                                                 if (settings.onAction) {\r
11333                                                         settings.onAction(focussedId);\r
11334                                                         Event.cancel(evt);\r
11335                                                 }\r
11336                                                 break;\r
11337                                 }\r
11338                         };\r
11339 \r
11340                         // Set up state and listeners for each item.\r
11341                         each(items, function(item, idx) {\r
11342                                 var tabindex, elm;\r
11343 \r
11344                                 if (!item.id) {\r
11345                                         item.id = dom.uniqueId('_mce_item_');\r
11346                                 }\r
11347 \r
11348                                 elm = dom.get(item.id);\r
11349 \r
11350                                 if (excludeFromTabOrder) {\r
11351                                         dom.bind(elm, 'blur', itemBlurred);\r
11352                                         tabindex = '-1';\r
11353                                 } else {\r
11354                                         tabindex = (idx === 0 ? '0' : '-1');\r
11355                                 }\r
11356 \r
11357                                 elm.setAttribute('tabindex', tabindex);\r
11358                                 dom.bind(elm, 'focus', itemFocussed);\r
11359                         });\r
11360                         \r
11361                         // Setup initial state for root element.\r
11362                         if (items[0]){\r
11363                                 focussedId = items[0].id;\r
11364                         }\r
11365 \r
11366                         dom.setAttrib(root, 'tabindex', '-1');\r
11367 \r
11368                         // Setup listeners for root element.\r
11369                         var rootElm = dom.get(root);\r
11370                         dom.bind(rootElm, 'focus', rootFocussed);\r
11371                         dom.bind(rootElm, 'keydown', rootKeydown);\r
11372                 }\r
11373         });\r
11374 })(tinymce);\r
11375 \r
11376 (function(tinymce) {\r
11377         // Shorten class names\r
11378         var DOM = tinymce.DOM, is = tinymce.is;\r
11379 \r
11380         tinymce.create('tinymce.ui.Control', {\r
11381                 Control : function(id, s, editor) {\r
11382                         this.id = id;\r
11383                         this.settings = s = s || {};\r
11384                         this.rendered = false;\r
11385                         this.onRender = new tinymce.util.Dispatcher(this);\r
11386                         this.classPrefix = '';\r
11387                         this.scope = s.scope || this;\r
11388                         this.disabled = 0;\r
11389                         this.active = 0;\r
11390                         this.editor = editor;\r
11391                 },\r
11392                 \r
11393                 setAriaProperty : function(property, value) {\r
11394                         var element = DOM.get(this.id + '_aria') || DOM.get(this.id);\r
11395                         if (element) {\r
11396                                 DOM.setAttrib(element, 'aria-' + property, !!value);\r
11397                         }\r
11398                 },\r
11399                 \r
11400                 focus : function() {\r
11401                         DOM.get(this.id).focus();\r
11402                 },\r
11403 \r
11404                 setDisabled : function(s) {\r
11405                         if (s != this.disabled) {\r
11406                                 this.setAriaProperty('disabled', s);\r
11407 \r
11408                                 this.setState('Disabled', s);\r
11409                                 this.setState('Enabled', !s);\r
11410                                 this.disabled = s;\r
11411                         }\r
11412                 },\r
11413 \r
11414                 isDisabled : function() {\r
11415                         return this.disabled;\r
11416                 },\r
11417 \r
11418                 setActive : function(s) {\r
11419                         if (s != this.active) {\r
11420                                 this.setState('Active', s);\r
11421                                 this.active = s;\r
11422                                 this.setAriaProperty('pressed', s);\r
11423                         }\r
11424                 },\r
11425 \r
11426                 isActive : function() {\r
11427                         return this.active;\r
11428                 },\r
11429 \r
11430                 setState : function(c, s) {\r
11431                         var n = DOM.get(this.id);\r
11432 \r
11433                         c = this.classPrefix + c;\r
11434 \r
11435                         if (s)\r
11436                                 DOM.addClass(n, c);\r
11437                         else\r
11438                                 DOM.removeClass(n, c);\r
11439                 },\r
11440 \r
11441                 isRendered : function() {\r
11442                         return this.rendered;\r
11443                 },\r
11444 \r
11445                 renderHTML : function() {\r
11446                 },\r
11447 \r
11448                 renderTo : function(n) {\r
11449                         DOM.setHTML(n, this.renderHTML());\r
11450                 },\r
11451 \r
11452                 postRender : function() {\r
11453                         var t = this, b;\r
11454 \r
11455                         // Set pending states\r
11456                         if (is(t.disabled)) {\r
11457                                 b = t.disabled;\r
11458                                 t.disabled = -1;\r
11459                                 t.setDisabled(b);\r
11460                         }\r
11461 \r
11462                         if (is(t.active)) {\r
11463                                 b = t.active;\r
11464                                 t.active = -1;\r
11465                                 t.setActive(b);\r
11466                         }\r
11467                 },\r
11468 \r
11469                 remove : function() {\r
11470                         DOM.remove(this.id);\r
11471                         this.destroy();\r
11472                 },\r
11473 \r
11474                 destroy : function() {\r
11475                         tinymce.dom.Event.clear(this.id);\r
11476                 }\r
11477         });\r
11478 })(tinymce);\r
11479 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
11480         Container : function(id, s, editor) {\r
11481                 this.parent(id, s, editor);\r
11482 \r
11483                 this.controls = [];\r
11484 \r
11485                 this.lookup = {};\r
11486         },\r
11487 \r
11488         add : function(c) {\r
11489                 this.lookup[c.id] = c;\r
11490                 this.controls.push(c);\r
11491 \r
11492                 return c;\r
11493         },\r
11494 \r
11495         get : function(n) {\r
11496                 return this.lookup[n];\r
11497         }\r
11498 });\r
11499 \r
11500 \r
11501 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
11502         Separator : function(id, s) {\r
11503                 this.parent(id, s);\r
11504                 this.classPrefix = 'mceSeparator';\r
11505                 this.setDisabled(true);\r
11506         },\r
11507 \r
11508         renderHTML : function() {\r
11509                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix, role : 'separator', 'aria-orientation' : 'vertical', tabindex : '-1'});\r
11510         }\r
11511 });\r
11512 \r
11513 (function(tinymce) {\r
11514         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
11515 \r
11516         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
11517                 MenuItem : function(id, s) {\r
11518                         this.parent(id, s);\r
11519                         this.classPrefix = 'mceMenuItem';\r
11520                 },\r
11521 \r
11522                 setSelected : function(s) {\r
11523                         this.setState('Selected', s);\r
11524                         this.setAriaProperty('checked', !!s);\r
11525                         this.selected = s;\r
11526                 },\r
11527 \r
11528                 isSelected : function() {\r
11529                         return this.selected;\r
11530                 },\r
11531 \r
11532                 postRender : function() {\r
11533                         var t = this;\r
11534                         \r
11535                         t.parent();\r
11536 \r
11537                         // Set pending state\r
11538                         if (is(t.selected))\r
11539                                 t.setSelected(t.selected);\r
11540                 }\r
11541         });\r
11542 })(tinymce);\r
11543 \r
11544 (function(tinymce) {\r
11545         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
11546 \r
11547         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
11548                 Menu : function(id, s) {\r
11549                         var t = this;\r
11550 \r
11551                         t.parent(id, s);\r
11552                         t.items = {};\r
11553                         t.collapsed = false;\r
11554                         t.menuCount = 0;\r
11555                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
11556                 },\r
11557 \r
11558                 expand : function(d) {\r
11559                         var t = this;\r
11560 \r
11561                         if (d) {\r
11562                                 walk(t, function(o) {\r
11563                                         if (o.expand)\r
11564                                                 o.expand();\r
11565                                 }, 'items', t);\r
11566                         }\r
11567 \r
11568                         t.collapsed = false;\r
11569                 },\r
11570 \r
11571                 collapse : function(d) {\r
11572                         var t = this;\r
11573 \r
11574                         if (d) {\r
11575                                 walk(t, function(o) {\r
11576                                         if (o.collapse)\r
11577                                                 o.collapse();\r
11578                                 }, 'items', t);\r
11579                         }\r
11580 \r
11581                         t.collapsed = true;\r
11582                 },\r
11583 \r
11584                 isCollapsed : function() {\r
11585                         return this.collapsed;\r
11586                 },\r
11587 \r
11588                 add : function(o) {\r
11589                         if (!o.settings)\r
11590                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
11591 \r
11592                         this.onAddItem.dispatch(this, o);\r
11593 \r
11594                         return this.items[o.id] = o;\r
11595                 },\r
11596 \r
11597                 addSeparator : function() {\r
11598                         return this.add({separator : true});\r
11599                 },\r
11600 \r
11601                 addMenu : function(o) {\r
11602                         if (!o.collapse)\r
11603                                 o = this.createMenu(o);\r
11604 \r
11605                         this.menuCount++;\r
11606 \r
11607                         return this.add(o);\r
11608                 },\r
11609 \r
11610                 hasMenus : function() {\r
11611                         return this.menuCount !== 0;\r
11612                 },\r
11613 \r
11614                 remove : function(o) {\r
11615                         delete this.items[o.id];\r
11616                 },\r
11617 \r
11618                 removeAll : function() {\r
11619                         var t = this;\r
11620 \r
11621                         walk(t, function(o) {\r
11622                                 if (o.removeAll)\r
11623                                         o.removeAll();\r
11624                                 else\r
11625                                         o.remove();\r
11626 \r
11627                                 o.destroy();\r
11628                         }, 'items', t);\r
11629 \r
11630                         t.items = {};\r
11631                 },\r
11632 \r
11633                 createMenu : function(o) {\r
11634                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
11635 \r
11636                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
11637 \r
11638                         return m;\r
11639                 }\r
11640         });\r
11641 })(tinymce);\r
11642 (function(tinymce) {\r
11643         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
11644 \r
11645         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
11646                 DropMenu : function(id, s) {\r
11647                         s = s || {};\r
11648                         s.container = s.container || DOM.doc.body;\r
11649                         s.offset_x = s.offset_x || 0;\r
11650                         s.offset_y = s.offset_y || 0;\r
11651                         s.vp_offset_x = s.vp_offset_x || 0;\r
11652                         s.vp_offset_y = s.vp_offset_y || 0;\r
11653 \r
11654                         if (is(s.icons) && !s.icons)\r
11655                                 s['class'] += ' mceNoIcons';\r
11656 \r
11657                         this.parent(id, s);\r
11658                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
11659                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
11660                         this.classPrefix = 'mceMenu';\r
11661                 },\r
11662 \r
11663                 createMenu : function(s) {\r
11664                         var t = this, cs = t.settings, m;\r
11665 \r
11666                         s.container = s.container || cs.container;\r
11667                         s.parent = t;\r
11668                         s.constrain = s.constrain || cs.constrain;\r
11669                         s['class'] = s['class'] || cs['class'];\r
11670                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
11671                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
11672                         s.keyboard_focus = cs.keyboard_focus;\r
11673                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
11674 \r
11675                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
11676 \r
11677                         return m;\r
11678                 },\r
11679                 \r
11680                 focus : function() {\r
11681                         var t = this;\r
11682                         if (t.keyboardNav) {\r
11683                                 t.keyboardNav.focus();\r
11684                         }\r
11685                 },\r
11686 \r
11687                 update : function() {\r
11688                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
11689 \r
11690                         tw = s.max_width ? Math.min(tb.offsetWidth, s.max_width) : tb.offsetWidth;\r
11691                         th = s.max_height ? Math.min(tb.offsetHeight, s.max_height) : tb.offsetHeight;\r
11692 \r
11693                         if (!DOM.boxModel)\r
11694                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
11695                         else\r
11696                                 t.element.setStyles({width : tw, height : th});\r
11697 \r
11698                         if (s.max_width)\r
11699                                 DOM.setStyle(co, 'width', tw);\r
11700 \r
11701                         if (s.max_height) {\r
11702                                 DOM.setStyle(co, 'height', th);\r
11703 \r
11704                                 if (tb.clientHeight < s.max_height)\r
11705                                         DOM.setStyle(co, 'overflow', 'hidden');\r
11706                         }\r
11707                 },\r
11708 \r
11709                 showMenu : function(x, y, px) {\r
11710                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
11711 \r
11712                         t.collapse(1);\r
11713 \r
11714                         if (t.isMenuVisible)\r
11715                                 return;\r
11716 \r
11717                         if (!t.rendered) {\r
11718                                 co = DOM.add(t.settings.container, t.renderNode());\r
11719 \r
11720                                 each(t.items, function(o) {\r
11721                                         o.postRender();\r
11722                                 });\r
11723 \r
11724                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
11725                         } else\r
11726                                 co = DOM.get('menu_' + t.id);\r
11727 \r
11728                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
11729                         if (!tinymce.isOpera)\r
11730                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
11731 \r
11732                         DOM.show(co);\r
11733                         t.update();\r
11734 \r
11735                         x += s.offset_x || 0;\r
11736                         y += s.offset_y || 0;\r
11737                         vp.w -= 4;\r
11738                         vp.h -= 4;\r
11739 \r
11740                         // Move inside viewport if not submenu\r
11741                         if (s.constrain) {\r
11742                                 w = co.clientWidth - ot;\r
11743                                 h = co.clientHeight - ot;\r
11744                                 mx = vp.x + vp.w;\r
11745                                 my = vp.y + vp.h;\r
11746 \r
11747                                 if ((x + s.vp_offset_x + w) > mx)\r
11748                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
11749 \r
11750                                 if ((y + s.vp_offset_y + h) > my)\r
11751                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
11752                         }\r
11753 \r
11754                         DOM.setStyles(co, {left : x , top : y});\r
11755                         t.element.update();\r
11756 \r
11757                         t.isMenuVisible = 1;\r
11758                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
11759                                 var m;\r
11760 \r
11761                                 e = e.target;\r
11762 \r
11763                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
11764                                         m = t.items[e.id];\r
11765 \r
11766                                         if (m.isDisabled())\r
11767                                                 return;\r
11768 \r
11769                                         dm = t;\r
11770 \r
11771                                         while (dm) {\r
11772                                                 if (dm.hideMenu)\r
11773                                                         dm.hideMenu();\r
11774 \r
11775                                                 dm = dm.settings.parent;\r
11776                                         }\r
11777 \r
11778                                         if (m.settings.onclick)\r
11779                                                 m.settings.onclick(e);\r
11780 \r
11781                                         return false; // Cancel to fix onbeforeunload problem\r
11782                                 }\r
11783                         });\r
11784 \r
11785                         if (t.hasMenus()) {\r
11786                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
11787                                         var m, r, mi;\r
11788 \r
11789                                         e = e.target;\r
11790                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
11791                                                 m = t.items[e.id];\r
11792 \r
11793                                                 if (t.lastMenu)\r
11794                                                         t.lastMenu.collapse(1);\r
11795 \r
11796                                                 if (m.isDisabled())\r
11797                                                         return;\r
11798 \r
11799                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
11800                                                         //p = DOM.getPos(s.container);\r
11801                                                         r = DOM.getRect(e);\r
11802                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
11803                                                         t.lastMenu = m;\r
11804                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
11805                                                 }\r
11806                                         }\r
11807                                 });\r
11808                         }\r
11809                         \r
11810                         Event.add(co, 'keydown', t._keyHandler, t);\r
11811 \r
11812                         t.onShowMenu.dispatch(t);\r
11813 \r
11814                         if (s.keyboard_focus) { \r
11815                                 t._setupKeyboardNav(); \r
11816                         }\r
11817                 },\r
11818 \r
11819                 hideMenu : function(c) {\r
11820                         var t = this, co = DOM.get('menu_' + t.id), e;\r
11821 \r
11822                         if (!t.isMenuVisible)\r
11823                                 return;\r
11824 \r
11825                         if (t.keyboardNav) t.keyboardNav.destroy();\r
11826                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
11827                         Event.remove(co, 'click', t.mouseClickFunc);\r
11828                         Event.remove(co, 'keydown', t._keyHandler);\r
11829                         DOM.hide(co);\r
11830                         t.isMenuVisible = 0;\r
11831 \r
11832                         if (!c)\r
11833                                 t.collapse(1);\r
11834 \r
11835                         if (t.element)\r
11836                                 t.element.hide();\r
11837 \r
11838                         if (e = DOM.get(t.id))\r
11839                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
11840 \r
11841                         t.onHideMenu.dispatch(t);\r
11842                 },\r
11843 \r
11844                 add : function(o) {\r
11845                         var t = this, co;\r
11846 \r
11847                         o = t.parent(o);\r
11848 \r
11849                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
11850                                 t._add(DOM.select('tbody', co)[0], o);\r
11851 \r
11852                         return o;\r
11853                 },\r
11854 \r
11855                 collapse : function(d) {\r
11856                         this.parent(d);\r
11857                         this.hideMenu(1);\r
11858                 },\r
11859 \r
11860                 remove : function(o) {\r
11861                         DOM.remove(o.id);\r
11862                         this.destroy();\r
11863 \r
11864                         return this.parent(o);\r
11865                 },\r
11866 \r
11867                 destroy : function() {\r
11868                         var t = this, co = DOM.get('menu_' + t.id);\r
11869 \r
11870                         if (t.keyboardNav) t.keyboardNav.destroy();\r
11871                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
11872                         Event.remove(DOM.select('a', co), 'focus', t.mouseOverFunc);\r
11873                         Event.remove(co, 'click', t.mouseClickFunc);\r
11874                         Event.remove(co, 'keydown', t._keyHandler);\r
11875 \r
11876                         if (t.element)\r
11877                                 t.element.remove();\r
11878 \r
11879                         DOM.remove(co);\r
11880                 },\r
11881 \r
11882                 renderNode : function() {\r
11883                         var t = this, s = t.settings, n, tb, co, w;\r
11884 \r
11885                         w = DOM.create('div', {role: 'listbox', id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000;outline:0'});\r
11886                         if (t.settings.parent) {\r
11887                                 DOM.setAttrib(w, 'aria-parent', 'menu_' + t.settings.parent.id);\r
11888                         }\r
11889                         co = DOM.add(w, 'div', {role: 'presentation', id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
11890                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
11891 \r
11892                         if (s.menu_line)\r
11893                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
11894 \r
11895 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
11896                         n = DOM.add(co, 'table', {role: 'presentation', id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
11897                         tb = DOM.add(n, 'tbody');\r
11898 \r
11899                         each(t.items, function(o) {\r
11900                                 t._add(tb, o);\r
11901                         });\r
11902 \r
11903                         t.rendered = true;\r
11904 \r
11905                         return w;\r
11906                 },\r
11907 \r
11908                 // Internal functions\r
11909                 _setupKeyboardNav : function(){\r
11910                         var contextMenu, menuItems, t=this; \r
11911                         contextMenu = DOM.get('menu_' + t.id);\r
11912                         menuItems = DOM.select('a[role=option]', 'menu_' + t.id);\r
11913                         menuItems.splice(0,0,contextMenu);\r
11914                         t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
11915                                 root: 'menu_' + t.id,\r
11916                                 items: menuItems,\r
11917                                 onCancel: function() {\r
11918                                         t.hideMenu();\r
11919                                 },\r
11920                                 enableUpDown: true\r
11921                         });\r
11922                         contextMenu.focus();\r
11923                 },\r
11924 \r
11925                 _keyHandler : function(evt) {\r
11926                         var t = this, e;\r
11927                         switch (evt.keyCode) {\r
11928                                 case 37: // Left\r
11929                                         if (t.settings.parent) {\r
11930                                                 t.hideMenu();\r
11931                                                 t.settings.parent.focus();\r
11932                                                 Event.cancel(evt);\r
11933                                         }\r
11934                                         break;\r
11935                                 case 39: // Right\r
11936                                         if (t.mouseOverFunc)\r
11937                                                 t.mouseOverFunc(evt);\r
11938                                         break;\r
11939                         }\r
11940                 },\r
11941 \r
11942                 _add : function(tb, o) {\r
11943                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
11944 \r
11945                         if (s.separator) {\r
11946                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
11947                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
11948 \r
11949                                 if (n = ro.previousSibling)\r
11950                                         DOM.addClass(n, 'mceLast');\r
11951 \r
11952                                 return;\r
11953                         }\r
11954 \r
11955                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
11956                         n = it = DOM.add(n, s.titleItem ? 'th' : 'td');\r
11957                         n = a = DOM.add(n, 'a', {id: o.id + '_aria',  role: s.titleItem ? 'presentation' : 'option', href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
11958 \r
11959                         if (s.parent) {\r
11960                                 DOM.setAttrib(a, 'aria-haspopup', 'true');\r
11961                                 DOM.setAttrib(a, 'aria-owns', 'menu_' + o.id);\r
11962                         }\r
11963 \r
11964                         DOM.addClass(it, s['class']);\r
11965 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
11966 \r
11967                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
11968 \r
11969                         if (s.icon_src)\r
11970                                 DOM.add(ic, 'img', {src : s.icon_src});\r
11971 \r
11972                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
11973 \r
11974                         if (o.settings.style) {\r
11975                                 if (typeof o.settings.style == "function")\r
11976                                         o.settings.style = o.settings.style();\r
11977 \r
11978                                 DOM.setAttrib(n, 'style', o.settings.style);\r
11979                         }\r
11980 \r
11981                         if (tb.childNodes.length == 1)\r
11982                                 DOM.addClass(ro, 'mceFirst');\r
11983 \r
11984                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
11985                                 DOM.addClass(ro, 'mceFirst');\r
11986 \r
11987                         if (o.collapse)\r
11988                                 DOM.addClass(ro, cp + 'ItemSub');\r
11989 \r
11990                         if (n = ro.previousSibling)\r
11991                                 DOM.removeClass(n, 'mceLast');\r
11992 \r
11993                         DOM.addClass(ro, 'mceLast');\r
11994                 }\r
11995         });\r
11996 })(tinymce);\r
11997 (function(tinymce) {\r
11998         var DOM = tinymce.DOM;\r
11999 \r
12000         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
12001                 Button : function(id, s, ed) {\r
12002                         this.parent(id, s, ed);\r
12003                         this.classPrefix = 'mceButton';\r
12004                 },\r
12005 \r
12006                 renderHTML : function() {\r
12007                         var cp = this.classPrefix, s = this.settings, h, l;\r
12008 \r
12009                         l = DOM.encode(s.label || '');\r
12010                         h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';\r
12011                         if (s.image && !(this.editor  &&this.editor.forcedHighContrastMode) )\r
12012                                 h += '<span class="mceIcon ' + s['class'] + '"><img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" /></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');\r
12013                         else\r
12014                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');\r
12015 \r
12016                         h += '<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="' + this.id + '_voice">' + s.title + '</span>';\r
12017                         h += '</a>';\r
12018                         return h;\r
12019                 },\r
12020 \r
12021                 postRender : function() {\r
12022                         var t = this, s = t.settings, imgBookmark;\r
12023 \r
12024                         // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so\r
12025                         // need to keep the selection in case the selection is lost\r
12026                         if (tinymce.isIE && t.editor) {\r
12027                                 tinymce.dom.Event.add(t.id, 'mousedown', function(e) {\r
12028                                         var nodeName = t.editor.selection.getNode().nodeName;\r
12029                                         imgBookmark = nodeName === 'IMG' ? t.editor.selection.getBookmark() : null;\r
12030                                 });\r
12031                         }\r
12032                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
12033                                 if (!t.isDisabled()) {\r
12034                                         // restore the selection in case the selection is lost in IE\r
12035                                         if (tinymce.isIE && t.editor && imgBookmark !== null) {\r
12036                                                 t.editor.selection.moveToBookmark(imgBookmark);\r
12037                                         }\r
12038                                         return s.onclick.call(s.scope, e);\r
12039                                 }\r
12040                         });\r
12041                         tinymce.dom.Event.add(t.id, 'keydown', function(e) {\r
12042                                 if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR) {\r
12043                                         tinymce.dom.Event.cancel(e);\r
12044                                         return s.onclick.call(s.scope, e);\r
12045                                 }\r
12046                         });\r
12047                 }\r
12048         });\r
12049 })(tinymce);\r
12050 \r
12051 (function(tinymce) {\r
12052         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;\r
12053 \r
12054         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
12055                 ListBox : function(id, s, ed) {\r
12056                         var t = this;\r
12057 \r
12058                         t.parent(id, s, ed);\r
12059 \r
12060                         t.items = [];\r
12061 \r
12062                         t.onChange = new Dispatcher(t);\r
12063 \r
12064                         t.onPostRender = new Dispatcher(t);\r
12065 \r
12066                         t.onAdd = new Dispatcher(t);\r
12067 \r
12068                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
12069 \r
12070                         t.classPrefix = 'mceListBox';\r
12071                         t.marked = {};\r
12072                 },\r
12073 \r
12074                 select : function(va) {\r
12075                         var t = this, fv, f;\r
12076 \r
12077                         t.marked = {};\r
12078 \r
12079                         if (va == undef)\r
12080                                 return t.selectByIndex(-1);\r
12081 \r
12082                         // Is string or number make function selector\r
12083                         if (va && typeof(va)=="function")\r
12084                                 f = va;\r
12085                         else {\r
12086                                 f = function(v) {\r
12087                                         return v == va;\r
12088                                 };\r
12089                         }\r
12090 \r
12091                         // Do we need to do something?\r
12092                         if (va != t.selectedValue) {\r
12093                                 // Find item\r
12094                                 each(t.items, function(o, i) {\r
12095                                         if (f(o.value)) {\r
12096                                                 fv = 1;\r
12097                                                 t.selectByIndex(i);\r
12098                                                 return false;\r
12099                                         }\r
12100                                 });\r
12101 \r
12102                                 if (!fv)\r
12103                                         t.selectByIndex(-1);\r
12104                         }\r
12105                 },\r
12106 \r
12107                 selectByIndex : function(idx) {\r
12108                         var t = this, e, o, label;\r
12109 \r
12110                         t.marked = {};\r
12111 \r
12112                         if (idx != t.selectedIndex) {\r
12113                                 e = DOM.get(t.id + '_text');\r
12114                                 label = DOM.get(t.id + '_voiceDesc');\r
12115                                 o = t.items[idx];\r
12116 \r
12117                                 if (o) {\r
12118                                         t.selectedValue = o.value;\r
12119                                         t.selectedIndex = idx;\r
12120                                         DOM.setHTML(e, DOM.encode(o.title));\r
12121                                         DOM.setHTML(label, t.settings.title + " - " + o.title);\r
12122                                         DOM.removeClass(e, 'mceTitle');\r
12123                                         DOM.setAttrib(t.id, 'aria-valuenow', o.title);\r
12124                                 } else {\r
12125                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
12126                                         DOM.setHTML(label, DOM.encode(t.settings.title));\r
12127                                         DOM.addClass(e, 'mceTitle');\r
12128                                         t.selectedValue = t.selectedIndex = null;\r
12129                                         DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);\r
12130                                 }\r
12131                                 e = 0;\r
12132                         }\r
12133                 },\r
12134 \r
12135                 mark : function(value) {\r
12136                         this.marked[value] = true;\r
12137                 },\r
12138 \r
12139                 add : function(n, v, o) {\r
12140                         var t = this;\r
12141 \r
12142                         o = o || {};\r
12143                         o = tinymce.extend(o, {\r
12144                                 title : n,\r
12145                                 value : v\r
12146                         });\r
12147 \r
12148                         t.items.push(o);\r
12149                         t.onAdd.dispatch(t, o);\r
12150                 },\r
12151 \r
12152                 getLength : function() {\r
12153                         return this.items.length;\r
12154                 },\r
12155 \r
12156                 renderHTML : function() {\r
12157                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
12158 \r
12159                         h = '<span role="listbox" aria-haspopup="true" aria-labelledby="' + t.id +'_voiceDesc" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
12160                         h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); \r
12161                         h += DOM.createHTML('a', {id : t.id + '_text', tabindex : -1, href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';\r
12162                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>') + '</td>';\r
12163                         h += '</tr></tbody></table></span>';\r
12164 \r
12165                         return h;\r
12166                 },\r
12167 \r
12168                 showMenu : function() {\r
12169                         var t = this, p2, e = DOM.get(this.id), m;\r
12170 \r
12171                         if (t.isDisabled() || t.items.length === 0)\r
12172                                 return;\r
12173 \r
12174                         if (t.menu && t.menu.isMenuVisible)\r
12175                                 return t.hideMenu();\r
12176 \r
12177                         if (!t.isMenuRendered) {\r
12178                                 t.renderMenu();\r
12179                                 t.isMenuRendered = true;\r
12180                         }\r
12181 \r
12182                         p2 = DOM.getPos(e);\r
12183 \r
12184                         m = t.menu;\r
12185                         m.settings.offset_x = p2.x;\r
12186                         m.settings.offset_y = p2.y;\r
12187                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
12188 \r
12189                         // Select in menu\r
12190                         each(t.items, function(o) {\r
12191                                 if (m.items[o.id]) {\r
12192                                         m.items[o.id].setSelected(0);\r
12193                                 }\r
12194                         });\r
12195 \r
12196                         each(t.items, function(o) {\r
12197                                 if (m.items[o.id] && t.marked[o.value]) {\r
12198                                         m.items[o.id].setSelected(1);\r
12199                                 }\r
12200 \r
12201                                 if (o.value === t.selectedValue) {\r
12202                                         m.items[o.id].setSelected(1);\r
12203                                 }\r
12204                         });\r
12205 \r
12206                         m.showMenu(0, e.clientHeight);\r
12207 \r
12208                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
12209                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
12210 \r
12211                         //DOM.get(t.id + '_text').focus();\r
12212                 },\r
12213 \r
12214                 hideMenu : function(e) {\r
12215                         var t = this;\r
12216 \r
12217                         if (t.menu && t.menu.isMenuVisible) {\r
12218                                 DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
12219 \r
12220                                 // Prevent double toogles by canceling the mouse click event to the button\r
12221                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
12222                                         return;\r
12223 \r
12224                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
12225                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
12226                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
12227                                         t.menu.hideMenu();\r
12228                                 }\r
12229                         }\r
12230                 },\r
12231 \r
12232                 renderMenu : function() {\r
12233                         var t = this, m;\r
12234 \r
12235                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
12236                                 menu_line : 1,\r
12237                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
12238                                 max_width : 250,\r
12239                                 max_height : 150\r
12240                         });\r
12241 \r
12242                         m.onHideMenu.add(function() {\r
12243                                 t.hideMenu();\r
12244                                 t.focus();\r
12245                         });\r
12246 \r
12247                         m.add({\r
12248                                 title : t.settings.title,\r
12249                                 'class' : 'mceMenuItemTitle',\r
12250                                 onclick : function() {\r
12251                                         if (t.settings.onselect('') !== false)\r
12252                                                 t.select(''); // Must be runned after\r
12253                                 }\r
12254                         });\r
12255 \r
12256                         each(t.items, function(o) {\r
12257                                 // No value then treat it as a title\r
12258                                 if (o.value === undef) {\r
12259                                         m.add({\r
12260                                                 title : o.title,\r
12261                                                 role : "option",\r
12262                                                 'class' : 'mceMenuItemTitle',\r
12263                                                 onclick : function() {\r
12264                                                         if (t.settings.onselect('') !== false)\r
12265                                                                 t.select(''); // Must be runned after\r
12266                                                 }\r
12267                                         });\r
12268                                 } else {\r
12269                                         o.id = DOM.uniqueId();\r
12270                                         o.role= "option";\r
12271                                         o.onclick = function() {\r
12272                                                 if (t.settings.onselect(o.value) !== false)\r
12273                                                         t.select(o.value); // Must be runned after\r
12274                                         };\r
12275 \r
12276                                         m.add(o);\r
12277                                 }\r
12278                         });\r
12279 \r
12280                         t.onRenderMenu.dispatch(t, m);\r
12281                         t.menu = m;\r
12282                 },\r
12283 \r
12284                 postRender : function() {\r
12285                         var t = this, cp = t.classPrefix;\r
12286 \r
12287                         Event.add(t.id, 'click', t.showMenu, t);\r
12288                         Event.add(t.id, 'keydown', function(evt) {\r
12289                                 if (evt.keyCode == 32) { // Space\r
12290                                         t.showMenu(evt);\r
12291                                         Event.cancel(evt);\r
12292                                 }\r
12293                         });\r
12294                         Event.add(t.id, 'focus', function() {\r
12295                                 if (!t._focused) {\r
12296                                         t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {\r
12297                                                 if (e.keyCode == 40) {\r
12298                                                         t.showMenu();\r
12299                                                         Event.cancel(e);\r
12300                                                 }\r
12301                                         });\r
12302                                         t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {\r
12303                                                 var v;\r
12304                                                 if (e.keyCode == 13) {\r
12305                                                         // Fake select on enter\r
12306                                                         v = t.selectedValue;\r
12307                                                         t.selectedValue = null; // Needs to be null to fake change\r
12308                                                         Event.cancel(e);\r
12309                                                         t.settings.onselect(v);\r
12310                                                 }\r
12311                                         });\r
12312                                 }\r
12313 \r
12314                                 t._focused = 1;\r
12315                         });\r
12316                         Event.add(t.id, 'blur', function() {\r
12317                                 Event.remove(t.id, 'keydown', t.keyDownHandler);\r
12318                                 Event.remove(t.id, 'keypress', t.keyPressHandler);\r
12319                                 t._focused = 0;\r
12320                         });\r
12321 \r
12322                         // Old IE doesn't have hover on all elements\r
12323                         if (tinymce.isIE6 || !DOM.boxModel) {\r
12324                                 Event.add(t.id, 'mouseover', function() {\r
12325                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
12326                                                 DOM.addClass(t.id, cp + 'Hover');\r
12327                                 });\r
12328 \r
12329                                 Event.add(t.id, 'mouseout', function() {\r
12330                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
12331                                                 DOM.removeClass(t.id, cp + 'Hover');\r
12332                                 });\r
12333                         }\r
12334 \r
12335                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
12336                 },\r
12337 \r
12338                 destroy : function() {\r
12339                         this.parent();\r
12340 \r
12341                         Event.clear(this.id + '_text');\r
12342                         Event.clear(this.id + '_open');\r
12343                 }\r
12344         });\r
12345 })(tinymce);\r
12346 \r
12347 (function(tinymce) {\r
12348         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;\r
12349 \r
12350         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
12351                 NativeListBox : function(id, s) {\r
12352                         this.parent(id, s);\r
12353                         this.classPrefix = 'mceNativeListBox';\r
12354                 },\r
12355 \r
12356                 setDisabled : function(s) {\r
12357                         DOM.get(this.id).disabled = s;\r
12358                         this.setAriaProperty('disabled', s);\r
12359                 },\r
12360 \r
12361                 isDisabled : function() {\r
12362                         return DOM.get(this.id).disabled;\r
12363                 },\r
12364 \r
12365                 select : function(va) {\r
12366                         var t = this, fv, f;\r
12367 \r
12368                         if (va == undef)\r
12369                                 return t.selectByIndex(-1);\r
12370 \r
12371                         // Is string or number make function selector\r
12372                         if (va && typeof(va)=="function")\r
12373                                 f = va;\r
12374                         else {\r
12375                                 f = function(v) {\r
12376                                         return v == va;\r
12377                                 };\r
12378                         }\r
12379 \r
12380                         // Do we need to do something?\r
12381                         if (va != t.selectedValue) {\r
12382                                 // Find item\r
12383                                 each(t.items, function(o, i) {\r
12384                                         if (f(o.value)) {\r
12385                                                 fv = 1;\r
12386                                                 t.selectByIndex(i);\r
12387                                                 return false;\r
12388                                         }\r
12389                                 });\r
12390 \r
12391                                 if (!fv)\r
12392                                         t.selectByIndex(-1);\r
12393                         }\r
12394                 },\r
12395 \r
12396                 selectByIndex : function(idx) {\r
12397                         DOM.get(this.id).selectedIndex = idx + 1;\r
12398                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
12399                 },\r
12400 \r
12401                 add : function(n, v, a) {\r
12402                         var o, t = this;\r
12403 \r
12404                         a = a || {};\r
12405                         a.value = v;\r
12406 \r
12407                         if (t.isRendered())\r
12408                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
12409 \r
12410                         o = {\r
12411                                 title : n,\r
12412                                 value : v,\r
12413                                 attribs : a\r
12414                         };\r
12415 \r
12416                         t.items.push(o);\r
12417                         t.onAdd.dispatch(t, o);\r
12418                 },\r
12419 \r
12420                 getLength : function() {\r
12421                         return this.items.length;\r
12422                 },\r
12423 \r
12424                 renderHTML : function() {\r
12425                         var h, t = this;\r
12426 \r
12427                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
12428 \r
12429                         each(t.items, function(it) {\r
12430                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
12431                         });\r
12432 \r
12433                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox', 'aria-labelledby': t.id + '_aria'}, h);\r
12434                         h += DOM.createHTML('span', {id : t.id + '_aria', 'style': 'display: none'}, t.settings.title);\r
12435                         return h;\r
12436                 },\r
12437 \r
12438                 postRender : function() {\r
12439                         var t = this, ch, changeListenerAdded = true;\r
12440 \r
12441                         t.rendered = true;\r
12442 \r
12443                         function onChange(e) {\r
12444                                 var v = t.items[e.target.selectedIndex - 1];\r
12445 \r
12446                                 if (v && (v = v.value)) {\r
12447                                         t.onChange.dispatch(t, v);\r
12448 \r
12449                                         if (t.settings.onselect)\r
12450                                                 t.settings.onselect(v);\r
12451                                 }\r
12452                         };\r
12453 \r
12454                         Event.add(t.id, 'change', onChange);\r
12455 \r
12456                         // Accessibility keyhandler\r
12457                         Event.add(t.id, 'keydown', function(e) {\r
12458                                 var bf, DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32;\r
12459 \r
12460                                 Event.remove(t.id, 'change', ch);\r
12461                                 changeListenerAdded = false;\r
12462 \r
12463                                 bf = Event.add(t.id, 'blur', function() {\r
12464                                         if (changeListenerAdded) return;\r
12465                                         changeListenerAdded = true;\r
12466                                         Event.add(t.id, 'change', onChange);\r
12467                                         Event.remove(t.id, 'blur', bf);\r
12468                                 });\r
12469 \r
12470                                 if (e.keyCode == DOM_VK_RETURN || e.keyCode == DOM_VK_SPACE) {\r
12471                                         onChange(e);\r
12472                                         return Event.cancel(e);\r
12473                                 } else if (e.keyCode == DOM_VK_DOWN || e.keyCode == DOM_VK_UP) {\r
12474                                         // allow native implementation (navigate select element options)\r
12475                                         e.stopImmediatePropagation();\r
12476                                 }\r
12477                         });\r
12478 \r
12479                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
12480                 }\r
12481         });\r
12482 })(tinymce);\r
12483 \r
12484 (function(tinymce) {\r
12485         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
12486 \r
12487         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
12488                 MenuButton : function(id, s, ed) {\r
12489                         this.parent(id, s, ed);\r
12490 \r
12491                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
12492 \r
12493                         s.menu_container = s.menu_container || DOM.doc.body;\r
12494                 },\r
12495 \r
12496                 showMenu : function() {\r
12497                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
12498 \r
12499                         if (t.isDisabled())\r
12500                                 return;\r
12501 \r
12502                         if (!t.isMenuRendered) {\r
12503                                 t.renderMenu();\r
12504                                 t.isMenuRendered = true;\r
12505                         }\r
12506 \r
12507                         if (t.isMenuVisible)\r
12508                                 return t.hideMenu();\r
12509 \r
12510                         p1 = DOM.getPos(t.settings.menu_container);\r
12511                         p2 = DOM.getPos(e);\r
12512 \r
12513                         m = t.menu;\r
12514                         m.settings.offset_x = p2.x;\r
12515                         m.settings.offset_y = p2.y;\r
12516                         m.settings.vp_offset_x = p2.x;\r
12517                         m.settings.vp_offset_y = p2.y;\r
12518                         m.settings.keyboard_focus = t._focused;\r
12519                         m.showMenu(0, e.firstChild.clientHeight);\r
12520 \r
12521                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
12522                         t.setState('Selected', 1);\r
12523 \r
12524                         t.isMenuVisible = 1;\r
12525                 },\r
12526 \r
12527                 renderMenu : function() {\r
12528                         var t = this, m;\r
12529 \r
12530                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
12531                                 menu_line : 1,\r
12532                                 'class' : this.classPrefix + 'Menu',\r
12533                                 icons : t.settings.icons\r
12534                         });\r
12535 \r
12536                         m.onHideMenu.add(function() {\r
12537                                 t.hideMenu();\r
12538                                 t.focus();\r
12539                         });\r
12540 \r
12541                         t.onRenderMenu.dispatch(t, m);\r
12542                         t.menu = m;\r
12543                 },\r
12544 \r
12545                 hideMenu : function(e) {\r
12546                         var t = this;\r
12547 \r
12548                         // Prevent double toogles by canceling the mouse click event to the button\r
12549                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
12550                                 return;\r
12551 \r
12552                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
12553                                 t.setState('Selected', 0);\r
12554                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
12555                                 if (t.menu)\r
12556                                         t.menu.hideMenu();\r
12557                         }\r
12558 \r
12559                         t.isMenuVisible = 0;\r
12560                 },\r
12561 \r
12562                 postRender : function() {\r
12563                         var t = this, s = t.settings;\r
12564 \r
12565                         Event.add(t.id, 'click', function() {\r
12566                                 if (!t.isDisabled()) {\r
12567                                         if (s.onclick)\r
12568                                                 s.onclick(t.value);\r
12569 \r
12570                                         t.showMenu();\r
12571                                 }\r
12572                         });\r
12573                 }\r
12574         });\r
12575 })(tinymce);\r
12576 \r
12577 (function(tinymce) {\r
12578         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
12579 \r
12580         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
12581                 SplitButton : function(id, s, ed) {\r
12582                         this.parent(id, s, ed);\r
12583                         this.classPrefix = 'mceSplitButton';\r
12584                 },\r
12585 \r
12586                 renderHTML : function() {\r
12587                         var h, t = this, s = t.settings, h1;\r
12588 \r
12589                         h = '<tbody><tr>';\r
12590 \r
12591                         if (s.image)\r
12592                                 h1 = DOM.createHTML('img ', {src : s.image, role: 'presentation', 'class' : 'mceAction ' + s['class']});\r
12593                         else\r
12594                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
12595 \r
12596                         h1 += DOM.createHTML('span', {'class': 'mceVoiceLabel mceIconOnly', id: t.id + '_voice', style: 'display:none;'}, s.title);\r
12597                         h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_action', tabindex: '-1', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
12598         \r
12599                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}, '<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');\r
12600                         h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_open', tabindex: '-1', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
12601 \r
12602                         h += '</tr></tbody>';\r
12603                         h = DOM.createHTML('table', { role: 'presentation',   'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
12604                         return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
12605                 },\r
12606 \r
12607                 postRender : function() {\r
12608                         var t = this, s = t.settings, activate;\r
12609 \r
12610                         if (s.onclick) {\r
12611                                 activate = function(evt) {\r
12612                                         if (!t.isDisabled()) {\r
12613                                                 s.onclick(t.value);\r
12614                                                 Event.cancel(evt);\r
12615                                         }\r
12616                                 };\r
12617                                 Event.add(t.id + '_action', 'click', activate);\r
12618                                 Event.add(t.id, ['click', 'keydown'], function(evt) {\r
12619                                         var DOM_VK_SPACE = 32, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_UP = 38, DOM_VK_DOWN = 40;\r
12620                                         if ((evt.keyCode === 32 || evt.keyCode === 13 || evt.keyCode === 14) && !evt.altKey && !evt.ctrlKey && !evt.metaKey) {\r
12621                                                 activate();\r
12622                                                 Event.cancel(evt);\r
12623                                         } else if (evt.type === 'click' || evt.keyCode === DOM_VK_DOWN) {\r
12624                                                 t.showMenu();\r
12625                                                 Event.cancel(evt);\r
12626                                         }\r
12627                                 });\r
12628                         }\r
12629 \r
12630                         Event.add(t.id + '_open', 'click', function (evt) {\r
12631                                 t.showMenu();\r
12632                                 Event.cancel(evt);\r
12633                         });\r
12634                         Event.add([t.id, t.id + '_open'], 'focus', function() {t._focused = 1;});\r
12635                         Event.add([t.id, t.id + '_open'], 'blur', function() {t._focused = 0;});\r
12636 \r
12637                         // Old IE doesn't have hover on all elements\r
12638                         if (tinymce.isIE6 || !DOM.boxModel) {\r
12639                                 Event.add(t.id, 'mouseover', function() {\r
12640                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
12641                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
12642                                 });\r
12643 \r
12644                                 Event.add(t.id, 'mouseout', function() {\r
12645                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
12646                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
12647                                 });\r
12648                         }\r
12649                 },\r
12650 \r
12651                 destroy : function() {\r
12652                         this.parent();\r
12653 \r
12654                         Event.clear(this.id + '_action');\r
12655                         Event.clear(this.id + '_open');\r
12656                         Event.clear(this.id);\r
12657                 }\r
12658         });\r
12659 })(tinymce);\r
12660 \r
12661 (function(tinymce) {\r
12662         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
12663 \r
12664         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
12665                 ColorSplitButton : function(id, s, ed) {\r
12666                         var t = this;\r
12667 \r
12668                         t.parent(id, s, ed);\r
12669 \r
12670                         t.settings = s = tinymce.extend({\r
12671                                 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
12672                                 grid_width : 8,\r
12673                                 default_color : '#888888'\r
12674                         }, t.settings);\r
12675 \r
12676                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
12677 \r
12678                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
12679 \r
12680                         t.value = s.default_color;\r
12681                 },\r
12682 \r
12683                 showMenu : function() {\r
12684                         var t = this, r, p, e, p2;\r
12685 \r
12686                         if (t.isDisabled())\r
12687                                 return;\r
12688 \r
12689                         if (!t.isMenuRendered) {\r
12690                                 t.renderMenu();\r
12691                                 t.isMenuRendered = true;\r
12692                         }\r
12693 \r
12694                         if (t.isMenuVisible)\r
12695                                 return t.hideMenu();\r
12696 \r
12697                         e = DOM.get(t.id);\r
12698                         DOM.show(t.id + '_menu');\r
12699                         DOM.addClass(e, 'mceSplitButtonSelected');\r
12700                         p2 = DOM.getPos(e);\r
12701                         DOM.setStyles(t.id + '_menu', {\r
12702                                 left : p2.x,\r
12703                                 top : p2.y + e.firstChild.clientHeight,\r
12704                                 zIndex : 200000\r
12705                         });\r
12706                         e = 0;\r
12707 \r
12708                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
12709                         t.onShowMenu.dispatch(t);\r
12710 \r
12711                         if (t._focused) {\r
12712                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
12713                                         if (e.keyCode == 27)\r
12714                                                 t.hideMenu();\r
12715                                 });\r
12716 \r
12717                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
12718                         }\r
12719 \r
12720                         t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
12721                                 root: t.id + '_menu',\r
12722                                 items: DOM.select('a', t.id + '_menu'),\r
12723                                 onCancel: function() {\r
12724                                         t.hideMenu();\r
12725                                         t.focus();\r
12726                                 }\r
12727                         });\r
12728 \r
12729                         t.keyboardNav.focus();\r
12730                         t.isMenuVisible = 1;\r
12731                 },\r
12732 \r
12733                 hideMenu : function(e) {\r
12734                         var t = this;\r
12735 \r
12736                         if (t.isMenuVisible) {\r
12737                                 // Prevent double toogles by canceling the mouse click event to the button\r
12738                                 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
12739                                         return;\r
12740 \r
12741                                 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
12742                                         DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
12743                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
12744                                         Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
12745                                         DOM.hide(t.id + '_menu');\r
12746                                 }\r
12747 \r
12748                                 t.isMenuVisible = 0;\r
12749                                 t.onHideMenu.dispatch();\r
12750                                 t.keyboardNav.destroy();\r
12751                         }\r
12752                 },\r
12753 \r
12754                 renderMenu : function() {\r
12755                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;\r
12756 \r
12757                         w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s.menu_class + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});\r
12758                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
12759                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
12760 \r
12761                         n = DOM.add(m, 'table', {role: 'presentation', 'class' : 'mceColorSplitMenu'});\r
12762                         tb = DOM.add(n, 'tbody');\r
12763 \r
12764                         // Generate color grid\r
12765                         i = 0;\r
12766                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
12767                                 c = c.replace(/^#/, '');\r
12768 \r
12769                                 if (!i--) {\r
12770                                         tr = DOM.add(tb, 'tr');\r
12771                                         i = s.grid_width - 1;\r
12772                                 }\r
12773 \r
12774                                 n = DOM.add(tr, 'td');\r
12775                                 var settings = {\r
12776                                         href : 'javascript:;',\r
12777                                         style : {\r
12778                                                 backgroundColor : '#' + c\r
12779                                         },\r
12780                                         'title': t.editor.getLang('colors.' + c, c),\r
12781                                         'data-mce-color' : '#' + c\r
12782                                 };\r
12783 \r
12784                                 // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.\r
12785                                 if (!tinymce.isIE ) {\r
12786                                         settings.role = 'option';\r
12787                                 }\r
12788 \r
12789                                 n = DOM.add(n, 'a', settings);\r
12790 \r
12791                                 if (t.editor.forcedHighContrastMode) {\r
12792                                         n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });\r
12793                                         if (n.getContext && (context = n.getContext("2d"))) {\r
12794                                                 context.fillStyle = '#' + c;\r
12795                                                 context.fillRect(0, 0, 16, 16);\r
12796                                         } else {\r
12797                                                 // No point leaving a canvas element around if it's not supported for drawing on anyway.\r
12798                                                 DOM.remove(n);\r
12799                                         }\r
12800                                 }\r
12801                         });\r
12802 \r
12803                         if (s.more_colors_func) {\r
12804                                 n = DOM.add(tb, 'tr');\r
12805                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
12806                                 n = DOM.add(n, 'a', {role: 'option', id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
12807 \r
12808                                 Event.add(n, 'click', function(e) {\r
12809                                         s.more_colors_func.call(s.more_colors_scope || this);\r
12810                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
12811                                 });\r
12812                         }\r
12813 \r
12814                         DOM.addClass(m, 'mceColorSplitMenu');\r
12815 \r
12816                         // Prevent IE from scrolling and hindering click to occur #4019\r
12817                         Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});\r
12818 \r
12819                         Event.add(t.id + '_menu', 'click', function(e) {\r
12820                                 var c;\r
12821 \r
12822                                 e = DOM.getParent(e.target, 'a', tb);\r
12823 \r
12824                                 if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))\r
12825                                         t.setColor(c);\r
12826 \r
12827                                 return false; // Prevent IE auto save warning\r
12828                         });\r
12829 \r
12830                         return w;\r
12831                 },\r
12832 \r
12833                 setColor : function(c) {\r
12834                         this.displayColor(c);\r
12835                         this.hideMenu();\r
12836                         this.settings.onselect(c);\r
12837                 },\r
12838                 \r
12839                 displayColor : function(c) {\r
12840                         var t = this;\r
12841 \r
12842                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
12843 \r
12844                         t.value = c;\r
12845                 },\r
12846 \r
12847                 postRender : function() {\r
12848                         var t = this, id = t.id;\r
12849 \r
12850                         t.parent();\r
12851                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
12852                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
12853                 },\r
12854 \r
12855                 destroy : function() {\r
12856                         var self = this;\r
12857 \r
12858                         self.parent();\r
12859 \r
12860                         Event.clear(self.id + '_menu');\r
12861                         Event.clear(self.id + '_more');\r
12862                         DOM.remove(self.id + '_menu');\r
12863 \r
12864                         if (self.keyboardNav) {\r
12865                                 self.keyboardNav.destroy();\r
12866                         }\r
12867                 }\r
12868         });\r
12869 })(tinymce);\r
12870 \r
12871 (function(tinymce) {\r
12872 // Shorten class names\r
12873 var dom = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event;\r
12874 tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {\r
12875         renderHTML : function() {\r
12876                 var t = this, h = [], controls = t.controls, each = tinymce.each, settings = t.settings;\r
12877 \r
12878                 h.push('<div id="' + t.id + '" role="group" aria-labelledby="' + t.id + '_voice">');\r
12879                 //TODO: ACC test this out - adding a role = application for getting the landmarks working well.\r
12880                 h.push("<span role='application'>");\r
12881                 h.push('<span id="' + t.id + '_voice" class="mceVoiceLabel" style="display:none;">' + dom.encode(settings.name) + '</span>');\r
12882                 each(controls, function(toolbar) {\r
12883                         h.push(toolbar.renderHTML());\r
12884                 });\r
12885                 h.push("</span>");\r
12886                 h.push('</div>');\r
12887 \r
12888                 return h.join('');\r
12889         },\r
12890         \r
12891         focus : function() {\r
12892                 var t = this;\r
12893                 dom.get(t.id).focus();\r
12894         },\r
12895         \r
12896         postRender : function() {\r
12897                 var t = this, items = [];\r
12898 \r
12899                 each(t.controls, function(toolbar) {\r
12900                         each (toolbar.controls, function(control) {\r
12901                                 if (control.id) {\r
12902                                         items.push(control);\r
12903                                 }\r
12904                         });\r
12905                 });\r
12906 \r
12907                 t.keyNav = new tinymce.ui.KeyboardNavigation({\r
12908                         root: t.id,\r
12909                         items: items,\r
12910                         onCancel: function() {\r
12911                                 //Move focus if webkit so that navigation back will read the item.\r
12912                                 if (tinymce.isWebKit) {\r
12913                                         dom.get(t.editor.id+"_ifr").focus();\r
12914                                 }\r
12915                                 t.editor.focus();\r
12916                         },\r
12917                         excludeFromTabOrder: !t.settings.tab_focus_toolbar\r
12918                 });\r
12919         },\r
12920         \r
12921         destroy : function() {\r
12922                 var self = this;\r
12923 \r
12924                 self.parent();\r
12925                 self.keyNav.destroy();\r
12926                 Event.clear(self.id);\r
12927         }\r
12928 });\r
12929 })(tinymce);\r
12930 \r
12931 (function(tinymce) {\r
12932 // Shorten class names\r
12933 var dom = tinymce.DOM, each = tinymce.each;\r
12934 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
12935         renderHTML : function() {\r
12936                 var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;\r
12937 \r
12938                 cl = t.controls;\r
12939                 for (i=0; i<cl.length; i++) {\r
12940                         // Get current control, prev control, next control and if the control is a list box or not\r
12941                         co = cl[i];\r
12942                         pr = cl[i - 1];\r
12943                         nx = cl[i + 1];\r
12944 \r
12945                         // Add toolbar start\r
12946                         if (i === 0) {\r
12947                                 c = 'mceToolbarStart';\r
12948 \r
12949                                 if (co.Button)\r
12950                                         c += ' mceToolbarStartButton';\r
12951                                 else if (co.SplitButton)\r
12952                                         c += ' mceToolbarStartSplitButton';\r
12953                                 else if (co.ListBox)\r
12954                                         c += ' mceToolbarStartListBox';\r
12955 \r
12956                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
12957                         }\r
12958 \r
12959                         // Add toolbar end before list box and after the previous button\r
12960                         // This is to fix the o2k7 editor skins\r
12961                         if (pr && co.ListBox) {\r
12962                                 if (pr.Button || pr.SplitButton)\r
12963                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
12964                         }\r
12965 \r
12966                         // Render control HTML\r
12967 \r
12968                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
12969                         if (dom.stdMode)\r
12970                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
12971                         else\r
12972                                 h += '<td>' + co.renderHTML() + '</td>';\r
12973 \r
12974                         // Add toolbar start after list box and before the next button\r
12975                         // This is to fix the o2k7 editor skins\r
12976                         if (nx && co.ListBox) {\r
12977                                 if (nx.Button || nx.SplitButton)\r
12978                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
12979                         }\r
12980                 }\r
12981 \r
12982                 c = 'mceToolbarEnd';\r
12983 \r
12984                 if (co.Button)\r
12985                         c += ' mceToolbarEndButton';\r
12986                 else if (co.SplitButton)\r
12987                         c += ' mceToolbarEndSplitButton';\r
12988                 else if (co.ListBox)\r
12989                         c += ' mceToolbarEndListBox';\r
12990 \r
12991                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
12992 \r
12993                 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || '', role: 'presentation', tabindex: '-1'}, '<tbody><tr>' + h + '</tr></tbody>');\r
12994         }\r
12995 });\r
12996 })(tinymce);\r
12997 \r
12998 (function(tinymce) {\r
12999         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
13000 \r
13001         tinymce.create('tinymce.AddOnManager', {\r
13002                 AddOnManager : function() {\r
13003                         var self = this;\r
13004 \r
13005                         self.items = [];\r
13006                         self.urls = {};\r
13007                         self.lookup = {};\r
13008                         self.onAdd = new Dispatcher(self);\r
13009                 },\r
13010 \r
13011                 get : function(n) {\r
13012                         if (this.lookup[n]) {\r
13013                                 return this.lookup[n].instance;\r
13014                         } else {\r
13015                                 return undefined;\r
13016                         }\r
13017                 },\r
13018 \r
13019                 dependencies : function(n) {\r
13020                         var result;\r
13021                         if (this.lookup[n]) {\r
13022                                 result = this.lookup[n].dependencies;\r
13023                         }\r
13024                         return result || [];\r
13025                 },\r
13026 \r
13027                 requireLangPack : function(n) {\r
13028                         var s = tinymce.settings;\r
13029 \r
13030                         if (s && s.language && s.language_load !== false)\r
13031                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
13032                 },\r
13033 \r
13034                 add : function(id, o, dependencies) {\r
13035                         this.items.push(o);\r
13036                         this.lookup[id] = {instance:o, dependencies:dependencies};\r
13037                         this.onAdd.dispatch(this, id, o);\r
13038 \r
13039                         return o;\r
13040                 },\r
13041                 createUrl: function(baseUrl, dep) {\r
13042                         if (typeof dep === "object") {\r
13043                                 return dep\r
13044                         } else {\r
13045                                 return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};\r
13046                         }\r
13047                 },\r
13048 \r
13049                 addComponents: function(pluginName, scripts) {\r
13050                         var pluginUrl = this.urls[pluginName];\r
13051                         tinymce.each(scripts, function(script){\r
13052                                 tinymce.ScriptLoader.add(pluginUrl+"/"+script); \r
13053                         });\r
13054                 },\r
13055 \r
13056                 load : function(n, u, cb, s) {\r
13057                         var t = this, url = u;\r
13058 \r
13059                         function loadDependencies() {\r
13060                                 var dependencies = t.dependencies(n);\r
13061                                 tinymce.each(dependencies, function(dep) {\r
13062                                         var newUrl = t.createUrl(u, dep);\r
13063                                         t.load(newUrl.resource, newUrl, undefined, undefined);\r
13064                                 });\r
13065                                 if (cb) {\r
13066                                         if (s) {\r
13067                                                 cb.call(s);\r
13068                                         } else {\r
13069                                                 cb.call(tinymce.ScriptLoader);\r
13070                                         }\r
13071                                 }\r
13072                         }\r
13073 \r
13074                         if (t.urls[n])\r
13075                                 return;\r
13076                         if (typeof u === "object")\r
13077                                 url = u.prefix + u.resource + u.suffix;\r
13078 \r
13079                         if (url.indexOf('/') !== 0 && url.indexOf('://') == -1)\r
13080                                 url = tinymce.baseURL + '/' + url;\r
13081 \r
13082                         t.urls[n] = url.substring(0, url.lastIndexOf('/'));\r
13083 \r
13084                         if (t.lookup[n]) {\r
13085                                 loadDependencies();\r
13086                         } else {\r
13087                                 tinymce.ScriptLoader.add(url, loadDependencies, s);\r
13088                         }\r
13089                 }\r
13090         });\r
13091 \r
13092         // Create plugin and theme managers\r
13093         tinymce.PluginManager = new tinymce.AddOnManager();\r
13094         tinymce.ThemeManager = new tinymce.AddOnManager();\r
13095 }(tinymce));\r
13096 \r
13097 (function(tinymce) {\r
13098         // Shorten names\r
13099         var each = tinymce.each, extend = tinymce.extend,\r
13100                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
13101                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
13102                 explode = tinymce.explode,\r
13103                 Dispatcher = tinymce.util.Dispatcher, undef, instanceCounter = 0;\r
13104 \r
13105         // Setup some URLs where the editor API is located and where the document is\r
13106         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
13107         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
13108                 tinymce.documentBaseURL += '/';\r
13109 \r
13110         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
13111 \r
13112         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
13113 \r
13114         // Add before unload listener\r
13115         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
13116         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
13117         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
13118 \r
13119         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
13120         Event.add(window, 'beforeunload', function(e) {\r
13121                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
13122         });\r
13123 \r
13124         tinymce.onAddEditor = new Dispatcher(tinymce);\r
13125 \r
13126         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
13127 \r
13128         tinymce.EditorManager = extend(tinymce, {\r
13129                 editors : [],\r
13130 \r
13131                 i18n : {},\r
13132 \r
13133                 activeEditor : null,\r
13134 \r
13135                 init : function(s) {\r
13136                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
13137 \r
13138                         function createId(elm) {\r
13139                                 var id = elm.id;\r
13140         \r
13141                                 // Use element id, or unique name or generate a unique id\r
13142                                 if (!id) {\r
13143                                         id = elm.name;\r
13144         \r
13145                                         if (id && !DOM.get(id)) {\r
13146                                                 id = elm.name;\r
13147                                         } else {\r
13148                                                 // Generate unique name\r
13149                                                 id = DOM.uniqueId();\r
13150                                         }\r
13151 \r
13152                                         elm.setAttribute('id', id);\r
13153                                 }\r
13154 \r
13155                                 return id;\r
13156                         };\r
13157 \r
13158                         function execCallback(se, n, s) {\r
13159                                 var f = se[n];\r
13160 \r
13161                                 if (!f)\r
13162                                         return;\r
13163 \r
13164                                 if (tinymce.is(f, 'string')) {\r
13165                                         s = f.replace(/\.\w+$/, '');\r
13166                                         s = s ? tinymce.resolve(s) : 0;\r
13167                                         f = tinymce.resolve(f);\r
13168                                 }\r
13169 \r
13170                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
13171                         };\r
13172 \r
13173                         function hasClass(n, c) {\r
13174                                 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
13175                         };\r
13176 \r
13177                         t.settings = s;\r
13178 \r
13179                         // Legacy call\r
13180                         Event.bind(window, 'ready', function() {\r
13181                                 var l, co;\r
13182 \r
13183                                 execCallback(s, 'onpageload');\r
13184 \r
13185                                 switch (s.mode) {\r
13186                                         case "exact":\r
13187                                                 l = s.elements || '';\r
13188 \r
13189                                                 if(l.length > 0) {\r
13190                                                         each(explode(l), function(v) {\r
13191                                                                 if (DOM.get(v)) {\r
13192                                                                         ed = new tinymce.Editor(v, s);\r
13193                                                                         el.push(ed);\r
13194                                                                         ed.render(1);\r
13195                                                                 } else {\r
13196                                                                         each(document.forms, function(f) {\r
13197                                                                                 each(f.elements, function(e) {\r
13198                                                                                         if (e.name === v) {\r
13199                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
13200                                                                                                 DOM.setAttrib(e, 'id', v);\r
13201 \r
13202                                                                                                 ed = new tinymce.Editor(v, s);\r
13203                                                                                                 el.push(ed);\r
13204                                                                                                 ed.render(1);\r
13205                                                                                         }\r
13206                                                                                 });\r
13207                                                                         });\r
13208                                                                 }\r
13209                                                         });\r
13210                                                 }\r
13211                                                 break;\r
13212 \r
13213                                         case "textareas":\r
13214                                         case "specific_textareas":\r
13215                                                 each(DOM.select('textarea'), function(elm) {\r
13216                                                         if (s.editor_deselector && hasClass(elm, s.editor_deselector))\r
13217                                                                 return;\r
13218 \r
13219                                                         if (!s.editor_selector || hasClass(elm, s.editor_selector)) {\r
13220                                                                 ed = new tinymce.Editor(createId(elm), s);\r
13221                                                                 el.push(ed);\r
13222                                                                 ed.render(1);\r
13223                                                         }\r
13224                                                 });\r
13225                                                 break;\r
13226                                         \r
13227                                         default:\r
13228                                                 if (s.types) {\r
13229                                                         // Process type specific selector\r
13230                                                         each(s.types, function(type) {\r
13231                                                                 each(DOM.select(type.selector), function(elm) {\r
13232                                                                         var editor = new tinymce.Editor(createId(elm), tinymce.extend({}, s, type));\r
13233                                                                         el.push(editor);\r
13234                                                                         editor.render(1);\r
13235                                                                 });\r
13236                                                         });\r
13237                                                 } else if (s.selector) {\r
13238                                                         // Process global selector\r
13239                                                         each(DOM.select(s.selector), function(elm) {\r
13240                                                                 var editor = new tinymce.Editor(createId(elm), s);\r
13241                                                                 el.push(editor);\r
13242                                                                 editor.render(1);\r
13243                                                         });\r
13244                                                 }\r
13245                                 }\r
13246 \r
13247                                 // Call onInit when all editors are initialized\r
13248                                 if (s.oninit) {\r
13249                                         l = co = 0;\r
13250 \r
13251                                         each(el, function(ed) {\r
13252                                                 co++;\r
13253 \r
13254                                                 if (!ed.initialized) {\r
13255                                                         // Wait for it\r
13256                                                         ed.onInit.add(function() {\r
13257                                                                 l++;\r
13258 \r
13259                                                                 // All done\r
13260                                                                 if (l == co)\r
13261                                                                         execCallback(s, 'oninit');\r
13262                                                         });\r
13263                                                 } else\r
13264                                                         l++;\r
13265 \r
13266                                                 // All done\r
13267                                                 if (l == co)\r
13268                                                         execCallback(s, 'oninit');                                      \r
13269                                         });\r
13270                                 }\r
13271                         });\r
13272                 },\r
13273 \r
13274                 get : function(id) {\r
13275                         if (id === undef)\r
13276                                 return this.editors;\r
13277 \r
13278                         if (!this.editors.hasOwnProperty(id))\r
13279                                 return undef;\r
13280 \r
13281                         return this.editors[id];\r
13282                 },\r
13283 \r
13284                 getInstanceById : function(id) {\r
13285                         return this.get(id);\r
13286                 },\r
13287 \r
13288                 add : function(editor) {\r
13289                         var self = this, editors = self.editors;\r
13290 \r
13291                         // Add named and index editor instance\r
13292                         editors[editor.id] = editor;\r
13293                         editors.push(editor);\r
13294 \r
13295                         self._setActive(editor);\r
13296                         self.onAddEditor.dispatch(self, editor);\r
13297 \r
13298 \r
13299                         return editor;\r
13300                 },\r
13301 \r
13302                 remove : function(editor) {\r
13303                         var t = this, i, editors = t.editors;\r
13304 \r
13305                         // Not in the collection\r
13306                         if (!editors[editor.id])\r
13307                                 return null;\r
13308 \r
13309                         delete editors[editor.id];\r
13310 \r
13311                         for (i = 0; i < editors.length; i++) {\r
13312                                 if (editors[i] == editor) {\r
13313                                         editors.splice(i, 1);\r
13314                                         break;\r
13315                                 }\r
13316                         }\r
13317 \r
13318                         // Select another editor since the active one was removed\r
13319                         if (t.activeEditor == editor)\r
13320                                 t._setActive(editors[0]);\r
13321 \r
13322                         editor.destroy();\r
13323                         t.onRemoveEditor.dispatch(t, editor);\r
13324 \r
13325                         return editor;\r
13326                 },\r
13327 \r
13328                 execCommand : function(c, u, v) {\r
13329                         var t = this, ed = t.get(v), w;\r
13330 \r
13331                         function clr() {\r
13332                                 ed.destroy();\r
13333                                 w.detachEvent('onunload', clr);\r
13334                                 w = w.tinyMCE = w.tinymce = null; // IE leak\r
13335                         };\r
13336 \r
13337                         // Manager commands\r
13338                         switch (c) {\r
13339                                 case "mceFocus":\r
13340                                         ed.focus();\r
13341                                         return true;\r
13342 \r
13343                                 case "mceAddEditor":\r
13344                                 case "mceAddControl":\r
13345                                         if (!t.get(v))\r
13346                                                 new tinymce.Editor(v, t.settings).render();\r
13347 \r
13348                                         return true;\r
13349 \r
13350                                 case "mceAddFrameControl":\r
13351                                         w = v.window;\r
13352 \r
13353                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
13354                                         w.tinyMCE = tinyMCE;\r
13355                                         w.tinymce = tinymce;\r
13356 \r
13357                                         tinymce.DOM.doc = w.document;\r
13358                                         tinymce.DOM.win = w;\r
13359 \r
13360                                         ed = new tinymce.Editor(v.element_id, v);\r
13361                                         ed.render();\r
13362 \r
13363                                         // Fix IE memory leaks\r
13364                                         if (tinymce.isIE && ! tinymce.isIE11) {\r
13365                                                 w.attachEvent('onunload', clr);\r
13366                                         }\r
13367 \r
13368                                         v.page_window = null;\r
13369 \r
13370                                         return true;\r
13371 \r
13372                                 case "mceRemoveEditor":\r
13373                                 case "mceRemoveControl":\r
13374                                         if (ed)\r
13375                                                 ed.remove();\r
13376 \r
13377                                         return true;\r
13378 \r
13379                                 case 'mceToggleEditor':\r
13380                                         if (!ed) {\r
13381                                                 t.execCommand('mceAddControl', 0, v);\r
13382                                                 return true;\r
13383                                         }\r
13384 \r
13385                                         if (ed.isHidden())\r
13386                                                 ed.show();\r
13387                                         else\r
13388                                                 ed.hide();\r
13389 \r
13390                                         return true;\r
13391                         }\r
13392 \r
13393                         // Run command on active editor\r
13394                         if (t.activeEditor)\r
13395                                 return t.activeEditor.execCommand(c, u, v);\r
13396 \r
13397                         return false;\r
13398                 },\r
13399 \r
13400                 execInstanceCommand : function(id, c, u, v) {\r
13401                         var ed = this.get(id);\r
13402 \r
13403                         if (ed)\r
13404                                 return ed.execCommand(c, u, v);\r
13405 \r
13406                         return false;\r
13407                 },\r
13408 \r
13409                 triggerSave : function() {\r
13410                         each(this.editors, function(e) {\r
13411                                 e.save();\r
13412                         });\r
13413                 },\r
13414 \r
13415                 addI18n : function(p, o) {\r
13416                         var lo, i18n = this.i18n;\r
13417 \r
13418                         if (!tinymce.is(p, 'string')) {\r
13419                                 each(p, function(o, lc) {\r
13420                                         each(o, function(o, g) {\r
13421                                                 each(o, function(o, k) {\r
13422                                                         if (g === 'common')\r
13423                                                                 i18n[lc + '.' + k] = o;\r
13424                                                         else\r
13425                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
13426                                                 });\r
13427                                         });\r
13428                                 });\r
13429                         } else {\r
13430                                 each(o, function(o, k) {\r
13431                                         i18n[p + '.' + k] = o;\r
13432                                 });\r
13433                         }\r
13434                 },\r
13435 \r
13436                 // Private methods\r
13437 \r
13438                 _setActive : function(editor) {\r
13439                         this.selectedInstance = this.activeEditor = editor;\r
13440                 }\r
13441         });\r
13442 })(tinymce);\r
13443 \r
13444 (function(tinymce) {\r
13445         // Shorten these names\r
13446         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
13447                 each = tinymce.each, isGecko = tinymce.isGecko,\r
13448                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
13449                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
13450                 explode = tinymce.explode;\r
13451 \r
13452         tinymce.create('tinymce.Editor', {\r
13453                 Editor : function(id, settings) {\r
13454                         var self = this, TRUE = true;\r
13455 \r
13456                         self.settings = settings = extend({\r
13457                                 id : id,\r
13458                                 language : 'en',\r
13459                                 theme : 'advanced',\r
13460                                 skin : 'default',\r
13461                                 delta_width : 0,\r
13462                                 delta_height : 0,\r
13463                                 popup_css : '',\r
13464                                 plugins : '',\r
13465                                 document_base_url : tinymce.documentBaseURL,\r
13466                                 add_form_submit_trigger : TRUE,\r
13467                                 submit_patch : TRUE,\r
13468                                 add_unload_trigger : TRUE,\r
13469                                 convert_urls : TRUE,\r
13470                                 relative_urls : TRUE,\r
13471                                 remove_script_host : TRUE,\r
13472                                 table_inline_editing : false,\r
13473                                 object_resizing : TRUE,\r
13474                                 accessibility_focus : TRUE,\r
13475                                 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
13476                                 visual : TRUE,\r
13477                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
13478                                 font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size\r
13479                                 apply_source_formatting : TRUE,\r
13480                                 directionality : 'ltr',\r
13481                                 forced_root_block : 'p',\r
13482                                 hidden_input : TRUE,\r
13483                                 padd_empty_editor : TRUE,\r
13484                                 render_ui : TRUE,\r
13485                                 indentation : '30px',\r
13486                                 fix_table_elements : TRUE,\r
13487                                 inline_styles : TRUE,\r
13488                                 convert_fonts_to_spans : TRUE,\r
13489                                 indent : 'simple',\r
13490                                 indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',\r
13491                                 indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',\r
13492                                 validate : TRUE,\r
13493                                 entity_encoding : 'named',\r
13494                                 url_converter : self.convertURL,\r
13495                                 url_converter_scope : self,\r
13496                                 ie7_compat : TRUE\r
13497                         }, settings);\r
13498 \r
13499                         self.id = self.editorId = id;\r
13500 \r
13501                         self.isNotDirty = false;\r
13502 \r
13503                         self.plugins = {};\r
13504 \r
13505                         self.documentBaseURI = new tinymce.util.URI(settings.document_base_url || tinymce.documentBaseURL, {\r
13506                                 base_uri : tinyMCE.baseURI\r
13507                         });\r
13508 \r
13509                         self.baseURI = tinymce.baseURI;\r
13510 \r
13511                         self.contentCSS = [];\r
13512 \r
13513                         self.contentStyles = [];\r
13514 \r
13515                         // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic\r
13516                         self.setupEvents();\r
13517 \r
13518                         // Internal command handler objects\r
13519                         self.execCommands = {};\r
13520                         self.queryStateCommands = {};\r
13521                         self.queryValueCommands = {};\r
13522 \r
13523                         // Call setup\r
13524                         self.execCallback('setup', self);\r
13525                 },\r
13526 \r
13527                 render : function(nst) {\r
13528                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
13529 \r
13530                         // Page is not loaded yet, wait for it\r
13531                         if (!Event.domLoaded) {\r
13532                                 Event.add(window, 'ready', function() {\r
13533                                         t.render();\r
13534                                 });\r
13535                                 return;\r
13536                         }\r
13537 \r
13538                         tinyMCE.settings = s;\r
13539 \r
13540                         // Element not found, then skip initialization\r
13541                         if (!t.getElement())\r
13542                                 return;\r
13543 \r
13544                         // Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff \r
13545                         // here since the browser says it has contentEditable support but there is no visible caret.\r
13546                         if (tinymce.isIDevice && !tinymce.isIOS5)\r
13547                                 return;\r
13548 \r
13549                         // Add hidden input for non input elements inside form elements\r
13550                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
13551                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
13552 \r
13553                         // Hide target element early to prevent content flashing\r
13554                         if (!s.content_editable) {\r
13555                                 t.orgVisibility = t.getElement().style.visibility;\r
13556                                 t.getElement().style.visibility = 'hidden';\r
13557                         }\r
13558 \r
13559                         if (tinymce.WindowManager)\r
13560                                 t.windowManager = new tinymce.WindowManager(t);\r
13561 \r
13562                         if (s.encoding == 'xml') {\r
13563                                 t.onGetContent.add(function(ed, o) {\r
13564                                         if (o.save)\r
13565                                                 o.content = DOM.encode(o.content);\r
13566                                 });\r
13567                         }\r
13568 \r
13569                         if (s.add_form_submit_trigger) {\r
13570                                 t.onSubmit.addToTop(function() {\r
13571                                         if (t.initialized) {\r
13572                                                 t.save();\r
13573                                                 t.isNotDirty = 1;\r
13574                                         }\r
13575                                 });\r
13576                         }\r
13577 \r
13578                         if (s.add_unload_trigger) {\r
13579                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
13580                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
13581                                                 t.save({format : 'raw', no_events : true});\r
13582                                 });\r
13583                         }\r
13584 \r
13585                         tinymce.addUnload(t.destroy, t);\r
13586 \r
13587                         if (s.submit_patch) {\r
13588                                 t.onBeforeRenderUI.add(function() {\r
13589                                         var n = t.getElement().form;\r
13590 \r
13591                                         if (!n)\r
13592                                                 return;\r
13593 \r
13594                                         // Already patched\r
13595                                         if (n._mceOldSubmit)\r
13596                                                 return;\r
13597 \r
13598                                         // Check page uses id="submit" or name="submit" for it's submit button\r
13599                                         if (!n.submit.nodeType && !n.submit.length) {\r
13600                                                 t.formElement = n;\r
13601                                                 n._mceOldSubmit = n.submit;\r
13602                                                 n.submit = function() {\r
13603                                                         // Save all instances\r
13604                                                         tinymce.triggerSave();\r
13605                                                         t.isNotDirty = 1;\r
13606 \r
13607                                                         return t.formElement._mceOldSubmit(t.formElement);\r
13608                                                 };\r
13609                                         }\r
13610 \r
13611                                         n = null;\r
13612                                 });\r
13613                         }\r
13614 \r
13615                         // Load scripts\r
13616                         function loadScripts() {\r
13617                                 if (s.language && s.language_load !== false)\r
13618                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
13619 \r
13620                                 if (s.theme && typeof s.theme != "function" && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
13621                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
13622 \r
13623                                 each(explode(s.plugins), function(p) {\r
13624                                         if (p &&!PluginManager.urls[p]) {\r
13625                                                 if (p.charAt(0) == '-') {\r
13626                                                         p = p.substr(1, p.length);\r
13627                                                         var dependencies = PluginManager.dependencies(p);\r
13628                                                         each(dependencies, function(dep) {\r
13629                                                                 var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};\r
13630                                                                 dep = PluginManager.createUrl(defaultSettings, dep);\r
13631                                                                 PluginManager.load(dep.resource, dep);\r
13632                                                         });\r
13633                                                 } else {\r
13634                                                         // Skip safari plugin, since it is removed as of 3.3b1\r
13635                                                         if (p == 'safari') {\r
13636                                                                 return;\r
13637                                                         }\r
13638                                                         PluginManager.load(p, {prefix:'plugins/', resource: p, suffix:'/editor_plugin' + tinymce.suffix + '.js'});\r
13639                                                 }\r
13640                                         }\r
13641                                 });\r
13642 \r
13643                                 // Init when que is loaded\r
13644                                 sl.loadQueue(function() {\r
13645                                         if (!t.removed)\r
13646                                                 t.init();\r
13647                                 });\r
13648                         };\r
13649 \r
13650                         loadScripts();\r
13651                 },\r
13652 \r
13653                 init : function() {\r
13654                         var n, t = this, s = t.settings, w, h, mh, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];\r
13655 \r
13656                         tinymce.add(t);\r
13657 \r
13658                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));\r
13659 \r
13660                         if (s.theme) {\r
13661                                 if (typeof s.theme != "function") {\r
13662                                         s.theme = s.theme.replace(/-/, '');\r
13663                                         o = ThemeManager.get(s.theme);\r
13664                                         t.theme = new o();\r
13665 \r
13666                                         if (t.theme.init)\r
13667                                                 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
13668                                 } else {\r
13669                                         t.theme = s.theme;\r
13670                                 }\r
13671                         }\r
13672 \r
13673                         function initPlugin(p) {\r
13674                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
13675                                 if (c && tinymce.inArray(initializedPlugins,p) === -1) {\r
13676                                         each(PluginManager.dependencies(p), function(dep){\r
13677                                                 initPlugin(dep);\r
13678                                         });\r
13679                                         po = new c(t, u);\r
13680 \r
13681                                         t.plugins[p] = po;\r
13682 \r
13683                                         if (po.init) {\r
13684                                                 po.init(t, u);\r
13685                                                 initializedPlugins.push(p);\r
13686                                         }\r
13687                                 }\r
13688                         }\r
13689                         \r
13690                         // Create all plugins\r
13691                         each(explode(s.plugins.replace(/\-/g, '')), initPlugin);\r
13692 \r
13693                         // Setup popup CSS path(s)\r
13694                         if (s.popup_css !== false) {\r
13695                                 if (s.popup_css)\r
13696                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
13697                                 else\r
13698                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
13699                         }\r
13700 \r
13701                         if (s.popup_css_add)\r
13702                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
13703 \r
13704                         t.controlManager = new tinymce.ControlManager(t);\r
13705 \r
13706                         // Enables users to override the control factory\r
13707                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
13708 \r
13709                         // Measure box\r
13710                         if (s.render_ui && t.theme) {\r
13711                                 t.orgDisplay = e.style.display;\r
13712 \r
13713                                 if (typeof s.theme != "function") {\r
13714                                         w = s.width || e.style.width || e.offsetWidth;\r
13715                                         h = s.height || e.style.height || e.offsetHeight;\r
13716                                         mh = s.min_height || 100;\r
13717                                         re = /^[0-9\.]+(|px)$/i;\r
13718 \r
13719                                         if (re.test('' + w))\r
13720                                                 w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);\r
13721 \r
13722                                         if (re.test('' + h))\r
13723                                                 h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), mh);\r
13724 \r
13725                                         // Render UI\r
13726                                         o = t.theme.renderUI({\r
13727                                                 targetNode : e,\r
13728                                                 width : w,\r
13729                                                 height : h,\r
13730                                                 deltaWidth : s.delta_width,\r
13731                                                 deltaHeight : s.delta_height\r
13732                                         });\r
13733 \r
13734                                         // Resize editor\r
13735                                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
13736                                                 width : w,\r
13737                                                 height : h\r
13738                                         });\r
13739 \r
13740                                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
13741                                         if (h < mh)\r
13742                                                 h = mh;\r
13743                                 } else {\r
13744                                         o = s.theme(t, e);\r
13745 \r
13746                                         // Convert element type to id:s\r
13747                                         if (o.editorContainer.nodeType) {\r
13748                                                 o.editorContainer = o.editorContainer.id = o.editorContainer.id || t.id + "_parent";\r
13749                                         }\r
13750 \r
13751                                         // Convert element type to id:s\r
13752                                         if (o.iframeContainer.nodeType) {\r
13753                                                 o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || t.id + "_iframecontainer";\r
13754                                         }\r
13755 \r
13756                                         // Use specified iframe height or the targets offsetHeight\r
13757                                         h = o.iframeHeight || e.offsetHeight;\r
13758 \r
13759                                         // Store away the selection when it's changed to it can be restored later with a editor.focus() call\r
13760                                         if (isIE) {\r
13761                                                 t.onInit.add(function(ed) {\r
13762                                                         ed.dom.bind(ed.getBody(), 'beforedeactivate keydown keyup', function() {\r
13763                                                                 ed.bookmark = ed.selection.getBookmark(1);\r
13764                                                         });\r
13765                                                 });\r
13766 \r
13767                                                 t.onNodeChange.add(function(ed) {\r
13768                                                         if (document.activeElement.id == ed.id + "_ifr") {\r
13769                                                                 ed.bookmark = ed.selection.getBookmark(1);\r
13770                                                         }\r
13771                                                 });\r
13772                                         }\r
13773                                 }\r
13774 \r
13775                                 t.editorContainer = o.editorContainer;\r
13776                         }\r
13777 \r
13778                         // Load specified content CSS last\r
13779                         if (s.content_css) {\r
13780                                 each(explode(s.content_css), function(u) {\r
13781                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));\r
13782                                 });\r
13783                         }\r
13784 \r
13785                         // Load specified content CSS last\r
13786                         if (s.content_style) {\r
13787                                 t.contentStyles.push(s.content_style);\r
13788                         }\r
13789 \r
13790                         // Content editable mode ends here\r
13791                         if (s.content_editable) {\r
13792                                 e = n = o = null; // Fix IE leak\r
13793                                 return t.initContentBody();\r
13794                         }\r
13795 \r
13796                         // User specified a document.domain value\r
13797                         if (document.domain && location.hostname != document.domain)\r
13798                                 tinymce.relaxedDomain = document.domain;\r
13799 \r
13800                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
13801 \r
13802                         // We only need to override paths if we have to\r
13803                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
13804                         if (s.document_base_url != tinymce.documentBaseURL)\r
13805                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
13806 \r
13807                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.\r
13808                         if (tinymce.isIE8) {\r
13809                                 if (s.ie7_compat)\r
13810                                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';\r
13811                                 else\r
13812                                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';\r
13813                         }\r
13814 \r
13815                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
13816 \r
13817                         // Load the CSS by injecting them into the HTML this will reduce "flicker"\r
13818                         for (i = 0; i < t.contentCSS.length; i++) {\r
13819                                 t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';\r
13820                         }\r
13821 \r
13822                         t.contentCSS = [];\r
13823 \r
13824                         bi = s.body_id || 'tinymce';\r
13825                         if (bi.indexOf('=') != -1) {\r
13826                                 bi = t.getParam('body_id', '', 'hash');\r
13827                                 bi = bi[t.id] || bi;\r
13828                         }\r
13829 \r
13830                         bc = s.body_class || '';\r
13831                         if (bc.indexOf('=') != -1) {\r
13832                                 bc = t.getParam('body_class', '', 'hash');\r
13833                                 bc = bc[t.id] || '';\r
13834                         }\r
13835 \r
13836                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';\r
13837 \r
13838                         // Domain relaxing enabled, then set document domain\r
13839                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {\r
13840                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
13841                                 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()';\r
13842                         }\r
13843 \r
13844                         // Create iframe\r
13845                         // TODO: ACC add the appropriate description on this.\r
13846                         n = DOM.add(o.iframeContainer, 'iframe', { \r
13847                                 id : t.id + "_ifr",\r
13848                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
13849                                 frameBorder : '0',\r
13850                                 allowTransparency : "true",\r
13851                                 title : s.aria_label,\r
13852                                 style : {\r
13853                                         width : '100%',\r
13854                                         height : h,\r
13855                                         display : 'block' // Important for Gecko to render the iframe correctly\r
13856                                 }\r
13857                         });\r
13858 \r
13859                         t.contentAreaContainer = o.iframeContainer;\r
13860 \r
13861                         if (o.editorContainer) {\r
13862                                 DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
13863                         }\r
13864 \r
13865                         // Restore visibility on target element\r
13866                         e.style.visibility = t.orgVisibility;\r
13867 \r
13868                         DOM.get(t.id).style.display = 'none';\r
13869                         DOM.setAttrib(t.id, 'aria-hidden', true);\r
13870 \r
13871                         if (!tinymce.relaxedDomain || !u)\r
13872                                 t.initContentBody();\r
13873 \r
13874                         e = n = o = null; // Cleanup\r
13875                 },\r
13876 \r
13877                 initContentBody : function() {\r
13878                         var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body, contentCssText;\r
13879 \r
13880                         // Setup iframe body\r
13881                         if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {\r
13882                                 doc.open();\r
13883                                 doc.write(self.iframeHTML);\r
13884                                 doc.close();\r
13885 \r
13886                                 if (tinymce.relaxedDomain)\r
13887                                         doc.domain = tinymce.relaxedDomain;\r
13888                         }\r
13889 \r
13890                         if (settings.content_editable) {\r
13891                                 DOM.addClass(targetElm, 'mceContentBody');\r
13892                                 self.contentDocument = doc = settings.content_document || document;\r
13893                                 self.contentWindow = settings.content_window || window;\r
13894                                 self.bodyElement = targetElm;\r
13895 \r
13896                                 // Prevent leak in IE\r
13897                                 settings.content_document = settings.content_window = null;\r
13898                         }\r
13899 \r
13900                         // It will not steal focus while setting contentEditable\r
13901                         body = self.getBody();\r
13902                         body.disabled = true;\r
13903 \r
13904                         if (!settings.readonly)\r
13905                                 body.contentEditable = self.getParam('content_editable_state', true);\r
13906 \r
13907                         body.disabled = false;\r
13908 \r
13909                         self.schema = new tinymce.html.Schema(settings);\r
13910 \r
13911                         self.dom = new tinymce.dom.DOMUtils(doc, {\r
13912                                 keep_values : true,\r
13913                                 url_converter : self.convertURL,\r
13914                                 url_converter_scope : self,\r
13915                                 hex_colors : settings.force_hex_style_colors,\r
13916                                 class_filter : settings.class_filter,\r
13917                                 update_styles : true,\r
13918                                 root_element : settings.content_editable ? self.id : null,\r
13919                                 schema : self.schema\r
13920                         });\r
13921 \r
13922                         self.parser = new tinymce.html.DomParser(settings, self.schema);\r
13923 \r
13924                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style\r
13925                         self.parser.addAttributeFilter('src,href,style', function(nodes, name) {\r
13926                                 var i = nodes.length, node, dom = self.dom, value, internalName;\r
13927 \r
13928                                 while (i--) {\r
13929                                         node = nodes[i];\r
13930                                         value = node.attr(name);\r
13931                                         internalName = 'data-mce-' + name;\r
13932 \r
13933                                         // Add internal attribute if we need to we don't on a refresh of the document\r
13934                                         if (!node.attributes.map[internalName]) {       \r
13935                                                 if (name === "style")\r
13936                                                         node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));\r
13937                                                 else\r
13938                                                         node.attr(internalName, self.convertURL(value, name, node.name));\r
13939                                         }\r
13940                                 }\r
13941                         });\r
13942 \r
13943                         // Keep scripts from executing\r
13944                         self.parser.addNodeFilter('script', function(nodes, name) {\r
13945                                 var i = nodes.length, node;\r
13946 \r
13947                                 while (i--) {\r
13948                                         node = nodes[i];\r
13949                                         node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));\r
13950                                 }\r
13951                         });\r
13952 \r
13953                         self.parser.addNodeFilter('#cdata', function(nodes, name) {\r
13954                                 var i = nodes.length, node;\r
13955 \r
13956                                 while (i--) {\r
13957                                         node = nodes[i];\r
13958                                         node.type = 8;\r
13959                                         node.name = '#comment';\r
13960                                         node.value = '[CDATA[' + node.value + ']]';\r
13961                                 }\r
13962                         });\r
13963 \r
13964                         self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {\r
13965                                 var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();\r
13966 \r
13967                                 while (i--) {\r
13968                                         node = nodes[i];\r
13969 \r
13970                                         if (node.isEmpty(nonEmptyElements))\r
13971                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;\r
13972                                 }\r
13973                         });\r
13974 \r
13975                         self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);\r
13976 \r
13977                         self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer, self);\r
13978 \r
13979                         self.formatter = new tinymce.Formatter(self);\r
13980 \r
13981                         self.undoManager = new tinymce.UndoManager(self);\r
13982 \r
13983                         self.forceBlocks = new tinymce.ForceBlocks(self);\r
13984                         self.enterKey = new tinymce.EnterKey(self);\r
13985                         self.editorCommands = new tinymce.EditorCommands(self);\r
13986 \r
13987                         self.onExecCommand.add(function(editor, command) {\r
13988                                 // Don't refresh the select lists until caret move\r
13989                                 if (!/^(FontName|FontSize)$/.test(command))\r
13990                                         self.nodeChanged();\r
13991                         });\r
13992 \r
13993                         // Pass through\r
13994                         self.serializer.onPreProcess.add(function(se, o) {\r
13995                                 return self.onPreProcess.dispatch(self, o, se);\r
13996                         });\r
13997 \r
13998                         self.serializer.onPostProcess.add(function(se, o) {\r
13999                                 return self.onPostProcess.dispatch(self, o, se);\r
14000                         });\r
14001 \r
14002                         self.onPreInit.dispatch(self);\r
14003 \r
14004                         if (!settings.browser_spellcheck && !settings.gecko_spellcheck)\r
14005                                 doc.body.spellcheck = false;\r
14006 \r
14007                         if (!settings.readonly) {\r
14008                                 self.bindNativeEvents();\r
14009                         }\r
14010 \r
14011                         self.controlManager.onPostRender.dispatch(self, self.controlManager);\r
14012                         self.onPostRender.dispatch(self);\r
14013 \r
14014                         self.quirks = tinymce.util.Quirks(self);\r
14015 \r
14016                         if (settings.directionality)\r
14017                                 body.dir = settings.directionality;\r
14018 \r
14019                         if (settings.nowrap)\r
14020                                 body.style.whiteSpace = "nowrap";\r
14021 \r
14022                         if (settings.protect) {\r
14023                                 self.onBeforeSetContent.add(function(ed, o) {\r
14024                                         each(settings.protect, function(pattern) {\r
14025                                                 o.content = o.content.replace(pattern, function(str) {\r
14026                                                         return '<!--mce:protected ' + escape(str) + '-->';\r
14027                                                 });\r
14028                                         });\r
14029                                 });\r
14030                         }\r
14031 \r
14032                         // Add visual aids when new contents is added\r
14033                         self.onSetContent.add(function() {\r
14034                                 self.addVisual(self.getBody());\r
14035                         });\r
14036 \r
14037                         // Remove empty contents\r
14038                         if (settings.padd_empty_editor) {\r
14039                                 self.onPostProcess.add(function(ed, o) {\r
14040                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
14041                                 });\r
14042                         }\r
14043 \r
14044                         self.load({initial : true, format : 'html'});\r
14045                         self.startContent = self.getContent({format : 'raw'});\r
14046 \r
14047                         self.initialized = true;\r
14048 \r
14049                         self.onInit.dispatch(self);\r
14050                         self.execCallback('setupcontent_callback', self.id, body, doc);\r
14051                         self.execCallback('init_instance_callback', self);\r
14052                         self.focus(true);\r
14053                         self.nodeChanged({initial : true});\r
14054 \r
14055                         // Add editor specific CSS styles\r
14056                         if (self.contentStyles.length > 0) {\r
14057                                 contentCssText = '';\r
14058 \r
14059                                 each(self.contentStyles, function(style) {\r
14060                                         contentCssText += style + "\r\n";\r
14061                                 });\r
14062 \r
14063                                 self.dom.addStyle(contentCssText);\r
14064                         }\r
14065 \r
14066                         // Load specified content CSS last\r
14067                         each(self.contentCSS, function(url) {\r
14068                                 self.dom.loadCSS(url);\r
14069                         });\r
14070 \r
14071                         // Handle auto focus\r
14072                         if (settings.auto_focus) {\r
14073                                 setTimeout(function () {\r
14074                                         var ed = tinymce.get(settings.auto_focus);\r
14075 \r
14076                                         ed.selection.select(ed.getBody(), 1);\r
14077                                         ed.selection.collapse(1);\r
14078                                         ed.getBody().focus();\r
14079                                         ed.getWin().focus();\r
14080                                 }, 100);\r
14081                         }\r
14082 \r
14083                         // Clean up references for IE\r
14084                         targetElm = doc = body = null;\r
14085                 },\r
14086 \r
14087                 focus : function(skip_focus) {\r
14088                         var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;\r
14089 \r
14090                         if (!skip_focus) {\r
14091                                 if (self.bookmark) {\r
14092                                         selection.moveToBookmark(self.bookmark);\r
14093                                         self.bookmark = null;\r
14094                                 }\r
14095 \r
14096                                 // Get selected control element\r
14097                                 ieRng = selection.getRng();\r
14098                                 if (ieRng.item) {\r
14099                                         controlElm = ieRng.item(0);\r
14100                                 }\r
14101 \r
14102                                 self._refreshContentEditable();\r
14103 \r
14104                                 // Focus the window iframe\r
14105                                 if (!contentEditable) {\r
14106                                         self.getWin().focus();\r
14107                                 }\r
14108 \r
14109                                 // Focus the body as well since it's contentEditable\r
14110                                 if (tinymce.isGecko || contentEditable) {\r
14111                                         body = self.getBody();\r
14112 \r
14113                                         // Check for setActive since it doesn't scroll to the element\r
14114                                         if (body.setActive && ! tinymce.isIE11) {\r
14115                                                 body.setActive();\r
14116                                         } else {\r
14117                                                 body.focus();\r
14118                                         }\r
14119 \r
14120                                         if (contentEditable) {\r
14121                                                 selection.normalize();\r
14122                                         }\r
14123                                 }\r
14124 \r
14125                                 // Restore selected control element\r
14126                                 // This is needed when for example an image is selected within a\r
14127                                 // layer a call to focus will then remove the control selection\r
14128                                 if (controlElm && controlElm.ownerDocument == doc) {\r
14129                                         ieRng = doc.body.createControlRange();\r
14130                                         ieRng.addElement(controlElm);\r
14131                                         ieRng.select();\r
14132                                 }\r
14133                         }\r
14134 \r
14135                         if (tinymce.activeEditor != self) {\r
14136                                 if ((oed = tinymce.activeEditor) != null)\r
14137                                         oed.onDeactivate.dispatch(oed, self);\r
14138 \r
14139                                 self.onActivate.dispatch(self, oed);\r
14140                         }\r
14141 \r
14142                         tinymce._setActive(self);\r
14143                 },\r
14144 \r
14145                 execCallback : function(n) {\r
14146                         var t = this, f = t.settings[n], s;\r
14147 \r
14148                         if (!f)\r
14149                                 return;\r
14150 \r
14151                         // Look through lookup\r
14152                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
14153                                 f = s.func;\r
14154                                 s = s.scope;\r
14155                         }\r
14156 \r
14157                         if (is(f, 'string')) {\r
14158                                 s = f.replace(/\.\w+$/, '');\r
14159                                 s = s ? tinymce.resolve(s) : 0;\r
14160                                 f = tinymce.resolve(f);\r
14161                                 t.callbackLookup = t.callbackLookup || {};\r
14162                                 t.callbackLookup[n] = {func : f, scope : s};\r
14163                         }\r
14164 \r
14165                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
14166                 },\r
14167 \r
14168                 translate : function(s) {\r
14169                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
14170 \r
14171                         if (!s)\r
14172                                 return '';\r
14173 \r
14174                         return i18n[c + '.' + s] || s.replace(/\{\#([^\}]+)\}/g, function(a, b) {\r
14175                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
14176                         });\r
14177                 },\r
14178 \r
14179                 getLang : function(n, dv) {\r
14180                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
14181                 },\r
14182 \r
14183                 getParam : function(n, dv, ty) {\r
14184                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
14185 \r
14186                         if (ty === 'hash') {\r
14187                                 o = {};\r
14188 \r
14189                                 if (is(v, 'string')) {\r
14190                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
14191                                                 v = v.split('=');\r
14192 \r
14193                                                 if (v.length > 1)\r
14194                                                         o[tr(v[0])] = tr(v[1]);\r
14195                                                 else\r
14196                                                         o[tr(v[0])] = tr(v);\r
14197                                         });\r
14198                                 } else\r
14199                                         o = v;\r
14200 \r
14201                                 return o;\r
14202                         }\r
14203 \r
14204                         return v;\r
14205                 },\r
14206 \r
14207                 nodeChanged : function(o) {\r
14208                         var self = this, selection = self.selection, node;\r
14209 \r
14210                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
14211                         if (self.initialized) {\r
14212                                 o = o || {};\r
14213 \r
14214                                 // Get start node\r
14215                                 node = selection.getStart() || self.getBody();\r
14216                                 node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state\r
14217 \r
14218                                 // Get parents and add them to object\r
14219                                 o.parents = [];\r
14220                                 self.dom.getParent(node, function(node) {\r
14221                                         if (node.nodeName == 'BODY')\r
14222                                                 return true;\r
14223 \r
14224                                         o.parents.push(node);\r
14225                                 });\r
14226 \r
14227                                 self.onNodeChange.dispatch(\r
14228                                         self,\r
14229                                         o ? o.controlManager || self.controlManager : self.controlManager,\r
14230                                         node,\r
14231                                         selection.isCollapsed(),\r
14232                                         o\r
14233                                 );\r
14234                         }\r
14235                 },\r
14236 \r
14237                 addButton : function(name, settings) {\r
14238                         var self = this;\r
14239 \r
14240                         self.buttons = self.buttons || {};\r
14241                         self.buttons[name] = settings;\r
14242                 },\r
14243 \r
14244                 addCommand : function(name, callback, scope) {\r
14245                         this.execCommands[name] = {func : callback, scope : scope || this};\r
14246                 },\r
14247 \r
14248                 addQueryStateHandler : function(name, callback, scope) {\r
14249                         this.queryStateCommands[name] = {func : callback, scope : scope || this};\r
14250                 },\r
14251 \r
14252                 addQueryValueHandler : function(name, callback, scope) {\r
14253                         this.queryValueCommands[name] = {func : callback, scope : scope || this};\r
14254                 },\r
14255 \r
14256                 addShortcut : function(pa, desc, cmd_func, sc) {\r
14257                         var t = this, c;\r
14258 \r
14259                         if (t.settings.custom_shortcuts === false)\r
14260                                 return false;\r
14261 \r
14262                         t.shortcuts = t.shortcuts || {};\r
14263 \r
14264                         if (is(cmd_func, 'string')) {\r
14265                                 c = cmd_func;\r
14266 \r
14267                                 cmd_func = function() {\r
14268                                         t.execCommand(c, false, null);\r
14269                                 };\r
14270                         }\r
14271 \r
14272                         if (is(cmd_func, 'object')) {\r
14273                                 c = cmd_func;\r
14274 \r
14275                                 cmd_func = function() {\r
14276                                         t.execCommand(c[0], c[1], c[2]);\r
14277                                 };\r
14278                         }\r
14279 \r
14280                         each(explode(pa), function(pa) {\r
14281                                 var o = {\r
14282                                         func : cmd_func,\r
14283                                         scope : sc || this,\r
14284                                         desc : t.translate(desc),\r
14285                                         alt : false,\r
14286                                         ctrl : false,\r
14287                                         shift : false\r
14288                                 };\r
14289 \r
14290                                 each(explode(pa, '+'), function(v) {\r
14291                                         switch (v) {\r
14292                                                 case 'alt':\r
14293                                                 case 'ctrl':\r
14294                                                 case 'shift':\r
14295                                                         o[v] = true;\r
14296                                                         break;\r
14297 \r
14298                                                 default:\r
14299                                                         o.charCode = v.charCodeAt(0);\r
14300                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
14301                                         }\r
14302                                 });\r
14303 \r
14304                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
14305                         });\r
14306 \r
14307                         return true;\r
14308                 },\r
14309 \r
14310                 execCommand : function(cmd, ui, val, a) {\r
14311                         var t = this, s = 0, o, st;\r
14312 \r
14313                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
14314                                 t.focus();\r
14315 \r
14316                         a = extend({}, a);\r
14317                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, a);\r
14318                         if (a.terminate)\r
14319                                 return false;\r
14320 \r
14321                         // Command callback\r
14322                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
14323                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14324                                 return true;\r
14325                         }\r
14326 \r
14327                         // Registred commands\r
14328                         if (o = t.execCommands[cmd]) {\r
14329                                 st = o.func.call(o.scope, ui, val);\r
14330 \r
14331                                 // Fall through on true\r
14332                                 if (st !== true) {\r
14333                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14334                                         return st;\r
14335                                 }\r
14336                         }\r
14337 \r
14338                         // Plugin commands\r
14339                         each(t.plugins, function(p) {\r
14340                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
14341                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14342                                         s = 1;\r
14343                                         return false;\r
14344                                 }\r
14345                         });\r
14346 \r
14347                         if (s)\r
14348                                 return true;\r
14349 \r
14350                         // Theme commands\r
14351                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
14352                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14353                                 return true;\r
14354                         }\r
14355 \r
14356                         // Editor commands\r
14357                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
14358                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14359                                 return true;\r
14360                         }\r
14361 \r
14362                         // Browser commands\r
14363                         t.getDoc().execCommand(cmd, ui, val);\r
14364                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
14365                 },\r
14366 \r
14367                 queryCommandState : function(cmd) {\r
14368                         var t = this, o, s;\r
14369 \r
14370                         // Is hidden then return undefined\r
14371                         if (t._isHidden())\r
14372                                 return;\r
14373 \r
14374                         // Registred commands\r
14375                         if (o = t.queryStateCommands[cmd]) {\r
14376                                 s = o.func.call(o.scope);\r
14377 \r
14378                                 // Fall though on true\r
14379                                 if (s !== true)\r
14380                                         return s;\r
14381                         }\r
14382 \r
14383                         // Registred commands\r
14384                         o = t.editorCommands.queryCommandState(cmd);\r
14385                         if (o !== -1)\r
14386                                 return o;\r
14387 \r
14388                         // Browser commands\r
14389                         try {\r
14390                                 return this.getDoc().queryCommandState(cmd);\r
14391                         } catch (ex) {\r
14392                                 // Fails sometimes see bug: 1896577\r
14393                         }\r
14394                 },\r
14395 \r
14396                 queryCommandValue : function(c) {\r
14397                         var t = this, o, s;\r
14398 \r
14399                         // Is hidden then return undefined\r
14400                         if (t._isHidden())\r
14401                                 return;\r
14402 \r
14403                         // Registred commands\r
14404                         if (o = t.queryValueCommands[c]) {\r
14405                                 s = o.func.call(o.scope);\r
14406 \r
14407                                 // Fall though on true\r
14408                                 if (s !== true)\r
14409                                         return s;\r
14410                         }\r
14411 \r
14412                         // Registred commands\r
14413                         o = t.editorCommands.queryCommandValue(c);\r
14414                         if (is(o))\r
14415                                 return o;\r
14416 \r
14417                         // Browser commands\r
14418                         try {\r
14419                                 return this.getDoc().queryCommandValue(c);\r
14420                         } catch (ex) {\r
14421                                 // Fails sometimes see bug: 1896577\r
14422                         }\r
14423                 },\r
14424 \r
14425                 show : function() {\r
14426                         var self = this;\r
14427 \r
14428                         DOM.show(self.getContainer());\r
14429                         DOM.hide(self.id);\r
14430                         self.load();\r
14431                 },\r
14432 \r
14433                 hide : function() {\r
14434                         var self = this, doc = self.getDoc();\r
14435 \r
14436                         // Fixed bug where IE has a blinking cursor left from the editor\r
14437                         if (isIE && doc)\r
14438                                 doc.execCommand('SelectAll');\r
14439 \r
14440                         // We must save before we hide so Safari doesn't crash\r
14441                         self.save();\r
14442 \r
14443                         // defer the call to hide to prevent an IE9 crash #4921\r
14444                         DOM.hide(self.getContainer());\r
14445                         DOM.setStyle(self.id, 'display', self.orgDisplay);\r
14446                 },\r
14447 \r
14448                 isHidden : function() {\r
14449                         return !DOM.isHidden(this.id);\r
14450                 },\r
14451 \r
14452                 setProgressState : function(b, ti, o) {\r
14453                         this.onSetProgressState.dispatch(this, b, ti, o);\r
14454 \r
14455                         return b;\r
14456                 },\r
14457 \r
14458                 load : function(o) {\r
14459                         var t = this, e = t.getElement(), h;\r
14460 \r
14461                         if (e) {\r
14462                                 o = o || {};\r
14463                                 o.load = true;\r
14464 \r
14465                                 // Double encode existing entities in the value\r
14466                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
14467                                 o.element = e;\r
14468 \r
14469                                 if (!o.no_events)\r
14470                                         t.onLoadContent.dispatch(t, o);\r
14471 \r
14472                                 o.element = e = null;\r
14473 \r
14474                                 return h;\r
14475                         }\r
14476                 },\r
14477 \r
14478                 save : function(o) {\r
14479                         var t = this, e = t.getElement(), h, f;\r
14480 \r
14481                         if (!e || !t.initialized)\r
14482                                 return;\r
14483 \r
14484                         o = o || {};\r
14485                         o.save = true;\r
14486 \r
14487                         o.element = e;\r
14488                         h = o.content = t.getContent(o);\r
14489 \r
14490                         if (!o.no_events)\r
14491                                 t.onSaveContent.dispatch(t, o);\r
14492 \r
14493                         h = o.content;\r
14494 \r
14495                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
14496                                 e.innerHTML = h;\r
14497 \r
14498                                 // Update hidden form element\r
14499                                 if (f = DOM.getParent(t.id, 'form')) {\r
14500                                         each(f.elements, function(e) {\r
14501                                                 if (e.name == t.id) {\r
14502                                                         e.value = h;\r
14503                                                         return false;\r
14504                                                 }\r
14505                                         });\r
14506                                 }\r
14507                         } else\r
14508                                 e.value = h;\r
14509 \r
14510                         o.element = e = null;\r
14511 \r
14512                         return h;\r
14513                 },\r
14514 \r
14515                 setContent : function(content, args) {\r
14516                         var self = this, rootNode, body = self.getBody(), forcedRootBlockName;\r
14517 \r
14518                         // Setup args object\r
14519                         args = args || {};\r
14520                         args.format = args.format || 'html';\r
14521                         args.set = true;\r
14522                         args.content = content;\r
14523 \r
14524                         // Do preprocessing\r
14525                         if (!args.no_events)\r
14526                                 self.onBeforeSetContent.dispatch(self, args);\r
14527 \r
14528                         content = args.content;\r
14529 \r
14530                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
14531                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
14532                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {\r
14533                                 forcedRootBlockName = self.settings.forced_root_block;\r
14534                                 if (forcedRootBlockName)\r
14535                                         content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';\r
14536                                 else\r
14537                                         content = '<br data-mce-bogus="1">';\r
14538 \r
14539                                 body.innerHTML = content;\r
14540                                 self.selection.select(body, true);\r
14541                                 self.selection.collapse(true);\r
14542                                 return;\r
14543                         }\r
14544 \r
14545                         // Parse and serialize the html\r
14546                         if (args.format !== 'raw') {\r
14547                                 content = new tinymce.html.Serializer({}, self.schema).serialize(\r
14548                                         self.parser.parse(content)\r
14549                                 );\r
14550                         }\r
14551 \r
14552                         // Set the new cleaned contents to the editor\r
14553                         args.content = tinymce.trim(content);\r
14554                         self.dom.setHTML(body, args.content);\r
14555 \r
14556                         // Do post processing\r
14557                         if (!args.no_events)\r
14558                                 self.onSetContent.dispatch(self, args);\r
14559 \r
14560                         // Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise\r
14561                         if (!self.settings.content_editable || document.activeElement === self.getBody()) {\r
14562                                 self.selection.normalize();\r
14563                         }\r
14564 \r
14565                         return args.content;\r
14566                 },\r
14567 \r
14568                 getContent : function(args) {\r
14569                         var self = this, content, body = self.getBody();\r
14570 \r
14571                         // Setup args object\r
14572                         args = args || {};\r
14573                         args.format = args.format || 'html';\r
14574                         args.get = true;\r
14575                         args.getInner = true;\r
14576 \r
14577                         // Do preprocessing\r
14578                         if (!args.no_events)\r
14579                                 self.onBeforeGetContent.dispatch(self, args);\r
14580 \r
14581                         // Get raw contents or by default the cleaned contents\r
14582                         if (args.format == 'raw')\r
14583                                 content = body.innerHTML;\r
14584                         else if (args.format == 'text')\r
14585                                 content = body.innerText || body.textContent;\r
14586                         else\r
14587                                 content = self.serializer.serialize(body, args);\r
14588 \r
14589                         // Trim whitespace in beginning/end of HTML\r
14590                         if (args.format != 'text') {\r
14591                                 args.content = tinymce.trim(content);\r
14592                         } else {\r
14593                                 args.content = content;\r
14594                         }\r
14595 \r
14596                         // Do post processing\r
14597                         if (!args.no_events)\r
14598                                 self.onGetContent.dispatch(self, args);\r
14599 \r
14600                         return args.content;\r
14601                 },\r
14602 \r
14603                 isDirty : function() {\r
14604                         var self = this;\r
14605 \r
14606                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;\r
14607                 },\r
14608 \r
14609                 getContainer : function() {\r
14610                         var self = this;\r
14611 \r
14612                         if (!self.container)\r
14613                                 self.container = DOM.get(self.editorContainer || self.id + '_parent');\r
14614 \r
14615                         return self.container;\r
14616                 },\r
14617 \r
14618                 getContentAreaContainer : function() {\r
14619                         return this.contentAreaContainer;\r
14620                 },\r
14621 \r
14622                 getElement : function() {\r
14623                         return DOM.get(this.settings.content_element || this.id);\r
14624                 },\r
14625 \r
14626                 getWin : function() {\r
14627                         var self = this, elm;\r
14628 \r
14629                         if (!self.contentWindow) {\r
14630                                 elm = DOM.get(self.id + "_ifr");\r
14631 \r
14632                                 if (elm)\r
14633                                         self.contentWindow = elm.contentWindow;\r
14634                         }\r
14635 \r
14636                         return self.contentWindow;\r
14637                 },\r
14638 \r
14639                 getDoc : function() {\r
14640                         var self = this, win;\r
14641 \r
14642                         if (!self.contentDocument) {\r
14643                                 win = self.getWin();\r
14644 \r
14645                                 if (win)\r
14646                                         self.contentDocument = win.document;\r
14647                         }\r
14648 \r
14649                         return self.contentDocument;\r
14650                 },\r
14651 \r
14652                 getBody : function() {\r
14653                         return this.bodyElement || this.getDoc().body;\r
14654                 },\r
14655 \r
14656                 convertURL : function(url, name, elm) {\r
14657                         var self = this, settings = self.settings;\r
14658 \r
14659                         // Use callback instead\r
14660                         if (settings.urlconverter_callback)\r
14661                                 return self.execCallback('urlconverter_callback', url, elm, true, name);\r
14662 \r
14663                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
14664                         if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0)\r
14665                                 return url;\r
14666 \r
14667                         // Convert to relative\r
14668                         if (settings.relative_urls)\r
14669                                 return self.documentBaseURI.toRelative(url);\r
14670 \r
14671                         // Convert to absolute\r
14672                         url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);\r
14673 \r
14674                         return url;\r
14675                 },\r
14676 \r
14677                 addVisual : function(elm) {\r
14678                         var self = this, settings = self.settings, dom = self.dom, cls;\r
14679 \r
14680                         elm = elm || self.getBody();\r
14681 \r
14682                         if (!is(self.hasVisual))\r
14683                                 self.hasVisual = settings.visual;\r
14684 \r
14685                         each(dom.select('table,a', elm), function(elm) {\r
14686                                 var value;\r
14687 \r
14688                                 switch (elm.nodeName) {\r
14689                                         case 'TABLE':\r
14690                                                 cls = settings.visual_table_class || 'mceItemTable';\r
14691                                                 value = dom.getAttrib(elm, 'border');\r
14692 \r
14693                                                 if (!value || value == '0') {\r
14694                                                         if (self.hasVisual)\r
14695                                                                 dom.addClass(elm, cls);\r
14696                                                         else\r
14697                                                                 dom.removeClass(elm, cls);\r
14698                                                 }\r
14699 \r
14700                                                 return;\r
14701 \r
14702                                         case 'A':\r
14703                                                 if (!dom.getAttrib(elm, 'href', false)) {\r
14704                                                         value = dom.getAttrib(elm, 'name') || elm.id;\r
14705                                                         cls = 'mceItemAnchor';\r
14706 \r
14707                                                         if (value) {\r
14708                                                                 if (self.hasVisual)\r
14709                                                                         dom.addClass(elm, cls);\r
14710                                                                 else\r
14711                                                                         dom.removeClass(elm, cls);\r
14712                                                         }\r
14713                                                 }\r
14714 \r
14715                                                 return;\r
14716                                 }\r
14717                         });\r
14718 \r
14719                         self.onVisualAid.dispatch(self, elm, self.hasVisual);\r
14720                 },\r
14721 \r
14722                 remove : function() {\r
14723                         var self = this, elm = self.getContainer(), doc = self.getDoc();\r
14724 \r
14725                         if (!self.removed) {\r
14726                                 self.removed = 1; // Cancels post remove event execution\r
14727 \r
14728                                 // Fixed bug where IE has a blinking cursor left from the editor\r
14729                                 if (isIE && doc)\r
14730                                         doc.execCommand('SelectAll');\r
14731 \r
14732                                 // We must save before we hide so Safari doesn't crash\r
14733                                 self.save();\r
14734 \r
14735                                 DOM.setStyle(self.id, 'display', self.orgDisplay);\r
14736 \r
14737                                 // Don't clear the window or document if content editable\r
14738                                 // is enabled since other instances might still be present\r
14739                                 if (!self.settings.content_editable) {\r
14740                                         Event.unbind(self.getWin());\r
14741                                         Event.unbind(self.getDoc());\r
14742                                 }\r
14743 \r
14744                                 Event.unbind(self.getBody());\r
14745                                 Event.clear(elm);\r
14746 \r
14747                                 self.execCallback('remove_instance_callback', self);\r
14748                                 self.onRemove.dispatch(self);\r
14749 \r
14750                                 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
14751                                 self.onExecCommand.listeners = [];\r
14752 \r
14753                                 tinymce.remove(self);\r
14754                                 DOM.remove(elm);\r
14755                         }\r
14756                 },\r
14757 \r
14758                 destroy : function(s) {\r
14759                         var t = this;\r
14760 \r
14761                         // One time is enough\r
14762                         if (t.destroyed)\r
14763                                 return;\r
14764 \r
14765                         // We must unbind on Gecko since it would otherwise produce the pesky "attempt to run compile-and-go script on a cleared scope" message\r
14766                         if (isGecko) {\r
14767                                 Event.unbind(t.getDoc());\r
14768                                 Event.unbind(t.getWin());\r
14769                                 Event.unbind(t.getBody());\r
14770                         }\r
14771 \r
14772                         if (!s) {\r
14773                                 tinymce.removeUnload(t.destroy);\r
14774                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
14775 \r
14776                                 // Manual destroy\r
14777                                 if (t.theme && t.theme.destroy)\r
14778                                         t.theme.destroy();\r
14779 \r
14780                                 // Destroy controls, selection and dom\r
14781                                 t.controlManager.destroy();\r
14782                                 t.selection.destroy();\r
14783                                 t.dom.destroy();\r
14784                         }\r
14785 \r
14786                         if (t.formElement) {\r
14787                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
14788                                 t.formElement._mceOldSubmit = null;\r
14789                         }\r
14790 \r
14791                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
14792 \r
14793                         if (t.selection)\r
14794                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
14795 \r
14796                         t.destroyed = 1;\r
14797                 },\r
14798 \r
14799                 // Internal functions\r
14800 \r
14801                 _refreshContentEditable : function() {\r
14802                         var self = this, body, parent;\r
14803 \r
14804                         // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again\r
14805                         if (self._isHidden()) {\r
14806                                 body = self.getBody();\r
14807                                 parent = body.parentNode;\r
14808 \r
14809                                 parent.removeChild(body);\r
14810                                 parent.appendChild(body);\r
14811 \r
14812                                 body.focus();\r
14813                         }\r
14814                 },\r
14815 \r
14816                 _isHidden : function() {\r
14817                         var s;\r
14818 \r
14819                         if (!isGecko)\r
14820                                 return 0;\r
14821 \r
14822                         // Weird, wheres that cursor selection?\r
14823                         s = this.selection.getSel();\r
14824                         return (!s || !s.rangeCount || s.rangeCount === 0);\r
14825                 }\r
14826         });\r
14827 })(tinymce);\r
14828 (function(tinymce) {\r
14829         var each = tinymce.each;\r
14830 \r
14831         tinymce.Editor.prototype.setupEvents = function() {\r
14832                 var self = this, settings = self.settings;\r
14833 \r
14834                 // Add events to the editor\r
14835                 each([\r
14836                         'onPreInit',\r
14837 \r
14838                         'onBeforeRenderUI',\r
14839 \r
14840                         'onPostRender',\r
14841 \r
14842                         'onLoad',\r
14843 \r
14844                         'onInit',\r
14845 \r
14846                         'onRemove',\r
14847 \r
14848                         'onActivate',\r
14849 \r
14850                         'onDeactivate',\r
14851 \r
14852                         'onClick',\r
14853 \r
14854                         'onEvent',\r
14855 \r
14856                         'onMouseUp',\r
14857 \r
14858                         'onMouseDown',\r
14859 \r
14860                         'onDblClick',\r
14861 \r
14862                         'onKeyDown',\r
14863 \r
14864                         'onKeyUp',\r
14865 \r
14866                         'onKeyPress',\r
14867 \r
14868                         'onContextMenu',\r
14869 \r
14870                         'onSubmit',\r
14871 \r
14872                         'onReset',\r
14873 \r
14874                         'onPaste',\r
14875 \r
14876                         'onPreProcess',\r
14877 \r
14878                         'onPostProcess',\r
14879 \r
14880                         'onBeforeSetContent',\r
14881 \r
14882                         'onBeforeGetContent',\r
14883 \r
14884                         'onSetContent',\r
14885 \r
14886                         'onGetContent',\r
14887 \r
14888                         'onLoadContent',\r
14889 \r
14890                         'onSaveContent',\r
14891 \r
14892                         'onNodeChange',\r
14893 \r
14894                         'onChange',\r
14895 \r
14896                         'onBeforeExecCommand',\r
14897 \r
14898                         'onExecCommand',\r
14899 \r
14900                         'onUndo',\r
14901 \r
14902                         'onRedo',\r
14903 \r
14904                         'onVisualAid',\r
14905 \r
14906                         'onSetProgressState',\r
14907 \r
14908                         'onSetAttrib'\r
14909                 ], function(name) {\r
14910                         self[name] = new tinymce.util.Dispatcher(self);\r
14911                 });\r
14912 \r
14913                 // Handle legacy cleanup_callback option\r
14914                 if (settings.cleanup_callback) {\r
14915                         self.onBeforeSetContent.add(function(ed, o) {\r
14916                                 o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
14917                         });\r
14918 \r
14919                         self.onPreProcess.add(function(ed, o) {\r
14920                                 if (o.set)\r
14921                                         ed.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
14922 \r
14923                                 if (o.get)\r
14924                                         ed.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
14925                         });\r
14926 \r
14927                         self.onPostProcess.add(function(ed, o) {\r
14928                                 if (o.set)\r
14929                                         o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
14930 \r
14931                                 if (o.get)                                              \r
14932                                         o.content = ed.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
14933                         });\r
14934                 }\r
14935 \r
14936                 // Handle legacy save_callback option\r
14937                 if (settings.save_callback) {\r
14938                         self.onGetContent.add(function(ed, o) {\r
14939                                 if (o.save)\r
14940                                         o.content = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());\r
14941                         });\r
14942                 }\r
14943 \r
14944                 // Handle legacy handle_event_callback option\r
14945                 if (settings.handle_event_callback) {\r
14946                         self.onEvent.add(function(ed, e, o) {\r
14947                                 if (self.execCallback('handle_event_callback', e, ed, o) === false) {\r
14948                                         e.preventDefault();\r
14949                                         e.stopPropagation();\r
14950                                 }\r
14951                         });\r
14952                 }\r
14953 \r
14954                 // Handle legacy handle_node_change_callback option\r
14955                 if (settings.handle_node_change_callback) {\r
14956                         self.onNodeChange.add(function(ed, cm, n) {\r
14957                                 ed.execCallback('handle_node_change_callback', ed.id, n, -1, -1, true, ed.selection.isCollapsed());\r
14958                         });\r
14959                 }\r
14960 \r
14961                 // Handle legacy save_callback option\r
14962                 if (settings.save_callback) {\r
14963                         self.onSaveContent.add(function(ed, o) {\r
14964                                 var h = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());\r
14965 \r
14966                                 if (h)\r
14967                                         o.content = h;\r
14968                         });\r
14969                 }\r
14970 \r
14971                 // Handle legacy onchange_callback option\r
14972                 if (settings.onchange_callback) {\r
14973                         self.onChange.add(function(ed, l) {\r
14974                                 ed.execCallback('onchange_callback', ed, l);\r
14975                         });\r
14976                 }\r
14977         };\r
14978 \r
14979         tinymce.Editor.prototype.bindNativeEvents = function() {\r
14980                 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
14981                 var self = this, i, settings = self.settings, dom = self.dom, nativeToDispatcherMap;\r
14982 \r
14983                 nativeToDispatcherMap = {\r
14984                         mouseup : 'onMouseUp',\r
14985                         mousedown : 'onMouseDown',\r
14986                         click : 'onClick',\r
14987                         keyup : 'onKeyUp',\r
14988                         keydown : 'onKeyDown',\r
14989                         keypress : 'onKeyPress',\r
14990                         submit : 'onSubmit',\r
14991                         reset : 'onReset',\r
14992                         contextmenu : 'onContextMenu',\r
14993                         dblclick : 'onDblClick',\r
14994                         paste : 'onPaste' // Doesn't work in all browsers yet\r
14995                 };\r
14996 \r
14997                 // Handler that takes a native event and sends it out to a dispatcher like onKeyDown\r
14998                 function eventHandler(evt, args) {\r
14999                         var type = evt.type;\r
15000 \r
15001                         // Don't fire events when it's removed\r
15002                         if (self.removed)\r
15003                                 return;\r
15004 \r
15005                         // Sends the native event out to a global dispatcher then to the specific event dispatcher\r
15006                         if (self.onEvent.dispatch(self, evt, args) !== false) {\r
15007                                 self[nativeToDispatcherMap[evt.fakeType || evt.type]].dispatch(self, evt, args);\r
15008                         }\r
15009                 };\r
15010 \r
15011                 // Opera doesn't support focus event for contentEditable elements so we need to fake it\r
15012                 function doOperaFocus(e) {\r
15013                         self.focus(true);\r
15014                 };\r
15015 \r
15016                 function nodeChanged(ed, e) {\r
15017                         // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything\r
15018                         if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) {\r
15019                                 self.selection.normalize();\r
15020                         }\r
15021 \r
15022                         self.nodeChanged();\r
15023                 }\r
15024 \r
15025                 // Add DOM events\r
15026                 each(nativeToDispatcherMap, function(dispatcherName, nativeName) {\r
15027                         var root = settings.content_editable ? self.getBody() : self.getDoc();\r
15028 \r
15029                         switch (nativeName) {\r
15030                                 case 'contextmenu':\r
15031                                         dom.bind(root, nativeName, eventHandler);\r
15032                                         break;\r
15033 \r
15034                                 case 'paste':\r
15035                                         dom.bind(self.getBody(), nativeName, eventHandler);\r
15036                                         break;\r
15037 \r
15038                                 case 'submit':\r
15039                                 case 'reset':\r
15040                                         dom.bind(self.getElement().form || tinymce.DOM.getParent(self.id, 'form'), nativeName, eventHandler);\r
15041                                         break;\r
15042 \r
15043                                 default:\r
15044                                         dom.bind(root, nativeName, eventHandler);\r
15045                         }\r
15046                 });\r
15047 \r
15048                 // Set the editor as active when focused\r
15049                 dom.bind(settings.content_editable ? self.getBody() : (tinymce.isGecko ? self.getDoc() : self.getWin()), 'focus', function(e) {\r
15050                         self.focus(true);\r
15051                 });\r
15052 \r
15053                 if (settings.content_editable && tinymce.isOpera) {\r
15054                         dom.bind(self.getBody(), 'click', doOperaFocus);\r
15055                         dom.bind(self.getBody(), 'keydown', doOperaFocus);\r
15056                 }\r
15057 \r
15058                 // Add node change handler\r
15059                 self.onMouseUp.add(nodeChanged);\r
15060 \r
15061                 self.onKeyUp.add(function(ed, e) {\r
15062                         var keyCode = e.keyCode;\r
15063 \r
15064                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)\r
15065                                 nodeChanged(ed, e);\r
15066                 });\r
15067 \r
15068                 // Add reset handler\r
15069                 self.onReset.add(function() {\r
15070                         self.setContent(self.startContent, {format : 'raw'});\r
15071                 });\r
15072 \r
15073                 // Add shortcuts\r
15074                 function handleShortcut(e, execute) {\r
15075                         if (e.altKey || e.ctrlKey || e.metaKey) {\r
15076                                 each(self.shortcuts, function(shortcut) {\r
15077                                         var ctrlState = tinymce.isMac ? e.metaKey : e.ctrlKey;\r
15078 \r
15079                                         if (shortcut.ctrl != ctrlState || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey)\r
15080                                                 return;\r
15081 \r
15082                                         if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {\r
15083                                                 e.preventDefault();\r
15084 \r
15085                                                 if (execute) {\r
15086                                                         shortcut.func.call(shortcut.scope);\r
15087                                                 }\r
15088 \r
15089                                                 return true;\r
15090                                         }\r
15091                                 });\r
15092                         }\r
15093                 };\r
15094 \r
15095                 self.onKeyUp.add(function(ed, e) {\r
15096                         handleShortcut(e);\r
15097                 });\r
15098 \r
15099                 self.onKeyPress.add(function(ed, e) {\r
15100                         handleShortcut(e);\r
15101                 });\r
15102 \r
15103                 self.onKeyDown.add(function(ed, e) {\r
15104                         handleShortcut(e, true);\r
15105                 });\r
15106 \r
15107                 if (tinymce.isOpera) {\r
15108                         self.onClick.add(function(ed, e) {\r
15109                                 e.preventDefault();\r
15110                         });\r
15111                 }\r
15112         };\r
15113 })(tinymce);\r
15114 (function(tinymce) {\r
15115         // Added for compression purposes\r
15116         var each = tinymce.each, undef, TRUE = true, FALSE = false;\r
15117 \r
15118         tinymce.EditorCommands = function(editor) {\r
15119                 var dom = editor.dom,\r
15120                         selection = editor.selection,\r
15121                         commands = {state: {}, exec : {}, value : {}},\r
15122                         settings = editor.settings,\r
15123                         formatter = editor.formatter,\r
15124                         bookmark;\r
15125 \r
15126                 function execCommand(command, ui, value) {\r
15127                         var func;\r
15128 \r
15129                         command = command.toLowerCase();\r
15130                         if (func = commands.exec[command]) {\r
15131                                 func(command, ui, value);\r
15132                                 return TRUE;\r
15133                         }\r
15134 \r
15135                         return FALSE;\r
15136                 };\r
15137 \r
15138                 function queryCommandState(command) {\r
15139                         var func;\r
15140 \r
15141                         command = command.toLowerCase();\r
15142                         if (func = commands.state[command])\r
15143                                 return func(command);\r
15144 \r
15145                         return -1;\r
15146                 };\r
15147 \r
15148                 function queryCommandValue(command) {\r
15149                         var func;\r
15150 \r
15151                         command = command.toLowerCase();\r
15152                         if (func = commands.value[command])\r
15153                                 return func(command);\r
15154 \r
15155                         return FALSE;\r
15156                 };\r
15157 \r
15158                 function addCommands(command_list, type) {\r
15159                         type = type || 'exec';\r
15160 \r
15161                         each(command_list, function(callback, command) {\r
15162                                 each(command.toLowerCase().split(','), function(command) {\r
15163                                         commands[type][command] = callback;\r
15164                                 });\r
15165                         });\r
15166                 };\r
15167 \r
15168                 // Expose public methods\r
15169                 tinymce.extend(this, {\r
15170                         execCommand : execCommand,\r
15171                         queryCommandState : queryCommandState,\r
15172                         queryCommandValue : queryCommandValue,\r
15173                         addCommands : addCommands\r
15174                 });\r
15175 \r
15176                 // Private methods\r
15177 \r
15178                 function execNativeCommand(command, ui, value) {\r
15179                         if (ui === undef)\r
15180                                 ui = FALSE;\r
15181 \r
15182                         if (value === undef)\r
15183                                 value = null;\r
15184 \r
15185                         return editor.getDoc().execCommand(command, ui, value);\r
15186                 };\r
15187 \r
15188                 function isFormatMatch(name) {\r
15189                         return formatter.match(name);\r
15190                 };\r
15191 \r
15192                 function toggleFormat(name, value) {\r
15193                         formatter.toggle(name, value ? {value : value} : undef);\r
15194                 };\r
15195 \r
15196                 function storeSelection(type) {\r
15197                         bookmark = selection.getBookmark(type);\r
15198                 };\r
15199 \r
15200                 function restoreSelection() {\r
15201                         selection.moveToBookmark(bookmark);\r
15202                 };\r
15203 \r
15204                 // Add execCommand overrides\r
15205                 addCommands({\r
15206                         // Ignore these, added for compatibility\r
15207                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
15208 \r
15209                         // Add undo manager logic\r
15210                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
15211                                 editor.undoManager.add();\r
15212                         },\r
15213 \r
15214                         'Cut,Copy,Paste' : function(command) {\r
15215                                 var doc = editor.getDoc(), failed;\r
15216 \r
15217                                 // Try executing the native command\r
15218                                 try {\r
15219                                         execNativeCommand(command);\r
15220                                 } catch (ex) {\r
15221                                         // Command failed\r
15222                                         failed = TRUE;\r
15223                                 }\r
15224 \r
15225                                 // Present alert message about clipboard access not being available\r
15226                                 if (failed || !doc.queryCommandSupported(command)) {\r
15227                                         if (tinymce.isGecko) {\r
15228                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
15229                                                         if (state)\r
15230                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
15231                                                 });\r
15232                                         } else\r
15233                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
15234                                 }\r
15235                         },\r
15236 \r
15237                         // Override unlink command\r
15238                         unlink : function(command) {\r
15239                                 if (selection.isCollapsed())\r
15240                                         selection.select(selection.getNode());\r
15241 \r
15242                                 execNativeCommand(command);\r
15243                                 selection.collapse(FALSE);\r
15244                         },\r
15245 \r
15246                         // Override justify commands to use the text formatter engine\r
15247                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
15248                                 var align = command.substring(7);\r
15249 \r
15250                                 // Remove all other alignments first\r
15251                                 each('left,center,right,full'.split(','), function(name) {\r
15252                                         if (align != name)\r
15253                                                 formatter.remove('align' + name);\r
15254                                 });\r
15255 \r
15256                                 toggleFormat('align' + align);\r
15257                                 execCommand('mceRepaint');\r
15258                         },\r
15259 \r
15260                         // Override list commands to fix WebKit bug\r
15261                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
15262                                 var listElm, listParent;\r
15263 \r
15264                                 execNativeCommand(command);\r
15265 \r
15266                                 // WebKit produces lists within block elements so we need to split them\r
15267                                 // we will replace the native list creation logic to custom logic later on\r
15268                                 // TODO: Remove this when the list creation logic is removed\r
15269                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
15270                                 if (listElm) {\r
15271                                         listParent = listElm.parentNode;\r
15272 \r
15273                                         // If list is within a text block then split that block\r
15274                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
15275                                                 storeSelection();\r
15276                                                 dom.split(listParent, listElm);\r
15277                                                 restoreSelection();\r
15278                                         }\r
15279                                 }\r
15280                         },\r
15281 \r
15282                         // Override commands to use the text formatter engine\r
15283                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
15284                                 toggleFormat(command);\r
15285                         },\r
15286 \r
15287                         // Override commands to use the text formatter engine\r
15288                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
15289                                 toggleFormat(command, value);\r
15290                         },\r
15291 \r
15292                         FontSize : function(command, ui, value) {\r
15293                                 var fontClasses, fontSizes;\r
15294 \r
15295                                 // Convert font size 1-7 to styles\r
15296                                 if (value >= 1 && value <= 7) {\r
15297                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
15298                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
15299 \r
15300                                         if (fontClasses)\r
15301                                                 value = fontClasses[value - 1] || value;\r
15302                                         else\r
15303                                                 value = fontSizes[value - 1] || value;\r
15304                                 }\r
15305 \r
15306                                 toggleFormat(command, value);\r
15307                         },\r
15308 \r
15309                         RemoveFormat : function(command) {\r
15310                                 formatter.remove(command);\r
15311                         },\r
15312 \r
15313                         mceBlockQuote : function(command) {\r
15314                                 toggleFormat('blockquote');\r
15315                         },\r
15316 \r
15317                         FormatBlock : function(command, ui, value) {\r
15318                                 return toggleFormat(value || 'p');\r
15319                         },\r
15320 \r
15321                         mceCleanup : function() {\r
15322                                 var bookmark = selection.getBookmark();\r
15323 \r
15324                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
15325 \r
15326                                 selection.moveToBookmark(bookmark);\r
15327                         },\r
15328 \r
15329                         mceRemoveNode : function(command, ui, value) {\r
15330                                 var node = value || selection.getNode();\r
15331 \r
15332                                 // Make sure that the body node isn't removed\r
15333                                 if (node != editor.getBody()) {\r
15334                                         storeSelection();\r
15335                                         editor.dom.remove(node, TRUE);\r
15336                                         restoreSelection();\r
15337                                 }\r
15338                         },\r
15339 \r
15340                         mceSelectNodeDepth : function(command, ui, value) {\r
15341                                 var counter = 0;\r
15342 \r
15343                                 dom.getParent(selection.getNode(), function(node) {\r
15344                                         if (node.nodeType == 1 && counter++ == value) {\r
15345                                                 selection.select(node);\r
15346                                                 return FALSE;\r
15347                                         }\r
15348                                 }, editor.getBody());\r
15349                         },\r
15350 \r
15351                         mceSelectNode : function(command, ui, value) {\r
15352                                 selection.select(value);\r
15353                         },\r
15354 \r
15355                         mceInsertContent : function(command, ui, value) {\r
15356                                 var parser, serializer, parentNode, rootNode, fragment, args,\r
15357                                         marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;\r
15358 \r
15359                                 //selection.normalize();\r
15360 \r
15361                                 // Setup parser and serializer\r
15362                                 parser = editor.parser;\r
15363                                 serializer = new tinymce.html.Serializer({}, editor.schema);\r
15364                                 bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';\r
15365 \r
15366                                 // Run beforeSetContent handlers on the HTML to be inserted\r
15367                                 args = {content: value, format: 'html'};\r
15368                                 selection.onBeforeSetContent.dispatch(selection, args);\r
15369                                 value = args.content;\r
15370 \r
15371                                 // Add caret at end of contents if it's missing\r
15372                                 if (value.indexOf('{$caret}') == -1)\r
15373                                         value += '{$caret}';\r
15374 \r
15375                                 // Replace the caret marker with a span bookmark element\r
15376                                 value = value.replace(/\{\$caret\}/, bookmarkHtml);\r
15377 \r
15378                                 // Insert node maker where we will insert the new HTML and get it's parent\r
15379                                 if (!selection.isCollapsed())\r
15380                                         editor.getDoc().execCommand('Delete', false, null);\r
15381 \r
15382                                 parentNode = selection.getNode();\r
15383 \r
15384                                 // Parse the fragment within the context of the parent node\r
15385                                 args = {context : parentNode.nodeName.toLowerCase()};\r
15386                                 fragment = parser.parse(value, args);\r
15387 \r
15388                                 // Move the caret to a more suitable location\r
15389                                 node = fragment.lastChild;\r
15390                                 if (node.attr('id') == 'mce_marker') {\r
15391                                         marker = node;\r
15392 \r
15393                                         for (node = node.prev; node; node = node.walk(true)) {\r
15394                                                 if (node.type == 3 || !dom.isBlock(node.name)) {\r
15395                                                         node.parent.insert(marker, node, node.name === 'br');\r
15396                                                         break;\r
15397                                                 }\r
15398                                         }\r
15399                                 }\r
15400 \r
15401                                 // If parser says valid we can insert the contents into that parent\r
15402                                 if (!args.invalid) {\r
15403                                         value = serializer.serialize(fragment);\r
15404 \r
15405                                         // Check if parent is empty or only has one BR element then set the innerHTML of that parent\r
15406                                         node = parentNode.firstChild;\r
15407                                         node2 = parentNode.lastChild;\r
15408                                         if (!node || (node === node2 && node.nodeName === 'BR'))\r
15409                                                 dom.setHTML(parentNode, value);\r
15410                                         else\r
15411                                                 selection.setContent(value);\r
15412                                 } else {\r
15413                                         // If the fragment was invalid within that context then we need\r
15414                                         // to parse and process the parent it's inserted into\r
15415 \r
15416                                         // Insert bookmark node and get the parent\r
15417                                         selection.setContent(bookmarkHtml);\r
15418                                         parentNode = selection.getNode();\r
15419                                         rootNode = editor.getBody();\r
15420 \r
15421                                         // Opera will return the document node when selection is in root\r
15422                                         if (parentNode.nodeType == 9)\r
15423                                                 parentNode = node = rootNode;\r
15424                                         else\r
15425                                                 node = parentNode;\r
15426 \r
15427                                         // Find the ancestor just before the root element\r
15428                                         while (node !== rootNode) {\r
15429                                                 parentNode = node;\r
15430                                                 node = node.parentNode;\r
15431                                         }\r
15432 \r
15433                                         // Get the outer/inner HTML depending on if we are in the root and parser and serialize that\r
15434                                         value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);\r
15435                                         value = serializer.serialize(\r
15436                                                 parser.parse(\r
15437                                                         // Need to replace by using a function since $ in the contents would otherwise be a problem\r
15438                                                         value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {\r
15439                                                                 return serializer.serialize(fragment);\r
15440                                                         })\r
15441                                                 )\r
15442                                         );\r
15443 \r
15444                                         // Set the inner/outer HTML depending on if we are in the root or not\r
15445                                         if (parentNode == rootNode)\r
15446                                                 dom.setHTML(rootNode, value);\r
15447                                         else\r
15448                                                 dom.setOuterHTML(parentNode, value);\r
15449                                 }\r
15450 \r
15451                                 marker = dom.get('mce_marker');\r
15452 \r
15453                                 // Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well\r
15454                                 nodeRect = dom.getRect(marker);\r
15455                                 viewPortRect = dom.getViewPort(editor.getWin());\r
15456 \r
15457                                 // Check if node is out side the viewport if it is then scroll to it\r
15458                                 if ((nodeRect.y + nodeRect.h > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||\r
15459                                         (nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {\r
15460                                         viewportBodyElement = tinymce.isIE ? editor.getDoc().documentElement : editor.getBody();\r
15461                                         viewportBodyElement.scrollLeft = nodeRect.x;\r
15462                                         viewportBodyElement.scrollTop = nodeRect.y - viewPortRect.h + 25;\r
15463                                 }\r
15464 \r
15465                                 // Move selection before marker and remove it\r
15466                                 rng = dom.createRng();\r
15467 \r
15468                                 // If previous sibling is a text node set the selection to the end of that node\r
15469                                 node = marker.previousSibling;\r
15470                                 if (node && node.nodeType == 3) {\r
15471                                         rng.setStart(node, node.nodeValue.length);\r
15472                                 } else {\r
15473                                         // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node\r
15474                                         rng.setStartBefore(marker);\r
15475                                         rng.setEndBefore(marker);\r
15476                                 }\r
15477 \r
15478                                 // Remove the marker node and set the new range\r
15479                                 dom.remove(marker);\r
15480                                 selection.setRng(rng);\r
15481 \r
15482                                 // Dispatch after event and add any visual elements needed\r
15483                                 selection.onSetContent.dispatch(selection, args);\r
15484                                 editor.addVisual();\r
15485                         },\r
15486 \r
15487                         mceInsertRawHTML : function(command, ui, value) {\r
15488                                 selection.setContent('tiny_mce_marker');\r
15489                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));\r
15490                         },\r
15491 \r
15492                         mceToggleFormat : function(command, ui, value) {\r
15493                                 toggleFormat(value);\r
15494                         },\r
15495 \r
15496                         mceSetContent : function(command, ui, value) {\r
15497                                 editor.setContent(value);\r
15498                         },\r
15499 \r
15500                         'Indent,Outdent' : function(command) {\r
15501                                 var intentValue, indentUnit, value;\r
15502 \r
15503                                 // Setup indent level\r
15504                                 intentValue = settings.indentation;\r
15505                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
15506                                 intentValue = parseInt(intentValue);\r
15507 \r
15508                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
15509                                         // If forced_root_blocks is set to false we don't have a block to indent so lets create a div\r
15510                                         if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {\r
15511                                                 formatter.apply('div');\r
15512                                         }\r
15513 \r
15514                                         each(selection.getSelectedBlocks(), function(element) {\r
15515                                                 if (command == 'outdent') {\r
15516                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
15517                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
15518                                                 } else\r
15519                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
15520                                         });\r
15521                                 } else\r
15522                                         execNativeCommand(command);\r
15523                         },\r
15524 \r
15525                         mceRepaint : function() {\r
15526                                 var bookmark;\r
15527 \r
15528                                 if (tinymce.isGecko) {\r
15529                                         try {\r
15530                                                 storeSelection(TRUE);\r
15531 \r
15532                                                 if (selection.getSel())\r
15533                                                         selection.getSel().selectAllChildren(editor.getBody());\r
15534 \r
15535                                                 selection.collapse(TRUE);\r
15536                                                 restoreSelection();\r
15537                                         } catch (ex) {\r
15538                                                 // Ignore\r
15539                                         }\r
15540                                 }\r
15541                         },\r
15542 \r
15543                         mceToggleFormat : function(command, ui, value) {\r
15544                                 formatter.toggle(value);\r
15545                         },\r
15546 \r
15547                         InsertHorizontalRule : function() {\r
15548                                 editor.execCommand('mceInsertContent', false, '<hr />');\r
15549                         },\r
15550 \r
15551                         mceToggleVisualAid : function() {\r
15552                                 editor.hasVisual = !editor.hasVisual;\r
15553                                 editor.addVisual();\r
15554                         },\r
15555 \r
15556                         mceReplaceContent : function(command, ui, value) {\r
15557                                 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
15558                         },\r
15559 \r
15560                         mceInsertLink : function(command, ui, value) {\r
15561                                 var anchor;\r
15562 \r
15563                                 if (typeof(value) == 'string')\r
15564                                         value = {href : value};\r
15565 \r
15566                                 anchor = dom.getParent(selection.getNode(), 'a');\r
15567 \r
15568                                 // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.\r
15569                                 value.href = value.href.replace(' ', '%20');\r
15570 \r
15571                                 // Remove existing links if there could be child links or that the href isn't specified\r
15572                                 if (!anchor || !value.href) {\r
15573                                         formatter.remove('link');\r
15574                                 }               \r
15575 \r
15576                                 // Apply new link to selection\r
15577                                 if (value.href) {\r
15578                                         formatter.apply('link', value, anchor);\r
15579                                 }\r
15580                         },\r
15581 \r
15582                         selectAll : function() {\r
15583                                 var root = dom.getRoot(), rng = dom.createRng();\r
15584 \r
15585                                 // Old IE does a better job with selectall than new versions\r
15586                                 if (selection.getRng().setStart) {\r
15587                                         rng.setStart(root, 0);\r
15588                                         rng.setEnd(root, root.childNodes.length);\r
15589 \r
15590                                         selection.setRng(rng);\r
15591                                 } else {\r
15592                                         execNativeCommand('SelectAll');\r
15593                                 }\r
15594                         }\r
15595                 });\r
15596 \r
15597                 // Add queryCommandState overrides\r
15598                 addCommands({\r
15599                         // Override justify commands\r
15600                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
15601                                 var name = 'align' + command.substring(7);\r
15602                                 var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();\r
15603                                 var matches = tinymce.map(nodes, function(node) {\r
15604                                         return !!formatter.matchNode(node, name);\r
15605                                 });\r
15606                                 return tinymce.inArray(matches, TRUE) !== -1;\r
15607                         },\r
15608 \r
15609                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
15610                                 return isFormatMatch(command);\r
15611                         },\r
15612 \r
15613                         mceBlockQuote : function() {\r
15614                                 return isFormatMatch('blockquote');\r
15615                         },\r
15616 \r
15617                         Outdent : function() {\r
15618                                 var node;\r
15619 \r
15620                                 if (settings.inline_styles) {\r
15621                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
15622                                                 return TRUE;\r
15623 \r
15624                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
15625                                                 return TRUE;\r
15626                                 }\r
15627 \r
15628                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
15629                         },\r
15630 \r
15631                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
15632                                 var list = dom.getParent(selection.getNode(), 'ul,ol');\r
15633                                 return list && \r
15634                                      (command === 'insertunorderedlist' && list.tagName === 'UL'\r
15635                                    || command === 'insertorderedlist' && list.tagName === 'OL');\r
15636                         }\r
15637                 }, 'state');\r
15638 \r
15639                 // Add queryCommandValue overrides\r
15640                 addCommands({\r
15641                         'FontSize,FontName' : function(command) {\r
15642                                 var value = 0, parent;\r
15643 \r
15644                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
15645                                         if (command == 'fontsize')\r
15646                                                 value = parent.style.fontSize;\r
15647                                         else\r
15648                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
15649                                 }\r
15650 \r
15651                                 return value;\r
15652                         }\r
15653                 }, 'value');\r
15654 \r
15655                 // Add undo manager logic\r
15656                 addCommands({\r
15657                         Undo : function() {\r
15658                                 editor.undoManager.undo();\r
15659                         },\r
15660 \r
15661                         Redo : function() {\r
15662                                 editor.undoManager.redo();\r
15663                         }\r
15664                 });\r
15665         };\r
15666 })(tinymce);\r
15667 \r
15668 (function(tinymce) {\r
15669         var Dispatcher = tinymce.util.Dispatcher;\r
15670 \r
15671         tinymce.UndoManager = function(editor) {\r
15672                 var self, index = 0, data = [], beforeBookmark, onAdd, onUndo, onRedo;\r
15673 \r
15674                 function getContent() {\r
15675                         // Remove whitespace before/after and remove pure bogus nodes\r
15676                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));\r
15677                 };\r
15678 \r
15679                 function addNonTypingUndoLevel() {\r
15680                         self.typing = false;\r
15681                         self.add();\r
15682                 };\r
15683 \r
15684                 // Create event instances\r
15685                 onBeforeAdd = new Dispatcher(self);\r
15686                 onAdd       = new Dispatcher(self);\r
15687                 onUndo      = new Dispatcher(self);\r
15688                 onRedo      = new Dispatcher(self);\r
15689 \r
15690                 // Pass though onAdd event from UndoManager to Editor as onChange\r
15691                 onAdd.add(function(undoman, level) {\r
15692                         if (undoman.hasUndo())\r
15693                                 return editor.onChange.dispatch(editor, level, undoman);\r
15694                 });\r
15695 \r
15696                 // Pass though onUndo event from UndoManager to Editor\r
15697                 onUndo.add(function(undoman, level) {\r
15698                         return editor.onUndo.dispatch(editor, level, undoman);\r
15699                 });\r
15700 \r
15701                 // Pass though onRedo event from UndoManager to Editor\r
15702                 onRedo.add(function(undoman, level) {\r
15703                         return editor.onRedo.dispatch(editor, level, undoman);\r
15704                 });\r
15705 \r
15706                 // Add initial undo level when the editor is initialized\r
15707                 editor.onInit.add(function() {\r
15708                         self.add();\r
15709                 });\r
15710 \r
15711                 // Get position before an execCommand is processed\r
15712                 editor.onBeforeExecCommand.add(function(ed, cmd, ui, val, args) {\r
15713                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {\r
15714                                 self.beforeChange();\r
15715                         }\r
15716                 });\r
15717 \r
15718                 // Add undo level after an execCommand call was made\r
15719                 editor.onExecCommand.add(function(ed, cmd, ui, val, args) {\r
15720                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {\r
15721                                 self.add();\r
15722                         }\r
15723                 });\r
15724 \r
15725                 // Add undo level on save contents, drag end and blur/focusout\r
15726                 editor.onSaveContent.add(addNonTypingUndoLevel);\r
15727                 editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);\r
15728                 editor.dom.bind(editor.getBody(), 'focusout', function(e) {\r
15729                         if (!editor.removed && self.typing) {\r
15730                                 addNonTypingUndoLevel();\r
15731                         }\r
15732                 });\r
15733 \r
15734                 editor.onKeyUp.add(function(editor, e) {\r
15735                         var keyCode = e.keyCode;\r
15736 \r
15737                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {\r
15738                                 addNonTypingUndoLevel();\r
15739                         }\r
15740                 });\r
15741 \r
15742                 editor.onKeyDown.add(function(editor, e) {\r
15743                         var keyCode = e.keyCode;\r
15744 \r
15745                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter\r
15746                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {\r
15747                                 if (self.typing) {\r
15748                                         addNonTypingUndoLevel();\r
15749                                 }\r
15750 \r
15751                                 return;\r
15752                         }\r
15753 \r
15754                         // If key isn't shift,ctrl,alt,capslock,metakey\r
15755                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {\r
15756                                 self.beforeChange();\r
15757                                 self.typing = true;\r
15758                                 self.add();\r
15759                         }\r
15760                 });\r
15761 \r
15762                 editor.onMouseDown.add(function(editor, e) {\r
15763                         if (self.typing) {\r
15764                                 addNonTypingUndoLevel();\r
15765                         }\r
15766                 });\r
15767 \r
15768                 // Add keyboard shortcuts for undo/redo keys\r
15769                 editor.addShortcut('ctrl+z', 'undo_desc', 'Undo');\r
15770                 editor.addShortcut('ctrl+y', 'redo_desc', 'Redo');\r
15771 \r
15772                 self = {\r
15773                         // Explose for debugging reasons\r
15774                         data : data,\r
15775 \r
15776                         typing : false,\r
15777                         \r
15778                         onBeforeAdd: onBeforeAdd,\r
15779 \r
15780                         onAdd : onAdd,\r
15781 \r
15782                         onUndo : onUndo,\r
15783 \r
15784                         onRedo : onRedo,\r
15785 \r
15786                         beforeChange : function() {\r
15787                                 beforeBookmark = editor.selection.getBookmark(2, true);\r
15788                         },\r
15789 \r
15790                         add : function(level) {\r
15791                                 var i, settings = editor.settings, lastLevel;\r
15792 \r
15793                                 level = level || {};\r
15794                                 level.content = getContent();\r
15795                                 \r
15796                                 self.onBeforeAdd.dispatch(self, level);\r
15797 \r
15798                                 // Add undo level if needed\r
15799                                 lastLevel = data[index];\r
15800                                 if (lastLevel && lastLevel.content == level.content)\r
15801                                         return null;\r
15802 \r
15803                                 // Set before bookmark on previous level\r
15804                                 if (data[index])\r
15805                                         data[index].beforeBookmark = beforeBookmark;\r
15806 \r
15807                                 // Time to compress\r
15808                                 if (settings.custom_undo_redo_levels) {\r
15809                                         if (data.length > settings.custom_undo_redo_levels) {\r
15810                                                 for (i = 0; i < data.length - 1; i++)\r
15811                                                         data[i] = data[i + 1];\r
15812 \r
15813                                                 data.length--;\r
15814                                                 index = data.length;\r
15815                                         }\r
15816                                 }\r
15817 \r
15818                                 // Get a non intrusive normalized bookmark\r
15819                                 level.bookmark = editor.selection.getBookmark(2, true);\r
15820 \r
15821                                 // Crop array if needed\r
15822                                 if (index < data.length - 1)\r
15823                                         data.length = index + 1;\r
15824 \r
15825                                 data.push(level);\r
15826                                 index = data.length - 1;\r
15827 \r
15828                                 self.onAdd.dispatch(self, level);\r
15829                                 editor.isNotDirty = 0;\r
15830 \r
15831                                 return level;\r
15832                         },\r
15833 \r
15834                         undo : function() {\r
15835                                 var level, i;\r
15836 \r
15837                                 if (self.typing) {\r
15838                                         self.add();\r
15839                                         self.typing = false;\r
15840                                 }\r
15841 \r
15842                                 if (index > 0) {\r
15843                                         level = data[--index];\r
15844 \r
15845                                         editor.setContent(level.content, {format : 'raw'});\r
15846                                         editor.selection.moveToBookmark(level.beforeBookmark);\r
15847 \r
15848                                         self.onUndo.dispatch(self, level);\r
15849                                 }\r
15850 \r
15851                                 return level;\r
15852                         },\r
15853 \r
15854                         redo : function() {\r
15855                                 var level;\r
15856 \r
15857                                 if (index < data.length - 1) {\r
15858                                         level = data[++index];\r
15859 \r
15860                                         editor.setContent(level.content, {format : 'raw'});\r
15861                                         editor.selection.moveToBookmark(level.bookmark);\r
15862 \r
15863                                         self.onRedo.dispatch(self, level);\r
15864                                 }\r
15865 \r
15866                                 return level;\r
15867                         },\r
15868 \r
15869                         clear : function() {\r
15870                                 data = [];\r
15871                                 index = 0;\r
15872                                 self.typing = false;\r
15873                         },\r
15874 \r
15875                         hasUndo : function() {\r
15876                                 return index > 0 || this.typing;\r
15877                         },\r
15878 \r
15879                         hasRedo : function() {\r
15880                                 return index < data.length - 1 && !this.typing;\r
15881                         }\r
15882                 };\r
15883 \r
15884                 return self;\r
15885         };\r
15886 })(tinymce);\r
15887 \r
15888 tinymce.ForceBlocks = function(editor) {\r
15889         var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();\r
15890 \r
15891         function addRootBlocks() {\r
15892                 var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped, isInEditorDocument;\r
15893 \r
15894                 if (!node || node.nodeType !== 1 || !settings.forced_root_block)\r
15895                         return;\r
15896 \r
15897                 // Check if node is wrapped in block\r
15898                 while (node && node != rootNode) {\r
15899                         if (blockElements[node.nodeName])\r
15900                                 return;\r
15901 \r
15902                         node = node.parentNode;\r
15903                 }\r
15904 \r
15905                 // Get current selection\r
15906                 rng = selection.getRng();\r
15907                 if (rng.setStart) {\r
15908                         startContainer = rng.startContainer;\r
15909                         startOffset = rng.startOffset;\r
15910                         endContainer = rng.endContainer;\r
15911                         endOffset = rng.endOffset;\r
15912                 } else {\r
15913                         // Force control range into text range\r
15914                         if (rng.item) {\r
15915                                 node = rng.item(0);\r
15916                                 rng = editor.getDoc().body.createTextRange();\r
15917                                 rng.moveToElementText(node);\r
15918                         }\r
15919 \r
15920                         isInEditorDocument = rng.parentElement().ownerDocument === editor.getDoc();\r
15921                         tmpRng = rng.duplicate();\r
15922                         tmpRng.collapse(true);\r
15923                         startOffset = tmpRng.move('character', offset) * -1;\r
15924 \r
15925                         if (!tmpRng.collapsed) {\r
15926                                 tmpRng = rng.duplicate();\r
15927                                 tmpRng.collapse(false);\r
15928                                 endOffset = (tmpRng.move('character', offset) * -1) - startOffset;\r
15929                         }\r
15930                 }\r
15931 \r
15932                 // Wrap non block elements and text nodes\r
15933                 node = rootNode.firstChild;\r
15934                 while (node) {\r
15935                         if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {\r
15936                                 // Remove empty text nodes\r
15937                                 if (node.nodeType === 3 && node.nodeValue.length == 0) {\r
15938                                         tempNode = node;\r
15939                                         node = node.nextSibling;\r
15940                                         dom.remove(tempNode);\r
15941                                         continue;\r
15942                                 }\r
15943 \r
15944                                 if (!rootBlockNode) {\r
15945                                         rootBlockNode = dom.create(settings.forced_root_block);\r
15946                                         node.parentNode.insertBefore(rootBlockNode, node);\r
15947                                         wrapped = true;\r
15948                                 }\r
15949 \r
15950                                 tempNode = node;\r
15951                                 node = node.nextSibling;\r
15952                                 rootBlockNode.appendChild(tempNode);\r
15953                         } else {\r
15954                                 rootBlockNode = null;\r
15955                                 node = node.nextSibling;\r
15956                         }\r
15957                 }\r
15958 \r
15959                 if (wrapped) {\r
15960                         if (rng.setStart) {\r
15961                                 rng.setStart(startContainer, startOffset);\r
15962                                 rng.setEnd(endContainer, endOffset);\r
15963                                 selection.setRng(rng);\r
15964                         } else {\r
15965                                 // Only select if the previous selection was inside the document to prevent auto focus in quirks mode\r
15966                                 if (isInEditorDocument) {\r
15967                                         try {\r
15968                                                 rng = editor.getDoc().body.createTextRange();\r
15969                                                 rng.moveToElementText(rootNode);\r
15970                                                 rng.collapse(true);\r
15971                                                 rng.moveStart('character', startOffset);\r
15972 \r
15973                                                 if (endOffset > 0)\r
15974                                                         rng.moveEnd('character', endOffset);\r
15975 \r
15976                                                 rng.select();\r
15977                                         } catch (ex) {\r
15978                                                 // Ignore\r
15979                                         }\r
15980                                 }\r
15981                         }\r
15982 \r
15983                         editor.nodeChanged();\r
15984                 }\r
15985         };\r
15986 \r
15987         // Force root blocks\r
15988         if (settings.forced_root_block) {\r
15989                 editor.onKeyUp.add(addRootBlocks);\r
15990                 editor.onNodeChange.add(addRootBlocks);\r
15991         }\r
15992 };\r
15993 \r
15994 (function(tinymce) {\r
15995         // Shorten names\r
15996         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
15997 \r
15998         tinymce.create('tinymce.ControlManager', {\r
15999                 ControlManager : function(ed, s) {\r
16000                         var t = this, i;\r
16001 \r
16002                         s = s || {};\r
16003                         t.editor = ed;\r
16004                         t.controls = {};\r
16005                         t.onAdd = new tinymce.util.Dispatcher(t);\r
16006                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
16007                         t.prefix = s.prefix || ed.id + '_';\r
16008                         t._cls = {};\r
16009 \r
16010                         t.onPostRender.add(function() {\r
16011                                 each(t.controls, function(c) {\r
16012                                         c.postRender();\r
16013                                 });\r
16014                         });\r
16015                 },\r
16016 \r
16017                 get : function(id) {\r
16018                         return this.controls[this.prefix + id] || this.controls[id];\r
16019                 },\r
16020 \r
16021                 setActive : function(id, s) {\r
16022                         var c = null;\r
16023 \r
16024                         if (c = this.get(id))\r
16025                                 c.setActive(s);\r
16026 \r
16027                         return c;\r
16028                 },\r
16029 \r
16030                 setDisabled : function(id, s) {\r
16031                         var c = null;\r
16032 \r
16033                         if (c = this.get(id))\r
16034                                 c.setDisabled(s);\r
16035 \r
16036                         return c;\r
16037                 },\r
16038 \r
16039                 add : function(c) {\r
16040                         var t = this;\r
16041 \r
16042                         if (c) {\r
16043                                 t.controls[c.id] = c;\r
16044                                 t.onAdd.dispatch(c, t);\r
16045                         }\r
16046 \r
16047                         return c;\r
16048                 },\r
16049 \r
16050                 createControl : function(name) {\r
16051                         var ctrl, i, l, self = this, editor = self.editor, factories, ctrlName;\r
16052 \r
16053                         // Build control factory cache\r
16054                         if (!self.controlFactories) {\r
16055                                 self.controlFactories = [];\r
16056                                 each(editor.plugins, function(plugin) {\r
16057                                         if (plugin.createControl) {\r
16058                                                 self.controlFactories.push(plugin);\r
16059                                         }\r
16060                                 });\r
16061                         }\r
16062 \r
16063                         // Create controls by asking cached factories\r
16064                         factories = self.controlFactories;\r
16065                         for (i = 0, l = factories.length; i < l; i++) {\r
16066                                 ctrl = factories[i].createControl(name, self);\r
16067 \r
16068                                 if (ctrl) {\r
16069                                         return self.add(ctrl);\r
16070                                 }\r
16071                         }\r
16072 \r
16073                         // Create sepearator\r
16074                         if (name === "|" || name === "separator") {\r
16075                                 return self.createSeparator();\r
16076                         }\r
16077 \r
16078                         // Create control from button collection\r
16079                         if (editor.buttons && (ctrl = editor.buttons[name])) {\r
16080                                 return self.createButton(name, ctrl);\r
16081                         }\r
16082 \r
16083                         return self.add(ctrl);\r
16084                 },\r
16085 \r
16086                 createDropMenu : function(id, s, cc) {\r
16087                         var t = this, ed = t.editor, c, bm, v, cls;\r
16088 \r
16089                         s = extend({\r
16090                                 'class' : 'mceDropDown',\r
16091                                 constrain : ed.settings.constrain_menus\r
16092                         }, s);\r
16093 \r
16094                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
16095                         if (v = ed.getParam('skin_variant'))\r
16096                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
16097 \r
16098                         s['class'] += ed.settings.directionality == "rtl" ? ' mceRtl' : '';\r
16099 \r
16100                         id = t.prefix + id;\r
16101                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
16102                         c = t.controls[id] = new cls(id, s);\r
16103                         c.onAddItem.add(function(c, o) {\r
16104                                 var s = o.settings;\r
16105 \r
16106                                 s.title = ed.getLang(s.title, s.title);\r
16107 \r
16108                                 if (!s.onclick) {\r
16109                                         s.onclick = function(v) {\r
16110                                                 if (s.cmd)\r
16111                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
16112                                         };\r
16113                                 }\r
16114                         });\r
16115 \r
16116                         ed.onRemove.add(function() {\r
16117                                 c.destroy();\r
16118                         });\r
16119 \r
16120                         // Fix for bug #1897785, #1898007\r
16121                         if (tinymce.isIE) {\r
16122                                 c.onShowMenu.add(function() {\r
16123                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
16124                                         ed.focus();\r
16125 \r
16126                                         bm = ed.selection.getBookmark(1);\r
16127                                 });\r
16128 \r
16129                                 c.onHideMenu.add(function() {\r
16130                                         if (bm) {\r
16131                                                 ed.selection.moveToBookmark(bm);\r
16132                                                 bm = 0;\r
16133                                         }\r
16134                                 });\r
16135                         }\r
16136 \r
16137                         return t.add(c);\r
16138                 },\r
16139 \r
16140                 createListBox : function(id, s, cc) {\r
16141                         var t = this, ed = t.editor, cmd, c, cls;\r
16142 \r
16143                         if (t.get(id))\r
16144                                 return null;\r
16145 \r
16146                         s.title = ed.translate(s.title);\r
16147                         s.scope = s.scope || ed;\r
16148 \r
16149                         if (!s.onselect) {\r
16150                                 s.onselect = function(v) {\r
16151                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
16152                                 };\r
16153                         }\r
16154 \r
16155                         s = extend({\r
16156                                 title : s.title,\r
16157                                 'class' : 'mce_' + id,\r
16158                                 scope : s.scope,\r
16159                                 control_manager : t\r
16160                         }, s);\r
16161 \r
16162                         id = t.prefix + id;\r
16163 \r
16164 \r
16165                         function useNativeListForAccessibility(ed) {\r
16166                                 return ed.settings.use_accessible_selects && !tinymce.isGecko\r
16167                         }\r
16168 \r
16169                         if (ed.settings.use_native_selects || useNativeListForAccessibility(ed))\r
16170                                 c = new tinymce.ui.NativeListBox(id, s);\r
16171                         else {\r
16172                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
16173                                 c = new cls(id, s, ed);\r
16174                         }\r
16175 \r
16176                         t.controls[id] = c;\r
16177 \r
16178                         // Fix focus problem in Safari\r
16179                         if (tinymce.isWebKit) {\r
16180                                 c.onPostRender.add(function(c, n) {\r
16181                                         // Store bookmark on mousedown\r
16182                                         Event.add(n, 'mousedown', function() {\r
16183                                                 ed.bookmark = ed.selection.getBookmark(1);\r
16184                                         });\r
16185 \r
16186                                         // Restore on focus, since it might be lost\r
16187                                         Event.add(n, 'focus', function() {\r
16188                                                 ed.selection.moveToBookmark(ed.bookmark);\r
16189                                                 ed.bookmark = null;\r
16190                                         });\r
16191                                 });\r
16192                         }\r
16193 \r
16194                         if (c.hideMenu)\r
16195                                 ed.onMouseDown.add(c.hideMenu, c);\r
16196 \r
16197                         return t.add(c);\r
16198                 },\r
16199 \r
16200                 createButton : function(id, s, cc) {\r
16201                         var t = this, ed = t.editor, o, c, cls;\r
16202 \r
16203                         if (t.get(id))\r
16204                                 return null;\r
16205 \r
16206                         s.title = ed.translate(s.title);\r
16207                         s.label = ed.translate(s.label);\r
16208                         s.scope = s.scope || ed;\r
16209 \r
16210                         if (!s.onclick && !s.menu_button) {\r
16211                                 s.onclick = function() {\r
16212                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
16213                                 };\r
16214                         }\r
16215 \r
16216                         s = extend({\r
16217                                 title : s.title,\r
16218                                 'class' : 'mce_' + id,\r
16219                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
16220                                 scope : s.scope,\r
16221                                 control_manager : t\r
16222                         }, s);\r
16223 \r
16224                         id = t.prefix + id;\r
16225 \r
16226                         if (s.menu_button) {\r
16227                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
16228                                 c = new cls(id, s, ed);\r
16229                                 ed.onMouseDown.add(c.hideMenu, c);\r
16230                         } else {\r
16231                                 cls = t._cls.button || tinymce.ui.Button;\r
16232                                 c = new cls(id, s, ed);\r
16233                         }\r
16234 \r
16235                         return t.add(c);\r
16236                 },\r
16237 \r
16238                 createMenuButton : function(id, s, cc) {\r
16239                         s = s || {};\r
16240                         s.menu_button = 1;\r
16241 \r
16242                         return this.createButton(id, s, cc);\r
16243                 },\r
16244 \r
16245                 createSplitButton : function(id, s, cc) {\r
16246                         var t = this, ed = t.editor, cmd, c, cls;\r
16247 \r
16248                         if (t.get(id))\r
16249                                 return null;\r
16250 \r
16251                         s.title = ed.translate(s.title);\r
16252                         s.scope = s.scope || ed;\r
16253 \r
16254                         if (!s.onclick) {\r
16255                                 s.onclick = function(v) {\r
16256                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
16257                                 };\r
16258                         }\r
16259 \r
16260                         if (!s.onselect) {\r
16261                                 s.onselect = function(v) {\r
16262                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
16263                                 };\r
16264                         }\r
16265 \r
16266                         s = extend({\r
16267                                 title : s.title,\r
16268                                 'class' : 'mce_' + id,\r
16269                                 scope : s.scope,\r
16270                                 control_manager : t\r
16271                         }, s);\r
16272 \r
16273                         id = t.prefix + id;\r
16274                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
16275                         c = t.add(new cls(id, s, ed));\r
16276                         ed.onMouseDown.add(c.hideMenu, c);\r
16277 \r
16278                         return c;\r
16279                 },\r
16280 \r
16281                 createColorSplitButton : function(id, s, cc) {\r
16282                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
16283 \r
16284                         if (t.get(id))\r
16285                                 return null;\r
16286 \r
16287                         s.title = ed.translate(s.title);\r
16288                         s.scope = s.scope || ed;\r
16289 \r
16290                         if (!s.onclick) {\r
16291                                 s.onclick = function(v) {\r
16292                                         if (tinymce.isIE)\r
16293                                                 bm = ed.selection.getBookmark(1);\r
16294 \r
16295                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
16296                                 };\r
16297                         }\r
16298 \r
16299                         if (!s.onselect) {\r
16300                                 s.onselect = function(v) {\r
16301                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
16302                                 };\r
16303                         }\r
16304 \r
16305                         s = extend({\r
16306                                 title : s.title,\r
16307                                 'class' : 'mce_' + id,\r
16308                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
16309                                 scope : s.scope,\r
16310                                 more_colors_title : ed.getLang('more_colors')\r
16311                         }, s);\r
16312 \r
16313                         id = t.prefix + id;\r
16314                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
16315                         c = new cls(id, s, ed);\r
16316                         ed.onMouseDown.add(c.hideMenu, c);\r
16317 \r
16318                         // Remove the menu element when the editor is removed\r
16319                         ed.onRemove.add(function() {\r
16320                                 c.destroy();\r
16321                         });\r
16322 \r
16323                         // Fix for bug #1897785, #1898007\r
16324                         if (tinymce.isIE) {\r
16325                                 c.onShowMenu.add(function() {\r
16326                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
16327                                         ed.focus();\r
16328                                         bm = ed.selection.getBookmark(1);\r
16329                                 });\r
16330 \r
16331                                 c.onHideMenu.add(function() {\r
16332                                         if (bm) {\r
16333                                                 ed.selection.moveToBookmark(bm);\r
16334                                                 bm = 0;\r
16335                                         }\r
16336                                 });\r
16337                         }\r
16338 \r
16339                         return t.add(c);\r
16340                 },\r
16341 \r
16342                 createToolbar : function(id, s, cc) {\r
16343                         var c, t = this, cls;\r
16344 \r
16345                         id = t.prefix + id;\r
16346                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
16347                         c = new cls(id, s, t.editor);\r
16348 \r
16349                         if (t.get(id))\r
16350                                 return null;\r
16351 \r
16352                         return t.add(c);\r
16353                 },\r
16354                 \r
16355                 createToolbarGroup : function(id, s, cc) {\r
16356                         var c, t = this, cls;\r
16357                         id = t.prefix + id;\r
16358                         cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;\r
16359                         c = new cls(id, s, t.editor);\r
16360                         \r
16361                         if (t.get(id))\r
16362                                 return null;\r
16363                         \r
16364                         return t.add(c);\r
16365                 },\r
16366 \r
16367                 createSeparator : function(cc) {\r
16368                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
16369 \r
16370                         return new cls();\r
16371                 },\r
16372 \r
16373                 setControlType : function(n, c) {\r
16374                         return this._cls[n.toLowerCase()] = c;\r
16375                 },\r
16376         \r
16377                 destroy : function() {\r
16378                         each(this.controls, function(c) {\r
16379                                 c.destroy();\r
16380                         });\r
16381 \r
16382                         this.controls = null;\r
16383                 }\r
16384         });\r
16385 })(tinymce);\r
16386 \r
16387 (function(tinymce) {\r
16388         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
16389 \r
16390         tinymce.create('tinymce.WindowManager', {\r
16391                 WindowManager : function(ed) {\r
16392                         var t = this;\r
16393 \r
16394                         t.editor = ed;\r
16395                         t.onOpen = new Dispatcher(t);\r
16396                         t.onClose = new Dispatcher(t);\r
16397                         t.params = {};\r
16398                         t.features = {};\r
16399                 },\r
16400 \r
16401                 open : function(s, p) {\r
16402                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
16403 \r
16404                         // Default some options\r
16405                         s = s || {};\r
16406                         p = p || {};\r
16407                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
16408                         sh = isOpera ? vp.h : screen.height;\r
16409                         s.name = s.name || 'mc_' + new Date().getTime();\r
16410                         s.width = parseInt(s.width || 320);\r
16411                         s.height = parseInt(s.height || 240);\r
16412                         s.resizable = true;\r
16413                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
16414                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
16415                         p.inline = false;\r
16416                         p.mce_width = s.width;\r
16417                         p.mce_height = s.height;\r
16418                         p.mce_auto_focus = s.auto_focus;\r
16419 \r
16420                         if (mo) {\r
16421                                 if (isIE) {\r
16422                                         s.center = true;\r
16423                                         s.help = false;\r
16424                                         s.dialogWidth = s.width + 'px';\r
16425                                         s.dialogHeight = s.height + 'px';\r
16426                                         s.scroll = s.scrollbars || false;\r
16427                                 }\r
16428                         }\r
16429 \r
16430                         // Build features string\r
16431                         each(s, function(v, k) {\r
16432                                 if (tinymce.is(v, 'boolean'))\r
16433                                         v = v ? 'yes' : 'no';\r
16434 \r
16435                                 if (!/^(name|url)$/.test(k)) {\r
16436                                         if (isIE && mo)\r
16437                                                 f += (f ? ';' : '') + k + ':' + v;\r
16438                                         else\r
16439                                                 f += (f ? ',' : '') + k + '=' + v;\r
16440                                 }\r
16441                         });\r
16442 \r
16443                         t.features = s;\r
16444                         t.params = p;\r
16445                         t.onOpen.dispatch(t, s, p);\r
16446 \r
16447                         u = s.url || s.file;\r
16448                         u = tinymce._addVer(u);\r
16449 \r
16450                         try {\r
16451                                 if (isIE && mo) {\r
16452                                         w = 1;\r
16453                                         window.showModalDialog(u, window, f);\r
16454                                 } else\r
16455                                         w = window.open(u, s.name, f);\r
16456                         } catch (ex) {\r
16457                                 // Ignore\r
16458                         }\r
16459 \r
16460                         if (!w)\r
16461                                 alert(t.editor.getLang('popup_blocked'));\r
16462                 },\r
16463 \r
16464                 close : function(w) {\r
16465                         w.close();\r
16466                         this.onClose.dispatch(this);\r
16467                 },\r
16468 \r
16469                 createInstance : function(cl, a, b, c, d, e) {\r
16470                         var f = tinymce.resolve(cl);\r
16471 \r
16472                         return new f(a, b, c, d, e);\r
16473                 },\r
16474 \r
16475                 confirm : function(t, cb, s, w) {\r
16476                         w = w || window;\r
16477 \r
16478                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
16479                 },\r
16480 \r
16481                 alert : function(tx, cb, s, w) {\r
16482                         var t = this;\r
16483 \r
16484                         w = w || window;\r
16485                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
16486 \r
16487                         if (cb)\r
16488                                 cb.call(s || t);\r
16489                 },\r
16490 \r
16491                 resizeBy : function(dw, dh, win) {\r
16492                         win.resizeBy(dw, dh);\r
16493                 },\r
16494 \r
16495                 // Internal functions\r
16496 \r
16497                 _decode : function(s) {\r
16498                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
16499                 }\r
16500         });\r
16501 }(tinymce));\r
16502 (function(tinymce) {\r
16503         tinymce.Formatter = function(ed) {\r
16504                 var formats = {},\r
16505                         each = tinymce.each,\r
16506                         dom = ed.dom,\r
16507                         selection = ed.selection,\r
16508                         TreeWalker = tinymce.dom.TreeWalker,\r
16509                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
16510                         isValid = ed.schema.isValidChild,\r
16511                         isArray = tinymce.isArray,\r
16512                         isBlock = dom.isBlock,\r
16513                         forcedRootBlock = ed.settings.forced_root_block,\r
16514                         nodeIndex = dom.nodeIndex,\r
16515                         INVISIBLE_CHAR = '\uFEFF',\r
16516                         MCE_ATTR_RE = /^(src|href|style)$/,\r
16517                         FALSE = false,\r
16518                         TRUE = true,\r
16519                         formatChangeData,\r
16520                         undef,\r
16521                         getContentEditable = dom.getContentEditable;\r
16522 \r
16523                 function isTextBlock(name) {\r
16524                         if (name.nodeType) {\r
16525                                 name = name.nodeName;\r
16526                         }\r
16527 \r
16528                         return !!ed.schema.getTextBlockElements()[name.toLowerCase()];\r
16529                 }\r
16530 \r
16531                 function getParents(node, selector) {\r
16532                         return dom.getParents(node, selector, dom.getRoot());\r
16533                 };\r
16534 \r
16535                 function isCaretNode(node) {\r
16536                         return node.nodeType === 1 && node.id === '_mce_caret';\r
16537                 };\r
16538 \r
16539                 function defaultFormats() {\r
16540                         register({\r
16541                                 alignleft : [\r
16542                                         {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},\r
16543                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}\r
16544                                 ],\r
16545 \r
16546                                 aligncenter : [\r
16547                                         {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},\r
16548                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
16549                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
16550                                 ],\r
16551 \r
16552                                 alignright : [\r
16553                                         {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},\r
16554                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}\r
16555                                 ],\r
16556 \r
16557                                 alignfull : [\r
16558                                         {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}\r
16559                                 ],\r
16560 \r
16561                                 bold : [\r
16562                                         {inline : 'strong', remove : 'all'},\r
16563                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
16564                                         {inline : 'b', remove : 'all'}\r
16565                                 ],\r
16566 \r
16567                                 italic : [\r
16568                                         {inline : 'em', remove : 'all'},\r
16569                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
16570                                         {inline : 'i', remove : 'all'}\r
16571                                 ],\r
16572 \r
16573                                 underline : [\r
16574                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
16575                                         {inline : 'u', remove : 'all'}\r
16576                                 ],\r
16577 \r
16578                                 strikethrough : [\r
16579                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
16580                                         {inline : 'strike', remove : 'all'}\r
16581                                 ],\r
16582 \r
16583                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},\r
16584                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},\r
16585                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
16586                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
16587                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
16588                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
16589                                 subscript : {inline : 'sub'},\r
16590                                 superscript : {inline : 'sup'},\r
16591 \r
16592                                 link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,\r
16593                                         onmatch : function(node) {\r
16594                                                 return true;\r
16595                                         },\r
16596 \r
16597                                         onformat : function(elm, fmt, vars) {\r
16598                                                 each(vars, function(value, key) {\r
16599                                                         dom.setAttrib(elm, key, value);\r
16600                                                 });\r
16601                                         }\r
16602                                 },\r
16603 \r
16604                                 removeformat : [\r
16605                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
16606                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
16607                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
16608                                 ]\r
16609                         });\r
16610 \r
16611                         // Register default block formats\r
16612                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
16613                                 register(name, {block : name, remove : 'all'});\r
16614                         });\r
16615 \r
16616                         // Register user defined formats\r
16617                         register(ed.settings.formats);\r
16618                 };\r
16619 \r
16620                 function addKeyboardShortcuts() {\r
16621                         // Add some inline shortcuts\r
16622                         ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');\r
16623                         ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');\r
16624                         ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');\r
16625 \r
16626                         // BlockFormat shortcuts keys\r
16627                         for (var i = 1; i <= 6; i++) {\r
16628                                 ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
16629                         }\r
16630 \r
16631                         ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);\r
16632                         ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);\r
16633                         ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);\r
16634                 };\r
16635 \r
16636                 // Public functions\r
16637 \r
16638                 function get(name) {\r
16639                         return name ? formats[name] : formats;\r
16640                 };\r
16641 \r
16642                 function register(name, format) {\r
16643                         if (name) {\r
16644                                 if (typeof(name) !== 'string') {\r
16645                                         each(name, function(format, name) {\r
16646                                                 register(name, format);\r
16647                                         });\r
16648                                 } else {\r
16649                                         // Force format into array and add it to internal collection\r
16650                                         format = format.length ? format : [format];\r
16651 \r
16652                                         each(format, function(format) {\r
16653                                                 // Set deep to false by default on selector formats this to avoid removing\r
16654                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
16655                                                 if (format.deep === undef)\r
16656                                                         format.deep = !format.selector;\r
16657 \r
16658                                                 // Default to true\r
16659                                                 if (format.split === undef)\r
16660                                                         format.split = !format.selector || format.inline;\r
16661 \r
16662                                                 // Default to true\r
16663                                                 if (format.remove === undef && format.selector && !format.inline)\r
16664                                                         format.remove = 'none';\r
16665 \r
16666                                                 // Mark format as a mixed format inline + block level\r
16667                                                 if (format.selector && format.inline) {\r
16668                                                         format.mixed = true;\r
16669                                                         format.block_expand = true;\r
16670                                                 }\r
16671 \r
16672                                                 // Split classes if needed\r
16673                                                 if (typeof(format.classes) === 'string')\r
16674                                                         format.classes = format.classes.split(/\s+/);\r
16675                                         });\r
16676 \r
16677                                         formats[name] = format;\r
16678                                 }\r
16679                         }\r
16680                 };\r
16681 \r
16682                 var getTextDecoration = function(node) {\r
16683                         var decoration;\r
16684 \r
16685                         ed.dom.getParent(node, function(n) {\r
16686                                 decoration = ed.dom.getStyle(n, 'text-decoration');\r
16687                                 return decoration && decoration !== 'none';\r
16688                         });\r
16689 \r
16690                         return decoration;\r
16691                 };\r
16692 \r
16693                 var processUnderlineAndColor = function(node) {\r
16694                         var textDecoration;\r
16695                         if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {\r
16696                                 textDecoration = getTextDecoration(node.parentNode);\r
16697                                 if (ed.dom.getStyle(node, 'color') && textDecoration) {\r
16698                                         ed.dom.setStyle(node, 'text-decoration', textDecoration);\r
16699                                 } else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {\r
16700                                         ed.dom.setStyle(node, 'text-decoration', null);\r
16701                                 }\r
16702                         }\r
16703                 };\r
16704 \r
16705                 function apply(name, vars, node) {\r
16706                         var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();\r
16707 \r
16708                         function setElementFormat(elm, fmt) {\r
16709                                 fmt = fmt || format;\r
16710 \r
16711                                 if (elm) {\r
16712                                         if (fmt.onformat) {\r
16713                                                 fmt.onformat(elm, fmt, vars, node);\r
16714                                         }\r
16715 \r
16716                                         each(fmt.styles, function(value, name) {\r
16717                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
16718                                         });\r
16719 \r
16720                                         each(fmt.attributes, function(value, name) {\r
16721                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
16722                                         });\r
16723 \r
16724                                         each(fmt.classes, function(value) {\r
16725                                                 value = replaceVars(value, vars);\r
16726 \r
16727                                                 if (!dom.hasClass(elm, value))\r
16728                                                         dom.addClass(elm, value);\r
16729                                         });\r
16730                                 }\r
16731                         };\r
16732                         function adjustSelectionToVisibleSelection() {\r
16733                                 function findSelectionEnd(start, end) {\r
16734                                         var walker = new TreeWalker(end);\r
16735                                         for (node = walker.current(); node; node = walker.prev()) {\r
16736                                                 if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {\r
16737                                                         return node;\r
16738                                                 }\r
16739                                         }\r
16740                                 };\r
16741 \r
16742                                 // Adjust selection so that a end container with a end offset of zero is not included in the selection\r
16743                                 // as this isn't visible to the user.\r
16744                                 var rng = ed.selection.getRng();\r
16745                                 var start = rng.startContainer;\r
16746                                 var end = rng.endContainer;\r
16747 \r
16748                                 if (start != end && rng.endOffset === 0) {\r
16749                                         var newEnd = findSelectionEnd(start, end);\r
16750                                         var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;\r
16751 \r
16752                                         rng.setEnd(newEnd, endOffset);\r
16753                                 }\r
16754 \r
16755                                 return rng;\r
16756                         }\r
16757                         \r
16758                         function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){\r
16759                                 var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;\r
16760                                 \r
16761                                 // find the index of the first child list.\r
16762                                 each(node.childNodes, function(n, index) {\r
16763                                         if (n.nodeName === "UL" || n.nodeName === "OL") {\r
16764                                                 listIndex = index;\r
16765                                                 list = n;\r
16766                                                 return false;\r
16767                                         }\r
16768                                 });\r
16769                                 \r
16770                                 // get the index of the bookmarks\r
16771                                 each(node.childNodes, function(n, index) {\r
16772                                         if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {\r
16773                                                 if (n.id == bookmark.id + "_start") {\r
16774                                                         startIndex = index;\r
16775                                                 } else if (n.id == bookmark.id + "_end") {\r
16776                                                         endIndex = index;\r
16777                                                 }\r
16778                                         }\r
16779                                 });\r
16780                                 \r
16781                                 // if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally\r
16782                                 if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {\r
16783                                         each(tinymce.grep(node.childNodes), process);\r
16784                                         return 0;\r
16785                                 } else {\r
16786                                         currentWrapElm = dom.clone(wrapElm, FALSE);\r
16787 \r
16788                                         // create a list of the nodes on the same side of the list as the selection\r
16789                                         each(tinymce.grep(node.childNodes), function(n, index) {\r
16790                                                 if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {\r
16791                                                         nodes.push(n); \r
16792                                                         n.parentNode.removeChild(n);\r
16793                                                 }\r
16794                                         });\r
16795 \r
16796                                         // insert the wrapping element either before or after the list.\r
16797                                         if (startIndex < listIndex) {\r
16798                                                 node.insertBefore(currentWrapElm, list);\r
16799                                         } else if (startIndex > listIndex) {\r
16800                                                 node.insertBefore(currentWrapElm, list.nextSibling);\r
16801                                         }\r
16802                                         \r
16803                                         // add the new nodes to the list.\r
16804                                         newWrappers.push(currentWrapElm);\r
16805 \r
16806                                         each(nodes, function(node) {\r
16807                                                 currentWrapElm.appendChild(node);\r
16808                                         });\r
16809 \r
16810                                         return currentWrapElm;\r
16811                                 }\r
16812                         };\r
16813 \r
16814                         function applyRngStyle(rng, bookmark, node_specific) {\r
16815                                 var newWrappers = [], wrapName, wrapElm, contentEditable = true;\r
16816 \r
16817                                 // Setup wrapper element\r
16818                                 wrapName = format.inline || format.block;\r
16819                                 wrapElm = dom.create(wrapName);\r
16820                                 setElementFormat(wrapElm);\r
16821 \r
16822                                 rangeUtils.walk(rng, function(nodes) {\r
16823                                         var currentWrapElm;\r
16824 \r
16825                                         function process(node) {\r
16826                                                 var nodeName, parentName, found, hasContentEditableState, lastContentEditable;\r
16827 \r
16828                                                 lastContentEditable = contentEditable;\r
16829                                                 nodeName = node.nodeName.toLowerCase();\r
16830                                                 parentName = node.parentNode.nodeName.toLowerCase();\r
16831 \r
16832                                                 // Node has a contentEditable value\r
16833                                                 if (node.nodeType === 1 && getContentEditable(node)) {\r
16834                                                         lastContentEditable = contentEditable;\r
16835                                                         contentEditable = getContentEditable(node) === "true";\r
16836                                                         hasContentEditableState = true; // We don't want to wrap the container only it's children\r
16837                                                 }\r
16838 \r
16839                                                 // Stop wrapping on br elements\r
16840                                                 if (isEq(nodeName, 'br')) {\r
16841                                                         currentWrapElm = 0;\r
16842 \r
16843                                                         // Remove any br elements when we wrap things\r
16844                                                         if (format.block)\r
16845                                                                 dom.remove(node);\r
16846 \r
16847                                                         return;\r
16848                                                 }\r
16849 \r
16850                                                 // If node is wrapper type\r
16851                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
16852                                                         currentWrapElm = 0;\r
16853                                                         return;\r
16854                                                 }\r
16855 \r
16856                                                 // Can we rename the block\r
16857                                                 if (contentEditable && !hasContentEditableState && format.block && !format.wrapper && isTextBlock(nodeName)) {\r
16858                                                         node = dom.rename(node, wrapName);\r
16859                                                         setElementFormat(node);\r
16860                                                         newWrappers.push(node);\r
16861                                                         currentWrapElm = 0;\r
16862                                                         return;\r
16863                                                 }\r
16864 \r
16865                                                 // Handle selector patterns\r
16866                                                 if (format.selector) {\r
16867                                                         // Look for matching formats\r
16868                                                         each(formatList, function(format) {\r
16869                                                                 // Check collapsed state if it exists\r
16870                                                                 if ('collapsed' in format && format.collapsed !== isCollapsed) {\r
16871                                                                         return;\r
16872                                                                 }\r
16873 \r
16874                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
16875                                                                         setElementFormat(node, format);\r
16876                                                                         found = true;\r
16877                                                                 }\r
16878                                                         });\r
16879 \r
16880                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
16881                                                         if (!format.inline || found) {\r
16882                                                                 currentWrapElm = 0;\r
16883                                                                 return;\r
16884                                                         }\r
16885                                                 }\r
16886 \r
16887                                                 // Is it valid to wrap this item\r
16888                                                 if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&\r
16889                                                                 !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && !isCaretNode(node) && (!format.inline || !isBlock(node))) {\r
16890                                                         // Start wrapping\r
16891                                                         if (!currentWrapElm) {\r
16892                                                                 // Wrap the node\r
16893                                                                 currentWrapElm = dom.clone(wrapElm, FALSE);\r
16894                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
16895                                                                 newWrappers.push(currentWrapElm);\r
16896                                                         }\r
16897 \r
16898                                                         currentWrapElm.appendChild(node);\r
16899                                                 } else if (nodeName == 'li' && bookmark) {\r
16900                                                         // Start wrapping - if we are in a list node and have a bookmark, then we will always begin by wrapping in a new element.\r
16901                                                         currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);\r
16902                                                 } else {\r
16903                                                         // Start a new wrapper for possible children\r
16904                                                         currentWrapElm = 0;\r
16905                                                         \r
16906                                                         each(tinymce.grep(node.childNodes), process);\r
16907 \r
16908                                                         if (hasContentEditableState) {\r
16909                                                                 contentEditable = lastContentEditable; // Restore last contentEditable state from stack\r
16910                                                         }\r
16911 \r
16912                                                         // End the last wrapper\r
16913                                                         currentWrapElm = 0;\r
16914                                                 }\r
16915                                         };\r
16916 \r
16917                                         // Process siblings from range\r
16918                                         each(nodes, process);\r
16919                                 });\r
16920 \r
16921                                 // Wrap links inside as well, for example color inside a link when the wrapper is around the link\r
16922                                 if (format.wrap_links === false) {\r
16923                                         each(newWrappers, function(node) {\r
16924                                                 function process(node) {\r
16925                                                         var i, currentWrapElm, children;\r
16926 \r
16927                                                         if (node.nodeName === 'A') {\r
16928                                                                 currentWrapElm = dom.clone(wrapElm, FALSE);\r
16929                                                                 newWrappers.push(currentWrapElm);\r
16930 \r
16931                                                                 children = tinymce.grep(node.childNodes);\r
16932                                                                 for (i = 0; i < children.length; i++)\r
16933                                                                         currentWrapElm.appendChild(children[i]);\r
16934 \r
16935                                                                 node.appendChild(currentWrapElm);\r
16936                                                         }\r
16937 \r
16938                                                         each(tinymce.grep(node.childNodes), process);\r
16939                                                 };\r
16940 \r
16941                                                 process(node);\r
16942                                         });\r
16943                                 }\r
16944 \r
16945                                 // Cleanup\r
16946                                 \r
16947                                 each(newWrappers, function(node) {\r
16948                                         var childCount;\r
16949 \r
16950                                         function getChildCount(node) {\r
16951                                                 var count = 0;\r
16952 \r
16953                                                 each(node.childNodes, function(node) {\r
16954                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
16955                                                                 count++;\r
16956                                                 });\r
16957 \r
16958                                                 return count;\r
16959                                         };\r
16960 \r
16961                                         function mergeStyles(node) {\r
16962                                                 var child, clone;\r
16963 \r
16964                                                 each(node.childNodes, function(node) {\r
16965                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
16966                                                                 child = node;\r
16967                                                                 return FALSE; // break loop\r
16968                                                         }\r
16969                                                 });\r
16970 \r
16971                                                 // If child was found and of the same type as the current node\r
16972                                                 if (child && matchName(child, format)) {\r
16973                                                         clone = dom.clone(child, FALSE);\r
16974                                                         setElementFormat(clone);\r
16975 \r
16976                                                         dom.replace(clone, node, TRUE);\r
16977                                                         dom.remove(child, 1);\r
16978                                                 }\r
16979 \r
16980                                                 return clone || node;\r
16981                                         };\r
16982 \r
16983                                         childCount = getChildCount(node);\r
16984 \r
16985                                         // Remove empty nodes but only if there is multiple wrappers and they are not block\r
16986                                         // elements so never remove single <h1></h1> since that would remove the currrent empty block element where the caret is at\r
16987                                         if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {\r
16988                                                 dom.remove(node, 1);\r
16989                                                 return;\r
16990                                         }\r
16991 \r
16992                                         if (format.inline || format.wrapper) {\r
16993                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
16994                                                 if (!format.exact && childCount === 1)\r
16995                                                         node = mergeStyles(node);\r
16996 \r
16997                                                 // Remove/merge children\r
16998                                                 each(formatList, function(format) {\r
16999                                                         // Merge all children of similar type will move styles from child to parent\r
17000                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
17001                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
17002                                                         each(dom.select(format.inline, node), function(child) {\r
17003                                                                 var parent;\r
17004 \r
17005                                                                 // When wrap_links is set to false we don't want\r
17006                                                                 // to remove the format on children within links\r
17007                                                                 if (format.wrap_links === false) {\r
17008                                                                         parent = child.parentNode;\r
17009 \r
17010                                                                         do {\r
17011                                                                                 if (parent.nodeName === 'A')\r
17012                                                                                         return;\r
17013                                                                         } while (parent = parent.parentNode);\r
17014                                                                 }\r
17015 \r
17016                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
17017                                                         });\r
17018                                                 });\r
17019 \r
17020                                                 // Remove child if direct parent is of same type\r
17021                                                 if (matchNode(node.parentNode, name, vars)) {\r
17022                                                         dom.remove(node, 1);\r
17023                                                         node = 0;\r
17024                                                         return TRUE;\r
17025                                                 }\r
17026 \r
17027                                                 // Look for parent with similar style format\r
17028                                                 if (format.merge_with_parents) {\r
17029                                                         dom.getParent(node.parentNode, function(parent) {\r
17030                                                                 if (matchNode(parent, name, vars)) {\r
17031                                                                         dom.remove(node, 1);\r
17032                                                                         node = 0;\r
17033                                                                         return TRUE;\r
17034                                                                 }\r
17035                                                         });\r
17036                                                 }\r
17037 \r
17038                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
17039                                                 if (node && format.merge_siblings !== false) {\r
17040                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
17041                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
17042                                                 }\r
17043                                         }\r
17044                                 });\r
17045                         };\r
17046 \r
17047                         if (format) {\r
17048                                 if (node) {\r
17049                                         if (node.nodeType) {\r
17050                                                 rng = dom.createRng();\r
17051                                                 rng.setStartBefore(node);\r
17052                                                 rng.setEndAfter(node);\r
17053                                                 applyRngStyle(expandRng(rng, formatList), null, true);\r
17054                                         } else {\r
17055                                                 applyRngStyle(node, null, true);\r
17056                                         }\r
17057                                 } else {\r
17058                                         if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
17059                                                 // Obtain selection node before selection is unselected by applyRngStyle()\r
17060                                                 var curSelNode = ed.selection.getNode();\r
17061 \r
17062                                                 // If the formats have a default block and we can't find a parent block then start wrapping it with a DIV this is for forced_root_blocks: false\r
17063                                                 // It's kind of a hack but people should be using the default block type P since all desktop editors work that way\r
17064                                                 if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {\r
17065                                                         apply(formatList[0].defaultBlock);\r
17066                                                 }\r
17067 \r
17068                                                 // Apply formatting to selection\r
17069                                                 ed.selection.setRng(adjustSelectionToVisibleSelection());\r
17070                                                 bookmark = selection.getBookmark();\r
17071                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);\r
17072 \r
17073                                                 // Colored nodes should be underlined so that the color of the underline matches the text color.\r
17074                                                 if (format.styles && (format.styles.color || format.styles.textDecoration)) {\r
17075                                                         tinymce.walk(curSelNode, processUnderlineAndColor, 'childNodes');\r
17076                                                         processUnderlineAndColor(curSelNode);\r
17077                                                 }\r
17078 \r
17079                                                 selection.moveToBookmark(bookmark);\r
17080                                                 moveStart(selection.getRng(TRUE));\r
17081                                                 ed.nodeChanged();\r
17082                                         } else\r
17083                                                 performCaretAction('apply', name, vars);\r
17084                                 }\r
17085                         }\r
17086                 };\r
17087 \r
17088                 function remove(name, vars, node) {\r
17089                         var formatList = get(name), format = formatList[0], bookmark, i, rng, contentEditable = true;\r
17090 \r
17091                         // Merges the styles for each node\r
17092                         function process(node) {\r
17093                                 var children, i, l, localContentEditable, lastContentEditable, hasContentEditableState;\r
17094 \r
17095                                 // Skip on text nodes as they have neither format to remove nor children\r
17096                                 if (node.nodeType === 3) {\r
17097                                         return;\r
17098                                 }\r
17099 \r
17100                                 // Node has a contentEditable value\r
17101                                 if (node.nodeType === 1 && getContentEditable(node)) {\r
17102                                         lastContentEditable = contentEditable;\r
17103                                         contentEditable = getContentEditable(node) === "true";\r
17104                                         hasContentEditableState = true; // We don't want to wrap the container only it's children\r
17105                                 }\r
17106 \r
17107                                 // Grab the children first since the nodelist might be changed\r
17108                                 children = tinymce.grep(node.childNodes);\r
17109 \r
17110                                 // Process current node\r
17111                                 if (contentEditable && !hasContentEditableState) {\r
17112                                         for (i = 0, l = formatList.length; i < l; i++) {\r
17113                                                 if (removeFormat(formatList[i], vars, node, node))\r
17114                                                         break;\r
17115                                         }\r
17116                                 }\r
17117 \r
17118                                 // Process the children\r
17119                                 if (format.deep) {\r
17120                                         if (children.length) {                                  \r
17121                                                 for (i = 0, l = children.length; i < l; i++)\r
17122                                                         process(children[i]);\r
17123 \r
17124                                                 if (hasContentEditableState) {\r
17125                                                         contentEditable = lastContentEditable; // Restore last contentEditable state from stack\r
17126                                                 }\r
17127                                         }\r
17128                                 }\r
17129                         };\r
17130 \r
17131                         function findFormatRoot(container) {\r
17132                                 var formatRoot;\r
17133 \r
17134                                 // Find format root\r
17135                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
17136                                         var format;\r
17137 \r
17138                                         // Find format root element\r
17139                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
17140                                                 // Is the node matching the format we are looking for\r
17141                                                 format = matchNode(parent, name, vars);\r
17142                                                 if (format && format.split !== false)\r
17143                                                         formatRoot = parent;\r
17144                                         }\r
17145                                 });\r
17146 \r
17147                                 return formatRoot;\r
17148                         };\r
17149 \r
17150                         function wrapAndSplit(format_root, container, target, split) {\r
17151                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
17152 \r
17153                                 // Format root found then clone formats and split it\r
17154                                 if (format_root) {\r
17155                                         formatRootParent = format_root.parentNode;\r
17156 \r
17157                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
17158                                                 clone = dom.clone(parent, FALSE);\r
17159 \r
17160                                                 for (i = 0; i < formatList.length; i++) {\r
17161                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
17162                                                                 clone = 0;\r
17163                                                                 break;\r
17164                                                         }\r
17165                                                 }\r
17166 \r
17167                                                 // Build wrapper node\r
17168                                                 if (clone) {\r
17169                                                         if (lastClone)\r
17170                                                                 clone.appendChild(lastClone);\r
17171 \r
17172                                                         if (!firstClone)\r
17173                                                                 firstClone = clone;\r
17174 \r
17175                                                         lastClone = clone;\r
17176                                                 }\r
17177                                         }\r
17178 \r
17179                                         // Never split block elements if the format is mixed\r
17180                                         if (split && (!format.mixed || !isBlock(format_root)))\r
17181                                                 container = dom.split(format_root, container);\r
17182 \r
17183                                         // Wrap container in cloned formats\r
17184                                         if (lastClone) {\r
17185                                                 target.parentNode.insertBefore(lastClone, target);\r
17186                                                 firstClone.appendChild(target);\r
17187                                         }\r
17188                                 }\r
17189 \r
17190                                 return container;\r
17191                         };\r
17192 \r
17193                         function splitToFormatRoot(container) {\r
17194                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
17195                         };\r
17196 \r
17197                         function unwrap(start) {\r
17198                                 var node = dom.get(start ? '_start' : '_end'),\r
17199                                         out = node[start ? 'firstChild' : 'lastChild'];\r
17200 \r
17201                                 // If the end is placed within the start the result will be removed\r
17202                                 // So this checks if the out node is a bookmark node if it is it\r
17203                                 // checks for another more suitable node\r
17204                                 if (isBookmarkNode(out))\r
17205                                         out = out[start ? 'firstChild' : 'lastChild'];\r
17206 \r
17207                                 dom.remove(node, true);\r
17208 \r
17209                                 return out;\r
17210                         };\r
17211 \r
17212                         function removeRngStyle(rng) {\r
17213                                 var startContainer, endContainer, node;\r
17214 \r
17215                                 rng = expandRng(rng, formatList, TRUE);\r
17216 \r
17217                                 if (format.split) {\r
17218                                         startContainer = getContainer(rng, TRUE);\r
17219                                         endContainer = getContainer(rng);\r
17220 \r
17221                                         if (startContainer != endContainer) {\r
17222                                                 // WebKit will render the table incorrectly if we wrap a TD in a SPAN so lets see if the can use the first child instead\r
17223                                                 // This will happen if you tripple click a table cell and use remove formatting\r
17224                                                 if (/^(TR|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {\r
17225                                                         startContainer = (startContainer.nodeName == "TD" ? startContainer.firstChild : startContainer.firstChild.firstChild) || startContainer;\r
17226                                                 }\r
17227 \r
17228                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
17229                                                 startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});\r
17230                                                 endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});\r
17231 \r
17232                                                 // Split start/end\r
17233                                                 splitToFormatRoot(startContainer);\r
17234                                                 splitToFormatRoot(endContainer);\r
17235 \r
17236                                                 // Unwrap start/end to get real elements again\r
17237                                                 startContainer = unwrap(TRUE);\r
17238                                                 endContainer = unwrap();\r
17239                                         } else\r
17240                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
17241 \r
17242                                         // Update range positions since they might have changed after the split operations\r
17243                                         rng.startContainer = startContainer.parentNode;\r
17244                                         rng.startOffset = nodeIndex(startContainer);\r
17245                                         rng.endContainer = endContainer.parentNode;\r
17246                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
17247                                 }\r
17248 \r
17249                                 // Remove items between start/end\r
17250                                 rangeUtils.walk(rng, function(nodes) {\r
17251                                         each(nodes, function(node) {\r
17252                                                 process(node);\r
17253 \r
17254                                                 // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.\r
17255                                                 if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && getTextDecoration(node.parentNode) === 'underline') {\r
17256                                                         removeFormat({'deep': false, 'exact': true, 'inline': 'span', 'styles': {'textDecoration' : 'underline'}}, null, node);\r
17257                                                 }\r
17258                                         });\r
17259                                 });\r
17260                         };\r
17261 \r
17262                         // Handle node\r
17263                         if (node) {\r
17264                                 if (node.nodeType) {\r
17265                                         rng = dom.createRng();\r
17266                                         rng.setStartBefore(node);\r
17267                                         rng.setEndAfter(node);\r
17268                                         removeRngStyle(rng);\r
17269                                 } else {\r
17270                                         removeRngStyle(node);\r
17271                                 }\r
17272 \r
17273                                 return;\r
17274                         }\r
17275 \r
17276                         if (!selection.isCollapsed() || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
17277                                 bookmark = selection.getBookmark();\r
17278                                 removeRngStyle(selection.getRng(TRUE));\r
17279                                 selection.moveToBookmark(bookmark);\r
17280 \r
17281                                 // Check if start element still has formatting then we are at: "<b>text|</b>text" and need to move the start into the next text node\r
17282                                 if (format.inline && match(name, vars, selection.getStart())) {\r
17283                                         moveStart(selection.getRng(true));\r
17284                                 }\r
17285 \r
17286                                 ed.nodeChanged();\r
17287                         } else\r
17288                                 performCaretAction('remove', name, vars);\r
17289                 };\r
17290 \r
17291                 function toggle(name, vars, node) {\r
17292                         var fmt = get(name);\r
17293 \r
17294                         if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle))\r
17295                                 remove(name, vars, node);\r
17296                         else\r
17297                                 apply(name, vars, node);\r
17298                 };\r
17299 \r
17300                 function matchNode(node, name, vars, similar) {\r
17301                         var formatList = get(name), format, i, classes;\r
17302 \r
17303                         function matchItems(node, format, item_name) {\r
17304                                 var key, value, items = format[item_name], i;\r
17305 \r
17306                                 // Custom match\r
17307                                 if (format.onmatch) {\r
17308                                         return format.onmatch(node, format, item_name);\r
17309                                 }\r
17310 \r
17311                                 // Check all items\r
17312                                 if (items) {\r
17313                                         // Non indexed object\r
17314                                         if (items.length === undef) {\r
17315                                                 for (key in items) {\r
17316                                                         if (items.hasOwnProperty(key)) {\r
17317                                                                 if (item_name === 'attributes')\r
17318                                                                         value = dom.getAttrib(node, key);\r
17319                                                                 else\r
17320                                                                         value = getStyle(node, key);\r
17321 \r
17322                                                                 if (similar && !value && !format.exact)\r
17323                                                                         return;\r
17324 \r
17325                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
17326                                                                         return;\r
17327                                                         }\r
17328                                                 }\r
17329                                         } else {\r
17330                                                 // Only one match needed for indexed arrays\r
17331                                                 for (i = 0; i < items.length; i++) {\r
17332                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
17333                                                                 return format;\r
17334                                                 }\r
17335                                         }\r
17336                                 }\r
17337 \r
17338                                 return format;\r
17339                         };\r
17340 \r
17341                         if (formatList && node) {\r
17342                                 // Check each format in list\r
17343                                 for (i = 0; i < formatList.length; i++) {\r
17344                                         format = formatList[i];\r
17345 \r
17346                                         // Name name, attributes, styles and classes\r
17347                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
17348                                                 // Match classes\r
17349                                                 if (classes = format.classes) {\r
17350                                                         for (i = 0; i < classes.length; i++) {\r
17351                                                                 if (!dom.hasClass(node, classes[i]))\r
17352                                                                         return;\r
17353                                                         }\r
17354                                                 }\r
17355 \r
17356                                                 return format;\r
17357                                         }\r
17358                                 }\r
17359                         }\r
17360                 };\r
17361 \r
17362                 function match(name, vars, node) {\r
17363                         var startNode;\r
17364 \r
17365                         function matchParents(node) {\r
17366                                 // Find first node with similar format settings\r
17367                                 node = dom.getParent(node, function(node) {\r
17368                                         return !!matchNode(node, name, vars, true);\r
17369                                 });\r
17370 \r
17371                                 // Do an exact check on the similar format element\r
17372                                 return matchNode(node, name, vars);\r
17373                         };\r
17374 \r
17375                         // Check specified node\r
17376                         if (node)\r
17377                                 return matchParents(node);\r
17378 \r
17379                         // Check selected node\r
17380                         node = selection.getNode();\r
17381                         if (matchParents(node))\r
17382                                 return TRUE;\r
17383 \r
17384                         // Check start node if it's different\r
17385                         startNode = selection.getStart();\r
17386                         if (startNode != node) {\r
17387                                 if (matchParents(startNode))\r
17388                                         return TRUE;\r
17389                         }\r
17390 \r
17391                         return FALSE;\r
17392                 };\r
17393 \r
17394                 function matchAll(names, vars) {\r
17395                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
17396 \r
17397                         // Check start of selection for formats\r
17398                         startElement = selection.getStart();\r
17399                         dom.getParent(startElement, function(node) {\r
17400                                 var i, name;\r
17401 \r
17402                                 for (i = 0; i < names.length; i++) {\r
17403                                         name = names[i];\r
17404 \r
17405                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
17406                                                 checkedMap[name] = true;\r
17407                                                 matchedFormatNames.push(name);\r
17408                                         }\r
17409                                 }\r
17410                         }, dom.getRoot());\r
17411 \r
17412                         return matchedFormatNames;\r
17413                 };\r
17414 \r
17415                 function canApply(name) {\r
17416                         var formatList = get(name), startNode, parents, i, x, selector;\r
17417 \r
17418                         if (formatList) {\r
17419                                 startNode = selection.getStart();\r
17420                                 parents = getParents(startNode);\r
17421 \r
17422                                 for (x = formatList.length - 1; x >= 0; x--) {\r
17423                                         selector = formatList[x].selector;\r
17424 \r
17425                                         // Format is not selector based, then always return TRUE\r
17426                                         if (!selector)\r
17427                                                 return TRUE;\r
17428 \r
17429                                         for (i = parents.length - 1; i >= 0; i--) {\r
17430                                                 if (dom.is(parents[i], selector))\r
17431                                                         return TRUE;\r
17432                                         }\r
17433                                 }\r
17434                         }\r
17435 \r
17436                         return FALSE;\r
17437                 };\r
17438 \r
17439                 function formatChanged(formats, callback, similar) {\r
17440                         var currentFormats;\r
17441 \r
17442                         // Setup format node change logic\r
17443                         if (!formatChangeData) {\r
17444                                 formatChangeData = {};\r
17445                                 currentFormats = {};\r
17446 \r
17447                                 ed.onNodeChange.addToTop(function(ed, cm, node) {\r
17448                                         var parents = getParents(node), matchedFormats = {};\r
17449 \r
17450                                         // Check for new formats\r
17451                                         each(formatChangeData, function(callbacks, format) {\r
17452                                                 each(parents, function(node) {\r
17453                                                         if (matchNode(node, format, {}, callbacks.similar)) {\r
17454                                                                 if (!currentFormats[format]) {\r
17455                                                                         // Execute callbacks\r
17456                                                                         each(callbacks, function(callback) {\r
17457                                                                                 callback(true, {node: node, format: format, parents: parents});\r
17458                                                                         });\r
17459 \r
17460                                                                         currentFormats[format] = callbacks;\r
17461                                                                 }\r
17462 \r
17463                                                                 matchedFormats[format] = callbacks;\r
17464                                                                 return false;\r
17465                                                         }\r
17466                                                 });\r
17467                                         });\r
17468 \r
17469                                         // Check if current formats still match\r
17470                                         each(currentFormats, function(callbacks, format) {\r
17471                                                 if (!matchedFormats[format]) {\r
17472                                                         delete currentFormats[format];\r
17473 \r
17474                                                         each(callbacks, function(callback) {\r
17475                                                                 callback(false, {node: node, format: format, parents: parents});\r
17476                                                         });\r
17477                                                 }\r
17478                                         });\r
17479                                 });\r
17480                         }\r
17481 \r
17482                         // Add format listeners\r
17483                         each(formats.split(','), function(format) {\r
17484                                 if (!formatChangeData[format]) {\r
17485                                         formatChangeData[format] = [];\r
17486                                         formatChangeData[format].similar = similar;\r
17487                                 }\r
17488 \r
17489                                 formatChangeData[format].push(callback);\r
17490                         });\r
17491 \r
17492                         return this;\r
17493                 };\r
17494 \r
17495                 // Expose to public\r
17496                 tinymce.extend(this, {\r
17497                         get : get,\r
17498                         register : register,\r
17499                         apply : apply,\r
17500                         remove : remove,\r
17501                         toggle : toggle,\r
17502                         match : match,\r
17503                         matchAll : matchAll,\r
17504                         matchNode : matchNode,\r
17505                         canApply : canApply,\r
17506                         formatChanged: formatChanged\r
17507                 });\r
17508 \r
17509                 // Initialize\r
17510                 defaultFormats();\r
17511                 addKeyboardShortcuts();\r
17512 \r
17513                 // Private functions\r
17514 \r
17515                 function matchName(node, format) {\r
17516                         // Check for inline match\r
17517                         if (isEq(node, format.inline))\r
17518                                 return TRUE;\r
17519 \r
17520                         // Check for block match\r
17521                         if (isEq(node, format.block))\r
17522                                 return TRUE;\r
17523 \r
17524                         // Check for selector match\r
17525                         if (format.selector)\r
17526                                 return dom.is(node, format.selector);\r
17527                 };\r
17528 \r
17529                 function isEq(str1, str2) {\r
17530                         str1 = str1 || '';\r
17531                         str2 = str2 || '';\r
17532 \r
17533                         str1 = '' + (str1.nodeName || str1);\r
17534                         str2 = '' + (str2.nodeName || str2);\r
17535 \r
17536                         return str1.toLowerCase() == str2.toLowerCase();\r
17537                 };\r
17538 \r
17539                 function getStyle(node, name) {\r
17540                         var styleVal = dom.getStyle(node, name);\r
17541 \r
17542                         // Force the format to hex\r
17543                         if (name == 'color' || name == 'backgroundColor')\r
17544                                 styleVal = dom.toHex(styleVal);\r
17545 \r
17546                         // Opera will return bold as 700\r
17547                         if (name == 'fontWeight' && styleVal == 700)\r
17548                                 styleVal = 'bold';\r
17549 \r
17550                         return '' + styleVal;\r
17551                 };\r
17552 \r
17553                 function replaceVars(value, vars) {\r
17554                         if (typeof(value) != "string")\r
17555                                 value = value(vars);\r
17556                         else if (vars) {\r
17557                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
17558                                         return vars[name] || str;\r
17559                                 });\r
17560                         }\r
17561 \r
17562                         return value;\r
17563                 };\r
17564 \r
17565                 function isWhiteSpaceNode(node) {\r
17566                         return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);\r
17567                 };\r
17568 \r
17569                 function wrap(node, name, attrs) {\r
17570                         var wrapper = dom.create(name, attrs);\r
17571 \r
17572                         node.parentNode.insertBefore(wrapper, node);\r
17573                         wrapper.appendChild(node);\r
17574 \r
17575                         return wrapper;\r
17576                 };\r
17577 \r
17578                 function expandRng(rng, format, remove) {\r
17579                         var sibling, lastIdx, leaf, endPoint,\r
17580                                 startContainer = rng.startContainer,\r
17581                                 startOffset = rng.startOffset,\r
17582                                 endContainer = rng.endContainer,\r
17583                                 endOffset = rng.endOffset;\r
17584 \r
17585                         // This function walks up the tree if there is no siblings before/after the node\r
17586                         function findParentContainer(start) {\r
17587                                 var container, parent, child, sibling, siblingName, root;\r
17588 \r
17589                                 container = parent = start ? startContainer : endContainer;\r
17590                                 siblingName = start ? 'previousSibling' : 'nextSibling';\r
17591                                 root = dom.getRoot();\r
17592 \r
17593                                 function isBogusBr(node) {\r
17594                                         return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;\r
17595                                 };\r
17596 \r
17597                                 // If it's a text node and the offset is inside the text\r
17598                                 if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {\r
17599                                         if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {\r
17600                                                 return container;\r
17601                                         }\r
17602                                 }\r
17603 \r
17604                                 for (;;) {\r
17605                                         // Stop expanding on block elements\r
17606                                         if (!format[0].block_expand && isBlock(parent))\r
17607                                                 return parent;\r
17608 \r
17609                                         // Walk left/right\r
17610                                         for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {\r
17611                                                 if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {\r
17612                                                         return parent;\r
17613                                                 }\r
17614                                         }\r
17615 \r
17616                                         // Check if we can move up are we at root level or body level\r
17617                                         if (parent.parentNode == root) {\r
17618                                                 container = parent;\r
17619                                                 break;\r
17620                                         }\r
17621 \r
17622                                         parent = parent.parentNode;\r
17623                                 }\r
17624 \r
17625                                 return container;\r
17626                         };\r
17627 \r
17628                         // This function walks down the tree to find the leaf at the selection.\r
17629                         // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.\r
17630                         function findLeaf(node, offset) {\r
17631                                 if (offset === undef)\r
17632                                         offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
17633                                 while (node && node.hasChildNodes()) {\r
17634                                         node = node.childNodes[offset];\r
17635                                         if (node)\r
17636                                                 offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
17637                                 }\r
17638                                 return { node: node, offset: offset };\r
17639                         }\r
17640 \r
17641                         // If index based start position then resolve it\r
17642                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
17643                                 lastIdx = startContainer.childNodes.length - 1;\r
17644                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
17645 \r
17646                                 if (startContainer.nodeType == 3)\r
17647                                         startOffset = 0;\r
17648                         }\r
17649 \r
17650                         // If index based end position then resolve it\r
17651                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
17652                                 lastIdx = endContainer.childNodes.length - 1;\r
17653                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
17654 \r
17655                                 if (endContainer.nodeType == 3)\r
17656                                         endOffset = endContainer.nodeValue.length;\r
17657                         }\r
17658 \r
17659                         // Expands the node to the closes contentEditable false element if it exists\r
17660                         function findParentContentEditable(node) {\r
17661                                 var parent = node;\r
17662 \r
17663                                 while (parent) {\r
17664                                         if (parent.nodeType === 1 && getContentEditable(parent)) {\r
17665                                                 return getContentEditable(parent) === "false" ? parent : node;\r
17666                                         }\r
17667 \r
17668                                         parent = parent.parentNode;\r
17669                                 }\r
17670 \r
17671                                 return node;\r
17672                         };\r
17673 \r
17674                         function findWordEndPoint(container, offset, start) {\r
17675                                 var walker, node, pos, lastTextNode;\r
17676 \r
17677                                 function findSpace(node, offset) {\r
17678                                         var pos, pos2, str = node.nodeValue;\r
17679 \r
17680                                         if (typeof(offset) == "undefined") {\r
17681                                                 offset = start ? str.length : 0;\r
17682                                         }\r
17683 \r
17684                                         if (start) {\r
17685                                                 pos = str.lastIndexOf(' ', offset);\r
17686                                                 pos2 = str.lastIndexOf('\u00a0', offset);\r
17687                                                 pos = pos > pos2 ? pos : pos2;\r
17688 \r
17689                                                 // Include the space on remove to avoid tag soup\r
17690                                                 if (pos !== -1 && !remove) {\r
17691                                                         pos++;\r
17692                                                 }\r
17693                                         } else {\r
17694                                                 pos = str.indexOf(' ', offset);\r
17695                                                 pos2 = str.indexOf('\u00a0', offset);\r
17696                                                 pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;\r
17697                                         }\r
17698 \r
17699                                         return pos;\r
17700                                 };\r
17701 \r
17702                                 if (container.nodeType === 3) {\r
17703                                         pos = findSpace(container, offset);\r
17704 \r
17705                                         if (pos !== -1) {\r
17706                                                 return {container : container, offset : pos};\r
17707                                         }\r
17708 \r
17709                                         lastTextNode = container;\r
17710                                 }\r
17711 \r
17712                                 // Walk the nodes inside the block\r
17713                                 walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());\r
17714                                 while (node = walker[start ? 'prev' : 'next']()) {\r
17715                                         if (node.nodeType === 3) {\r
17716                                                 lastTextNode = node;\r
17717                                                 pos = findSpace(node);\r
17718 \r
17719                                                 if (pos !== -1) {\r
17720                                                         return {container : node, offset : pos};\r
17721                                                 }\r
17722                                         } else if (isBlock(node)) {\r
17723                                                 break;\r
17724                                         }\r
17725                                 }\r
17726 \r
17727                                 if (lastTextNode) {\r
17728                                         if (start) {\r
17729                                                 offset = 0;\r
17730                                         } else {\r
17731                                                 offset = lastTextNode.length;\r
17732                                         }\r
17733 \r
17734                                         return {container: lastTextNode, offset: offset};\r
17735                                 }\r
17736                         };\r
17737 \r
17738                         function findSelectorEndPoint(container, sibling_name) {\r
17739                                 var parents, i, y, curFormat;\r
17740 \r
17741                                 if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name])\r
17742                                         container = container[sibling_name];\r
17743 \r
17744                                 parents = getParents(container);\r
17745                                 for (i = 0; i < parents.length; i++) {\r
17746                                         for (y = 0; y < format.length; y++) {\r
17747                                                 curFormat = format[y];\r
17748 \r
17749                                                 // If collapsed state is set then skip formats that doesn't match that\r
17750                                                 if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)\r
17751                                                         continue;\r
17752 \r
17753                                                 if (dom.is(parents[i], curFormat.selector))\r
17754                                                         return parents[i];\r
17755                                         }\r
17756                                 }\r
17757 \r
17758                                 return container;\r
17759                         };\r
17760 \r
17761                         function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
17762                                 var node;\r
17763 \r
17764                                 // Expand to block of similar type\r
17765                                 if (!format[0].wrapper)\r
17766                                         node = dom.getParent(container, format[0].block);\r
17767 \r
17768                                 // Expand to first wrappable block element or any block element\r
17769                                 if (!node)\r
17770                                         node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isTextBlock);\r
17771 \r
17772                                 // Exclude inner lists from wrapping\r
17773                                 if (node && format[0].wrapper)\r
17774                                         node = getParents(node, 'ul,ol').reverse()[0] || node;\r
17775 \r
17776                                 // Didn't find a block element look for first/last wrappable element\r
17777                                 if (!node) {\r
17778                                         node = container;\r
17779 \r
17780                                         while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
17781                                                 node = node[sibling_name];\r
17782 \r
17783                                                 // Break on BR but include it will be removed later on\r
17784                                                 // we can't remove it now since we need to check if it can be wrapped\r
17785                                                 if (isEq(node, 'br'))\r
17786                                                         break;\r
17787                                         }\r
17788                                 }\r
17789 \r
17790                                 return node || container;\r
17791                         };\r
17792 \r
17793                         // Expand to closest contentEditable element\r
17794                         startContainer = findParentContentEditable(startContainer);\r
17795                         endContainer = findParentContentEditable(endContainer);\r
17796 \r
17797                         // Exclude bookmark nodes if possible\r
17798                         if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {\r
17799                                 startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;\r
17800                                 startContainer = startContainer.nextSibling || startContainer;\r
17801 \r
17802                                 if (startContainer.nodeType == 3)\r
17803                                         startOffset = 0;\r
17804                         }\r
17805 \r
17806                         if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {\r
17807                                 endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;\r
17808                                 endContainer = endContainer.previousSibling || endContainer;\r
17809 \r
17810                                 if (endContainer.nodeType == 3)\r
17811                                         endOffset = endContainer.length;\r
17812                         }\r
17813 \r
17814                         if (format[0].inline) {\r
17815                                 if (rng.collapsed) {\r
17816                                         // Expand left to closest word boundery\r
17817                                         endPoint = findWordEndPoint(startContainer, startOffset, true);\r
17818                                         if (endPoint) {\r
17819                                                 startContainer = endPoint.container;\r
17820                                                 startOffset = endPoint.offset;\r
17821                                         }\r
17822 \r
17823                                         // Expand right to closest word boundery\r
17824                                         endPoint = findWordEndPoint(endContainer, endOffset);\r
17825                                         if (endPoint) {\r
17826                                                 endContainer = endPoint.container;\r
17827                                                 endOffset = endPoint.offset;\r
17828                                         }\r
17829                                 }\r
17830 \r
17831                                 // Avoid applying formatting to a trailing space.\r
17832                                 leaf = findLeaf(endContainer, endOffset);\r
17833                                 if (leaf.node) {\r
17834                                         while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)\r
17835                                                 leaf = findLeaf(leaf.node.previousSibling);\r
17836 \r
17837                                         if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&\r
17838                                                         leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {\r
17839 \r
17840                                                 if (leaf.offset > 1) {\r
17841                                                         endContainer = leaf.node;\r
17842                                                         endContainer.splitText(leaf.offset - 1);\r
17843                                                 }\r
17844                                         }\r
17845                                 }\r
17846                         }\r
17847 \r
17848                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
17849                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
17850                         // This will reduce the number of wrapper elements that needs to be created\r
17851                         // Move start point up the tree\r
17852                         if (format[0].inline || format[0].block_expand) {\r
17853                                 if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {\r
17854                                         startContainer = findParentContainer(true);\r
17855                                 }\r
17856 \r
17857                                 if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {\r
17858                                         endContainer = findParentContainer();\r
17859                                 }\r
17860                         }\r
17861 \r
17862                         // Expand start/end container to matching selector\r
17863                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
17864                                 // Find new startContainer/endContainer if there is better one\r
17865                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
17866                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
17867                         }\r
17868 \r
17869                         // Expand start/end container to matching block element or text node\r
17870                         if (format[0].block || format[0].selector) {\r
17871                                 // Find new startContainer/endContainer if there is better one\r
17872                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
17873                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
17874 \r
17875                                 // Non block element then try to expand up the leaf\r
17876                                 if (format[0].block) {\r
17877                                         if (!isBlock(startContainer))\r
17878                                                 startContainer = findParentContainer(true);\r
17879 \r
17880                                         if (!isBlock(endContainer))\r
17881                                                 endContainer = findParentContainer();\r
17882                                 }\r
17883                         }\r
17884 \r
17885                         // Setup index for startContainer\r
17886                         if (startContainer.nodeType == 1) {\r
17887                                 startOffset = nodeIndex(startContainer);\r
17888                                 startContainer = startContainer.parentNode;\r
17889                         }\r
17890 \r
17891                         // Setup index for endContainer\r
17892                         if (endContainer.nodeType == 1) {\r
17893                                 endOffset = nodeIndex(endContainer) + 1;\r
17894                                 endContainer = endContainer.parentNode;\r
17895                         }\r
17896 \r
17897                         // Return new range like object\r
17898                         return {\r
17899                                 startContainer : startContainer,\r
17900                                 startOffset : startOffset,\r
17901                                 endContainer : endContainer,\r
17902                                 endOffset : endOffset\r
17903                         };\r
17904                 }\r
17905 \r
17906                 function removeFormat(format, vars, node, compare_node) {\r
17907                         var i, attrs, stylesModified;\r
17908 \r
17909                         // Check if node matches format\r
17910                         if (!matchName(node, format))\r
17911                                 return FALSE;\r
17912 \r
17913                         // Should we compare with format attribs and styles\r
17914                         if (format.remove != 'all') {\r
17915                                 // Remove styles\r
17916                                 each(format.styles, function(value, name) {\r
17917                                         value = replaceVars(value, vars);\r
17918 \r
17919                                         // Indexed array\r
17920                                         if (typeof(name) === 'number') {\r
17921                                                 name = value;\r
17922                                                 compare_node = 0;\r
17923                                         }\r
17924 \r
17925                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
17926                                                 dom.setStyle(node, name, '');\r
17927 \r
17928                                         stylesModified = 1;\r
17929                                 });\r
17930 \r
17931                                 // Remove style attribute if it's empty\r
17932                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
17933                                         node.removeAttribute('style');\r
17934                                         node.removeAttribute('data-mce-style');\r
17935                                 }\r
17936 \r
17937                                 // Remove attributes\r
17938                                 each(format.attributes, function(value, name) {\r
17939                                         var valueOut;\r
17940 \r
17941                                         value = replaceVars(value, vars);\r
17942 \r
17943                                         // Indexed array\r
17944                                         if (typeof(name) === 'number') {\r
17945                                                 name = value;\r
17946                                                 compare_node = 0;\r
17947                                         }\r
17948 \r
17949                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
17950                                                 // Keep internal classes\r
17951                                                 if (name == 'class') {\r
17952                                                         value = dom.getAttrib(node, name);\r
17953                                                         if (value) {\r
17954                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
17955                                                                 valueOut = '';\r
17956                                                                 each(value.split(/\s+/), function(cls) {\r
17957                                                                         if (/mce\w+/.test(cls))\r
17958                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
17959                                                                 });\r
17960 \r
17961                                                                 // We got some internal classes left\r
17962                                                                 if (valueOut) {\r
17963                                                                         dom.setAttrib(node, name, valueOut);\r
17964                                                                         return;\r
17965                                                                 }\r
17966                                                         }\r
17967                                                 }\r
17968 \r
17969                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
17970                                                 if (name == "class")\r
17971                                                         node.removeAttribute('className');\r
17972 \r
17973                                                 // Remove mce prefixed attributes\r
17974                                                 if (MCE_ATTR_RE.test(name))\r
17975                                                         node.removeAttribute('data-mce-' + name);\r
17976 \r
17977                                                 node.removeAttribute(name);\r
17978                                         }\r
17979                                 });\r
17980 \r
17981                                 // Remove classes\r
17982                                 each(format.classes, function(value) {\r
17983                                         value = replaceVars(value, vars);\r
17984 \r
17985                                         if (!compare_node || dom.hasClass(compare_node, value))\r
17986                                                 dom.removeClass(node, value);\r
17987                                 });\r
17988 \r
17989                                 // Check for non internal attributes\r
17990                                 attrs = dom.getAttribs(node);\r
17991                                 for (i = 0; i < attrs.length; i++) {\r
17992                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
17993                                                 return FALSE;\r
17994                                 }\r
17995                         }\r
17996 \r
17997                         // Remove the inline child if it's empty for example <b> or <span>\r
17998                         if (format.remove != 'none') {\r
17999                                 removeNode(node, format);\r
18000                                 return TRUE;\r
18001                         }\r
18002                 };\r
18003 \r
18004                 function removeNode(node, format) {\r
18005                         var parentNode = node.parentNode, rootBlockElm;\r
18006 \r
18007                         function find(node, next, inc) {\r
18008                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
18009 \r
18010                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
18011                         };\r
18012 \r
18013                         if (format.block) {\r
18014                                 if (!forcedRootBlock) {\r
18015                                         // Append BR elements if needed before we remove the block\r
18016                                         if (isBlock(node) && !isBlock(parentNode)) {\r
18017                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
18018                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
18019 \r
18020                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
18021                                                         node.appendChild(dom.create('br'));\r
18022                                         }\r
18023                                 } else {\r
18024                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
18025                                         if (parentNode == dom.getRoot()) {\r
18026                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
18027                                                         each(tinymce.grep(node.childNodes), function(node) {\r
18028                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
18029                                                                         if (!rootBlockElm)\r
18030                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
18031                                                                         else\r
18032                                                                                 rootBlockElm.appendChild(node);\r
18033                                                                 } else\r
18034                                                                         rootBlockElm = 0;\r
18035                                                         });\r
18036                                                 }\r
18037                                         }\r
18038                                 }\r
18039                         }\r
18040 \r
18041                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
18042                         if (format.selector && format.inline && !isEq(format.inline, node))\r
18043                                 return;\r
18044 \r
18045                         dom.remove(node, 1);\r
18046                 };\r
18047 \r
18048                 function getNonWhiteSpaceSibling(node, next, inc) {\r
18049                         if (node) {\r
18050                                 next = next ? 'nextSibling' : 'previousSibling';\r
18051 \r
18052                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
18053                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
18054                                                 return node;\r
18055                                 }\r
18056                         }\r
18057                 };\r
18058 \r
18059                 function isBookmarkNode(node) {\r
18060                         return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';\r
18061                 };\r
18062 \r
18063                 function mergeSiblings(prev, next) {\r
18064                         var marker, sibling, tmpSibling;\r
18065 \r
18066                         function compareElements(node1, node2) {\r
18067                                 // Not the same name\r
18068                                 if (node1.nodeName != node2.nodeName)\r
18069                                         return FALSE;\r
18070 \r
18071                                 function getAttribs(node) {\r
18072                                         var attribs = {};\r
18073 \r
18074                                         each(dom.getAttribs(node), function(attr) {\r
18075                                                 var name = attr.nodeName.toLowerCase();\r
18076 \r
18077                                                 // Don't compare internal attributes or style\r
18078                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
18079                                                         attribs[name] = dom.getAttrib(node, name);\r
18080                                         });\r
18081 \r
18082                                         return attribs;\r
18083                                 };\r
18084 \r
18085                                 function compareObjects(obj1, obj2) {\r
18086                                         var value, name;\r
18087 \r
18088                                         for (name in obj1) {\r
18089                                                 // Obj1 has item obj2 doesn't have\r
18090                                                 if (obj1.hasOwnProperty(name)) {\r
18091                                                         value = obj2[name];\r
18092 \r
18093                                                         // Obj2 doesn't have obj1 item\r
18094                                                         if (value === undef)\r
18095                                                                 return FALSE;\r
18096 \r
18097                                                         // Obj2 item has a different value\r
18098                                                         if (obj1[name] != value)\r
18099                                                                 return FALSE;\r
18100 \r
18101                                                         // Delete similar value\r
18102                                                         delete obj2[name];\r
18103                                                 }\r
18104                                         }\r
18105 \r
18106                                         // Check if obj 2 has something obj 1 doesn't have\r
18107                                         for (name in obj2) {\r
18108                                                 // Obj2 has item obj1 doesn't have\r
18109                                                 if (obj2.hasOwnProperty(name))\r
18110                                                         return FALSE;\r
18111                                         }\r
18112 \r
18113                                         return TRUE;\r
18114                                 };\r
18115 \r
18116                                 // Attribs are not the same\r
18117                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
18118                                         return FALSE;\r
18119 \r
18120                                 // Styles are not the same\r
18121                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
18122                                         return FALSE;\r
18123 \r
18124                                 return TRUE;\r
18125                         };\r
18126 \r
18127                         function findElementSibling(node, sibling_name) {\r
18128                                 for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
18129                                         if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)\r
18130                                                 return node;\r
18131 \r
18132                                         if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
18133                                                 return sibling;\r
18134                                 }\r
18135 \r
18136                                 return node;\r
18137                         };\r
18138 \r
18139                         // Check if next/prev exists and that they are elements\r
18140                         if (prev && next) {\r
18141                                 // If previous sibling is empty then jump over it\r
18142                                 prev = findElementSibling(prev, 'previousSibling');\r
18143                                 next = findElementSibling(next, 'nextSibling');\r
18144 \r
18145                                 // Compare next and previous nodes\r
18146                                 if (compareElements(prev, next)) {\r
18147                                         // Append nodes between\r
18148                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
18149                                                 tmpSibling = sibling;\r
18150                                                 sibling = sibling.nextSibling;\r
18151                                                 prev.appendChild(tmpSibling);\r
18152                                         }\r
18153 \r
18154                                         // Remove next node\r
18155                                         dom.remove(next);\r
18156 \r
18157                                         // Move children into prev node\r
18158                                         each(tinymce.grep(next.childNodes), function(node) {\r
18159                                                 prev.appendChild(node);\r
18160                                         });\r
18161 \r
18162                                         return prev;\r
18163                                 }\r
18164                         }\r
18165 \r
18166                         return next;\r
18167                 };\r
18168 \r
18169                 function getContainer(rng, start) {\r
18170                         var container, offset, lastIdx, walker;\r
18171 \r
18172                         container = rng[start ? 'startContainer' : 'endContainer'];\r
18173                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
18174 \r
18175                         if (container.nodeType == 1) {\r
18176                                 lastIdx = container.childNodes.length - 1;\r
18177 \r
18178                                 if (!start && offset)\r
18179                                         offset--;\r
18180 \r
18181                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
18182                         }\r
18183 \r
18184                         // If start text node is excluded then walk to the next node\r
18185                         if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {\r
18186                                 container = new TreeWalker(container, ed.getBody()).next() || container;\r
18187                         }\r
18188 \r
18189                         // If end text node is excluded then walk to the previous node\r
18190                         if (container.nodeType === 3 && !start && offset === 0) {\r
18191                                 container = new TreeWalker(container, ed.getBody()).prev() || container;\r
18192                         }\r
18193 \r
18194                         return container;\r
18195                 };\r
18196 \r
18197                 function performCaretAction(type, name, vars) {\r
18198                         var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;\r
18199 \r
18200                         // Creates a caret container bogus element\r
18201                         function createCaretContainer(fill) {\r
18202                                 var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});\r
18203 \r
18204                                 if (fill) {\r
18205                                         caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));\r
18206                                 }\r
18207 \r
18208                                 return caretContainer;\r
18209                         };\r
18210 \r
18211                         function isCaretContainerEmpty(node, nodes) {\r
18212                                 while (node) {\r
18213                                         if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {\r
18214                                                 return false;\r
18215                                         }\r
18216 \r
18217                                         // Collect nodes\r
18218                                         if (nodes && node.nodeType === 1) {\r
18219                                                 nodes.push(node);\r
18220                                         }\r
18221 \r
18222                                         node = node.firstChild;\r
18223                                 }\r
18224 \r
18225                                 return true;\r
18226                         };\r
18227                         \r
18228                         // Returns any parent caret container element\r
18229                         function getParentCaretContainer(node) {\r
18230                                 while (node) {\r
18231                                         if (node.id === caretContainerId) {\r
18232                                                 return node;\r
18233                                         }\r
18234 \r
18235                                         node = node.parentNode;\r
18236                                 }\r
18237                         };\r
18238 \r
18239                         // Finds the first text node in the specified node\r
18240                         function findFirstTextNode(node) {\r
18241                                 var walker;\r
18242 \r
18243                                 if (node) {\r
18244                                         walker = new TreeWalker(node, node);\r
18245 \r
18246                                         for (node = walker.current(); node; node = walker.next()) {\r
18247                                                 if (node.nodeType === 3) {\r
18248                                                         return node;\r
18249                                                 }\r
18250                                         }\r
18251                                 }\r
18252                         };\r
18253 \r
18254                         // Removes the caret container for the specified node or all on the current document\r
18255                         function removeCaretContainer(node, move_caret) {\r
18256                                 var child, rng;\r
18257 \r
18258                                 if (!node) {\r
18259                                         node = getParentCaretContainer(selection.getStart());\r
18260 \r
18261                                         if (!node) {\r
18262                                                 while (node = dom.get(caretContainerId)) {\r
18263                                                         removeCaretContainer(node, false);\r
18264                                                 }\r
18265                                         }\r
18266                                 } else {\r
18267                                         rng = selection.getRng(true);\r
18268 \r
18269                                         if (isCaretContainerEmpty(node)) {\r
18270                                                 if (move_caret !== false) {\r
18271                                                         rng.setStartBefore(node);\r
18272                                                         rng.setEndBefore(node);\r
18273                                                 }\r
18274 \r
18275                                                 dom.remove(node);\r
18276                                         } else {\r
18277                                                 child = findFirstTextNode(node);\r
18278 \r
18279                                                 if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {\r
18280                                                         child = child.deleteData(0, 1);\r
18281                                                 }\r
18282 \r
18283                                                 dom.remove(node, 1);\r
18284                                         }\r
18285 \r
18286                                         selection.setRng(rng);\r
18287                                 }\r
18288                         };\r
18289                         \r
18290                         // Applies formatting to the caret postion\r
18291                         function applyCaretFormat() {\r
18292                                 var rng, caretContainer, textNode, offset, bookmark, container, text;\r
18293 \r
18294                                 rng = selection.getRng(true);\r
18295                                 offset = rng.startOffset;\r
18296                                 container = rng.startContainer;\r
18297                                 text = container.nodeValue;\r
18298 \r
18299                                 caretContainer = getParentCaretContainer(selection.getStart());\r
18300                                 if (caretContainer) {\r
18301                                         textNode = findFirstTextNode(caretContainer);\r
18302                                 }\r
18303 \r
18304                                 // Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character\r
18305                                 if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {\r
18306                                         // Get bookmark of caret position\r
18307                                         bookmark = selection.getBookmark();\r
18308 \r
18309                                         // Collapse bookmark range (WebKit)\r
18310                                         rng.collapse(true);\r
18311 \r
18312                                         // Expand the range to the closest word and split it at those points\r
18313                                         rng = expandRng(rng, get(name));\r
18314                                         rng = rangeUtils.split(rng);\r
18315 \r
18316                                         // Apply the format to the range\r
18317                                         apply(name, vars, rng);\r
18318 \r
18319                                         // Move selection back to caret position\r
18320                                         selection.moveToBookmark(bookmark);\r
18321                                 } else {\r
18322                                         if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {\r
18323                                                 caretContainer = createCaretContainer(true);\r
18324                                                 textNode = caretContainer.firstChild;\r
18325 \r
18326                                                 rng.insertNode(caretContainer);\r
18327                                                 offset = 1;\r
18328 \r
18329                                                 apply(name, vars, caretContainer);\r
18330                                         } else {\r
18331                                                 apply(name, vars, caretContainer);\r
18332                                         }\r
18333 \r
18334                                         // Move selection to text node\r
18335                                         selection.setCursorLocation(textNode, offset);\r
18336                                 }\r
18337                         };\r
18338 \r
18339                         function removeCaretFormat() {\r
18340                                 var rng = selection.getRng(true), container, offset, bookmark,\r
18341                                         hasContentAfter, node, formatNode, parents = [], i, caretContainer;\r
18342 \r
18343                                 container = rng.startContainer;\r
18344                                 offset = rng.startOffset;\r
18345                                 node = container;\r
18346 \r
18347                                 if (container.nodeType == 3) {\r
18348                                         if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {\r
18349                                                 hasContentAfter = true;\r
18350                                         }\r
18351 \r
18352                                         node = node.parentNode;\r
18353                                 }\r
18354 \r
18355                                 while (node) {\r
18356                                         if (matchNode(node, name, vars)) {\r
18357                                                 formatNode = node;\r
18358                                                 break;\r
18359                                         }\r
18360 \r
18361                                         if (node.nextSibling) {\r
18362                                                 hasContentAfter = true;\r
18363                                         }\r
18364 \r
18365                                         parents.push(node);\r
18366                                         node = node.parentNode;\r
18367                                 }\r
18368 \r
18369                                 // Node doesn't have the specified format\r
18370                                 if (!formatNode) {\r
18371                                         return;\r
18372                                 }\r
18373 \r
18374                                 // Is there contents after the caret then remove the format on the element\r
18375                                 if (hasContentAfter) {\r
18376                                         // Get bookmark of caret position\r
18377                                         bookmark = selection.getBookmark();\r
18378 \r
18379                                         // Collapse bookmark range (WebKit)\r
18380                                         rng.collapse(true);\r
18381 \r
18382                                         // Expand the range to the closest word and split it at those points\r
18383                                         rng = expandRng(rng, get(name), true);\r
18384                                         rng = rangeUtils.split(rng);\r
18385 \r
18386                                         // Remove the format from the range\r
18387                                         remove(name, vars, rng);\r
18388 \r
18389                                         // Move selection back to caret position\r
18390                                         selection.moveToBookmark(bookmark);\r
18391                                 } else {\r
18392                                         caretContainer = createCaretContainer();\r
18393 \r
18394                                         node = caretContainer;\r
18395                                         for (i = parents.length - 1; i >= 0; i--) {\r
18396                                                 node.appendChild(dom.clone(parents[i], false));\r
18397                                                 node = node.firstChild;\r
18398                                         }\r
18399 \r
18400                                         // Insert invisible character into inner most format element\r
18401                                         node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));\r
18402                                         node = node.firstChild;\r
18403 \r
18404                                         var block = dom.getParent(formatNode, isTextBlock);\r
18405 \r
18406                                         if (block && dom.isEmpty(block)) {\r
18407                                                 // Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>\r
18408                                                 formatNode.parentNode.replaceChild(caretContainer, formatNode);\r
18409                                         } else {\r
18410                                                 // Insert caret container after the formated node\r
18411                                                 dom.insertAfter(caretContainer, formatNode);\r
18412                                         }\r
18413 \r
18414                                         // Move selection to text node\r
18415                                         selection.setCursorLocation(node, 1);\r
18416 \r
18417                                         // If the formatNode is empty, we can remove it safely. \r
18418                                         if (dom.isEmpty(formatNode)) {\r
18419                                                 dom.remove(formatNode);\r
18420                                         }\r
18421                                 }\r
18422                         };\r
18423 \r
18424                         // Checks if the parent caret container node isn't empty if that is the case it\r
18425                         // will remove the bogus state on all children that isn't empty\r
18426                         function unmarkBogusCaretParents() {\r
18427                                 var i, caretContainer, node;\r
18428 \r
18429                                 caretContainer = getParentCaretContainer(selection.getStart());\r
18430                                 if (caretContainer && !dom.isEmpty(caretContainer)) {\r
18431                                         tinymce.walk(caretContainer, function(node) {\r
18432                                                 if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {\r
18433                                                         dom.setAttrib(node, 'data-mce-bogus', null);\r
18434                                                 }\r
18435                                         }, 'childNodes');\r
18436                                 }\r
18437                         };\r
18438 \r
18439                         // Only bind the caret events once\r
18440                         if (!self._hasCaretEvents) {\r
18441                                 // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements\r
18442                                 ed.onBeforeGetContent.addToTop(function() {\r
18443                                         var nodes = [], i;\r
18444 \r
18445                                         if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {\r
18446                                                 // Mark children\r
18447                                                 i = nodes.length;\r
18448                                                 while (i--) {\r
18449                                                         dom.setAttrib(nodes[i], 'data-mce-bogus', '1');\r
18450                                                 }\r
18451                                         }\r
18452                                 });\r
18453 \r
18454                                 // Remove caret container on mouse up and on key up\r
18455                                 tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {\r
18456                                         ed[name].addToTop(function() {\r
18457                                                 removeCaretContainer();\r
18458                                                 unmarkBogusCaretParents();\r
18459                                         });\r
18460                                 });\r
18461 \r
18462                                 // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys\r
18463                                 ed.onKeyDown.addToTop(function(ed, e) {\r
18464                                         var keyCode = e.keyCode;\r
18465 \r
18466                                         if (keyCode == 8 || keyCode == 37 || keyCode == 39) {\r
18467                                                 removeCaretContainer(getParentCaretContainer(selection.getStart()));\r
18468                                         }\r
18469 \r
18470                                         unmarkBogusCaretParents();\r
18471                                 });\r
18472 \r
18473                                 // Remove bogus state if they got filled by contents using editor.selection.setContent\r
18474                                 selection.onSetContent.add(unmarkBogusCaretParents);\r
18475 \r
18476                                 self._hasCaretEvents = true;\r
18477                         }\r
18478 \r
18479                         // Do apply or remove caret format\r
18480                         if (type == "apply") {\r
18481                                 applyCaretFormat();\r
18482                         } else {\r
18483                                 removeCaretFormat();\r
18484                         }\r
18485                 };\r
18486 \r
18487                 function moveStart(rng) {\r
18488                         var container = rng.startContainer,\r
18489                                         offset = rng.startOffset, isAtEndOfText,\r
18490                                         walker, node, nodes, tmpNode;\r
18491 \r
18492                         // Convert text node into index if possible\r
18493                         if (container.nodeType == 3 && offset >= container.nodeValue.length) {\r
18494                                 // Get the parent container location and walk from there\r
18495                                 offset = nodeIndex(container);\r
18496                                 container = container.parentNode;\r
18497                                 isAtEndOfText = true;\r
18498                         }\r
18499 \r
18500                         // Move startContainer/startOffset in to a suitable node\r
18501                         if (container.nodeType == 1) {\r
18502                                 nodes = container.childNodes;\r
18503                                 container = nodes[Math.min(offset, nodes.length - 1)];\r
18504                                 walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));\r
18505 \r
18506                                 // If offset is at end of the parent node walk to the next one\r
18507                                 if (offset > nodes.length - 1 || isAtEndOfText)\r
18508                                         walker.next();\r
18509 \r
18510                                 for (node = walker.current(); node; node = walker.next()) {\r
18511                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
18512                                                 // IE has a "neat" feature where it moves the start node into the closest element\r
18513                                                 // we can avoid this by inserting an element before it and then remove it after we set the selection\r
18514                                                 tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
18515                                                 node.parentNode.insertBefore(tmpNode, node);\r
18516 \r
18517                                                 // Set selection and remove tmpNode\r
18518                                                 rng.setStart(node, 0);\r
18519                                                 selection.setRng(rng);\r
18520                                                 dom.remove(tmpNode);\r
18521 \r
18522                                                 return;\r
18523                                         }\r
18524                                 }\r
18525                         }\r
18526                 };\r
18527         };\r
18528 })(tinymce);\r
18529 \r
18530 tinymce.onAddEditor.add(function(tinymce, ed) {\r
18531         var filters, fontSizes, dom, settings = ed.settings;\r
18532 \r
18533         function replaceWithSpan(node, styles) {\r
18534                 tinymce.each(styles, function(value, name) {\r
18535                         if (value)\r
18536                                 dom.setStyle(node, name, value);\r
18537                 });\r
18538 \r
18539                 dom.rename(node, 'span');\r
18540         };\r
18541 \r
18542         function convert(editor, params) {\r
18543                 dom = editor.dom;\r
18544 \r
18545                 if (settings.convert_fonts_to_spans) {\r
18546                         tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
18547                                 filters[node.nodeName.toLowerCase()](ed.dom, node);\r
18548                         });\r
18549                 }\r
18550         };\r
18551 \r
18552         if (settings.inline_styles) {\r
18553                 fontSizes = tinymce.explode(settings.font_size_legacy_values);\r
18554 \r
18555                 filters = {\r
18556                         font : function(dom, node) {\r
18557                                 replaceWithSpan(node, {\r
18558                                         backgroundColor : node.style.backgroundColor,\r
18559                                         color : node.color,\r
18560                                         fontFamily : node.face,\r
18561                                         fontSize : fontSizes[parseInt(node.size, 10) - 1]\r
18562                                 });\r
18563                         },\r
18564 \r
18565                         u : function(dom, node) {\r
18566                                 replaceWithSpan(node, {\r
18567                                         textDecoration : 'underline'\r
18568                                 });\r
18569                         },\r
18570 \r
18571                         strike : function(dom, node) {\r
18572                                 replaceWithSpan(node, {\r
18573                                         textDecoration : 'line-through'\r
18574                                 });\r
18575                         }\r
18576                 };\r
18577 \r
18578                 ed.onPreProcess.add(convert);\r
18579                 ed.onSetContent.add(convert);\r
18580 \r
18581                 ed.onInit.add(function() {\r
18582                         ed.selection.onSetContent.add(convert);\r
18583                 });\r
18584         }\r
18585 });\r
18586 \r
18587 (function(tinymce) {\r
18588         var TreeWalker = tinymce.dom.TreeWalker;\r
18589 \r
18590         tinymce.EnterKey = function(editor) {\r
18591                 var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager, nonEmptyElementsMap = editor.schema.getNonEmptyElements();\r
18592 \r
18593                 function handleEnterKey(evt) {\r
18594                         var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,\r
18595                                 newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;\r
18596 \r
18597                         // Returns true if the block can be split into two blocks or not\r
18598                         function canSplitBlock(node) {\r
18599                                 return node &&\r
18600                                         dom.isBlock(node) &&\r
18601                                         !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&\r
18602                                         !/^(fixed|absolute)/i.test(node.style.position) && \r
18603                                         dom.getContentEditable(node) !== "true";\r
18604                         };\r
18605 \r
18606                         // Renders empty block on IE\r
18607                         function renderBlockOnIE(block) {\r
18608                                 var oldRng;\r
18609 \r
18610                                 if (tinymce.isIE && !tinymce.isIE11 && dom.isBlock(block)) {\r
18611                                         oldRng = selection.getRng();\r
18612                                         block.appendChild(dom.create('span', null, '\u00a0'));\r
18613                                         selection.select(block);\r
18614                                         block.lastChild.outerHTML = '';\r
18615                                         selection.setRng(oldRng);\r
18616                                 }\r
18617                         };\r
18618 \r
18619                         // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>\r
18620                         function trimInlineElementsOnLeftSideOfBlock(block) {\r
18621                                 var node = block, firstChilds = [], i;\r
18622 \r
18623                                 // Find inner most first child ex: <p><i><b>*</b></i></p>\r
18624                                 while (node = node.firstChild) {\r
18625                                         if (dom.isBlock(node)) {\r
18626                                                 return;\r
18627                                         }\r
18628 \r
18629                                         if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {\r
18630                                                 firstChilds.push(node);\r
18631                                         }\r
18632                                 }\r
18633 \r
18634                                 i = firstChilds.length;\r
18635                                 while (i--) {\r
18636                                         node = firstChilds[i];\r
18637                                         if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {\r
18638                                                 dom.remove(node);\r
18639                                         } else {\r
18640                                                 // Remove <a> </a> see #5381\r
18641                                                 if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {\r
18642                                                         dom.remove(node);\r
18643                                                 }\r
18644                                         }\r
18645                                 }\r
18646                         };\r
18647                         \r
18648                         // Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image\r
18649                         function moveToCaretPosition(root) {\r
18650                                 var walker, node, rng, y, viewPort, lastNode = root, tempElm;\r
18651 \r
18652                                 rng = dom.createRng();\r
18653 \r
18654                                 if (root.hasChildNodes()) {\r
18655                                         walker = new TreeWalker(root, root);\r
18656 \r
18657                                         while (node = walker.current()) {\r
18658                                                 if (node.nodeType == 3) {\r
18659                                                         rng.setStart(node, 0);\r
18660                                                         rng.setEnd(node, 0);\r
18661                                                         break;\r
18662                                                 }\r
18663 \r
18664                                                 if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {\r
18665                                                         rng.setStartBefore(node);\r
18666                                                         rng.setEndBefore(node);\r
18667                                                         break;\r
18668                                                 }\r
18669 \r
18670                                                 lastNode = node;\r
18671                                                 node = walker.next();\r
18672                                         }\r
18673 \r
18674                                         if (!node) {\r
18675                                                 rng.setStart(lastNode, 0);\r
18676                                                 rng.setEnd(lastNode, 0);\r
18677                                         }\r
18678                                 } else {\r
18679                                         if (root.nodeName == 'BR') {\r
18680                                                 if (root.nextSibling && dom.isBlock(root.nextSibling)) {\r
18681                                                         // Trick on older IE versions to render the caret before the BR between two lists\r
18682                                                         if (!documentMode || documentMode < 9) {\r
18683                                                                 tempElm = dom.create('br');\r
18684                                                                 root.parentNode.insertBefore(tempElm, root);\r
18685                                                         }\r
18686 \r
18687                                                         rng.setStartBefore(root);\r
18688                                                         rng.setEndBefore(root);\r
18689                                                 } else {\r
18690                                                         rng.setStartAfter(root);\r
18691                                                         rng.setEndAfter(root);\r
18692                                                 }\r
18693                                         } else {\r
18694                                                 rng.setStart(root, 0);\r
18695                                                 rng.setEnd(root, 0);\r
18696                                         }\r
18697                                 }\r
18698 \r
18699                                 selection.setRng(rng);\r
18700 \r
18701                                 // Remove tempElm created for old IE:s\r
18702                                 dom.remove(tempElm);\r
18703 \r
18704                                 viewPort = dom.getViewPort(editor.getWin());\r
18705 \r
18706                                 // 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
18707                                 y = dom.getPos(root).y;\r
18708                                 if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {\r
18709                                         editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks\r
18710                                 }\r
18711                         };\r
18712 \r
18713                         // Creates a new block element by cloning the current one or creating a new one if the name is specified\r
18714                         // This function will also copy any text formatting from the parent block and add it to the new one\r
18715                         function createNewBlock(name) {\r
18716                                 var node = container, block, clonedNode, caretNode;\r
18717 \r
18718                                 block = name || parentBlockName == "TABLE" ? dom.create(name || newBlockName) : parentBlock.cloneNode(false);\r
18719                                 caretNode = block;\r
18720 \r
18721                                 // Clone any parent styles\r
18722                                 if (settings.keep_styles !== false) {\r
18723                                         do {\r
18724                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
18725                                                         // Never clone a caret containers\r
18726                                                         if (node.id == '_mce_caret') {\r
18727                                                                 continue;\r
18728                                                         }\r
18729 \r
18730                                                         clonedNode = node.cloneNode(false);\r
18731                                                         dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique\r
18732 \r
18733                                                         if (block.hasChildNodes()) {\r
18734                                                                 clonedNode.appendChild(block.firstChild);\r
18735                                                                 block.appendChild(clonedNode);\r
18736                                                         } else {\r
18737                                                                 caretNode = clonedNode;\r
18738                                                                 block.appendChild(clonedNode);\r
18739                                                         }\r
18740                                                 }\r
18741                                         } while (node = node.parentNode);\r
18742                                 }\r
18743 \r
18744                                 // BR is needed in empty blocks on non IE browsers\r
18745                                 if (!tinymce.isIE || tinymce.isIE11) {\r
18746                                         caretNode.innerHTML = '<br data-mce-bogus="1">';\r
18747                                 }\r
18748 \r
18749                                 return block;\r
18750                         };\r
18751 \r
18752                         // Returns true/false if the caret is at the start/end of the parent block element\r
18753                         function isCaretAtStartOrEndOfBlock(start) {\r
18754                                 var walker, node, name;\r
18755 \r
18756                                 // Caret is in the middle of a text node like "a|b"\r
18757                                 if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {\r
18758                                         return false;\r
18759                                 }\r
18760 \r
18761                                 // If after the last element in block node edge case for #5091\r
18762                                 if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {\r
18763                                         return true;\r
18764                                 }\r
18765 \r
18766                                 // If the caret if before the first element in parentBlock\r
18767                                 if (start && container.nodeType == 1 && container == parentBlock.firstChild) {\r
18768                                         return true;\r
18769                                 }\r
18770 \r
18771                                 // Caret can be before/after a table\r
18772                                 if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {\r
18773                                         return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);\r
18774                                 }\r
18775 \r
18776                                 // Walk the DOM and look for text nodes or non empty elements\r
18777                                 walker = new TreeWalker(container, parentBlock);\r
18778         \r
18779                                 // If caret is in beginning or end of a text block then jump to the next/previous node\r
18780                                 if (container.nodeType == 3) {\r
18781                                         if (start && offset == 0) {\r
18782                                                 walker.prev();\r
18783                                         } else if (!start && offset == container.nodeValue.length) {\r
18784                                                 walker.next();\r
18785                                         }\r
18786                                 }\r
18787 \r
18788                                 while (node = walker.current()) {\r
18789                                         if (node.nodeType === 1) {\r
18790                                                 // Ignore bogus elements\r
18791                                                 if (!node.getAttribute('data-mce-bogus')) {\r
18792                                                         // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>\r
18793                                                         name = node.nodeName.toLowerCase();\r
18794                                                         if (nonEmptyElementsMap[name] && name !== 'br') {\r
18795                                                                 return false;\r
18796                                                         }\r
18797                                                 }\r
18798                                         } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {\r
18799                                                 return false;\r
18800                                         }\r
18801 \r
18802                                         if (start) {\r
18803                                                 walker.prev();\r
18804                                         } else {\r
18805                                                 walker.next();\r
18806                                         }\r
18807                                 }\r
18808 \r
18809                                 return true;\r
18810                         };\r
18811 \r
18812                         // Wraps any text nodes or inline elements in the specified forced root block name\r
18813                         function wrapSelfAndSiblingsInDefaultBlock(container, offset) {\r
18814                                 var newBlock, parentBlock, startNode, node, next, blockName = newBlockName || 'P';\r
18815 \r
18816                                 // Not in a block element or in a table cell or caption\r
18817                                 parentBlock = dom.getParent(container, dom.isBlock);\r
18818                                 if (!parentBlock || !canSplitBlock(parentBlock)) {\r
18819                                         parentBlock = parentBlock || editableRoot;\r
18820 \r
18821                                         if (!parentBlock.hasChildNodes()) {\r
18822                                                 newBlock = dom.create(blockName);\r
18823                                                 parentBlock.appendChild(newBlock);\r
18824                                                 rng.setStart(newBlock, 0);\r
18825                                                 rng.setEnd(newBlock, 0);\r
18826                                                 return newBlock;\r
18827                                         }\r
18828 \r
18829                                         // Find parent that is the first child of parentBlock\r
18830                                         node = container;\r
18831                                         while (node.parentNode != parentBlock) {\r
18832                                                 node = node.parentNode;\r
18833                                         }\r
18834 \r
18835                                         // Loop left to find start node start wrapping at\r
18836                                         while (node && !dom.isBlock(node)) {\r
18837                                                 startNode = node;\r
18838                                                 node = node.previousSibling;\r
18839                                         }\r
18840 \r
18841                                         if (startNode) {\r
18842                                                 newBlock = dom.create(blockName);\r
18843                                                 startNode.parentNode.insertBefore(newBlock, startNode);\r
18844 \r
18845                                                 // Start wrapping until we hit a block\r
18846                                                 node = startNode;\r
18847                                                 while (node && !dom.isBlock(node)) {\r
18848                                                         next = node.nextSibling;\r
18849                                                         newBlock.appendChild(node);\r
18850                                                         node = next;\r
18851                                                 }\r
18852 \r
18853                                                 // Restore range to it's past location\r
18854                                                 rng.setStart(container, offset);\r
18855                                                 rng.setEnd(container, offset);\r
18856                                         }\r
18857                                 }\r
18858 \r
18859                                 return container;\r
18860                         };\r
18861 \r
18862                         // Inserts a block or br before/after or in the middle of a split list of the LI is empty\r
18863                         function handleEmptyListItem() {\r
18864                                 function isFirstOrLastLi(first) {\r
18865                                         var node = containerBlock[first ? 'firstChild' : 'lastChild'];\r
18866 \r
18867                                         // Find first/last element since there might be whitespace there\r
18868                                         while (node) {\r
18869                                                 if (node.nodeType == 1) {\r
18870                                                         break;\r
18871                                                 }\r
18872 \r
18873                                                 node = node[first ? 'nextSibling' : 'previousSibling'];\r
18874                                         }\r
18875 \r
18876                                         return node === parentBlock;\r
18877                                 };\r
18878 \r
18879                                 newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');\r
18880 \r
18881                                 if (isFirstOrLastLi(true) && isFirstOrLastLi()) {\r
18882                                         // Is first and last list item then replace the OL/UL with a text block\r
18883                                         dom.replace(newBlock, containerBlock);\r
18884                                 } else if (isFirstOrLastLi(true)) {\r
18885                                         // First LI in list then remove LI and add text block before list\r
18886                                         containerBlock.parentNode.insertBefore(newBlock, containerBlock);\r
18887                                 } else if (isFirstOrLastLi()) {\r
18888                                         // Last LI in list then temove LI and add text block after list\r
18889                                         dom.insertAfter(newBlock, containerBlock);\r
18890                                         renderBlockOnIE(newBlock);\r
18891                                 } else {\r
18892                                         // Middle LI in list the split the list and insert a text block in the middle\r
18893                                         // Extract after fragment and insert it after the current block\r
18894                                         tmpRng = rng.cloneRange();\r
18895                                         tmpRng.setStartAfter(parentBlock);\r
18896                                         tmpRng.setEndAfter(containerBlock);\r
18897                                         fragment = tmpRng.extractContents();\r
18898                                         dom.insertAfter(fragment, containerBlock);\r
18899                                         dom.insertAfter(newBlock, containerBlock);\r
18900                                 }\r
18901 \r
18902                                 dom.remove(parentBlock);\r
18903                                 moveToCaretPosition(newBlock);\r
18904                                 undoManager.add();\r
18905                         };\r
18906 \r
18907                         // Walks the parent block to the right and look for any contents\r
18908                         function hasRightSideContent() {\r
18909                                 var walker = new TreeWalker(container, parentBlock), node;\r
18910 \r
18911                                 while (node = walker.next()) {\r
18912                                         if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {\r
18913                                                 return true;\r
18914                                         }\r
18915                                 }\r
18916                         }\r
18917 \r
18918                         // Inserts a BR element if the forced_root_block option is set to false or empty string\r
18919                         function insertBr() {\r
18920                                 var brElm, extraBr, marker;\r
18921 \r
18922                                 if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {\r
18923                                         // Insert extra BR element at the end block elements\r
18924                                         if ((!tinymce.isIE || tinymce.isIE11) && !hasRightSideContent()) {\r
18925                                                 brElm = dom.create('br');\r
18926                                                 rng.insertNode(brElm);\r
18927                                                 rng.setStartAfter(brElm);\r
18928                                                 rng.setEndAfter(brElm);\r
18929                                                 extraBr = true;\r
18930                                         }\r
18931                                 }\r
18932 \r
18933                                 brElm = dom.create('br');\r
18934                                 rng.insertNode(brElm);\r
18935 \r
18936                                 // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it\r
18937                                 if ((tinymce.isIE && !tinymce.isIE11) && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {\r
18938                                         brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);\r
18939                                 }\r
18940 \r
18941                                 // Insert temp marker and scroll to that\r
18942                                 marker = dom.create('span', {}, '&nbsp;');\r
18943                                 brElm.parentNode.insertBefore(marker, brElm);\r
18944                                 selection.scrollIntoView(marker);\r
18945                                 dom.remove(marker);\r
18946 \r
18947                                 if (!extraBr) {\r
18948                                         rng.setStartAfter(brElm);\r
18949                                         rng.setEndAfter(brElm);\r
18950                                 } else {\r
18951                                         rng.setStartBefore(brElm);\r
18952                                         rng.setEndBefore(brElm);\r
18953                                 }\r
18954 \r
18955                                 selection.setRng(rng);\r
18956                                 undoManager.add();\r
18957                         };\r
18958 \r
18959                         // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element\r
18960                         function trimLeadingLineBreaks(node) {\r
18961                                 do {\r
18962                                         if (node.nodeType === 3) {\r
18963                                                 node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');\r
18964                                         }\r
18965 \r
18966                                         node = node.firstChild;\r
18967                                 } while (node);\r
18968                         };\r
18969 \r
18970                         function getEditableRoot(node) {\r
18971                                 var root = dom.getRoot(), parent, editableRoot;\r
18972 \r
18973                                 // Get all parents until we hit a non editable parent or the root\r
18974                                 parent = node;\r
18975                                 while (parent !== root && dom.getContentEditable(parent) !== "false") {\r
18976                                         if (dom.getContentEditable(parent) === "true") {\r
18977                                                 editableRoot = parent;\r
18978                                         }\r
18979 \r
18980                                         parent = parent.parentNode;\r
18981                                 }\r
18982                                 \r
18983                                 return parent !== root ? editableRoot : root;\r
18984                         };\r
18985 \r
18986                         // Adds a BR at the end of blocks that only contains an IMG or INPUT since these might be floated and then they won't expand the block\r
18987                         function addBrToBlockIfNeeded(block) {\r
18988                                 var lastChild;\r
18989 \r
18990                                 // IE will render the blocks correctly other browsers needs a BR\r
18991                                 if (!tinymce.isIE || tinymce.isIE11) {\r
18992                                         block.normalize(); // Remove empty text nodes that got left behind by the extract\r
18993 \r
18994                                         // Check if the block is empty or contains a floated last child\r
18995                                         lastChild = block.lastChild;\r
18996                                         if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {\r
18997                                                 dom.add(block, 'br');\r
18998                                         }\r
18999                                 }\r
19000                         };\r
19001 \r
19002                         // Delete any selected contents\r
19003                         if (!rng.collapsed) {\r
19004                                 editor.execCommand('Delete');\r
19005                                 return;\r
19006                         }\r
19007 \r
19008                         // Event is blocked by some other handler for example the lists plugin\r
19009                         if (evt.isDefaultPrevented()) {\r
19010                                 return;\r
19011                         }\r
19012 \r
19013                         // Setup range items and newBlockName\r
19014                         container = rng.startContainer;\r
19015                         offset = rng.startOffset;\r
19016                         newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;\r
19017                         newBlockName = newBlockName ? newBlockName.toUpperCase() : '';\r
19018                         documentMode = dom.doc.documentMode;\r
19019                         shiftKey = evt.shiftKey;\r
19020 \r
19021                         // Resolve node index\r
19022                         if (container.nodeType == 1 && container.hasChildNodes()) {\r
19023                                 isAfterLastNodeInContainer = offset > container.childNodes.length - 1;\r
19024                                 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;\r
19025                                 if (isAfterLastNodeInContainer && container.nodeType == 3) {\r
19026                                         offset = container.nodeValue.length;\r
19027                                 } else {\r
19028                                         offset = 0;\r
19029                                 }\r
19030                         }\r
19031 \r
19032                         // Get editable root node normaly the body element but sometimes a div or span\r
19033                         editableRoot = getEditableRoot(container);\r
19034 \r
19035                         // If there is no editable root then enter is done inside a contentEditable false element\r
19036                         if (!editableRoot) {\r
19037                                 return;\r
19038                         }\r
19039 \r
19040                         undoManager.beforeChange();\r
19041 \r
19042                         // If editable root isn't block nor the root of the editor\r
19043                         if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {\r
19044                                 if (!newBlockName || shiftKey) {\r
19045                                         insertBr();\r
19046                                 }\r
19047 \r
19048                                 return;\r
19049                         }\r
19050 \r
19051                         // Wrap the current node and it's sibling in a default block if it's needed.\r
19052                         // for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>\r
19053                         // This won't happen if root blocks are disabled or the shiftKey is pressed\r
19054                         if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {\r
19055                                 container = wrapSelfAndSiblingsInDefaultBlock(container, offset);\r
19056                         }\r
19057 \r
19058                         // Find parent block and setup empty block paddings\r
19059                         parentBlock = dom.getParent(container, dom.isBlock);\r
19060                         containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;\r
19061 \r
19062                         // Setup block names\r
19063                         parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5\r
19064                         containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5\r
19065 \r
19066                         // Enter inside block contained within a LI then split or insert before/after LI\r
19067                         if (containerBlockName == 'LI' && !evt.ctrlKey) {\r
19068                                 parentBlock = containerBlock;\r
19069                                 parentBlockName = containerBlockName;\r
19070                         }\r
19071 \r
19072                         // Handle enter in LI\r
19073                         if (parentBlockName == 'LI') {\r
19074                                 if (!newBlockName && shiftKey) {\r
19075                                         insertBr();\r
19076                                         return;\r
19077                                 }\r
19078 \r
19079                                 // Handle enter inside an empty list item\r
19080                                 if (dom.isEmpty(parentBlock)) {\r
19081                                         // Let the list plugin or browser handle nested lists for now\r
19082                                         if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {\r
19083                                                 return false;\r
19084                                         }\r
19085 \r
19086                                         handleEmptyListItem();\r
19087                                         return;\r
19088                                 }\r
19089                         }\r
19090 \r
19091                         // Don't split PRE tags but insert a BR instead easier when writing code samples etc\r
19092                         if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {\r
19093                                 if (!shiftKey) {\r
19094                                         insertBr();\r
19095                                         return;\r
19096                                 }\r
19097                         } else {\r
19098                                 // If no root block is configured then insert a BR by default or if the shiftKey is pressed\r
19099                                 if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {\r
19100                                         insertBr();\r
19101                                         return;\r
19102                                 }\r
19103                         }\r
19104 \r
19105                         // Default block name if it's not configured\r
19106                         newBlockName = newBlockName || 'P';\r
19107 \r
19108                         // Insert new block before/after the parent block depending on caret location\r
19109                         if (isCaretAtStartOrEndOfBlock()) {\r
19110                                 // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup\r
19111                                 if (/^(H[1-6]|PRE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {\r
19112                                         newBlock = createNewBlock(newBlockName);\r
19113                                 } else {\r
19114                                         newBlock = createNewBlock();\r
19115                                 }\r
19116 \r
19117                                 // Split the current container block element if enter is pressed inside an empty inner block element\r
19118                                 if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {\r
19119                                         // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P\r
19120                                         newBlock = dom.split(containerBlock, parentBlock);\r
19121                                 } else {\r
19122                                         dom.insertAfter(newBlock, parentBlock);\r
19123                                 }\r
19124 \r
19125                                 moveToCaretPosition(newBlock);\r
19126                         } else if (isCaretAtStartOrEndOfBlock(true)) {\r
19127                                 // Insert new block before\r
19128                                 newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);\r
19129                                 renderBlockOnIE(newBlock);\r
19130                         } else {\r
19131                                 // Extract after fragment and insert it after the current block\r
19132                                 tmpRng = rng.cloneRange();\r
19133                                 tmpRng.setEndAfter(parentBlock);\r
19134                                 fragment = tmpRng.extractContents();\r
19135                                 trimLeadingLineBreaks(fragment);\r
19136                                 newBlock = fragment.firstChild;\r
19137                                 dom.insertAfter(fragment, parentBlock);\r
19138                                 trimInlineElementsOnLeftSideOfBlock(newBlock);\r
19139                                 addBrToBlockIfNeeded(parentBlock);\r
19140                                 moveToCaretPosition(newBlock);\r
19141                         }\r
19142 \r
19143                         dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique\r
19144                         undoManager.add();\r
19145                 }\r
19146 \r
19147                 editor.onKeyDown.add(function(ed, evt) {\r
19148                         if (evt.keyCode == 13) {\r
19149                                 if (handleEnterKey(evt) !== false) {\r
19150                                         evt.preventDefault();\r
19151                                 }\r
19152                         }\r
19153                 });\r
19154         };\r
19155 })(tinymce);\r
19156 \r