adding all weblabels from weblabels.fsf.org
[weblabels.fsf.org.git] / www.fsf.org / 20131028 / files / tinymce / tiny_mce_src.js
CommitLineData
5a920362 1var tinymce = {\r
2 majorVersion : '3',\r
3 minorVersion : '2.7',\r
4 releaseDate : '2009-09-22',\r
5\r
6 _init : function() {\r
7 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
8\r
9 t.isOpera = w.opera && opera.buildNumber;\r
10\r
11 t.isWebKit = /WebKit/.test(ua);\r
12\r
13 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
14\r
15 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
16\r
17 t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
18\r
19 t.isMac = ua.indexOf('Mac') != -1;\r
20\r
21 t.isAir = /adobeair/i.test(ua);\r
22\r
23 // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
24 if (w.tinyMCEPreInit) {\r
25 t.suffix = tinyMCEPreInit.suffix;\r
26 t.baseURL = tinyMCEPreInit.base;\r
27 t.query = tinyMCEPreInit.query;\r
28 return;\r
29 }\r
30\r
31 // Get suffix and base\r
32 t.suffix = '';\r
33\r
34 // If base element found, add that infront of baseURL\r
35 nl = d.getElementsByTagName('base');\r
36 for (i=0; i<nl.length; i++) {\r
37 if (v = nl[i].href) {\r
38 // Host only value like http://site.com or http://site.com:8008\r
39 if (/^https?:\/\/[^\/]+$/.test(v))\r
40 v += '/';\r
41\r
42 base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
43 }\r
44 }\r
45\r
46 function getBase(n) {\r
47 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {\r
48 if (/_(src|dev)\.js/g.test(n.src))\r
49 t.suffix = '_src';\r
50\r
51 if ((p = n.src.indexOf('?')) != -1)\r
52 t.query = n.src.substring(p + 1);\r
53\r
54 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
55\r
56 // If path to script is relative and a base href was found add that one infront\r
57 // the src property will always be an absolute one on non IE browsers and IE 8\r
58 // so this logic will basically only be executed on older IE versions\r
59 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
60 t.baseURL = base + t.baseURL;\r
61\r
62 return t.baseURL;\r
63 }\r
64\r
65 return null;\r
66 };\r
67\r
68 // Check document\r
69 nl = d.getElementsByTagName('script');\r
70 for (i=0; i<nl.length; i++) {\r
71 if (getBase(nl[i]))\r
72 return;\r
73 }\r
74\r
75 // Check head\r
76 n = d.getElementsByTagName('head')[0];\r
77 if (n) {\r
78 nl = n.getElementsByTagName('script');\r
79 for (i=0; i<nl.length; i++) {\r
80 if (getBase(nl[i]))\r
81 return;\r
82 }\r
83 }\r
84\r
85 return;\r
86 },\r
87\r
88 is : function(o, t) {\r
89 var n = typeof(o);\r
90\r
91 if (!t)\r
92 return n != 'undefined';\r
93\r
94 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
95 return true;\r
96\r
97 return n == t;\r
98 },\r
99\r
100 each : function(o, cb, s) {\r
101 var n, l;\r
102\r
103 if (!o)\r
104 return 0;\r
105\r
106 s = s || o;\r
107\r
108 if (typeof(o.length) != 'undefined') {\r
109 // Indexed arrays, needed for Safari\r
110 for (n=0, l = o.length; n<l; n++) {\r
111 if (cb.call(s, o[n], n, o) === false)\r
112 return 0;\r
113 }\r
114 } else {\r
115 // Hashtables\r
116 for (n in o) {\r
117 if (o.hasOwnProperty(n)) {\r
118 if (cb.call(s, o[n], n, o) === false)\r
119 return 0;\r
120 }\r
121 }\r
122 }\r
123\r
124 return 1;\r
125 },\r
126\r
127 map : function(a, f) {\r
128 var o = [];\r
129\r
130 tinymce.each(a, function(v) {\r
131 o.push(f(v));\r
132 });\r
133\r
134 return o;\r
135 },\r
136\r
137 grep : function(a, f) {\r
138 var o = [];\r
139\r
140 tinymce.each(a, function(v) {\r
141 if (!f || f(v))\r
142 o.push(v);\r
143 });\r
144\r
145 return o;\r
146 },\r
147\r
148 inArray : function(a, v) {\r
149 var i, l;\r
150\r
151 if (a) {\r
152 for (i = 0, l = a.length; i < l; i++) {\r
153 if (a[i] === v)\r
154 return i;\r
155 }\r
156 }\r
157\r
158 return -1;\r
159 },\r
160\r
161 extend : function(o, e) {\r
162 var i, a = arguments;\r
163\r
164 for (i=1; i<a.length; i++) {\r
165 e = a[i];\r
166\r
167 tinymce.each(e, function(v, n) {\r
168 if (typeof(v) !== 'undefined')\r
169 o[n] = v;\r
170 });\r
171 }\r
172\r
173 return o;\r
174 },\r
175\r
176 trim : function(s) {\r
177 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');\r
178 },\r
179\r
180 create : function(s, p) {\r
181 var t = this, sp, ns, cn, scn, c, de = 0;\r
182\r
183 // Parse : <prefix> <class>:<super class>\r
184 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
185 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
186\r
187 // Create namespace for new class\r
188 ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
189\r
190 // Class already exists\r
191 if (ns[cn])\r
192 return;\r
193\r
194 // Make pure static class\r
195 if (s[2] == 'static') {\r
196 ns[cn] = p;\r
197\r
198 if (this.onCreate)\r
199 this.onCreate(s[2], s[3], ns[cn]);\r
200\r
201 return;\r
202 }\r
203\r
204 // Create default constructor\r
205 if (!p[cn]) {\r
206 p[cn] = function() {};\r
207 de = 1;\r
208 }\r
209\r
210 // Add constructor and methods\r
211 ns[cn] = p[cn];\r
212 t.extend(ns[cn].prototype, p);\r
213\r
214 // Extend\r
215 if (s[5]) {\r
216 sp = t.resolve(s[5]).prototype;\r
217 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
218\r
219 // Extend constructor\r
220 c = ns[cn];\r
221 if (de) {\r
222 // Add passthrough constructor\r
223 ns[cn] = function() {\r
224 return sp[scn].apply(this, arguments);\r
225 };\r
226 } else {\r
227 // Add inherit constructor\r
228 ns[cn] = function() {\r
229 this.parent = sp[scn];\r
230 return c.apply(this, arguments);\r
231 };\r
232 }\r
233 ns[cn].prototype[cn] = ns[cn];\r
234\r
235 // Add super methods\r
236 t.each(sp, function(f, n) {\r
237 ns[cn].prototype[n] = sp[n];\r
238 });\r
239\r
240 // Add overridden methods\r
241 t.each(p, function(f, n) {\r
242 // Extend methods if needed\r
243 if (sp[n]) {\r
244 ns[cn].prototype[n] = function() {\r
245 this.parent = sp[n];\r
246 return f.apply(this, arguments);\r
247 };\r
248 } else {\r
249 if (n != cn)\r
250 ns[cn].prototype[n] = f;\r
251 }\r
252 });\r
253 }\r
254\r
255 // Add static methods\r
256 t.each(p['static'], function(f, n) {\r
257 ns[cn][n] = f;\r
258 });\r
259\r
260 if (this.onCreate)\r
261 this.onCreate(s[2], s[3], ns[cn].prototype);\r
262 },\r
263\r
264 walk : function(o, f, n, s) {\r
265 s = s || this;\r
266\r
267 if (o) {\r
268 if (n)\r
269 o = o[n];\r
270\r
271 tinymce.each(o, function(o, i) {\r
272 if (f.call(s, o, i, n) === false)\r
273 return false;\r
274\r
275 tinymce.walk(o, f, n, s);\r
276 });\r
277 }\r
278 },\r
279\r
280 createNS : function(n, o) {\r
281 var i, v;\r
282\r
283 o = o || window;\r
284\r
285 n = n.split('.');\r
286 for (i=0; i<n.length; i++) {\r
287 v = n[i];\r
288\r
289 if (!o[v])\r
290 o[v] = {};\r
291\r
292 o = o[v];\r
293 }\r
294\r
295 return o;\r
296 },\r
297\r
298 resolve : function(n, o) {\r
299 var i, l;\r
300\r
301 o = o || window;\r
302\r
303 n = n.split('.');\r
304 for (i = 0, l = n.length; i < l; i++) {\r
305 o = o[n[i]];\r
306\r
307 if (!o)\r
308 break;\r
309 }\r
310\r
311 return o;\r
312 },\r
313\r
314 addUnload : function(f, s) {\r
315 var t = this, w = window;\r
316\r
317 f = {func : f, scope : s || this};\r
318\r
319 if (!t.unloads) {\r
320 function unload() {\r
321 var li = t.unloads, o, n;\r
322\r
323 if (li) {\r
324 // Call unload handlers\r
325 for (n in li) {\r
326 o = li[n];\r
327\r
328 if (o && o.func)\r
329 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
330 }\r
331\r
332 // Detach unload function\r
333 if (w.detachEvent) {\r
334 w.detachEvent('onbeforeunload', fakeUnload);\r
335 w.detachEvent('onunload', unload);\r
336 } else if (w.removeEventListener)\r
337 w.removeEventListener('unload', unload, false);\r
338\r
339 // Destroy references\r
340 t.unloads = o = li = w = unload = 0;\r
341\r
342 // Run garbarge collector on IE\r
343 if (window.CollectGarbage)\r
344 window.CollectGarbage();\r
345 }\r
346 };\r
347\r
348 function fakeUnload() {\r
349 var d = document;\r
350\r
351 // Is there things still loading, then do some magic\r
352 if (d.readyState == 'interactive') {\r
353 function stop() {\r
354 // Prevent memory leak\r
355 d.detachEvent('onstop', stop);\r
356\r
357 // Call unload handler\r
358 if (unload)\r
359 unload();\r
360\r
361 d = 0;\r
362 };\r
363\r
364 // Fire unload when the currently loading page is stopped\r
365 if (d)\r
366 d.attachEvent('onstop', stop);\r
367\r
368 // Remove onstop listener after a while to prevent the unload function\r
369 // to execute if the user presses cancel in an onbeforeunload\r
370 // confirm dialog and then presses the browser stop button\r
371 window.setTimeout(function() {\r
372 if (d)\r
373 d.detachEvent('onstop', stop);\r
374 }, 0);\r
375 }\r
376 };\r
377\r
378 // Attach unload handler\r
379 if (w.attachEvent) {\r
380 w.attachEvent('onunload', unload);\r
381 w.attachEvent('onbeforeunload', fakeUnload);\r
382 } else if (w.addEventListener)\r
383 w.addEventListener('unload', unload, false);\r
384\r
385 // Setup initial unload handler array\r
386 t.unloads = [f];\r
387 } else\r
388 t.unloads.push(f);\r
389\r
390 return f;\r
391 },\r
392\r
393 removeUnload : function(f) {\r
394 var u = this.unloads, r = null;\r
395\r
396 tinymce.each(u, function(o, i) {\r
397 if (o && o.func == f) {\r
398 u.splice(i, 1);\r
399 r = f;\r
400 return false;\r
401 }\r
402 });\r
403\r
404 return r;\r
405 },\r
406\r
407 explode : function(s, d) {\r
408 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
409 },\r
410\r
411 _addVer : function(u) {\r
412 var v;\r
413\r
414 if (!this.query)\r
415 return u;\r
416\r
417 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
418\r
419 if (u.indexOf('#') == -1)\r
420 return u + v;\r
421\r
422 return u.replace('#', v + '#');\r
423 }\r
424\r
425 };\r
426\r
427// Required for GZip AJAX loading\r
428window.tinymce = tinymce;\r
429\r
430// Initialize the API\r
431tinymce._init();\r
432tinymce.create('tinymce.util.Dispatcher', {\r
433 scope : null,\r
434 listeners : null,\r
435\r
436 Dispatcher : function(s) {\r
437 this.scope = s || this;\r
438 this.listeners = [];\r
439 },\r
440\r
441 add : function(cb, s) {\r
442 this.listeners.push({cb : cb, scope : s || this.scope});\r
443\r
444 return cb;\r
445 },\r
446\r
447 addToTop : function(cb, s) {\r
448 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
449\r
450 return cb;\r
451 },\r
452\r
453 remove : function(cb) {\r
454 var l = this.listeners, o = null;\r
455\r
456 tinymce.each(l, function(c, i) {\r
457 if (cb == c.cb) {\r
458 o = cb;\r
459 l.splice(i, 1);\r
460 return false;\r
461 }\r
462 });\r
463\r
464 return o;\r
465 },\r
466\r
467 dispatch : function() {\r
468 var s, a = arguments, i, li = this.listeners, c;\r
469\r
470 // Needs to be a real loop since the listener count might change while looping\r
471 // And this is also more efficient\r
472 for (i = 0; i<li.length; i++) {\r
473 c = li[i];\r
474 s = c.cb.apply(c.scope, a);\r
475\r
476 if (s === false)\r
477 break;\r
478 }\r
479\r
480 return s;\r
481 }\r
482\r
483 });\r
484(function() {\r
485 var each = tinymce.each;\r
486\r
487 tinymce.create('tinymce.util.URI', {\r
488 URI : function(u, s) {\r
489 var t = this, o, a, b;\r
490\r
491 // Trim whitespace\r
492 u = tinymce.trim(u);\r
493\r
494 // Default settings\r
495 s = t.settings = s || {};\r
496\r
497 // Strange app protocol or local anchor\r
498 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
499 t.source = u;\r
500 return;\r
501 }\r
502\r
503 // Absolute path with no host, fake host and protocol\r
504 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
505 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
506\r
507 // Relative path http:// or protocol relative //path\r
508 if (!/^\w*:?\/\//.test(u))\r
509 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
510\r
511 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
512 u = u.replace(/@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
513 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
514 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
515 var s = u[i];\r
516\r
517 // Zope 3 workaround, they use @@something\r
518 if (s)\r
519 s = s.replace(/\(mce_at\)/g, '@');\r
520\r
521 t[v] = s;\r
522 });\r
523\r
524 if (b = s.base_uri) {\r
525 if (!t.protocol)\r
526 t.protocol = b.protocol;\r
527\r
528 if (!t.userInfo)\r
529 t.userInfo = b.userInfo;\r
530\r
531 if (!t.port && t.host == 'mce_host')\r
532 t.port = b.port;\r
533\r
534 if (!t.host || t.host == 'mce_host')\r
535 t.host = b.host;\r
536\r
537 t.source = '';\r
538 }\r
539\r
540 //t.path = t.path || '/';\r
541 },\r
542\r
543 setPath : function(p) {\r
544 var t = this;\r
545\r
546 p = /^(.*?)\/?(\w+)?$/.exec(p);\r
547\r
548 // Update path parts\r
549 t.path = p[0];\r
550 t.directory = p[1];\r
551 t.file = p[2];\r
552\r
553 // Rebuild source\r
554 t.source = '';\r
555 t.getURI();\r
556 },\r
557\r
558 toRelative : function(u) {\r
559 var t = this, o;\r
560\r
561 if (u === "./")\r
562 return u;\r
563\r
564 u = new tinymce.util.URI(u, {base_uri : t});\r
565\r
566 // Not on same domain/port or protocol\r
567 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
568 return u.getURI();\r
569\r
570 o = t.toRelPath(t.path, u.path);\r
571\r
572 // Add query\r
573 if (u.query)\r
574 o += '?' + u.query;\r
575\r
576 // Add anchor\r
577 if (u.anchor)\r
578 o += '#' + u.anchor;\r
579\r
580 return o;\r
581 },\r
582 \r
583 toAbsolute : function(u, nh) {\r
584 var u = new tinymce.util.URI(u, {base_uri : this});\r
585\r
586 return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
587 },\r
588\r
589 toRelPath : function(base, path) {\r
590 var items, bp = 0, out = '', i, l;\r
591\r
592 // Split the paths\r
593 base = base.substring(0, base.lastIndexOf('/'));\r
594 base = base.split('/');\r
595 items = path.split('/');\r
596\r
597 if (base.length >= items.length) {\r
598 for (i = 0, l = base.length; i < l; i++) {\r
599 if (i >= items.length || base[i] != items[i]) {\r
600 bp = i + 1;\r
601 break;\r
602 }\r
603 }\r
604 }\r
605\r
606 if (base.length < items.length) {\r
607 for (i = 0, l = items.length; i < l; i++) {\r
608 if (i >= base.length || base[i] != items[i]) {\r
609 bp = i + 1;\r
610 break;\r
611 }\r
612 }\r
613 }\r
614\r
615 if (bp == 0) {\r
616 return "./";\r
617 }\r
618\r
619 if (bp == 1)\r
620 return path;\r
621\r
622 for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
623 out += "../";\r
624\r
625 for (i = bp - 1, l = items.length; i < l; i++) {\r
626 if (i != bp - 1)\r
627 out += "/" + items[i];\r
628 else\r
629 out += items[i];\r
630 }\r
631\r
632 return out;\r
633 },\r
634\r
635 toAbsPath : function(base, path) {\r
636 var i, nb = 0, o = [], tr, outPath;\r
637\r
638 // Split paths\r
639 tr = /\/$/.test(path) ? '/' : '';\r
640 base = base.split('/');\r
641 path = path.split('/');\r
642\r
643 // Remove empty chunks\r
644 each(base, function(k) {\r
645 if (k)\r
646 o.push(k);\r
647 });\r
648\r
649 base = o;\r
650\r
651 // Merge relURLParts chunks\r
652 for (i = path.length - 1, o = []; i >= 0; i--) {\r
653 // Ignore empty or .\r
654 if (path[i].length == 0 || path[i] == ".")\r
655 continue;\r
656\r
657 // Is parent\r
658 if (path[i] == '..') {\r
659 nb++;\r
660 continue;\r
661 }\r
662\r
663 // Move up\r
664 if (nb > 0) {\r
665 nb--;\r
666 continue;\r
667 }\r
668\r
669 o.push(path[i]);\r
670 }\r
671\r
672 i = base.length - nb;\r
673\r
674 // If /a/b/c or /\r
675 if (i <= 0)\r
676 outPath = o.reverse().join('/');\r
677 else\r
678 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
679\r
680 // Add front / if it's needed\r
681 if (outPath.indexOf('/') !== 0)\r
682 outPath = '/' + outPath;\r
683\r
684 // Add traling / if it's needed\r
685 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
686 outPath += tr;\r
687\r
688 return outPath;\r
689 },\r
690\r
691 getURI : function(nh) {\r
692 var s, t = this;\r
693\r
694 // Rebuild source\r
695 if (!t.source || nh) {\r
696 s = '';\r
697\r
698 if (!nh) {\r
699 if (t.protocol)\r
700 s += t.protocol + '://';\r
701\r
702 if (t.userInfo)\r
703 s += t.userInfo + '@';\r
704\r
705 if (t.host)\r
706 s += t.host;\r
707\r
708 if (t.port)\r
709 s += ':' + t.port;\r
710 }\r
711\r
712 if (t.path)\r
713 s += t.path;\r
714\r
715 if (t.query)\r
716 s += '?' + t.query;\r
717\r
718 if (t.anchor)\r
719 s += '#' + t.anchor;\r
720\r
721 t.source = s;\r
722 }\r
723\r
724 return t.source;\r
725 }\r
726 });\r
727})();\r
728\r
729(function() {\r
730 var each = tinymce.each;\r
731\r
732 tinymce.create('static tinymce.util.Cookie', {\r
733 getHash : function(n) {\r
734 var v = this.get(n), h;\r
735\r
736 if (v) {\r
737 each(v.split('&'), function(v) {\r
738 v = v.split('=');\r
739 h = h || {};\r
740 h[unescape(v[0])] = unescape(v[1]);\r
741 });\r
742 }\r
743\r
744 return h;\r
745 },\r
746\r
747 setHash : function(n, v, e, p, d, s) {\r
748 var o = '';\r
749\r
750 each(v, function(v, k) {\r
751 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
752 });\r
753\r
754 this.set(n, o, e, p, d, s);\r
755 },\r
756\r
757 get : function(n) {\r
758 var c = document.cookie, e, p = n + "=", b;\r
759\r
760 // Strict mode\r
761 if (!c)\r
762 return;\r
763\r
764 b = c.indexOf("; " + p);\r
765\r
766 if (b == -1) {\r
767 b = c.indexOf(p);\r
768\r
769 if (b != 0)\r
770 return null;\r
771 } else\r
772 b += 2;\r
773\r
774 e = c.indexOf(";", b);\r
775\r
776 if (e == -1)\r
777 e = c.length;\r
778\r
779 return unescape(c.substring(b + p.length, e));\r
780 },\r
781\r
782 set : function(n, v, e, p, d, s) {\r
783 document.cookie = n + "=" + escape(v) +\r
784 ((e) ? "; expires=" + e.toGMTString() : "") +\r
785 ((p) ? "; path=" + escape(p) : "") +\r
786 ((d) ? "; domain=" + d : "") +\r
787 ((s) ? "; secure" : "");\r
788 },\r
789\r
790 remove : function(n, p) {\r
791 var d = new Date();\r
792\r
793 d.setTime(d.getTime() - 1000);\r
794\r
795 this.set(n, '', d, p, d);\r
796 }\r
797 });\r
798})();\r
799tinymce.create('static tinymce.util.JSON', {\r
800 serialize : function(o) {\r
801 var i, v, s = tinymce.util.JSON.serialize, t;\r
802\r
803 if (o == null)\r
804 return 'null';\r
805\r
806 t = typeof o;\r
807\r
808 if (t == 'string') {\r
809 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
810\r
811 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
812 i = v.indexOf(b);\r
813\r
814 if (i + 1)\r
815 return '\\' + v.charAt(i + 1);\r
816\r
817 a = b.charCodeAt().toString(16);\r
818\r
819 return '\\u' + '0000'.substring(a.length) + a;\r
820 }) + '"';\r
821 }\r
822\r
823 if (t == 'object') {\r
824 if (o.hasOwnProperty && o instanceof Array) {\r
825 for (i=0, v = '['; i<o.length; i++)\r
826 v += (i > 0 ? ',' : '') + s(o[i]);\r
827\r
828 return v + ']';\r
829 }\r
830\r
831 v = '{';\r
832\r
833 for (i in o)\r
834 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
835\r
836 return v + '}';\r
837 }\r
838\r
839 return '' + o;\r
840 },\r
841\r
842 parse : function(s) {\r
843 try {\r
844 return eval('(' + s + ')');\r
845 } catch (ex) {\r
846 // Ignore\r
847 }\r
848 }\r
849\r
850 });\r
851tinymce.create('static tinymce.util.XHR', {\r
852 send : function(o) {\r
853 var x, t, w = window, c = 0;\r
854\r
855 // Default settings\r
856 o.scope = o.scope || this;\r
857 o.success_scope = o.success_scope || o.scope;\r
858 o.error_scope = o.error_scope || o.scope;\r
859 o.async = o.async === false ? false : true;\r
860 o.data = o.data || '';\r
861\r
862 function get(s) {\r
863 x = 0;\r
864\r
865 try {\r
866 x = new ActiveXObject(s);\r
867 } catch (ex) {\r
868 }\r
869\r
870 return x;\r
871 };\r
872\r
873 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
874\r
875 if (x) {\r
876 if (x.overrideMimeType)\r
877 x.overrideMimeType(o.content_type);\r
878\r
879 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
880\r
881 if (o.content_type)\r
882 x.setRequestHeader('Content-Type', o.content_type);\r
883\r
884 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
885\r
886 x.send(o.data);\r
887\r
888 function ready() {\r
889 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
890 if (o.success && c < 10000 && x.status == 200)\r
891 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
892 else if (o.error)\r
893 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
894\r
895 x = null;\r
896 } else\r
897 w.setTimeout(ready, 10);\r
898 };\r
899\r
900 // Syncronous request\r
901 if (!o.async)\r
902 return ready();\r
903\r
904 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
905 t = w.setTimeout(ready, 10);\r
906 }\r
907 }\r
908});\r
909(function() {\r
910 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
911\r
912 tinymce.create('tinymce.util.JSONRequest', {\r
913 JSONRequest : function(s) {\r
914 this.settings = extend({\r
915 }, s);\r
916 this.count = 0;\r
917 },\r
918\r
919 send : function(o) {\r
920 var ecb = o.error, scb = o.success;\r
921\r
922 o = extend(this.settings, o);\r
923\r
924 o.success = function(c, x) {\r
925 c = JSON.parse(c);\r
926\r
927 if (typeof(c) == 'undefined') {\r
928 c = {\r
929 error : 'JSON Parse error.'\r
930 };\r
931 }\r
932\r
933 if (c.error)\r
934 ecb.call(o.error_scope || o.scope, c.error, x);\r
935 else\r
936 scb.call(o.success_scope || o.scope, c.result);\r
937 };\r
938\r
939 o.error = function(ty, x) {\r
940 ecb.call(o.error_scope || o.scope, ty, x);\r
941 };\r
942\r
943 o.data = JSON.serialize({\r
944 id : o.id || 'c' + (this.count++),\r
945 method : o.method,\r
946 params : o.params\r
947 });\r
948\r
949 // JSON content type for Ruby on rails. Bug: #1883287\r
950 o.content_type = 'application/json';\r
951\r
952 XHR.send(o);\r
953 },\r
954\r
955 'static' : {\r
956 sendRPC : function(o) {\r
957 return new tinymce.util.JSONRequest().send(o);\r
958 }\r
959 }\r
960 });\r
961}());\r
962(function(tinymce) {\r
963 // Shorten names\r
964 var each = tinymce.each, is = tinymce.is;\r
965 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;\r
966\r
967 tinymce.create('tinymce.dom.DOMUtils', {\r
968 doc : null,\r
969 root : null,\r
970 files : null,\r
971 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
972 props : {\r
973 "for" : "htmlFor",\r
974 "class" : "className",\r
975 className : "className",\r
976 checked : "checked",\r
977 disabled : "disabled",\r
978 maxlength : "maxLength",\r
979 readonly : "readOnly",\r
980 selected : "selected",\r
981 value : "value",\r
982 id : "id",\r
983 name : "name",\r
984 type : "type"\r
985 },\r
986\r
987 DOMUtils : function(d, s) {\r
988 var t = this;\r
989\r
990 t.doc = d;\r
991 t.win = window;\r
992 t.files = {};\r
993 t.cssFlicker = false;\r
994 t.counter = 0;\r
995 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
996 t.stdMode = d.documentMode === 8;\r
997\r
998 t.settings = s = tinymce.extend({\r
999 keep_values : false,\r
1000 hex_colors : 1,\r
1001 process_html : 1\r
1002 }, s);\r
1003\r
1004 // Fix IE6SP2 flicker and check it failed for pre SP2\r
1005 if (tinymce.isIE6) {\r
1006 try {\r
1007 d.execCommand('BackgroundImageCache', false, true);\r
1008 } catch (e) {\r
1009 t.cssFlicker = true;\r
1010 }\r
1011 }\r
1012\r
1013 tinymce.addUnload(t.destroy, t);\r
1014 },\r
1015\r
1016 getRoot : function() {\r
1017 var t = this, s = t.settings;\r
1018\r
1019 return (s && t.get(s.root_element)) || t.doc.body;\r
1020 },\r
1021\r
1022 getViewPort : function(w) {\r
1023 var d, b;\r
1024\r
1025 w = !w ? this.win : w;\r
1026 d = w.document;\r
1027 b = this.boxModel ? d.documentElement : d.body;\r
1028\r
1029 // Returns viewport size excluding scrollbars\r
1030 return {\r
1031 x : w.pageXOffset || b.scrollLeft,\r
1032 y : w.pageYOffset || b.scrollTop,\r
1033 w : w.innerWidth || b.clientWidth,\r
1034 h : w.innerHeight || b.clientHeight\r
1035 };\r
1036 },\r
1037\r
1038 getRect : function(e) {\r
1039 var p, t = this, sr;\r
1040\r
1041 e = t.get(e);\r
1042 p = t.getPos(e);\r
1043 sr = t.getSize(e);\r
1044\r
1045 return {\r
1046 x : p.x,\r
1047 y : p.y,\r
1048 w : sr.w,\r
1049 h : sr.h\r
1050 };\r
1051 },\r
1052\r
1053 getSize : function(e) {\r
1054 var t = this, w, h;\r
1055\r
1056 e = t.get(e);\r
1057 w = t.getStyle(e, 'width');\r
1058 h = t.getStyle(e, 'height');\r
1059\r
1060 // Non pixel value, then force offset/clientWidth\r
1061 if (w.indexOf('px') === -1)\r
1062 w = 0;\r
1063\r
1064 // Non pixel value, then force offset/clientWidth\r
1065 if (h.indexOf('px') === -1)\r
1066 h = 0;\r
1067\r
1068 return {\r
1069 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1070 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1071 };\r
1072 },\r
1073\r
1074 getParent : function(n, f, r) {\r
1075 return this.getParents(n, f, r, false);\r
1076 },\r
1077\r
1078 getParents : function(n, f, r, c) {\r
1079 var t = this, na, se = t.settings, o = [];\r
1080\r
1081 n = t.get(n);\r
1082 c = c === undefined;\r
1083\r
1084 if (se.strict_root)\r
1085 r = r || t.getRoot();\r
1086\r
1087 // Wrap node name as func\r
1088 if (is(f, 'string')) {\r
1089 na = f;\r
1090\r
1091 if (f === '*') {\r
1092 f = function(n) {return n.nodeType == 1;};\r
1093 } else {\r
1094 f = function(n) {\r
1095 return t.is(n, na);\r
1096 };\r
1097 }\r
1098 }\r
1099\r
1100 while (n) {\r
1101 if (n == r || !n.nodeType || n.nodeType === 9)\r
1102 break;\r
1103\r
1104 if (!f || f(n)) {\r
1105 if (c)\r
1106 o.push(n);\r
1107 else\r
1108 return n;\r
1109 }\r
1110\r
1111 n = n.parentNode;\r
1112 }\r
1113\r
1114 return c ? o : null;\r
1115 },\r
1116\r
1117 get : function(e) {\r
1118 var n;\r
1119\r
1120 if (e && this.doc && typeof(e) == 'string') {\r
1121 n = e;\r
1122 e = this.doc.getElementById(e);\r
1123\r
1124 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1125 if (e && e.id !== n)\r
1126 return this.doc.getElementsByName(n)[1];\r
1127 }\r
1128\r
1129 return e;\r
1130 },\r
1131\r
1132 getNext : function(node, selector) {\r
1133 return this._findSib(node, selector, 'nextSibling');\r
1134 },\r
1135\r
1136 getPrev : function(node, selector) {\r
1137 return this._findSib(node, selector, 'previousSibling');\r
1138 },\r
1139\r
1140 select : function(pa, s) {\r
1141 var t = this;\r
1142\r
1143 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1144 },\r
1145\r
1146 is : function(n, patt) {\r
1147 return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0;\r
1148 },\r
1149\r
1150 add : function(p, n, a, h, c) {\r
1151 var t = this;\r
1152\r
1153 return this.run(p, function(p) {\r
1154 var e, k;\r
1155\r
1156 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1157 t.setAttribs(e, a);\r
1158\r
1159 if (h) {\r
1160 if (h.nodeType)\r
1161 e.appendChild(h);\r
1162 else\r
1163 t.setHTML(e, h);\r
1164 }\r
1165\r
1166 return !c ? p.appendChild(e) : e;\r
1167 });\r
1168 },\r
1169\r
1170 create : function(n, a, h) {\r
1171 return this.add(this.doc.createElement(n), n, a, h, 1);\r
1172 },\r
1173\r
1174 createHTML : function(n, a, h) {\r
1175 var o = '', t = this, k;\r
1176\r
1177 o += '<' + n;\r
1178\r
1179 for (k in a) {\r
1180 if (a.hasOwnProperty(k))\r
1181 o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1182 }\r
1183\r
1184 if (tinymce.is(h))\r
1185 return o + '>' + h + '</' + n + '>';\r
1186\r
1187 return o + ' />';\r
1188 },\r
1189\r
1190 remove : function(n, k) {\r
1191 var t = this;\r
1192\r
1193 return this.run(n, function(n) {\r
1194 var p, g, i;\r
1195\r
1196 p = n.parentNode;\r
1197\r
1198 if (!p)\r
1199 return null;\r
1200\r
1201 if (k) {\r
1202 for (i = n.childNodes.length - 1; i >= 0; i--)\r
1203 t.insertAfter(n.childNodes[i], n);\r
1204\r
1205 //each(n.childNodes, function(c) {\r
1206 // p.insertBefore(c.cloneNode(true), n);\r
1207 //});\r
1208 }\r
1209\r
1210 // Fix IE psuedo leak\r
1211 if (t.fixPsuedoLeaks) {\r
1212 p = n.cloneNode(true);\r
1213 k = 'IELeakGarbageBin';\r
1214 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});\r
1215 g.appendChild(n);\r
1216 g.innerHTML = '';\r
1217\r
1218 return p;\r
1219 }\r
1220\r
1221 return p.removeChild(n);\r
1222 });\r
1223 },\r
1224\r
1225 setStyle : function(n, na, v) {\r
1226 var t = this;\r
1227\r
1228 return t.run(n, function(e) {\r
1229 var s, i;\r
1230\r
1231 s = e.style;\r
1232\r
1233 // Camelcase it, if needed\r
1234 na = na.replace(/-(\D)/g, function(a, b){\r
1235 return b.toUpperCase();\r
1236 });\r
1237\r
1238 // Default px suffix on these\r
1239 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1240 v += 'px';\r
1241\r
1242 switch (na) {\r
1243 case 'opacity':\r
1244 // IE specific opacity\r
1245 if (isIE) {\r
1246 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1247\r
1248 if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1249 s.display = 'inline-block';\r
1250 }\r
1251\r
1252 // Fix for older browsers\r
1253 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1254 break;\r
1255\r
1256 case 'float':\r
1257 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1258 break;\r
1259 \r
1260 default:\r
1261 s[na] = v || '';\r
1262 }\r
1263\r
1264 // Force update of the style data\r
1265 if (t.settings.update_styles)\r
1266 t.setAttrib(e, 'mce_style');\r
1267 });\r
1268 },\r
1269\r
1270 getStyle : function(n, na, c) {\r
1271 n = this.get(n);\r
1272\r
1273 if (!n)\r
1274 return false;\r
1275\r
1276 // Gecko\r
1277 if (this.doc.defaultView && c) {\r
1278 // Remove camelcase\r
1279 na = na.replace(/[A-Z]/g, function(a){\r
1280 return '-' + a;\r
1281 });\r
1282\r
1283 try {\r
1284 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1285 } catch (ex) {\r
1286 // Old safari might fail\r
1287 return null;\r
1288 }\r
1289 }\r
1290\r
1291 // Camelcase it, if needed\r
1292 na = na.replace(/-(\D)/g, function(a, b){\r
1293 return b.toUpperCase();\r
1294 });\r
1295\r
1296 if (na == 'float')\r
1297 na = isIE ? 'styleFloat' : 'cssFloat';\r
1298\r
1299 // IE & Opera\r
1300 if (n.currentStyle && c)\r
1301 return n.currentStyle[na];\r
1302\r
1303 return n.style[na];\r
1304 },\r
1305\r
1306 setStyles : function(e, o) {\r
1307 var t = this, s = t.settings, ol;\r
1308\r
1309 ol = s.update_styles;\r
1310 s.update_styles = 0;\r
1311\r
1312 each(o, function(v, n) {\r
1313 t.setStyle(e, n, v);\r
1314 });\r
1315\r
1316 // Update style info\r
1317 s.update_styles = ol;\r
1318 if (s.update_styles)\r
1319 t.setAttrib(e, s.cssText);\r
1320 },\r
1321\r
1322 setAttrib : function(e, n, v) {\r
1323 var t = this;\r
1324\r
1325 // Whats the point\r
1326 if (!e || !n)\r
1327 return;\r
1328\r
1329 // Strict XML mode\r
1330 if (t.settings.strict)\r
1331 n = n.toLowerCase();\r
1332\r
1333 return this.run(e, function(e) {\r
1334 var s = t.settings;\r
1335\r
1336 switch (n) {\r
1337 case "style":\r
1338 if (!is(v, 'string')) {\r
1339 each(v, function(v, n) {\r
1340 t.setStyle(e, n, v);\r
1341 });\r
1342\r
1343 return;\r
1344 }\r
1345\r
1346 // No mce_style for elements with these since they might get resized by the user\r
1347 if (s.keep_values) {\r
1348 if (v && !t._isRes(v))\r
1349 e.setAttribute('mce_style', v, 2);\r
1350 else\r
1351 e.removeAttribute('mce_style', 2);\r
1352 }\r
1353\r
1354 e.style.cssText = v;\r
1355 break;\r
1356\r
1357 case "class":\r
1358 e.className = v || ''; // Fix IE null bug\r
1359 break;\r
1360\r
1361 case "src":\r
1362 case "href":\r
1363 if (s.keep_values) {\r
1364 if (s.url_converter)\r
1365 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1366\r
1367 t.setAttrib(e, 'mce_' + n, v, 2);\r
1368 }\r
1369\r
1370 break;\r
1371 \r
1372 case "shape":\r
1373 e.setAttribute('mce_style', v);\r
1374 break;\r
1375 }\r
1376\r
1377 if (is(v) && v !== null && v.length !== 0)\r
1378 e.setAttribute(n, '' + v, 2);\r
1379 else\r
1380 e.removeAttribute(n, 2);\r
1381 });\r
1382 },\r
1383\r
1384 setAttribs : function(e, o) {\r
1385 var t = this;\r
1386\r
1387 return this.run(e, function(e) {\r
1388 each(o, function(v, n) {\r
1389 t.setAttrib(e, n, v);\r
1390 });\r
1391 });\r
1392 },\r
1393\r
1394 getAttrib : function(e, n, dv) {\r
1395 var v, t = this;\r
1396\r
1397 e = t.get(e);\r
1398\r
1399 if (!e || e.nodeType !== 1)\r
1400 return false;\r
1401\r
1402 if (!is(dv))\r
1403 dv = '';\r
1404\r
1405 // Try the mce variant for these\r
1406 if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1407 v = e.getAttribute("mce_" + n);\r
1408\r
1409 if (v)\r
1410 return v;\r
1411 }\r
1412\r
1413 if (isIE && t.props[n]) {\r
1414 v = e[t.props[n]];\r
1415 v = v && v.nodeValue ? v.nodeValue : v;\r
1416 }\r
1417\r
1418 if (!v)\r
1419 v = e.getAttribute(n, 2);\r
1420\r
1421 // Check boolean attribs\r
1422 if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1423 if (e[t.props[n]] === true && v === '')\r
1424 return n;\r
1425\r
1426 return v ? n : '';\r
1427 }\r
1428\r
1429 // Inner input elements will override attributes on form elements\r
1430 if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1431 return e.getAttributeNode(n).nodeValue;\r
1432\r
1433 if (n === 'style') {\r
1434 v = v || e.style.cssText;\r
1435\r
1436 if (v) {\r
1437 v = t.serializeStyle(t.parseStyle(v));\r
1438\r
1439 if (t.settings.keep_values && !t._isRes(v))\r
1440 e.setAttribute('mce_style', v);\r
1441 }\r
1442 }\r
1443\r
1444 // Remove Apple and WebKit stuff\r
1445 if (isWebKit && n === "class" && v)\r
1446 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1447\r
1448 // Handle IE issues\r
1449 if (isIE) {\r
1450 switch (n) {\r
1451 case 'rowspan':\r
1452 case 'colspan':\r
1453 // IE returns 1 as default value\r
1454 if (v === 1)\r
1455 v = '';\r
1456\r
1457 break;\r
1458\r
1459 case 'size':\r
1460 // IE returns +0 as default value for size\r
1461 if (v === '+0' || v === 20 || v === 0)\r
1462 v = '';\r
1463\r
1464 break;\r
1465\r
1466 case 'width':\r
1467 case 'height':\r
1468 case 'vspace':\r
1469 case 'checked':\r
1470 case 'disabled':\r
1471 case 'readonly':\r
1472 if (v === 0)\r
1473 v = '';\r
1474\r
1475 break;\r
1476\r
1477 case 'hspace':\r
1478 // IE returns -1 as default value\r
1479 if (v === -1)\r
1480 v = '';\r
1481\r
1482 break;\r
1483\r
1484 case 'maxlength':\r
1485 case 'tabindex':\r
1486 // IE returns default value\r
1487 if (v === 32768 || v === 2147483647 || v === '32768')\r
1488 v = '';\r
1489\r
1490 break;\r
1491\r
1492 case 'multiple':\r
1493 case 'compact':\r
1494 case 'noshade':\r
1495 case 'nowrap':\r
1496 if (v === 65535)\r
1497 return n;\r
1498\r
1499 return dv;\r
1500\r
1501 case 'shape':\r
1502 v = v.toLowerCase();\r
1503 break;\r
1504\r
1505 default:\r
1506 // IE has odd anonymous function for event attributes\r
1507 if (n.indexOf('on') === 0 && v)\r
1508 v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1509 }\r
1510 }\r
1511\r
1512 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1513 },\r
1514\r
1515 getPos : function(n, ro) {\r
1516 var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1517\r
1518 n = t.get(n);\r
1519 ro = ro || d.body;\r
1520\r
1521 if (n) {\r
1522 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1523 if (isIE && !t.stdMode) {\r
1524 n = n.getBoundingClientRect();\r
1525 e = t.boxModel ? d.documentElement : d.body;\r
1526 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1527 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1528 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset\r
1529\r
1530 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1531 }\r
1532\r
1533 r = n;\r
1534 while (r && r != ro && r.nodeType) {\r
1535 x += r.offsetLeft || 0;\r
1536 y += r.offsetTop || 0;\r
1537 r = r.offsetParent;\r
1538 }\r
1539\r
1540 r = n.parentNode;\r
1541 while (r && r != ro && r.nodeType) {\r
1542 x -= r.scrollLeft || 0;\r
1543 y -= r.scrollTop || 0;\r
1544 r = r.parentNode;\r
1545 }\r
1546 }\r
1547\r
1548 return {x : x, y : y};\r
1549 },\r
1550\r
1551 parseStyle : function(st) {\r
1552 var t = this, s = t.settings, o = {};\r
1553\r
1554 if (!st)\r
1555 return o;\r
1556\r
1557 function compress(p, s, ot) {\r
1558 var t, r, b, l;\r
1559\r
1560 // Get values and check it it needs compressing\r
1561 t = o[p + '-top' + s];\r
1562 if (!t)\r
1563 return;\r
1564\r
1565 r = o[p + '-right' + s];\r
1566 if (t != r)\r
1567 return;\r
1568\r
1569 b = o[p + '-bottom' + s];\r
1570 if (r != b)\r
1571 return;\r
1572\r
1573 l = o[p + '-left' + s];\r
1574 if (b != l)\r
1575 return;\r
1576\r
1577 // Compress\r
1578 o[ot] = l;\r
1579 delete o[p + '-top' + s];\r
1580 delete o[p + '-right' + s];\r
1581 delete o[p + '-bottom' + s];\r
1582 delete o[p + '-left' + s];\r
1583 };\r
1584\r
1585 function compress2(ta, a, b, c) {\r
1586 var t;\r
1587\r
1588 t = o[a];\r
1589 if (!t)\r
1590 return;\r
1591\r
1592 t = o[b];\r
1593 if (!t)\r
1594 return;\r
1595\r
1596 t = o[c];\r
1597 if (!t)\r
1598 return;\r
1599\r
1600 // Compress\r
1601 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1602 delete o[a];\r
1603 delete o[b];\r
1604 delete o[c];\r
1605 };\r
1606\r
1607 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1608\r
1609 each(st.split(';'), function(v) {\r
1610 var sv, ur = [];\r
1611\r
1612 if (v) {\r
1613 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1614 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1615 v = v.split(':');\r
1616 sv = tinymce.trim(v[1]);\r
1617 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1618\r
1619 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1620 return t.toHex(v);\r
1621 });\r
1622\r
1623 if (s.url_converter) {\r
1624 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1625 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1626 });\r
1627 }\r
1628\r
1629 o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1630 }\r
1631 });\r
1632\r
1633 compress("border", "", "border");\r
1634 compress("border", "-width", "border-width");\r
1635 compress("border", "-color", "border-color");\r
1636 compress("border", "-style", "border-style");\r
1637 compress("padding", "", "padding");\r
1638 compress("margin", "", "margin");\r
1639 compress2('border', 'border-width', 'border-style', 'border-color');\r
1640\r
1641 if (isIE) {\r
1642 // Remove pointless border\r
1643 if (o.border == 'medium none')\r
1644 o.border = '';\r
1645 }\r
1646\r
1647 return o;\r
1648 },\r
1649\r
1650 serializeStyle : function(o) {\r
1651 var s = '';\r
1652\r
1653 each(o, function(v, k) {\r
1654 if (k && v) {\r
1655 if (tinymce.isGecko && k.indexOf('-moz-') === 0)\r
1656 return;\r
1657\r
1658 switch (k) {\r
1659 case 'color':\r
1660 case 'background-color':\r
1661 v = v.toLowerCase();\r
1662 break;\r
1663 }\r
1664\r
1665 s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1666 }\r
1667 });\r
1668\r
1669 return s;\r
1670 },\r
1671\r
1672 loadCSS : function(u) {\r
1673 var t = this, d = t.doc, head;\r
1674\r
1675 if (!u)\r
1676 u = '';\r
1677\r
1678 head = t.select('head')[0];\r
1679\r
1680 each(u.split(','), function(u) {\r
1681 var link;\r
1682\r
1683 if (t.files[u])\r
1684 return;\r
1685\r
1686 t.files[u] = true;\r
1687 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1688\r
1689 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1690 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1691 // It's ugly but it seems to work fine.\r
1692 if (isIE && d.documentMode) {\r
1693 link.onload = function() {\r
1694 d.recalc();\r
1695 link.onload = null;\r
1696 };\r
1697 }\r
1698\r
1699 head.appendChild(link);\r
1700 });\r
1701 },\r
1702\r
1703 addClass : function(e, c) {\r
1704 return this.run(e, function(e) {\r
1705 var o;\r
1706\r
1707 if (!c)\r
1708 return 0;\r
1709\r
1710 if (this.hasClass(e, c))\r
1711 return e.className;\r
1712\r
1713 o = this.removeClass(e, c);\r
1714\r
1715 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1716 });\r
1717 },\r
1718\r
1719 removeClass : function(e, c) {\r
1720 var t = this, re;\r
1721\r
1722 return t.run(e, function(e) {\r
1723 var v;\r
1724\r
1725 if (t.hasClass(e, c)) {\r
1726 if (!re)\r
1727 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1728\r
1729 v = e.className.replace(re, ' ');\r
1730\r
1731 return e.className = tinymce.trim(v != ' ' ? v : '');\r
1732 }\r
1733\r
1734 return e.className;\r
1735 });\r
1736 },\r
1737\r
1738 hasClass : function(n, c) {\r
1739 n = this.get(n);\r
1740\r
1741 if (!n || !c)\r
1742 return false;\r
1743\r
1744 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1745 },\r
1746\r
1747 show : function(e) {\r
1748 return this.setStyle(e, 'display', 'block');\r
1749 },\r
1750\r
1751 hide : function(e) {\r
1752 return this.setStyle(e, 'display', 'none');\r
1753 },\r
1754\r
1755 isHidden : function(e) {\r
1756 e = this.get(e);\r
1757\r
1758 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1759 },\r
1760\r
1761 uniqueId : function(p) {\r
1762 return (!p ? 'mce_' : p) + (this.counter++);\r
1763 },\r
1764\r
1765 setHTML : function(e, h) {\r
1766 var t = this;\r
1767\r
1768 return this.run(e, function(e) {\r
1769 var x, i, nl, n, p, x;\r
1770\r
1771 h = t.processHTML(h);\r
1772\r
1773 if (isIE) {\r
1774 function set() {\r
1775 try {\r
1776 // IE will remove comments from the beginning\r
1777 // unless you padd the contents with something\r
1778 e.innerHTML = '<br />' + h;\r
1779 e.removeChild(e.firstChild);\r
1780 } catch (ex) {\r
1781 // 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
1782 // This seems to fix this problem\r
1783\r
1784 // Remove all child nodes\r
1785 while (e.firstChild)\r
1786 e.firstChild.removeNode();\r
1787\r
1788 // Create new div with HTML contents and a BR infront to keep comments\r
1789 x = t.create('div');\r
1790 x.innerHTML = '<br />' + h;\r
1791\r
1792 // Add all children from div to target\r
1793 each (x.childNodes, function(n, i) {\r
1794 // Skip br element\r
1795 if (i)\r
1796 e.appendChild(n);\r
1797 });\r
1798 }\r
1799 };\r
1800\r
1801 // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1802 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1803 // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1804 if (t.settings.fix_ie_paragraphs)\r
1805 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');\r
1806\r
1807 set();\r
1808\r
1809 if (t.settings.fix_ie_paragraphs) {\r
1810 // Check for odd paragraphs this is a sign of a broken DOM\r
1811 nl = e.getElementsByTagName("p");\r
1812 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1813 n = nl[i];\r
1814\r
1815 if (!n.hasChildNodes()) {\r
1816 if (!n.mce_keep) {\r
1817 x = 1; // Is broken\r
1818 break;\r
1819 }\r
1820\r
1821 n.removeAttribute('mce_keep');\r
1822 }\r
1823 }\r
1824 }\r
1825\r
1826 // Time to fix the madness IE left us\r
1827 if (x) {\r
1828 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1829 // after we use innerHTML we can fix the DOM tree\r
1830 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 mce_tmp="1">');\r
1831 h = h.replace(/<\/p>/g, '</div>');\r
1832\r
1833 // Set the new HTML with DIVs\r
1834 set();\r
1835\r
1836 // Replace all DIV elements with he mce_tmp attibute back to paragraphs\r
1837 // This is needed since IE has a annoying bug see above for details\r
1838 // This is a slow process but it has to be done. :(\r
1839 if (t.settings.fix_ie_paragraphs) {\r
1840 nl = e.getElementsByTagName("DIV");\r
1841 for (i = nl.length - 1; i >= 0; i--) {\r
1842 n = nl[i];\r
1843\r
1844 // Is it a temp div\r
1845 if (n.mce_tmp) {\r
1846 // Create new paragraph\r
1847 p = t.doc.createElement('p');\r
1848\r
1849 // Copy all attributes\r
1850 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1851 var v;\r
1852\r
1853 if (b !== 'mce_tmp') {\r
1854 v = n.getAttribute(b);\r
1855\r
1856 if (!v && b === 'class')\r
1857 v = n.className;\r
1858\r
1859 p.setAttribute(b, v);\r
1860 }\r
1861 });\r
1862\r
1863 // Append all children to new paragraph\r
1864 for (x = 0; x<n.childNodes.length; x++)\r
1865 p.appendChild(n.childNodes[x].cloneNode(true));\r
1866\r
1867 // Replace div with new paragraph\r
1868 n.swapNode(p);\r
1869 }\r
1870 }\r
1871 }\r
1872 }\r
1873 } else\r
1874 e.innerHTML = h;\r
1875\r
1876 return h;\r
1877 });\r
1878 },\r
1879\r
1880 processHTML : function(h) {\r
1881 var t = this, s = t.settings, codeBlocks = [];\r
1882\r
1883 if (!s.process_html)\r
1884 return h;\r
1885\r
1886 // Convert strong and em to b and i in FF since it can't handle them\r
1887 if (tinymce.isGecko) {\r
1888 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');\r
1889 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');\r
1890 } else if (isIE) {\r
1891 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
1892 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
1893 }\r
1894\r
1895 // Fix some issues\r
1896 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
1897\r
1898 // Store away src and href in mce_src and mce_href since browsers mess them up\r
1899 if (s.keep_values) {\r
1900 // Wrap scripts and styles in comments for serialization purposes\r
1901 if (/<script|noscript|style/i.test(h)) {\r
1902 function trim(s) {\r
1903 // Remove prefix and suffix code for element\r
1904 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
1905 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
1906 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
1907 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
1908\r
1909 return s;\r
1910 };\r
1911\r
1912 // Wrap the script contents in CDATA and keep them from executing\r
1913 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
1914 // Force type attribute\r
1915 if (!attribs)\r
1916 attribs = ' type="text/javascript"';\r
1917\r
1918 // Convert the src attribute of the scripts\r
1919 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
1920 if (s.url_converter)\r
1921 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
1922\r
1923 return 'mce_src="' + url + '"';\r
1924 });\r
1925\r
1926 // Wrap text contents\r
1927 if (tinymce.trim(text)) {\r
1928 codeBlocks.push(trim(text));\r
1929 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
1930 }\r
1931\r
1932 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
1933 });\r
1934\r
1935 // Wrap style elements\r
1936 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
1937 // Wrap text contents\r
1938 if (text) {\r
1939 codeBlocks.push(trim(text));\r
1940 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
1941 }\r
1942\r
1943 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' mce_bogus="1">' + text + '</style>';\r
1944 });\r
1945\r
1946 // Wrap noscript elements\r
1947 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
1948 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
1949 });\r
1950 }\r
1951\r
1952 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
1953\r
1954 // Remove false bool attributes and force attributes into xhtml style attr="attr"\r
1955 h = h.replace(/<([\w:]+) [^>]*(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)[^>]*>/gi, function(val) {\r
1956 function handle(val, name, value) {\r
1957 // Remove false/0 attribs\r
1958 if (value === 'false' || value === '0')\r
1959 return '';\r
1960\r
1961 return ' ' + name + '="' + name + '"';\r
1962 };\r
1963\r
1964 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\"]([^\"]+)[\"]/gi, handle); // W3C\r
1965 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\']([^\']+)[\']/gi, handle); // W3C\r
1966 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=([^\s\"\'>]+)/gi, handle); // IE\r
1967 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)([\s>])/gi, ' $1="$1"$2'); // Force attr="attr"\r
1968\r
1969 return val;\r
1970 });\r
1971\r
1972 // Process all tags with src, href or style\r
1973 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {\r
1974 function handle(m, b, c) {\r
1975 var u = c;\r
1976\r
1977 // Tag already got a mce_ version\r
1978 if (a.indexOf('mce_' + b) != -1)\r
1979 return m;\r
1980\r
1981 if (b == 'style') {\r
1982 // No mce_style for elements with these since they might get resized by the user\r
1983 if (t._isRes(c))\r
1984 return m;\r
1985\r
1986 // Parse and serialize the style to convert for example uppercase styles like "BORDER: 1px"\r
1987 u = t.encode(t.serializeStyle(t.parseStyle(u)));\r
1988 } else if (b != 'coords' && b != 'shape') {\r
1989 if (s.url_converter)\r
1990 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));\r
1991 }\r
1992\r
1993 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';\r
1994 };\r
1995\r
1996 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C\r
1997 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C\r
1998\r
1999 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE\r
2000 });\r
2001\r
2002 // Restore script blocks\r
2003 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2004 return codeBlocks[idx];\r
2005 });\r
2006 }\r
2007\r
2008 return h;\r
2009 },\r
2010\r
2011 getOuterHTML : function(e) {\r
2012 var d;\r
2013\r
2014 e = this.get(e);\r
2015\r
2016 if (!e)\r
2017 return null;\r
2018\r
2019 if (e.outerHTML !== undefined)\r
2020 return e.outerHTML;\r
2021\r
2022 d = (e.ownerDocument || this.doc).createElement("body");\r
2023 d.appendChild(e.cloneNode(true));\r
2024\r
2025 return d.innerHTML;\r
2026 },\r
2027\r
2028 setOuterHTML : function(e, h, d) {\r
2029 var t = this;\r
2030\r
2031 function setHTML(e, h, d) {\r
2032 var n, tp;\r
2033 \r
2034 tp = d.createElement("body");\r
2035 tp.innerHTML = h;\r
2036\r
2037 n = tp.lastChild;\r
2038 while (n) {\r
2039 t.insertAfter(n.cloneNode(true), e);\r
2040 n = n.previousSibling;\r
2041 }\r
2042\r
2043 t.remove(e);\r
2044 };\r
2045\r
2046 return this.run(e, function(e) {\r
2047 e = t.get(e);\r
2048\r
2049 // Only set HTML on elements\r
2050 if (e.nodeType == 1) {\r
2051 d = d || e.ownerDocument || t.doc;\r
2052\r
2053 if (isIE) {\r
2054 try {\r
2055 // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2056 if (isIE && e.nodeType == 1)\r
2057 e.outerHTML = h;\r
2058 else\r
2059 setHTML(e, h, d);\r
2060 } catch (ex) {\r
2061 // Fix for unknown runtime error\r
2062 setHTML(e, h, d);\r
2063 }\r
2064 } else\r
2065 setHTML(e, h, d);\r
2066 }\r
2067 });\r
2068 },\r
2069\r
2070 decode : function(s) {\r
2071 var e, n, v;\r
2072\r
2073 // Look for entities to decode\r
2074 if (/&[^;]+;/.test(s)) {\r
2075 // Decode the entities using a div element not super efficient but less code\r
2076 e = this.doc.createElement("div");\r
2077 e.innerHTML = s;\r
2078\r
2079 n = e.firstChild;\r
2080 v = '';\r
2081\r
2082 if (n) {\r
2083 do {\r
2084 v += n.nodeValue;\r
2085 } while (n.nextSibling);\r
2086 }\r
2087\r
2088 return v || s;\r
2089 }\r
2090\r
2091 return s;\r
2092 },\r
2093\r
2094 encode : function(s) {\r
2095 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {\r
2096 switch (c) {\r
2097 case '&':\r
2098 return '&amp;';\r
2099\r
2100 case '"':\r
2101 return '&quot;';\r
2102\r
2103 case '<':\r
2104 return '&lt;';\r
2105\r
2106 case '>':\r
2107 return '&gt;';\r
2108 }\r
2109\r
2110 return c;\r
2111 }) : s;\r
2112 },\r
2113\r
2114 insertAfter : function(n, r) {\r
2115 var t = this;\r
2116\r
2117 r = t.get(r);\r
2118\r
2119 return this.run(n, function(n) {\r
2120 var p, ns;\r
2121\r
2122 p = r.parentNode;\r
2123 ns = r.nextSibling;\r
2124\r
2125 if (ns)\r
2126 p.insertBefore(n, ns);\r
2127 else\r
2128 p.appendChild(n);\r
2129\r
2130 return n;\r
2131 });\r
2132 },\r
2133\r
2134 isBlock : function(n) {\r
2135 if (n.nodeType && n.nodeType !== 1)\r
2136 return false;\r
2137\r
2138 n = n.nodeName || n;\r
2139\r
2140 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TH|TBODY|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);\r
2141 },\r
2142\r
2143 replace : function(n, o, k) {\r
2144 var t = this;\r
2145\r
2146 if (is(o, 'array'))\r
2147 n = n.cloneNode(true);\r
2148\r
2149 return t.run(o, function(o) {\r
2150 if (k) {\r
2151 each(o.childNodes, function(c) {\r
2152 n.appendChild(c.cloneNode(true));\r
2153 });\r
2154 }\r
2155\r
2156 // Fix IE psuedo leak for elements since replacing elements if fairly common\r
2157 // Will break parentNode for some unknown reason\r
2158 if (t.fixPsuedoLeaks && o.nodeType === 1) {\r
2159 o.parentNode.insertBefore(n, o);\r
2160 t.remove(o);\r
2161 return n;\r
2162 }\r
2163\r
2164 return o.parentNode.replaceChild(n, o);\r
2165 });\r
2166 },\r
2167\r
2168 findCommonAncestor : function(a, b) {\r
2169 var ps = a, pe;\r
2170\r
2171 while (ps) {\r
2172 pe = b;\r
2173\r
2174 while (pe && ps != pe)\r
2175 pe = pe.parentNode;\r
2176\r
2177 if (ps == pe)\r
2178 break;\r
2179\r
2180 ps = ps.parentNode;\r
2181 }\r
2182\r
2183 if (!ps && a.ownerDocument)\r
2184 return a.ownerDocument.documentElement;\r
2185\r
2186 return ps;\r
2187 },\r
2188\r
2189 toHex : function(s) {\r
2190 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2191\r
2192 function hex(s) {\r
2193 s = parseInt(s).toString(16);\r
2194\r
2195 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2196 };\r
2197\r
2198 if (c) {\r
2199 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2200\r
2201 return s;\r
2202 }\r
2203\r
2204 return s;\r
2205 },\r
2206\r
2207 getClasses : function() {\r
2208 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2209\r
2210 if (t.classes)\r
2211 return t.classes;\r
2212\r
2213 function addClasses(s) {\r
2214 // IE style imports\r
2215 each(s.imports, function(r) {\r
2216 addClasses(r);\r
2217 });\r
2218\r
2219 each(s.cssRules || s.rules, function(r) {\r
2220 // Real type or fake it on IE\r
2221 switch (r.type || 1) {\r
2222 // Rule\r
2223 case 1:\r
2224 if (r.selectorText) {\r
2225 each(r.selectorText.split(','), function(v) {\r
2226 v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2227\r
2228 // Is internal or it doesn't contain a class\r
2229 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2230 return;\r
2231\r
2232 // Remove everything but class name\r
2233 ov = v;\r
2234 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2235\r
2236 // Filter classes\r
2237 if (f && !(v = f(v, ov)))\r
2238 return;\r
2239\r
2240 if (!lo[v]) {\r
2241 cl.push({'class' : v});\r
2242 lo[v] = 1;\r
2243 }\r
2244 });\r
2245 }\r
2246 break;\r
2247\r
2248 // Import\r
2249 case 3:\r
2250 addClasses(r.styleSheet);\r
2251 break;\r
2252 }\r
2253 });\r
2254 };\r
2255\r
2256 try {\r
2257 each(t.doc.styleSheets, addClasses);\r
2258 } catch (ex) {\r
2259 // Ignore\r
2260 }\r
2261\r
2262 if (cl.length > 0)\r
2263 t.classes = cl;\r
2264\r
2265 return cl;\r
2266 },\r
2267\r
2268 run : function(e, f, s) {\r
2269 var t = this, o;\r
2270\r
2271 if (t.doc && typeof(e) === 'string')\r
2272 e = t.get(e);\r
2273\r
2274 if (!e)\r
2275 return false;\r
2276\r
2277 s = s || this;\r
2278 if (!e.nodeType && (e.length || e.length === 0)) {\r
2279 o = [];\r
2280\r
2281 each(e, function(e, i) {\r
2282 if (e) {\r
2283 if (typeof(e) == 'string')\r
2284 e = t.doc.getElementById(e);\r
2285\r
2286 o.push(f.call(s, e, i));\r
2287 }\r
2288 });\r
2289\r
2290 return o;\r
2291 }\r
2292\r
2293 return f.call(s, e);\r
2294 },\r
2295\r
2296 getAttribs : function(n) {\r
2297 var o;\r
2298\r
2299 n = this.get(n);\r
2300\r
2301 if (!n)\r
2302 return [];\r
2303\r
2304 if (isIE) {\r
2305 o = [];\r
2306\r
2307 // Object will throw exception in IE\r
2308 if (n.nodeName == 'OBJECT')\r
2309 return n.attributes;\r
2310\r
2311 // IE doesn't keep the selected attribute if you clone option elements\r
2312 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2313 o.push({specified : 1, nodeName : 'selected'});\r
2314\r
2315 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2316 n.cloneNode(false).outerHTML.replace(/<\/?[\w:]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=\w+|>/gi, '').replace(/[\w:]+/gi, function(a) {\r
2317 o.push({specified : 1, nodeName : a});\r
2318 });\r
2319\r
2320 return o;\r
2321 }\r
2322\r
2323 return n.attributes;\r
2324 },\r
2325\r
2326 destroy : function(s) {\r
2327 var t = this;\r
2328\r
2329 if (t.events)\r
2330 t.events.destroy();\r
2331\r
2332 t.win = t.doc = t.root = t.events = null;\r
2333\r
2334 // Manual destroy then remove unload handler\r
2335 if (!s)\r
2336 tinymce.removeUnload(t.destroy);\r
2337 },\r
2338\r
2339 createRng : function() {\r
2340 var d = this.doc;\r
2341\r
2342 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2343 },\r
2344\r
2345 split : function(pe, e, re) {\r
2346 var t = this, r = t.createRng(), bef, aft, pa;\r
2347\r
2348 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence\r
2349 // but we don't want that in our code since it serves no purpose\r
2350 // For example if this is chopped:\r
2351 // <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2352 // would produce:\r
2353 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2354 // this function will then trim of empty edges and produce:\r
2355 // <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2356 function trimEdge(n, na) {\r
2357 n = n[na];\r
2358\r
2359 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na]))\r
2360 t.remove(n[na]);\r
2361 };\r
2362\r
2363 function isEmpty(n) {\r
2364 n = t.getOuterHTML(n);\r
2365 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars\r
2366 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
2367\r
2368 return n.replace(/[ \t\r\n]+|&nbsp;|&#160;/g, '') == '';\r
2369 };\r
2370\r
2371 // Added until Gecko can create real HTML documents using implementation.createHTMLDocument\r
2372 // this is to future proof it if Gecko decides to implement the error checking for range methods.\r
2373 function nodeIndex(n) {\r
2374 var i = 0;\r
2375\r
2376 while (n.previousSibling) {\r
2377 i++;\r
2378 n = n.previousSibling;\r
2379 }\r
2380\r
2381 return i;\r
2382 };\r
2383\r
2384 if (pe && e) {\r
2385 // Get before chunk\r
2386 r.setStart(pe.parentNode, nodeIndex(pe));\r
2387 r.setEnd(e.parentNode, nodeIndex(e));\r
2388 bef = r.extractContents();\r
2389\r
2390 // Get after chunk\r
2391 r = t.createRng();\r
2392 r.setStart(e.parentNode, nodeIndex(e) + 1);\r
2393 r.setEnd(pe.parentNode, nodeIndex(pe) + 1);\r
2394 aft = r.extractContents();\r
2395\r
2396 // Insert chunks and remove parent\r
2397 pa = pe.parentNode;\r
2398\r
2399 // Remove right side edge of the before contents\r
2400 trimEdge(bef, 'lastChild');\r
2401\r
2402 if (!isEmpty(bef))\r
2403 pa.insertBefore(bef, pe);\r
2404\r
2405 if (re)\r
2406 pa.replaceChild(re, e);\r
2407 else\r
2408 pa.insertBefore(e, pe);\r
2409\r
2410 // Remove left site edge of the after contents\r
2411 trimEdge(aft, 'firstChild');\r
2412\r
2413 if (!isEmpty(aft))\r
2414 pa.insertBefore(aft, pe);\r
2415\r
2416 t.remove(pe);\r
2417\r
2418 return re || e;\r
2419 }\r
2420 },\r
2421\r
2422 bind : function(target, name, func, scope) {\r
2423 var t = this;\r
2424\r
2425 if (!t.events)\r
2426 t.events = new tinymce.dom.EventUtils();\r
2427\r
2428 return t.events.add(target, name, func, scope || this);\r
2429 },\r
2430\r
2431 unbind : function(target, name, func) {\r
2432 var t = this;\r
2433\r
2434 if (!t.events)\r
2435 t.events = new tinymce.dom.EventUtils();\r
2436\r
2437 return t.events.remove(target, name, func);\r
2438 },\r
2439\r
2440 _findSib : function(node, selector, name) {\r
2441 var t = this, f = selector;\r
2442\r
2443 if (node) {\r
2444 // If expression make a function of it using is\r
2445 if (is(f, 'string')) {\r
2446 f = function(node) {\r
2447 return t.is(node, selector);\r
2448 };\r
2449 }\r
2450\r
2451 // Loop all siblings\r
2452 for (node = node[name]; node; node = node[name]) {\r
2453 if (f(node))\r
2454 return node;\r
2455 }\r
2456 }\r
2457\r
2458 return null;\r
2459 },\r
2460\r
2461 _isRes : function(c) {\r
2462 // Is live resizble element\r
2463 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2464 }\r
2465\r
2466 /*\r
2467 walk : function(n, f, s) {\r
2468 var d = this.doc, w;\r
2469\r
2470 if (d.createTreeWalker) {\r
2471 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2472\r
2473 while ((n = w.nextNode()) != null)\r
2474 f.call(s || this, n);\r
2475 } else\r
2476 tinymce.walk(n, f, 'childNodes', s);\r
2477 }\r
2478 */\r
2479\r
2480 /*\r
2481 toRGB : function(s) {\r
2482 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2483\r
2484 if (c) {\r
2485 // #FFF -> #FFFFFF\r
2486 if (!is(c[3]))\r
2487 c[3] = c[2] = c[1];\r
2488\r
2489 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2490 }\r
2491\r
2492 return s;\r
2493 }\r
2494 */\r
2495 });\r
2496\r
2497 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2498})(tinymce);\r
2499\r
2500(function(ns) {\r
2501 // Traverse constants\r
2502 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend;\r
2503\r
2504 function indexOf(child, parent) {\r
2505 var i, node;\r
2506\r
2507 if (child.parentNode != parent)\r
2508 return -1;\r
2509\r
2510 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling)\r
2511 i++;\r
2512\r
2513 return i;\r
2514 };\r
2515\r
2516 function nodeIndex(n) {\r
2517 var i = 0;\r
2518\r
2519 while (n.previousSibling) {\r
2520 i++;\r
2521 n = n.previousSibling;\r
2522 }\r
2523\r
2524 return i;\r
2525 };\r
2526\r
2527 function getSelectedNode(container, offset) {\r
2528 var child;\r
2529\r
2530 if (container.nodeType == 3 /* TEXT_NODE */)\r
2531 return container;\r
2532\r
2533 if (offset < 0)\r
2534 return container;\r
2535\r
2536 child = container.firstChild;\r
2537 while (child != null && offset > 0) {\r
2538 --offset;\r
2539 child = child.nextSibling;\r
2540 }\r
2541\r
2542 if (child != null)\r
2543 return child;\r
2544\r
2545 return container;\r
2546 };\r
2547\r
2548 // Range constructor\r
2549 function Range(dom) {\r
2550 var d = dom.doc;\r
2551\r
2552 extend(this, {\r
2553 dom : dom,\r
2554\r
2555 // Inital states\r
2556 startContainer : d,\r
2557 startOffset : 0,\r
2558 endContainer : d,\r
2559 endOffset : 0,\r
2560 collapsed : true,\r
2561 commonAncestorContainer : d,\r
2562\r
2563 // Range constants\r
2564 START_TO_START : 0,\r
2565 START_TO_END : 1,\r
2566 END_TO_END : 2,\r
2567 END_TO_START : 3\r
2568 });\r
2569 };\r
2570\r
2571 // Add range methods\r
2572 extend(Range.prototype, {\r
2573 setStart : function(n, o) {\r
2574 this._setEndPoint(true, n, o);\r
2575 },\r
2576\r
2577 setEnd : function(n, o) {\r
2578 this._setEndPoint(false, n, o);\r
2579 },\r
2580\r
2581 setStartBefore : function(n) {\r
2582 this.setStart(n.parentNode, nodeIndex(n));\r
2583 },\r
2584\r
2585 setStartAfter : function(n) {\r
2586 this.setStart(n.parentNode, nodeIndex(n) + 1);\r
2587 },\r
2588\r
2589 setEndBefore : function(n) {\r
2590 this.setEnd(n.parentNode, nodeIndex(n));\r
2591 },\r
2592\r
2593 setEndAfter : function(n) {\r
2594 this.setEnd(n.parentNode, nodeIndex(n) + 1);\r
2595 },\r
2596\r
2597 collapse : function(ts) {\r
2598 var t = this;\r
2599\r
2600 if (ts) {\r
2601 t.endContainer = t.startContainer;\r
2602 t.endOffset = t.startOffset;\r
2603 } else {\r
2604 t.startContainer = t.endContainer;\r
2605 t.startOffset = t.endOffset;\r
2606 }\r
2607\r
2608 t.collapsed = true;\r
2609 },\r
2610\r
2611 selectNode : function(n) {\r
2612 this.setStartBefore(n);\r
2613 this.setEndAfter(n);\r
2614 },\r
2615\r
2616 selectNodeContents : function(n) {\r
2617 this.setStart(n, 0);\r
2618 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2619 },\r
2620\r
2621 compareBoundaryPoints : function(h, r) {\r
2622 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset;\r
2623\r
2624 // Check START_TO_START\r
2625 if (h === 0)\r
2626 return t._compareBoundaryPoints(sc, so, sc, so);\r
2627\r
2628 // Check START_TO_END\r
2629 if (h === 1)\r
2630 return t._compareBoundaryPoints(sc, so, ec, eo);\r
2631\r
2632 // Check END_TO_END\r
2633 if (h === 2)\r
2634 return t._compareBoundaryPoints(ec, eo, ec, eo);\r
2635\r
2636 // Check END_TO_START\r
2637 if (h === 3)\r
2638 return t._compareBoundaryPoints(ec, eo, sc, so);\r
2639 },\r
2640\r
2641 deleteContents : function() {\r
2642 this._traverse(DELETE);\r
2643 },\r
2644\r
2645 extractContents : function() {\r
2646 return this._traverse(EXTRACT);\r
2647 },\r
2648\r
2649 cloneContents : function() {\r
2650 return this._traverse(CLONE);\r
2651 },\r
2652\r
2653 insertNode : function(n) {\r
2654 var t = this, nn, o;\r
2655\r
2656 // Node is TEXT_NODE or CDATA\r
2657 if (n.nodeType === 3 || n.nodeType === 4) {\r
2658 nn = t.startContainer.splitText(t.startOffset);\r
2659 t.startContainer.parentNode.insertBefore(n, nn);\r
2660 } else {\r
2661 // Insert element node\r
2662 if (t.startContainer.childNodes.length > 0)\r
2663 o = t.startContainer.childNodes[t.startOffset];\r
2664\r
2665 t.startContainer.insertBefore(n, o);\r
2666 }\r
2667 },\r
2668\r
2669 surroundContents : function(n) {\r
2670 var t = this, f = t.extractContents();\r
2671\r
2672 t.insertNode(n);\r
2673 n.appendChild(f);\r
2674 t.selectNode(n);\r
2675 },\r
2676\r
2677 cloneRange : function() {\r
2678 var t = this;\r
2679\r
2680 return extend(new Range(t.dom), {\r
2681 startContainer : t.startContainer,\r
2682 startOffset : t.startOffset,\r
2683 endContainer : t.endContainer,\r
2684 endOffset : t.endOffset,\r
2685 collapsed : t.collapsed,\r
2686 commonAncestorContainer : t.commonAncestorContainer\r
2687 });\r
2688 },\r
2689\r
2690/*\r
2691 detach : function() {\r
2692 // Not implemented\r
2693 },\r
2694*/\r
2695 // Internal methods\r
2696\r
2697 _isCollapsed : function() {\r
2698 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset);\r
2699 },\r
2700\r
2701 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) {\r
2702 var c, offsetC, n, cmnRoot, childA, childB;\r
2703\r
2704 // In the first case the boundary-points have the same container. A is before B \r
2705 // if its offset is less than the offset of B, A is equal to B if its offset is \r
2706 // equal to the offset of B, and A is after B if its offset is greater than the \r
2707 // offset of B.\r
2708 if (containerA == containerB) {\r
2709 if (offsetA == offsetB) {\r
2710 return 0; // equal\r
2711 } else if (offsetA < offsetB) {\r
2712 return -1; // before\r
2713 } else {\r
2714 return 1; // after\r
2715 }\r
2716 }\r
2717\r
2718 // In the second case a child node C of the container of A is an ancestor \r
2719 // container of B. In this case, A is before B if the offset of A is less than or \r
2720 // equal to the index of the child node C and A is after B otherwise.\r
2721 c = containerB;\r
2722 while (c && c.parentNode != containerA) {\r
2723 c = c.parentNode;\r
2724 }\r
2725 if (c) {\r
2726 offsetC = 0;\r
2727 n = containerA.firstChild;\r
2728\r
2729 while (n != c && offsetC < offsetA) {\r
2730 offsetC++;\r
2731 n = n.nextSibling;\r
2732 }\r
2733\r
2734 if (offsetA <= offsetC) {\r
2735 return -1; // before\r
2736 } else {\r
2737 return 1; // after\r
2738 }\r
2739 }\r
2740\r
2741 // In the third case a child node C of the container of B is an ancestor container \r
2742 // of A. In this case, A is before B if the index of the child node C is less than \r
2743 // the offset of B and A is after B otherwise.\r
2744 c = containerA;\r
2745 while (c && c.parentNode != containerB) {\r
2746 c = c.parentNode;\r
2747 }\r
2748\r
2749 if (c) {\r
2750 offsetC = 0;\r
2751 n = containerB.firstChild;\r
2752\r
2753 while (n != c && offsetC < offsetB) {\r
2754 offsetC++;\r
2755 n = n.nextSibling;\r
2756 }\r
2757\r
2758 if (offsetC < offsetB) {\r
2759 return -1; // before\r
2760 } else {\r
2761 return 1; // after\r
2762 }\r
2763 }\r
2764\r
2765 // In the fourth case, none of three other cases hold: the containers of A and B \r
2766 // are siblings or descendants of sibling nodes. In this case, A is before B if \r
2767 // the container of A is before the container of B in a pre-order traversal of the\r
2768 // Ranges' context tree and A is after B otherwise.\r
2769 cmnRoot = this.dom.findCommonAncestor(containerA, containerB);\r
2770 childA = containerA;\r
2771\r
2772 while (childA && childA.parentNode != cmnRoot) {\r
2773 childA = childA.parentNode; \r
2774 }\r
2775\r
2776 if (!childA) {\r
2777 childA = cmnRoot;\r
2778 }\r
2779\r
2780 childB = containerB;\r
2781 while (childB && childB.parentNode != cmnRoot) {\r
2782 childB = childB.parentNode;\r
2783 }\r
2784\r
2785 if (!childB) {\r
2786 childB = cmnRoot;\r
2787 }\r
2788\r
2789 if (childA == childB) {\r
2790 return 0; // equal\r
2791 }\r
2792\r
2793 n = cmnRoot.firstChild;\r
2794 while (n) {\r
2795 if (n == childA) {\r
2796 return -1; // before\r
2797 }\r
2798\r
2799 if (n == childB) {\r
2800 return 1; // after\r
2801 }\r
2802\r
2803 n = n.nextSibling;\r
2804 }\r
2805 },\r
2806\r
2807 _setEndPoint : function(st, n, o) {\r
2808 var t = this, ec, sc;\r
2809\r
2810 if (st) {\r
2811 t.startContainer = n;\r
2812 t.startOffset = o;\r
2813 } else {\r
2814 t.endContainer = n;\r
2815 t.endOffset = o;\r
2816 }\r
2817\r
2818 // If one boundary-point of a Range is set to have a root container \r
2819 // other than the current one for the Range, the Range is collapsed to \r
2820 // the new position. This enforces the restriction that both boundary-\r
2821 // points of a Range must have the same root container.\r
2822 ec = t.endContainer;\r
2823 while (ec.parentNode)\r
2824 ec = ec.parentNode;\r
2825\r
2826 sc = t.startContainer;\r
2827 while (sc.parentNode)\r
2828 sc = sc.parentNode;\r
2829\r
2830 if (sc != ec) {\r
2831 t.collapse(st);\r
2832 } else {\r
2833 // The start position of a Range is guaranteed to never be after the \r
2834 // end position. To enforce this restriction, if the start is set to \r
2835 // be at a position after the end, the Range is collapsed to that \r
2836 // position.\r
2837 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0)\r
2838 t.collapse(st);\r
2839 }\r
2840\r
2841 t.collapsed = t._isCollapsed();\r
2842 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer);\r
2843 },\r
2844\r
2845 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :)\r
2846\r
2847 _traverse : function(how) {\r
2848 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2849\r
2850 if (t.startContainer == t.endContainer)\r
2851 return t._traverseSameContainer(how);\r
2852\r
2853 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {\r
2854 if (p == t.startContainer)\r
2855 return t._traverseCommonStartContainer(c, how);\r
2856\r
2857 ++endContainerDepth;\r
2858 }\r
2859\r
2860 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {\r
2861 if (p == t.endContainer)\r
2862 return t._traverseCommonEndContainer(c, how);\r
2863\r
2864 ++startContainerDepth;\r
2865 }\r
2866\r
2867 depthDiff = startContainerDepth - endContainerDepth;\r
2868\r
2869 startNode = t.startContainer;\r
2870 while (depthDiff > 0) {\r
2871 startNode = startNode.parentNode;\r
2872 depthDiff--;\r
2873 }\r
2874\r
2875 endNode = t.endContainer;\r
2876 while (depthDiff < 0) {\r
2877 endNode = endNode.parentNode;\r
2878 depthDiff++;\r
2879 }\r
2880\r
2881 // ascend the ancestor hierarchy until we have a common parent.\r
2882 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
2883 startNode = sp;\r
2884 endNode = ep;\r
2885 }\r
2886\r
2887 return t._traverseCommonAncestors(startNode, endNode, how);\r
2888 },\r
2889\r
2890 _traverseSameContainer : function(how) {\r
2891 var t = this, frag, s, sub, n, cnt, sibling, xferNode;\r
2892\r
2893 if (how != DELETE)\r
2894 frag = t.dom.doc.createDocumentFragment();\r
2895\r
2896 // If selection is empty, just return the fragment\r
2897 if (t.startOffset == t.endOffset)\r
2898 return frag;\r
2899\r
2900 // Text node needs special case handling\r
2901 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) {\r
2902 // get the substring\r
2903 s = t.startContainer.nodeValue;\r
2904 sub = s.substring(t.startOffset, t.endOffset);\r
2905\r
2906 // set the original text node to its new value\r
2907 if (how != CLONE) {\r
2908 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset);\r
2909\r
2910 // Nothing is partially selected, so collapse to start point\r
2911 t.collapse(true);\r
2912 }\r
2913\r
2914 if (how == DELETE)\r
2915 return null;\r
2916\r
2917 frag.appendChild(t.dom.doc.createTextNode(sub));\r
2918 return frag;\r
2919 }\r
2920\r
2921 // Copy nodes between the start/end offsets.\r
2922 n = getSelectedNode(t.startContainer, t.startOffset);\r
2923 cnt = t.endOffset - t.startOffset;\r
2924\r
2925 while (cnt > 0) {\r
2926 sibling = n.nextSibling;\r
2927 xferNode = t._traverseFullySelected(n, how);\r
2928\r
2929 if (frag)\r
2930 frag.appendChild( xferNode );\r
2931\r
2932 --cnt;\r
2933 n = sibling;\r
2934 }\r
2935\r
2936 // Nothing is partially selected, so collapse to start point\r
2937 if (how != CLONE)\r
2938 t.collapse(true);\r
2939\r
2940 return frag;\r
2941 },\r
2942\r
2943 _traverseCommonStartContainer : function(endAncestor, how) {\r
2944 var t = this, frag, n, endIdx, cnt, sibling, xferNode;\r
2945\r
2946 if (how != DELETE)\r
2947 frag = t.dom.doc.createDocumentFragment();\r
2948\r
2949 n = t._traverseRightBoundary(endAncestor, how);\r
2950\r
2951 if (frag)\r
2952 frag.appendChild(n);\r
2953\r
2954 endIdx = indexOf(endAncestor, t.startContainer);\r
2955 cnt = endIdx - t.startOffset;\r
2956\r
2957 if (cnt <= 0) {\r
2958 // Collapse to just before the endAncestor, which \r
2959 // is partially selected.\r
2960 if (how != CLONE) {\r
2961 t.setEndBefore(endAncestor);\r
2962 t.collapse(false);\r
2963 }\r
2964\r
2965 return frag;\r
2966 }\r
2967\r
2968 n = endAncestor.previousSibling;\r
2969 while (cnt > 0) {\r
2970 sibling = n.previousSibling;\r
2971 xferNode = t._traverseFullySelected(n, how);\r
2972\r
2973 if (frag)\r
2974 frag.insertBefore(xferNode, frag.firstChild);\r
2975\r
2976 --cnt;\r
2977 n = sibling;\r
2978 }\r
2979\r
2980 // Collapse to just before the endAncestor, which \r
2981 // is partially selected.\r
2982 if (how != CLONE) {\r
2983 t.setEndBefore(endAncestor);\r
2984 t.collapse(false);\r
2985 }\r
2986\r
2987 return frag;\r
2988 },\r
2989\r
2990 _traverseCommonEndContainer : function(startAncestor, how) {\r
2991 var t = this, frag, startIdx, n, cnt, sibling, xferNode;\r
2992\r
2993 if (how != DELETE)\r
2994 frag = t.dom.doc.createDocumentFragment();\r
2995\r
2996 n = t._traverseLeftBoundary(startAncestor, how);\r
2997 if (frag)\r
2998 frag.appendChild(n);\r
2999\r
3000 startIdx = indexOf(startAncestor, t.endContainer);\r
3001 ++startIdx; // Because we already traversed it....\r
3002\r
3003 cnt = t.endOffset - startIdx;\r
3004 n = startAncestor.nextSibling;\r
3005 while (cnt > 0) {\r
3006 sibling = n.nextSibling;\r
3007 xferNode = t._traverseFullySelected(n, how);\r
3008\r
3009 if (frag)\r
3010 frag.appendChild(xferNode);\r
3011\r
3012 --cnt;\r
3013 n = sibling;\r
3014 }\r
3015\r
3016 if (how != CLONE) {\r
3017 t.setStartAfter(startAncestor);\r
3018 t.collapse(true);\r
3019 }\r
3020\r
3021 return frag;\r
3022 },\r
3023\r
3024 _traverseCommonAncestors : function(startAncestor, endAncestor, how) {\r
3025 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3026\r
3027 if (how != DELETE)\r
3028 frag = t.dom.doc.createDocumentFragment();\r
3029\r
3030 n = t._traverseLeftBoundary(startAncestor, how);\r
3031 if (frag)\r
3032 frag.appendChild(n);\r
3033\r
3034 commonParent = startAncestor.parentNode;\r
3035 startOffset = indexOf(startAncestor, commonParent);\r
3036 endOffset = indexOf(endAncestor, commonParent);\r
3037 ++startOffset;\r
3038\r
3039 cnt = endOffset - startOffset;\r
3040 sibling = startAncestor.nextSibling;\r
3041\r
3042 while (cnt > 0) {\r
3043 nextSibling = sibling.nextSibling;\r
3044 n = t._traverseFullySelected(sibling, how);\r
3045\r
3046 if (frag)\r
3047 frag.appendChild(n);\r
3048\r
3049 sibling = nextSibling;\r
3050 --cnt;\r
3051 }\r
3052\r
3053 n = t._traverseRightBoundary(endAncestor, how);\r
3054\r
3055 if (frag)\r
3056 frag.appendChild(n);\r
3057\r
3058 if (how != CLONE) {\r
3059 t.setStartAfter(startAncestor);\r
3060 t.collapse(true);\r
3061 }\r
3062\r
3063 return frag;\r
3064 },\r
3065\r
3066 _traverseRightBoundary : function(root, how) {\r
3067 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent;\r
3068 var isFullySelected = next != t.endContainer;\r
3069\r
3070 if (next == root)\r
3071 return t._traverseNode(next, isFullySelected, false, how);\r
3072\r
3073 parent = next.parentNode;\r
3074 clonedParent = t._traverseNode(parent, false, false, how);\r
3075\r
3076 while (parent != null) {\r
3077 while (next != null) {\r
3078 prevSibling = next.previousSibling;\r
3079 clonedChild = t._traverseNode(next, isFullySelected, false, how);\r
3080\r
3081 if (how != DELETE)\r
3082 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3083\r
3084 isFullySelected = true;\r
3085 next = prevSibling;\r
3086 }\r
3087\r
3088 if (parent == root)\r
3089 return clonedParent;\r
3090\r
3091 next = parent.previousSibling;\r
3092 parent = parent.parentNode;\r
3093\r
3094 clonedGrandParent = t._traverseNode(parent, false, false, how);\r
3095\r
3096 if (how != DELETE)\r
3097 clonedGrandParent.appendChild(clonedParent);\r
3098\r
3099 clonedParent = clonedGrandParent;\r
3100 }\r
3101\r
3102 // should never occur\r
3103 return null;\r
3104 },\r
3105\r
3106 _traverseLeftBoundary : function(root, how) {\r
3107 var t = this, next = getSelectedNode(t.startContainer, t.startOffset);\r
3108 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3109\r
3110 if (next == root)\r
3111 return t._traverseNode(next, isFullySelected, true, how);\r
3112\r
3113 parent = next.parentNode;\r
3114 clonedParent = t._traverseNode(parent, false, true, how);\r
3115\r
3116 while (parent != null) {\r
3117 while (next != null) {\r
3118 nextSibling = next.nextSibling;\r
3119 clonedChild = t._traverseNode(next, isFullySelected, true, how);\r
3120\r
3121 if (how != DELETE)\r
3122 clonedParent.appendChild(clonedChild);\r
3123\r
3124 isFullySelected = true;\r
3125 next = nextSibling;\r
3126 }\r
3127\r
3128 if (parent == root)\r
3129 return clonedParent;\r
3130\r
3131 next = parent.nextSibling;\r
3132 parent = parent.parentNode;\r
3133\r
3134 clonedGrandParent = t._traverseNode(parent, false, true, how);\r
3135\r
3136 if (how != DELETE)\r
3137 clonedGrandParent.appendChild(clonedParent);\r
3138\r
3139 clonedParent = clonedGrandParent;\r
3140 }\r
3141\r
3142 // should never occur\r
3143 return null;\r
3144 },\r
3145\r
3146 _traverseNode : function(n, isFullySelected, isLeft, how) {\r
3147 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3148\r
3149 if (isFullySelected)\r
3150 return t._traverseFullySelected(n, how);\r
3151\r
3152 if (n.nodeType == 3 /* TEXT_NODE */) {\r
3153 txtValue = n.nodeValue;\r
3154\r
3155 if (isLeft) {\r
3156 offset = t.startOffset;\r
3157 newNodeValue = txtValue.substring(offset);\r
3158 oldNodeValue = txtValue.substring(0, offset);\r
3159 } else {\r
3160 offset = t.endOffset;\r
3161 newNodeValue = txtValue.substring(0, offset);\r
3162 oldNodeValue = txtValue.substring(offset);\r
3163 }\r
3164\r
3165 if (how != CLONE)\r
3166 n.nodeValue = oldNodeValue;\r
3167\r
3168 if (how == DELETE)\r
3169 return null;\r
3170\r
3171 newNode = n.cloneNode(false);\r
3172 newNode.nodeValue = newNodeValue;\r
3173\r
3174 return newNode;\r
3175 }\r
3176\r
3177 if (how == DELETE)\r
3178 return null;\r
3179\r
3180 return n.cloneNode(false);\r
3181 },\r
3182\r
3183 _traverseFullySelected : function(n, how) {\r
3184 var t = this;\r
3185\r
3186 if (how != DELETE)\r
3187 return how == CLONE ? n.cloneNode(true) : n;\r
3188\r
3189 n.parentNode.removeChild(n);\r
3190 return null;\r
3191 }\r
3192 });\r
3193\r
3194 ns.Range = Range;\r
3195})(tinymce.dom);\r
3196(function() {\r
3197 function Selection(selection) {\r
3198 var t = this, invisibleChar = '\uFEFF', range, lastIERng;\r
3199\r
3200 function compareRanges(rng1, rng2) {\r
3201 if (rng1 && rng2) {\r
3202 // Both are control ranges and the selected element matches\r
3203 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
3204 return 1;\r
3205\r
3206 // Both are text ranges and the range matches\r
3207 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
3208 return 1;\r
3209 }\r
3210\r
3211 return 0;\r
3212 };\r
3213\r
3214 function getRange() {\r
3215 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos, endPos, element, sc, ec, collapsed;\r
3216\r
3217 function findIndex(element) {\r
3218 var nl = element.parentNode.childNodes, i;\r
3219\r
3220 for (i = nl.length - 1; i >= 0; i--) {\r
3221 if (nl[i] == element)\r
3222 return i;\r
3223 }\r
3224\r
3225 return -1;\r
3226 };\r
3227\r
3228 function findEndPoint(start) {\r
3229 var rng = ieRange.duplicate(), parent, i, nl, n, offset = 0, index = 0, pos, tmpRng;\r
3230\r
3231 // Insert marker character\r
3232 rng.collapse(start);\r
3233 parent = rng.parentElement();\r
3234 rng.pasteHTML(invisibleChar); // Needs to be a pasteHTML instead of .text = since IE has a bug with nodeValue\r
3235\r
3236 // Find marker character\r
3237 nl = parent.childNodes;\r
3238 for (i = 0; i < nl.length; i++) {\r
3239 n = nl[i];\r
3240\r
3241 // Calculate node index excluding text node fragmentation\r
3242 if (i > 0 && (n.nodeType !== 3 || nl[i - 1].nodeType !== 3))\r
3243 index++;\r
3244\r
3245 // If text node then calculate offset\r
3246 if (n.nodeType === 3) {\r
3247 // Look for marker\r
3248 pos = n.nodeValue.indexOf(invisibleChar);\r
3249 if (pos !== -1) {\r
3250 offset += pos;\r
3251 break;\r
3252 }\r
3253\r
3254 offset += n.nodeValue.length;\r
3255 } else\r
3256 offset = 0;\r
3257 }\r
3258\r
3259 // Remove marker character\r
3260 rng.moveStart('character', -1);\r
3261 rng.text = '';\r
3262\r
3263 return {index : index, offset : offset, parent : parent};\r
3264 };\r
3265\r
3266 // If selection is outside the current document just return an empty range\r
3267 element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3268 if (element.ownerDocument != dom.doc)\r
3269 return domRange;\r
3270\r
3271 // Handle control selection or text selection of a image\r
3272 if (ieRange.item || !element.hasChildNodes()) {\r
3273 domRange.setStart(element.parentNode, findIndex(element));\r
3274 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3275\r
3276 return domRange;\r
3277 }\r
3278\r
3279 // Check collapsed state\r
3280 collapsed = selection.isCollapsed();\r
3281\r
3282 // Find start and end pos index and offset\r
3283 startPos = findEndPoint(true);\r
3284 endPos = findEndPoint(false);\r
3285\r
3286 // Normalize the elements to avoid fragmented dom\r
3287 startPos.parent.normalize();\r
3288 endPos.parent.normalize();\r
3289\r
3290 // Set start container and offset\r
3291 sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)];\r
3292\r
3293 if (sc.nodeType != 3)\r
3294 domRange.setStart(startPos.parent, startPos.index);\r
3295 else\r
3296 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset);\r
3297\r
3298 // Set end container and offset\r
3299 ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)];\r
3300\r
3301 if (ec.nodeType != 3) {\r
3302 if (!collapsed)\r
3303 endPos.index++;\r
3304\r
3305 domRange.setEnd(endPos.parent, endPos.index);\r
3306 } else\r
3307 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset);\r
3308\r
3309 // If not collapsed then make sure offsets are valid\r
3310 if (!collapsed) {\r
3311 sc = domRange.startContainer;\r
3312 if (sc.nodeType == 1)\r
3313 domRange.setStart(sc, Math.min(domRange.startOffset, sc.childNodes.length));\r
3314\r
3315 ec = domRange.endContainer;\r
3316 if (ec.nodeType == 1)\r
3317 domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length));\r
3318 }\r
3319\r
3320 // Restore selection to new range\r
3321 t.addRange(domRange);\r
3322\r
3323 return domRange;\r
3324 };\r
3325\r
3326 this.addRange = function(rng) {\r
3327 var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo;\r
3328\r
3329 // Setup some shorter versions\r
3330 sc = rng.startContainer;\r
3331 so = rng.startOffset;\r
3332 ec = rng.endContainer;\r
3333 eo = rng.endOffset;\r
3334 ieRng = body.createTextRange();\r
3335\r
3336 // Find element\r
3337 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;\r
3338 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;\r
3339\r
3340 // Single element selection\r
3341 if (sc == ec && sc.nodeType == 1) {\r
3342 // Make control selection for some elements\r
3343 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {\r
3344 ieRng = body.createControlRange();\r
3345 ieRng.addElement(sc);\r
3346 } else {\r
3347 ieRng = body.createTextRange();\r
3348\r
3349 // Padd empty elements with invisible character\r
3350 if (!sc.hasChildNodes() && sc.canHaveHTML)\r
3351 sc.innerHTML = invisibleChar;\r
3352\r
3353 // Select element contents\r
3354 ieRng.moveToElementText(sc);\r
3355\r
3356 // If it's only containing a padding remove it so the caret remains\r
3357 if (sc.innerHTML == invisibleChar) {\r
3358 ieRng.collapse(true);\r
3359 sc.removeChild(sc.firstChild);\r
3360 }\r
3361 }\r
3362\r
3363 if (so == eo)\r
3364 ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);\r
3365\r
3366 ieRng.select();\r
3367\r
3368 return;\r
3369 }\r
3370\r
3371 function getCharPos(container, offset) {\r
3372 var nodeVal, rng, pos;\r
3373\r
3374 if (container.nodeType != 3)\r
3375 return -1;\r
3376\r
3377 nodeVal = container.nodeValue;\r
3378 rng = body.createTextRange();\r
3379\r
3380 // Insert marker at offset position\r
3381 container.nodeValue = nodeVal.substring(0, offset) + invisibleChar + nodeVal.substring(offset);\r
3382\r
3383 // Find char pos of marker and remove it\r
3384 rng.moveToElementText(container.parentNode);\r
3385 rng.findText(invisibleChar);\r
3386 pos = Math.abs(rng.moveStart('character', -0xFFFFF));\r
3387 container.nodeValue = nodeVal;\r
3388\r
3389 return pos;\r
3390 };\r
3391\r
3392 // Collapsed range\r
3393 if (rng.collapsed) {\r
3394 pos = getCharPos(sc, so);\r
3395\r
3396 ieRng = body.createTextRange();\r
3397 ieRng.move('character', pos);\r
3398 ieRng.select();\r
3399\r
3400 return;\r
3401 } else {\r
3402 // If same text container\r
3403 if (sc == ec && sc.nodeType == 3) {\r
3404 startPos = getCharPos(sc, so);\r
3405\r
3406 ieRng = body.createTextRange();\r
3407 ieRng.move('character', startPos);\r
3408 ieRng.moveEnd('character', eo - so);\r
3409 ieRng.select();\r
3410\r
3411 return;\r
3412 }\r
3413\r
3414 // Get caret positions\r
3415 startPos = getCharPos(sc, so);\r
3416 endPos = getCharPos(ec, eo);\r
3417 ieRng = body.createTextRange();\r
3418\r
3419 // Move start of range to start character position or start element\r
3420 if (startPos == -1) {\r
3421 ieRng.moveToElementText(sc);\r
3422 startPos = 0;\r
3423 } else\r
3424 ieRng.move('character', startPos);\r
3425\r
3426 // Move end of range to end character position or end element\r
3427 tmpRng = body.createTextRange();\r
3428\r
3429 if (endPos == -1)\r
3430 tmpRng.moveToElementText(ec);\r
3431 else\r
3432 tmpRng.move('character', endPos);\r
3433\r
3434 ieRng.setEndPoint('EndToEnd', tmpRng);\r
3435 ieRng.select();\r
3436\r
3437 return;\r
3438 }\r
3439 };\r
3440\r
3441 this.getRangeAt = function() {\r
3442 // Setup new range if the cache is empty\r
3443 if (!range || !compareRanges(lastIERng, selection.getRng())) {\r
3444 range = getRange();\r
3445\r
3446 // Store away text range for next call\r
3447 lastIERng = selection.getRng();\r
3448 }\r
3449\r
3450 // Return cached range\r
3451 return range;\r
3452 };\r
3453\r
3454 this.destroy = function() {\r
3455 // Destroy cached range and last IE range to avoid memory leaks\r
3456 lastIERng = range = null;\r
3457 };\r
3458 };\r
3459\r
3460 // Expose the selection object\r
3461 tinymce.dom.TridentSelection = Selection;\r
3462})();\r
3463\r
3464/*\r
3465 * Sizzle CSS Selector Engine - v1.0\r
3466 * Copyright 2009, The Dojo Foundation\r
3467 * Released under the MIT, BSD, and GPL Licenses.\r
3468 * More information: http://sizzlejs.com/\r
3469 */\r
3470(function(){\r
3471\r
3472var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,\r
3473 done = 0,\r
3474 toString = Object.prototype.toString,\r
3475 hasDuplicate = false;\r
3476\r
3477var Sizzle = function(selector, context, results, seed) {\r
3478 results = results || [];\r
3479 var origContext = context = context || document;\r
3480\r
3481 if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
3482 return [];\r
3483 }\r
3484 \r
3485 if ( !selector || typeof selector !== "string" ) {\r
3486 return results;\r
3487 }\r
3488\r
3489 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);\r
3490 \r
3491 // Reset the position of the chunker regexp (start from head)\r
3492 chunker.lastIndex = 0;\r
3493 \r
3494 while ( (m = chunker.exec(selector)) !== null ) {\r
3495 parts.push( m[1] );\r
3496 \r
3497 if ( m[2] ) {\r
3498 extra = RegExp.rightContext;\r
3499 break;\r
3500 }\r
3501 }\r
3502\r
3503 if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
3504 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
3505 set = posProcess( parts[0] + parts[1], context );\r
3506 } else {\r
3507 set = Expr.relative[ parts[0] ] ?\r
3508 [ context ] :\r
3509 Sizzle( parts.shift(), context );\r
3510\r
3511 while ( parts.length ) {\r
3512 selector = parts.shift();\r
3513\r
3514 if ( Expr.relative[ selector ] )\r
3515 selector += parts.shift();\r
3516\r
3517 set = posProcess( selector, set );\r
3518 }\r
3519 }\r
3520 } else {\r
3521 // Take a shortcut and set the context if the root selector is an ID\r
3522 // (but not if it'll be faster if the inner selector is an ID)\r
3523 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
3524 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
3525 var ret = Sizzle.find( parts.shift(), context, contextXML );\r
3526 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
3527 }\r
3528\r
3529 if ( context ) {\r
3530 var ret = seed ?\r
3531 { expr: parts.pop(), set: makeArray(seed) } :\r
3532 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
3533 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
3534\r
3535 if ( parts.length > 0 ) {\r
3536 checkSet = makeArray(set);\r
3537 } else {\r
3538 prune = false;\r
3539 }\r
3540\r
3541 while ( parts.length ) {\r
3542 var cur = parts.pop(), pop = cur;\r
3543\r
3544 if ( !Expr.relative[ cur ] ) {\r
3545 cur = "";\r
3546 } else {\r
3547 pop = parts.pop();\r
3548 }\r
3549\r
3550 if ( pop == null ) {\r
3551 pop = context;\r
3552 }\r
3553\r
3554 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
3555 }\r
3556 } else {\r
3557 checkSet = parts = [];\r
3558 }\r
3559 }\r
3560\r
3561 if ( !checkSet ) {\r
3562 checkSet = set;\r
3563 }\r
3564\r
3565 if ( !checkSet ) {\r
3566 throw "Syntax error, unrecognized expression: " + (cur || selector);\r
3567 }\r
3568\r
3569 if ( toString.call(checkSet) === "[object Array]" ) {\r
3570 if ( !prune ) {\r
3571 results.push.apply( results, checkSet );\r
3572 } else if ( context && context.nodeType === 1 ) {\r
3573 for ( var i = 0; checkSet[i] != null; i++ ) {\r
3574 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {\r
3575 results.push( set[i] );\r
3576 }\r
3577 }\r
3578 } else {\r
3579 for ( var i = 0; checkSet[i] != null; i++ ) {\r
3580 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
3581 results.push( set[i] );\r
3582 }\r
3583 }\r
3584 }\r
3585 } else {\r
3586 makeArray( checkSet, results );\r
3587 }\r
3588\r
3589 if ( extra ) {\r
3590 Sizzle( extra, origContext, results, seed );\r
3591 Sizzle.uniqueSort( results );\r
3592 }\r
3593\r
3594 return results;\r
3595};\r
3596\r
3597Sizzle.uniqueSort = function(results){\r
3598 if ( sortOrder ) {\r
3599 hasDuplicate = false;\r
3600 results.sort(sortOrder);\r
3601\r
3602 if ( hasDuplicate ) {\r
3603 for ( var i = 1; i < results.length; i++ ) {\r
3604 if ( results[i] === results[i-1] ) {\r
3605 results.splice(i--, 1);\r
3606 }\r
3607 }\r
3608 }\r
3609 }\r
3610};\r
3611\r
3612Sizzle.matches = function(expr, set){\r
3613 return Sizzle(expr, null, null, set);\r
3614};\r
3615\r
3616Sizzle.find = function(expr, context, isXML){\r
3617 var set, match;\r
3618\r
3619 if ( !expr ) {\r
3620 return [];\r
3621 }\r
3622\r
3623 for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
3624 var type = Expr.order[i], match;\r
3625 \r
3626 if ( (match = Expr.match[ type ].exec( expr )) ) {\r
3627 var left = RegExp.leftContext;\r
3628\r
3629 if ( left.substr( left.length - 1 ) !== "\\" ) {\r
3630 match[1] = (match[1] || "").replace(/\\/g, "");\r
3631 set = Expr.find[ type ]( match, context, isXML );\r
3632 if ( set != null ) {\r
3633 expr = expr.replace( Expr.match[ type ], "" );\r
3634 break;\r
3635 }\r
3636 }\r
3637 }\r
3638 }\r
3639\r
3640 if ( !set ) {\r
3641 set = context.getElementsByTagName("*");\r
3642 }\r
3643\r
3644 return {set: set, expr: expr};\r
3645};\r
3646\r
3647Sizzle.filter = function(expr, set, inplace, not){\r
3648 var old = expr, result = [], curLoop = set, match, anyFound,\r
3649 isXMLFilter = set && set[0] && isXML(set[0]);\r
3650\r
3651 while ( expr && set.length ) {\r
3652 for ( var type in Expr.filter ) {\r
3653 if ( (match = Expr.match[ type ].exec( expr )) != null ) {\r
3654 var filter = Expr.filter[ type ], found, item;\r
3655 anyFound = false;\r
3656\r
3657 if ( curLoop == result ) {\r
3658 result = [];\r
3659 }\r
3660\r
3661 if ( Expr.preFilter[ type ] ) {\r
3662 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
3663\r
3664 if ( !match ) {\r
3665 anyFound = found = true;\r
3666 } else if ( match === true ) {\r
3667 continue;\r
3668 }\r
3669 }\r
3670\r
3671 if ( match ) {\r
3672 for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
3673 if ( item ) {\r
3674 found = filter( item, match, i, curLoop );\r
3675 var pass = not ^ !!found;\r
3676\r
3677 if ( inplace && found != null ) {\r
3678 if ( pass ) {\r
3679 anyFound = true;\r
3680 } else {\r
3681 curLoop[i] = false;\r
3682 }\r
3683 } else if ( pass ) {\r
3684 result.push( item );\r
3685 anyFound = true;\r
3686 }\r
3687 }\r
3688 }\r
3689 }\r
3690\r
3691 if ( found !== undefined ) {\r
3692 if ( !inplace ) {\r
3693 curLoop = result;\r
3694 }\r
3695\r
3696 expr = expr.replace( Expr.match[ type ], "" );\r
3697\r
3698 if ( !anyFound ) {\r
3699 return [];\r
3700 }\r
3701\r
3702 break;\r
3703 }\r
3704 }\r
3705 }\r
3706\r
3707 // Improper expression\r
3708 if ( expr == old ) {\r
3709 if ( anyFound == null ) {\r
3710 throw "Syntax error, unrecognized expression: " + expr;\r
3711 } else {\r
3712 break;\r
3713 }\r
3714 }\r
3715\r
3716 old = expr;\r
3717 }\r
3718\r
3719 return curLoop;\r
3720};\r
3721\r
3722var Expr = Sizzle.selectors = {\r
3723 order: [ "ID", "NAME", "TAG" ],\r
3724 match: {\r
3725 ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,\r
3726 CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,\r
3727 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,\r
3728 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
3729 TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,\r
3730 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,\r
3731 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,\r
3732 PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/\r
3733 },\r
3734 attrMap: {\r
3735 "class": "className",\r
3736 "for": "htmlFor"\r
3737 },\r
3738 attrHandle: {\r
3739 href: function(elem){\r
3740 return elem.getAttribute("href");\r
3741 }\r
3742 },\r
3743 relative: {\r
3744 "+": function(checkSet, part, isXML){\r
3745 var isPartStr = typeof part === "string",\r
3746 isTag = isPartStr && !/\W/.test(part),\r
3747 isPartStrNotTag = isPartStr && !isTag;\r
3748\r
3749 if ( isTag && !isXML ) {\r
3750 part = part.toUpperCase();\r
3751 }\r
3752\r
3753 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
3754 if ( (elem = checkSet[i]) ) {\r
3755 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
3756\r
3757 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?\r
3758 elem || false :\r
3759 elem === part;\r
3760 }\r
3761 }\r
3762\r
3763 if ( isPartStrNotTag ) {\r
3764 Sizzle.filter( part, checkSet, true );\r
3765 }\r
3766 },\r
3767 ">": function(checkSet, part, isXML){\r
3768 var isPartStr = typeof part === "string";\r
3769\r
3770 if ( isPartStr && !/\W/.test(part) ) {\r
3771 part = isXML ? part : part.toUpperCase();\r
3772\r
3773 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
3774 var elem = checkSet[i];\r
3775 if ( elem ) {\r
3776 var parent = elem.parentNode;\r
3777 checkSet[i] = parent.nodeName === part ? parent : false;\r
3778 }\r
3779 }\r
3780 } else {\r
3781 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
3782 var elem = checkSet[i];\r
3783 if ( elem ) {\r
3784 checkSet[i] = isPartStr ?\r
3785 elem.parentNode :\r
3786 elem.parentNode === part;\r
3787 }\r
3788 }\r
3789\r
3790 if ( isPartStr ) {\r
3791 Sizzle.filter( part, checkSet, true );\r
3792 }\r
3793 }\r
3794 },\r
3795 "": function(checkSet, part, isXML){\r
3796 var doneName = done++, checkFn = dirCheck;\r
3797\r
3798 if ( !part.match(/\W/) ) {\r
3799 var nodeCheck = part = isXML ? part : part.toUpperCase();\r
3800 checkFn = dirNodeCheck;\r
3801 }\r
3802\r
3803 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
3804 },\r
3805 "~": function(checkSet, part, isXML){\r
3806 var doneName = done++, checkFn = dirCheck;\r
3807\r
3808 if ( typeof part === "string" && !part.match(/\W/) ) {\r
3809 var nodeCheck = part = isXML ? part : part.toUpperCase();\r
3810 checkFn = dirNodeCheck;\r
3811 }\r
3812\r
3813 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
3814 }\r
3815 },\r
3816 find: {\r
3817 ID: function(match, context, isXML){\r
3818 if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
3819 var m = context.getElementById(match[1]);\r
3820 return m ? [m] : [];\r
3821 }\r
3822 },\r
3823 NAME: function(match, context, isXML){\r
3824 if ( typeof context.getElementsByName !== "undefined" ) {\r
3825 var ret = [], results = context.getElementsByName(match[1]);\r
3826\r
3827 for ( var i = 0, l = results.length; i < l; i++ ) {\r
3828 if ( results[i].getAttribute("name") === match[1] ) {\r
3829 ret.push( results[i] );\r
3830 }\r
3831 }\r
3832\r
3833 return ret.length === 0 ? null : ret;\r
3834 }\r
3835 },\r
3836 TAG: function(match, context){\r
3837 return context.getElementsByTagName(match[1]);\r
3838 }\r
3839 },\r
3840 preFilter: {\r
3841 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
3842 match = " " + match[1].replace(/\\/g, "") + " ";\r
3843\r
3844 if ( isXML ) {\r
3845 return match;\r
3846 }\r
3847\r
3848 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
3849 if ( elem ) {\r
3850 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {\r
3851 if ( !inplace )\r
3852 result.push( elem );\r
3853 } else if ( inplace ) {\r
3854 curLoop[i] = false;\r
3855 }\r
3856 }\r
3857 }\r
3858\r
3859 return false;\r
3860 },\r
3861 ID: function(match){\r
3862 return match[1].replace(/\\/g, "");\r
3863 },\r
3864 TAG: function(match, curLoop){\r
3865 for ( var i = 0; curLoop[i] === false; i++ ){}\r
3866 return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();\r
3867 },\r
3868 CHILD: function(match){\r
3869 if ( match[1] == "nth" ) {\r
3870 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
3871 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
3872 match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||\r
3873 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
3874\r
3875 // calculate the numbers (first)n+(last) including if they are negative\r
3876 match[2] = (test[1] + (test[2] || 1)) - 0;\r
3877 match[3] = test[3] - 0;\r
3878 }\r
3879\r
3880 // TODO: Move to normal caching system\r
3881 match[0] = done++;\r
3882\r
3883 return match;\r
3884 },\r
3885 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
3886 var name = match[1].replace(/\\/g, "");\r
3887 \r
3888 if ( !isXML && Expr.attrMap[name] ) {\r
3889 match[1] = Expr.attrMap[name];\r
3890 }\r
3891\r
3892 if ( match[2] === "~=" ) {\r
3893 match[4] = " " + match[4] + " ";\r
3894 }\r
3895\r
3896 return match;\r
3897 },\r
3898 PSEUDO: function(match, curLoop, inplace, result, not){\r
3899 if ( match[1] === "not" ) {\r
3900 // If we're dealing with a complex expression, or a simple one\r
3901 if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {\r
3902 match[3] = Sizzle(match[3], null, null, curLoop);\r
3903 } else {\r
3904 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
3905 if ( !inplace ) {\r
3906 result.push.apply( result, ret );\r
3907 }\r
3908 return false;\r
3909 }\r
3910 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
3911 return true;\r
3912 }\r
3913 \r
3914 return match;\r
3915 },\r
3916 POS: function(match){\r
3917 match.unshift( true );\r
3918 return match;\r
3919 }\r
3920 },\r
3921 filters: {\r
3922 enabled: function(elem){\r
3923 return elem.disabled === false && elem.type !== "hidden";\r
3924 },\r
3925 disabled: function(elem){\r
3926 return elem.disabled === true;\r
3927 },\r
3928 checked: function(elem){\r
3929 return elem.checked === true;\r
3930 },\r
3931 selected: function(elem){\r
3932 // Accessing this property makes selected-by-default\r
3933 // options in Safari work properly\r
3934 elem.parentNode.selectedIndex;\r
3935 return elem.selected === true;\r
3936 },\r
3937 parent: function(elem){\r
3938 return !!elem.firstChild;\r
3939 },\r
3940 empty: function(elem){\r
3941 return !elem.firstChild;\r
3942 },\r
3943 has: function(elem, i, match){\r
3944 return !!Sizzle( match[3], elem ).length;\r
3945 },\r
3946 header: function(elem){\r
3947 return /h\d/i.test( elem.nodeName );\r
3948 },\r
3949 text: function(elem){\r
3950 return "text" === elem.type;\r
3951 },\r
3952 radio: function(elem){\r
3953 return "radio" === elem.type;\r
3954 },\r
3955 checkbox: function(elem){\r
3956 return "checkbox" === elem.type;\r
3957 },\r
3958 file: function(elem){\r
3959 return "file" === elem.type;\r
3960 },\r
3961 password: function(elem){\r
3962 return "password" === elem.type;\r
3963 },\r
3964 submit: function(elem){\r
3965 return "submit" === elem.type;\r
3966 },\r
3967 image: function(elem){\r
3968 return "image" === elem.type;\r
3969 },\r
3970 reset: function(elem){\r
3971 return "reset" === elem.type;\r
3972 },\r
3973 button: function(elem){\r
3974 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";\r
3975 },\r
3976 input: function(elem){\r
3977 return /input|select|textarea|button/i.test(elem.nodeName);\r
3978 }\r
3979 },\r
3980 setFilters: {\r
3981 first: function(elem, i){\r
3982 return i === 0;\r
3983 },\r
3984 last: function(elem, i, match, array){\r
3985 return i === array.length - 1;\r
3986 },\r
3987 even: function(elem, i){\r
3988 return i % 2 === 0;\r
3989 },\r
3990 odd: function(elem, i){\r
3991 return i % 2 === 1;\r
3992 },\r
3993 lt: function(elem, i, match){\r
3994 return i < match[3] - 0;\r
3995 },\r
3996 gt: function(elem, i, match){\r
3997 return i > match[3] - 0;\r
3998 },\r
3999 nth: function(elem, i, match){\r
4000 return match[3] - 0 == i;\r
4001 },\r
4002 eq: function(elem, i, match){\r
4003 return match[3] - 0 == i;\r
4004 }\r
4005 },\r
4006 filter: {\r
4007 PSEUDO: function(elem, match, i, array){\r
4008 var name = match[1], filter = Expr.filters[ name ];\r
4009\r
4010 if ( filter ) {\r
4011 return filter( elem, i, match, array );\r
4012 } else if ( name === "contains" ) {\r
4013 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;\r
4014 } else if ( name === "not" ) {\r
4015 var not = match[3];\r
4016\r
4017 for ( var i = 0, l = not.length; i < l; i++ ) {\r
4018 if ( not[i] === elem ) {\r
4019 return false;\r
4020 }\r
4021 }\r
4022\r
4023 return true;\r
4024 }\r
4025 },\r
4026 CHILD: function(elem, match){\r
4027 var type = match[1], node = elem;\r
4028 switch (type) {\r
4029 case 'only':\r
4030 case 'first':\r
4031 while (node = node.previousSibling) {\r
4032 if ( node.nodeType === 1 ) return false;\r
4033 }\r
4034 if ( type == 'first') return true;\r
4035 node = elem;\r
4036 case 'last':\r
4037 while (node = node.nextSibling) {\r
4038 if ( node.nodeType === 1 ) return false;\r
4039 }\r
4040 return true;\r
4041 case 'nth':\r
4042 var first = match[2], last = match[3];\r
4043\r
4044 if ( first == 1 && last == 0 ) {\r
4045 return true;\r
4046 }\r
4047 \r
4048 var doneName = match[0],\r
4049 parent = elem.parentNode;\r
4050 \r
4051 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
4052 var count = 0;\r
4053 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
4054 if ( node.nodeType === 1 ) {\r
4055 node.nodeIndex = ++count;\r
4056 }\r
4057 } \r
4058 parent.sizcache = doneName;\r
4059 }\r
4060 \r
4061 var diff = elem.nodeIndex - last;\r
4062 if ( first == 0 ) {\r
4063 return diff == 0;\r
4064 } else {\r
4065 return ( diff % first == 0 && diff / first >= 0 );\r
4066 }\r
4067 }\r
4068 },\r
4069 ID: function(elem, match){\r
4070 return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
4071 },\r
4072 TAG: function(elem, match){\r
4073 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;\r
4074 },\r
4075 CLASS: function(elem, match){\r
4076 return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
4077 .indexOf( match ) > -1;\r
4078 },\r
4079 ATTR: function(elem, match){\r
4080 var name = match[1],\r
4081 result = Expr.attrHandle[ name ] ?\r
4082 Expr.attrHandle[ name ]( elem ) :\r
4083 elem[ name ] != null ?\r
4084 elem[ name ] :\r
4085 elem.getAttribute( name ),\r
4086 value = result + "",\r
4087 type = match[2],\r
4088 check = match[4];\r
4089\r
4090 return result == null ?\r
4091 type === "!=" :\r
4092 type === "=" ?\r
4093 value === check :\r
4094 type === "*=" ?\r
4095 value.indexOf(check) >= 0 :\r
4096 type === "~=" ?\r
4097 (" " + value + " ").indexOf(check) >= 0 :\r
4098 !check ?\r
4099 value && result !== false :\r
4100 type === "!=" ?\r
4101 value != check :\r
4102 type === "^=" ?\r
4103 value.indexOf(check) === 0 :\r
4104 type === "$=" ?\r
4105 value.substr(value.length - check.length) === check :\r
4106 type === "|=" ?\r
4107 value === check || value.substr(0, check.length + 1) === check + "-" :\r
4108 false;\r
4109 },\r
4110 POS: function(elem, match, i, array){\r
4111 var name = match[2], filter = Expr.setFilters[ name ];\r
4112\r
4113 if ( filter ) {\r
4114 return filter( elem, i, match, array );\r
4115 }\r
4116 }\r
4117 }\r
4118};\r
4119\r
4120var origPOS = Expr.match.POS;\r
4121\r
4122for ( var type in Expr.match ) {\r
4123 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );\r
4124}\r
4125\r
4126var makeArray = function(array, results) {\r
4127 array = Array.prototype.slice.call( array );\r
4128\r
4129 if ( results ) {\r
4130 results.push.apply( results, array );\r
4131 return results;\r
4132 }\r
4133 \r
4134 return array;\r
4135};\r
4136\r
4137// Perform a simple check to determine if the browser is capable of\r
4138// converting a NodeList to an array using builtin methods.\r
4139try {\r
4140 Array.prototype.slice.call( document.documentElement.childNodes );\r
4141\r
4142// Provide a fallback method if it does not work\r
4143} catch(e){\r
4144 makeArray = function(array, results) {\r
4145 var ret = results || [];\r
4146\r
4147 if ( toString.call(array) === "[object Array]" ) {\r
4148 Array.prototype.push.apply( ret, array );\r
4149 } else {\r
4150 if ( typeof array.length === "number" ) {\r
4151 for ( var i = 0, l = array.length; i < l; i++ ) {\r
4152 ret.push( array[i] );\r
4153 }\r
4154 } else {\r
4155 for ( var i = 0; array[i]; i++ ) {\r
4156 ret.push( array[i] );\r
4157 }\r
4158 }\r
4159 }\r
4160\r
4161 return ret;\r
4162 };\r
4163}\r
4164\r
4165var sortOrder;\r
4166\r
4167if ( document.documentElement.compareDocumentPosition ) {\r
4168 sortOrder = function( a, b ) {\r
4169 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
4170 if ( ret === 0 ) {\r
4171 hasDuplicate = true;\r
4172 }\r
4173 return ret;\r
4174 };\r
4175} else if ( "sourceIndex" in document.documentElement ) {\r
4176 sortOrder = function( a, b ) {\r
4177 var ret = a.sourceIndex - b.sourceIndex;\r
4178 if ( ret === 0 ) {\r
4179 hasDuplicate = true;\r
4180 }\r
4181 return ret;\r
4182 };\r
4183} else if ( document.createRange ) {\r
4184 sortOrder = function( a, b ) {\r
4185 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
4186 aRange.setStart(a, 0);\r
4187 aRange.setEnd(a, 0);\r
4188 bRange.setStart(b, 0);\r
4189 bRange.setEnd(b, 0);\r
4190 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
4191 if ( ret === 0 ) {\r
4192 hasDuplicate = true;\r
4193 }\r
4194 return ret;\r
4195 };\r
4196}\r
4197\r
4198// Check to see if the browser returns elements by name when\r
4199// querying by getElementById (and provide a workaround)\r
4200(function(){\r
4201 // We're going to inject a fake input element with a specified name\r
4202 var form = document.createElement("div"),\r
4203 id = "script" + (new Date).getTime();\r
4204 form.innerHTML = "<a name='" + id + "'/>";\r
4205\r
4206 // Inject it into the root element, check its status, and remove it quickly\r
4207 var root = document.documentElement;\r
4208 root.insertBefore( form, root.firstChild );\r
4209\r
4210 // The workaround has to do additional checks after a getElementById\r
4211 // Which slows things down for other browsers (hence the branching)\r
4212 if ( !!document.getElementById( id ) ) {\r
4213 Expr.find.ID = function(match, context, isXML){\r
4214 if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4215 var m = context.getElementById(match[1]);\r
4216 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
4217 }\r
4218 };\r
4219\r
4220 Expr.filter.ID = function(elem, match){\r
4221 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
4222 return elem.nodeType === 1 && node && node.nodeValue === match;\r
4223 };\r
4224 }\r
4225\r
4226 root.removeChild( form );\r
4227})();\r
4228\r
4229(function(){\r
4230 // Check to see if the browser returns only elements\r
4231 // when doing getElementsByTagName("*")\r
4232\r
4233 // Create a fake element\r
4234 var div = document.createElement("div");\r
4235 div.appendChild( document.createComment("") );\r
4236\r
4237 // Make sure no comments are found\r
4238 if ( div.getElementsByTagName("*").length > 0 ) {\r
4239 Expr.find.TAG = function(match, context){\r
4240 var results = context.getElementsByTagName(match[1]);\r
4241\r
4242 // Filter out possible comments\r
4243 if ( match[1] === "*" ) {\r
4244 var tmp = [];\r
4245\r
4246 for ( var i = 0; results[i]; i++ ) {\r
4247 if ( results[i].nodeType === 1 ) {\r
4248 tmp.push( results[i] );\r
4249 }\r
4250 }\r
4251\r
4252 results = tmp;\r
4253 }\r
4254\r
4255 return results;\r
4256 };\r
4257 }\r
4258\r
4259 // Check to see if an attribute returns normalized href attributes\r
4260 div.innerHTML = "<a href='#'></a>";\r
4261 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
4262 div.firstChild.getAttribute("href") !== "#" ) {\r
4263 Expr.attrHandle.href = function(elem){\r
4264 return elem.getAttribute("href", 2);\r
4265 };\r
4266 }\r
4267})();\r
4268\r
4269if ( document.querySelectorAll ) (function(){\r
4270 var oldSizzle = Sizzle, div = document.createElement("div");\r
4271 div.innerHTML = "<p class='TEST'></p>";\r
4272\r
4273 // Safari can't handle uppercase or unicode characters when\r
4274 // in quirks mode.\r
4275 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
4276 return;\r
4277 }\r
4278 \r
4279 Sizzle = function(query, context, extra, seed){\r
4280 context = context || document;\r
4281\r
4282 // Only use querySelectorAll on non-XML documents\r
4283 // (ID selectors don't work in non-HTML documents)\r
4284 if ( !seed && context.nodeType === 9 && !isXML(context) ) {\r
4285 try {\r
4286 return makeArray( context.querySelectorAll(query), extra );\r
4287 } catch(e){}\r
4288 }\r
4289 \r
4290 return oldSizzle(query, context, extra, seed);\r
4291 };\r
4292\r
4293 for ( var prop in oldSizzle ) {\r
4294 Sizzle[ prop ] = oldSizzle[ prop ];\r
4295 }\r
4296})();\r
4297\r
4298if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){\r
4299 var div = document.createElement("div");\r
4300 div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
4301\r
4302 // Opera can't find a second classname (in 9.6)\r
4303 if ( div.getElementsByClassName("e").length === 0 )\r
4304 return;\r
4305\r
4306 // Safari caches class attributes, doesn't catch changes (in 3.2)\r
4307 div.lastChild.className = "e";\r
4308\r
4309 if ( div.getElementsByClassName("e").length === 1 )\r
4310 return;\r
4311\r
4312 Expr.order.splice(1, 0, "CLASS");\r
4313 Expr.find.CLASS = function(match, context, isXML) {\r
4314 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
4315 return context.getElementsByClassName(match[1]);\r
4316 }\r
4317 };\r
4318})();\r
4319\r
4320function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4321 var sibDir = dir == "previousSibling" && !isXML;\r
4322 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4323 var elem = checkSet[i];\r
4324 if ( elem ) {\r
4325 if ( sibDir && elem.nodeType === 1 ){\r
4326 elem.sizcache = doneName;\r
4327 elem.sizset = i;\r
4328 }\r
4329 elem = elem[dir];\r
4330 var match = false;\r
4331\r
4332 while ( elem ) {\r
4333 if ( elem.sizcache === doneName ) {\r
4334 match = checkSet[elem.sizset];\r
4335 break;\r
4336 }\r
4337\r
4338 if ( elem.nodeType === 1 && !isXML ){\r
4339 elem.sizcache = doneName;\r
4340 elem.sizset = i;\r
4341 }\r
4342\r
4343 if ( elem.nodeName === cur ) {\r
4344 match = elem;\r
4345 break;\r
4346 }\r
4347\r
4348 elem = elem[dir];\r
4349 }\r
4350\r
4351 checkSet[i] = match;\r
4352 }\r
4353 }\r
4354}\r
4355\r
4356function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4357 var sibDir = dir == "previousSibling" && !isXML;\r
4358 for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4359 var elem = checkSet[i];\r
4360 if ( elem ) {\r
4361 if ( sibDir && elem.nodeType === 1 ) {\r
4362 elem.sizcache = doneName;\r
4363 elem.sizset = i;\r
4364 }\r
4365 elem = elem[dir];\r
4366 var match = false;\r
4367\r
4368 while ( elem ) {\r
4369 if ( elem.sizcache === doneName ) {\r
4370 match = checkSet[elem.sizset];\r
4371 break;\r
4372 }\r
4373\r
4374 if ( elem.nodeType === 1 ) {\r
4375 if ( !isXML ) {\r
4376 elem.sizcache = doneName;\r
4377 elem.sizset = i;\r
4378 }\r
4379 if ( typeof cur !== "string" ) {\r
4380 if ( elem === cur ) {\r
4381 match = true;\r
4382 break;\r
4383 }\r
4384\r
4385 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
4386 match = elem;\r
4387 break;\r
4388 }\r
4389 }\r
4390\r
4391 elem = elem[dir];\r
4392 }\r
4393\r
4394 checkSet[i] = match;\r
4395 }\r
4396 }\r
4397}\r
4398\r
4399var contains = document.compareDocumentPosition ? function(a, b){\r
4400 return a.compareDocumentPosition(b) & 16;\r
4401} : function(a, b){\r
4402 return a !== b && (a.contains ? a.contains(b) : true);\r
4403};\r
4404\r
4405var isXML = function(elem){\r
4406 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||\r
4407 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";\r
4408};\r
4409\r
4410var posProcess = function(selector, context){\r
4411 var tmpSet = [], later = "", match,\r
4412 root = context.nodeType ? [context] : context;\r
4413\r
4414 // Position selectors must be done after the filter\r
4415 // And so must :not(positional) so we move all PSEUDOs to the end\r
4416 while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
4417 later += match[0];\r
4418 selector = selector.replace( Expr.match.PSEUDO, "" );\r
4419 }\r
4420\r
4421 selector = Expr.relative[selector] ? selector + "*" : selector;\r
4422\r
4423 for ( var i = 0, l = root.length; i < l; i++ ) {\r
4424 Sizzle( selector, root[i], tmpSet );\r
4425 }\r
4426\r
4427 return Sizzle.filter( later, tmpSet );\r
4428};\r
4429\r
4430// EXPOSE\r
4431\r
4432window.tinymce.dom.Sizzle = Sizzle;\r
4433\r
4434})();\r
4435\r
4436(function(tinymce) {\r
4437 // Shorten names\r
4438 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
4439\r
4440 tinymce.create('tinymce.dom.EventUtils', {\r
4441 EventUtils : function() {\r
4442 this.inits = [];\r
4443 this.events = [];\r
4444 },\r
4445\r
4446 add : function(o, n, f, s) {\r
4447 var cb, t = this, el = t.events, r;\r
4448\r
4449 if (n instanceof Array) {\r
4450 r = [];\r
4451\r
4452 each(n, function(n) {\r
4453 r.push(t.add(o, n, f, s));\r
4454 });\r
4455\r
4456 return r;\r
4457 }\r
4458\r
4459 // Handle array\r
4460 if (o && o.hasOwnProperty && o instanceof Array) {\r
4461 r = [];\r
4462\r
4463 each(o, function(o) {\r
4464 o = DOM.get(o);\r
4465 r.push(t.add(o, n, f, s));\r
4466 });\r
4467\r
4468 return r;\r
4469 }\r
4470\r
4471 o = DOM.get(o);\r
4472\r
4473 if (!o)\r
4474 return;\r
4475\r
4476 // Setup event callback\r
4477 cb = function(e) {\r
4478 // Is all events disabled\r
4479 if (t.disabled)\r
4480 return;\r
4481\r
4482 e = e || window.event;\r
4483\r
4484 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
4485 if (e && isIE) {\r
4486 if (!e.target)\r
4487 e.target = e.srcElement;\r
4488\r
4489 // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
4490 tinymce.extend(e, t._stoppers);\r
4491 }\r
4492\r
4493 if (!s)\r
4494 return f(e);\r
4495\r
4496 return f.call(s, e);\r
4497 };\r
4498\r
4499 if (n == 'unload') {\r
4500 tinymce.unloads.unshift({func : cb});\r
4501 return cb;\r
4502 }\r
4503\r
4504 if (n == 'init') {\r
4505 if (t.domLoaded)\r
4506 cb();\r
4507 else\r
4508 t.inits.push(cb);\r
4509\r
4510 return cb;\r
4511 }\r
4512\r
4513 // Store away listener reference\r
4514 el.push({\r
4515 obj : o,\r
4516 name : n,\r
4517 func : f,\r
4518 cfunc : cb,\r
4519 scope : s\r
4520 });\r
4521\r
4522 t._add(o, n, cb);\r
4523\r
4524 return f;\r
4525 },\r
4526\r
4527 remove : function(o, n, f) {\r
4528 var t = this, a = t.events, s = false, r;\r
4529\r
4530 // Handle array\r
4531 if (o && o.hasOwnProperty && o instanceof Array) {\r
4532 r = [];\r
4533\r
4534 each(o, function(o) {\r
4535 o = DOM.get(o);\r
4536 r.push(t.remove(o, n, f));\r
4537 });\r
4538\r
4539 return r;\r
4540 }\r
4541\r
4542 o = DOM.get(o);\r
4543\r
4544 each(a, function(e, i) {\r
4545 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
4546 a.splice(i, 1);\r
4547 t._remove(o, n, e.cfunc);\r
4548 s = true;\r
4549 return false;\r
4550 }\r
4551 });\r
4552\r
4553 return s;\r
4554 },\r
4555\r
4556 clear : function(o) {\r
4557 var t = this, a = t.events, i, e;\r
4558\r
4559 if (o) {\r
4560 o = DOM.get(o);\r
4561\r
4562 for (i = a.length - 1; i >= 0; i--) {\r
4563 e = a[i];\r
4564\r
4565 if (e.obj === o) {\r
4566 t._remove(e.obj, e.name, e.cfunc);\r
4567 e.obj = e.cfunc = null;\r
4568 a.splice(i, 1);\r
4569 }\r
4570 }\r
4571 }\r
4572 },\r
4573\r
4574 cancel : function(e) {\r
4575 if (!e)\r
4576 return false;\r
4577\r
4578 this.stop(e);\r
4579\r
4580 return this.prevent(e);\r
4581 },\r
4582\r
4583 stop : function(e) {\r
4584 if (e.stopPropagation)\r
4585 e.stopPropagation();\r
4586 else\r
4587 e.cancelBubble = true;\r
4588\r
4589 return false;\r
4590 },\r
4591\r
4592 prevent : function(e) {\r
4593 if (e.preventDefault)\r
4594 e.preventDefault();\r
4595 else\r
4596 e.returnValue = false;\r
4597\r
4598 return false;\r
4599 },\r
4600\r
4601 destroy : function() {\r
4602 var t = this;\r
4603\r
4604 each(t.events, function(e, i) {\r
4605 t._remove(e.obj, e.name, e.cfunc);\r
4606 e.obj = e.cfunc = null;\r
4607 });\r
4608\r
4609 t.events = [];\r
4610 t = null;\r
4611 },\r
4612\r
4613 _add : function(o, n, f) {\r
4614 if (o.attachEvent)\r
4615 o.attachEvent('on' + n, f);\r
4616 else if (o.addEventListener)\r
4617 o.addEventListener(n, f, false);\r
4618 else\r
4619 o['on' + n] = f;\r
4620 },\r
4621\r
4622 _remove : function(o, n, f) {\r
4623 if (o) {\r
4624 try {\r
4625 if (o.detachEvent)\r
4626 o.detachEvent('on' + n, f);\r
4627 else if (o.removeEventListener)\r
4628 o.removeEventListener(n, f, false);\r
4629 else\r
4630 o['on' + n] = null;\r
4631 } catch (ex) {\r
4632 // Might fail with permission denined on IE so we just ignore that\r
4633 }\r
4634 }\r
4635 },\r
4636\r
4637 _pageInit : function(win) {\r
4638 var t = this;\r
4639\r
4640 // Keep it from running more than once\r
4641 if (t.domLoaded)\r
4642 return;\r
4643\r
4644 t.domLoaded = true;\r
4645\r
4646 each(t.inits, function(c) {\r
4647 c();\r
4648 });\r
4649\r
4650 t.inits = [];\r
4651 },\r
4652\r
4653 _wait : function(win) {\r
4654 var t = this, doc = win.document;\r
4655\r
4656 // No need since the document is already loaded\r
4657 if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4658 t.domLoaded = 1;\r
4659 return;\r
4660 }\r
4661\r
4662 // Use IE method\r
4663 if (doc.attachEvent) {\r
4664 doc.attachEvent("onreadystatechange", function() {\r
4665 if (doc.readyState === "complete") {\r
4666 doc.detachEvent("onreadystatechange", arguments.callee);\r
4667 t._pageInit(win);\r
4668 }\r
4669 });\r
4670\r
4671 if (doc.documentElement.doScroll && win == win.top) {\r
4672 (function() {\r
4673 if (t.domLoaded)\r
4674 return;\r
4675\r
4676 try {\r
4677 // If IE is used, use the trick by Diego Perini\r
4678 // http://javascript.nwbox.com/IEContentLoaded/\r
4679 doc.documentElement.doScroll("left");\r
4680 } catch (ex) {\r
4681 setTimeout(arguments.callee, 0);\r
4682 return;\r
4683 }\r
4684\r
4685 t._pageInit(win);\r
4686 })();\r
4687 }\r
4688 } else if (doc.addEventListener) {\r
4689 t._add(win, 'DOMContentLoaded', function() {\r
4690 t._pageInit(win);\r
4691 });\r
4692 }\r
4693\r
4694 t._add(win, 'load', function() {\r
4695 t._pageInit(win);\r
4696 });\r
4697 },\r
4698\r
4699 _stoppers : {\r
4700 preventDefault : function() {\r
4701 this.returnValue = false;\r
4702 },\r
4703\r
4704 stopPropagation : function() {\r
4705 this.cancelBubble = true;\r
4706 }\r
4707 }\r
4708 });\r
4709\r
4710 Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4711\r
4712 // Dispatch DOM content loaded event for IE and Safari\r
4713 Event._wait(window);\r
4714\r
4715 tinymce.addUnload(function() {\r
4716 Event.destroy();\r
4717 });\r
4718})(tinymce);\r
4719(function(tinymce) {\r
4720 var each = tinymce.each;\r
4721\r
4722 tinymce.create('tinymce.dom.Element', {\r
4723 Element : function(id, s) {\r
4724 var t = this, dom, el;\r
4725\r
4726 s = s || {};\r
4727 t.id = id;\r
4728 t.dom = dom = s.dom || tinymce.DOM;\r
4729 t.settings = s;\r
4730\r
4731 // Only IE leaks DOM references, this is a lot faster\r
4732 if (!tinymce.isIE)\r
4733 el = t.dom.get(t.id);\r
4734\r
4735 each([\r
4736 'getPos',\r
4737 'getRect',\r
4738 'getParent',\r
4739 'add',\r
4740 'setStyle',\r
4741 'getStyle',\r
4742 'setStyles',\r
4743 'setAttrib',\r
4744 'setAttribs',\r
4745 'getAttrib',\r
4746 'addClass',\r
4747 'removeClass',\r
4748 'hasClass',\r
4749 'getOuterHTML',\r
4750 'setOuterHTML',\r
4751 'remove',\r
4752 'show',\r
4753 'hide',\r
4754 'isHidden',\r
4755 'setHTML',\r
4756 'get'\r
4757 ], function(k) {\r
4758 t[k] = function() {\r
4759 var a = [id], i;\r
4760\r
4761 for (i = 0; i < arguments.length; i++)\r
4762 a.push(arguments[i]);\r
4763\r
4764 a = dom[k].apply(dom, a);\r
4765 t.update(k);\r
4766\r
4767 return a;\r
4768 };\r
4769 });\r
4770 },\r
4771\r
4772 on : function(n, f, s) {\r
4773 return tinymce.dom.Event.add(this.id, n, f, s);\r
4774 },\r
4775\r
4776 getXY : function() {\r
4777 return {\r
4778 x : parseInt(this.getStyle('left')),\r
4779 y : parseInt(this.getStyle('top'))\r
4780 };\r
4781 },\r
4782\r
4783 getSize : function() {\r
4784 var n = this.dom.get(this.id);\r
4785\r
4786 return {\r
4787 w : parseInt(this.getStyle('width') || n.clientWidth),\r
4788 h : parseInt(this.getStyle('height') || n.clientHeight)\r
4789 };\r
4790 },\r
4791\r
4792 moveTo : function(x, y) {\r
4793 this.setStyles({left : x, top : y});\r
4794 },\r
4795\r
4796 moveBy : function(x, y) {\r
4797 var p = this.getXY();\r
4798\r
4799 this.moveTo(p.x + x, p.y + y);\r
4800 },\r
4801\r
4802 resizeTo : function(w, h) {\r
4803 this.setStyles({width : w, height : h});\r
4804 },\r
4805\r
4806 resizeBy : function(w, h) {\r
4807 var s = this.getSize();\r
4808\r
4809 this.resizeTo(s.w + w, s.h + h);\r
4810 },\r
4811\r
4812 update : function(k) {\r
4813 var t = this, b, dom = t.dom;\r
4814\r
4815 if (tinymce.isIE6 && t.settings.blocker) {\r
4816 k = k || '';\r
4817\r
4818 // Ignore getters\r
4819 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4820 return;\r
4821\r
4822 // Remove blocker on remove\r
4823 if (k == 'remove') {\r
4824 dom.remove(t.blocker);\r
4825 return;\r
4826 }\r
4827\r
4828 if (!t.blocker) {\r
4829 t.blocker = dom.uniqueId();\r
4830 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
4831 dom.setStyle(b, 'opacity', 0);\r
4832 } else\r
4833 b = dom.get(t.blocker);\r
4834\r
4835 dom.setStyle(b, 'left', t.getStyle('left', 1));\r
4836 dom.setStyle(b, 'top', t.getStyle('top', 1));\r
4837 dom.setStyle(b, 'width', t.getStyle('width', 1));\r
4838 dom.setStyle(b, 'height', t.getStyle('height', 1));\r
4839 dom.setStyle(b, 'display', t.getStyle('display', 1));\r
4840 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);\r
4841 }\r
4842 }\r
4843 });\r
4844})(tinymce);\r
4845(function(tinymce) {\r
4846 function trimNl(s) {\r
4847 return s.replace(/[\n\r]+/g, '');\r
4848 };\r
4849\r
4850 // Shorten names\r
4851 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
4852\r
4853 tinymce.create('tinymce.dom.Selection', {\r
4854 Selection : function(dom, win, serializer) {\r
4855 var t = this;\r
4856\r
4857 t.dom = dom;\r
4858 t.win = win;\r
4859 t.serializer = serializer;\r
4860\r
4861 // Add events\r
4862 each([\r
4863 'onBeforeSetContent',\r
4864 'onBeforeGetContent',\r
4865 'onSetContent',\r
4866 'onGetContent'\r
4867 ], function(e) {\r
4868 t[e] = new tinymce.util.Dispatcher(t);\r
4869 });\r
4870\r
4871 // No W3C Range support\r
4872 if (!t.win.getSelection)\r
4873 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
4874\r
4875 // Prevent leaks\r
4876 tinymce.addUnload(t.destroy, t);\r
4877 },\r
4878\r
4879 getContent : function(s) {\r
4880 var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
4881\r
4882 s = s || {};\r
4883 wb = wa = '';\r
4884 s.get = true;\r
4885 s.format = s.format || 'html';\r
4886 t.onBeforeGetContent.dispatch(t, s);\r
4887\r
4888 if (s.format == 'text')\r
4889 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
4890\r
4891 if (r.cloneContents) {\r
4892 n = r.cloneContents();\r
4893\r
4894 if (n)\r
4895 e.appendChild(n);\r
4896 } else if (is(r.item) || is(r.htmlText))\r
4897 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
4898 else\r
4899 e.innerHTML = r.toString();\r
4900\r
4901 // Keep whitespace before and after\r
4902 if (/^\s/.test(e.innerHTML))\r
4903 wb = ' ';\r
4904\r
4905 if (/\s+$/.test(e.innerHTML))\r
4906 wa = ' ';\r
4907\r
4908 s.getInner = true;\r
4909\r
4910 s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
4911 t.onGetContent.dispatch(t, s);\r
4912\r
4913 return s.content;\r
4914 },\r
4915\r
4916 setContent : function(h, s) {\r
4917 var t = this, r = t.getRng(), c, d = t.win.document;\r
4918\r
4919 s = s || {format : 'html'};\r
4920 s.set = true;\r
4921 h = s.content = t.dom.processHTML(h);\r
4922\r
4923 // Dispatch before set content event\r
4924 t.onBeforeSetContent.dispatch(t, s);\r
4925 h = s.content;\r
4926\r
4927 if (r.insertNode) {\r
4928 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
4929 h += '<span id="__caret">_</span>';\r
4930\r
4931 // Delete and insert new node\r
4932 r.deleteContents();\r
4933 r.insertNode(t.getRng().createContextualFragment(h));\r
4934\r
4935 // Move to caret marker\r
4936 c = t.dom.get('__caret');\r
4937\r
4938 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
4939 r = d.createRange();\r
4940 r.setStartBefore(c);\r
4941 r.setEndAfter(c);\r
4942 t.setRng(r);\r
4943\r
4944 // Delete the marker, and hopefully the caret gets placed in the right location\r
4945 // Removed this since it seems to remove &nbsp; in FF and simply deleting it\r
4946 // doesn't seem to affect the caret position in any browser\r
4947 //d.execCommand('Delete', false, null);\r
4948\r
4949 // Remove the caret position\r
4950 t.dom.remove('__caret');\r
4951 } else {\r
4952 if (r.item) {\r
4953 // Delete content and get caret text selection\r
4954 d.execCommand('Delete', false, null);\r
4955 r = t.getRng();\r
4956 }\r
4957\r
4958 r.pasteHTML(h);\r
4959 }\r
4960\r
4961 // Dispatch set content event\r
4962 t.onSetContent.dispatch(t, s);\r
4963 },\r
4964\r
4965 getStart : function() {\r
4966 var t = this, r = t.getRng(), e;\r
4967\r
4968 if (isIE) {\r
4969 if (r.item)\r
4970 return r.item(0);\r
4971\r
4972 r = r.duplicate();\r
4973 r.collapse(1);\r
4974 e = r.parentElement();\r
4975\r
4976 if (e && e.nodeName == 'BODY')\r
4977 return e.firstChild;\r
4978\r
4979 return e;\r
4980 } else {\r
4981 e = r.startContainer;\r
4982\r
4983 if (e.nodeName == 'BODY')\r
4984 return e.firstChild;\r
4985\r
4986 return t.dom.getParent(e, '*');\r
4987 }\r
4988 },\r
4989\r
4990 getEnd : function() {\r
4991 var t = this, r = t.getRng(), e;\r
4992\r
4993 if (isIE) {\r
4994 if (r.item)\r
4995 return r.item(0);\r
4996\r
4997 r = r.duplicate();\r
4998 r.collapse(0);\r
4999 e = r.parentElement();\r
5000\r
5001 if (e && e.nodeName == 'BODY')\r
5002 return e.lastChild;\r
5003\r
5004 return e;\r
5005 } else {\r
5006 e = r.endContainer;\r
5007\r
5008 if (e.nodeName == 'BODY')\r
5009 return e.lastChild;\r
5010\r
5011 return t.dom.getParent(e, '*');\r
5012 }\r
5013 },\r
5014\r
5015 getBookmark : function(si) {\r
5016 var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv;\r
5017 sx = vp.x;\r
5018 sy = vp.y;\r
5019\r
5020 // Simple bookmark fast but not as persistent\r
5021 if (si)\r
5022 return {rng : r, scrollX : sx, scrollY : sy};\r
5023\r
5024 // Handle IE\r
5025 if (isIE) {\r
5026 // Control selection\r
5027 if (r.item) {\r
5028 e = r.item(0);\r
5029\r
5030 each(t.dom.select(e.nodeName), function(n, i) {\r
5031 if (e == n) {\r
5032 sp = i;\r
5033 return false;\r
5034 }\r
5035 });\r
5036\r
5037 return {\r
5038 tag : e.nodeName,\r
5039 index : sp,\r
5040 scrollX : sx,\r
5041 scrollY : sy\r
5042 };\r
5043 }\r
5044\r
5045 // Text selection\r
5046 tr = t.dom.doc.body.createTextRange();\r
5047 tr.moveToElementText(ro);\r
5048 tr.collapse(true);\r
5049 bp = Math.abs(tr.move('character', c));\r
5050\r
5051 tr = r.duplicate();\r
5052 tr.collapse(true);\r
5053 sp = Math.abs(tr.move('character', c));\r
5054\r
5055 tr = r.duplicate();\r
5056 tr.collapse(false);\r
5057 le = Math.abs(tr.move('character', c)) - sp;\r
5058\r
5059 return {\r
5060 start : sp - bp,\r
5061 length : le,\r
5062 scrollX : sx,\r
5063 scrollY : sy\r
5064 };\r
5065 }\r
5066\r
5067 // Handle W3C\r
5068 e = t.getNode();\r
5069 s = t.getSel();\r
5070\r
5071 if (!s)\r
5072 return null;\r
5073\r
5074 // Image selection\r
5075 if (e && e.nodeName == 'IMG') {\r
5076 return {\r
5077 scrollX : sx,\r
5078 scrollY : sy\r
5079 };\r
5080 }\r
5081\r
5082 // Text selection\r
5083\r
5084 function getPos(r, sn, en) {\r
5085 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};\r
5086\r
5087 while ((n = w.nextNode()) != null) {\r
5088 if (n == sn)\r
5089 d.start = p;\r
5090\r
5091 if (n == en) {\r
5092 d.end = p;\r
5093 return d;\r
5094 }\r
5095\r
5096 p += trimNl(n.nodeValue || '').length;\r
5097 }\r
5098\r
5099 return null;\r
5100 };\r
5101\r
5102 // Caret or selection\r
5103 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {\r
5104 e = getPos(ro, s.anchorNode, s.focusNode);\r
5105\r
5106 if (!e)\r
5107 return {scrollX : sx, scrollY : sy};\r
5108\r
5109 // Count whitespace before\r
5110 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});\r
5111\r
5112 return {\r
5113 start : Math.max(e.start + s.anchorOffset - wb, 0),\r
5114 end : Math.max(e.end + s.focusOffset - wb, 0),\r
5115 scrollX : sx,\r
5116 scrollY : sy,\r
5117 beg : s.anchorOffset - wb == 0\r
5118 };\r
5119 } else {\r
5120 e = getPos(ro, r.startContainer, r.endContainer);\r
5121\r
5122 // Count whitespace before start and end container\r
5123 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});\r
5124 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});\r
5125\r
5126 if (!e)\r
5127 return {scrollX : sx, scrollY : sy};\r
5128\r
5129 return {\r
5130 start : Math.max(e.start + r.startOffset - wb, 0),\r
5131 end : Math.max(e.end + r.endOffset - wa, 0),\r
5132 scrollX : sx,\r
5133 scrollY : sy,\r
5134 beg : r.startOffset - wb == 0\r
5135 };\r
5136 }\r
5137 },\r
5138\r
5139 moveToBookmark : function(b) {\r
5140 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;\r
5141\r
5142 function getPos(r, sp, ep) {\r
5143 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;\r
5144\r
5145 while ((n = w.nextNode()) != null) {\r
5146 wa = wb = 0;\r
5147\r
5148 nv = n.nodeValue || '';\r
5149 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});\r
5150 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});\r
5151\r
5152 nvl = trimNl(nv).length;\r
5153 p += nvl;\r
5154\r
5155 if (p >= sp && !d.startNode) {\r
5156 o = sp - (p - nvl);\r
5157\r
5158 // Fix for odd quirk in FF\r
5159 if (b.beg && o >= nvl)\r
5160 continue;\r
5161\r
5162 d.startNode = n;\r
5163 d.startOffset = o + wb;\r
5164 }\r
5165\r
5166 if (p >= ep) {\r
5167 d.endNode = n;\r
5168 d.endOffset = ep - (p - nvl) + wb;\r
5169 return d;\r
5170 }\r
5171 }\r
5172\r
5173 return null;\r
5174 };\r
5175\r
5176 if (!b)\r
5177 return false;\r
5178\r
5179 t.win.scrollTo(b.scrollX, b.scrollY);\r
5180\r
5181 // Handle explorer\r
5182 if (isIE) {\r
5183 t.tridentSel.destroy();\r
5184\r
5185 // Handle simple\r
5186 if (r = b.rng) {\r
5187 try {\r
5188 r.select();\r
5189 } catch (ex) {\r
5190 // Ignore\r
5191 }\r
5192\r
5193 return true;\r
5194 }\r
5195\r
5196 t.win.focus();\r
5197\r
5198 // Handle control bookmark\r
5199 if (b.tag) {\r
5200 r = ro.createControlRange();\r
5201\r
5202 each(t.dom.select(b.tag), function(n, i) {\r
5203 if (i == b.index)\r
5204 r.addElement(n);\r
5205 });\r
5206 } else {\r
5207 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs\r
5208 try {\r
5209 // Incorrect bookmark\r
5210 if (b.start < 0)\r
5211 return true;\r
5212\r
5213 r = s.createRange();\r
5214 r.moveToElementText(ro);\r
5215 r.collapse(true);\r
5216 r.moveStart('character', b.start);\r
5217 r.moveEnd('character', b.length);\r
5218 } catch (ex2) {\r
5219 return true;\r
5220 }\r
5221 }\r
5222\r
5223 try {\r
5224 r.select();\r
5225 } catch (ex) {\r
5226 // Needed for some odd IE bug #1843306\r
5227 }\r
5228\r
5229 return true;\r
5230 }\r
5231\r
5232 // Handle W3C\r
5233 if (!s)\r
5234 return false;\r
5235\r
5236 // Handle simple\r
5237 if (b.rng) {\r
5238 s.removeAllRanges();\r
5239 s.addRange(b.rng);\r
5240 } else {\r
5241 if (is(b.start) && is(b.end)) {\r
5242 try {\r
5243 sd = getPos(ro, b.start, b.end);\r
5244\r
5245 if (sd) {\r
5246 r = t.dom.doc.createRange();\r
5247 r.setStart(sd.startNode, sd.startOffset);\r
5248 r.setEnd(sd.endNode, sd.endOffset);\r
5249 s.removeAllRanges();\r
5250 s.addRange(r);\r
5251 }\r
5252\r
5253 if (!tinymce.isOpera)\r
5254 t.win.focus();\r
5255 } catch (ex) {\r
5256 // Ignore\r
5257 }\r
5258 }\r
5259 }\r
5260 },\r
5261\r
5262 select : function(n, c) {\r
5263 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;\r
5264\r
5265 function find(n, start) {\r
5266 var walker, o;\r
5267\r
5268 if (n) {\r
5269 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
5270\r
5271 // Find first/last non empty text node\r
5272 while (n = walker.nextNode()) {\r
5273 o = n;\r
5274\r
5275 if (tinymce.trim(n.nodeValue).length != 0) {\r
5276 if (start)\r
5277 return n;\r
5278 else\r
5279 o = n;\r
5280 }\r
5281 }\r
5282 }\r
5283\r
5284 return o;\r
5285 };\r
5286\r
5287 if (isIE) {\r
5288 try {\r
5289 b = d.body;\r
5290\r
5291 if (/^(IMG|TABLE)$/.test(n.nodeName)) {\r
5292 r = b.createControlRange();\r
5293 r.addElement(n);\r
5294 } else {\r
5295 r = b.createTextRange();\r
5296 r.moveToElementText(n);\r
5297 }\r
5298\r
5299 r.select();\r
5300 } catch (ex) {\r
5301 // Throws illigal agrument in IE some times\r
5302 }\r
5303 } else {\r
5304 if (c) {\r
5305 fn = find(n, 1) || t.dom.select('br:first', n)[0];\r
5306 ln = find(n, 0) || t.dom.select('br:last', n)[0];\r
5307\r
5308 if (fn && ln) {\r
5309 r = d.createRange();\r
5310\r
5311 if (fn.nodeName == 'BR')\r
5312 r.setStartBefore(fn);\r
5313 else\r
5314 r.setStart(fn, 0);\r
5315\r
5316 if (ln.nodeName == 'BR')\r
5317 r.setEndBefore(ln);\r
5318 else\r
5319 r.setEnd(ln, ln.nodeValue.length);\r
5320 } else\r
5321 r.selectNode(n);\r
5322 } else\r
5323 r.selectNode(n);\r
5324\r
5325 t.setRng(r);\r
5326 }\r
5327\r
5328\r
5329 return n;\r
5330 },\r
5331\r
5332 isCollapsed : function() {\r
5333 var t = this, r = t.getRng(), s = t.getSel();\r
5334\r
5335 if (!r || r.item)\r
5336 return false;\r
5337\r
5338 return !s || r.boundingWidth == 0 || r.collapsed;\r
5339 },\r
5340\r
5341 collapse : function(b) {\r
5342 var t = this, r = t.getRng(), n;\r
5343\r
5344 // Control range on IE\r
5345 if (r.item) {\r
5346 n = r.item(0);\r
5347 r = this.win.document.body.createTextRange();\r
5348 r.moveToElementText(n);\r
5349 }\r
5350\r
5351 r.collapse(!!b);\r
5352 t.setRng(r);\r
5353 },\r
5354\r
5355 getSel : function() {\r
5356 var t = this, w = this.win;\r
5357\r
5358 return w.getSelection ? w.getSelection() : w.document.selection;\r
5359 },\r
5360\r
5361 getRng : function(w3c) {\r
5362 var t = this, s, r;\r
5363\r
5364 // Found tridentSel object then we need to use that one\r
5365 if (w3c && t.tridentSel)\r
5366 return t.tridentSel.getRangeAt(0);\r
5367\r
5368 try {\r
5369 if (s = t.getSel())\r
5370 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
5371 } catch (ex) {\r
5372 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
5373 }\r
5374\r
5375 // No range found then create an empty one\r
5376 // This can occur when the editor is placed in a hidden container element on Gecko\r
5377 // Or on IE when there was an exception\r
5378 if (!r)\r
5379 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();\r
5380\r
5381 return r;\r
5382 },\r
5383\r
5384 setRng : function(r) {\r
5385 var s, t = this;\r
5386\r
5387 if (!t.tridentSel) {\r
5388 s = t.getSel();\r
5389\r
5390 if (s) {\r
5391 s.removeAllRanges();\r
5392 s.addRange(r);\r
5393 }\r
5394 } else {\r
5395 // Is W3C Range\r
5396 if (r.cloneRange) {\r
5397 t.tridentSel.addRange(r);\r
5398 return;\r
5399 }\r
5400\r
5401 // Is IE specific range\r
5402 try {\r
5403 r.select();\r
5404 } catch (ex) {\r
5405 // Needed for some odd IE bug #1843306\r
5406 }\r
5407 }\r
5408 },\r
5409\r
5410 setNode : function(n) {\r
5411 var t = this;\r
5412\r
5413 t.setContent(t.dom.getOuterHTML(n));\r
5414\r
5415 return n;\r
5416 },\r
5417\r
5418 getNode : function() {\r
5419 var t = this, r = t.getRng(), s = t.getSel(), e;\r
5420\r
5421 if (!isIE) {\r
5422 // Range maybe lost after the editor is made visible again\r
5423 if (!r)\r
5424 return t.dom.getRoot();\r
5425\r
5426 e = r.commonAncestorContainer;\r
5427\r
5428 // Handle selection a image or other control like element such as anchors\r
5429 if (!r.collapsed) {\r
5430 // If the anchor node is a element instead of a text node then return this element\r
5431 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) \r
5432 return s.anchorNode.childNodes[s.anchorOffset]; \r
5433\r
5434 if (r.startContainer == r.endContainer) {\r
5435 if (r.startOffset - r.endOffset < 2) {\r
5436 if (r.startContainer.hasChildNodes())\r
5437 e = r.startContainer.childNodes[r.startOffset];\r
5438 }\r
5439 }\r
5440 }\r
5441\r
5442 return t.dom.getParent(e, '*');\r
5443 }\r
5444\r
5445 return r.item ? r.item(0) : r.parentElement();\r
5446 },\r
5447\r
5448 getSelectedBlocks : function(st, en) {\r
5449 var t = this, dom = t.dom, sb, eb, n, bl = [];\r
5450\r
5451 sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
5452 eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
5453\r
5454 if (sb)\r
5455 bl.push(sb);\r
5456\r
5457 if (sb && eb && sb != eb) {\r
5458 n = sb;\r
5459\r
5460 while ((n = n.nextSibling) && n != eb) {\r
5461 if (dom.isBlock(n))\r
5462 bl.push(n);\r
5463 }\r
5464 }\r
5465\r
5466 if (eb && sb != eb)\r
5467 bl.push(eb);\r
5468\r
5469 return bl;\r
5470 },\r
5471\r
5472 destroy : function(s) {\r
5473 var t = this;\r
5474\r
5475 t.win = null;\r
5476\r
5477 if (t.tridentSel)\r
5478 t.tridentSel.destroy();\r
5479\r
5480 // Manual destroy then remove unload handler\r
5481 if (!s)\r
5482 tinymce.removeUnload(t.destroy);\r
5483 }\r
5484 });\r
5485})(tinymce);\r
5486(function(tinymce) {\r
5487 tinymce.create('tinymce.dom.XMLWriter', {\r
5488 node : null,\r
5489\r
5490 XMLWriter : function(s) {\r
5491 // Get XML document\r
5492 function getXML() {\r
5493 var i = document.implementation;\r
5494\r
5495 if (!i || !i.createDocument) {\r
5496 // Try IE objects\r
5497 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
5498 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
5499 } else\r
5500 return i.createDocument('', '', null);\r
5501 };\r
5502\r
5503 this.doc = getXML();\r
5504 \r
5505 // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
5506 this.valid = tinymce.isOpera || tinymce.isWebKit;\r
5507\r
5508 this.reset();\r
5509 },\r
5510\r
5511 reset : function() {\r
5512 var t = this, d = t.doc;\r
5513\r
5514 if (d.firstChild)\r
5515 d.removeChild(d.firstChild);\r
5516\r
5517 t.node = d.appendChild(d.createElement("html"));\r
5518 },\r
5519\r
5520 writeStartElement : function(n) {\r
5521 var t = this;\r
5522\r
5523 t.node = t.node.appendChild(t.doc.createElement(n));\r
5524 },\r
5525\r
5526 writeAttribute : function(n, v) {\r
5527 if (this.valid)\r
5528 v = v.replace(/>/g, '%MCGT%');\r
5529\r
5530 this.node.setAttribute(n, v);\r
5531 },\r
5532\r
5533 writeEndElement : function() {\r
5534 this.node = this.node.parentNode;\r
5535 },\r
5536\r
5537 writeFullEndElement : function() {\r
5538 var t = this, n = t.node;\r
5539\r
5540 n.appendChild(t.doc.createTextNode(""));\r
5541 t.node = n.parentNode;\r
5542 },\r
5543\r
5544 writeText : function(v) {\r
5545 if (this.valid)\r
5546 v = v.replace(/>/g, '%MCGT%');\r
5547\r
5548 this.node.appendChild(this.doc.createTextNode(v));\r
5549 },\r
5550\r
5551 writeCDATA : function(v) {\r
5552 this.node.appendChild(this.doc.createCDATASection(v));\r
5553 },\r
5554\r
5555 writeComment : function(v) {\r
5556 // Fix for bug #2035694\r
5557 if (tinymce.isIE)\r
5558 v = v.replace(/^\-|\-$/g, ' ');\r
5559\r
5560 this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
5561 },\r
5562\r
5563 getContent : function() {\r
5564 var h;\r
5565\r
5566 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
5567 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
5568 h = h.replace(/ ?\/>/g, ' />');\r
5569\r
5570 if (this.valid)\r
5571 h = h.replace(/\%MCGT%/g, '&gt;');\r
5572\r
5573 return h;\r
5574 }\r
5575 });\r
5576})(tinymce);\r
5577(function(tinymce) {\r
5578 tinymce.create('tinymce.dom.StringWriter', {\r
5579 str : null,\r
5580 tags : null,\r
5581 count : 0,\r
5582 settings : null,\r
5583 indent : null,\r
5584\r
5585 StringWriter : function(s) {\r
5586 this.settings = tinymce.extend({\r
5587 indent_char : ' ',\r
5588 indentation : 0\r
5589 }, s);\r
5590\r
5591 this.reset();\r
5592 },\r
5593\r
5594 reset : function() {\r
5595 this.indent = '';\r
5596 this.str = "";\r
5597 this.tags = [];\r
5598 this.count = 0;\r
5599 },\r
5600\r
5601 writeStartElement : function(n) {\r
5602 this._writeAttributesEnd();\r
5603 this.writeRaw('<' + n);\r
5604 this.tags.push(n);\r
5605 this.inAttr = true;\r
5606 this.count++;\r
5607 this.elementCount = this.count;\r
5608 },\r
5609\r
5610 writeAttribute : function(n, v) {\r
5611 var t = this;\r
5612\r
5613 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
5614 },\r
5615\r
5616 writeEndElement : function() {\r
5617 var n;\r
5618\r
5619 if (this.tags.length > 0) {\r
5620 n = this.tags.pop();\r
5621\r
5622 if (this._writeAttributesEnd(1))\r
5623 this.writeRaw('</' + n + '>');\r
5624\r
5625 if (this.settings.indentation > 0)\r
5626 this.writeRaw('\n');\r
5627 }\r
5628 },\r
5629\r
5630 writeFullEndElement : function() {\r
5631 if (this.tags.length > 0) {\r
5632 this._writeAttributesEnd();\r
5633 this.writeRaw('</' + this.tags.pop() + '>');\r
5634\r
5635 if (this.settings.indentation > 0)\r
5636 this.writeRaw('\n');\r
5637 }\r
5638 },\r
5639\r
5640 writeText : function(v) {\r
5641 this._writeAttributesEnd();\r
5642 this.writeRaw(this.encode(v));\r
5643 this.count++;\r
5644 },\r
5645\r
5646 writeCDATA : function(v) {\r
5647 this._writeAttributesEnd();\r
5648 this.writeRaw('<![CDATA[' + v + ']]>');\r
5649 this.count++;\r
5650 },\r
5651\r
5652 writeComment : function(v) {\r
5653 this._writeAttributesEnd();\r
5654 this.writeRaw('<!-- ' + v + '-->');\r
5655 this.count++;\r
5656 },\r
5657\r
5658 writeRaw : function(v) {\r
5659 this.str += v;\r
5660 },\r
5661\r
5662 encode : function(s) {\r
5663 return s.replace(/[<>&"]/g, function(v) {\r
5664 switch (v) {\r
5665 case '<':\r
5666 return '&lt;';\r
5667\r
5668 case '>':\r
5669 return '&gt;';\r
5670\r
5671 case '&':\r
5672 return '&amp;';\r
5673\r
5674 case '"':\r
5675 return '&quot;';\r
5676 }\r
5677\r
5678 return v;\r
5679 });\r
5680 },\r
5681\r
5682 getContent : function() {\r
5683 return this.str;\r
5684 },\r
5685\r
5686 _writeAttributesEnd : function(s) {\r
5687 if (!this.inAttr)\r
5688 return;\r
5689\r
5690 this.inAttr = false;\r
5691\r
5692 if (s && this.elementCount == this.count) {\r
5693 this.writeRaw(' />');\r
5694 return false;\r
5695 }\r
5696\r
5697 this.writeRaw('>');\r
5698\r
5699 return true;\r
5700 }\r
5701 });\r
5702})(tinymce);\r
5703(function(tinymce) {\r
5704 // Shorten names\r
5705 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5706\r
5707 function wildcardToRE(s) {\r
5708 return s.replace(/([?+*])/g, '.$1');\r
5709 };\r
5710\r
5711 tinymce.create('tinymce.dom.Serializer', {\r
5712 Serializer : function(s) {\r
5713 var t = this;\r
5714\r
5715 t.key = 0;\r
5716 t.onPreProcess = new Dispatcher(t);\r
5717 t.onPostProcess = new Dispatcher(t);\r
5718\r
5719 try {\r
5720 t.writer = new tinymce.dom.XMLWriter();\r
5721 } catch (ex) {\r
5722 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5723 t.writer = new tinymce.dom.StringWriter();\r
5724 }\r
5725\r
5726 // Default settings\r
5727 t.settings = s = extend({\r
5728 dom : tinymce.DOM,\r
5729 valid_nodes : 0,\r
5730 node_filter : 0,\r
5731 attr_filter : 0,\r
5732 invalid_attrs : /^(mce_|_moz_|sizset|sizcache)/,\r
5733 closed : /(br|hr|input|meta|img|link|param|area)/,\r
5734 entity_encoding : 'named',\r
5735 entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro',\r
5736 valid_elements : '*[*]',\r
5737 extended_valid_elements : 0,\r
5738 valid_child_elements : 0,\r
5739 invalid_elements : 0,\r
5740 fix_table_elements : 1,\r
5741 fix_list_elements : true,\r
5742 fix_content_duplication : true,\r
5743 convert_fonts_to_spans : false,\r
5744 font_size_classes : 0,\r
5745 font_size_style_values : 0,\r
5746 apply_source_formatting : 0,\r
5747 indent_mode : 'simple',\r
5748 indent_char : '\t',\r
5749 indent_levels : 1,\r
5750 remove_linebreaks : 1,\r
5751 remove_redundant_brs : 1,\r
5752 element_format : 'xhtml'\r
5753 }, s);\r
5754\r
5755 t.dom = s.dom;\r
5756\r
5757 if (s.remove_redundant_brs) {\r
5758 t.onPostProcess.add(function(se, o) {\r
5759 // Remove single BR at end of block elements since they get rendered\r
5760 o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5761 // Check if it's a single element\r
5762 if (/^<br \/>\s*<\//.test(a))\r
5763 return '</' + c + '>';\r
5764\r
5765 return a;\r
5766 });\r
5767 });\r
5768 }\r
5769\r
5770 // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
5771 if (s.element_format == 'html') {\r
5772 t.onPostProcess.add(function(se, o) {\r
5773 o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
5774 });\r
5775 }\r
5776\r
5777 if (s.fix_list_elements) {\r
5778 t.onPreProcess.add(function(se, o) {\r
5779 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
5780\r
5781 function prevNode(e, n) {\r
5782 var a = n.split(','), i;\r
5783\r
5784 while ((e = e.previousSibling) != null) {\r
5785 for (i=0; i<a.length; i++) {\r
5786 if (e.nodeName == a[i])\r
5787 return e;\r
5788 }\r
5789 }\r
5790\r
5791 return null;\r
5792 };\r
5793\r
5794 for (x=0; x<a.length; x++) {\r
5795 nl = t.dom.select(a[x], o.node);\r
5796\r
5797 for (i=0; i<nl.length; i++) {\r
5798 n = nl[i];\r
5799 p = n.parentNode;\r
5800\r
5801 if (r.test(p.nodeName)) {\r
5802 np = prevNode(n, 'LI');\r
5803\r
5804 if (!np) {\r
5805 np = t.dom.create('li');\r
5806 np.innerHTML = '&nbsp;';\r
5807 np.appendChild(n);\r
5808 p.insertBefore(np, p.firstChild);\r
5809 } else\r
5810 np.appendChild(n);\r
5811 }\r
5812 }\r
5813 }\r
5814 });\r
5815 }\r
5816\r
5817 if (s.fix_table_elements) {\r
5818 t.onPreProcess.add(function(se, o) {\r
5819 // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
5820 // so Opera users with an older version will have to live with less compaible output not much we can do here\r
5821 if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
5822 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
5823 var parent = t.dom.getParent(n.parentNode, 'table,p');\r
5824\r
5825 if (parent.nodeName != 'TABLE') {\r
5826 try {\r
5827 t.dom.split(parent, n);\r
5828 } catch (ex) {\r
5829 // IE can sometimes fire an unknown runtime error so we just ignore it\r
5830 }\r
5831 }\r
5832 });\r
5833 }\r
5834 });\r
5835 }\r
5836 },\r
5837\r
5838 setEntities : function(s) {\r
5839 var t = this, a, i, l = {}, re = '', v;\r
5840\r
5841 // No need to setup more than once\r
5842 if (t.entityLookup)\r
5843 return;\r
5844\r
5845 // Build regex and lookup array\r
5846 a = s.split(',');\r
5847 for (i = 0; i < a.length; i += 2) {\r
5848 v = a[i];\r
5849\r
5850 // Don't add default &amp; &quot; etc.\r
5851 if (v == 34 || v == 38 || v == 60 || v == 62)\r
5852 continue;\r
5853\r
5854 l[String.fromCharCode(a[i])] = a[i + 1];\r
5855\r
5856 v = parseInt(a[i]).toString(16);\r
5857 re += '\\u' + '0000'.substring(v.length) + v;\r
5858 }\r
5859\r
5860 if (!re) {\r
5861 t.settings.entity_encoding = 'raw';\r
5862 return;\r
5863 }\r
5864\r
5865 t.entitiesRE = new RegExp('[' + re + ']', 'g');\r
5866 t.entityLookup = l;\r
5867 },\r
5868\r
5869 setValidChildRules : function(s) {\r
5870 this.childRules = null;\r
5871 this.addValidChildRules(s);\r
5872 },\r
5873\r
5874 addValidChildRules : function(s) {\r
5875 var t = this, inst, intr, bloc;\r
5876\r
5877 if (!s)\r
5878 return;\r
5879\r
5880 inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment';\r
5881 intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment';\r
5882 bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP';\r
5883\r
5884 each(s.split(','), function(s) {\r
5885 var p = s.split(/\[|\]/), re;\r
5886\r
5887 s = '';\r
5888 each(p[1].split('|'), function(v) {\r
5889 if (s)\r
5890 s += '|';\r
5891\r
5892 switch (v) {\r
5893 case '%itrans':\r
5894 v = intr;\r
5895 break;\r
5896\r
5897 case '%itrans_na':\r
5898 v = intr.substring(2);\r
5899 break;\r
5900\r
5901 case '%istrict':\r
5902 v = inst;\r
5903 break;\r
5904\r
5905 case '%istrict_na':\r
5906 v = inst.substring(2);\r
5907 break;\r
5908\r
5909 case '%btrans':\r
5910 v = bloc;\r
5911 break;\r
5912\r
5913 case '%bstrict':\r
5914 v = bloc;\r
5915 break;\r
5916 }\r
5917\r
5918 s += v;\r
5919 });\r
5920 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');\r
5921\r
5922 each(p[0].split('/'), function(s) {\r
5923 t.childRules = t.childRules || {};\r
5924 t.childRules[s] = re;\r
5925 });\r
5926 });\r
5927\r
5928 // Build regex\r
5929 s = '';\r
5930 each(t.childRules, function(v, k) {\r
5931 if (s)\r
5932 s += '|';\r
5933\r
5934 s += k;\r
5935 });\r
5936\r
5937 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');\r
5938\r
5939 /*console.debug(t.parentElementsRE.toString());\r
5940 each(t.childRules, function(v) {\r
5941 console.debug(v.toString());\r
5942 });*/\r
5943 },\r
5944\r
5945 setRules : function(s) {\r
5946 var t = this;\r
5947\r
5948 t._setup();\r
5949 t.rules = {};\r
5950 t.wildRules = [];\r
5951 t.validElements = {};\r
5952\r
5953 return t.addRules(s);\r
5954 },\r
5955\r
5956 addRules : function(s) {\r
5957 var t = this, dr;\r
5958\r
5959 if (!s)\r
5960 return;\r
5961\r
5962 t._setup();\r
5963\r
5964 each(s.split(','), function(s) {\r
5965 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
5966\r
5967 // Extend with default rules\r
5968 if (dr)\r
5969 at = tinymce.extend([], dr.attribs);\r
5970\r
5971 // Parse attributes\r
5972 if (p.length > 1) {\r
5973 each(p[1].split('|'), function(s) {\r
5974 var ar = {}, i;\r
5975\r
5976 at = at || [];\r
5977\r
5978 // Parse attribute rule\r
5979 s = s.replace(/::/g, '~');\r
5980 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
5981 s[2] = s[2].replace(/~/g, ':');\r
5982\r
5983 // Add required attributes\r
5984 if (s[1] == '!') {\r
5985 ra = ra || [];\r
5986 ra.push(s[2]);\r
5987 }\r
5988\r
5989 // Remove inherited attributes\r
5990 if (s[1] == '-') {\r
5991 for (i = 0; i <at.length; i++) {\r
5992 if (at[i].name == s[2]) {\r
5993 at.splice(i, 1);\r
5994 return;\r
5995 }\r
5996 }\r
5997 }\r
5998\r
5999 switch (s[3]) {\r
6000 // Add default attrib values\r
6001 case '=':\r
6002 ar.defaultVal = s[4] || '';\r
6003 break;\r
6004\r
6005 // Add forced attrib values\r
6006 case ':':\r
6007 ar.forcedVal = s[4];\r
6008 break;\r
6009\r
6010 // Add validation values\r
6011 case '<':\r
6012 ar.validVals = s[4].split('?');\r
6013 break;\r
6014 }\r
6015\r
6016 if (/[*.?]/.test(s[2])) {\r
6017 wat = wat || [];\r
6018 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
6019 wat.push(ar);\r
6020 } else {\r
6021 ar.name = s[2];\r
6022 at.push(ar);\r
6023 }\r
6024\r
6025 va.push(s[2]);\r
6026 });\r
6027 }\r
6028\r
6029 // Handle element names\r
6030 each(tn, function(s, i) {\r
6031 var pr = s.charAt(0), x = 1, ru = {};\r
6032\r
6033 // Extend with default rule data\r
6034 if (dr) {\r
6035 if (dr.noEmpty)\r
6036 ru.noEmpty = dr.noEmpty;\r
6037\r
6038 if (dr.fullEnd)\r
6039 ru.fullEnd = dr.fullEnd;\r
6040\r
6041 if (dr.padd)\r
6042 ru.padd = dr.padd;\r
6043 }\r
6044\r
6045 // Handle prefixes\r
6046 switch (pr) {\r
6047 case '-':\r
6048 ru.noEmpty = true;\r
6049 break;\r
6050\r
6051 case '+':\r
6052 ru.fullEnd = true;\r
6053 break;\r
6054\r
6055 case '#':\r
6056 ru.padd = true;\r
6057 break;\r
6058\r
6059 default:\r
6060 x = 0;\r
6061 }\r
6062\r
6063 tn[i] = s = s.substring(x);\r
6064 t.validElements[s] = 1;\r
6065\r
6066 // Add element name or element regex\r
6067 if (/[*.?]/.test(tn[0])) {\r
6068 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
6069 t.wildRules = t.wildRules || {};\r
6070 t.wildRules.push(ru);\r
6071 } else {\r
6072 ru.name = tn[0];\r
6073\r
6074 // Store away default rule\r
6075 if (tn[0] == '@')\r
6076 dr = ru;\r
6077\r
6078 t.rules[s] = ru;\r
6079 }\r
6080\r
6081 ru.attribs = at;\r
6082\r
6083 if (ra)\r
6084 ru.requiredAttribs = ra;\r
6085\r
6086 if (wat) {\r
6087 // Build valid attributes regexp\r
6088 s = '';\r
6089 each(va, function(v) {\r
6090 if (s)\r
6091 s += '|';\r
6092\r
6093 s += '(' + wildcardToRE(v) + ')';\r
6094 });\r
6095 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
6096 ru.wildAttribs = wat;\r
6097 }\r
6098 });\r
6099 });\r
6100\r
6101 // Build valid elements regexp\r
6102 s = '';\r
6103 each(t.validElements, function(v, k) {\r
6104 if (s)\r
6105 s += '|';\r
6106\r
6107 if (k != '@')\r
6108 s += k;\r
6109 });\r
6110 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
6111\r
6112 //console.debug(t.validElementsRE.toString());\r
6113 //console.dir(t.rules);\r
6114 //console.dir(t.wildRules);\r
6115 },\r
6116\r
6117 findRule : function(n) {\r
6118 var t = this, rl = t.rules, i, r;\r
6119\r
6120 t._setup();\r
6121\r
6122 // Exact match\r
6123 r = rl[n];\r
6124 if (r)\r
6125 return r;\r
6126\r
6127 // Try wildcards\r
6128 rl = t.wildRules;\r
6129 for (i = 0; i < rl.length; i++) {\r
6130 if (rl[i].nameRE.test(n))\r
6131 return rl[i];\r
6132 }\r
6133\r
6134 return null;\r
6135 },\r
6136\r
6137 findAttribRule : function(ru, n) {\r
6138 var i, wa = ru.wildAttribs;\r
6139\r
6140 for (i = 0; i < wa.length; i++) {\r
6141 if (wa[i].nameRE.test(n))\r
6142 return wa[i];\r
6143 }\r
6144\r
6145 return null;\r
6146 },\r
6147\r
6148 serialize : function(n, o) {\r
6149 var h, t = this, doc, oldDoc, impl, selected;\r
6150\r
6151 t._setup();\r
6152 o = o || {};\r
6153 o.format = o.format || 'html';\r
6154 t.processObj = o;\r
6155\r
6156 // IE looses the selected attribute on option elements so we need to store it\r
6157 // See: http://support.microsoft.com/kb/829907\r
6158 if (isIE) {\r
6159 selected = [];\r
6160 each(n.getElementsByTagName('option'), function(n) {\r
6161 var v = t.dom.getAttrib(n, 'selected');\r
6162\r
6163 selected.push(v ? v : null);\r
6164 });\r
6165 }\r
6166\r
6167 n = n.cloneNode(true);\r
6168\r
6169 // IE looses the selected attribute on option elements so we need to restore it\r
6170 if (isIE) {\r
6171 each(n.getElementsByTagName('option'), function(n, i) {\r
6172 t.dom.setAttrib(n, 'selected', selected[i]);\r
6173 });\r
6174 }\r
6175\r
6176 // Nodes needs to be attached to something in WebKit/Opera\r
6177 // Older builds of Opera crashes if you attach the node to an document created dynamically\r
6178 // and since we can't feature detect a crash we need to sniff the acutal build number\r
6179 // This fix will make DOM ranges and make Sizzle happy!\r
6180 impl = n.ownerDocument.implementation;\r
6181 if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
6182 // Create an empty HTML document\r
6183 doc = impl.createHTMLDocument("");\r
6184\r
6185 // Add the element or it's children if it's a body element to the new document\r
6186 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
6187 doc.body.appendChild(doc.importNode(node, true));\r
6188 });\r
6189\r
6190 // Grab first child or body element for serialization\r
6191 if (n.nodeName != 'BODY')\r
6192 n = doc.body.firstChild;\r
6193 else\r
6194 n = doc.body;\r
6195\r
6196 // set the new document in DOMUtils so createElement etc works\r
6197 oldDoc = t.dom.doc;\r
6198 t.dom.doc = doc;\r
6199 }\r
6200\r
6201 t.key = '' + (parseInt(t.key) + 1);\r
6202\r
6203 // Pre process\r
6204 if (!o.no_events) {\r
6205 o.node = n;\r
6206 t.onPreProcess.dispatch(t, o);\r
6207 }\r
6208\r
6209 // Serialize HTML DOM into a string\r
6210 t.writer.reset();\r
6211 t._serializeNode(n, o.getInner);\r
6212\r
6213 // Post process\r
6214 o.content = t.writer.getContent();\r
6215\r
6216 // Restore the old document if it was changed\r
6217 if (oldDoc)\r
6218 t.dom.doc = oldDoc;\r
6219\r
6220 if (!o.no_events)\r
6221 t.onPostProcess.dispatch(t, o);\r
6222\r
6223 t._postProcess(o);\r
6224 o.node = null;\r
6225\r
6226 return tinymce.trim(o.content);\r
6227 },\r
6228\r
6229 // Internal functions\r
6230\r
6231 _postProcess : function(o) {\r
6232 var t = this, s = t.settings, h = o.content, sc = [], p;\r
6233\r
6234 if (o.format == 'html') {\r
6235 // Protect some elements\r
6236 p = t._protect({\r
6237 content : h,\r
6238 patterns : [\r
6239 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
6240 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
6241 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
6242 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
6243 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
6244 ]\r
6245 });\r
6246\r
6247 h = p.content;\r
6248\r
6249 // Entity encode\r
6250 if (s.entity_encoding !== 'raw')\r
6251 h = t._encode(h);\r
6252\r
6253 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
6254/* if (o.set)\r
6255 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
6256 else\r
6257 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
6258\r
6259 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
6260 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
6261 // This process is only done when getting contents out from the editor.\r
6262 if (!o.set) {\r
6263 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
6264 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
6265\r
6266 if (s.remove_linebreaks) {\r
6267 h = h.replace(/\r?\n|\r/g, ' ');\r
6268 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
6269 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
6270 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start\r
6271 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start\r
6272 h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>'); // Trim block end\r
6273 }\r
6274\r
6275 // Simple indentation\r
6276 if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
6277 // Add line breaks before and after block elements\r
6278 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
6279 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
6280 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
6281 h = h.replace(/\n\n/g, '\n');\r
6282 }\r
6283 }\r
6284\r
6285 h = t._unprotect(h, p);\r
6286\r
6287 // Restore CDATA sections\r
6288 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
6289\r
6290 // Restore the \u00a0 character if raw mode is enabled\r
6291 if (s.entity_encoding == 'raw')\r
6292 h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
6293\r
6294 // Restore noscript elements\r
6295 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
6296 return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
6297 });\r
6298 }\r
6299\r
6300 o.content = h;\r
6301 },\r
6302\r
6303 _serializeNode : function(n, inn) {\r
6304 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed;\r
6305\r
6306 if (!s.node_filter || s.node_filter(n)) {\r
6307 switch (n.nodeType) {\r
6308 case 1: // Element\r
6309 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))\r
6310 return;\r
6311\r
6312 iv = false;\r
6313 hc = n.hasChildNodes();\r
6314 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();\r
6315\r
6316 // Add correct prefix on IE\r
6317 if (isIE) {\r
6318 if (n.scopeName !== 'HTML' && n.scopeName !== 'html')\r
6319 nn = n.scopeName + ':' + nn;\r
6320 }\r
6321\r
6322 // Remove mce prefix on IE needed for the abbr element\r
6323 if (nn.indexOf('mce:') === 0)\r
6324 nn = nn.substring(4);\r
6325\r
6326 // Check if valid\r
6327 if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {\r
6328 iv = true;\r
6329 break;\r
6330 }\r
6331\r
6332 if (isIE) {\r
6333 // Fix IE content duplication (DOM can have multiple copies of the same node)\r
6334 if (s.fix_content_duplication) {\r
6335 if (n.mce_serialized == t.key)\r
6336 return;\r
6337\r
6338 n.mce_serialized = t.key;\r
6339 }\r
6340\r
6341 // IE sometimes adds a / infront of the node name\r
6342 if (nn.charAt(0) == '/')\r
6343 nn = nn.substring(1);\r
6344 } else if (isGecko) {\r
6345 // Ignore br elements\r
6346 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
6347 return;\r
6348 }\r
6349\r
6350 // Check if valid child\r
6351 if (t.childRules) {\r
6352 if (t.parentElementsRE.test(t.elementName)) {\r
6353 if (!t.childRules[t.elementName].test(nn)) {\r
6354 iv = true;\r
6355 break;\r
6356 }\r
6357 }\r
6358\r
6359 t.elementName = nn;\r
6360 }\r
6361\r
6362 ru = t.findRule(nn);\r
6363 nn = ru.name || nn;\r
6364 closed = s.closed.test(nn);\r
6365\r
6366 // Skip empty nodes or empty node name in IE\r
6367 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
6368 iv = true;\r
6369 break;\r
6370 }\r
6371\r
6372 // Check required\r
6373 if (ru.requiredAttribs) {\r
6374 a = ru.requiredAttribs;\r
6375\r
6376 for (i = a.length - 1; i >= 0; i--) {\r
6377 if (this.dom.getAttrib(n, a[i]) !== '')\r
6378 break;\r
6379 }\r
6380\r
6381 // None of the required was there\r
6382 if (i == -1) {\r
6383 iv = true;\r
6384 break;\r
6385 }\r
6386 }\r
6387\r
6388 w.writeStartElement(nn);\r
6389\r
6390 // Add ordered attributes\r
6391 if (ru.attribs) {\r
6392 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
6393 a = at[i];\r
6394 v = t._getAttrib(n, a);\r
6395\r
6396 if (v !== null)\r
6397 w.writeAttribute(a.name, v);\r
6398 }\r
6399 }\r
6400\r
6401 // Add wild attributes\r
6402 if (ru.validAttribsRE) {\r
6403 at = t.dom.getAttribs(n);\r
6404 for (i=at.length-1; i>-1; i--) {\r
6405 no = at[i];\r
6406\r
6407 if (no.specified) {\r
6408 a = no.nodeName.toLowerCase();\r
6409\r
6410 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
6411 continue;\r
6412\r
6413 ar = t.findAttribRule(ru, a);\r
6414 v = t._getAttrib(n, ar, a);\r
6415\r
6416 if (v !== null)\r
6417 w.writeAttribute(a, v);\r
6418 }\r
6419 }\r
6420 }\r
6421\r
6422 // Write text from script\r
6423 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
6424 w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
6425 w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
6426 hc = false;\r
6427 break;\r
6428 }\r
6429\r
6430 // Padd empty nodes with a &nbsp;\r
6431 if (ru.padd) {\r
6432 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
6433 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
6434 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus'))\r
6435 w.writeText('\u00a0');\r
6436 } else if (!hc)\r
6437 w.writeText('\u00a0'); // No children then padd it\r
6438 }\r
6439\r
6440 break;\r
6441\r
6442 case 3: // Text\r
6443 // Check if valid child\r
6444 if (t.childRules && t.parentElementsRE.test(t.elementName)) {\r
6445 if (!t.childRules[t.elementName].test(n.nodeName))\r
6446 return;\r
6447 }\r
6448\r
6449 return w.writeText(n.nodeValue);\r
6450\r
6451 case 4: // CDATA\r
6452 return w.writeCDATA(n.nodeValue);\r
6453\r
6454 case 8: // Comment\r
6455 return w.writeComment(n.nodeValue);\r
6456 }\r
6457 } else if (n.nodeType == 1)\r
6458 hc = n.hasChildNodes();\r
6459\r
6460 if (hc && !closed) {\r
6461 cn = n.firstChild;\r
6462\r
6463 while (cn) {\r
6464 t._serializeNode(cn);\r
6465 t.elementName = nn;\r
6466 cn = cn.nextSibling;\r
6467 }\r
6468 }\r
6469\r
6470 // Write element end\r
6471 if (!iv) {\r
6472 if (!closed)\r
6473 w.writeFullEndElement();\r
6474 else\r
6475 w.writeEndElement();\r
6476 }\r
6477 },\r
6478\r
6479 _protect : function(o) {\r
6480 var t = this;\r
6481\r
6482 o.items = o.items || [];\r
6483\r
6484 function enc(s) {\r
6485 return s.replace(/[\r\n\\]/g, function(c) {\r
6486 if (c === '\n')\r
6487 return '\\n';\r
6488 else if (c === '\\')\r
6489 return '\\\\';\r
6490\r
6491 return '\\r';\r
6492 });\r
6493 };\r
6494\r
6495 function dec(s) {\r
6496 return s.replace(/\\[\\rn]/g, function(c) {\r
6497 if (c === '\\n')\r
6498 return '\n';\r
6499 else if (c === '\\\\')\r
6500 return '\\';\r
6501\r
6502 return '\r';\r
6503 });\r
6504 };\r
6505\r
6506 each(o.patterns, function(p) {\r
6507 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
6508 b = dec(b);\r
6509\r
6510 if (p.encode)\r
6511 b = t._encode(b);\r
6512\r
6513 o.items.push(b);\r
6514 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
6515 }));\r
6516 });\r
6517\r
6518 return o;\r
6519 },\r
6520\r
6521 _unprotect : function(h, o) {\r
6522 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
6523 return o.items[parseInt(b)];\r
6524 });\r
6525\r
6526 o.items = [];\r
6527\r
6528 return h;\r
6529 },\r
6530\r
6531 _encode : function(h) {\r
6532 var t = this, s = t.settings, l;\r
6533\r
6534 // Entity encode\r
6535 if (s.entity_encoding !== 'raw') {\r
6536 if (s.entity_encoding.indexOf('named') != -1) {\r
6537 t.setEntities(s.entities);\r
6538 l = t.entityLookup;\r
6539\r
6540 h = h.replace(t.entitiesRE, function(a) {\r
6541 var v;\r
6542\r
6543 if (v = l[a])\r
6544 a = '&' + v + ';';\r
6545\r
6546 return a;\r
6547 });\r
6548 }\r
6549\r
6550 if (s.entity_encoding.indexOf('numeric') != -1) {\r
6551 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6552 return '&#' + a.charCodeAt(0) + ';';\r
6553 });\r
6554 }\r
6555 }\r
6556\r
6557 return h;\r
6558 },\r
6559\r
6560 _setup : function() {\r
6561 var t = this, s = this.settings;\r
6562\r
6563 if (t.done)\r
6564 return;\r
6565\r
6566 t.done = 1;\r
6567\r
6568 t.setRules(s.valid_elements);\r
6569 t.addRules(s.extended_valid_elements);\r
6570 t.addValidChildRules(s.valid_child_elements);\r
6571\r
6572 if (s.invalid_elements)\r
6573 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
6574\r
6575 if (s.attrib_value_filter)\r
6576 t.attribValueFilter = s.attribValueFilter;\r
6577 },\r
6578\r
6579 _getAttrib : function(n, a, na) {\r
6580 var i, v;\r
6581\r
6582 na = na || a.name;\r
6583\r
6584 if (a.forcedVal && (v = a.forcedVal)) {\r
6585 if (v === '{$uid}')\r
6586 return this.dom.uniqueId();\r
6587\r
6588 return v;\r
6589 }\r
6590\r
6591 v = this.dom.getAttrib(n, na);\r
6592\r
6593 switch (na) {\r
6594 case 'rowspan':\r
6595 case 'colspan':\r
6596 // Whats the point? Remove usless attribute value\r
6597 if (v == '1')\r
6598 v = '';\r
6599\r
6600 break;\r
6601 }\r
6602\r
6603 if (this.attribValueFilter)\r
6604 v = this.attribValueFilter(na, v, n);\r
6605\r
6606 if (a.validVals) {\r
6607 for (i = a.validVals.length - 1; i >= 0; i--) {\r
6608 if (v == a.validVals[i])\r
6609 break;\r
6610 }\r
6611\r
6612 if (i == -1)\r
6613 return null;\r
6614 }\r
6615\r
6616 if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
6617 v = a.defaultVal;\r
6618\r
6619 if (v === '{$uid}')\r
6620 return this.dom.uniqueId();\r
6621\r
6622 return v;\r
6623 } else {\r
6624 // Remove internal mceItemXX classes when content is extracted from editor\r
6625 if (na == 'class' && this.processObj.get)\r
6626 v = v.replace(/\s?mceItem\w+\s?/g, '');\r
6627 }\r
6628\r
6629 if (v === '')\r
6630 return null;\r
6631\r
6632\r
6633 return v;\r
6634 }\r
6635 });\r
6636})(tinymce);\r
6637(function(tinymce) {\r
6638 var each = tinymce.each, Event = tinymce.dom.Event;\r
6639\r
6640 tinymce.create('tinymce.dom.ScriptLoader', {\r
6641 ScriptLoader : function(s) {\r
6642 this.settings = s || {};\r
6643 this.queue = [];\r
6644 this.lookup = {};\r
6645 },\r
6646\r
6647 isDone : function(u) {\r
6648 return this.lookup[u] ? this.lookup[u].state == 2 : 0;\r
6649 },\r
6650\r
6651 markDone : function(u) {\r
6652 this.lookup[u] = {state : 2, url : u};\r
6653 },\r
6654\r
6655 add : function(u, cb, s, pr) {\r
6656 var t = this, lo = t.lookup, o;\r
6657\r
6658 if (o = lo[u]) {\r
6659 // Is loaded fire callback\r
6660 if (cb && o.state == 2)\r
6661 cb.call(s || this);\r
6662\r
6663 return o;\r
6664 }\r
6665\r
6666 o = {state : 0, url : u, func : cb, scope : s || this};\r
6667\r
6668 if (pr)\r
6669 t.queue.unshift(o);\r
6670 else\r
6671 t.queue.push(o);\r
6672\r
6673 lo[u] = o;\r
6674\r
6675 return o;\r
6676 },\r
6677\r
6678 load : function(u, cb, s) {\r
6679 var t = this, o;\r
6680\r
6681 if (o = t.lookup[u]) {\r
6682 // Is loaded fire callback\r
6683 if (cb && o.state == 2)\r
6684 cb.call(s || t);\r
6685\r
6686 return o;\r
6687 }\r
6688\r
6689 function loadScript(u) {\r
6690 if (Event.domLoaded || t.settings.strict_mode) {\r
6691 tinymce.util.XHR.send({\r
6692 url : tinymce._addVer(u),\r
6693 error : t.settings.error,\r
6694 async : false,\r
6695 success : function(co) {\r
6696 t.eval(co);\r
6697 }\r
6698 });\r
6699 } else\r
6700 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');\r
6701 };\r
6702\r
6703 if (!tinymce.is(u, 'string')) {\r
6704 each(u, function(u) {\r
6705 loadScript(u);\r
6706 });\r
6707\r
6708 if (cb)\r
6709 cb.call(s || t);\r
6710 } else {\r
6711 loadScript(u);\r
6712\r
6713 if (cb)\r
6714 cb.call(s || t);\r
6715 }\r
6716 },\r
6717\r
6718 loadQueue : function(cb, s) {\r
6719 var t = this;\r
6720\r
6721 if (!t.queueLoading) {\r
6722 t.queueLoading = 1;\r
6723 t.queueCallbacks = [];\r
6724\r
6725 t.loadScripts(t.queue, function() {\r
6726 t.queueLoading = 0;\r
6727\r
6728 if (cb)\r
6729 cb.call(s || t);\r
6730\r
6731 each(t.queueCallbacks, function(o) {\r
6732 o.func.call(o.scope);\r
6733 });\r
6734 });\r
6735 } else if (cb)\r
6736 t.queueCallbacks.push({func : cb, scope : s || t});\r
6737 },\r
6738\r
6739 eval : function(co) {\r
6740 var w = window;\r
6741\r
6742 // Evaluate script\r
6743 if (!w.execScript) {\r
6744 try {\r
6745 eval.call(w, co);\r
6746 } catch (ex) {\r
6747 eval(co, w); // Firefox 3.0a8\r
6748 }\r
6749 } else\r
6750 w.execScript(co); // IE\r
6751 },\r
6752\r
6753 loadScripts : function(sc, cb, s) {\r
6754 var t = this, lo = t.lookup;\r
6755\r
6756 function done(o) {\r
6757 o.state = 2; // Has been loaded\r
6758\r
6759 // Run callback\r
6760 if (o.func)\r
6761 o.func.call(o.scope || t);\r
6762 };\r
6763\r
6764 function allDone() {\r
6765 var l;\r
6766\r
6767 // Check if all files are loaded\r
6768 l = sc.length;\r
6769 each(sc, function(o) {\r
6770 o = lo[o.url];\r
6771\r
6772 if (o.state === 2) {// It has finished loading\r
6773 done(o);\r
6774 l--;\r
6775 } else\r
6776 load(o);\r
6777 });\r
6778\r
6779 // They are all loaded\r
6780 if (l === 0 && cb) {\r
6781 cb.call(s || t);\r
6782 cb = 0;\r
6783 }\r
6784 };\r
6785\r
6786 function load(o) {\r
6787 if (o.state > 0)\r
6788 return;\r
6789\r
6790 o.state = 1; // Is loading\r
6791\r
6792 tinymce.dom.ScriptLoader.loadScript(o.url, function() {\r
6793 done(o);\r
6794 allDone();\r
6795 });\r
6796\r
6797 /*\r
6798 tinymce.util.XHR.send({\r
6799 url : o.url,\r
6800 error : t.settings.error,\r
6801 success : function(co) {\r
6802 t.eval(co);\r
6803 done(o);\r
6804 allDone();\r
6805 }\r
6806 });\r
6807 */\r
6808 };\r
6809\r
6810 each(sc, function(o) {\r
6811 var u = o.url;\r
6812\r
6813 // Add to queue if needed\r
6814 if (!lo[u]) {\r
6815 lo[u] = o;\r
6816 t.queue.push(o);\r
6817 } else\r
6818 o = lo[u];\r
6819\r
6820 // Is already loading or has been loaded\r
6821 if (o.state > 0)\r
6822 return;\r
6823\r
6824 if (!Event.domLoaded && !t.settings.strict_mode) {\r
6825 var ix, ol = '';\r
6826\r
6827 // Add onload events\r
6828 if (cb || o.func) {\r
6829 o.state = 1; // Is loading\r
6830\r
6831 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {\r
6832 done(o);\r
6833 allDone();\r
6834 });\r
6835\r
6836 if (tinymce.isIE)\r
6837 ol = ' onreadystatechange="';\r
6838 else\r
6839 ol = ' onload="';\r
6840\r
6841 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';\r
6842 }\r
6843\r
6844 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');\r
6845\r
6846 if (!o.func)\r
6847 done(o);\r
6848 } else\r
6849 load(o);\r
6850 });\r
6851\r
6852 allDone();\r
6853 },\r
6854\r
6855 // Static methods\r
6856 'static' : {\r
6857 _addOnLoad : function(f) {\r
6858 var t = this;\r
6859\r
6860 t._funcs = t._funcs || [];\r
6861 t._funcs.push(f);\r
6862\r
6863 return t._funcs.length - 1;\r
6864 },\r
6865\r
6866 _onLoad : function(e, u, ix) {\r
6867 if (!tinymce.isIE || e.readyState == 'complete')\r
6868 this._funcs[ix].call(this);\r
6869 },\r
6870\r
6871 loadScript : function(u, cb) {\r
6872 var id = tinymce.DOM.uniqueId(), e;\r
6873\r
6874 function done() {\r
6875 Event.clear(id);\r
6876 tinymce.DOM.remove(id);\r
6877\r
6878 if (cb) {\r
6879 cb.call(document, u);\r
6880 cb = 0;\r
6881 }\r
6882 };\r
6883\r
6884 if (tinymce.isIE) {\r
6885/* Event.add(e, 'readystatechange', function(e) {\r
6886 if (e.target && e.target.readyState == 'complete')\r
6887 done();\r
6888 });*/\r
6889\r
6890 tinymce.util.XHR.send({\r
6891 url : tinymce._addVer(u),\r
6892 async : false,\r
6893 success : function(co) {\r
6894 window.execScript(co);\r
6895 done();\r
6896 }\r
6897 });\r
6898 } else {\r
6899 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)});\r
6900 Event.add(e, 'load', done);\r
6901\r
6902 // Check for head or body\r
6903 (document.getElementsByTagName('head')[0] || document.body).appendChild(e);\r
6904 }\r
6905 }\r
6906 }\r
6907 });\r
6908\r
6909 // Global script loader\r
6910 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6911})(tinymce);\r
6912(function(tinymce) {\r
6913 // Shorten class names\r
6914 var DOM = tinymce.DOM, is = tinymce.is;\r
6915\r
6916 tinymce.create('tinymce.ui.Control', {\r
6917 Control : function(id, s) {\r
6918 this.id = id;\r
6919 this.settings = s = s || {};\r
6920 this.rendered = false;\r
6921 this.onRender = new tinymce.util.Dispatcher(this);\r
6922 this.classPrefix = '';\r
6923 this.scope = s.scope || this;\r
6924 this.disabled = 0;\r
6925 this.active = 0;\r
6926 },\r
6927\r
6928 setDisabled : function(s) {\r
6929 var e;\r
6930\r
6931 if (s != this.disabled) {\r
6932 e = DOM.get(this.id);\r
6933\r
6934 // Add accessibility title for unavailable actions\r
6935 if (e && this.settings.unavailable_prefix) {\r
6936 if (s) {\r
6937 this.prevTitle = e.title;\r
6938 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
6939 } else\r
6940 e.title = this.prevTitle;\r
6941 }\r
6942\r
6943 this.setState('Disabled', s);\r
6944 this.setState('Enabled', !s);\r
6945 this.disabled = s;\r
6946 }\r
6947 },\r
6948\r
6949 isDisabled : function() {\r
6950 return this.disabled;\r
6951 },\r
6952\r
6953 setActive : function(s) {\r
6954 if (s != this.active) {\r
6955 this.setState('Active', s);\r
6956 this.active = s;\r
6957 }\r
6958 },\r
6959\r
6960 isActive : function() {\r
6961 return this.active;\r
6962 },\r
6963\r
6964 setState : function(c, s) {\r
6965 var n = DOM.get(this.id);\r
6966\r
6967 c = this.classPrefix + c;\r
6968\r
6969 if (s)\r
6970 DOM.addClass(n, c);\r
6971 else\r
6972 DOM.removeClass(n, c);\r
6973 },\r
6974\r
6975 isRendered : function() {\r
6976 return this.rendered;\r
6977 },\r
6978\r
6979 renderHTML : function() {\r
6980 },\r
6981\r
6982 renderTo : function(n) {\r
6983 DOM.setHTML(n, this.renderHTML());\r
6984 },\r
6985\r
6986 postRender : function() {\r
6987 var t = this, b;\r
6988\r
6989 // Set pending states\r
6990 if (is(t.disabled)) {\r
6991 b = t.disabled;\r
6992 t.disabled = -1;\r
6993 t.setDisabled(b);\r
6994 }\r
6995\r
6996 if (is(t.active)) {\r
6997 b = t.active;\r
6998 t.active = -1;\r
6999 t.setActive(b);\r
7000 }\r
7001 },\r
7002\r
7003 remove : function() {\r
7004 DOM.remove(this.id);\r
7005 this.destroy();\r
7006 },\r
7007\r
7008 destroy : function() {\r
7009 tinymce.dom.Event.clear(this.id);\r
7010 }\r
7011 });\r
7012})(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
7013 Container : function(id, s) {\r
7014 this.parent(id, s);\r
7015\r
7016 this.controls = [];\r
7017\r
7018 this.lookup = {};\r
7019 },\r
7020\r
7021 add : function(c) {\r
7022 this.lookup[c.id] = c;\r
7023 this.controls.push(c);\r
7024\r
7025 return c;\r
7026 },\r
7027\r
7028 get : function(n) {\r
7029 return this.lookup[n];\r
7030 }\r
7031});\r
7032\r
7033tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
7034 Separator : function(id, s) {\r
7035 this.parent(id, s);\r
7036 this.classPrefix = 'mceSeparator';\r
7037 },\r
7038\r
7039 renderHTML : function() {\r
7040 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
7041 }\r
7042});\r
7043(function(tinymce) {\r
7044 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7045\r
7046 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
7047 MenuItem : function(id, s) {\r
7048 this.parent(id, s);\r
7049 this.classPrefix = 'mceMenuItem';\r
7050 },\r
7051\r
7052 setSelected : function(s) {\r
7053 this.setState('Selected', s);\r
7054 this.selected = s;\r
7055 },\r
7056\r
7057 isSelected : function() {\r
7058 return this.selected;\r
7059 },\r
7060\r
7061 postRender : function() {\r
7062 var t = this;\r
7063 \r
7064 t.parent();\r
7065\r
7066 // Set pending state\r
7067 if (is(t.selected))\r
7068 t.setSelected(t.selected);\r
7069 }\r
7070 });\r
7071})(tinymce);\r
7072(function(tinymce) {\r
7073 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7074\r
7075 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
7076 Menu : function(id, s) {\r
7077 var t = this;\r
7078\r
7079 t.parent(id, s);\r
7080 t.items = {};\r
7081 t.collapsed = false;\r
7082 t.menuCount = 0;\r
7083 t.onAddItem = new tinymce.util.Dispatcher(this);\r
7084 },\r
7085\r
7086 expand : function(d) {\r
7087 var t = this;\r
7088\r
7089 if (d) {\r
7090 walk(t, function(o) {\r
7091 if (o.expand)\r
7092 o.expand();\r
7093 }, 'items', t);\r
7094 }\r
7095\r
7096 t.collapsed = false;\r
7097 },\r
7098\r
7099 collapse : function(d) {\r
7100 var t = this;\r
7101\r
7102 if (d) {\r
7103 walk(t, function(o) {\r
7104 if (o.collapse)\r
7105 o.collapse();\r
7106 }, 'items', t);\r
7107 }\r
7108\r
7109 t.collapsed = true;\r
7110 },\r
7111\r
7112 isCollapsed : function() {\r
7113 return this.collapsed;\r
7114 },\r
7115\r
7116 add : function(o) {\r
7117 if (!o.settings)\r
7118 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
7119\r
7120 this.onAddItem.dispatch(this, o);\r
7121\r
7122 return this.items[o.id] = o;\r
7123 },\r
7124\r
7125 addSeparator : function() {\r
7126 return this.add({separator : true});\r
7127 },\r
7128\r
7129 addMenu : function(o) {\r
7130 if (!o.collapse)\r
7131 o = this.createMenu(o);\r
7132\r
7133 this.menuCount++;\r
7134\r
7135 return this.add(o);\r
7136 },\r
7137\r
7138 hasMenus : function() {\r
7139 return this.menuCount !== 0;\r
7140 },\r
7141\r
7142 remove : function(o) {\r
7143 delete this.items[o.id];\r
7144 },\r
7145\r
7146 removeAll : function() {\r
7147 var t = this;\r
7148\r
7149 walk(t, function(o) {\r
7150 if (o.removeAll)\r
7151 o.removeAll();\r
7152 else\r
7153 o.remove();\r
7154\r
7155 o.destroy();\r
7156 }, 'items', t);\r
7157\r
7158 t.items = {};\r
7159 },\r
7160\r
7161 createMenu : function(o) {\r
7162 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
7163\r
7164 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
7165\r
7166 return m;\r
7167 }\r
7168 });\r
7169})(tinymce);(function(tinymce) {\r
7170 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
7171\r
7172 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
7173 DropMenu : function(id, s) {\r
7174 s = s || {};\r
7175 s.container = s.container || DOM.doc.body;\r
7176 s.offset_x = s.offset_x || 0;\r
7177 s.offset_y = s.offset_y || 0;\r
7178 s.vp_offset_x = s.vp_offset_x || 0;\r
7179 s.vp_offset_y = s.vp_offset_y || 0;\r
7180\r
7181 if (is(s.icons) && !s.icons)\r
7182 s['class'] += ' mceNoIcons';\r
7183\r
7184 this.parent(id, s);\r
7185 this.onShowMenu = new tinymce.util.Dispatcher(this);\r
7186 this.onHideMenu = new tinymce.util.Dispatcher(this);\r
7187 this.classPrefix = 'mceMenu';\r
7188 },\r
7189\r
7190 createMenu : function(s) {\r
7191 var t = this, cs = t.settings, m;\r
7192\r
7193 s.container = s.container || cs.container;\r
7194 s.parent = t;\r
7195 s.constrain = s.constrain || cs.constrain;\r
7196 s['class'] = s['class'] || cs['class'];\r
7197 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
7198 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
7199 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
7200\r
7201 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
7202\r
7203 return m;\r
7204 },\r
7205\r
7206 update : function() {\r
7207 var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
7208\r
7209 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
7210 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
7211\r
7212 if (!DOM.boxModel)\r
7213 t.element.setStyles({width : tw + 2, height : th + 2});\r
7214 else\r
7215 t.element.setStyles({width : tw, height : th});\r
7216\r
7217 if (s.max_width)\r
7218 DOM.setStyle(co, 'width', tw);\r
7219\r
7220 if (s.max_height) {\r
7221 DOM.setStyle(co, 'height', th);\r
7222\r
7223 if (tb.clientHeight < s.max_height)\r
7224 DOM.setStyle(co, 'overflow', 'hidden');\r
7225 }\r
7226 },\r
7227\r
7228 showMenu : function(x, y, px) {\r
7229 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
7230\r
7231 t.collapse(1);\r
7232\r
7233 if (t.isMenuVisible)\r
7234 return;\r
7235\r
7236 if (!t.rendered) {\r
7237 co = DOM.add(t.settings.container, t.renderNode());\r
7238\r
7239 each(t.items, function(o) {\r
7240 o.postRender();\r
7241 });\r
7242\r
7243 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7244 } else\r
7245 co = DOM.get('menu_' + t.id);\r
7246\r
7247 // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
7248 if (!tinymce.isOpera)\r
7249 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
7250\r
7251 DOM.show(co);\r
7252 t.update();\r
7253\r
7254 x += s.offset_x || 0;\r
7255 y += s.offset_y || 0;\r
7256 vp.w -= 4;\r
7257 vp.h -= 4;\r
7258\r
7259 // Move inside viewport if not submenu\r
7260 if (s.constrain) {\r
7261 w = co.clientWidth - ot;\r
7262 h = co.clientHeight - ot;\r
7263 mx = vp.x + vp.w;\r
7264 my = vp.y + vp.h;\r
7265\r
7266 if ((x + s.vp_offset_x + w) > mx)\r
7267 x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
7268\r
7269 if ((y + s.vp_offset_y + h) > my)\r
7270 y = Math.max(0, (my - s.vp_offset_y) - h);\r
7271 }\r
7272\r
7273 DOM.setStyles(co, {left : x , top : y});\r
7274\r
7275 t.element.update();\r
7276\r
7277 t.isMenuVisible = 1;\r
7278 t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
7279 var m;\r
7280\r
7281 e = e.target;\r
7282\r
7283 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
7284 m = t.items[e.id];\r
7285\r
7286 if (m.isDisabled())\r
7287 return;\r
7288\r
7289 dm = t;\r
7290\r
7291 while (dm) {\r
7292 if (dm.hideMenu)\r
7293 dm.hideMenu();\r
7294\r
7295 dm = dm.settings.parent;\r
7296 }\r
7297\r
7298 if (m.settings.onclick)\r
7299 m.settings.onclick(e);\r
7300\r
7301 return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7302 }\r
7303 });\r
7304\r
7305 if (t.hasMenus()) {\r
7306 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
7307 var m, r, mi;\r
7308\r
7309 e = e.target;\r
7310 if (e && (e = DOM.getParent(e, 'tr'))) {\r
7311 m = t.items[e.id];\r
7312\r
7313 if (t.lastMenu)\r
7314 t.lastMenu.collapse(1);\r
7315\r
7316 if (m.isDisabled())\r
7317 return;\r
7318\r
7319 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
7320 //p = DOM.getPos(s.container);\r
7321 r = DOM.getRect(e);\r
7322 m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
7323 t.lastMenu = m;\r
7324 DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
7325 }\r
7326 }\r
7327 });\r
7328 }\r
7329\r
7330 t.onShowMenu.dispatch(t);\r
7331\r
7332 if (s.keyboard_focus) {\r
7333 Event.add(co, 'keydown', t._keyHandler, t);\r
7334 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
7335 t._focusIdx = 0;\r
7336 }\r
7337 },\r
7338\r
7339 hideMenu : function(c) {\r
7340 var t = this, co = DOM.get('menu_' + t.id), e;\r
7341\r
7342 if (!t.isMenuVisible)\r
7343 return;\r
7344\r
7345 Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7346 Event.remove(co, 'click', t.mouseClickFunc);\r
7347 Event.remove(co, 'keydown', t._keyHandler);\r
7348 DOM.hide(co);\r
7349 t.isMenuVisible = 0;\r
7350\r
7351 if (!c)\r
7352 t.collapse(1);\r
7353\r
7354 if (t.element)\r
7355 t.element.hide();\r
7356\r
7357 if (e = DOM.get(t.id))\r
7358 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
7359\r
7360 t.onHideMenu.dispatch(t);\r
7361 },\r
7362\r
7363 add : function(o) {\r
7364 var t = this, co;\r
7365\r
7366 o = t.parent(o);\r
7367\r
7368 if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
7369 t._add(DOM.select('tbody', co)[0], o);\r
7370\r
7371 return o;\r
7372 },\r
7373\r
7374 collapse : function(d) {\r
7375 this.parent(d);\r
7376 this.hideMenu(1);\r
7377 },\r
7378\r
7379 remove : function(o) {\r
7380 DOM.remove(o.id);\r
7381 this.destroy();\r
7382\r
7383 return this.parent(o);\r
7384 },\r
7385\r
7386 destroy : function() {\r
7387 var t = this, co = DOM.get('menu_' + t.id);\r
7388\r
7389 Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7390 Event.remove(co, 'click', t.mouseClickFunc);\r
7391\r
7392 if (t.element)\r
7393 t.element.remove();\r
7394\r
7395 DOM.remove(co);\r
7396 },\r
7397\r
7398 renderNode : function() {\r
7399 var t = this, s = t.settings, n, tb, co, w;\r
7400\r
7401 if (s['class'].indexOf ('mceListBoxMenu') != -1 && tinymce.EditorManager.settings.theme_advanced_toolbar_location == 'external') {\r
7402 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:fixed;_position:absolute;left:0;top:0;z-index:200000'});\r
7403 } else {\r
7404 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
7405 }\r
7406 co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
7407 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7408\r
7409 if (s.menu_line)\r
7410 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
7411\r
7412// n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
7413 n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
7414 tb = DOM.add(n, 'tbody');\r
7415\r
7416 each(t.items, function(o) {\r
7417 t._add(tb, o);\r
7418 });\r
7419\r
7420 t.rendered = true;\r
7421\r
7422 return w;\r
7423 },\r
7424\r
7425 // Internal functions\r
7426\r
7427 _keyHandler : function(e) {\r
7428 var t = this, kc = e.keyCode;\r
7429\r
7430 function focus(d) {\r
7431 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7432\r
7433 if (e) {\r
7434 t._focusIdx = i;\r
7435 e.focus();\r
7436 }\r
7437 };\r
7438\r
7439 switch (kc) {\r
7440 case 38:\r
7441 focus(-1); // Select first link\r
7442 return;\r
7443\r
7444 case 40:\r
7445 focus(1);\r
7446 return;\r
7447\r
7448 case 13:\r
7449 return;\r
7450\r
7451 case 27:\r
7452 return this.hideMenu();\r
7453 }\r
7454 },\r
7455\r
7456 _add : function(tb, o) {\r
7457 var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7458\r
7459 if (s.separator) {\r
7460 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7461 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7462\r
7463 if (n = ro.previousSibling)\r
7464 DOM.addClass(n, 'mceLast');\r
7465\r
7466 return;\r
7467 }\r
7468\r
7469 n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7470 n = it = DOM.add(n, 'td');\r
7471 n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7472\r
7473 DOM.addClass(it, s['class']);\r
7474// n = DOM.add(n, 'span', {'class' : 'item'});\r
7475\r
7476 ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7477\r
7478 if (s.icon_src)\r
7479 DOM.add(ic, 'img', {src : s.icon_src});\r
7480\r
7481 n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7482\r
7483 if (o.settings.style)\r
7484 DOM.setAttrib(n, 'style', o.settings.style);\r
7485\r
7486 if (tb.childNodes.length == 1)\r
7487 DOM.addClass(ro, 'mceFirst');\r
7488\r
7489 if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7490 DOM.addClass(ro, 'mceFirst');\r
7491\r
7492 if (o.collapse)\r
7493 DOM.addClass(ro, cp + 'ItemSub');\r
7494\r
7495 if (n = ro.previousSibling)\r
7496 DOM.removeClass(n, 'mceLast');\r
7497\r
7498 DOM.addClass(ro, 'mceLast');\r
7499 }\r
7500 });\r
7501})(tinymce);(function(tinymce) {\r
7502 var DOM = tinymce.DOM;\r
7503\r
7504 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7505 Button : function(id, s) {\r
7506 this.parent(id, s);\r
7507 this.classPrefix = 'mceButton';\r
7508 },\r
7509\r
7510 renderHTML : function() {\r
7511 var cp = this.classPrefix, s = this.settings, h, l;\r
7512\r
7513 l = DOM.encode(s.label || '');\r
7514 h = '<a id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM.encode(s.title) + '">';\r
7515\r
7516 if (s.image)\r
7517 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7518 else\r
7519 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7520\r
7521 return h;\r
7522 },\r
7523\r
7524 postRender : function() {\r
7525 var t = this, s = t.settings;\r
7526\r
7527 tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7528 if (!t.isDisabled())\r
7529 return s.onclick.call(s.scope, e);\r
7530 });\r
7531 }\r
7532 });\r
7533})(tinymce);\r
7534(function(tinymce) {\r
7535 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7536\r
7537 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
7538 ListBox : function(id, s) {\r
7539 var t = this;\r
7540\r
7541 t.parent(id, s);\r
7542\r
7543 t.items = [];\r
7544\r
7545 t.onChange = new Dispatcher(t);\r
7546\r
7547 t.onPostRender = new Dispatcher(t);\r
7548\r
7549 t.onAdd = new Dispatcher(t);\r
7550\r
7551 t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7552\r
7553 t.classPrefix = 'mceListBox';\r
7554 },\r
7555\r
7556 select : function(va) {\r
7557 var t = this, fv, f;\r
7558\r
7559 if (va == undefined)\r
7560 return t.selectByIndex(-1);\r
7561\r
7562 // Is string or number make function selector\r
7563 if (va && va.call)\r
7564 f = va;\r
7565 else {\r
7566 f = function(v) {\r
7567 return v == va;\r
7568 };\r
7569 }\r
7570\r
7571 // Do we need to do something?\r
7572 if (va != t.selectedValue) {\r
7573 // Find item\r
7574 each(t.items, function(o, i) {\r
7575 if (f(o.value)) {\r
7576 fv = 1;\r
7577 t.selectByIndex(i);\r
7578 return false;\r
7579 }\r
7580 });\r
7581\r
7582 if (!fv)\r
7583 t.selectByIndex(-1);\r
7584 }\r
7585 },\r
7586\r
7587 selectByIndex : function(idx) {\r
7588 var t = this, e, o;\r
7589\r
7590 if (idx != t.selectedIndex) {\r
7591 e = DOM.get(t.id + '_text');\r
7592 o = t.items[idx];\r
7593\r
7594 if (o) {\r
7595 t.selectedValue = o.value;\r
7596 t.selectedIndex = idx;\r
7597 DOM.setHTML(e, DOM.encode(o.title));\r
7598 DOM.removeClass(e, 'mceTitle');\r
7599 } else {\r
7600 DOM.setHTML(e, DOM.encode(t.settings.title));\r
7601 DOM.addClass(e, 'mceTitle');\r
7602 t.selectedValue = t.selectedIndex = null;\r
7603 }\r
7604\r
7605 e = 0;\r
7606 }\r
7607 },\r
7608\r
7609 add : function(n, v, o) {\r
7610 var t = this;\r
7611\r
7612 o = o || {};\r
7613 o = tinymce.extend(o, {\r
7614 title : n,\r
7615 value : v\r
7616 });\r
7617\r
7618 t.items.push(o);\r
7619 t.onAdd.dispatch(t, o);\r
7620 },\r
7621\r
7622 getLength : function() {\r
7623 return this.items.length;\r
7624 },\r
7625\r
7626 renderHTML : function() {\r
7627 var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
7628\r
7629 h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
7630 h += '<td>' + DOM.createHTML('a', {id : t.id + '_text', href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';\r
7631 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';\r
7632 h += '</tr></tbody></table>';\r
7633\r
7634 return h;\r
7635 },\r
7636\r
7637 showMenu : function() {\r
7638 var t = this, p1, p2, e = DOM.get(this.id), m;\r
7639\r
7640 if (t.isDisabled() || t.items.length == 0)\r
7641 return;\r
7642\r
7643 if (t.menu && t.menu.isMenuVisible)\r
7644 return t.hideMenu();\r
7645\r
7646 if (!t.isMenuRendered) {\r
7647 t.renderMenu();\r
7648 t.isMenuRendered = true;\r
7649 }\r
7650\r
7651 p1 = DOM.getPos(this.settings.menu_container);\r
7652 p2 = DOM.getPos(e);\r
7653\r
7654 m = t.menu;\r
7655 m.settings.offset_x = p2.x;\r
7656 m.settings.offset_y = p2.y;\r
7657 m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
7658\r
7659 // Select in menu\r
7660 if (t.oldID)\r
7661 m.items[t.oldID].setSelected(0);\r
7662\r
7663 each(t.items, function(o) {\r
7664 if (o.value === t.selectedValue) {\r
7665 m.items[o.id].setSelected(1);\r
7666 t.oldID = o.id;\r
7667 }\r
7668 });\r
7669\r
7670 m.showMenu(0, e.clientHeight);\r
7671\r
7672 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7673 DOM.addClass(t.id, t.classPrefix + 'Selected');\r
7674\r
7675 //DOM.get(t.id + '_text').focus();\r
7676 },\r
7677\r
7678 hideMenu : function(e) {\r
7679 var t = this;\r
7680\r
7681 // Prevent double toogles by canceling the mouse click event to the button\r
7682 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
7683 return;\r
7684\r
7685 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7686 DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
7687 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7688\r
7689 if (t.menu)\r
7690 t.menu.hideMenu();\r
7691 }\r
7692 },\r
7693\r
7694 renderMenu : function() {\r
7695 var t = this, m;\r
7696\r
7697 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7698 menu_line : 1,\r
7699 'class' : t.classPrefix + 'Menu mceNoIcons',\r
7700 max_width : 150,\r
7701 max_height : 500\r
7702 });\r
7703\r
7704 m.onHideMenu.add(t.hideMenu, t);\r
7705\r
7706 m.add({\r
7707 title : t.settings.title,\r
7708 'class' : 'mceMenuItemTitle',\r
7709 onclick : function() {\r
7710 if (t.settings.onselect('') !== false)\r
7711 t.select(''); // Must be runned after\r
7712 }\r
7713 });\r
7714\r
7715 each(t.items, function(o) {\r
7716 o.id = DOM.uniqueId();\r
7717 o.onclick = function() {\r
7718 if (t.settings.onselect(o.value) !== false)\r
7719 t.select(o.value); // Must be runned after\r
7720 };\r
7721\r
7722 m.add(o);\r
7723 });\r
7724\r
7725 t.onRenderMenu.dispatch(t, m);\r
7726 t.menu = m;\r
7727 },\r
7728\r
7729 postRender : function() {\r
7730 var t = this, cp = t.classPrefix;\r
7731\r
7732 Event.add(t.id, 'click', t.showMenu, t);\r
7733 Event.add(t.id + '_text', 'focus', function(e) {\r
7734 if (!t._focused) {\r
7735 t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
7736 var idx = -1, v, kc = e.keyCode;\r
7737\r
7738 // Find current index\r
7739 each(t.items, function(v, i) {\r
7740 if (t.selectedValue == v.value)\r
7741 idx = i;\r
7742 });\r
7743\r
7744 // Move up/down\r
7745 if (kc == 38)\r
7746 v = t.items[idx - 1];\r
7747 else if (kc == 40)\r
7748 v = t.items[idx + 1];\r
7749 else if (kc == 13) {\r
7750 // Fake select on enter\r
7751 v = t.selectedValue;\r
7752 t.selectedValue = null; // Needs to be null to fake change\r
7753 t.settings.onselect(v);\r
7754 return Event.cancel(e);\r
7755 }\r
7756\r
7757 if (v) {\r
7758 t.hideMenu();\r
7759 t.select(v.value);\r
7760 }\r
7761 });\r
7762 }\r
7763\r
7764 t._focused = 1;\r
7765 });\r
7766 Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
7767\r
7768 // Old IE doesn't have hover on all elements\r
7769 if (tinymce.isIE6 || !DOM.boxModel) {\r
7770 Event.add(t.id, 'mouseover', function() {\r
7771 if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7772 DOM.addClass(t.id, cp + 'Hover');\r
7773 });\r
7774\r
7775 Event.add(t.id, 'mouseout', function() {\r
7776 if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
7777 DOM.removeClass(t.id, cp + 'Hover');\r
7778 });\r
7779 }\r
7780\r
7781 t.onPostRender.dispatch(t, DOM.get(t.id));\r
7782 },\r
7783\r
7784 destroy : function() {\r
7785 this.parent();\r
7786\r
7787 Event.clear(this.id + '_text');\r
7788 Event.clear(this.id + '_open');\r
7789 }\r
7790 });\r
7791})(tinymce);(function(tinymce) {\r
7792 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7793\r
7794 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
7795 NativeListBox : function(id, s) {\r
7796 this.parent(id, s);\r
7797 this.classPrefix = 'mceNativeListBox';\r
7798 },\r
7799\r
7800 setDisabled : function(s) {\r
7801 DOM.get(this.id).disabled = s;\r
7802 },\r
7803\r
7804 isDisabled : function() {\r
7805 return DOM.get(this.id).disabled;\r
7806 },\r
7807\r
7808 select : function(va) {\r
7809 var t = this, fv, f;\r
7810\r
7811 if (va == undefined)\r
7812 return t.selectByIndex(-1);\r
7813\r
7814 // Is string or number make function selector\r
7815 if (va && va.call)\r
7816 f = va;\r
7817 else {\r
7818 f = function(v) {\r
7819 return v == va;\r
7820 };\r
7821 }\r
7822\r
7823 // Do we need to do something?\r
7824 if (va != t.selectedValue) {\r
7825 // Find item\r
7826 each(t.items, function(o, i) {\r
7827 if (f(o.value)) {\r
7828 fv = 1;\r
7829 t.selectByIndex(i);\r
7830 return false;\r
7831 }\r
7832 });\r
7833\r
7834 if (!fv)\r
7835 t.selectByIndex(-1);\r
7836 }\r
7837 },\r
7838\r
7839 selectByIndex : function(idx) {\r
7840 DOM.get(this.id).selectedIndex = idx + 1;\r
7841 this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
7842 },\r
7843\r
7844 add : function(n, v, a) {\r
7845 var o, t = this;\r
7846\r
7847 a = a || {};\r
7848 a.value = v;\r
7849\r
7850 if (t.isRendered())\r
7851 DOM.add(DOM.get(this.id), 'option', a, n);\r
7852\r
7853 o = {\r
7854 title : n,\r
7855 value : v,\r
7856 attribs : a\r
7857 };\r
7858\r
7859 t.items.push(o);\r
7860 t.onAdd.dispatch(t, o);\r
7861 },\r
7862\r
7863 getLength : function() {\r
7864 return DOM.get(this.id).options.length - 1;\r
7865 },\r
7866\r
7867 renderHTML : function() {\r
7868 var h, t = this;\r
7869\r
7870 h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
7871\r
7872 each(t.items, function(it) {\r
7873 h += DOM.createHTML('option', {value : it.value}, it.title);\r
7874 });\r
7875\r
7876 h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
7877\r
7878 return h;\r
7879 },\r
7880\r
7881 postRender : function() {\r
7882 var t = this, ch;\r
7883\r
7884 t.rendered = true;\r
7885\r
7886 function onChange(e) {\r
7887 var v = t.items[e.target.selectedIndex - 1];\r
7888\r
7889 if (v && (v = v.value)) {\r
7890 t.onChange.dispatch(t, v);\r
7891\r
7892 if (t.settings.onselect)\r
7893 t.settings.onselect(v);\r
7894 }\r
7895 };\r
7896\r
7897 Event.add(t.id, 'change', onChange);\r
7898\r
7899 // Accessibility keyhandler\r
7900 Event.add(t.id, 'keydown', function(e) {\r
7901 var bf;\r
7902\r
7903 Event.remove(t.id, 'change', ch);\r
7904\r
7905 bf = Event.add(t.id, 'blur', function() {\r
7906 Event.add(t.id, 'change', onChange);\r
7907 Event.remove(t.id, 'blur', bf);\r
7908 });\r
7909\r
7910 if (e.keyCode == 13 || e.keyCode == 32) {\r
7911 onChange(e);\r
7912 return Event.cancel(e);\r
7913 }\r
7914 });\r
7915\r
7916 t.onPostRender.dispatch(t, DOM.get(t.id));\r
7917 }\r
7918 });\r
7919})(tinymce);(function(tinymce) {\r
7920 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
7921\r
7922 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
7923 MenuButton : function(id, s) {\r
7924 this.parent(id, s);\r
7925\r
7926 this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7927\r
7928 s.menu_container = s.menu_container || DOM.doc.body;\r
7929 },\r
7930\r
7931 showMenu : function() {\r
7932 var t = this, p1, p2, e = DOM.get(t.id), m;\r
7933\r
7934 if (t.isDisabled())\r
7935 return;\r
7936\r
7937 if (!t.isMenuRendered) {\r
7938 t.renderMenu();\r
7939 t.isMenuRendered = true;\r
7940 }\r
7941\r
7942 if (t.isMenuVisible)\r
7943 return t.hideMenu();\r
7944\r
7945 p1 = DOM.getPos(t.settings.menu_container);\r
7946 p2 = DOM.getPos(e);\r
7947\r
7948 m = t.menu;\r
7949 m.settings.offset_x = p2.x;\r
7950 m.settings.offset_y = p2.y;\r
7951 m.settings.vp_offset_x = p2.x;\r
7952 m.settings.vp_offset_y = p2.y;\r
7953 m.settings.keyboard_focus = t._focused;\r
7954 m.showMenu(0, e.clientHeight);\r
7955\r
7956 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
7957 t.setState('Selected', 1);\r
7958\r
7959 t.isMenuVisible = 1;\r
7960 },\r
7961\r
7962 renderMenu : function() {\r
7963 var t = this, m;\r
7964\r
7965 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
7966 menu_line : 1,\r
7967 'class' : this.classPrefix + 'Menu',\r
7968 icons : t.settings.icons\r
7969 });\r
7970\r
7971 m.onHideMenu.add(t.hideMenu, t);\r
7972\r
7973 t.onRenderMenu.dispatch(t, m);\r
7974 t.menu = m;\r
7975 },\r
7976\r
7977 hideMenu : function(e) {\r
7978 var t = this;\r
7979\r
7980 // Prevent double toogles by canceling the mouse click event to the button\r
7981 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
7982 return;\r
7983\r
7984 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
7985 t.setState('Selected', 0);\r
7986 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
7987 if (t.menu)\r
7988 t.menu.hideMenu();\r
7989 }\r
7990\r
7991 t.isMenuVisible = 0;\r
7992 },\r
7993\r
7994 postRender : function() {\r
7995 var t = this, s = t.settings;\r
7996\r
7997 Event.add(t.id, 'click', function() {\r
7998 if (!t.isDisabled()) {\r
7999 if (s.onclick)\r
8000 s.onclick(t.value);\r
8001\r
8002 t.showMenu();\r
8003 }\r
8004 });\r
8005 }\r
8006 });\r
8007})(tinymce);(function(tinymce) {\r
8008 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8009\r
8010 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
8011 SplitButton : function(id, s) {\r
8012 this.parent(id, s);\r
8013 this.classPrefix = 'mceSplitButton';\r
8014 },\r
8015\r
8016 renderHTML : function() {\r
8017 var h, t = this, s = t.settings, h1;\r
8018\r
8019 h = '<tbody><tr>';\r
8020\r
8021 if (s.image)\r
8022 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
8023 else\r
8024 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
8025\r
8026 h += '<td>' + DOM.createHTML('a', {id : t.id + '_action', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
8027 \r
8028 h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
8029 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
8030\r
8031 h += '</tr></tbody>';\r
8032\r
8033 return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
8034 },\r
8035\r
8036 postRender : function() {\r
8037 var t = this, s = t.settings;\r
8038\r
8039 if (s.onclick) {\r
8040 Event.add(t.id + '_action', 'click', function() {\r
8041 if (!t.isDisabled())\r
8042 s.onclick(t.value);\r
8043 });\r
8044 }\r
8045\r
8046 Event.add(t.id + '_open', 'click', t.showMenu, t);\r
8047 Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
8048 Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
8049\r
8050 // Old IE doesn't have hover on all elements\r
8051 if (tinymce.isIE6 || !DOM.boxModel) {\r
8052 Event.add(t.id, 'mouseover', function() {\r
8053 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8054 DOM.addClass(t.id, 'mceSplitButtonHover');\r
8055 });\r
8056\r
8057 Event.add(t.id, 'mouseout', function() {\r
8058 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8059 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
8060 });\r
8061 }\r
8062 },\r
8063\r
8064 destroy : function() {\r
8065 this.parent();\r
8066\r
8067 Event.clear(this.id + '_action');\r
8068 Event.clear(this.id + '_open');\r
8069 }\r
8070 });\r
8071})(tinymce);\r
8072(function(tinymce) {\r
8073 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
8074\r
8075 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
8076 ColorSplitButton : function(id, s) {\r
8077 var t = this;\r
8078\r
8079 t.parent(id, s);\r
8080\r
8081 t.settings = s = tinymce.extend({\r
8082 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
8083 grid_width : 8,\r
8084 default_color : '#888888'\r
8085 }, t.settings);\r
8086\r
8087 t.onShowMenu = new tinymce.util.Dispatcher(t);\r
8088\r
8089 t.onHideMenu = new tinymce.util.Dispatcher(t);\r
8090\r
8091 t.value = s.default_color;\r
8092 },\r
8093\r
8094 showMenu : function() {\r
8095 var t = this, r, p, e, p2;\r
8096\r
8097 if (t.isDisabled())\r
8098 return;\r
8099\r
8100 if (!t.isMenuRendered) {\r
8101 t.renderMenu();\r
8102 t.isMenuRendered = true;\r
8103 }\r
8104\r
8105 if (t.isMenuVisible)\r
8106 return t.hideMenu();\r
8107\r
8108 e = DOM.get(t.id);\r
8109 DOM.show(t.id + '_menu');\r
8110 DOM.addClass(e, 'mceSplitButtonSelected');\r
8111 p2 = DOM.getPos(e);\r
8112 DOM.setStyles(t.id + '_menu', {\r
8113 left : p2.x,\r
8114 top : p2.y + e.clientHeight,\r
8115 zIndex : 200000\r
8116 });\r
8117 e = 0;\r
8118\r
8119 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8120 t.onShowMenu.dispatch(t);\r
8121\r
8122 if (t._focused) {\r
8123 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
8124 if (e.keyCode == 27)\r
8125 t.hideMenu();\r
8126 });\r
8127\r
8128 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
8129 }\r
8130\r
8131 t.isMenuVisible = 1;\r
8132 },\r
8133\r
8134 hideMenu : function(e) {\r
8135 var t = this;\r
8136\r
8137 // Prevent double toogles by canceling the mouse click event to the button\r
8138 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
8139 return;\r
8140\r
8141 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
8142 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
8143 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8144 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
8145 DOM.hide(t.id + '_menu');\r
8146 }\r
8147\r
8148 t.onHideMenu.dispatch(t);\r
8149\r
8150 t.isMenuVisible = 0;\r
8151 },\r
8152\r
8153 renderMenu : function() {\r
8154 var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
8155\r
8156 w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});\r
8157 m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
8158 DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
8159\r
8160 n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
8161 tb = DOM.add(n, 'tbody');\r
8162\r
8163 // Generate color grid\r
8164 i = 0;\r
8165 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
8166 c = c.replace(/^#/, '');\r
8167\r
8168 if (!i--) {\r
8169 tr = DOM.add(tb, 'tr');\r
8170 i = s.grid_width - 1;\r
8171 }\r
8172\r
8173 n = DOM.add(tr, 'td');\r
8174\r
8175 n = DOM.add(n, 'a', {\r
8176 href : 'javascript:;',\r
8177 style : {\r
8178 backgroundColor : '#' + c\r
8179 },\r
8180 mce_color : '#' + c\r
8181 });\r
8182 });\r
8183\r
8184 if (s.more_colors_func) {\r
8185 n = DOM.add(tb, 'tr');\r
8186 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
8187 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
8188\r
8189 Event.add(n, 'click', function(e) {\r
8190 s.more_colors_func.call(s.more_colors_scope || this);\r
8191 return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8192 });\r
8193 }\r
8194\r
8195 DOM.addClass(m, 'mceColorSplitMenu');\r
8196\r
8197 Event.add(t.id + '_menu', 'click', function(e) {\r
8198 var c;\r
8199\r
8200 e = e.target;\r
8201\r
8202 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))\r
8203 t.setColor(c);\r
8204\r
8205 return Event.cancel(e); // Prevent IE auto save warning\r
8206 });\r
8207\r
8208 return w;\r
8209 },\r
8210\r
8211 setColor : function(c) {\r
8212 var t = this;\r
8213\r
8214 DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
8215\r
8216 t.value = c;\r
8217 t.hideMenu();\r
8218 t.settings.onselect(c);\r
8219 },\r
8220\r
8221 postRender : function() {\r
8222 var t = this, id = t.id;\r
8223\r
8224 t.parent();\r
8225 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
8226 DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
8227 },\r
8228\r
8229 destroy : function() {\r
8230 this.parent();\r
8231\r
8232 Event.clear(this.id + '_menu');\r
8233 Event.clear(this.id + '_more');\r
8234 DOM.remove(this.id + '_menu');\r
8235 }\r
8236 });\r
8237})(tinymce);\r
8238tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
8239 renderHTML : function() {\r
8240 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
8241\r
8242 cl = t.controls;\r
8243 for (i=0; i<cl.length; i++) {\r
8244 // Get current control, prev control, next control and if the control is a list box or not\r
8245 co = cl[i];\r
8246 pr = cl[i - 1];\r
8247 nx = cl[i + 1];\r
8248\r
8249 // Add toolbar start\r
8250 if (i === 0) {\r
8251 c = 'mceToolbarStart';\r
8252\r
8253 if (co.Button)\r
8254 c += ' mceToolbarStartButton';\r
8255 else if (co.SplitButton)\r
8256 c += ' mceToolbarStartSplitButton';\r
8257 else if (co.ListBox)\r
8258 c += ' mceToolbarStartListBox';\r
8259\r
8260 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8261 }\r
8262\r
8263 // Add toolbar end before list box and after the previous button\r
8264 // This is to fix the o2k7 editor skins\r
8265 if (pr && co.ListBox) {\r
8266 if (pr.Button || pr.SplitButton)\r
8267 h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8268 }\r
8269\r
8270 // Render control HTML\r
8271\r
8272 // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
8273 if (dom.stdMode)\r
8274 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
8275 else\r
8276 h += '<td>' + co.renderHTML() + '</td>';\r
8277\r
8278 // Add toolbar start after list box and before the next button\r
8279 // This is to fix the o2k7 editor skins\r
8280 if (nx && co.ListBox) {\r
8281 if (nx.Button || nx.SplitButton)\r
8282 h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8283 }\r
8284 }\r
8285\r
8286 c = 'mceToolbarEnd';\r
8287\r
8288 if (co.Button)\r
8289 c += ' mceToolbarEndButton';\r
8290 else if (co.SplitButton)\r
8291 c += ' mceToolbarEndSplitButton';\r
8292 else if (co.ListBox)\r
8293 c += ' mceToolbarEndListBox';\r
8294\r
8295 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8296\r
8297 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>');\r
8298 }\r
8299});\r
8300(function(tinymce) {\r
8301 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
8302\r
8303 tinymce.create('tinymce.AddOnManager', {\r
8304 items : [],\r
8305 urls : {},\r
8306 lookup : {},\r
8307\r
8308 onAdd : new Dispatcher(this),\r
8309\r
8310 get : function(n) {\r
8311 return this.lookup[n];\r
8312 },\r
8313\r
8314 requireLangPack : function(n) {\r
8315 var u, s = tinymce.EditorManager.settings;\r
8316\r
8317 if (s && s.language) {\r
8318 u = this.urls[n] + '/langs/' + s.language + '.js';\r
8319\r
8320 if (!tinymce.dom.Event.domLoaded && !s.strict_mode)\r
8321 tinymce.ScriptLoader.load(u);\r
8322 else\r
8323 tinymce.ScriptLoader.add(u);\r
8324 }\r
8325 },\r
8326\r
8327 add : function(id, o) {\r
8328 this.items.push(o);\r
8329 this.lookup[id] = o;\r
8330 this.onAdd.dispatch(this, id, o);\r
8331\r
8332 return o;\r
8333 },\r
8334\r
8335 load : function(n, u, cb, s) {\r
8336 var t = this;\r
8337\r
8338 if (t.urls[n])\r
8339 return;\r
8340\r
8341 if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
8342 u = tinymce.baseURL + '/' + u;\r
8343\r
8344 t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
8345 tinymce.ScriptLoader.add(u, cb, s);\r
8346 }\r
8347 });\r
8348\r
8349 // Create plugin and theme managers\r
8350 tinymce.PluginManager = new tinymce.AddOnManager();\r
8351 tinymce.ThemeManager = new tinymce.AddOnManager();\r
8352}(tinymce));\r
8353\r
8354(function(tinymce) {\r
8355 // Shorten names\r
8356 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;\r
8357\r
8358 tinymce.create('static tinymce.EditorManager', {\r
8359 editors : {},\r
8360\r
8361 i18n : {},\r
8362\r
8363 activeEditor : null,\r
8364\r
8365 preInit : function() {\r
8366 var t = this, lo = window.location;\r
8367\r
8368 // Plone fix\r
8369 var lo_array = lo.href.split('/');\r
8370 if (lo.href.indexOf('portal_factory') != -1) {\r
8371 while (lo_array[lo_array.length-1] != 'portal_factory') {\r
8372 lo_array.pop();\r
8373 }\r
8374 lo_array.pop();\r
8375 } else {\r
8376 if (lo_array.length > 4) {\r
8377 lo_array.pop();\r
8378 }\r
8379 if (lo_array.length > 4) {\r
8380 lo_array.pop();\r
8381 }\r
8382 }\r
8383 tinymce.documentBaseURL = lo_array.join('/') + '/';\r
8384\r
8385 // Plone fix when editing contextual portlets\r
8386 if (lo.href.indexOf('++contextportlets++') != -1) {\r
8387 tinymce.documentBaseURL = tinymce.documentBaseURL.substr(0, lo.href.indexOf('++contextportlets++'));\r
8388 }\r
8389 \r
8390 // Setup some URLs where the editor API is located and where the document is\r
8391 /*\r
8392 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
8393 if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
8394 tinymce.documentBaseURL += '/';\r
8395 */\r
8396\r
8397 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
8398 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
8399\r
8400 // Add before unload listener\r
8401 // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
8402 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
8403 t.onBeforeUnload = new tinymce.util.Dispatcher(t);\r
8404\r
8405 // Must be on window or IE will leak if the editor is placed in frame or iframe\r
8406 Event.add(window, 'beforeunload', function(e) {\r
8407 t.onBeforeUnload.dispatch(t, e);\r
8408 });\r
8409 },\r
8410\r
8411 init : function(s) {\r
8412 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;\r
8413\r
8414 function execCallback(se, n, s) {\r
8415 var f = se[n];\r
8416\r
8417 if (!f)\r
8418 return;\r
8419\r
8420 if (tinymce.is(f, 'string')) {\r
8421 s = f.replace(/\.\w+$/, '');\r
8422 s = s ? tinymce.resolve(s) : 0;\r
8423 f = tinymce.resolve(f);\r
8424 }\r
8425\r
8426 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8427 };\r
8428\r
8429 s = extend({\r
8430 theme : "simple",\r
8431 language : "en",\r
8432 strict_loading_mode : document.contentType == 'application/xhtml+xml'\r
8433 }, s);\r
8434\r
8435 t.settings = s;\r
8436\r
8437 // If page not loaded and strict mode isn't enabled then load them\r
8438 if (!Event.domLoaded && !s.strict_loading_mode) {\r
8439 // Load language\r
8440 if (s.language)\r
8441 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
8442\r
8443 // Load theme\r
8444 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
8445 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
8446\r
8447 // Load plugins\r
8448 if (s.plugins) {\r
8449 pl = explode(s.plugins);\r
8450\r
8451 // Load rest if plugins\r
8452 each(pl, function(v) {\r
8453 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {\r
8454 // Skip safari plugin for other browsers\r
8455 if (!tinymce.isWebKit && v == 'safari')\r
8456 return;\r
8457\r
8458 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');\r
8459 }\r
8460 });\r
8461 }\r
8462\r
8463 sl.loadQueue();\r
8464 }\r
8465\r
8466 // Legacy call\r
8467 Event.add(document, 'init', function() {\r
8468 var l, co;\r
8469\r
8470 execCallback(s, 'onpageload');\r
8471\r
8472 // Verify that it's a valid browser\r
8473 if (s.browsers) {\r
8474 l = false;\r
8475\r
8476 each(explode(s.browsers), function(v) {\r
8477 switch (v) {\r
8478 case 'ie':\r
8479 case 'msie':\r
8480 if (tinymce.isIE)\r
8481 l = true;\r
8482 break;\r
8483\r
8484 case 'gecko':\r
8485 if (tinymce.isGecko)\r
8486 l = true;\r
8487 break;\r
8488\r
8489 case 'safari':\r
8490 case 'webkit':\r
8491 if (tinymce.isWebKit)\r
8492 l = true;\r
8493 break;\r
8494\r
8495 case 'opera':\r
8496 if (tinymce.isOpera)\r
8497 l = true;\r
8498\r
8499 break;\r
8500 }\r
8501 });\r
8502\r
8503 // Not a valid one\r
8504 if (!l)\r
8505 return;\r
8506 }\r
8507\r
8508 switch (s.mode) {\r
8509 case "exact":\r
8510 l = s.elements || '';\r
8511\r
8512 if(l.length > 0) {\r
8513 each(explode(l), function(v) {\r
8514 if (DOM.get(v)) {\r
8515 ed = new tinymce.Editor(v, s);\r
8516 el.push(ed);\r
8517 ed.render(1);\r
8518 } else {\r
8519 c = 0;\r
8520\r
8521 each(document.forms, function(f) {\r
8522 each(f.elements, function(e) {\r
8523 if (e.name === v) {\r
8524 v = 'mce_editor_' + c;\r
8525 DOM.setAttrib(e, 'id', v);\r
8526\r
8527 ed = new tinymce.Editor(v, s);\r
8528 el.push(ed);\r
8529 ed.render(1);\r
8530 }\r
8531 });\r
8532 });\r
8533 }\r
8534 });\r
8535 }\r
8536 break;\r
8537\r
8538 case "textareas":\r
8539 case "specific_textareas":\r
8540 function hasClass(n, c) {\r
8541 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8542 };\r
8543\r
8544 each(DOM.select('textarea'), function(v) {\r
8545 if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8546 return;\r
8547\r
8548 if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8549 // Can we use the name\r
8550 e = DOM.get(v.name);\r
8551 if (!v.id && !e)\r
8552 v.id = v.name;\r
8553\r
8554 // Generate unique name if missing or already exists\r
8555 if (!v.id || t.get(v.id))\r
8556 v.id = DOM.uniqueId();\r
8557\r
8558 ed = new tinymce.Editor(v.id, s);\r
8559 el.push(ed);\r
8560 ed.render(1);\r
8561 }\r
8562 });\r
8563 break;\r
8564 }\r
8565\r
8566 // Call onInit when all editors are initialized\r
8567 if (s.oninit) {\r
8568 l = co = 0;\r
8569\r
8570 each (el, function(ed) {\r
8571 co++;\r
8572\r
8573 if (!ed.initialized) {\r
8574 // Wait for it\r
8575 ed.onInit.add(function() {\r
8576 l++;\r
8577\r
8578 // All done\r
8579 if (l == co)\r
8580 execCallback(s, 'oninit');\r
8581 });\r
8582 } else\r
8583 l++;\r
8584\r
8585 // All done\r
8586 if (l == co)\r
8587 execCallback(s, 'oninit'); \r
8588 });\r
8589 }\r
8590 });\r
8591 },\r
8592\r
8593 get : function(id) {\r
8594 return this.editors[id];\r
8595 },\r
8596\r
8597 getInstanceById : function(id) {\r
8598 return this.get(id);\r
8599 },\r
8600\r
8601 add : function(e) {\r
8602 this.editors[e.id] = e;\r
8603 this._setActive(e);\r
8604\r
8605 return e;\r
8606 },\r
8607\r
8608 remove : function(e) {\r
8609 var t = this;\r
8610\r
8611 // Not in the collection\r
8612 if (!t.editors[e.id])\r
8613 return null;\r
8614\r
8615 delete t.editors[e.id];\r
8616\r
8617 // Select another editor since the active one was removed\r
8618 if (t.activeEditor == e) {\r
8619 t._setActive(null);\r
8620\r
8621 each(t.editors, function(e) {\r
8622 t._setActive(e);\r
8623 return false; // Break\r
8624 });\r
8625 }\r
8626\r
8627 e.destroy();\r
8628\r
8629 return e;\r
8630 },\r
8631\r
8632 execCommand : function(c, u, v) {\r
8633 var t = this, ed = t.get(v), w;\r
8634\r
8635 // Manager commands\r
8636 switch (c) {\r
8637 case "mceFocus":\r
8638 ed.focus();\r
8639 return true;\r
8640\r
8641 case "mceAddEditor":\r
8642 case "mceAddControl":\r
8643 if (!t.get(v))\r
8644 new tinymce.Editor(v, t.settings).render();\r
8645\r
8646 return true;\r
8647\r
8648 case "mceAddFrameControl":\r
8649 w = v.window;\r
8650\r
8651 // Add tinyMCE global instance and tinymce namespace to specified window\r
8652 w.tinyMCE = tinyMCE;\r
8653 w.tinymce = tinymce;\r
8654\r
8655 tinymce.DOM.doc = w.document;\r
8656 tinymce.DOM.win = w;\r
8657\r
8658 ed = new tinymce.Editor(v.element_id, v);\r
8659 ed.render();\r
8660\r
8661 // Fix IE memory leaks\r
8662 if (tinymce.isIE) {\r
8663 function clr() {\r
8664 ed.destroy();\r
8665 w.detachEvent('onunload', clr);\r
8666 w = w.tinyMCE = w.tinymce = null; // IE leak\r
8667 };\r
8668\r
8669 w.attachEvent('onunload', clr);\r
8670 }\r
8671\r
8672 v.page_window = null;\r
8673\r
8674 return true;\r
8675\r
8676 case "mceRemoveEditor":\r
8677 case "mceRemoveControl":\r
8678 if (ed)\r
8679 ed.remove();\r
8680\r
8681 return true;\r
8682\r
8683 case 'mceToggleEditor':\r
8684 if (!ed) {\r
8685 t.execCommand('mceAddControl', 0, v);\r
8686 return true;\r
8687 }\r
8688\r
8689 if (ed.isHidden())\r
8690 ed.show();\r
8691 else\r
8692 ed.hide();\r
8693\r
8694 return true;\r
8695 }\r
8696\r
8697 // Run command on active editor\r
8698 if (t.activeEditor)\r
8699 return t.activeEditor.execCommand(c, u, v);\r
8700\r
8701 return false;\r
8702 },\r
8703\r
8704 execInstanceCommand : function(id, c, u, v) {\r
8705 var ed = this.get(id);\r
8706\r
8707 if (ed)\r
8708 return ed.execCommand(c, u, v);\r
8709\r
8710 return false;\r
8711 },\r
8712\r
8713 triggerSave : function() {\r
8714 each(this.editors, function(e) {\r
8715 e.save();\r
8716 });\r
8717 },\r
8718\r
8719 addI18n : function(p, o) {\r
8720 var lo, i18n = this.i18n;\r
8721\r
8722 if (!tinymce.is(p, 'string')) {\r
8723 each(p, function(o, lc) {\r
8724 each(o, function(o, g) {\r
8725 each(o, function(o, k) {\r
8726 if (g === 'common')\r
8727 i18n[lc + '.' + k] = o;\r
8728 else\r
8729 i18n[lc + '.' + g + '.' + k] = o;\r
8730 });\r
8731 });\r
8732 });\r
8733 } else {\r
8734 each(o, function(o, k) {\r
8735 i18n[p + '.' + k] = o;\r
8736 });\r
8737 }\r
8738 },\r
8739\r
8740 // Private methods\r
8741\r
8742 _setActive : function(e) {\r
8743 this.selectedInstance = this.activeEditor = e;\r
8744 }\r
8745 });\r
8746\r
8747 tinymce.EditorManager.preInit();\r
8748})(tinymce);\r
8749\r
8750var tinyMCE = window.tinyMCE = tinymce.EditorManager;\r
8751\r
8752(function(tinymce) {\r
8753 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;\r
8754 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;\r
8755 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;\r
8756 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
8757\r
8758 tinymce.create('tinymce.Editor', {\r
8759 Editor : function(id, s) {\r
8760 var t = this;\r
8761\r
8762 t.id = t.editorId = id;\r
8763\r
8764 t.execCommands = {};\r
8765 t.queryStateCommands = {};\r
8766 t.queryValueCommands = {};\r
8767\r
8768 t.isNotDirty = false;\r
8769\r
8770 t.plugins = {};\r
8771\r
8772 // Add events to the editor\r
8773 each([\r
8774 'onPreInit',\r
8775\r
8776 'onBeforeRenderUI',\r
8777\r
8778 'onPostRender',\r
8779\r
8780 'onInit',\r
8781\r
8782 'onRemove',\r
8783\r
8784 'onActivate',\r
8785\r
8786 'onDeactivate',\r
8787\r
8788 'onClick',\r
8789\r
8790 'onEvent',\r
8791\r
8792 'onMouseUp',\r
8793\r
8794 'onMouseDown',\r
8795\r
8796 'onDblClick',\r
8797\r
8798 'onKeyDown',\r
8799\r
8800 'onKeyUp',\r
8801\r
8802 'onKeyPress',\r
8803\r
8804 'onContextMenu',\r
8805\r
8806 'onSubmit',\r
8807\r
8808 'onReset',\r
8809\r
8810 'onPaste',\r
8811\r
8812 'onPreProcess',\r
8813\r
8814 'onPostProcess',\r
8815\r
8816 'onBeforeSetContent',\r
8817\r
8818 'onBeforeGetContent',\r
8819\r
8820 'onSetContent',\r
8821\r
8822 'onGetContent',\r
8823\r
8824 'onLoadContent',\r
8825\r
8826 'onSaveContent',\r
8827\r
8828 'onNodeChange',\r
8829\r
8830 'onChange',\r
8831\r
8832 'onBeforeExecCommand',\r
8833\r
8834 'onExecCommand',\r
8835\r
8836 'onUndo',\r
8837\r
8838 'onRedo',\r
8839\r
8840 'onVisualAid',\r
8841\r
8842 'onSetProgressState'\r
8843 ], function(e) {\r
8844 t[e] = new Dispatcher(t);\r
8845 });\r
8846\r
8847 t.settings = s = extend({\r
8848 id : id,\r
8849 language : 'en',\r
8850 docs_language : 'en',\r
8851 theme : 'simple',\r
8852 skin : 'default',\r
8853 delta_width : 0,\r
8854 delta_height : 0,\r
8855 popup_css : '',\r
8856 plugins : '',\r
8857 document_base_url : tinymce.documentBaseURL,\r
8858 add_form_submit_trigger : 1,\r
8859 submit_patch : 1,\r
8860 add_unload_trigger : 1,\r
8861 convert_urls : 1,\r
8862 relative_urls : 1,\r
8863 remove_script_host : 1,\r
8864 table_inline_editing : 0,\r
8865 object_resizing : 1,\r
8866 cleanup : 1,\r
8867 accessibility_focus : 1,\r
8868 custom_shortcuts : 1,\r
8869 custom_undo_redo_keyboard_shortcuts : 1,\r
8870 custom_undo_redo_restore_selection : 1,\r
8871 custom_undo_redo : 1,\r
8872 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',\r
8873 visual_table_class : 'mceItemTable',\r
8874 visual : 1,\r
8875 inline_styles : true,\r
8876 convert_fonts_to_spans : true,\r
8877 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
8878 apply_source_formatting : 1,\r
8879 directionality : 'ltr',\r
8880 forced_root_block : 'p',\r
8881 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',\r
8882 hidden_input : 1,\r
8883 padd_empty_editor : 1,\r
8884 render_ui : 1,\r
8885 init_theme : 1,\r
8886 force_p_newlines : 1,\r
8887 indentation : '30px',\r
8888 keep_styles : 1,\r
8889 fix_table_elements : 1,\r
8890 removeformat_selector : 'span,b,strong,em,i,font,u,strike'\r
8891 }, s);\r
8892\r
8893 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
8894 base_uri : tinyMCE.baseURI\r
8895 });\r
8896\r
8897 t.baseURI = EditorManager.baseURI;\r
8898\r
8899 // Call setup\r
8900 t.execCallback('setup', t);\r
8901 },\r
8902\r
8903 render : function(nst) {\r
8904 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
8905\r
8906 // Page is not loaded yet, wait for it\r
8907 if (!Event.domLoaded) {\r
8908 Event.add(document, 'init', function() {\r
8909 t.render();\r
8910 });\r
8911 return;\r
8912 }\r
8913\r
8914 // Force strict loading mode if render us called by user and not internally\r
8915 if (!nst) {\r
8916 s.strict_loading_mode = 1;\r
8917 tinyMCE.settings = s;\r
8918 }\r
8919\r
8920 // Element not found, then skip initialization\r
8921 if (!t.getElement())\r
8922 return;\r
8923\r
8924 if (s.strict_loading_mode) {\r
8925 sl.settings.strict_mode = s.strict_loading_mode;\r
8926 tinymce.DOM.settings.strict = 1;\r
8927 }\r
8928\r
8929 // Add hidden input for non input elements inside form elements\r
8930 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
8931 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
8932\r
8933 if (tinymce.WindowManager)\r
8934 t.windowManager = new tinymce.WindowManager(t);\r
8935\r
8936 if (s.encoding == 'xml') {\r
8937 t.onGetContent.add(function(ed, o) {\r
8938 if (o.save)\r
8939 o.content = DOM.encode(o.content);\r
8940 });\r
8941 }\r
8942\r
8943 if (s.add_form_submit_trigger) {\r
8944 t.onSubmit.addToTop(function() {\r
8945 if (t.initialized) {\r
8946 t.save();\r
8947 t.isNotDirty = 1;\r
8948 }\r
8949 });\r
8950 }\r
8951\r
8952 if (s.add_unload_trigger) {\r
8953 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
8954 if (t.initialized && !t.destroyed && !t.isHidden())\r
8955 t.save({format : 'raw', no_events : true});\r
8956 });\r
8957 }\r
8958\r
8959 tinymce.addUnload(t.destroy, t);\r
8960\r
8961 if (s.submit_patch) {\r
8962 t.onBeforeRenderUI.add(function() {\r
8963 var n = t.getElement().form;\r
8964\r
8965 if (!n)\r
8966 return;\r
8967\r
8968 // Already patched\r
8969 if (n._mceOldSubmit)\r
8970 return;\r
8971\r
8972 // Check page uses id="submit" or name="submit" for it's submit button\r
8973 if (!n.submit.nodeType && !n.submit.length) {\r
8974 t.formElement = n;\r
8975 n._mceOldSubmit = n.submit;\r
8976 n.submit = function() {\r
8977 // Save all instances\r
8978 EditorManager.triggerSave();\r
8979 t.isNotDirty = 1;\r
8980\r
8981 return t.formElement._mceOldSubmit(t.formElement);\r
8982 };\r
8983 }\r
8984\r
8985 n = null;\r
8986 });\r
8987 }\r
8988\r
8989 // Load scripts\r
8990 function loadScripts() {\r
8991 if (s.language)\r
8992 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
8993\r
8994 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
8995 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
8996\r
8997 each(explode(s.plugins), function(p) {\r
8998 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
8999 // Skip safari plugin for other browsers\r
9000 if (!isWebKit && p == 'safari')\r
9001 return;\r
9002\r
9003 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
9004 }\r
9005 });\r
9006\r
9007 // Init when que is loaded\r
9008 sl.loadQueue(function() {\r
9009 if (!t.removed)\r
9010 t.init();\r
9011 });\r
9012 };\r
9013\r
9014 loadScripts();\r
9015 },\r
9016\r
9017 init : function() {\r
9018 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
9019\r
9020 EditorManager.add(t);\r
9021\r
9022 if (s.theme) {\r
9023 s.theme = s.theme.replace(/-/, '');\r
9024 o = ThemeManager.get(s.theme);\r
9025 t.theme = new o();\r
9026\r
9027 if (t.theme.init && s.init_theme)\r
9028 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
9029 }\r
9030\r
9031 // Create all plugins\r
9032 each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
9033 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
9034\r
9035 if (c) {\r
9036 po = new c(t, u);\r
9037\r
9038 t.plugins[p] = po;\r
9039\r
9040 if (po.init)\r
9041 po.init(t, u);\r
9042 }\r
9043 });\r
9044\r
9045 // Setup popup CSS path(s)\r
9046 if (s.popup_css !== false) {\r
9047 if (s.popup_css)\r
9048 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
9049 else\r
9050 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
9051 }\r
9052\r
9053 if (s.popup_css_add)\r
9054 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
9055\r
9056 t.controlManager = new tinymce.ControlManager(t);\r
9057\r
9058 t.undoManager = new tinymce.UndoManager(t);\r
9059\r
9060 // Pass through\r
9061 t.undoManager.onAdd.add(function(um, l) {\r
9062 if (!l.initial)\r
9063 return t.onChange.dispatch(t, l, um);\r
9064 });\r
9065\r
9066 t.undoManager.onUndo.add(function(um, l) {\r
9067 return t.onUndo.dispatch(t, l, um);\r
9068 });\r
9069\r
9070 t.undoManager.onRedo.add(function(um, l) {\r
9071 return t.onRedo.dispatch(t, l, um);\r
9072 });\r
9073\r
9074 if (s.custom_undo_redo) {\r
9075 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
9076 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
9077 t.undoManager.add();\r
9078 });\r
9079 }\r
9080\r
9081 t.onExecCommand.add(function(ed, c) {\r
9082 // Don't refresh the select lists until caret move\r
9083 if (!/^(FontName|FontSize)$/.test(c))\r
9084 t.nodeChanged();\r
9085 });\r
9086\r
9087 // Remove ghost selections on images and tables in Gecko\r
9088 if (isGecko) {\r
9089 function repaint(a, o) {\r
9090 if (!o || !o.initial)\r
9091 t.execCommand('mceRepaint');\r
9092 };\r
9093\r
9094 t.onUndo.add(repaint);\r
9095 t.onRedo.add(repaint);\r
9096 t.onSetContent.add(repaint);\r
9097 }\r
9098\r
9099 // Enables users to override the control factory\r
9100 t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
9101\r
9102 // Measure box\r
9103 if (s.render_ui) {\r
9104 w = s.width || e.style.width || e.offsetWidth;\r
9105 h = s.height || e.style.height || e.offsetHeight;\r
9106 t.orgDisplay = e.style.display;\r
9107 re = /^[0-9\.]+(|px)$/i;\r
9108\r
9109 if (re.test('' + w))\r
9110 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
9111\r
9112 if (re.test('' + h))\r
9113 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
9114\r
9115 // Render UI\r
9116 o = t.theme.renderUI({\r
9117 targetNode : e,\r
9118 width : w,\r
9119 height : h,\r
9120 deltaWidth : s.delta_width,\r
9121 deltaHeight : s.delta_height\r
9122 });\r
9123\r
9124 t.editorContainer = o.editorContainer;\r
9125 }\r
9126\r
9127 // User specified a document.domain value\r
9128 if (document.domain && location.hostname != document.domain)\r
9129 tinymce.relaxedDomain = document.domain;\r
9130\r
9131 // Resize editor\r
9132 if (s.theme_advanced_toolbar_location != 'external') {\r
9133 DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
9134 width : w,\r
9135 height : h\r
9136 });\r
9137 }\r
9138\r
9139 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
9140 if (h < 100)\r
9141 h = 100;\r
9142\r
9143 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
9144\r
9145 // We only need to override paths if we have to\r
9146 // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
9147 //if (s.document_base_url != tinymce.documentBaseURL)\r
9148 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
9149\r
9150 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
9151\r
9152 if (tinymce.relaxedDomain)\r
9153 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
9154\r
9155 bi = s.body_id || 'tinymce';\r
9156 if (bi.indexOf('=') != -1) {\r
9157 bi = t.getParam('body_id', '', 'hash');\r
9158 bi = bi[t.id] || bi;\r
9159 }\r
9160\r
9161 bc = s.body_class || '';\r
9162 if (bc.indexOf('=') != -1) {\r
9163 bc = t.getParam('body_class', '', 'hash');\r
9164 bc = bc[t.id] || '';\r
9165 }\r
9166\r
9167 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
9168\r
9169 // Domain relaxing enabled, then set document domain\r
9170 if (tinymce.relaxedDomain) {\r
9171 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
9172 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
9173 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';\r
9174 else if (tinymce.isOpera)\r
9175 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()'; \r
9176 }\r
9177\r
9178 // Create iframe\r
9179 n = DOM.add(o.iframeContainer, 'iframe', {\r
9180 id : t.id + "_ifr",\r
9181 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
9182 frameBorder : '0',\r
9183 style : {\r
9184 width : '100%',\r
9185 height : h\r
9186 }\r
9187 });\r
9188\r
9189 t.contentAreaContainer = o.iframeContainer;\r
9190 DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
9191 DOM.get(t.id).style.display = 'none';\r
9192\r
9193 if (!isIE || !tinymce.relaxedDomain)\r
9194 t.setupIframe();\r
9195\r
9196 e = n = o = null; // Cleanup\r
9197 },\r
9198\r
9199 setupIframe : function() {\r
9200 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
9201\r
9202 // Setup iframe body\r
9203 if (!isIE || !tinymce.relaxedDomain) {\r
9204 d.open();\r
9205 d.write(t.iframeHTML);\r
9206 d.close();\r
9207 }\r
9208\r
9209 // Design mode needs to be added here Ctrl+A will fail otherwise\r
9210 if (!isIE) {\r
9211 try {\r
9212 if (!s.readonly)\r
9213 d.designMode = 'On';\r
9214 } catch (ex) {\r
9215 // Will fail on Gecko if the editor is placed in an hidden container element\r
9216 // The design mode will be set ones the editor is focused\r
9217 }\r
9218 }\r
9219\r
9220 // IE needs to use contentEditable or it will display non secure items for HTTPS\r
9221 if (isIE) {\r
9222 // It will not steal focus if we hide it while setting contentEditable\r
9223 b = t.getBody();\r
9224 DOM.hide(b);\r
9225\r
9226 if (!s.readonly)\r
9227 b.contentEditable = true;\r
9228\r
9229 DOM.show(b);\r
9230 }\r
9231\r
9232 t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
9233 keep_values : true,\r
9234 url_converter : t.convertURL,\r
9235 url_converter_scope : t,\r
9236 hex_colors : s.force_hex_style_colors,\r
9237 class_filter : s.class_filter,\r
9238 update_styles : 1,\r
9239 fix_ie_paragraphs : 1\r
9240 });\r
9241\r
9242 t.serializer = new tinymce.dom.Serializer(extend(s, {\r
9243 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
9244 dom : t.dom\r
9245 }));\r
9246\r
9247 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
9248\r
9249 t.forceBlocks = new tinymce.ForceBlocks(t, {\r
9250 forced_root_block : s.forced_root_block\r
9251 });\r
9252 t.editorCommands = new tinymce.EditorCommands(t);\r
9253\r
9254 // Pass through\r
9255 t.serializer.onPreProcess.add(function(se, o) {\r
9256 return t.onPreProcess.dispatch(t, o, se);\r
9257 });\r
9258\r
9259 t.serializer.onPostProcess.add(function(se, o) {\r
9260 return t.onPostProcess.dispatch(t, o, se);\r
9261 });\r
9262\r
9263 t.onPreInit.dispatch(t);\r
9264\r
9265 if (!s.gecko_spellcheck)\r
9266 t.getBody().spellcheck = 0;\r
9267\r
9268 if (!s.readonly)\r
9269 t._addEvents();\r
9270\r
9271 t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
9272 t.onPostRender.dispatch(t);\r
9273\r
9274 if (s.directionality)\r
9275 t.getBody().dir = s.directionality;\r
9276\r
9277 if (s.nowrap)\r
9278 t.getBody().style.whiteSpace = "nowrap";\r
9279\r
9280 if (s.custom_elements) {\r
9281 function handleCustom(ed, o) {\r
9282 each(explode(s.custom_elements), function(v) {\r
9283 var n;\r
9284\r
9285 if (v.indexOf('~') === 0) {\r
9286 v = v.substring(1);\r
9287 n = 'span';\r
9288 } else\r
9289 n = 'div';\r
9290\r
9291 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');\r
9292 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
9293 });\r
9294 };\r
9295\r
9296 t.onBeforeSetContent.add(handleCustom);\r
9297 t.onPostProcess.add(function(ed, o) {\r
9298 if (o.set)\r
9299 handleCustom(ed, o);\r
9300 });\r
9301 }\r
9302\r
9303 if (s.handle_node_change_callback) {\r
9304 t.onNodeChange.add(function(ed, cm, n) {\r
9305 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
9306 });\r
9307 }\r
9308\r
9309 if (s.save_callback) {\r
9310 t.onSaveContent.add(function(ed, o) {\r
9311 var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9312\r
9313 if (h)\r
9314 o.content = h;\r
9315 });\r
9316 }\r
9317\r
9318 if (s.onchange_callback) {\r
9319 t.onChange.add(function(ed, l) {\r
9320 t.execCallback('onchange_callback', t, l);\r
9321 });\r
9322 }\r
9323\r
9324 if (s.convert_newlines_to_brs) {\r
9325 t.onBeforeSetContent.add(function(ed, o) {\r
9326 if (o.initial)\r
9327 o.content = o.content.replace(/\r?\n/g, '<br />');\r
9328 });\r
9329 }\r
9330\r
9331 if (s.fix_nesting && isIE) {\r
9332 t.onBeforeSetContent.add(function(ed, o) {\r
9333 o.content = t._fixNesting(o.content);\r
9334 });\r
9335 }\r
9336\r
9337 if (s.preformatted) {\r
9338 t.onPostProcess.add(function(ed, o) {\r
9339 o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
9340 o.content = o.content.replace(/<\/pre>\s*$/, '');\r
9341\r
9342 if (o.set)\r
9343 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
9344 });\r
9345 }\r
9346\r
9347 if (s.verify_css_classes) {\r
9348 t.serializer.attribValueFilter = function(n, v) {\r
9349 var s, cl;\r
9350\r
9351 if (n == 'class') {\r
9352 // Build regexp for classes\r
9353 if (!t.classesRE) {\r
9354 cl = t.dom.getClasses();\r
9355\r
9356 if (cl.length > 0) {\r
9357 s = '';\r
9358\r
9359 each (cl, function(o) {\r
9360 s += (s ? '|' : '') + o['class'];\r
9361 });\r
9362\r
9363 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
9364 }\r
9365 }\r
9366\r
9367 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
9368 }\r
9369\r
9370 return v;\r
9371 };\r
9372 }\r
9373\r
9374 if (s.convert_fonts_to_spans)\r
9375 t._convertFonts();\r
9376\r
9377 if (s.inline_styles)\r
9378 t._convertInlineElements();\r
9379\r
9380 if (s.cleanup_callback) {\r
9381 t.onBeforeSetContent.add(function(ed, o) {\r
9382 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9383 });\r
9384\r
9385 t.onPreProcess.add(function(ed, o) {\r
9386 if (o.set)\r
9387 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
9388\r
9389 if (o.get)\r
9390 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
9391 });\r
9392\r
9393 t.onPostProcess.add(function(ed, o) {\r
9394 if (o.set)\r
9395 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9396\r
9397 if (o.get) \r
9398 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
9399 });\r
9400 }\r
9401\r
9402 if (s.save_callback) {\r
9403 t.onGetContent.add(function(ed, o) {\r
9404 if (o.save)\r
9405 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9406 });\r
9407 }\r
9408\r
9409 if (s.handle_event_callback) {\r
9410 t.onEvent.add(function(ed, e, o) {\r
9411 if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9412 Event.cancel(e);\r
9413 });\r
9414 }\r
9415\r
9416 // Add visual aids when new contents is added\r
9417 t.onSetContent.add(function() {\r
9418 t.addVisual(t.getBody());\r
9419 });\r
9420\r
9421 // Remove empty contents\r
9422 if (s.padd_empty_editor) {\r
9423 t.onPostProcess.add(function(ed, o) {\r
9424 o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9425 });\r
9426 }\r
9427\r
9428 if (isGecko) {\r
9429 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9430 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9431 function fixLinks(ed, o) {\r
9432 each(ed.dom.select('a'), function(n) {\r
9433 var pn = n.parentNode;\r
9434\r
9435 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9436 ed.dom.add(pn, 'br', {'mce_bogus' : 1});\r
9437 });\r
9438 };\r
9439\r
9440 t.onExecCommand.add(function(ed, cmd) {\r
9441 if (cmd === 'CreateLink')\r
9442 fixLinks(ed);\r
9443 });\r
9444\r
9445 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9446\r
9447 if (!s.readonly) {\r
9448 try {\r
9449 // Design mode must be set here once again to fix a bug where\r
9450 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9451 d.designMode = 'Off';\r
9452 d.designMode = 'On';\r
9453 } catch (ex) {\r
9454 // Will fail on Gecko if the editor is placed in an hidden container element\r
9455 // The design mode will be set ones the editor is focused\r
9456 }\r
9457 }\r
9458 }\r
9459\r
9460 // A small timeout was needed since firefox will remove. Bug: #1838304\r
9461 setTimeout(function () {\r
9462 if (t.removed)\r
9463 return;\r
9464\r
9465 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9466 t.startContent = t.getContent({format : 'raw'});\r
9467 t.undoManager.add({initial : true});\r
9468 t.initialized = true;\r
9469\r
9470 t.onInit.dispatch(t);\r
9471 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9472 t.execCallback('init_instance_callback', t);\r
9473 t.focus(true);\r
9474 t.nodeChanged({initial : 1});\r
9475\r
9476 // Load specified content CSS last\r
9477 if (s.content_css) {\r
9478 tinymce.each(explode(s.content_css), function(u) {\r
9479 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9480 });\r
9481 }\r
9482\r
9483 // Handle auto focus\r
9484 if (s.auto_focus) {\r
9485 setTimeout(function () {\r
9486 var ed = EditorManager.get(s.auto_focus);\r
9487\r
9488 ed.selection.select(ed.getBody(), 1);\r
9489 ed.selection.collapse(1);\r
9490 ed.getWin().focus();\r
9491 }, 100);\r
9492 }\r
9493 }, 1);\r
9494\r
9495 // Changed behavior of the blur and focus event so it matches the api docs :XXX doesn't fully work yet\r
9496 Event.add(!isGecko ? t.getWin() : t.getDoc(), 'blur', function(e) {\r
9497 t.onDeactivate.dispatch(t, e);\r
9498 });\r
9499 Event.add(!isGecko ? t.getWin() : t.getDoc(), 'focus', function(e) {\r
9500 t.onActivate.dispatch(t, e);\r
9501 });\r
9502\r
9503 e = null;\r
9504 },\r
9505\r
9506 focus : function(sf) {\r
9507 var oed, t = this, ce = t.settings.content_editable;\r
9508\r
9509 if (!sf) {\r
9510 // Is not content editable or the selection is outside the area in IE\r
9511 // the IE statement is needed to avoid bluring if element selections inside layers since\r
9512 // the layer is like it's own document in IE\r
9513 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))\r
9514 t.getWin().focus();\r
9515\r
9516 }\r
9517\r
9518 // Changed behavior of the blur and focus event so it matches the api docs\r
9519 /*\r
9520 if (EditorManager.activeEditor != t) {\r
9521 if ((oed = EditorManager.activeEditor) != null)\r
9522 oed.onDeactivate.dispatch(oed, t);\r
9523\r
9524 t.onActivate.dispatch(t, oed);\r
9525 }\r
9526 */\r
9527\r
9528 EditorManager._setActive(t);\r
9529 },\r
9530\r
9531 execCallback : function(n) {\r
9532 var t = this, f = t.settings[n], s;\r
9533\r
9534 if (!f)\r
9535 return;\r
9536\r
9537 // Look through lookup\r
9538 if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
9539 f = s.func;\r
9540 s = s.scope;\r
9541 }\r
9542\r
9543 if (is(f, 'string')) {\r
9544 s = f.replace(/\.\w+$/, '');\r
9545 s = s ? tinymce.resolve(s) : 0;\r
9546 f = tinymce.resolve(f);\r
9547 t.callbackLookup = t.callbackLookup || {};\r
9548 t.callbackLookup[n] = {func : f, scope : s};\r
9549 }\r
9550\r
9551 return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
9552 },\r
9553\r
9554 translate : function(s) {\r
9555 var c = this.settings.language || 'en', i18n = EditorManager.i18n;\r
9556\r
9557 if (!s)\r
9558 return '';\r
9559\r
9560 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
9561 return i18n[c + '.' + b] || '{#' + b + '}';\r
9562 });\r
9563 },\r
9564\r
9565 getLang : function(n, dv) {\r
9566 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
9567 },\r
9568\r
9569 getParam : function(n, dv, ty) {\r
9570 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
9571\r
9572 if (ty === 'hash') {\r
9573 o = {};\r
9574\r
9575 if (is(v, 'string')) {\r
9576 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
9577 v = v.split('=');\r
9578\r
9579 if (v.length > 1)\r
9580 o[tr(v[0])] = tr(v[1]);\r
9581 else\r
9582 o[tr(v[0])] = tr(v);\r
9583 });\r
9584 } else\r
9585 o = v;\r
9586\r
9587 return o;\r
9588 }\r
9589\r
9590 return v;\r
9591 },\r
9592\r
9593 nodeChanged : function(o) {\r
9594 var t = this, s = t.selection, n = s.getNode() || t.getBody();\r
9595\r
9596 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
9597 if (t.initialized) {\r
9598 t.onNodeChange.dispatch(\r
9599 t,\r
9600 o ? o.controlManager || t.controlManager : t.controlManager,\r
9601 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state\r
9602 s.isCollapsed(),\r
9603 o\r
9604 );\r
9605 }\r
9606 },\r
9607\r
9608 addButton : function(n, s) {\r
9609 var t = this;\r
9610\r
9611 t.buttons = t.buttons || {};\r
9612 t.buttons[n] = s;\r
9613 },\r
9614\r
9615 addCommand : function(n, f, s) {\r
9616 this.execCommands[n] = {func : f, scope : s || this};\r
9617 },\r
9618\r
9619 addQueryStateHandler : function(n, f, s) {\r
9620 this.queryStateCommands[n] = {func : f, scope : s || this};\r
9621 },\r
9622\r
9623 addQueryValueHandler : function(n, f, s) {\r
9624 this.queryValueCommands[n] = {func : f, scope : s || this};\r
9625 },\r
9626\r
9627 addShortcut : function(pa, desc, cmd_func, sc) {\r
9628 var t = this, c;\r
9629\r
9630 if (!t.settings.custom_shortcuts)\r
9631 return false;\r
9632\r
9633 t.shortcuts = t.shortcuts || {};\r
9634\r
9635 if (is(cmd_func, 'string')) {\r
9636 c = cmd_func;\r
9637\r
9638 cmd_func = function() {\r
9639 t.execCommand(c, false, null);\r
9640 };\r
9641 }\r
9642\r
9643 if (is(cmd_func, 'object')) {\r
9644 c = cmd_func;\r
9645\r
9646 cmd_func = function() {\r
9647 t.execCommand(c[0], c[1], c[2]);\r
9648 };\r
9649 }\r
9650\r
9651 each(explode(pa), function(pa) {\r
9652 var o = {\r
9653 func : cmd_func,\r
9654 scope : sc || this,\r
9655 desc : desc,\r
9656 alt : false,\r
9657 ctrl : false,\r
9658 shift : false\r
9659 };\r
9660\r
9661 each(explode(pa, '+'), function(v) {\r
9662 switch (v) {\r
9663 case 'alt':\r
9664 case 'ctrl':\r
9665 case 'shift':\r
9666 o[v] = true;\r
9667 break;\r
9668\r
9669 default:\r
9670 o.charCode = v.charCodeAt(0);\r
9671 o.keyCode = v.toUpperCase().charCodeAt(0);\r
9672 }\r
9673 });\r
9674\r
9675 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
9676 });\r
9677\r
9678 return true;\r
9679 },\r
9680\r
9681 execCommand : function(cmd, ui, val, a) {\r
9682 var t = this, s = 0, o, st;\r
9683\r
9684 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
9685 t.focus();\r
9686\r
9687 o = {};\r
9688 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
9689 if (o.terminate)\r
9690 return false;\r
9691\r
9692 // Command callback\r
9693 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
9694 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9695 return true;\r
9696 }\r
9697\r
9698 // Registred commands\r
9699 if (o = t.execCommands[cmd]) {\r
9700 st = o.func.call(o.scope, ui, val);\r
9701\r
9702 // Fall through on true\r
9703 if (st !== true) {\r
9704 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9705 return st;\r
9706 }\r
9707 }\r
9708\r
9709 // Plugin commands\r
9710 each(t.plugins, function(p) {\r
9711 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
9712 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9713 s = 1;\r
9714 return false;\r
9715 }\r
9716 });\r
9717\r
9718 if (s)\r
9719 return true;\r
9720\r
9721 // Theme commands\r
9722 if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
9723 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9724 return true;\r
9725 }\r
9726\r
9727 // Execute global commands\r
9728 if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
9729 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9730 return true;\r
9731 }\r
9732\r
9733 // Editor commands\r
9734 if (t.editorCommands.execCommand(cmd, ui, val)) {\r
9735 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9736 return true;\r
9737 }\r
9738\r
9739 // Browser commands\r
9740 t.getDoc().execCommand(cmd, ui, val);\r
9741 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
9742 },\r
9743\r
9744 queryCommandState : function(c) {\r
9745 var t = this, o, s;\r
9746\r
9747 // Is hidden then return undefined\r
9748 if (t._isHidden())\r
9749 return;\r
9750\r
9751 // Registred commands\r
9752 if (o = t.queryStateCommands[c]) {\r
9753 s = o.func.call(o.scope);\r
9754\r
9755 // Fall though on true\r
9756 if (s !== true)\r
9757 return s;\r
9758 }\r
9759\r
9760 // Registred commands\r
9761 o = t.editorCommands.queryCommandState(c);\r
9762 if (o !== -1)\r
9763 return o;\r
9764\r
9765 // Browser commands\r
9766 try {\r
9767 return this.getDoc().queryCommandState(c);\r
9768 } catch (ex) {\r
9769 // Fails sometimes see bug: 1896577\r
9770 }\r
9771 },\r
9772\r
9773 queryCommandValue : function(c) {\r
9774 var t = this, o, s;\r
9775\r
9776 // Is hidden then return undefined\r
9777 if (t._isHidden())\r
9778 return;\r
9779\r
9780 // Registred commands\r
9781 if (o = t.queryValueCommands[c]) {\r
9782 s = o.func.call(o.scope);\r
9783\r
9784 // Fall though on true\r
9785 if (s !== true)\r
9786 return s;\r
9787 }\r
9788\r
9789 // Registred commands\r
9790 o = t.editorCommands.queryCommandValue(c);\r
9791 if (is(o))\r
9792 return o;\r
9793\r
9794 // Browser commands\r
9795 try {\r
9796 return this.getDoc().queryCommandValue(c);\r
9797 } catch (ex) {\r
9798 // Fails sometimes see bug: 1896577\r
9799 }\r
9800 },\r
9801\r
9802 show : function() {\r
9803 var t = this;\r
9804\r
9805 DOM.show(t.getContainer());\r
9806 DOM.hide(t.id);\r
9807 t.load();\r
9808 },\r
9809\r
9810 hide : function() {\r
9811 var t = this, d = t.getDoc();\r
9812\r
9813 // Fixed bug where IE has a blinking cursor left from the editor\r
9814 if (isIE && d)\r
9815 d.execCommand('SelectAll');\r
9816\r
9817 // We must save before we hide so Safari doesn't crash\r
9818 t.save();\r
9819 DOM.hide(t.getContainer());\r
9820 DOM.setStyle(t.id, 'display', t.orgDisplay);\r
9821 },\r
9822\r
9823 isHidden : function() {\r
9824 return !DOM.isHidden(this.id);\r
9825 },\r
9826\r
9827 setProgressState : function(b, ti, o) {\r
9828 this.onSetProgressState.dispatch(this, b, ti, o);\r
9829\r
9830 return b;\r
9831 },\r
9832\r
9833 load : function(o) {\r
9834 var t = this, e = t.getElement(), h;\r
9835\r
9836 if (e) {\r
9837 o = o || {};\r
9838 o.load = true;\r
9839\r
9840 // Double encode existing entities in the value\r
9841 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
9842 o.element = e;\r
9843\r
9844 if (!o.no_events)\r
9845 t.onLoadContent.dispatch(t, o);\r
9846\r
9847 o.element = e = null;\r
9848\r
9849 return h;\r
9850 }\r
9851 },\r
9852\r
9853 save : function(o) {\r
9854 var t = this, e = t.getElement(), h, f;\r
9855\r
9856 if (!e || !t.initialized)\r
9857 return;\r
9858\r
9859 o = o || {};\r
9860 o.save = true;\r
9861\r
9862 // Add undo level will trigger onchange event\r
9863 if (!o.no_events) {\r
9864 t.undoManager.typing = 0;\r
9865 t.undoManager.add();\r
9866 }\r
9867\r
9868 o.element = e;\r
9869 h = o.content = t.getContent(o);\r
9870\r
9871 if (!o.no_events)\r
9872 t.onSaveContent.dispatch(t, o);\r
9873\r
9874 h = o.content;\r
9875\r
9876 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
9877 e.innerHTML = h;\r
9878\r
9879 // Update hidden form element\r
9880 if (f = DOM.getParent(t.id, 'form')) {\r
9881 each(f.elements, function(e) {\r
9882 if (e.name == t.id) {\r
9883 e.value = h;\r
9884 return false;\r
9885 }\r
9886 });\r
9887 }\r
9888 } else\r
9889 e.value = h;\r
9890\r
9891 o.element = e = null;\r
9892\r
9893 return h;\r
9894 },\r
9895\r
9896 setContent : function(h, o) {\r
9897 var t = this;\r
9898\r
9899 o = o || {};\r
9900 o.format = o.format || 'html';\r
9901 o.set = true;\r
9902 o.content = h;\r
9903\r
9904 if (!o.no_events)\r
9905 t.onBeforeSetContent.dispatch(t, o);\r
9906\r
9907 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
9908 // It will also be impossible to place the caret in the editor unless there is a BR element present\r
9909 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
9910 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');\r
9911 o.format = 'raw';\r
9912 }\r
9913\r
9914 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
9915\r
9916 if (o.format != 'raw' && t.settings.cleanup) {\r
9917 o.getInner = true;\r
9918 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
9919 }\r
9920\r
9921 if (!o.no_events)\r
9922 t.onSetContent.dispatch(t, o);\r
9923\r
9924 return o.content;\r
9925 },\r
9926\r
9927 getContent : function(o) {\r
9928 var t = this, h;\r
9929\r
9930 o = o || {};\r
9931 o.format = o.format || 'html';\r
9932 o.get = true;\r
9933\r
9934 if (!o.no_events)\r
9935 t.onBeforeGetContent.dispatch(t, o);\r
9936\r
9937 if (o.format != 'raw' && t.settings.cleanup) {\r
9938 o.getInner = true;\r
9939 h = t.serializer.serialize(t.getBody(), o);\r
9940 } else\r
9941 h = t.getBody().innerHTML;\r
9942\r
9943 h = h.replace(/^\s*|\s*$/g, '');\r
9944 o.content = h;\r
9945\r
9946 if (!o.no_events)\r
9947 t.onGetContent.dispatch(t, o);\r
9948\r
9949 return o.content;\r
9950 },\r
9951\r
9952 isDirty : function() {\r
9953 var t = this;\r
9954\r
9955 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
9956 },\r
9957\r
9958 getContainer : function() {\r
9959 var t = this;\r
9960\r
9961 if (!t.container)\r
9962 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
9963\r
9964 return t.container;\r
9965 },\r
9966\r
9967 getContentAreaContainer : function() {\r
9968 return this.contentAreaContainer;\r
9969 },\r
9970\r
9971 getElement : function() {\r
9972 return DOM.get(this.settings.content_element || this.id);\r
9973 },\r
9974\r
9975 getWin : function() {\r
9976 var t = this, e;\r
9977\r
9978 if (!t.contentWindow) {\r
9979 e = DOM.get(t.id + "_ifr");\r
9980\r
9981 if (e)\r
9982 t.contentWindow = e.contentWindow;\r
9983 }\r
9984\r
9985 return t.contentWindow;\r
9986 },\r
9987\r
9988 getDoc : function() {\r
9989 var t = this, w;\r
9990\r
9991 if (!t.contentDocument) {\r
9992 w = t.getWin();\r
9993\r
9994 if (w)\r
9995 t.contentDocument = w.document;\r
9996 }\r
9997\r
9998 return t.contentDocument;\r
9999 },\r
10000\r
10001 getBody : function() {\r
10002 return this.bodyElement || this.getDoc().body;\r
10003 },\r
10004\r
10005 convertURL : function(u, n, e) {\r
10006 var t = this, s = t.settings;\r
10007\r
10008 // Use callback instead\r
10009 if (s.urlconverter_callback)\r
10010 return t.execCallback('urlconverter_callback', u, e, true, n);\r
10011\r
10012 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
10013 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
10014 return u;\r
10015\r
10016 // Convert to relative\r
10017 if (s.relative_urls)\r
10018 return t.documentBaseURI.toRelative(u);\r
10019\r
10020 // Convert to absolute\r
10021 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
10022\r
10023 return u;\r
10024 },\r
10025\r
10026 addVisual : function(e) {\r
10027 var t = this, s = t.settings;\r
10028\r
10029 e = e || t.getBody();\r
10030\r
10031 if (!is(t.hasVisual))\r
10032 t.hasVisual = s.visual;\r
10033\r
10034 each(t.dom.select('table,a', e), function(e) {\r
10035 var v;\r
10036\r
10037 switch (e.nodeName) {\r
10038 case 'TABLE':\r
10039 v = t.dom.getAttrib(e, 'border');\r
10040\r
10041 if (!v || v == '0') {\r
10042 if (t.hasVisual)\r
10043 t.dom.addClass(e, s.visual_table_class);\r
10044 else\r
10045 t.dom.removeClass(e, s.visual_table_class);\r
10046 }\r
10047\r
10048 return;\r
10049\r
10050 case 'A':\r
10051 v = t.dom.getAttrib(e, 'name');\r
10052\r
10053 if (v) {\r
10054 if (t.hasVisual)\r
10055 t.dom.addClass(e, 'mceItemAnchor');\r
10056 else\r
10057 t.dom.removeClass(e, 'mceItemAnchor');\r
10058 }\r
10059\r
10060 return;\r
10061 }\r
10062 });\r
10063\r
10064 t.onVisualAid.dispatch(t, e, t.hasVisual);\r
10065 },\r
10066\r
10067 remove : function() {\r
10068 var t = this, e = t.getContainer();\r
10069\r
10070 t.removed = 1; // Cancels post remove event execution\r
10071 t.hide();\r
10072\r
10073 t.execCallback('remove_instance_callback', t);\r
10074 t.onRemove.dispatch(t);\r
10075\r
10076 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
10077 t.onExecCommand.listeners = [];\r
10078\r
10079 EditorManager.remove(t);\r
10080 DOM.remove(e);\r
10081 },\r
10082\r
10083 destroy : function(s) {\r
10084 var t = this;\r
10085\r
10086 // One time is enough\r
10087 if (t.destroyed)\r
10088 return;\r
10089\r
10090 if (!s) {\r
10091 tinymce.removeUnload(t.destroy);\r
10092 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
10093\r
10094 // Manual destroy\r
10095 if (t.theme && t.theme.destroy)\r
10096 t.theme.destroy();\r
10097\r
10098 // Destroy controls, selection and dom\r
10099 t.controlManager.destroy();\r
10100 t.selection.destroy();\r
10101 t.dom.destroy();\r
10102\r
10103 // Remove all events\r
10104\r
10105 // Don't clear the window or document if content editable\r
10106 // is enabled since other instances might still be present\r
10107 if (!t.settings.content_editable) {\r
10108 Event.clear(t.getWin());\r
10109 Event.clear(t.getDoc());\r
10110 }\r
10111\r
10112 Event.clear(t.getBody());\r
10113 Event.clear(t.formElement);\r
10114 }\r
10115\r
10116 if (t.formElement) {\r
10117 t.formElement.submit = t.formElement._mceOldSubmit;\r
10118 t.formElement._mceOldSubmit = null;\r
10119 }\r
10120\r
10121 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
10122\r
10123 if (t.selection)\r
10124 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
10125\r
10126 t.destroyed = 1;\r
10127 },\r
10128\r
10129 // Internal functions\r
10130\r
10131 _addEvents : function() {\r
10132 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
10133 var t = this, i, s = t.settings, lo = {\r
10134 mouseup : 'onMouseUp',\r
10135 mousedown : 'onMouseDown',\r
10136 click : 'onClick',\r
10137 keyup : 'onKeyUp',\r
10138 keydown : 'onKeyDown',\r
10139 keypress : 'onKeyPress',\r
10140 submit : 'onSubmit',\r
10141 reset : 'onReset',\r
10142 contextmenu : 'onContextMenu',\r
10143 dblclick : 'onDblClick',\r
10144 paste : 'onPaste' // Doesn't work in all browsers yet\r
10145 };\r
10146\r
10147 function eventHandler(e, o) {\r
10148 var ty = e.type;\r
10149\r
10150 // Don't fire events when it's removed\r
10151 if (t.removed)\r
10152 return;\r
10153\r
10154 // Generic event handler\r
10155 if (t.onEvent.dispatch(t, e, o) !== false) {\r
10156 // Specific event handler\r
10157 t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
10158 }\r
10159 };\r
10160\r
10161 // Add DOM events\r
10162 each(lo, function(v, k) {\r
10163 switch (k) {\r
10164 case 'contextmenu':\r
10165 if (tinymce.isOpera) {\r
10166 // Fake contextmenu on Opera\r
10167 t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
10168 if (e.ctrlKey) {\r
10169 e.fakeType = 'contextmenu';\r
10170 eventHandler(e);\r
10171 }\r
10172 });\r
10173 } else\r
10174 t.dom.bind(t.getBody(), k, eventHandler);\r
10175 break;\r
10176\r
10177 case 'paste':\r
10178 t.dom.bind(t.getBody(), k, function(e) {\r
10179 eventHandler(e);\r
10180 });\r
10181 break;\r
10182\r
10183 case 'submit':\r
10184 case 'reset':\r
10185 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
10186 break;\r
10187\r
10188 default:\r
10189 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
10190 }\r
10191 });\r
10192\r
10193 t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
10194 t.focus(true);\r
10195 });\r
10196\r
10197 // Fixes bug where a specified document_base_uri could result in broken images\r
10198 // This will also fix drag drop of images in Gecko\r
10199 if (tinymce.isGecko) {\r
10200 // Convert all images to absolute URLs\r
10201/* t.onSetContent.add(function(ed, o) {\r
10202 each(ed.dom.select('img'), function(e) {\r
10203 var v;\r
10204\r
10205 if (v = e.getAttribute('mce_src'))\r
10206 e.src = t.documentBaseURI.toAbsolute(v);\r
10207 })\r
10208 });*/\r
10209\r
10210 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
10211 var v;\r
10212\r
10213 e = e.target;\r
10214\r
10215 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))\r
10216 e.src = t.documentBaseURI.toAbsolute(v);\r
10217 });\r
10218 }\r
10219\r
10220 // Set various midas options in Gecko\r
10221 if (isGecko) {\r
10222 function setOpts() {\r
10223 var t = this, d = t.getDoc(), s = t.settings;\r
10224\r
10225 if (isGecko && !s.readonly) {\r
10226 if (t._isHidden()) {\r
10227 try {\r
10228 if (!s.content_editable)\r
10229 d.designMode = 'On';\r
10230 } catch (ex) {\r
10231 // Fails if it's hidden\r
10232 }\r
10233 }\r
10234\r
10235 try {\r
10236 // Try new Gecko method\r
10237 d.execCommand("styleWithCSS", 0, false);\r
10238 } catch (ex) {\r
10239 // Use old method\r
10240 if (!t._isHidden())\r
10241 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
10242 }\r
10243\r
10244 if (!s.table_inline_editing)\r
10245 try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
10246\r
10247 if (!s.object_resizing)\r
10248 try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
10249 }\r
10250 };\r
10251\r
10252 t.onBeforeExecCommand.add(setOpts);\r
10253 t.onMouseDown.add(setOpts);\r
10254 }\r
10255\r
10256 // Add node change handlers\r
10257 t.onMouseUp.add(t.nodeChanged);\r
10258 t.onClick.add(t.nodeChanged);\r
10259 t.onKeyUp.add(function(ed, e) {\r
10260 var c = e.keyCode;\r
10261\r
10262 if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)\r
10263 t.nodeChanged();\r
10264 });\r
10265\r
10266 // Add reset handler\r
10267 t.onReset.add(function() {\r
10268 t.setContent(t.startContent, {format : 'raw'});\r
10269 });\r
10270\r
10271 // Add shortcuts\r
10272 if (s.custom_shortcuts) {\r
10273 if (s.custom_undo_redo_keyboard_shortcuts) {\r
10274 t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
10275 t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
10276 }\r
10277\r
10278 // Add default shortcuts for gecko\r
10279 if (isGecko) {\r
10280 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
10281 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
10282 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
10283 }\r
10284\r
10285 // BlockFormat shortcuts keys\r
10286 for (i=1; i<=6; i++)\r
10287 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);\r
10288\r
10289 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
10290 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
10291 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
10292\r
10293 function find(e) {\r
10294 var v = null;\r
10295\r
10296 if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
10297 return v;\r
10298\r
10299 each(t.shortcuts, function(o) {\r
10300 if (tinymce.isMac && o.ctrl != e.metaKey)\r
10301 return;\r
10302 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
10303 return;\r
10304\r
10305 if (o.alt != e.altKey)\r
10306 return;\r
10307\r
10308 if (o.shift != e.shiftKey)\r
10309 return;\r
10310\r
10311 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
10312 v = o;\r
10313 return false;\r
10314 }\r
10315 });\r
10316\r
10317 return v;\r
10318 };\r
10319\r
10320 t.onKeyUp.add(function(ed, e) {\r
10321 var o = find(e);\r
10322\r
10323 if (o)\r
10324 return Event.cancel(e);\r
10325 });\r
10326\r
10327 t.onKeyPress.add(function(ed, e) {\r
10328 var o = find(e);\r
10329\r
10330 if (o)\r
10331 return Event.cancel(e);\r
10332 });\r
10333\r
10334 t.onKeyDown.add(function(ed, e) {\r
10335 var o = find(e);\r
10336\r
10337 if (o) {\r
10338 o.func.call(o.scope);\r
10339 return Event.cancel(e);\r
10340 }\r
10341 });\r
10342 }\r
10343\r
10344 if (tinymce.isIE) {\r
10345 // Fix so resize will only update the width and height attributes not the styles of an image\r
10346 // It will also block mceItemNoResize items\r
10347 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
10348 var re = t.resizeInfo, cb;\r
10349\r
10350 e = e.target;\r
10351\r
10352 // Don't do this action for non image elements\r
10353 if (e.nodeName !== 'IMG')\r
10354 return;\r
10355\r
10356 if (re)\r
10357 t.dom.unbind(re.node, re.ev, re.cb);\r
10358\r
10359 if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
10360 ev = 'resizeend';\r
10361 cb = t.dom.bind(e, ev, function(e) {\r
10362 var v;\r
10363\r
10364 e = e.target;\r
10365\r
10366 if (v = t.dom.getStyle(e, 'width')) {\r
10367 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
10368 t.dom.setStyle(e, 'width', '');\r
10369 }\r
10370\r
10371 if (v = t.dom.getStyle(e, 'height')) {\r
10372 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
10373 t.dom.setStyle(e, 'height', '');\r
10374 }\r
10375 });\r
10376 } else {\r
10377 ev = 'resizestart';\r
10378 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
10379 }\r
10380\r
10381 re = t.resizeInfo = {\r
10382 node : e,\r
10383 ev : ev,\r
10384 cb : cb\r
10385 };\r
10386 });\r
10387\r
10388 t.onKeyDown.add(function(ed, e) {\r
10389 switch (e.keyCode) {\r
10390 case 8:\r
10391 // Fix IE control + backspace browser bug\r
10392 if (t.selection.getRng().item) {\r
10393 t.selection.getRng().item(0).removeNode();\r
10394 return Event.cancel(e);\r
10395 }\r
10396 }\r
10397 });\r
10398\r
10399 /*if (t.dom.boxModel) {\r
10400 t.getBody().style.height = '100%';\r
10401\r
10402 Event.add(t.getWin(), 'resize', function(e) {\r
10403 var docElm = t.getDoc().documentElement;\r
10404\r
10405 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10406 });\r
10407 }*/\r
10408 }\r
10409\r
10410 if (tinymce.isOpera) {\r
10411 t.onClick.add(function(ed, e) {\r
10412 Event.prevent(e);\r
10413 });\r
10414 }\r
10415\r
10416 // Add custom undo/redo handlers\r
10417 if (s.custom_undo_redo) {\r
10418 function addUndo() {\r
10419 t.undoManager.typing = 0;\r
10420 t.undoManager.add();\r
10421 };\r
10422\r
10423 // Add undo level on editor blur\r
10424 if (tinymce.isIE) {\r
10425 t.dom.bind(t.getWin(), 'blur', function(e) {\r
10426 var n;\r
10427\r
10428 // Check added for fullscreen bug\r
10429 if (t.selection) {\r
10430 n = t.selection.getNode();\r
10431\r
10432 // Add undo level is selection was lost to another document\r
10433 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())\r
10434 addUndo();\r
10435 }\r
10436 });\r
10437 } else {\r
10438 t.dom.bind(t.getDoc(), 'blur', function() {\r
10439 if (t.selection && !t.removed)\r
10440 addUndo();\r
10441 });\r
10442 }\r
10443\r
10444 t.onMouseDown.add(addUndo);\r
10445\r
10446 t.onKeyUp.add(function(ed, e) {\r
10447 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {\r
10448 t.undoManager.typing = 0;\r
10449 t.undoManager.add();\r
10450 }\r
10451 });\r
10452\r
10453 t.onKeyDown.add(function(ed, e) {\r
10454 // Is caracter positon keys\r
10455 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10456 if (t.undoManager.typing) {\r
10457 t.undoManager.add();\r
10458 t.undoManager.typing = 0;\r
10459 }\r
10460\r
10461 return;\r
10462 }\r
10463\r
10464 if (!t.undoManager.typing) {\r
10465 t.undoManager.add();\r
10466 t.undoManager.typing = 1;\r
10467 }\r
10468 });\r
10469 }\r
10470 },\r
10471\r
10472 _convertInlineElements : function() {\r
10473 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;\r
10474\r
10475 function convert(ed, o) {\r
10476 if (!s.inline_styles)\r
10477 return;\r
10478\r
10479 if (o.get) {\r
10480 each(t.dom.select('table,u,strike', o.node), function(n) {\r
10481 switch (n.nodeName) {\r
10482 case 'TABLE':\r
10483 if (v = dom.getAttrib(n, 'height')) {\r
10484 dom.setStyle(n, 'height', v);\r
10485 dom.setAttrib(n, 'height', '');\r
10486 }\r
10487 break;\r
10488\r
10489 case 'U':\r
10490 case 'STRIKE':\r
10491 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});\r
10492 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';\r
10493 dom.setAttrib(n, 'mce_style', '');\r
10494 dom.setAttrib(n, 'mce_name', 'span');\r
10495 break;\r
10496 }\r
10497 });\r
10498 } else if (o.set) {\r
10499 each(t.dom.select('table,span', o.node).reverse(), function(n) {\r
10500 if (n.nodeName == 'TABLE') {\r
10501 if (v = dom.getStyle(n, 'height'))\r
10502 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));\r
10503 } else {\r
10504 // Convert spans to elements\r
10505 if (n.style.textDecoration == 'underline')\r
10506 na = 'u';\r
10507 else if (n.style.textDecoration == 'line-through')\r
10508 na = 'strike';\r
10509 else\r
10510 na = '';\r
10511\r
10512 if (na) {\r
10513 n.style.textDecoration = '';\r
10514 dom.setAttrib(n, 'mce_style', '');\r
10515\r
10516 e = dom.create(na, {\r
10517 style : dom.getAttrib(n, 'style')\r
10518 });\r
10519\r
10520 dom.replace(e, n, 1);\r
10521 }\r
10522 }\r
10523 });\r
10524 }\r
10525 };\r
10526\r
10527 t.onPreProcess.add(convert);\r
10528\r
10529 if (!s.cleanup_on_startup) {\r
10530 t.onSetContent.add(function(ed, o) {\r
10531 if (o.initial)\r
10532 convert(t, {node : t.getBody(), set : 1});\r
10533 });\r
10534 }\r
10535 },\r
10536\r
10537 _convertFonts : function() {\r
10538 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;\r
10539\r
10540 // No need\r
10541 if (!s.inline_styles)\r
10542 return;\r
10543\r
10544 // Font pt values and font size names\r
10545 fz = [8, 10, 12, 14, 18, 24, 36];\r
10546 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];\r
10547\r
10548 if (sl = s.font_size_style_values)\r
10549 sl = explode(sl);\r
10550\r
10551 if (cl = s.font_size_classes)\r
10552 cl = explode(cl);\r
10553\r
10554 function process(no) {\r
10555 var n, sp, nl, x;\r
10556\r
10557 // Keep unit tests happy\r
10558 if (!s.inline_styles)\r
10559 return;\r
10560\r
10561 nl = t.dom.select('font', no);\r
10562 for (x = nl.length - 1; x >= 0; x--) {\r
10563 n = nl[x];\r
10564\r
10565 sp = dom.create('span', {\r
10566 style : dom.getAttrib(n, 'style'),\r
10567 'class' : dom.getAttrib(n, 'class')\r
10568 });\r
10569\r
10570 dom.setStyles(sp, {\r
10571 fontFamily : dom.getAttrib(n, 'face'),\r
10572 color : dom.getAttrib(n, 'color'),\r
10573 backgroundColor : n.style.backgroundColor\r
10574 });\r
10575\r
10576 if (n.size) {\r
10577 if (sl)\r
10578 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);\r
10579 else\r
10580 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);\r
10581 }\r
10582\r
10583 dom.setAttrib(sp, 'mce_style', '');\r
10584 dom.replace(sp, n, 1);\r
10585 }\r
10586 };\r
10587\r
10588 // Run on cleanup\r
10589 t.onPreProcess.add(function(ed, o) {\r
10590 if (o.get)\r
10591 process(o.node);\r
10592 });\r
10593\r
10594 t.onSetContent.add(function(ed, o) {\r
10595 if (o.initial)\r
10596 process(o.node);\r
10597 });\r
10598 },\r
10599\r
10600 _isHidden : function() {\r
10601 var s;\r
10602\r
10603 if (!isGecko)\r
10604 return 0;\r
10605\r
10606 // Weird, wheres that cursor selection?\r
10607 s = this.selection.getSel();\r
10608 return (!s || !s.rangeCount || s.rangeCount == 0);\r
10609 },\r
10610\r
10611 // Fix for bug #1867292\r
10612 _fixNesting : function(s) {\r
10613 var d = [], i;\r
10614\r
10615 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
10616 var e;\r
10617\r
10618 // Handle end element\r
10619 if (b === '/') {\r
10620 if (!d.length)\r
10621 return '';\r
10622\r
10623 if (c !== d[d.length - 1].tag) {\r
10624 for (i=d.length - 1; i>=0; i--) {\r
10625 if (d[i].tag === c) {\r
10626 d[i].close = 1;\r
10627 break;\r
10628 }\r
10629 }\r
10630\r
10631 return '';\r
10632 } else {\r
10633 d.pop();\r
10634\r
10635 if (d.length && d[d.length - 1].close) {\r
10636 a = a + '</' + d[d.length - 1].tag + '>';\r
10637 d.pop();\r
10638 }\r
10639 }\r
10640 } else {\r
10641 // Ignore these\r
10642 if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
10643 return a;\r
10644\r
10645 // Ignore closed ones\r
10646 if (/\/>$/.test(a))\r
10647 return a;\r
10648\r
10649 d.push({tag : c}); // Push start element\r
10650 }\r
10651\r
10652 return a;\r
10653 });\r
10654\r
10655 // End all open tags\r
10656 for (i=d.length - 1; i>=0; i--)\r
10657 s += '</' + d[i].tag + '>';\r
10658\r
10659 return s;\r
10660 }\r
10661 });\r
10662})(tinymce);\r
10663(function(tinymce) {\r
10664 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;\r
10665\r
10666 tinymce.create('tinymce.EditorCommands', {\r
10667 EditorCommands : function(ed) {\r
10668 this.editor = ed;\r
10669 },\r
10670\r
10671 execCommand : function(cmd, ui, val) {\r
10672 var t = this, ed = t.editor, f;\r
10673\r
10674 switch (cmd) {\r
10675 // Ignore these\r
10676 case 'mceResetDesignMode':\r
10677 case 'mceBeginUndoLevel':\r
10678 return true;\r
10679\r
10680 // Ignore these\r
10681 case 'unlink':\r
10682 t.UnLink();\r
10683 return true;\r
10684\r
10685 // Bundle these together\r
10686 case 'JustifyLeft':\r
10687 case 'JustifyCenter':\r
10688 case 'JustifyRight':\r
10689 case 'JustifyFull':\r
10690 t.mceJustify(cmd, cmd.substring(7).toLowerCase());\r
10691 return true;\r
10692\r
10693 default:\r
10694 f = this[cmd];\r
10695\r
10696 if (f) {\r
10697 f.call(this, ui, val);\r
10698 return true;\r
10699 }\r
10700 }\r
10701\r
10702 return false;\r
10703 },\r
10704\r
10705 Indent : function() {\r
10706 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;\r
10707\r
10708 // Setup indent level\r
10709 iv = ed.settings.indentation;\r
10710 iu = /[a-z%]+$/i.exec(iv);\r
10711 iv = parseInt(iv);\r
10712\r
10713 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {\r
10714 each(s.getSelectedBlocks(), function(e) {\r
10715 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);\r
10716 });\r
10717\r
10718 return;\r
10719 }\r
10720\r
10721 ed.getDoc().execCommand('Indent', false, null);\r
10722\r
10723 if (isIE) {\r
10724 d.getParent(s.getNode(), function(n) {\r
10725 if (n.nodeName == 'BLOCKQUOTE') {\r
10726 n.dir = n.style.cssText = '';\r
10727 }\r
10728 });\r
10729 }\r
10730 },\r
10731\r
10732 Outdent : function() {\r
10733 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;\r
10734\r
10735 // Setup indent level\r
10736 iv = ed.settings.indentation;\r
10737 iu = /[a-z%]+$/i.exec(iv);\r
10738 iv = parseInt(iv);\r
10739\r
10740 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {\r
10741 each(s.getSelectedBlocks(), function(e) {\r
10742 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);\r
10743 d.setStyle(e, 'paddingLeft', v ? v + iu : '');\r
10744 });\r
10745\r
10746 return;\r
10747 }\r
10748\r
10749 ed.getDoc().execCommand('Outdent', false, null);\r
10750 },\r
10751\r
10752/*\r
10753 mceSetAttribute : function(u, v) {\r
10754 var ed = this.editor, d = ed.dom, e;\r
10755\r
10756 if (e = d.getParent(ed.selection.getNode(), d.isBlock))\r
10757 d.setAttrib(e, v.name, v.value);\r
10758 },\r
10759*/\r
10760 mceSetContent : function(u, v) {\r
10761 this.editor.setContent(v);\r
10762 },\r
10763\r
10764 mceToggleVisualAid : function() {\r
10765 var ed = this.editor;\r
10766\r
10767 ed.hasVisual = !ed.hasVisual;\r
10768 ed.addVisual();\r
10769 },\r
10770\r
10771 mceReplaceContent : function(u, v) {\r
10772 var s = this.editor.selection;\r
10773\r
10774 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));\r
10775 },\r
10776\r
10777 mceInsertLink : function(u, v) {\r
10778 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'a');\r
10779\r
10780 if (tinymce.is(v, 'string'))\r
10781 v = {href : v};\r
10782\r
10783 function set(e) {\r
10784 each(v, function(v, k) {\r
10785 ed.dom.setAttrib(e, k, v);\r
10786 });\r
10787 };\r
10788\r
10789 if (!e) {\r
10790 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');\r
10791 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) {\r
10792 set(e);\r
10793 });\r
10794 } else {\r
10795 if (v.href)\r
10796 set(e);\r
10797 else\r
10798 ed.dom.remove(e, 1);\r
10799 }\r
10800 },\r
10801\r
10802 UnLink : function() {\r
10803 var ed = this.editor, s = ed.selection;\r
10804\r
10805 if (s.isCollapsed())\r
10806 s.select(s.getNode());\r
10807\r
10808 ed.getDoc().execCommand('unlink', false, null);\r
10809 s.collapse(0);\r
10810 },\r
10811\r
10812 FontName : function(u, v) {\r
10813 var t = this, ed = t.editor, s = ed.selection, e;\r
10814\r
10815 if (!v) {\r
10816 if (s.isCollapsed())\r
10817 s.select(s.getNode());\r
10818 } else {\r
10819 if (ed.settings.convert_fonts_to_spans)\r
10820 t._applyInlineStyle('span', {style : {fontFamily : v}});\r
10821 else\r
10822 ed.getDoc().execCommand('FontName', false, v);\r
10823 }\r
10824 },\r
10825\r
10826 FontSize : function(u, v) {\r
10827 var ed = this.editor, s = ed.settings, fc, fs;\r
10828\r
10829 // Use style options instead\r
10830 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) {\r
10831 fs = tinymce.explode(s.font_size_style_values);\r
10832 fc = tinymce.explode(s.font_size_classes);\r
10833\r
10834 if (fc)\r
10835 v = fc[v - 1] || v;\r
10836 else\r
10837 v = fs[v - 1] || v;\r
10838 }\r
10839\r
10840 if (v >= 1 && v <= 7)\r
10841 ed.getDoc().execCommand('FontSize', false, v);\r
10842 else\r
10843 this._applyInlineStyle('span', {style : {fontSize : v}});\r
10844 },\r
10845\r
10846 queryCommandValue : function(c) {\r
10847 var f = this['queryValue' + c];\r
10848\r
10849 if (f)\r
10850 return f.call(this, c);\r
10851\r
10852 return false;\r
10853 },\r
10854\r
10855 queryCommandState : function(cmd) {\r
10856 var f;\r
10857\r
10858 switch (cmd) {\r
10859 // Bundle these together\r
10860 case 'JustifyLeft':\r
10861 case 'JustifyCenter':\r
10862 case 'JustifyRight':\r
10863 case 'JustifyFull':\r
10864 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());\r
10865\r
10866 default:\r
10867 if (f = this['queryState' + cmd])\r
10868 return f.call(this, cmd);\r
10869 }\r
10870\r
10871 return -1;\r
10872 },\r
10873\r
10874 _queryState : function(c) {\r
10875 try {\r
10876 return this.editor.getDoc().queryCommandState(c);\r
10877 } catch (ex) {\r
10878 // Ignore exception\r
10879 }\r
10880 },\r
10881\r
10882 _queryVal : function(c) {\r
10883 try {\r
10884 return this.editor.getDoc().queryCommandValue(c);\r
10885 } catch (ex) {\r
10886 // Ignore exception\r
10887 }\r
10888 },\r
10889\r
10890 queryValueFontSize : function() {\r
10891 var ed = this.editor, v = 0, p;\r
10892\r
10893 if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))\r
10894 v = p.style.fontSize;\r
10895\r
10896 if (!v && (isOpera || isWebKit)) {\r
10897 if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))\r
10898 v = p.size;\r
10899\r
10900 return v;\r
10901 }\r
10902\r
10903 return v || this._queryVal('FontSize');\r
10904 },\r
10905\r
10906 queryValueFontName : function() {\r
10907 var ed = this.editor, v = 0, p;\r
10908\r
10909 if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))\r
10910 v = p.face;\r
10911\r
10912 if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))\r
10913 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
10914\r
10915 if (!v)\r
10916 v = this._queryVal('FontName');\r
10917\r
10918 return v;\r
10919 },\r
10920\r
10921 mceJustify : function(c, v) {\r
10922 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;\r
10923\r
10924 if (ed.settings.inline_styles && this.queryStateJustify(c, v))\r
10925 rm = 1;\r
10926\r
10927 bl = dom.getParent(n, ed.dom.isBlock);\r
10928\r
10929 if (nn == 'IMG') {\r
10930 if (v == 'full')\r
10931 return;\r
10932\r
10933 if (rm) {\r
10934 if (v == 'center')\r
10935 dom.setStyle(bl || n.parentNode, 'textAlign', '');\r
10936\r
10937 dom.setStyle(n, 'float', '');\r
10938 this.mceRepaint();\r
10939 return;\r
10940 }\r
10941\r
10942 if (v == 'center') {\r
10943 // Do not change table elements\r
10944 if (bl && /^(TD|TH)$/.test(bl.nodeName))\r
10945 bl = 0;\r
10946\r
10947 if (!bl || bl.childNodes.length > 1) {\r
10948 nb = dom.create('p');\r
10949 nb.appendChild(n.cloneNode(false));\r
10950\r
10951 if (bl)\r
10952 dom.insertAfter(nb, bl);\r
10953 else\r
10954 dom.insertAfter(nb, n);\r
10955\r
10956 dom.remove(n);\r
10957 n = nb.firstChild;\r
10958 bl = nb;\r
10959 }\r
10960\r
10961 dom.setStyle(bl, 'textAlign', v);\r
10962 dom.setStyle(n, 'float', '');\r
10963 } else {\r
10964 dom.setStyle(n, 'float', v);\r
10965 dom.setStyle(bl || n.parentNode, 'textAlign', '');\r
10966 }\r
10967\r
10968 this.mceRepaint();\r
10969 return;\r
10970 }\r
10971\r
10972 // Handle the alignment outselfs, less quirks in all browsers\r
10973 if (ed.settings.inline_styles && ed.settings.forced_root_block) {\r
10974 if (rm)\r
10975 v = '';\r
10976\r
10977 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {\r
10978 dom.setAttrib(e, 'align', '');\r
10979 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);\r
10980 });\r
10981\r
10982 return;\r
10983 } else if (!rm)\r
10984 ed.getDoc().execCommand(c, false, null);\r
10985\r
10986 if (ed.settings.inline_styles) {\r
10987 if (rm) {\r
10988 dom.getParent(ed.selection.getNode(), function(n) {\r
10989 if (n.style && n.style.textAlign)\r
10990 dom.setStyle(n, 'textAlign', '');\r
10991 });\r
10992\r
10993 return;\r
10994 }\r
10995\r
10996 each(dom.select('*'), function(n) {\r
10997 var v = n.align;\r
10998\r
10999 if (v) {\r
11000 if (v == 'full')\r
11001 v = 'justify';\r
11002\r
11003 dom.setStyle(n, 'textAlign', v);\r
11004 dom.setAttrib(n, 'align', '');\r
11005 }\r
11006 });\r
11007 }\r
11008 },\r
11009\r
11010 mceSetCSSClass : function(u, v) {\r
11011 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});\r
11012 },\r
11013\r
11014 getSelectedElement : function() {\r
11015 var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re;\r
11016\r
11017 if (se.isCollapsed() || r.item)\r
11018 return se.getNode();\r
11019\r
11020 // Setup regexp\r
11021 re = ed.settings.merge_styles_invalid_parents;\r
11022 if (tinymce.is(re, 'string'))\r
11023 re = new RegExp(re, 'i');\r
11024\r
11025 if (isIE) {\r
11026 r1 = r.duplicate();\r
11027 r1.collapse(true);\r
11028 sc = r1.parentElement();\r
11029\r
11030 r2 = r.duplicate();\r
11031 r2.collapse(false);\r
11032 ec = r2.parentElement();\r
11033\r
11034 if (sc != ec) {\r
11035 r1.move('character', 1);\r
11036 sc = r1.parentElement();\r
11037 }\r
11038\r
11039 if (sc == ec) {\r
11040 r1 = r.duplicate();\r
11041 r1.moveToElementText(sc);\r
11042\r
11043 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)\r
11044 return re && re.test(sc.nodeName) ? null : sc;\r
11045 }\r
11046 } else {\r
11047 function getParent(n) {\r
11048 return dom.getParent(n, '*');\r
11049 };\r
11050\r
11051 sc = r.startContainer;\r
11052 ec = r.endContainer;\r
11053 so = r.startOffset;\r
11054 eo = r.endOffset;\r
11055\r
11056 if (!r.collapsed) {\r
11057 if (sc == ec) {\r
11058 if (so - eo < 2) {\r
11059 if (sc.hasChildNodes()) {\r
11060 sp = sc.childNodes[so];\r
11061 return re && re.test(sp.nodeName) ? null : sp;\r
11062 }\r
11063 }\r
11064 }\r
11065 }\r
11066\r
11067 if (sc.nodeType != 3 || ec.nodeType != 3)\r
11068 return null;\r
11069\r
11070 if (so == 0) {\r
11071 sp = getParent(sc);\r
11072\r
11073 if (sp && sp.firstChild != sc)\r
11074 sp = null;\r
11075 }\r
11076\r
11077 if (so == sc.nodeValue.length) {\r
11078 e = sc.nextSibling;\r
11079\r
11080 if (e && e.nodeType == 1)\r
11081 sp = sc.nextSibling;\r
11082 }\r
11083\r
11084 if (eo == 0) {\r
11085 e = ec.previousSibling;\r
11086\r
11087 if (e && e.nodeType == 1)\r
11088 ep = e;\r
11089 }\r
11090\r
11091 if (eo == ec.nodeValue.length) {\r
11092 ep = getParent(ec);\r
11093\r
11094 if (ep && ep.lastChild != ec)\r
11095 ep = null;\r
11096 }\r
11097\r
11098 // Same element\r
11099 if (sp == ep)\r
11100 return re && sp && re.test(sp.nodeName) ? null : sp;\r
11101 }\r
11102\r
11103 return null;\r
11104 },\r
11105\r
11106 mceSetStyleInfo : function(u, v) {\r
11107 var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re;\r
11108\r
11109 function set(n, e) {\r
11110 if (n.nodeType == 1) {\r
11111 switch (v.command) {\r
11112 case 'setattrib':\r
11113 return dom.setAttrib(n, v.name, v.value);\r
11114\r
11115 case 'setstyle':\r
11116 return dom.setStyle(n, v.name, v.value);\r
11117\r
11118 case 'removeformat':\r
11119 return dom.setAttrib(n, 'class', '');\r
11120 }\r
11121 }\r
11122 };\r
11123\r
11124 // Setup regexp\r
11125 re = ed.settings.merge_styles_invalid_parents;\r
11126 if (tinymce.is(re, 'string'))\r
11127 re = new RegExp(re, 'i');\r
11128\r
11129 // Set style info on selected element\r
11130 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)\r
11131 set(e, 1);\r
11132 else {\r
11133 // Generate wrappers and set styles on them\r
11134 d.execCommand('FontName', false, '__');\r
11135 each(dom.select('span,font'), function(n) {\r
11136 var sp, e;\r
11137\r
11138 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {\r
11139 sp = dom.create(nn, {mce_new : '1'});\r
11140\r
11141 set(sp);\r
11142\r
11143 each (n.childNodes, function(n) {\r
11144 sp.appendChild(n.cloneNode(true));\r
11145 });\r
11146\r
11147 dom.replace(sp, n);\r
11148 }\r
11149 });\r
11150 }\r
11151\r
11152 // Remove wrappers inside new ones\r
11153 each(dom.select(nn).reverse(), function(n) {\r
11154 var p = n.parentNode;\r
11155\r
11156 // Check if it's an old span in a new wrapper\r
11157 if (!dom.getAttrib(n, 'mce_new')) {\r
11158 // Find new wrapper\r
11159 p = dom.getParent(n, '*[mce_new]');\r
11160\r
11161 if (p)\r
11162 dom.remove(n, 1);\r
11163 }\r
11164 });\r
11165\r
11166 // Merge wrappers with parent wrappers\r
11167 each(dom.select(nn).reverse(), function(n) {\r
11168 var p = n.parentNode;\r
11169\r
11170 if (!p || !dom.getAttrib(n, 'mce_new'))\r
11171 return;\r
11172\r
11173 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')\r
11174 return;\r
11175\r
11176 // Has parent of the same type and only child\r
11177 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)\r
11178 return dom.remove(p, 1);\r
11179\r
11180 // Has parent that is more suitable to have the class and only child\r
11181 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {\r
11182 set(p); // Set style info on parent instead\r
11183 dom.setAttrib(n, 'class', '');\r
11184 }\r
11185 });\r
11186\r
11187 // Remove empty wrappers\r
11188 each(dom.select(nn).reverse(), function(n) {\r
11189 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {\r
11190 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))\r
11191 return dom.remove(n, 1);\r
11192\r
11193 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker\r
11194 }\r
11195 });\r
11196\r
11197 s.moveToBookmark(b);\r
11198 },\r
11199\r
11200 queryStateJustify : function(c, v) {\r
11201 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;\r
11202\r
11203 if (n && n.nodeName == 'IMG') {\r
11204 if (dom.getStyle(n, 'float') == v)\r
11205 return 1;\r
11206\r
11207 return n.parentNode.style.textAlign == v;\r
11208 }\r
11209\r
11210 n = dom.getParent(ed.selection.getStart(), function(n) {\r
11211 return n.nodeType == 1 && n.style.textAlign;\r
11212 });\r
11213\r
11214 if (v == 'full')\r
11215 v = 'justify';\r
11216\r
11217 if (ed.settings.inline_styles)\r
11218 return (n && n.style.textAlign == v);\r
11219\r
11220 return this._queryState(c);\r
11221 },\r
11222\r
11223 ForeColor : function(ui, v) {\r
11224 var ed = this.editor;\r
11225\r
11226 if (ed.settings.convert_fonts_to_spans) {\r
11227 this._applyInlineStyle('span', {style : {color : v}});\r
11228 return;\r
11229 } else\r
11230 ed.getDoc().execCommand('ForeColor', false, v);\r
11231 },\r
11232\r
11233 HiliteColor : function(ui, val) {\r
11234 var t = this, ed = t.editor, d = ed.getDoc();\r
11235\r
11236 if (ed.settings.convert_fonts_to_spans) {\r
11237 this._applyInlineStyle('span', {style : {backgroundColor : val}});\r
11238 return;\r
11239 }\r
11240\r
11241 function set(s) {\r
11242 if (!isGecko)\r
11243 return;\r
11244\r
11245 try {\r
11246 // Try new Gecko method\r
11247 d.execCommand("styleWithCSS", 0, s);\r
11248 } catch (ex) {\r
11249 // Use old\r
11250 d.execCommand("useCSS", 0, !s);\r
11251 }\r
11252 };\r
11253\r
11254 if (isGecko || isOpera) {\r
11255 set(true);\r
11256 d.execCommand('hilitecolor', false, val);\r
11257 set(false);\r
11258 } else\r
11259 d.execCommand('BackColor', false, val);\r
11260 },\r
11261\r
11262 FormatBlock : function(ui, val) {\r
11263 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;\r
11264\r
11265 function isBlock(n) {\r
11266 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);\r
11267 };\r
11268\r
11269 bl = dom.getParent(s.getNode(), function(n) {\r
11270 return isBlock(n);\r
11271 });\r
11272\r
11273 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>\r
11274 // FF and Opera doesn't change parent DIV elements if you switch format\r
11275 if (bl) {\r
11276 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {\r
11277 // Rename block element\r
11278 nb = ed.dom.create(val);\r
11279\r
11280 each(dom.getAttribs(bl), function(v) {\r
11281 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));\r
11282 });\r
11283\r
11284 b = s.getBookmark();\r
11285 dom.replace(nb, bl, 1);\r
11286 s.moveToBookmark(b);\r
11287 ed.nodeChanged();\r
11288 return;\r
11289 }\r
11290 }\r
11291\r
11292 val = ed.settings.forced_root_block ? (val || '<p>') : val;\r
11293\r
11294 if (val.indexOf('<') == -1)\r
11295 val = '<' + val + '>';\r
11296\r
11297 if (tinymce.isGecko)\r
11298 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');\r
11299\r
11300 ed.getDoc().execCommand('FormatBlock', false, val);\r
11301 },\r
11302\r
11303 mceCleanup : function() {\r
11304 var ed = this.editor, s = ed.selection, b = s.getBookmark();\r
11305 ed.setContent(ed.getContent());\r
11306 s.moveToBookmark(b);\r
11307 },\r
11308\r
11309 mceRemoveNode : function(ui, val) {\r
11310 var ed = this.editor, s = ed.selection, b, n = val || s.getNode();\r
11311\r
11312 // Make sure that the body node isn't removed\r
11313 if (n == ed.getBody())\r
11314 return;\r
11315\r
11316 b = s.getBookmark();\r
11317 ed.dom.remove(n, 1);\r
11318 s.moveToBookmark(b);\r
11319 ed.nodeChanged();\r
11320 },\r
11321\r
11322 mceSelectNodeDepth : function(ui, val) {\r
11323 var ed = this.editor, s = ed.selection, c = 0;\r
11324\r
11325 ed.dom.getParent(s.getNode(), function(n) {\r
11326 if (n.nodeType == 1 && c++ == val) {\r
11327 s.select(n);\r
11328 ed.nodeChanged();\r
11329 return false;\r
11330 }\r
11331 }, ed.getBody());\r
11332 },\r
11333\r
11334 mceSelectNode : function(u, v) {\r
11335 this.editor.selection.select(v);\r
11336 },\r
11337\r
11338 mceInsertContent : function(ui, val) {\r
11339 this.editor.selection.setContent(val);\r
11340 },\r
11341\r
11342 mceInsertRawHTML : function(ui, val) {\r
11343 var ed = this.editor;\r
11344\r
11345 ed.selection.setContent('tiny_mce_marker');\r
11346 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));\r
11347 },\r
11348\r
11349 mceRepaint : function() {\r
11350 var s, b, e = this.editor;\r
11351\r
11352 if (tinymce.isGecko) {\r
11353 try {\r
11354 s = e.selection;\r
11355 b = s.getBookmark(true);\r
11356\r
11357 if (s.getSel())\r
11358 s.getSel().selectAllChildren(e.getBody());\r
11359\r
11360 s.collapse(true);\r
11361 s.moveToBookmark(b);\r
11362 } catch (ex) {\r
11363 // Ignore\r
11364 }\r
11365 }\r
11366 },\r
11367\r
11368 queryStateUnderline : function() {\r
11369 var ed = this.editor, n = ed.selection.getNode();\r
11370\r
11371 if (n && n.nodeName == 'A')\r
11372 return false;\r
11373\r
11374 return this._queryState('Underline');\r
11375 },\r
11376\r
11377 queryStateOutdent : function() {\r
11378 var ed = this.editor, n;\r
11379\r
11380 if (ed.settings.inline_styles) {\r
11381 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)\r
11382 return true;\r
11383\r
11384 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)\r
11385 return true;\r
11386 }\r
11387\r
11388 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE'));\r
11389 },\r
11390\r
11391 queryStateInsertUnorderedList : function() {\r
11392 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');\r
11393 },\r
11394\r
11395 queryStateInsertOrderedList : function() {\r
11396 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');\r
11397 },\r
11398\r
11399 queryStatemceBlockQuote : function() {\r
11400 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});\r
11401 },\r
11402\r
11403 _applyInlineStyle : function(na, at, op) {\r
11404 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found;\r
11405\r
11406 na = na.toUpperCase();\r
11407\r
11408 if (op && op.check_classes && at['class'])\r
11409 op.check_classes.push(at['class']);\r
11410\r
11411 function removeEmpty() {\r
11412 each(dom.select(na).reverse(), function(n) {\r
11413 var c = 0;\r
11414\r
11415 // Check if there is any attributes\r
11416 each(dom.getAttribs(n), function(an) {\r
11417 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') {\r
11418 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName));\r
11419 c++;\r
11420 }\r
11421 });\r
11422\r
11423 // No attributes then remove the element and keep the children\r
11424 if (c == 0)\r
11425 dom.remove(n, 1);\r
11426 });\r
11427 };\r
11428\r
11429 function replaceFonts() {\r
11430 var bm;\r
11431\r
11432 each(dom.select('span,font'), function(n) {\r
11433 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') {\r
11434 if (!bm)\r
11435 bm = ed.selection.getBookmark();\r
11436\r
11437 at._mce_new = '1';\r
11438 dom.replace(dom.create(na, at), n, 1);\r
11439 }\r
11440 });\r
11441\r
11442 // Remove redundant elements\r
11443 each(dom.select(na + '[_mce_new]'), function(n) {\r
11444 function removeStyle(n) {\r
11445 if (n.nodeType == 1) {\r
11446 each(at.style, function(v, k) {\r
11447 dom.setStyle(n, k, '');\r
11448 });\r
11449\r
11450 // Remove spans with the same class or marked classes\r
11451 if (at['class'] && n.className && op) {\r
11452 each(op.check_classes, function(c) {\r
11453 if (dom.hasClass(n, c))\r
11454 dom.removeClass(n, c);\r
11455 });\r
11456 }\r
11457 }\r
11458 };\r
11459\r
11460 // Remove specified style information from child elements\r
11461 each(dom.select(na, n), removeStyle);\r
11462\r
11463 // Remove the specified style information on parent if current node is only child (IE)\r
11464 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1)\r
11465 removeStyle(n.parentNode);\r
11466\r
11467 // Remove the child elements style info if a parent already has it\r
11468 dom.getParent(n.parentNode, function(pn) {\r
11469 if (pn.nodeType == 1) {\r
11470 if (at.style) {\r
11471 each(at.style, function(v, k) {\r
11472 var sv;\r
11473\r
11474 if (!lo[k] && (sv = dom.getStyle(pn, k))) {\r
11475 if (sv === v)\r
11476 dom.setStyle(n, k, '');\r
11477\r
11478 lo[k] = 1;\r
11479 }\r
11480 });\r
11481 }\r
11482\r
11483 // Remove spans with the same class or marked classes\r
11484 if (at['class'] && pn.className && op) {\r
11485 each(op.check_classes, function(c) {\r
11486 if (dom.hasClass(pn, c))\r
11487 dom.removeClass(n, c);\r
11488 });\r
11489 }\r
11490 }\r
11491\r
11492 return false;\r
11493 });\r
11494\r
11495 n.removeAttribute('_mce_new');\r
11496 });\r
11497\r
11498 removeEmpty();\r
11499 ed.selection.moveToBookmark(bm);\r
11500\r
11501 return !!bm;\r
11502 };\r
11503\r
11504 // Create inline elements\r
11505 ed.focus();\r
11506 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
11507 replaceFonts();\r
11508\r
11509 if (kh = t._applyInlineStyle.keyhandler) {\r
11510 ed.onKeyUp.remove(kh);\r
11511 ed.onKeyPress.remove(kh);\r
11512 ed.onKeyDown.remove(kh);\r
11513 ed.onSetContent.remove(t._applyInlineStyle.chandler);\r
11514 }\r
11515\r
11516 if (ed.selection.isCollapsed()) {\r
11517 // IE will format the current word so this code can't be executed on that browser\r
11518 if (!isIE) {\r
11519 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) {\r
11520 each(at.style, function(v, k) {\r
11521 var kv;\r
11522\r
11523 if (kv = dom.getStyle(n, k)) {\r
11524 if (kv == v) {\r
11525 dom.setStyle(n, k, '');\r
11526 found = 2;\r
11527 return false;\r
11528 }\r
11529\r
11530 found = 1;\r
11531 return false;\r
11532 }\r
11533 });\r
11534\r
11535 if (found)\r
11536 return false;\r
11537 });\r
11538\r
11539 if (found == 2) {\r
11540 bm = ed.selection.getBookmark();\r
11541\r
11542 removeEmpty();\r
11543\r
11544 ed.selection.moveToBookmark(bm);\r
11545\r
11546 // Node change needs to be detached since the onselect event\r
11547 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix!\r
11548 window.setTimeout(function() {\r
11549 ed.nodeChanged();\r
11550 }, 1);\r
11551\r
11552 return;\r
11553 }\r
11554 }\r
11555\r
11556 // Start collecting styles\r
11557 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style);\r
11558\r
11559 t._applyInlineStyle.chandler = ed.onSetContent.add(function() {\r
11560 delete t._pendingStyles;\r
11561 });\r
11562\r
11563 t._applyInlineStyle.keyhandler = kh = function(e) {\r
11564 // Use pending styles\r
11565 if (t._pendingStyles) {\r
11566 at.style = t._pendingStyles;\r
11567 delete t._pendingStyles;\r
11568 }\r
11569\r
11570 if (replaceFonts()) {\r
11571 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler);\r
11572 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler);\r
11573 }\r
11574\r
11575 if (e.type == 'keyup')\r
11576 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler);\r
11577 };\r
11578\r
11579 ed.onKeyDown.add(kh);\r
11580 ed.onKeyPress.add(kh);\r
11581 ed.onKeyUp.add(kh);\r
11582 } else\r
11583 t._pendingStyles = 0;\r
11584 }\r
11585 });\r
11586})(tinymce);(function(tinymce) {\r
11587 tinymce.create('tinymce.UndoManager', {\r
11588 index : 0,\r
11589 data : null,\r
11590 typing : 0,\r
11591\r
11592 UndoManager : function(ed) {\r
11593 var t = this, Dispatcher = tinymce.util.Dispatcher;\r
11594\r
11595 t.editor = ed;\r
11596 t.data = [];\r
11597 t.onAdd = new Dispatcher(this);\r
11598 t.onUndo = new Dispatcher(this);\r
11599 t.onRedo = new Dispatcher(this);\r
11600 },\r
11601\r
11602 add : function(l) {\r
11603 var t = this, i, ed = t.editor, b, s = ed.settings, la;\r
11604\r
11605 l = l || {};\r
11606 l.content = l.content || ed.getContent({format : 'raw', no_events : 1});\r
11607\r
11608 // Add undo level if needed\r
11609 l.content = l.content.replace(/^\s*|\s*$/g, '');\r
11610 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];\r
11611 if (!l.initial && la && l.content == la.content)\r
11612 return null;\r
11613\r
11614 // Time to compress\r
11615 if (s.custom_undo_redo_levels) {\r
11616 if (t.data.length > s.custom_undo_redo_levels) {\r
11617 for (i = 0; i < t.data.length - 1; i++)\r
11618 t.data[i] = t.data[i + 1];\r
11619\r
11620 t.data.length--;\r
11621 t.index = t.data.length;\r
11622 }\r
11623 }\r
11624\r
11625 if (s.custom_undo_redo_restore_selection && !l.initial)\r
11626 l.bookmark = b = l.bookmark || ed.selection.getBookmark();\r
11627\r
11628 if (t.index < t.data.length)\r
11629 t.index++;\r
11630\r
11631 // Only initial marked undo levels should be allowed as first item\r
11632 // This to workaround a bug with Firefox and the blur event\r
11633 if (t.data.length === 0 && !l.initial)\r
11634 return null;\r
11635\r
11636 // Add level\r
11637 t.data.length = t.index + 1;\r
11638 t.data[t.index++] = l;\r
11639\r
11640 if (l.initial)\r
11641 t.index = 0;\r
11642\r
11643 // Set initial bookmark use first real undo level\r
11644 if (t.data.length == 2 && t.data[0].initial)\r
11645 t.data[0].bookmark = b;\r
11646\r
11647 t.onAdd.dispatch(t, l);\r
11648 ed.isNotDirty = 0;\r
11649\r
11650 //console.dir(t.data);\r
11651\r
11652 return l;\r
11653 },\r
11654\r
11655 undo : function() {\r
11656 var t = this, ed = t.editor, l = l, i;\r
11657\r
11658 if (t.typing) {\r
11659 t.add();\r
11660 t.typing = 0;\r
11661 }\r
11662\r
11663 if (t.index > 0) {\r
11664 // If undo on last index then take snapshot\r
11665 if (t.index == t.data.length && t.index > 1) {\r
11666 i = t.index;\r
11667 t.typing = 0;\r
11668\r
11669 if (!t.add())\r
11670 t.index = i;\r
11671\r
11672 --t.index;\r
11673 }\r
11674\r
11675 l = t.data[--t.index];\r
11676 ed.setContent(l.content, {format : 'raw'});\r
11677 ed.selection.moveToBookmark(l.bookmark);\r
11678\r
11679 t.onUndo.dispatch(t, l);\r
11680 }\r
11681\r
11682 return l;\r
11683 },\r
11684\r
11685 redo : function() {\r
11686 var t = this, ed = t.editor, l = null;\r
11687\r
11688 if (t.index < t.data.length - 1) {\r
11689 l = t.data[++t.index];\r
11690 ed.setContent(l.content, {format : 'raw'});\r
11691 ed.selection.moveToBookmark(l.bookmark);\r
11692\r
11693 t.onRedo.dispatch(t, l);\r
11694 }\r
11695\r
11696 return l;\r
11697 },\r
11698\r
11699 clear : function() {\r
11700 var t = this;\r
11701\r
11702 t.data = [];\r
11703 t.index = 0;\r
11704 t.typing = 0;\r
11705 t.add({initial : true});\r
11706 },\r
11707\r
11708 hasUndo : function() {\r
11709 return this.index != 0 || this.typing;\r
11710 },\r
11711\r
11712 hasRedo : function() {\r
11713 return this.index < this.data.length - 1;\r
11714 }\r
11715 });\r
11716})(tinymce);\r
11717(function(tinymce) {\r
11718 // Shorten names\r
11719 var Event, isIE, isGecko, isOpera, each, extend;\r
11720\r
11721 Event = tinymce.dom.Event;\r
11722 isIE = tinymce.isIE;\r
11723 isGecko = tinymce.isGecko;\r
11724 isOpera = tinymce.isOpera;\r
11725 each = tinymce.each;\r
11726 extend = tinymce.extend;\r
11727\r
11728 // Checks if the selection/caret is at the end of the specified block element\r
11729 function isAtEnd(rng, par) {\r
11730 var rng2 = par.ownerDocument.createRange();\r
11731\r
11732 rng2.setStart(rng.endContainer, rng.endOffset);\r
11733 rng2.setEndAfter(par);\r
11734\r
11735 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element\r
11736 return rng2.cloneContents().textContent.length == 0;\r
11737 };\r
11738\r
11739 function isEmpty(n) {\r
11740 n = n.innerHTML;\r
11741\r
11742 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
11743 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
11744\r
11745 return n.replace(/[ \t\r\n]+/g, '') == '';\r
11746 };\r
11747\r
11748 tinymce.create('tinymce.ForceBlocks', {\r
11749 ForceBlocks : function(ed) {\r
11750 var t = this, s = ed.settings, elm;\r
11751\r
11752 t.editor = ed;\r
11753 t.dom = ed.dom;\r
11754 elm = (s.forced_root_block || 'p').toLowerCase();\r
11755 s.element = elm.toUpperCase();\r
11756\r
11757 ed.onPreInit.add(t.setup, t);\r
11758\r
11759 t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
11760 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11761 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11762 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
11763 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
11764\r
11765 function padd(ed, o) {\r
11766 if (isOpera)\r
11767 o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
11768\r
11769 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
11770\r
11771 if (!isIE && !isOpera && o.set) {\r
11772 // Use &nbsp; instead of BR in padded paragraphs\r
11773 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
11774 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
11775 } else\r
11776 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
11777 };\r
11778\r
11779 ed.onBeforeSetContent.add(padd);\r
11780 ed.onPostProcess.add(padd);\r
11781\r
11782 if (s.forced_root_block) {\r
11783 ed.onInit.add(t.forceRoots, t);\r
11784 ed.onSetContent.add(t.forceRoots, t);\r
11785 ed.onBeforeGetContent.add(t.forceRoots, t);\r
11786 }\r
11787 },\r
11788\r
11789 setup : function() {\r
11790 var t = this, ed = t.editor, s = ed.settings;\r
11791\r
11792 // Force root blocks when typing and when getting output\r
11793 if (s.forced_root_block) {\r
11794 ed.onKeyUp.add(t.forceRoots, t);\r
11795 ed.onPreProcess.add(t.forceRoots, t);\r
11796 }\r
11797\r
11798 if (s.force_br_newlines) {\r
11799 // Force IE to produce BRs on enter\r
11800 if (isIE) {\r
11801 ed.onKeyPress.add(function(ed, e) {\r
11802 var n, s = ed.selection;\r
11803\r
11804 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {\r
11805 s.setContent('<br id="__" /> ', {format : 'raw'});\r
11806 n = ed.dom.get('__');\r
11807 n.removeAttribute('id');\r
11808 s.select(n);\r
11809 s.collapse();\r
11810 return Event.cancel(e);\r
11811 }\r
11812 });\r
11813 }\r
11814\r
11815 return;\r
11816 }\r
11817\r
11818 if (!isIE && s.force_p_newlines) {\r
11819/* ed.onPreProcess.add(function(ed, o) {\r
11820 each(ed.dom.select('br', o.node), function(n) {\r
11821 var p = n.parentNode;\r
11822\r
11823 // Replace <p><br /></p> with <p>&nbsp;</p>\r
11824 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {\r
11825 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);\r
11826 }\r
11827 });\r
11828 });*/\r
11829\r
11830 ed.onKeyPress.add(function(ed, e) {\r
11831 if (e.keyCode == 13 && !e.shiftKey) {\r
11832 if (!t.insertPara(e))\r
11833 Event.cancel(e);\r
11834 }\r
11835 });\r
11836\r
11837 if (isGecko) {\r
11838 ed.onKeyDown.add(function(ed, e) {\r
11839 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
11840 t.backspaceDelete(e, e.keyCode == 8);\r
11841 });\r
11842 }\r
11843 }\r
11844\r
11845 function ren(rn, na) {\r
11846 var ne = ed.dom.create(na);\r
11847\r
11848 each(rn.attributes, function(a) {\r
11849 if (a.specified && a.nodeValue)\r
11850 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);\r
11851 });\r
11852\r
11853 each(rn.childNodes, function(n) {\r
11854 ne.appendChild(n.cloneNode(true));\r
11855 });\r
11856\r
11857 rn.parentNode.replaceChild(ne, rn);\r
11858\r
11859 return ne;\r
11860 };\r
11861\r
11862 // Padd empty inline elements within block elements\r
11863 // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
11864 ed.onPreProcess.add(function(ed, o) {\r
11865 each(ed.dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
11866 if (isEmpty(p)) {\r
11867 each(ed.dom.select('span,em,strong,b,i', o.node), function(n) {\r
11868 if (!n.hasChildNodes()) {\r
11869 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
11870 return false; // Break the loop one padding is enough\r
11871 }\r
11872 });\r
11873 }\r
11874 });\r
11875 });\r
11876\r
11877 // IE specific fixes\r
11878 if (isIE) {\r
11879 // Replaces IE:s auto generated paragraphs with the specified element name\r
11880 if (s.element != 'P') {\r
11881 ed.onKeyPress.add(function(ed, e) {\r
11882 t.lastElm = ed.selection.getNode().nodeName;\r
11883 });\r
11884\r
11885 ed.onKeyUp.add(function(ed, e) {\r
11886 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();\r
11887\r
11888 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
11889 n = ren(n, s.element);\r
11890 sel.select(n);\r
11891 sel.collapse();\r
11892 ed.nodeChanged();\r
11893 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
11894 bl = ed.dom.getParent(n, 'p');\r
11895\r
11896 if (bl) {\r
11897 ren(bl, s.element);\r
11898 ed.nodeChanged();\r
11899 }\r
11900 }\r
11901 });\r
11902 }\r
11903 }\r
11904 },\r
11905\r
11906 find : function(n, t, s) {\r
11907 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;\r
11908\r
11909 while (n = w.nextNode()) {\r
11910 c++;\r
11911\r
11912 // Index by node\r
11913 if (t == 0 && n == s)\r
11914 return c;\r
11915\r
11916 // Node by index\r
11917 if (t == 1 && c == s)\r
11918 return n;\r
11919 }\r
11920\r
11921 return -1;\r
11922 },\r
11923\r
11924 forceRoots : function(ed, e) {\r
11925 var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF;\r
11926 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
11927\r
11928 // Fix for bug #1863847\r
11929 //if (e && e.keyCode == 13)\r
11930 // return true;\r
11931\r
11932 // Wrap non blocks into blocks\r
11933 for (i = nl.length - 1; i >= 0; i--) {\r
11934 nx = nl[i];\r
11935\r
11936 // Is text or non block element\r
11937 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11938 if (!bl) {\r
11939 // Create new block but ignore whitespace\r
11940 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11941 // Store selection\r
11942 if (si == -2 && r) {\r
11943 if (!isIE) {\r
11944 // If selection is element then mark it\r
11945 if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11946 // Save the id of the selected element\r
11947 eid = n.getAttribute("id");\r
11948 n.setAttribute("id", "__mce");\r
11949 } else {\r
11950 // If element is inside body, might not be the case in contentEdiable mode\r
11951 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11952 so = r.startOffset;\r
11953 eo = r.endOffset;\r
11954 si = t.find(b, 0, r.startContainer);\r
11955 ei = t.find(b, 0, r.endContainer);\r
11956 }\r
11957 }\r
11958 } else {\r
11959 tr = d.body.createTextRange();\r
11960 tr.moveToElementText(b);\r
11961 tr.collapse(1);\r
11962 bp = tr.move('character', c) * -1;\r
11963\r
11964 tr = r.duplicate();\r
11965 tr.collapse(1);\r
11966 sp = tr.move('character', c) * -1;\r
11967\r
11968 tr = r.duplicate();\r
11969 tr.collapse(0);\r
11970 le = (tr.move('character', c) * -1) - sp;\r
11971\r
11972 si = sp - bp;\r
11973 ei = le;\r
11974 }\r
11975 }\r
11976\r
11977 // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11978 // See: http://support.microsoft.com/kb/829907\r
11979 bl = ed.dom.create(ed.settings.forced_root_block);\r
11980 nx.parentNode.replaceChild(bl, nx);\r
11981 bl.appendChild(nx);\r
11982 }\r
11983 } else {\r
11984 if (bl.hasChildNodes())\r
11985 bl.insertBefore(nx, bl.firstChild);\r
11986 else\r
11987 bl.appendChild(nx);\r
11988 }\r
11989 } else\r
11990 bl = null; // Time to create new block\r
11991 }\r
11992\r
11993 // Restore selection\r
11994 if (si != -2) {\r
11995 if (!isIE) {\r
11996 bl = b.getElementsByTagName(ed.settings.element)[0];\r
11997 r = d.createRange();\r
11998\r
11999 // Select last location or generated block\r
12000 if (si != -1)\r
12001 r.setStart(t.find(b, 1, si), so);\r
12002 else\r
12003 r.setStart(bl, 0);\r
12004\r
12005 // Select last location or generated block\r
12006 if (ei != -1)\r
12007 r.setEnd(t.find(b, 1, ei), eo);\r
12008 else\r
12009 r.setEnd(bl, 0);\r
12010\r
12011 if (s) {\r
12012 s.removeAllRanges();\r
12013 s.addRange(r);\r
12014 }\r
12015 } else {\r
12016 try {\r
12017 r = s.createRange();\r
12018 r.moveToElementText(b);\r
12019 r.collapse(1);\r
12020 r.moveStart('character', si);\r
12021 r.moveEnd('character', ei);\r
12022 r.select();\r
12023 } catch (ex) {\r
12024 // Ignore\r
12025 }\r
12026 }\r
12027 } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
12028 // Restore the id of the selected element\r
12029 if (eid)\r
12030 n.setAttribute('id', eid);\r
12031 else\r
12032 n.removeAttribute('id');\r
12033\r
12034 // Move caret before selected element\r
12035 r = d.createRange();\r
12036 r.setStartBefore(n);\r
12037 r.setEndBefore(n);\r
12038 se.setRng(r);\r
12039 }\r
12040 },\r
12041\r
12042 getParentBlock : function(n) {\r
12043 var d = this.dom;\r
12044\r
12045 return d.getParent(n, d.isBlock);\r
12046 },\r
12047\r
12048 insertPara : function(e) {\r
12049 var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;\r
12050 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;\r
12051\r
12052 // If root blocks are forced then use Operas default behavior since it's really good\r
12053// Removed due to bug: #1853816\r
12054// if (se.forced_root_block && isOpera)\r
12055// return true;\r
12056\r
12057 // Setup before range\r
12058 rb = d.createRange();\r
12059\r
12060 // If is before the first block element and in body, then move it into first block element\r
12061 rb.setStart(s.anchorNode, s.anchorOffset);\r
12062 rb.collapse(true);\r
12063\r
12064 // Setup after range\r
12065 ra = d.createRange();\r
12066\r
12067 // If is before the first block element and in body, then move it into first block element\r
12068 ra.setStart(s.focusNode, s.focusOffset);\r
12069 ra.collapse(true);\r
12070\r
12071 // Setup start/end points\r
12072 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
12073 sn = dir ? s.anchorNode : s.focusNode;\r
12074 so = dir ? s.anchorOffset : s.focusOffset;\r
12075 en = dir ? s.focusNode : s.anchorNode;\r
12076 eo = dir ? s.focusOffset : s.anchorOffset;\r
12077\r
12078 // If selection is in empty table cell\r
12079 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
12080 if (sn.firstChild.nodeName == 'BR')\r
12081 dom.remove(sn.firstChild); // Remove BR\r
12082\r
12083 // Create two new block elements\r
12084 if (sn.childNodes.length == 0) {\r
12085 ed.dom.add(sn, se.element, null, '<br />');\r
12086 aft = ed.dom.add(sn, se.element, null, '<br />');\r
12087 } else {\r
12088 n = sn.innerHTML;\r
12089 sn.innerHTML = '';\r
12090 ed.dom.add(sn, se.element, null, n);\r
12091 aft = ed.dom.add(sn, se.element, null, '<br />');\r
12092 }\r
12093\r
12094 // Move caret into the last one\r
12095 r = d.createRange();\r
12096 r.selectNodeContents(aft);\r
12097 r.collapse(1);\r
12098 ed.selection.setRng(r);\r
12099\r
12100 return false;\r
12101 }\r
12102\r
12103 // If the caret is in an invalid location in FF we need to move it into the first block\r
12104 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
12105 sn = en = sn.firstChild;\r
12106 so = eo = 0;\r
12107 rb = d.createRange();\r
12108 rb.setStart(sn, 0);\r
12109 ra = d.createRange();\r
12110 ra.setStart(en, 0);\r
12111 }\r
12112\r
12113 // Never use body as start or end node\r
12114 sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12115 sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
12116 en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12117 en = en.nodeName == "BODY" ? en.firstChild : en;\r
12118\r
12119 // Get start and end blocks\r
12120 sb = t.getParentBlock(sn);\r
12121 eb = t.getParentBlock(en);\r
12122 bn = sb ? sb.nodeName : se.element; // Get block name to create\r
12123\r
12124 // Return inside list use default browser behavior\r
12125 if (t.dom.getParent(sb, 'ol,ul,pre'))\r
12126 return true;\r
12127\r
12128 // If caption or absolute layers then always generate new blocks within\r
12129 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12130 bn = se.element;\r
12131 sb = null;\r
12132 }\r
12133\r
12134 // If caption or absolute layers then always generate new blocks within\r
12135 if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12136 bn = se.element;\r
12137 eb = null;\r
12138 }\r
12139\r
12140 // Use P instead\r
12141 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
12142 bn = se.element;\r
12143 sb = eb = null;\r
12144 }\r
12145\r
12146 // Setup new before and after blocks\r
12147 bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
12148 aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
12149\r
12150 // Remove id from after clone\r
12151 aft.removeAttribute('id');\r
12152\r
12153 // Is header and cursor is at the end, then force paragraph under\r
12154 if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
12155 aft = ed.dom.create(se.element);\r
12156\r
12157 // Find start chop node\r
12158 n = sc = sn;\r
12159 do {\r
12160 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12161 break;\r
12162\r
12163 sc = n;\r
12164 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
12165\r
12166 // Find end chop node\r
12167 n = ec = en;\r
12168 do {\r
12169 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12170 break;\r
12171\r
12172 ec = n;\r
12173 } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
12174\r
12175 // Place first chop part into before block element\r
12176 if (sc.nodeName == bn)\r
12177 rb.setStart(sc, 0);\r
12178 else\r
12179 rb.setStartBefore(sc);\r
12180\r
12181 rb.setEnd(sn, so);\r
12182 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12183\r
12184 // Place secnd chop part within new block element\r
12185 try {\r
12186 ra.setEndAfter(ec);\r
12187 } catch(ex) {\r
12188 //console.debug(s.focusNode, s.focusOffset);\r
12189 }\r
12190\r
12191 ra.setStart(en, eo);\r
12192 aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12193\r
12194 // Create range around everything\r
12195 r = d.createRange();\r
12196 if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
12197 r.setStartBefore(sc.parentNode);\r
12198 } else {\r
12199 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
12200 r.setStartBefore(rb.startContainer);\r
12201 else\r
12202 r.setStart(rb.startContainer, rb.startOffset);\r
12203 }\r
12204\r
12205 if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
12206 r.setEndAfter(ec.parentNode);\r
12207 else\r
12208 r.setEnd(ra.endContainer, ra.endOffset);\r
12209\r
12210 // Delete and replace it with new block elements\r
12211 r.deleteContents();\r
12212\r
12213 if (isOpera)\r
12214 ed.getWin().scrollTo(0, vp.y);\r
12215\r
12216 // Never wrap blocks in blocks\r
12217 if (bef.firstChild && bef.firstChild.nodeName == bn)\r
12218 bef.innerHTML = bef.firstChild.innerHTML;\r
12219\r
12220 if (aft.firstChild && aft.firstChild.nodeName == bn)\r
12221 aft.innerHTML = aft.firstChild.innerHTML;\r
12222\r
12223 // Padd empty blocks\r
12224 if (isEmpty(bef))\r
12225 bef.innerHTML = '<br />';\r
12226\r
12227 function appendStyles(e, en) {\r
12228 var nl = [], nn, n, i;\r
12229\r
12230 e.innerHTML = '';\r
12231\r
12232 // Make clones of style elements\r
12233 if (se.keep_styles) {\r
12234 n = en;\r
12235 do {\r
12236 // We only want style specific elements\r
12237 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
12238 nn = n.cloneNode(false);\r
12239 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
12240 nl.push(nn);\r
12241 }\r
12242 } while (n = n.parentNode);\r
12243 }\r
12244\r
12245 // Append style elements to aft\r
12246 if (nl.length > 0) {\r
12247 for (i = nl.length - 1, nn = e; i >= 0; i--)\r
12248 nn = nn.appendChild(nl[i]);\r
12249\r
12250 // Padd most inner style element\r
12251 nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12252 return nl[0]; // Move caret to most inner element\r
12253 } else\r
12254 e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12255 };\r
12256\r
12257 // Fill empty afterblook with current style\r
12258 if (isEmpty(aft))\r
12259 car = appendStyles(aft, en);\r
12260\r
12261 // Opera needs this one backwards for older versions\r
12262 if (isOpera && parseFloat(opera.version()) < 9.5) {\r
12263 r.insertNode(bef);\r
12264 r.insertNode(aft);\r
12265 } else {\r
12266 r.insertNode(aft);\r
12267 r.insertNode(bef);\r
12268 }\r
12269\r
12270 // Normalize\r
12271 aft.normalize();\r
12272 bef.normalize();\r
12273\r
12274 function first(n) {\r
12275 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n;\r
12276 };\r
12277\r
12278 // Move cursor and scroll into view\r
12279 r = d.createRange();\r
12280 r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
12281 r.collapse(1);\r
12282 s.removeAllRanges();\r
12283 s.addRange(r);\r
12284\r
12285 // 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
12286 y = ed.dom.getPos(aft).y;\r
12287 ch = aft.clientHeight;\r
12288\r
12289 // Is element within viewport\r
12290 if (y < vp.y || y + ch > vp.y + vp.h) {\r
12291 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks\r
12292 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight));\r
12293 }\r
12294\r
12295 return false;\r
12296 },\r
12297\r
12298 backspaceDelete : function(e, bs) {\r
12299 var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;\r
12300\r
12301 /*\r
12302 var par, rng, nextBlock;\r
12303\r
12304 // Delete key will not merge paragraphs on Gecko so we need to do this manually\r
12305 // Hitting the delete key at the following caret position doesn't merge the elements <p>A|</p><p>B</p>\r
12306 // This logic will merge them into this: <p>A|B</p>\r
12307 if (e.keyCode == 46) {\r
12308 if (r.collapsed) {\r
12309 par = dom.getParent(sc, 'p,h1,h2,h3,h4,h5,h6,div');\r
12310\r
12311 if (par) {\r
12312 rng = dom.createRng();\r
12313\r
12314 rng.setStart(sc, r.startOffset);\r
12315 rng.setEndAfter(par);\r
12316\r
12317 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element\r
12318 if (dom.getOuterHTML(rng.cloneContents()).replace(/<[^>]+>/g, '').length == 0) {\r
12319 nextBlock = dom.getNext(par, 'p,h1,h2,h3,h4,h5,h6,div');\r
12320\r
12321 // Copy all children from next sibling block and remove it\r
12322 if (nextBlock) {\r
12323 each(nextBlock.childNodes, function(node) {\r
12324 par.appendChild(node.cloneNode(true));\r
12325 });\r
12326\r
12327 dom.remove(nextBlock);\r
12328 }\r
12329\r
12330 // Block the default even since the Gecko team might eventually fix this\r
12331 // We will remove this logic once they do we can't feature detect this one\r
12332 e.preventDefault();\r
12333 return;\r
12334 }\r
12335 }\r
12336 }\r
12337 }\r
12338 */\r
12339\r
12340 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
12341 // This workaround removes the element by hand and moves the caret to the previous element\r
12342 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
12343 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
12344 // Find previous block element\r
12345 n = sc;\r
12346 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
12347\r
12348 if (n) {\r
12349 if (sc != b.firstChild) {\r
12350 // Find last text node\r
12351 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
12352 while (tn = w.nextNode())\r
12353 n = tn;\r
12354\r
12355 // Place caret at the end of last text node\r
12356 r = ed.getDoc().createRange();\r
12357 r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
12358 r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
12359 se.setRng(r);\r
12360\r
12361 // Remove the target container\r
12362 ed.dom.remove(sc);\r
12363 }\r
12364\r
12365 return Event.cancel(e);\r
12366 }\r
12367 }\r
12368 }\r
12369\r
12370 // Gecko generates BR elements here and there, we don't like those so lets remove them\r
12371 function handler(e) {\r
12372 var pr;\r
12373\r
12374 e = e.target;\r
12375\r
12376 // A new BR was created in a block element, remove it\r
12377 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {\r
12378 pr = e.previousSibling;\r
12379\r
12380 Event.remove(b, 'DOMNodeInserted', handler);\r
12381\r
12382 // Is there whitespace at the end of the node before then we might need the pesky BR\r
12383 // to place the caret at a correct location see bug: #2013943\r
12384 if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))\r
12385 return;\r
12386\r
12387 // Only remove BR elements that got inserted in the middle of the text\r
12388 if (e.previousSibling || e.nextSibling)\r
12389 ed.dom.remove(e);\r
12390 }\r
12391 };\r
12392\r
12393 // Listen for new nodes\r
12394 Event._add(b, 'DOMNodeInserted', handler);\r
12395\r
12396 // Remove listener\r
12397 window.setTimeout(function() {\r
12398 Event._remove(b, 'DOMNodeInserted', handler);\r
12399 }, 1);\r
12400 }\r
12401 });\r
12402})(tinymce);\r
12403(function(tinymce) {\r
12404 // Shorten names\r
12405 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
12406\r
12407 tinymce.create('tinymce.ControlManager', {\r
12408 ControlManager : function(ed, s) {\r
12409 var t = this, i;\r
12410\r
12411 s = s || {};\r
12412 t.editor = ed;\r
12413 t.controls = {};\r
12414 t.onAdd = new tinymce.util.Dispatcher(t);\r
12415 t.onPostRender = new tinymce.util.Dispatcher(t);\r
12416 t.prefix = s.prefix || ed.id + '_';\r
12417 t._cls = {};\r
12418\r
12419 t.onPostRender.add(function() {\r
12420 each(t.controls, function(c) {\r
12421 c.postRender();\r
12422 });\r
12423 });\r
12424 },\r
12425\r
12426 get : function(id) {\r
12427 return this.controls[this.prefix + id] || this.controls[id];\r
12428 },\r
12429\r
12430 setActive : function(id, s) {\r
12431 var c = null;\r
12432\r
12433 if (c = this.get(id))\r
12434 c.setActive(s);\r
12435\r
12436 return c;\r
12437 },\r
12438\r
12439 setDisabled : function(id, s) {\r
12440 var c = null;\r
12441\r
12442 if (c = this.get(id))\r
12443 c.setDisabled(s);\r
12444\r
12445 return c;\r
12446 },\r
12447\r
12448 add : function(c) {\r
12449 var t = this;\r
12450\r
12451 if (c) {\r
12452 t.controls[c.id] = c;\r
12453 t.onAdd.dispatch(c, t);\r
12454 }\r
12455\r
12456 return c;\r
12457 },\r
12458\r
12459 createControl : function(n) {\r
12460 var c, t = this, ed = t.editor;\r
12461\r
12462 each(ed.plugins, function(p) {\r
12463 if (p.createControl) {\r
12464 c = p.createControl(n, t);\r
12465\r
12466 if (c)\r
12467 return false;\r
12468 }\r
12469 });\r
12470\r
12471 switch (n) {\r
12472 case "|":\r
12473 case "separator":\r
12474 return t.createSeparator();\r
12475 }\r
12476\r
12477 if (!c && ed.buttons && (c = ed.buttons[n]))\r
12478 return t.createButton(n, c);\r
12479\r
12480 return t.add(c);\r
12481 },\r
12482\r
12483 createDropMenu : function(id, s, cc) {\r
12484 var t = this, ed = t.editor, c, bm, v, cls;\r
12485\r
12486 s = extend({\r
12487 'class' : 'mceDropDown',\r
12488 constrain : ed.settings.constrain_menus\r
12489 }, s);\r
12490\r
12491 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
12492 if (v = ed.getParam('skin_variant'))\r
12493 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
12494\r
12495 id = t.prefix + id;\r
12496 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
12497 c = t.controls[id] = new cls(id, s);\r
12498 c.onAddItem.add(function(c, o) {\r
12499 var s = o.settings;\r
12500\r
12501 s.title = ed.getLang(s.title, s.title);\r
12502\r
12503 if (!s.onclick) {\r
12504 s.onclick = function(v) {\r
12505 ed.execCommand(s.cmd, s.ui || false, s.value);\r
12506 };\r
12507 }\r
12508 });\r
12509\r
12510 ed.onRemove.add(function() {\r
12511 c.destroy();\r
12512 });\r
12513\r
12514 // Fix for bug #1897785, #1898007\r
12515 if (tinymce.isIE) {\r
12516 c.onShowMenu.add(function() {\r
12517 // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12518 ed.focus();\r
12519\r
12520 bm = ed.selection.getBookmark(1);\r
12521 });\r
12522\r
12523 c.onHideMenu.add(function() {\r
12524 if (bm) {\r
12525 ed.selection.moveToBookmark(bm);\r
12526 bm = 0;\r
12527 }\r
12528 });\r
12529 }\r
12530\r
12531 return t.add(c);\r
12532 },\r
12533\r
12534 createListBox : function(id, s, cc) {\r
12535 var t = this, ed = t.editor, cmd, c, cls;\r
12536\r
12537 if (t.get(id))\r
12538 return null;\r
12539\r
12540 s.title = ed.translate(s.title);\r
12541 s.scope = s.scope || ed;\r
12542\r
12543 if (!s.onselect) {\r
12544 s.onselect = function(v) {\r
12545 ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12546 };\r
12547 }\r
12548\r
12549 s = extend({\r
12550 title : s.title,\r
12551 'class' : 'mce_' + id,\r
12552 scope : s.scope,\r
12553 control_manager : t\r
12554 }, s);\r
12555\r
12556 id = t.prefix + id;\r
12557\r
12558 if (ed.settings.use_native_selects)\r
12559 c = new tinymce.ui.NativeListBox(id, s);\r
12560 else {\r
12561 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
12562 c = new cls(id, s);\r
12563 }\r
12564\r
12565 t.controls[id] = c;\r
12566\r
12567 // Fix focus problem in Safari\r
12568 if (tinymce.isWebKit) {\r
12569 c.onPostRender.add(function(c, n) {\r
12570 // Store bookmark on mousedown\r
12571 Event.add(n, 'mousedown', function() {\r
12572 ed.bookmark = ed.selection.getBookmark(1);\r
12573 });\r
12574\r
12575 // Restore on focus, since it might be lost\r
12576 Event.add(n, 'focus', function() {\r
12577 ed.selection.moveToBookmark(ed.bookmark);\r
12578 ed.bookmark = null;\r
12579 });\r
12580 });\r
12581 }\r
12582\r
12583 if (c.hideMenu)\r
12584 ed.onMouseDown.add(c.hideMenu, c);\r
12585\r
12586 return t.add(c);\r
12587 },\r
12588\r
12589 createButton : function(id, s, cc) {\r
12590 var t = this, ed = t.editor, o, c, cls;\r
12591\r
12592 if (t.get(id))\r
12593 return null;\r
12594\r
12595 s.title = ed.translate(s.title);\r
12596 s.label = ed.translate(s.label);\r
12597 s.scope = s.scope || ed;\r
12598\r
12599 if (!s.onclick && !s.menu_button) {\r
12600 s.onclick = function() {\r
12601 ed.execCommand(s.cmd, s.ui || false, s.value);\r
12602 };\r
12603 }\r
12604\r
12605 s = extend({\r
12606 title : s.title,\r
12607 'class' : 'mce_' + id,\r
12608 unavailable_prefix : ed.getLang('unavailable', ''),\r
12609 scope : s.scope,\r
12610 control_manager : t\r
12611 }, s);\r
12612\r
12613 id = t.prefix + id;\r
12614\r
12615 if (s.menu_button) {\r
12616 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
12617 c = new cls(id, s);\r
12618 ed.onMouseDown.add(c.hideMenu, c);\r
12619 } else {\r
12620 cls = t._cls.button || tinymce.ui.Button;\r
12621 c = new cls(id, s);\r
12622 }\r
12623\r
12624 return t.add(c);\r
12625 },\r
12626\r
12627 createMenuButton : function(id, s, cc) {\r
12628 s = s || {};\r
12629 s.menu_button = 1;\r
12630\r
12631 return this.createButton(id, s, cc);\r
12632 },\r
12633\r
12634 createSplitButton : function(id, s, cc) {\r
12635 var t = this, ed = t.editor, cmd, c, cls;\r
12636\r
12637 if (t.get(id))\r
12638 return null;\r
12639\r
12640 s.title = ed.translate(s.title);\r
12641 s.scope = s.scope || ed;\r
12642\r
12643 if (!s.onclick) {\r
12644 s.onclick = function(v) {\r
12645 ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12646 };\r
12647 }\r
12648\r
12649 if (!s.onselect) {\r
12650 s.onselect = function(v) {\r
12651 ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12652 };\r
12653 }\r
12654\r
12655 s = extend({\r
12656 title : s.title,\r
12657 'class' : 'mce_' + id,\r
12658 scope : s.scope,\r
12659 control_manager : t\r
12660 }, s);\r
12661\r
12662 id = t.prefix + id;\r
12663 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
12664 c = t.add(new cls(id, s));\r
12665 ed.onMouseDown.add(c.hideMenu, c);\r
12666\r
12667 return c;\r
12668 },\r
12669\r
12670 createColorSplitButton : function(id, s, cc) {\r
12671 var t = this, ed = t.editor, cmd, c, cls, bm;\r
12672\r
12673 if (t.get(id))\r
12674 return null;\r
12675\r
12676 s.title = ed.translate(s.title);\r
12677 s.scope = s.scope || ed;\r
12678\r
12679 if (!s.onclick) {\r
12680 s.onclick = function(v) {\r
12681 if (tinymce.isIE)\r
12682 bm = ed.selection.getBookmark(1);\r
12683\r
12684 ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12685 };\r
12686 }\r
12687\r
12688 if (!s.onselect) {\r
12689 s.onselect = function(v) {\r
12690 ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12691 };\r
12692 }\r
12693\r
12694 s = extend({\r
12695 title : s.title,\r
12696 'class' : 'mce_' + id,\r
12697 'menu_class' : ed.getParam('skin') + 'Skin',\r
12698 scope : s.scope,\r
12699 more_colors_title : ed.getLang('more_colors')\r
12700 }, s);\r
12701\r
12702 id = t.prefix + id;\r
12703 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
12704 c = new cls(id, s);\r
12705 ed.onMouseDown.add(c.hideMenu, c);\r
12706\r
12707 // Remove the menu element when the editor is removed\r
12708 ed.onRemove.add(function() {\r
12709 c.destroy();\r
12710 });\r
12711\r
12712 // Fix for bug #1897785, #1898007\r
12713 if (tinymce.isIE) {\r
12714 c.onShowMenu.add(function() {\r
12715 // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12716 ed.focus();\r
12717 bm = ed.selection.getBookmark(1);\r
12718 });\r
12719\r
12720 c.onHideMenu.add(function() {\r
12721 if (bm) {\r
12722 ed.selection.moveToBookmark(bm);\r
12723 bm = 0;\r
12724 }\r
12725 });\r
12726 }\r
12727\r
12728 return t.add(c);\r
12729 },\r
12730\r
12731 createToolbar : function(id, s, cc) {\r
12732 var c, t = this, cls;\r
12733\r
12734 id = t.prefix + id;\r
12735 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
12736 c = new cls(id, s);\r
12737\r
12738 if (t.get(id))\r
12739 return null;\r
12740\r
12741 return t.add(c);\r
12742 },\r
12743\r
12744 createSeparator : function(cc) {\r
12745 var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
12746\r
12747 return new cls();\r
12748 },\r
12749\r
12750 setControlType : function(n, c) {\r
12751 return this._cls[n.toLowerCase()] = c;\r
12752 },\r
12753\r
12754 destroy : function() {\r
12755 each(this.controls, function(c) {\r
12756 c.destroy();\r
12757 });\r
12758\r
12759 this.controls = null;\r
12760 }\r
12761 });\r
12762})(tinymce);\r
12763(function(tinymce) {\r
12764 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
12765\r
12766 tinymce.create('tinymce.WindowManager', {\r
12767 WindowManager : function(ed) {\r
12768 var t = this;\r
12769\r
12770 t.editor = ed;\r
12771 t.onOpen = new Dispatcher(t);\r
12772 t.onClose = new Dispatcher(t);\r
12773 t.params = {};\r
12774 t.features = {};\r
12775 },\r
12776\r
12777 open : function(s, p) {\r
12778 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
12779\r
12780 // Default some options\r
12781 s = s || {};\r
12782 p = p || {};\r
12783 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
12784 sh = isOpera ? vp.h : screen.height;\r
12785 s.name = s.name || 'mc_' + new Date().getTime();\r
12786 s.width = parseInt(s.width || 320);\r
12787 s.height = parseInt(s.height || 240);\r
12788 s.resizable = true;\r
12789 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
12790 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
12791 p.inline = false;\r
12792 p.mce_width = s.width;\r
12793 p.mce_height = s.height;\r
12794 p.mce_auto_focus = s.auto_focus;\r
12795\r
12796 if (mo) {\r
12797 if (isIE) {\r
12798 s.center = true;\r
12799 s.help = false;\r
12800 s.dialogWidth = s.width + 'px';\r
12801 s.dialogHeight = s.height + 'px';\r
12802 s.scroll = s.scrollbars || false;\r
12803 }\r
12804 }\r
12805\r
12806 // Build features string\r
12807 each(s, function(v, k) {\r
12808 if (tinymce.is(v, 'boolean'))\r
12809 v = v ? 'yes' : 'no';\r
12810\r
12811 if (!/^(name|url)$/.test(k)) {\r
12812 if (isIE && mo)\r
12813 f += (f ? ';' : '') + k + ':' + v;\r
12814 else\r
12815 f += (f ? ',' : '') + k + '=' + v;\r
12816 }\r
12817 });\r
12818\r
12819 t.features = s;\r
12820 t.params = p;\r
12821 t.onOpen.dispatch(t, s, p);\r
12822\r
12823 u = s.url || s.file;\r
12824 u = tinymce._addVer(u);\r
12825\r
12826 try {\r
12827 if (isIE && mo) {\r
12828 w = 1;\r
12829 window.showModalDialog(u, window, f);\r
12830 } else\r
12831 w = window.open(u, s.name, f);\r
12832 } catch (ex) {\r
12833 // Ignore\r
12834 }\r
12835\r
12836 if (!w)\r
12837 alert(t.editor.getLang('popup_blocked'));\r
12838 },\r
12839\r
12840 close : function(w) {\r
12841 w.close();\r
12842 this.onClose.dispatch(this);\r
12843 },\r
12844\r
12845 createInstance : function(cl, a, b, c, d, e) {\r
12846 var f = tinymce.resolve(cl);\r
12847\r
12848 return new f(a, b, c, d, e);\r
12849 },\r
12850\r
12851 confirm : function(t, cb, s, w) {\r
12852 w = w || window;\r
12853\r
12854 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
12855 },\r
12856\r
12857 alert : function(tx, cb, s, w) {\r
12858 var t = this;\r
12859\r
12860 w = w || window;\r
12861 w.alert(t._decode(t.editor.getLang(tx, tx)));\r
12862\r
12863 if (cb)\r
12864 cb.call(s || t);\r
12865 },\r
12866\r
12867 // Internal functions\r
12868\r
12869 _decode : function(s) {\r
12870 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
12871 }\r
12872 });\r
12873}(tinymce));(function(tinymce) {\r
12874 tinymce.CommandManager = function() {\r
12875 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
12876\r
12877 function add(collection, cmd, func, scope) {\r
12878 if (typeof(cmd) == 'string')\r
12879 cmd = [cmd];\r
12880\r
12881 tinymce.each(cmd, function(cmd) {\r
12882 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
12883 });\r
12884 };\r
12885\r
12886 tinymce.extend(this, {\r
12887 add : function(cmd, func, scope) {\r
12888 add(execCommands, cmd, func, scope);\r
12889 },\r
12890\r
12891 addQueryStateHandler : function(cmd, func, scope) {\r
12892 add(queryStateCommands, cmd, func, scope);\r
12893 },\r
12894\r
12895 addQueryValueHandler : function(cmd, func, scope) {\r
12896 add(queryValueCommands, cmd, func, scope);\r
12897 },\r
12898\r
12899 execCommand : function(scope, cmd, ui, value, args) {\r
12900 if (cmd = execCommands[cmd.toLowerCase()]) {\r
12901 if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
12902 return true;\r
12903 }\r
12904 },\r
12905\r
12906 queryCommandValue : function() {\r
12907 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
12908 return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12909 },\r
12910\r
12911 queryCommandState : function() {\r
12912 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
12913 return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12914 }\r
12915 });\r
12916 };\r
12917\r
12918 tinymce.GlobalCommands = new tinymce.CommandManager();\r
12919})(tinymce);(function(tinymce) {\r
12920 function processRange(dom, start, end, callback) {\r
12921 var ancestor, n, startPoint, endPoint, sib;\r
12922\r
12923 function findEndPoint(n, c) {\r
12924 do {\r
12925 if (n.parentNode == c)\r
12926 return n;\r
12927\r
12928 n = n.parentNode;\r
12929 } while(n);\r
12930 };\r
12931\r
12932 function process(n) {\r
12933 callback(n);\r
12934 tinymce.walk(n, callback, 'childNodes');\r
12935 };\r
12936\r
12937 // Find common ancestor and end points\r
12938 ancestor = dom.findCommonAncestor(start, end);\r
12939 startPoint = findEndPoint(start, ancestor) || start;\r
12940 endPoint = findEndPoint(end, ancestor) || end;\r
12941\r
12942 // Process left leaf\r
12943 for (n = start; n && n != startPoint; n = n.parentNode) {\r
12944 for (sib = n.nextSibling; sib; sib = sib.nextSibling)\r
12945 process(sib);\r
12946 }\r
12947\r
12948 // Process middle from start to end point\r
12949 if (startPoint != endPoint) {\r
12950 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling)\r
12951 process(n);\r
12952 } else\r
12953 process(startPoint);\r
12954\r
12955 // Process right leaf\r
12956 for (n = end; n && n != endPoint; n = n.parentNode) {\r
12957 for (sib = n.previousSibling; sib; sib = sib.previousSibling)\r
12958 process(sib);\r
12959 }\r
12960 };\r
12961\r
12962 tinymce.GlobalCommands.add('RemoveFormat', function() {\r
12963 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n;\r
12964\r
12965 function findFormatRoot(n) {\r
12966 var sp;\r
12967\r
12968 dom.getParent(n, function(n) {\r
12969 if (dom.is(n, ed.getParam('removeformat_selector')))\r
12970 sp = n;\r
12971\r
12972 return dom.isBlock(n);\r
12973 }, ed.getBody());\r
12974\r
12975 return sp;\r
12976 };\r
12977\r
12978 function collect(n) {\r
12979 if (dom.is(n, ed.getParam('removeformat_selector')))\r
12980 nodes.push(n);\r
12981 };\r
12982\r
12983 function walk(n) {\r
12984 collect(n);\r
12985 tinymce.walk(n, collect, 'childNodes');\r
12986 };\r
12987\r
12988 bm = s.getBookmark();\r
12989 sc = r.startContainer;\r
12990 ec = r.endContainer;\r
12991 so = r.startOffset;\r
12992 eo = r.endOffset;\r
12993 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;\r
12994 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;\r
12995\r
12996 // Same container\r
12997 if (sc == ec) { // TEXT_NODE\r
12998 start = findFormatRoot(sc);\r
12999\r
13000 // Handle single text node\r
13001 if (sc.nodeType == 3) {\r
13002 if (start && start.nodeType == 1) { // ELEMENT\r
13003 n = sc.splitText(so);\r
13004 n.splitText(eo - so);\r
13005 dom.split(start, n);\r
13006\r
13007 s.moveToBookmark(bm);\r
13008 }\r
13009\r
13010 return;\r
13011 }\r
13012\r
13013 // Handle single element\r
13014 walk(dom.split(start, sc) || sc);\r
13015 } else {\r
13016 // Find start/end format root\r
13017 start = findFormatRoot(sc);\r
13018 end = findFormatRoot(ec);\r
13019\r
13020 // Split start text node\r
13021 if (start) {\r
13022 if (sc.nodeType == 3) { // TEXT\r
13023 // Since IE doesn't support white space nodes in the DOM we need to\r
13024 // add this invisible character so that the splitText function can split the contents\r
13025 if (so == sc.nodeValue.length)\r
13026 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix\r
13027\r
13028 sc = sc.splitText(so);\r
13029 }\r
13030 }\r
13031\r
13032 // Split end text node\r
13033 if (end) {\r
13034 if (ec.nodeType == 3) // TEXT\r
13035 ec.splitText(eo);\r
13036 }\r
13037\r
13038 // If the start and end format root is the same then we need to wrap\r
13039 // the end node in a span since the split calls might change the reference\r
13040 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p>\r
13041 if (start && start == end)\r
13042 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec);\r
13043\r
13044 // Split all start containers down to the format root\r
13045 if (start)\r
13046 start = dom.split(start, sc);\r
13047 else\r
13048 start = sc;\r
13049\r
13050 // If there is a span wrapper use that one instead\r
13051 if (n = dom.get('__end')) {\r
13052 ec = n;\r
13053 end = findFormatRoot(ec);\r
13054 }\r
13055\r
13056 // Split all end containers down to the format root\r
13057 if (end)\r
13058 end = dom.split(end, ec);\r
13059 else\r
13060 end = ec;\r
13061\r
13062 // Collect nodes in between\r
13063 processRange(dom, start, end, collect);\r
13064\r
13065 // Remove invisible character for IE workaround if we find it\r
13066 if (sc.nodeValue == '\uFEFF')\r
13067 sc.nodeValue = '';\r
13068\r
13069 // Process start/end container elements\r
13070 walk(ec);\r
13071 walk(sc);\r
13072 }\r
13073\r
13074 // Remove all collected nodes\r
13075 tinymce.each(nodes, function(n) {\r
13076 dom.remove(n, 1);\r
13077 });\r
13078\r
13079 // Remove leftover wrapper\r
13080 dom.remove('__end', 1);\r
13081\r
13082 s.moveToBookmark(bm);\r
13083 });\r
13084})(tinymce);\r
13085(function(tinymce) {\r
13086 tinymce.GlobalCommands.add('mceBlockQuote', function() {\r
13087 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;\r
13088\r
13089 function getBQ(e) {\r
13090 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});\r
13091 };\r
13092\r
13093 // Get start/end block\r
13094 sb = dom.getParent(s.getStart(), dom.isBlock);\r
13095 eb = dom.getParent(s.getEnd(), dom.isBlock);\r
13096\r
13097 // Remove blockquote(s)\r
13098 if (bq = getBQ(sb)) {\r
13099 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))\r
13100 bm = s.getBookmark();\r
13101\r
13102 // Move all elements after the end block into new bq\r
13103 if (getBQ(eb)) {\r
13104 bq2 = bq.cloneNode(false);\r
13105\r
13106 while (n = eb.nextSibling)\r
13107 bq2.appendChild(n.parentNode.removeChild(n));\r
13108 }\r
13109\r
13110 // Add new bq after\r
13111 if (bq2)\r
13112 dom.insertAfter(bq2, bq);\r
13113\r
13114 // Move all selected blocks after the current bq\r
13115 nl = s.getSelectedBlocks(sb, eb);\r
13116 for (i = nl.length - 1; i >= 0; i--) {\r
13117 dom.insertAfter(nl[i], bq);\r
13118 }\r
13119\r
13120 // Empty bq, then remove it\r
13121 if (/^\s*$/.test(bq.innerHTML))\r
13122 dom.remove(bq, 1); // Keep children so boomark restoration works correctly\r
13123\r
13124 // Empty bq, then remote it\r
13125 if (bq2 && /^\s*$/.test(bq2.innerHTML))\r
13126 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly\r
13127\r
13128 if (!bm) {\r
13129 // Move caret inside empty block element\r
13130 if (!tinymce.isIE) {\r
13131 r = ed.getDoc().createRange();\r
13132 r.setStart(sb, 0);\r
13133 r.setEnd(sb, 0);\r
13134 s.setRng(r);\r
13135 } else {\r
13136 s.select(sb);\r
13137 s.collapse(0);\r
13138\r
13139 // IE misses the empty block some times element so we must move back the caret\r
13140 if (dom.getParent(s.getStart(), dom.isBlock) != sb) {\r
13141 r = s.getRng();\r
13142 r.move('character', -1);\r
13143 r.select();\r
13144 }\r
13145 }\r
13146 } else\r
13147 ed.selection.moveToBookmark(bm);\r
13148\r
13149 return;\r
13150 }\r
13151\r
13152 // Since IE can start with a totally empty document we need to add the first bq and paragraph\r
13153 if (tinymce.isIE && !sb && !eb) {\r
13154 ed.getDoc().execCommand('Indent');\r
13155 n = getBQ(s.getNode());\r
13156 n.style.margin = n.dir = ''; // IE adds margin and dir to bq\r
13157 return;\r
13158 }\r
13159\r
13160 if (!sb || !eb)\r
13161 return;\r
13162\r
13163 // If empty paragraph node then do not use bookmark\r
13164 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))\r
13165 bm = s.getBookmark();\r
13166\r
13167 // Move selected block elements into a bq\r
13168 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {\r
13169 // Found existing BQ add to this one\r
13170 if (e.nodeName == 'BLOCKQUOTE' && !bq) {\r
13171 bq = e;\r
13172 return;\r
13173 }\r
13174\r
13175 // No BQ found, create one\r
13176 if (!bq) {\r
13177 bq = dom.create('blockquote');\r
13178 e.parentNode.insertBefore(bq, e);\r
13179 }\r
13180\r
13181 // Add children from existing BQ\r
13182 if (e.nodeName == 'BLOCKQUOTE' && bq) {\r
13183 n = e.firstChild;\r
13184\r
13185 while (n) {\r
13186 bq.appendChild(n.cloneNode(true));\r
13187 n = n.nextSibling;\r
13188 }\r
13189\r
13190 dom.remove(e);\r
13191 return;\r
13192 }\r
13193\r
13194 // Add non BQ element to BQ\r
13195 bq.appendChild(dom.remove(e));\r
13196 });\r
13197\r
13198 if (!bm) {\r
13199 // Move caret inside empty block element\r
13200 if (!tinymce.isIE) {\r
13201 r = ed.getDoc().createRange();\r
13202 r.setStart(sb, 0);\r
13203 r.setEnd(sb, 0);\r
13204 s.setRng(r);\r
13205 } else {\r
13206 s.select(sb);\r
13207 s.collapse(1);\r
13208 }\r
13209 } else\r
13210 s.moveToBookmark(bm);\r
13211 });\r
13212})(tinymce);\r
13213(function(tinymce) {\r
13214 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) {\r
13215 tinymce.GlobalCommands.add(cmd, function() {\r
13216 var ed = this, doc = ed.getDoc();\r
13217\r
13218 try {\r
13219 doc.execCommand(cmd, false, null);\r
13220\r
13221 // On WebKit the command will just be ignored if it's not enabled\r
13222 if (!doc.queryCommandEnabled(cmd))\r
13223 throw 'Error';\r
13224 } catch (ex) {\r
13225 if (tinymce.isGecko) {\r
13226 ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {\r
13227 if (s)\r
13228 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
13229 });\r
13230 } else\r
13231 ed.windowManager.alert(ed.getLang('clipboard_no_support'));\r
13232 }\r
13233 });\r
13234 });\r
13235})(tinymce);\r
13236(function(tinymce) {\r
13237 tinymce.GlobalCommands.add('InsertHorizontalRule', function() {\r
13238 if (tinymce.isOpera)\r
13239 return this.getDoc().execCommand('InsertHorizontalRule', false, '');\r
13240\r
13241 this.selection.setContent('<hr />');\r
13242 });\r
13243})(tinymce);\r
13244(function() {\r
13245 var cmds = tinymce.GlobalCommands;\r
13246\r
13247 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() {\r
13248 this.undoManager.add();\r
13249 });\r
13250\r
13251 cmds.add('Undo', function() {\r
13252 var ed = this;\r
13253\r
13254 if (ed.settings.custom_undo_redo) {\r
13255 ed.undoManager.undo();\r
13256 ed.nodeChanged();\r
13257 return true;\r
13258 }\r
13259\r
13260 return false; // Run browser command\r
13261 });\r
13262\r
13263 cmds.add('Redo', function() {\r
13264 var ed = this;\r
13265\r
13266 if (ed.settings.custom_undo_redo) {\r
13267 ed.undoManager.redo();\r
13268 ed.nodeChanged();\r
13269 return true;\r
13270 }\r
13271\r
13272 return false; // Run browser command\r
13273 });\r
13274})();\r