fix salary JS calculation, ordering classement
[auf_rh_dae.git] / project / assets / js / tiny_mce / tiny_mce_src.js
CommitLineData
ec517164 1(function(win) {\r
2 var whiteSpaceRe = /^\s*|\s*$/g,\r
3 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';\r
4\r
5 var tinymce = {\r
6 majorVersion : '3',\r
7\r
8 minorVersion : '3.9.4',\r
9\r
10 releaseDate : '2011-04-13',\r
11\r
12 _init : function() {\r
13 var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
14\r
15 t.isOpera = win.opera && opera.buildNumber;\r
16\r
17 t.isWebKit = /WebKit/.test(ua);\r
18\r
19 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
20\r
21 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
22\r
23 t.isIE9 = t.isIE && !/MSIE [5678]/.test(ua);\r
24\r
25 t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
26\r
27 t.isMac = ua.indexOf('Mac') != -1;\r
28\r
29 t.isAir = /adobeair/i.test(ua);\r
30\r
31 t.isIDevice = /(iPad|iPhone)/.test(ua);\r
32\r
33 // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
34 if (win.tinyMCEPreInit) {\r
35 t.suffix = tinyMCEPreInit.suffix;\r
36 t.baseURL = tinyMCEPreInit.base;\r
37 t.query = tinyMCEPreInit.query;\r
38 return;\r
39 }\r
40\r
41 // Get suffix and base\r
42 t.suffix = '';\r
43\r
44 // If base element found, add that infront of baseURL\r
45 nl = d.getElementsByTagName('base');\r
46 for (i=0; i<nl.length; i++) {\r
47 if (v = nl[i].href) {\r
48 // Host only value like http://site.com or http://site.com:8008\r
49 if (/^https?:\/\/[^\/]+$/.test(v))\r
50 v += '/';\r
51\r
52 base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
53 }\r
54 }\r
55\r
56 function getBase(n) {\r
57 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {\r
58 if (/_(src|dev)\.js/g.test(n.src))\r
59 t.suffix = '_src';\r
60\r
61 if ((p = n.src.indexOf('?')) != -1)\r
62 t.query = n.src.substring(p + 1);\r
63\r
64 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
65\r
66 // If path to script is relative and a base href was found add that one infront\r
67 // the src property will always be an absolute one on non IE browsers and IE 8\r
68 // so this logic will basically only be executed on older IE versions\r
69 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
70 t.baseURL = base + t.baseURL;\r
71\r
72 return t.baseURL;\r
73 }\r
74\r
75 return null;\r
76 };\r
77\r
78 // Check document\r
79 nl = d.getElementsByTagName('script');\r
80 for (i=0; i<nl.length; i++) {\r
81 if (getBase(nl[i]))\r
82 return;\r
83 }\r
84\r
85 // Check head\r
86 n = d.getElementsByTagName('head')[0];\r
87 if (n) {\r
88 nl = n.getElementsByTagName('script');\r
89 for (i=0; i<nl.length; i++) {\r
90 if (getBase(nl[i]))\r
91 return;\r
92 }\r
93 }\r
94\r
95 return;\r
96 },\r
97\r
98 is : function(o, t) {\r
99 if (!t)\r
100 return o !== undefined;\r
101\r
102 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
103 return true;\r
104\r
105 return typeof(o) == t;\r
106 },\r
107\r
108 each : function(o, cb, s) {\r
109 var n, l;\r
110\r
111 if (!o)\r
112 return 0;\r
113\r
114 s = s || o;\r
115\r
116 if (o.length !== undefined) {\r
117 // Indexed arrays, needed for Safari\r
118 for (n=0, l = o.length; n < l; n++) {\r
119 if (cb.call(s, o[n], n, o) === false)\r
120 return 0;\r
121 }\r
122 } else {\r
123 // Hashtables\r
124 for (n in o) {\r
125 if (o.hasOwnProperty(n)) {\r
126 if (cb.call(s, o[n], n, o) === false)\r
127 return 0;\r
128 }\r
129 }\r
130 }\r
131\r
132 return 1;\r
133 },\r
134\r
135\r
136 map : function(a, f) {\r
137 var o = [];\r
138\r
139 tinymce.each(a, function(v) {\r
140 o.push(f(v));\r
141 });\r
142\r
143 return o;\r
144 },\r
145\r
146 grep : function(a, f) {\r
147 var o = [];\r
148\r
149 tinymce.each(a, function(v) {\r
150 if (!f || f(v))\r
151 o.push(v);\r
152 });\r
153\r
154 return o;\r
155 },\r
156\r
157 inArray : function(a, v) {\r
158 var i, l;\r
159\r
160 if (a) {\r
161 for (i = 0, l = a.length; i < l; i++) {\r
162 if (a[i] === v)\r
163 return i;\r
164 }\r
165 }\r
166\r
167 return -1;\r
168 },\r
169\r
170 extend : function(o, e) {\r
171 var i, l, a = arguments;\r
172\r
173 for (i = 1, l = a.length; i < l; i++) {\r
174 e = a[i];\r
175\r
176 tinymce.each(e, function(v, n) {\r
177 if (v !== undefined)\r
178 o[n] = v;\r
179 });\r
180 }\r
181\r
182 return o;\r
183 },\r
184\r
185\r
186 trim : function(s) {\r
187 return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
188 },\r
189\r
190 create : function(s, p) {\r
191 var t = this, sp, ns, cn, scn, c, de = 0;\r
192\r
193 // Parse : <prefix> <class>:<super class>\r
194 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
195 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
196\r
197 // Create namespace for new class\r
198 ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
199\r
200 // Class already exists\r
201 if (ns[cn])\r
202 return;\r
203\r
204 // Make pure static class\r
205 if (s[2] == 'static') {\r
206 ns[cn] = p;\r
207\r
208 if (this.onCreate)\r
209 this.onCreate(s[2], s[3], ns[cn]);\r
210\r
211 return;\r
212 }\r
213\r
214 // Create default constructor\r
215 if (!p[cn]) {\r
216 p[cn] = function() {};\r
217 de = 1;\r
218 }\r
219\r
220 // Add constructor and methods\r
221 ns[cn] = p[cn];\r
222 t.extend(ns[cn].prototype, p);\r
223\r
224 // Extend\r
225 if (s[5]) {\r
226 sp = t.resolve(s[5]).prototype;\r
227 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
228\r
229 // Extend constructor\r
230 c = ns[cn];\r
231 if (de) {\r
232 // Add passthrough constructor\r
233 ns[cn] = function() {\r
234 return sp[scn].apply(this, arguments);\r
235 };\r
236 } else {\r
237 // Add inherit constructor\r
238 ns[cn] = function() {\r
239 this.parent = sp[scn];\r
240 return c.apply(this, arguments);\r
241 };\r
242 }\r
243 ns[cn].prototype[cn] = ns[cn];\r
244\r
245 // Add super methods\r
246 t.each(sp, function(f, n) {\r
247 ns[cn].prototype[n] = sp[n];\r
248 });\r
249\r
250 // Add overridden methods\r
251 t.each(p, function(f, n) {\r
252 // Extend methods if needed\r
253 if (sp[n]) {\r
254 ns[cn].prototype[n] = function() {\r
255 this.parent = sp[n];\r
256 return f.apply(this, arguments);\r
257 };\r
258 } else {\r
259 if (n != cn)\r
260 ns[cn].prototype[n] = f;\r
261 }\r
262 });\r
263 }\r
264\r
265 // Add static methods\r
266 t.each(p['static'], function(f, n) {\r
267 ns[cn][n] = f;\r
268 });\r
269\r
270 if (this.onCreate)\r
271 this.onCreate(s[2], s[3], ns[cn].prototype);\r
272 },\r
273\r
274 walk : function(o, f, n, s) {\r
275 s = s || this;\r
276\r
277 if (o) {\r
278 if (n)\r
279 o = o[n];\r
280\r
281 tinymce.each(o, function(o, i) {\r
282 if (f.call(s, o, i, n) === false)\r
283 return false;\r
284\r
285 tinymce.walk(o, f, n, s);\r
286 });\r
287 }\r
288 },\r
289\r
290 createNS : function(n, o) {\r
291 var i, v;\r
292\r
293 o = o || win;\r
294\r
295 n = n.split('.');\r
296 for (i=0; i<n.length; i++) {\r
297 v = n[i];\r
298\r
299 if (!o[v])\r
300 o[v] = {};\r
301\r
302 o = o[v];\r
303 }\r
304\r
305 return o;\r
306 },\r
307\r
308 resolve : function(n, o) {\r
309 var i, l;\r
310\r
311 o = o || win;\r
312\r
313 n = n.split('.');\r
314 for (i = 0, l = n.length; i < l; i++) {\r
315 o = o[n[i]];\r
316\r
317 if (!o)\r
318 break;\r
319 }\r
320\r
321 return o;\r
322 },\r
323\r
324 addUnload : function(f, s) {\r
325 var t = this;\r
326\r
327 f = {func : f, scope : s || this};\r
328\r
329 if (!t.unloads) {\r
330 function unload() {\r
331 var li = t.unloads, o, n;\r
332\r
333 if (li) {\r
334 // Call unload handlers\r
335 for (n in li) {\r
336 o = li[n];\r
337\r
338 if (o && o.func)\r
339 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
340 }\r
341\r
342 // Detach unload function\r
343 if (win.detachEvent) {\r
344 win.detachEvent('onbeforeunload', fakeUnload);\r
345 win.detachEvent('onunload', unload);\r
346 } else if (win.removeEventListener)\r
347 win.removeEventListener('unload', unload, false);\r
348\r
349 // Destroy references\r
350 t.unloads = o = li = w = unload = 0;\r
351\r
352 // Run garbarge collector on IE\r
353 if (win.CollectGarbage)\r
354 CollectGarbage();\r
355 }\r
356 };\r
357\r
358 function fakeUnload() {\r
359 var d = document;\r
360\r
361 // Is there things still loading, then do some magic\r
362 if (d.readyState == 'interactive') {\r
363 function stop() {\r
364 // Prevent memory leak\r
365 d.detachEvent('onstop', stop);\r
366\r
367 // Call unload handler\r
368 if (unload)\r
369 unload();\r
370\r
371 d = 0;\r
372 };\r
373\r
374 // Fire unload when the currently loading page is stopped\r
375 if (d)\r
376 d.attachEvent('onstop', stop);\r
377\r
378 // Remove onstop listener after a while to prevent the unload function\r
379 // to execute if the user presses cancel in an onbeforeunload\r
380 // confirm dialog and then presses the browser stop button\r
381 win.setTimeout(function() {\r
382 if (d)\r
383 d.detachEvent('onstop', stop);\r
384 }, 0);\r
385 }\r
386 };\r
387\r
388 // Attach unload handler\r
389 if (win.attachEvent) {\r
390 win.attachEvent('onunload', unload);\r
391 win.attachEvent('onbeforeunload', fakeUnload);\r
392 } else if (win.addEventListener)\r
393 win.addEventListener('unload', unload, false);\r
394\r
395 // Setup initial unload handler array\r
396 t.unloads = [f];\r
397 } else\r
398 t.unloads.push(f);\r
399\r
400 return f;\r
401 },\r
402\r
403 removeUnload : function(f) {\r
404 var u = this.unloads, r = null;\r
405\r
406 tinymce.each(u, function(o, i) {\r
407 if (o && o.func == f) {\r
408 u.splice(i, 1);\r
409 r = f;\r
410 return false;\r
411 }\r
412 });\r
413\r
414 return r;\r
415 },\r
416\r
417 explode : function(s, d) {\r
418 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
419 },\r
420\r
421 _addVer : function(u) {\r
422 var v;\r
423\r
424 if (!this.query)\r
425 return u;\r
426\r
427 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
428\r
429 if (u.indexOf('#') == -1)\r
430 return u + v;\r
431\r
432 return u.replace('#', v + '#');\r
433 },\r
434\r
435 // Fix function for IE 9 where regexps isn't working correctly\r
436 // Todo: remove me once MS fixes the bug\r
437 _replace : function(find, replace, str) {\r
438 // On IE9 we have to fake $x replacement\r
439 if (isRegExpBroken) {\r
440 return str.replace(find, function() {\r
441 var val = replace, args = arguments, i;\r
442\r
443 for (i = 0; i < args.length - 2; i++) {\r
444 if (args[i] === undefined) {\r
445 val = val.replace(new RegExp('\\$' + i, 'g'), '');\r
446 } else {\r
447 val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);\r
448 }\r
449 }\r
450\r
451 return val;\r
452 });\r
453 }\r
454\r
455 return str.replace(find, replace);\r
456 }\r
457\r
458 };\r
459\r
460 // Initialize the API\r
461 tinymce._init();\r
462\r
463 // Expose tinymce namespace to the global namespace (window)\r
464 win.tinymce = win.tinyMCE = tinymce;\r
465})(window);\r
466\r
467\r
468\r
469tinymce.create('tinymce.util.Dispatcher', {\r
470 scope : null,\r
471 listeners : null,\r
472\r
473 Dispatcher : function(s) {\r
474 this.scope = s || this;\r
475 this.listeners = [];\r
476 },\r
477\r
478 add : function(cb, s) {\r
479 this.listeners.push({cb : cb, scope : s || this.scope});\r
480\r
481 return cb;\r
482 },\r
483\r
484 addToTop : function(cb, s) {\r
485 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
486\r
487 return cb;\r
488 },\r
489\r
490 remove : function(cb) {\r
491 var l = this.listeners, o = null;\r
492\r
493 tinymce.each(l, function(c, i) {\r
494 if (cb == c.cb) {\r
495 o = cb;\r
496 l.splice(i, 1);\r
497 return false;\r
498 }\r
499 });\r
500\r
501 return o;\r
502 },\r
503\r
504 dispatch : function() {\r
505 var s, a = arguments, i, li = this.listeners, c;\r
506\r
507 // Needs to be a real loop since the listener count might change while looping\r
508 // And this is also more efficient\r
509 for (i = 0; i<li.length; i++) {\r
510 c = li[i];\r
511 s = c.cb.apply(c.scope, a);\r
512\r
513 if (s === false)\r
514 break;\r
515 }\r
516\r
517 return s;\r
518 }\r
519\r
520 });\r
521\r
522(function() {\r
523 var each = tinymce.each;\r
524\r
525 tinymce.create('tinymce.util.URI', {\r
526 URI : function(u, s) {\r
527 var t = this, o, a, b;\r
528\r
529 // Trim whitespace\r
530 u = tinymce.trim(u);\r
531\r
532 // Default settings\r
533 s = t.settings = s || {};\r
534\r
535 // Strange app protocol or local anchor\r
536 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
537 t.source = u;\r
538 return;\r
539 }\r
540\r
541 // Absolute path with no host, fake host and protocol\r
542 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
543 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
544\r
545 // Relative path http:// or protocol relative //path\r
546 if (!/^\w*:?\/\//.test(u))\r
547 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
548\r
549 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
550 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
551 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
552 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
553 var s = u[i];\r
554\r
555 // Zope 3 workaround, they use @@something\r
556 if (s)\r
557 s = s.replace(/\(mce_at\)/g, '@@');\r
558\r
559 t[v] = s;\r
560 });\r
561\r
562 if (b = s.base_uri) {\r
563 if (!t.protocol)\r
564 t.protocol = b.protocol;\r
565\r
566 if (!t.userInfo)\r
567 t.userInfo = b.userInfo;\r
568\r
569 if (!t.port && t.host == 'mce_host')\r
570 t.port = b.port;\r
571\r
572 if (!t.host || t.host == 'mce_host')\r
573 t.host = b.host;\r
574\r
575 t.source = '';\r
576 }\r
577\r
578 //t.path = t.path || '/';\r
579 },\r
580\r
581 setPath : function(p) {\r
582 var t = this;\r
583\r
584 p = /^(.*?)\/?(\w+)?$/.exec(p);\r
585\r
586 // Update path parts\r
587 t.path = p[0];\r
588 t.directory = p[1];\r
589 t.file = p[2];\r
590\r
591 // Rebuild source\r
592 t.source = '';\r
593 t.getURI();\r
594 },\r
595\r
596 toRelative : function(u) {\r
597 var t = this, o;\r
598\r
599 if (u === "./")\r
600 return u;\r
601\r
602 u = new tinymce.util.URI(u, {base_uri : t});\r
603\r
604 // Not on same domain/port or protocol\r
605 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
606 return u.getURI();\r
607\r
608 o = t.toRelPath(t.path, u.path);\r
609\r
610 // Add query\r
611 if (u.query)\r
612 o += '?' + u.query;\r
613\r
614 // Add anchor\r
615 if (u.anchor)\r
616 o += '#' + u.anchor;\r
617\r
618 return o;\r
619 },\r
620 \r
621 toAbsolute : function(u, nh) {\r
622 var u = new tinymce.util.URI(u, {base_uri : this});\r
623\r
624 return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
625 },\r
626\r
627 toRelPath : function(base, path) {\r
628 var items, bp = 0, out = '', i, l;\r
629\r
630 // Split the paths\r
631 base = base.substring(0, base.lastIndexOf('/'));\r
632 base = base.split('/');\r
633 items = path.split('/');\r
634\r
635 if (base.length >= items.length) {\r
636 for (i = 0, l = base.length; i < l; i++) {\r
637 if (i >= items.length || base[i] != items[i]) {\r
638 bp = i + 1;\r
639 break;\r
640 }\r
641 }\r
642 }\r
643\r
644 if (base.length < items.length) {\r
645 for (i = 0, l = items.length; i < l; i++) {\r
646 if (i >= base.length || base[i] != items[i]) {\r
647 bp = i + 1;\r
648 break;\r
649 }\r
650 }\r
651 }\r
652\r
653 if (bp == 1)\r
654 return path;\r
655\r
656 for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
657 out += "../";\r
658\r
659 for (i = bp - 1, l = items.length; i < l; i++) {\r
660 if (i != bp - 1)\r
661 out += "/" + items[i];\r
662 else\r
663 out += items[i];\r
664 }\r
665\r
666 return out;\r
667 },\r
668\r
669 toAbsPath : function(base, path) {\r
670 var i, nb = 0, o = [], tr, outPath;\r
671\r
672 // Split paths\r
673 tr = /\/$/.test(path) ? '/' : '';\r
674 base = base.split('/');\r
675 path = path.split('/');\r
676\r
677 // Remove empty chunks\r
678 each(base, function(k) {\r
679 if (k)\r
680 o.push(k);\r
681 });\r
682\r
683 base = o;\r
684\r
685 // Merge relURLParts chunks\r
686 for (i = path.length - 1, o = []; i >= 0; i--) {\r
687 // Ignore empty or .\r
688 if (path[i].length == 0 || path[i] == ".")\r
689 continue;\r
690\r
691 // Is parent\r
692 if (path[i] == '..') {\r
693 nb++;\r
694 continue;\r
695 }\r
696\r
697 // Move up\r
698 if (nb > 0) {\r
699 nb--;\r
700 continue;\r
701 }\r
702\r
703 o.push(path[i]);\r
704 }\r
705\r
706 i = base.length - nb;\r
707\r
708 // If /a/b/c or /\r
709 if (i <= 0)\r
710 outPath = o.reverse().join('/');\r
711 else\r
712 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
713\r
714 // Add front / if it's needed\r
715 if (outPath.indexOf('/') !== 0)\r
716 outPath = '/' + outPath;\r
717\r
718 // Add traling / if it's needed\r
719 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
720 outPath += tr;\r
721\r
722 return outPath;\r
723 },\r
724\r
725 getURI : function(nh) {\r
726 var s, t = this;\r
727\r
728 // Rebuild source\r
729 if (!t.source || nh) {\r
730 s = '';\r
731\r
732 if (!nh) {\r
733 if (t.protocol)\r
734 s += t.protocol + '://';\r
735\r
736 if (t.userInfo)\r
737 s += t.userInfo + '@';\r
738\r
739 if (t.host)\r
740 s += t.host;\r
741\r
742 if (t.port)\r
743 s += ':' + t.port;\r
744 }\r
745\r
746 if (t.path)\r
747 s += t.path;\r
748\r
749 if (t.query)\r
750 s += '?' + t.query;\r
751\r
752 if (t.anchor)\r
753 s += '#' + t.anchor;\r
754\r
755 t.source = s;\r
756 }\r
757\r
758 return t.source;\r
759 }\r
760 });\r
761})();\r
762\r
763(function() {\r
764 var each = tinymce.each;\r
765\r
766 tinymce.create('static tinymce.util.Cookie', {\r
767 getHash : function(n) {\r
768 var v = this.get(n), h;\r
769\r
770 if (v) {\r
771 each(v.split('&'), function(v) {\r
772 v = v.split('=');\r
773 h = h || {};\r
774 h[unescape(v[0])] = unescape(v[1]);\r
775 });\r
776 }\r
777\r
778 return h;\r
779 },\r
780\r
781 setHash : function(n, v, e, p, d, s) {\r
782 var o = '';\r
783\r
784 each(v, function(v, k) {\r
785 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
786 });\r
787\r
788 this.set(n, o, e, p, d, s);\r
789 },\r
790\r
791 get : function(n) {\r
792 var c = document.cookie, e, p = n + "=", b;\r
793\r
794 // Strict mode\r
795 if (!c)\r
796 return;\r
797\r
798 b = c.indexOf("; " + p);\r
799\r
800 if (b == -1) {\r
801 b = c.indexOf(p);\r
802\r
803 if (b != 0)\r
804 return null;\r
805 } else\r
806 b += 2;\r
807\r
808 e = c.indexOf(";", b);\r
809\r
810 if (e == -1)\r
811 e = c.length;\r
812\r
813 return unescape(c.substring(b + p.length, e));\r
814 },\r
815\r
816 set : function(n, v, e, p, d, s) {\r
817 document.cookie = n + "=" + escape(v) +\r
818 ((e) ? "; expires=" + e.toGMTString() : "") +\r
819 ((p) ? "; path=" + escape(p) : "") +\r
820 ((d) ? "; domain=" + d : "") +\r
821 ((s) ? "; secure" : "");\r
822 },\r
823\r
824 remove : function(n, p) {\r
825 var d = new Date();\r
826\r
827 d.setTime(d.getTime() - 1000);\r
828\r
829 this.set(n, '', d, p, d);\r
830 }\r
831 });\r
832})();\r
833\r
834tinymce.create('static tinymce.util.JSON', {\r
835 serialize : function(o) {\r
836 var i, v, s = tinymce.util.JSON.serialize, t;\r
837\r
838 if (o == null)\r
839 return 'null';\r
840\r
841 t = typeof o;\r
842\r
843 if (t == 'string') {\r
844 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
845\r
846 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
847 i = v.indexOf(b);\r
848\r
849 if (i + 1)\r
850 return '\\' + v.charAt(i + 1);\r
851\r
852 a = b.charCodeAt().toString(16);\r
853\r
854 return '\\u' + '0000'.substring(a.length) + a;\r
855 }) + '"';\r
856 }\r
857\r
858 if (t == 'object') {\r
859 if (o.hasOwnProperty && o instanceof Array) {\r
860 for (i=0, v = '['; i<o.length; i++)\r
861 v += (i > 0 ? ',' : '') + s(o[i]);\r
862\r
863 return v + ']';\r
864 }\r
865\r
866 v = '{';\r
867\r
868 for (i in o)\r
869 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
870\r
871 return v + '}';\r
872 }\r
873\r
874 return '' + o;\r
875 },\r
876\r
877 parse : function(s) {\r
878 try {\r
879 return eval('(' + s + ')');\r
880 } catch (ex) {\r
881 // Ignore\r
882 }\r
883 }\r
884\r
885 });\r
886\r
887tinymce.create('static tinymce.util.XHR', {\r
888 send : function(o) {\r
889 var x, t, w = window, c = 0;\r
890\r
891 // Default settings\r
892 o.scope = o.scope || this;\r
893 o.success_scope = o.success_scope || o.scope;\r
894 o.error_scope = o.error_scope || o.scope;\r
895 o.async = o.async === false ? false : true;\r
896 o.data = o.data || '';\r
897\r
898 function get(s) {\r
899 x = 0;\r
900\r
901 try {\r
902 x = new ActiveXObject(s);\r
903 } catch (ex) {\r
904 }\r
905\r
906 return x;\r
907 };\r
908\r
909 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
910\r
911 if (x) {\r
912 if (x.overrideMimeType)\r
913 x.overrideMimeType(o.content_type);\r
914\r
915 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
916\r
917 if (o.content_type)\r
918 x.setRequestHeader('Content-Type', o.content_type);\r
919\r
920 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
921\r
922 x.send(o.data);\r
923\r
924 function ready() {\r
925 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
926 if (o.success && c < 10000 && x.status == 200)\r
927 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
928 else if (o.error)\r
929 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
930\r
931 x = null;\r
932 } else\r
933 w.setTimeout(ready, 10);\r
934 };\r
935\r
936 // Syncronous request\r
937 if (!o.async)\r
938 return ready();\r
939\r
940 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
941 t = w.setTimeout(ready, 10);\r
942 }\r
943 }\r
944});\r
945\r
946(function() {\r
947 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
948\r
949 tinymce.create('tinymce.util.JSONRequest', {\r
950 JSONRequest : function(s) {\r
951 this.settings = extend({\r
952 }, s);\r
953 this.count = 0;\r
954 },\r
955\r
956 send : function(o) {\r
957 var ecb = o.error, scb = o.success;\r
958\r
959 o = extend(this.settings, o);\r
960\r
961 o.success = function(c, x) {\r
962 c = JSON.parse(c);\r
963\r
964 if (typeof(c) == 'undefined') {\r
965 c = {\r
966 error : 'JSON Parse error.'\r
967 };\r
968 }\r
969\r
970 if (c.error)\r
971 ecb.call(o.error_scope || o.scope, c.error, x);\r
972 else\r
973 scb.call(o.success_scope || o.scope, c.result);\r
974 };\r
975\r
976 o.error = function(ty, x) {\r
977 ecb.call(o.error_scope || o.scope, ty, x);\r
978 };\r
979\r
980 o.data = JSON.serialize({\r
981 id : o.id || 'c' + (this.count++),\r
982 method : o.method,\r
983 params : o.params\r
984 });\r
985\r
986 // JSON content type for Ruby on rails. Bug: #1883287\r
987 o.content_type = 'application/json';\r
988\r
989 XHR.send(o);\r
990 },\r
991\r
992 'static' : {\r
993 sendRPC : function(o) {\r
994 return new tinymce.util.JSONRequest().send(o);\r
995 }\r
996 }\r
997 });\r
998}());\r
999(function(tinymce) {\r
1000 // Shorten names\r
1001 var each = tinymce.each,\r
1002 is = tinymce.is,\r
1003 isWebKit = tinymce.isWebKit,\r
1004 isIE = tinymce.isIE,\r
1005 blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/,\r
1006 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
1007 mceAttribs = makeMap('src,href,style,coords,shape'),\r
1008 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
1009 encodeCharsRe = /[<>&\"]/g,\r
1010 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
1011 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
1012 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
1013\r
1014 function makeMap(str) {\r
1015 var map = {}, i;\r
1016\r
1017 str = str.split(',');\r
1018 for (i = str.length; i >= 0; i--)\r
1019 map[str[i]] = 1;\r
1020\r
1021 return map;\r
1022 };\r
1023\r
1024 tinymce.create('tinymce.dom.DOMUtils', {\r
1025 doc : null,\r
1026 root : null,\r
1027 files : null,\r
1028 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1029 props : {\r
1030 "for" : "htmlFor",\r
1031 "class" : "className",\r
1032 className : "className",\r
1033 checked : "checked",\r
1034 disabled : "disabled",\r
1035 maxlength : "maxLength",\r
1036 readonly : "readOnly",\r
1037 selected : "selected",\r
1038 value : "value",\r
1039 id : "id",\r
1040 name : "name",\r
1041 type : "type"\r
1042 },\r
1043\r
1044 DOMUtils : function(d, s) {\r
1045 var t = this, globalStyle;\r
1046\r
1047 t.doc = d;\r
1048 t.win = window;\r
1049 t.files = {};\r
1050 t.cssFlicker = false;\r
1051 t.counter = 0;\r
1052 t.stdMode = d.documentMode >= 8;\r
1053 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
1054\r
1055 t.settings = s = tinymce.extend({\r
1056 keep_values : false,\r
1057 hex_colors : 1,\r
1058 process_html : 1\r
1059 }, s);\r
1060\r
1061 // Fix IE6SP2 flicker and check it failed for pre SP2\r
1062 if (tinymce.isIE6) {\r
1063 try {\r
1064 d.execCommand('BackgroundImageCache', false, true);\r
1065 } catch (e) {\r
1066 t.cssFlicker = true;\r
1067 }\r
1068 }\r
1069\r
1070 // Build styles list\r
1071 if (s.valid_styles) {\r
1072 t._styles = {};\r
1073\r
1074 // Convert styles into a rule list\r
1075 each(s.valid_styles, function(value, key) {\r
1076 t._styles[key] = tinymce.explode(value);\r
1077 });\r
1078 }\r
1079\r
1080 tinymce.addUnload(t.destroy, t);\r
1081 },\r
1082\r
1083 getRoot : function() {\r
1084 var t = this, s = t.settings;\r
1085\r
1086 return (s && t.get(s.root_element)) || t.doc.body;\r
1087 },\r
1088\r
1089 getViewPort : function(w) {\r
1090 var d, b;\r
1091\r
1092 w = !w ? this.win : w;\r
1093 d = w.document;\r
1094 b = this.boxModel ? d.documentElement : d.body;\r
1095\r
1096 // Returns viewport size excluding scrollbars\r
1097 return {\r
1098 x : w.pageXOffset || b.scrollLeft,\r
1099 y : w.pageYOffset || b.scrollTop,\r
1100 w : w.innerWidth || b.clientWidth,\r
1101 h : w.innerHeight || b.clientHeight\r
1102 };\r
1103 },\r
1104\r
1105 getRect : function(e) {\r
1106 var p, t = this, sr;\r
1107\r
1108 e = t.get(e);\r
1109 p = t.getPos(e);\r
1110 sr = t.getSize(e);\r
1111\r
1112 return {\r
1113 x : p.x,\r
1114 y : p.y,\r
1115 w : sr.w,\r
1116 h : sr.h\r
1117 };\r
1118 },\r
1119\r
1120 getSize : function(e) {\r
1121 var t = this, w, h;\r
1122\r
1123 e = t.get(e);\r
1124 w = t.getStyle(e, 'width');\r
1125 h = t.getStyle(e, 'height');\r
1126\r
1127 // Non pixel value, then force offset/clientWidth\r
1128 if (w.indexOf('px') === -1)\r
1129 w = 0;\r
1130\r
1131 // Non pixel value, then force offset/clientWidth\r
1132 if (h.indexOf('px') === -1)\r
1133 h = 0;\r
1134\r
1135 return {\r
1136 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1137 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1138 };\r
1139 },\r
1140\r
1141 getParent : function(n, f, r) {\r
1142 return this.getParents(n, f, r, false);\r
1143 },\r
1144\r
1145 getParents : function(n, f, r, c) {\r
1146 var t = this, na, se = t.settings, o = [];\r
1147\r
1148 n = t.get(n);\r
1149 c = c === undefined;\r
1150\r
1151 if (se.strict_root)\r
1152 r = r || t.getRoot();\r
1153\r
1154 // Wrap node name as func\r
1155 if (is(f, 'string')) {\r
1156 na = f;\r
1157\r
1158 if (f === '*') {\r
1159 f = function(n) {return n.nodeType == 1;};\r
1160 } else {\r
1161 f = function(n) {\r
1162 return t.is(n, na);\r
1163 };\r
1164 }\r
1165 }\r
1166\r
1167 while (n) {\r
1168 if (n == r || !n.nodeType || n.nodeType === 9)\r
1169 break;\r
1170\r
1171 if (!f || f(n)) {\r
1172 if (c)\r
1173 o.push(n);\r
1174 else\r
1175 return n;\r
1176 }\r
1177\r
1178 n = n.parentNode;\r
1179 }\r
1180\r
1181 return c ? o : null;\r
1182 },\r
1183\r
1184 get : function(e) {\r
1185 var n;\r
1186\r
1187 if (e && this.doc && typeof(e) == 'string') {\r
1188 n = e;\r
1189 e = this.doc.getElementById(e);\r
1190\r
1191 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1192 if (e && e.id !== n)\r
1193 return this.doc.getElementsByName(n)[1];\r
1194 }\r
1195\r
1196 return e;\r
1197 },\r
1198\r
1199 getNext : function(node, selector) {\r
1200 return this._findSib(node, selector, 'nextSibling');\r
1201 },\r
1202\r
1203 getPrev : function(node, selector) {\r
1204 return this._findSib(node, selector, 'previousSibling');\r
1205 },\r
1206\r
1207\r
1208 select : function(pa, s) {\r
1209 var t = this;\r
1210\r
1211 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1212 },\r
1213\r
1214 is : function(n, selector) {\r
1215 var i;\r
1216\r
1217 // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
1218 if (n.length === undefined) {\r
1219 // Simple all selector\r
1220 if (selector === '*')\r
1221 return n.nodeType == 1;\r
1222\r
1223 // Simple selector just elements\r
1224 if (simpleSelectorRe.test(selector)) {\r
1225 selector = selector.toLowerCase().split(/,/);\r
1226 n = n.nodeName.toLowerCase();\r
1227\r
1228 for (i = selector.length - 1; i >= 0; i--) {\r
1229 if (selector[i] == n)\r
1230 return true;\r
1231 }\r
1232\r
1233 return false;\r
1234 }\r
1235 }\r
1236\r
1237 return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
1238 },\r
1239\r
1240\r
1241 add : function(p, n, a, h, c) {\r
1242 var t = this;\r
1243\r
1244 return this.run(p, function(p) {\r
1245 var e, k;\r
1246\r
1247 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1248 t.setAttribs(e, a);\r
1249\r
1250 if (h) {\r
1251 if (h.nodeType)\r
1252 e.appendChild(h);\r
1253 else\r
1254 t.setHTML(e, h);\r
1255 }\r
1256\r
1257 return !c ? p.appendChild(e) : e;\r
1258 });\r
1259 },\r
1260\r
1261 create : function(n, a, h) {\r
1262 return this.add(this.doc.createElement(n), n, a, h, 1);\r
1263 },\r
1264\r
1265 createHTML : function(n, a, h) {\r
1266 var o = '', t = this, k;\r
1267\r
1268 o += '<' + n;\r
1269\r
1270 for (k in a) {\r
1271 if (a.hasOwnProperty(k))\r
1272 o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1273 }\r
1274\r
1275 // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
1276 if (typeof(h) != "undefined")\r
1277 return o + '>' + h + '</' + n + '>';\r
1278\r
1279 return o + ' />';\r
1280 },\r
1281\r
1282 remove : function(node, keep_children) {\r
1283 return this.run(node, function(node) {\r
1284 var parent, child;\r
1285\r
1286 parent = node.parentNode;\r
1287\r
1288 if (!parent)\r
1289 return null;\r
1290\r
1291 if (keep_children) {\r
1292 while (child = node.firstChild) {\r
1293 // IE 8 will crash if you don't remove completely empty text nodes\r
1294 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
1295 parent.insertBefore(child, node);\r
1296 else\r
1297 node.removeChild(child);\r
1298 }\r
1299 }\r
1300\r
1301 return parent.removeChild(node);\r
1302 });\r
1303 },\r
1304\r
1305 setStyle : function(n, na, v) {\r
1306 var t = this;\r
1307\r
1308 return t.run(n, function(e) {\r
1309 var s, i;\r
1310\r
1311 s = e.style;\r
1312\r
1313 // Camelcase it, if needed\r
1314 na = na.replace(/-(\D)/g, function(a, b){\r
1315 return b.toUpperCase();\r
1316 });\r
1317\r
1318 // Default px suffix on these\r
1319 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1320 v += 'px';\r
1321\r
1322 switch (na) {\r
1323 case 'opacity':\r
1324 // IE specific opacity\r
1325 if (isIE) {\r
1326 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1327\r
1328 if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1329 s.display = 'inline-block';\r
1330 }\r
1331\r
1332 // Fix for older browsers\r
1333 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1334 break;\r
1335\r
1336 case 'float':\r
1337 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1338 break;\r
1339\r
1340 default:\r
1341 s[na] = v || '';\r
1342 }\r
1343\r
1344 // Force update of the style data\r
1345 if (t.settings.update_styles)\r
1346 t.setAttrib(e, '_mce_style');\r
1347 });\r
1348 },\r
1349\r
1350 getStyle : function(n, na, c) {\r
1351 n = this.get(n);\r
1352\r
1353 if (!n)\r
1354 return false;\r
1355\r
1356 // Gecko\r
1357 if (this.doc.defaultView && c) {\r
1358 // Remove camelcase\r
1359 na = na.replace(/[A-Z]/g, function(a){\r
1360 return '-' + a;\r
1361 });\r
1362\r
1363 try {\r
1364 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1365 } catch (ex) {\r
1366 // Old safari might fail\r
1367 return null;\r
1368 }\r
1369 }\r
1370\r
1371 // Camelcase it, if needed\r
1372 na = na.replace(/-(\D)/g, function(a, b){\r
1373 return b.toUpperCase();\r
1374 });\r
1375\r
1376 if (na == 'float')\r
1377 na = isIE ? 'styleFloat' : 'cssFloat';\r
1378\r
1379 // IE & Opera\r
1380 if (n.currentStyle && c)\r
1381 return n.currentStyle[na];\r
1382\r
1383 return n.style[na];\r
1384 },\r
1385\r
1386 setStyles : function(e, o) {\r
1387 var t = this, s = t.settings, ol;\r
1388\r
1389 ol = s.update_styles;\r
1390 s.update_styles = 0;\r
1391\r
1392 each(o, function(v, n) {\r
1393 t.setStyle(e, n, v);\r
1394 });\r
1395\r
1396 // Update style info\r
1397 s.update_styles = ol;\r
1398 if (s.update_styles)\r
1399 t.setAttrib(e, s.cssText);\r
1400 },\r
1401\r
1402 removeAllAttribs: function(e) {\r
1403 return this.run(e, function(e) {\r
1404 var attrs = e.attributes;\r
1405 for (var i = attrs.length - 1; i >= 0; i--) {\r
1406 e.removeAttributeNode(attrs.item(i));\r
1407 }\r
1408 });\r
1409 },\r
1410\r
1411 setAttrib : function(e, n, v) {\r
1412 var t = this;\r
1413\r
1414 // Whats the point\r
1415 if (!e || !n)\r
1416 return;\r
1417\r
1418 // Strict XML mode\r
1419 if (t.settings.strict)\r
1420 n = n.toLowerCase();\r
1421\r
1422 return this.run(e, function(e) {\r
1423 var s = t.settings;\r
1424\r
1425 switch (n) {\r
1426 case "style":\r
1427 if (!is(v, 'string')) {\r
1428 each(v, function(v, n) {\r
1429 t.setStyle(e, n, v);\r
1430 });\r
1431\r
1432 return;\r
1433 }\r
1434\r
1435 // No mce_style for elements with these since they might get resized by the user\r
1436 if (s.keep_values) {\r
1437 if (v && !t._isRes(v))\r
1438 e.setAttribute('_mce_style', v, 2);\r
1439 else\r
1440 e.removeAttribute('_mce_style', 2);\r
1441 }\r
1442\r
1443 e.style.cssText = v;\r
1444 break;\r
1445\r
1446 case "class":\r
1447 e.className = v || ''; // Fix IE null bug\r
1448 break;\r
1449\r
1450 case "src":\r
1451 case "href":\r
1452 if (s.keep_values) {\r
1453 if (s.url_converter)\r
1454 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1455\r
1456 t.setAttrib(e, '_mce_' + n, v, 2);\r
1457 }\r
1458\r
1459 break;\r
1460\r
1461 case "shape":\r
1462 e.setAttribute('_mce_style', v);\r
1463 break;\r
1464 }\r
1465\r
1466 if (is(v) && v !== null && v.length !== 0)\r
1467 e.setAttribute(n, '' + v, 2);\r
1468 else\r
1469 e.removeAttribute(n, 2);\r
1470 });\r
1471 },\r
1472\r
1473 setAttribs : function(e, o) {\r
1474 var t = this;\r
1475\r
1476 return this.run(e, function(e) {\r
1477 each(o, function(v, n) {\r
1478 t.setAttrib(e, n, v);\r
1479 });\r
1480 });\r
1481 },\r
1482\r
1483 getAttrib : function(e, n, dv) {\r
1484 var v, t = this;\r
1485\r
1486 e = t.get(e);\r
1487\r
1488 if (!e || e.nodeType !== 1)\r
1489 return false;\r
1490\r
1491 if (!is(dv))\r
1492 dv = '';\r
1493\r
1494 // Try the mce variant for these\r
1495 if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1496 v = e.getAttribute("_mce_" + n);\r
1497\r
1498 if (v)\r
1499 return v;\r
1500 }\r
1501\r
1502 if (isIE && t.props[n]) {\r
1503 v = e[t.props[n]];\r
1504 v = v && v.nodeValue ? v.nodeValue : v;\r
1505 }\r
1506\r
1507 if (!v)\r
1508 v = e.getAttribute(n, 2);\r
1509\r
1510 // Check boolean attribs\r
1511 if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1512 if (e[t.props[n]] === true && v === '')\r
1513 return n;\r
1514\r
1515 return v ? n : '';\r
1516 }\r
1517\r
1518 // Inner input elements will override attributes on form elements\r
1519 if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1520 return e.getAttributeNode(n).nodeValue;\r
1521\r
1522 if (n === 'style') {\r
1523 v = v || e.style.cssText;\r
1524\r
1525 if (v) {\r
1526 v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1527\r
1528 if (t.settings.keep_values && !t._isRes(v))\r
1529 e.setAttribute('_mce_style', v);\r
1530 }\r
1531 }\r
1532\r
1533 // Remove Apple and WebKit stuff\r
1534 if (isWebKit && n === "class" && v)\r
1535 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1536\r
1537 // Handle IE issues\r
1538 if (isIE) {\r
1539 switch (n) {\r
1540 case 'rowspan':\r
1541 case 'colspan':\r
1542 // IE returns 1 as default value\r
1543 if (v === 1)\r
1544 v = '';\r
1545\r
1546 break;\r
1547\r
1548 case 'size':\r
1549 // IE returns +0 as default value for size\r
1550 if (v === '+0' || v === 20 || v === 0)\r
1551 v = '';\r
1552\r
1553 break;\r
1554\r
1555 case 'width':\r
1556 case 'height':\r
1557 case 'vspace':\r
1558 case 'checked':\r
1559 case 'disabled':\r
1560 case 'readonly':\r
1561 if (v === 0)\r
1562 v = '';\r
1563\r
1564 break;\r
1565\r
1566 case 'hspace':\r
1567 // IE returns -1 as default value\r
1568 if (v === -1)\r
1569 v = '';\r
1570\r
1571 break;\r
1572\r
1573 case 'maxlength':\r
1574 case 'tabindex':\r
1575 // IE returns default value\r
1576 if (v === 32768 || v === 2147483647 || v === '32768')\r
1577 v = '';\r
1578\r
1579 break;\r
1580\r
1581 case 'multiple':\r
1582 case 'compact':\r
1583 case 'noshade':\r
1584 case 'nowrap':\r
1585 if (v === 65535)\r
1586 return n;\r
1587\r
1588 return dv;\r
1589\r
1590 case 'shape':\r
1591 v = v.toLowerCase();\r
1592 break;\r
1593\r
1594 default:\r
1595 // IE has odd anonymous function for event attributes\r
1596 if (n.indexOf('on') === 0 && v)\r
1597 v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
1598 }\r
1599 }\r
1600\r
1601 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1602 },\r
1603\r
1604 getPos : function(n, ro) {\r
1605 var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1606\r
1607 n = t.get(n);\r
1608 ro = ro || d.body;\r
1609\r
1610 if (n) {\r
1611 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1612 if (isIE && !t.stdMode) {\r
1613 n = n.getBoundingClientRect();\r
1614 e = t.boxModel ? d.documentElement : d.body;\r
1615 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1616 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1617\r
1618 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1619 }\r
1620\r
1621 r = n;\r
1622 while (r && r != ro && r.nodeType) {\r
1623 x += r.offsetLeft || 0;\r
1624 y += r.offsetTop || 0;\r
1625 r = r.offsetParent;\r
1626 }\r
1627\r
1628 r = n.parentNode;\r
1629 while (r && r != ro && r.nodeType) {\r
1630 x -= r.scrollLeft || 0;\r
1631 y -= r.scrollTop || 0;\r
1632 r = r.parentNode;\r
1633 }\r
1634 }\r
1635\r
1636 return {x : x, y : y};\r
1637 },\r
1638\r
1639 parseStyle : function(st) {\r
1640 var t = this, s = t.settings, o = {};\r
1641\r
1642 if (!st)\r
1643 return o;\r
1644\r
1645 function compress(p, s, ot) {\r
1646 var t, r, b, l;\r
1647\r
1648 // Get values and check it it needs compressing\r
1649 t = o[p + '-top' + s];\r
1650 if (!t)\r
1651 return;\r
1652\r
1653 r = o[p + '-right' + s];\r
1654 if (t != r)\r
1655 return;\r
1656\r
1657 b = o[p + '-bottom' + s];\r
1658 if (r != b)\r
1659 return;\r
1660\r
1661 l = o[p + '-left' + s];\r
1662 if (b != l)\r
1663 return;\r
1664\r
1665 // Compress\r
1666 o[ot] = l;\r
1667 delete o[p + '-top' + s];\r
1668 delete o[p + '-right' + s];\r
1669 delete o[p + '-bottom' + s];\r
1670 delete o[p + '-left' + s];\r
1671 };\r
1672\r
1673 function canCompress(key) {\r
1674 var split, value = o[key];\r
1675 if (!value || value.indexOf(' ') < 0) {\r
1676 return;\r
1677 }\r
1678 split = value.split(' ');\r
1679 if (each(split, function(v) { return v === split[0]; })) {\r
1680 o[key] = split[0];\r
1681 return true;\r
1682 } else {\r
1683 return false;\r
1684 }\r
1685 }\r
1686\r
1687 function compress2(ta, a, b, c) {\r
1688 var t;\r
1689 \r
1690 if (!canCompress(a))\r
1691 return;\r
1692\r
1693 if (!canCompress(b))\r
1694 return;\r
1695\r
1696 if (!canCompress(c))\r
1697 return;\r
1698\r
1699 // Compress\r
1700 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1701 delete o[a];\r
1702 delete o[b];\r
1703 delete o[c];\r
1704 };\r
1705\r
1706 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1707\r
1708 each(st.split(';'), function(v) {\r
1709 var sv, ur = [];\r
1710\r
1711 if (v) {\r
1712 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1713 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1714 v = v.split(':');\r
1715 sv = tinymce.trim(v[1]);\r
1716 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1717\r
1718 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1719 return t.toHex(v);\r
1720 });\r
1721\r
1722 if (s.url_converter) {\r
1723 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1724 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1725 });\r
1726 }\r
1727\r
1728 o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1729 }\r
1730 });\r
1731\r
1732 compress("border", "", "border");\r
1733 compress("border", "-width", "border-width");\r
1734 compress("border", "-color", "border-color");\r
1735 compress("border", "-style", "border-style");\r
1736 compress("padding", "", "padding");\r
1737 compress("margin", "", "margin");\r
1738 compress2('border', 'border-width', 'border-style', 'border-color');\r
1739\r
1740 if (isIE) {\r
1741 // Remove pointless border\r
1742 if (o.border == 'medium none')\r
1743 o.border = '';\r
1744 }\r
1745\r
1746 return o;\r
1747 },\r
1748\r
1749 serializeStyle : function(o, name) {\r
1750 var t = this, s = '';\r
1751\r
1752 function add(v, k) {\r
1753 if (k && v) {\r
1754 // Remove browser specific styles like -moz- or -webkit-\r
1755 if (k.indexOf('-') === 0)\r
1756 return;\r
1757\r
1758 switch (k) {\r
1759 case 'font-weight':\r
1760 // Opera will output bold as 700\r
1761 if (v == 700)\r
1762 v = 'bold';\r
1763\r
1764 break;\r
1765\r
1766 case 'color':\r
1767 case 'background-color':\r
1768 v = v.toLowerCase();\r
1769 break;\r
1770 }\r
1771\r
1772 s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1773 }\r
1774 };\r
1775\r
1776 // Validate style output\r
1777 if (name && t._styles) {\r
1778 each(t._styles['*'], function(name) {\r
1779 add(o[name], name);\r
1780 });\r
1781\r
1782 each(t._styles[name.toLowerCase()], function(name) {\r
1783 add(o[name], name);\r
1784 });\r
1785 } else\r
1786 each(o, add);\r
1787\r
1788 return s;\r
1789 },\r
1790\r
1791 loadCSS : function(u) {\r
1792 var t = this, d = t.doc, head;\r
1793\r
1794 if (!u)\r
1795 u = '';\r
1796\r
1797 head = t.select('head')[0];\r
1798\r
1799 each(u.split(','), function(u) {\r
1800 var link;\r
1801\r
1802 if (t.files[u])\r
1803 return;\r
1804\r
1805 t.files[u] = true;\r
1806 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1807\r
1808 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1809 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1810 // It's ugly but it seems to work fine.\r
1811 if (isIE && d.documentMode && d.recalc) {\r
1812 link.onload = function() {\r
1813 d.recalc();\r
1814 link.onload = null;\r
1815 };\r
1816 }\r
1817\r
1818 head.appendChild(link);\r
1819 });\r
1820 },\r
1821\r
1822 addClass : function(e, c) {\r
1823 return this.run(e, function(e) {\r
1824 var o;\r
1825\r
1826 if (!c)\r
1827 return 0;\r
1828\r
1829 if (this.hasClass(e, c))\r
1830 return e.className;\r
1831\r
1832 o = this.removeClass(e, c);\r
1833\r
1834 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1835 });\r
1836 },\r
1837\r
1838 removeClass : function(e, c) {\r
1839 var t = this, re;\r
1840\r
1841 return t.run(e, function(e) {\r
1842 var v;\r
1843\r
1844 if (t.hasClass(e, c)) {\r
1845 if (!re)\r
1846 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1847\r
1848 v = e.className.replace(re, ' ');\r
1849 v = tinymce.trim(v != ' ' ? v : '');\r
1850\r
1851 e.className = v;\r
1852\r
1853 // Empty class attr\r
1854 if (!v) {\r
1855 e.removeAttribute('class');\r
1856 e.removeAttribute('className');\r
1857 }\r
1858\r
1859 return v;\r
1860 }\r
1861\r
1862 return e.className;\r
1863 });\r
1864 },\r
1865\r
1866 hasClass : function(n, c) {\r
1867 n = this.get(n);\r
1868\r
1869 if (!n || !c)\r
1870 return false;\r
1871\r
1872 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1873 },\r
1874\r
1875 show : function(e) {\r
1876 return this.setStyle(e, 'display', 'block');\r
1877 },\r
1878\r
1879 hide : function(e) {\r
1880 return this.setStyle(e, 'display', 'none');\r
1881 },\r
1882\r
1883 isHidden : function(e) {\r
1884 e = this.get(e);\r
1885\r
1886 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1887 },\r
1888\r
1889 uniqueId : function(p) {\r
1890 return (!p ? 'mce_' : p) + (this.counter++);\r
1891 },\r
1892\r
1893 setHTML : function(e, h) {\r
1894 var t = this;\r
1895\r
1896 return this.run(e, function(e) {\r
1897 var x, i, nl, n, p, x;\r
1898\r
1899 h = t.processHTML(h);\r
1900\r
1901 if (isIE) {\r
1902 function set() {\r
1903 // Remove all child nodes\r
1904 while (e.firstChild)\r
1905 e.removeChild(e.firstChild);\r
1906\r
1907 try {\r
1908 // IE will remove comments from the beginning\r
1909 // unless you padd the contents with something\r
1910 e.innerHTML = '<br />' + h;\r
1911 e.removeChild(e.firstChild);\r
1912 } catch (ex) {\r
1913 // 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
1914 // This seems to fix this problem\r
1915\r
1916 // Create new div with HTML contents and a BR infront to keep comments\r
1917 x = t.create('div');\r
1918 x.innerHTML = '<br />' + h;\r
1919\r
1920 // Add all children from div to target\r
1921 each (x.childNodes, function(n, i) {\r
1922 // Skip br element\r
1923 if (i)\r
1924 e.appendChild(n);\r
1925 });\r
1926 }\r
1927 };\r
1928\r
1929 // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1930 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1931 // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1932 if (t.settings.fix_ie_paragraphs)\r
1933 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
1934\r
1935 set();\r
1936\r
1937 if (t.settings.fix_ie_paragraphs) {\r
1938 // Check for odd paragraphs this is a sign of a broken DOM\r
1939 nl = e.getElementsByTagName("p");\r
1940 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1941 n = nl[i];\r
1942\r
1943 if (!n.hasChildNodes()) {\r
1944 if (!n._mce_keep) {\r
1945 x = 1; // Is broken\r
1946 break;\r
1947 }\r
1948\r
1949 n.removeAttribute('_mce_keep');\r
1950 }\r
1951 }\r
1952 }\r
1953\r
1954 // Time to fix the madness IE left us\r
1955 if (x) {\r
1956 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1957 // after we use innerHTML we can fix the DOM tree\r
1958 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
1959 h = h.replace(/<\/p>/gi, '</div>');\r
1960\r
1961 // Set the new HTML with DIVs\r
1962 set();\r
1963\r
1964 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
1965 // This is needed since IE has a annoying bug see above for details\r
1966 // This is a slow process but it has to be done. :(\r
1967 if (t.settings.fix_ie_paragraphs) {\r
1968 nl = e.getElementsByTagName("DIV");\r
1969 for (i = nl.length - 1; i >= 0; i--) {\r
1970 n = nl[i];\r
1971\r
1972 // Is it a temp div\r
1973 if (n.hasAttribute("_mce_tmp")) {\r
1974 // Create new paragraph\r
1975 p = t.doc.createElement('p');\r
1976\r
1977 // Copy all attributes\r
1978 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1979 var v;\r
1980\r
1981 if (b !== '_mce_tmp') {\r
1982 v = n.getAttribute(b);\r
1983\r
1984 if (!v && b === 'class')\r
1985 v = n.className;\r
1986\r
1987 p.setAttribute(b, v);\r
1988 }\r
1989 });\r
1990\r
1991 // Append all children to new paragraph\r
1992 for (x = 0; x<n.childNodes.length; x++)\r
1993 p.appendChild(n.childNodes[x].cloneNode(true));\r
1994\r
1995 // Replace div with new paragraph\r
1996 n.swapNode(p);\r
1997 }\r
1998 }\r
1999 }\r
2000 }\r
2001 } else\r
2002 e.innerHTML = h;\r
2003\r
2004 return h;\r
2005 });\r
2006 },\r
2007\r
2008 processHTML : function(h) {\r
2009 var t = this, s = t.settings, codeBlocks = [];\r
2010\r
2011 if (!s.process_html)\r
2012 return h;\r
2013\r
2014 if (isIE) {\r
2015 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
2016 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
2017 }\r
2018\r
2019 // Force tags open, and on IE9 replace $1$2 that got left behind due to bugs in their RegExp engine\r
2020 h = tinymce._replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>', h); // Force open\r
2021\r
2022 // Store away src and href in _mce_src and mce_href since browsers mess them up\r
2023 if (s.keep_values) {\r
2024 // Wrap scripts and styles in comments for serialization purposes\r
2025 if (/<script|noscript|style/i.test(h)) {\r
2026 function trim(s) {\r
2027 // Remove prefix and suffix code for element\r
2028 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
2029 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
2030 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
2031 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
2032\r
2033 return s;\r
2034 };\r
2035\r
2036 // Wrap the script contents in CDATA and keep them from executing\r
2037 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
2038 // Force type attribute\r
2039 if (!attribs)\r
2040 attribs = ' type="text/javascript"';\r
2041\r
2042 // Convert the src attribute of the scripts\r
2043 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
2044 if (s.url_converter)\r
2045 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
2046\r
2047 return '_mce_src="' + url + '"';\r
2048 });\r
2049\r
2050 // Wrap text contents\r
2051 if (tinymce.trim(text)) {\r
2052 codeBlocks.push(trim(text));\r
2053 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2054 }\r
2055\r
2056 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2057 });\r
2058\r
2059 // Wrap style elements\r
2060 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2061 // Wrap text contents\r
2062 if (text) {\r
2063 codeBlocks.push(trim(text));\r
2064 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2065 }\r
2066\r
2067 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2068 });\r
2069\r
2070 // Wrap noscript elements\r
2071 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2072 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2073 });\r
2074 }\r
2075\r
2076 h = tinymce._replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->', h);\r
2077\r
2078 // This function processes the attributes in the HTML string to force boolean\r
2079 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2080 function processTags(html) {\r
2081 return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2082 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2083 var mceValue;\r
2084\r
2085 name = name.toLowerCase();\r
2086 value = value || val2 || val3 || "";\r
2087\r
2088 // Treat boolean attributes\r
2089 if (boolAttrs[name]) {\r
2090 // false or 0 is treated as a missing attribute\r
2091 if (value === 'false' || value === '0')\r
2092 return;\r
2093\r
2094 return name + '="' + name + '"';\r
2095 }\r
2096\r
2097 // Is attribute one that needs special treatment\r
2098 if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2099 mceValue = t.decode(value);\r
2100\r
2101 // Convert URLs to relative/absolute ones\r
2102 if (s.url_converter && (name == "src" || name == "href"))\r
2103 mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2104\r
2105 // Process styles lowercases them and compresses them\r
2106 if (name == 'style')\r
2107 mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2108\r
2109 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2110 }\r
2111\r
2112 return match;\r
2113 }) + end + '>';\r
2114 });\r
2115 };\r
2116\r
2117 h = processTags(h);\r
2118\r
2119 // Restore script blocks\r
2120 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2121 return codeBlocks[idx];\r
2122 });\r
2123 }\r
2124\r
2125 return h;\r
2126 },\r
2127\r
2128 getOuterHTML : function(e) {\r
2129 var d;\r
2130\r
2131 e = this.get(e);\r
2132\r
2133 if (!e)\r
2134 return null;\r
2135\r
2136 if (e.outerHTML !== undefined)\r
2137 return e.outerHTML;\r
2138\r
2139 d = (e.ownerDocument || this.doc).createElement("body");\r
2140 d.appendChild(e.cloneNode(true));\r
2141\r
2142 return d.innerHTML;\r
2143 },\r
2144\r
2145 setOuterHTML : function(e, h, d) {\r
2146 var t = this;\r
2147\r
2148 function setHTML(e, h, d) {\r
2149 var n, tp;\r
2150\r
2151 tp = d.createElement("body");\r
2152 tp.innerHTML = h;\r
2153\r
2154 n = tp.lastChild;\r
2155 while (n) {\r
2156 t.insertAfter(n.cloneNode(true), e);\r
2157 n = n.previousSibling;\r
2158 }\r
2159\r
2160 t.remove(e);\r
2161 };\r
2162\r
2163 return this.run(e, function(e) {\r
2164 e = t.get(e);\r
2165\r
2166 // Only set HTML on elements\r
2167 if (e.nodeType == 1) {\r
2168 d = d || e.ownerDocument || t.doc;\r
2169\r
2170 if (isIE) {\r
2171 try {\r
2172 // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2173 if (isIE && e.nodeType == 1)\r
2174 e.outerHTML = h;\r
2175 else\r
2176 setHTML(e, h, d);\r
2177 } catch (ex) {\r
2178 // Fix for unknown runtime error\r
2179 setHTML(e, h, d);\r
2180 }\r
2181 } else\r
2182 setHTML(e, h, d);\r
2183 }\r
2184 });\r
2185 },\r
2186\r
2187 decode : function(s) {\r
2188 var e, n, v;\r
2189\r
2190 // Look for entities to decode\r
2191 if (/&[\w#]+;/.test(s)) {\r
2192 // Decode the entities using a div element not super efficient but less code\r
2193 e = this.doc.createElement("div");\r
2194 e.innerHTML = s;\r
2195 n = e.firstChild;\r
2196 v = '';\r
2197\r
2198 if (n) {\r
2199 do {\r
2200 v += n.nodeValue;\r
2201 } while (n = n.nextSibling);\r
2202 }\r
2203\r
2204 return v || s;\r
2205 }\r
2206\r
2207 return s;\r
2208 },\r
2209\r
2210 encode : function(str) {\r
2211 return ('' + str).replace(encodeCharsRe, function(chr) {\r
2212 return encodedChars[chr];\r
2213 });\r
2214 },\r
2215\r
2216 insertAfter : function(node, reference_node) {\r
2217 reference_node = this.get(reference_node);\r
2218\r
2219 return this.run(node, function(node) {\r
2220 var parent, nextSibling;\r
2221\r
2222 parent = reference_node.parentNode;\r
2223 nextSibling = reference_node.nextSibling;\r
2224\r
2225 if (nextSibling)\r
2226 parent.insertBefore(node, nextSibling);\r
2227 else\r
2228 parent.appendChild(node);\r
2229\r
2230 return node;\r
2231 });\r
2232 },\r
2233\r
2234 isBlock : function(n) {\r
2235 if (n.nodeType && n.nodeType !== 1)\r
2236 return false;\r
2237\r
2238 n = n.nodeName || n;\r
2239\r
2240 return blockRe.test(n);\r
2241 },\r
2242\r
2243 replace : function(n, o, k) {\r
2244 var t = this;\r
2245\r
2246 if (is(o, 'array'))\r
2247 n = n.cloneNode(true);\r
2248\r
2249 return t.run(o, function(o) {\r
2250 if (k) {\r
2251 each(tinymce.grep(o.childNodes), function(c) {\r
2252 n.appendChild(c);\r
2253 });\r
2254 }\r
2255\r
2256 return o.parentNode.replaceChild(n, o);\r
2257 });\r
2258 },\r
2259\r
2260 rename : function(elm, name) {\r
2261 var t = this, newElm;\r
2262\r
2263 if (elm.nodeName != name.toUpperCase()) {\r
2264 // Rename block element\r
2265 newElm = t.create(name);\r
2266\r
2267 // Copy attribs to new block\r
2268 each(t.getAttribs(elm), function(attr_node) {\r
2269 t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2270 });\r
2271\r
2272 // Replace block\r
2273 t.replace(newElm, elm, 1);\r
2274 }\r
2275\r
2276 return newElm || elm;\r
2277 },\r
2278\r
2279 findCommonAncestor : function(a, b) {\r
2280 var ps = a, pe;\r
2281\r
2282 while (ps) {\r
2283 pe = b;\r
2284\r
2285 while (pe && ps != pe)\r
2286 pe = pe.parentNode;\r
2287\r
2288 if (ps == pe)\r
2289 break;\r
2290\r
2291 ps = ps.parentNode;\r
2292 }\r
2293\r
2294 if (!ps && a.ownerDocument)\r
2295 return a.ownerDocument.documentElement;\r
2296\r
2297 return ps;\r
2298 },\r
2299\r
2300 toHex : function(s) {\r
2301 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2302\r
2303 function hex(s) {\r
2304 s = parseInt(s).toString(16);\r
2305\r
2306 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2307 };\r
2308\r
2309 if (c) {\r
2310 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2311\r
2312 return s;\r
2313 }\r
2314\r
2315 return s;\r
2316 },\r
2317\r
2318 getClasses : function() {\r
2319 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2320\r
2321 if (t.classes)\r
2322 return t.classes;\r
2323\r
2324 function addClasses(s) {\r
2325 // IE style imports\r
2326 each(s.imports, function(r) {\r
2327 addClasses(r);\r
2328 });\r
2329\r
2330 each(s.cssRules || s.rules, function(r) {\r
2331 // Real type or fake it on IE\r
2332 switch (r.type || 1) {\r
2333 // Rule\r
2334 case 1:\r
2335 if (r.selectorText) {\r
2336 each(r.selectorText.split(','), function(v) {\r
2337 v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2338\r
2339 // Is internal or it doesn't contain a class\r
2340 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2341 return;\r
2342\r
2343 // Remove everything but class name\r
2344 ov = v;\r
2345 v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
2346\r
2347 // Filter classes\r
2348 if (f && !(v = f(v, ov)))\r
2349 return;\r
2350\r
2351 if (!lo[v]) {\r
2352 cl.push({'class' : v});\r
2353 lo[v] = 1;\r
2354 }\r
2355 });\r
2356 }\r
2357 break;\r
2358\r
2359 // Import\r
2360 case 3:\r
2361 addClasses(r.styleSheet);\r
2362 break;\r
2363 }\r
2364 });\r
2365 };\r
2366\r
2367 try {\r
2368 each(t.doc.styleSheets, addClasses);\r
2369 } catch (ex) {\r
2370 // Ignore\r
2371 }\r
2372\r
2373 if (cl.length > 0)\r
2374 t.classes = cl;\r
2375\r
2376 return cl;\r
2377 },\r
2378\r
2379 run : function(e, f, s) {\r
2380 var t = this, o;\r
2381\r
2382 if (t.doc && typeof(e) === 'string')\r
2383 e = t.get(e);\r
2384\r
2385 if (!e)\r
2386 return false;\r
2387\r
2388 s = s || this;\r
2389 if (!e.nodeType && (e.length || e.length === 0)) {\r
2390 o = [];\r
2391\r
2392 each(e, function(e, i) {\r
2393 if (e) {\r
2394 if (typeof(e) == 'string')\r
2395 e = t.doc.getElementById(e);\r
2396\r
2397 o.push(f.call(s, e, i));\r
2398 }\r
2399 });\r
2400\r
2401 return o;\r
2402 }\r
2403\r
2404 return f.call(s, e);\r
2405 },\r
2406\r
2407 getAttribs : function(n) {\r
2408 var o;\r
2409\r
2410 n = this.get(n);\r
2411\r
2412 if (!n)\r
2413 return [];\r
2414\r
2415 if (isIE) {\r
2416 o = [];\r
2417\r
2418 // Object will throw exception in IE\r
2419 if (n.nodeName == 'OBJECT')\r
2420 return n.attributes;\r
2421\r
2422 // IE doesn't keep the selected attribute if you clone option elements\r
2423 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2424 o.push({specified : 1, nodeName : 'selected'});\r
2425\r
2426 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2427 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2428 o.push({specified : 1, nodeName : a});\r
2429 });\r
2430\r
2431 return o;\r
2432 }\r
2433\r
2434 return n.attributes;\r
2435 },\r
2436\r
2437 destroy : function(s) {\r
2438 var t = this;\r
2439\r
2440 if (t.events)\r
2441 t.events.destroy();\r
2442\r
2443 t.win = t.doc = t.root = t.events = null;\r
2444\r
2445 // Manual destroy then remove unload handler\r
2446 if (!s)\r
2447 tinymce.removeUnload(t.destroy);\r
2448 },\r
2449\r
2450 createRng : function() {\r
2451 var d = this.doc;\r
2452\r
2453 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2454 },\r
2455\r
2456 nodeIndex : function(node, normalized) {\r
2457 var idx = 0, lastNodeType, lastNode, nodeType, nodeValueExists;\r
2458\r
2459 if (node) {\r
2460 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2461 nodeType = node.nodeType;\r
2462\r
2463 // Normalize text nodes\r
2464 if (normalized && nodeType == 3) {\r
2465 // ensure that text nodes that have been removed are handled correctly in Internet Explorer.\r
2466 // (the nodeValue attribute will not exist, and will error here).\r
2467 nodeValueExists = false;\r
2468 try {nodeValueExists == node.nodeValue.length} catch (c) {}\r
2469 if (nodeType == lastNodeType || !nodeValueExists)\r
2470 continue;\r
2471 }\r
2472 idx++;\r
2473 lastNodeType = nodeType;\r
2474 }\r
2475 }\r
2476\r
2477 return idx;\r
2478 },\r
2479\r
2480 split : function(pe, e, re) {\r
2481 var t = this, r = t.createRng(), bef, aft, pa;\r
2482\r
2483 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2484 // but we don't want that in our code since it serves no purpose for the end user\r
2485 // For example if this is chopped:\r
2486 // <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2487 // would produce:\r
2488 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2489 // this function will then trim of empty edges and produce:\r
2490 // <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2491 function trim(node) {\r
2492 var i, children = node.childNodes;\r
2493\r
2494 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2495 return;\r
2496\r
2497 for (i = children.length - 1; i >= 0; i--)\r
2498 trim(children[i]);\r
2499\r
2500 if (node.nodeType != 9) {\r
2501 // Keep non whitespace text nodes\r
2502 if (node.nodeType == 3 && node.nodeValue.length > 0) {\r
2503 // If parent element isn't a block or there isn't any useful contents for example "<p> </p>"\r
2504 if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)\r
2505 return;\r
2506 }\r
2507\r
2508 if (node.nodeType == 1) {\r
2509 // If the only child is a bookmark then move it up\r
2510 children = node.childNodes;\r
2511 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2512 node.parentNode.insertBefore(children[0], node);\r
2513\r
2514 // Keep non empty elements or img, hr etc\r
2515 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2516 return;\r
2517 }\r
2518\r
2519 t.remove(node);\r
2520 }\r
2521\r
2522 return node;\r
2523 };\r
2524\r
2525 if (pe && e) {\r
2526 // Get before chunk\r
2527 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2528 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2529 bef = r.extractContents();\r
2530\r
2531 // Get after chunk\r
2532 r = t.createRng();\r
2533 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2534 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2535 aft = r.extractContents();\r
2536\r
2537 // Insert before chunk\r
2538 pa = pe.parentNode;\r
2539 pa.insertBefore(trim(bef), pe);\r
2540\r
2541 // Insert middle chunk\r
2542 if (re)\r
2543 pa.replaceChild(re, e);\r
2544 else\r
2545 pa.insertBefore(e, pe);\r
2546\r
2547 // Insert after chunk\r
2548 pa.insertBefore(trim(aft), pe);\r
2549 t.remove(pe);\r
2550\r
2551 return re || e;\r
2552 }\r
2553 },\r
2554\r
2555 bind : function(target, name, func, scope) {\r
2556 var t = this;\r
2557\r
2558 if (!t.events)\r
2559 t.events = new tinymce.dom.EventUtils();\r
2560\r
2561 return t.events.add(target, name, func, scope || this);\r
2562 },\r
2563\r
2564 unbind : function(target, name, func) {\r
2565 var t = this;\r
2566\r
2567 if (!t.events)\r
2568 t.events = new tinymce.dom.EventUtils();\r
2569\r
2570 return t.events.remove(target, name, func);\r
2571 },\r
2572\r
2573\r
2574 _findSib : function(node, selector, name) {\r
2575 var t = this, f = selector;\r
2576\r
2577 if (node) {\r
2578 // If expression make a function of it using is\r
2579 if (is(f, 'string')) {\r
2580 f = function(node) {\r
2581 return t.is(node, selector);\r
2582 };\r
2583 }\r
2584\r
2585 // Loop all siblings\r
2586 for (node = node[name]; node; node = node[name]) {\r
2587 if (f(node))\r
2588 return node;\r
2589 }\r
2590 }\r
2591\r
2592 return null;\r
2593 },\r
2594\r
2595 _isRes : function(c) {\r
2596 // Is live resizble element\r
2597 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2598 }\r
2599\r
2600 /*\r
2601 walk : function(n, f, s) {\r
2602 var d = this.doc, w;\r
2603\r
2604 if (d.createTreeWalker) {\r
2605 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2606\r
2607 while ((n = w.nextNode()) != null)\r
2608 f.call(s || this, n);\r
2609 } else\r
2610 tinymce.walk(n, f, 'childNodes', s);\r
2611 }\r
2612 */\r
2613\r
2614 /*\r
2615 toRGB : function(s) {\r
2616 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2617\r
2618 if (c) {\r
2619 // #FFF -> #FFFFFF\r
2620 if (!is(c[3]))\r
2621 c[3] = c[2] = c[1];\r
2622\r
2623 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2624 }\r
2625\r
2626 return s;\r
2627 }\r
2628 */\r
2629 });\r
2630\r
2631 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2632})(tinymce);\r
2633\r
2634(function(ns) {\r
2635 // Range constructor\r
2636 function Range(dom) {\r
2637 var t = this,\r
2638 doc = dom.doc,\r
2639 EXTRACT = 0,\r
2640 CLONE = 1,\r
2641 DELETE = 2,\r
2642 TRUE = true,\r
2643 FALSE = false,\r
2644 START_OFFSET = 'startOffset',\r
2645 START_CONTAINER = 'startContainer',\r
2646 END_CONTAINER = 'endContainer',\r
2647 END_OFFSET = 'endOffset',\r
2648 extend = tinymce.extend,\r
2649 nodeIndex = dom.nodeIndex;\r
2650\r
2651 extend(t, {\r
2652 // Inital states\r
2653 startContainer : doc,\r
2654 startOffset : 0,\r
2655 endContainer : doc,\r
2656 endOffset : 0,\r
2657 collapsed : TRUE,\r
2658 commonAncestorContainer : doc,\r
2659\r
2660 // Range constants\r
2661 START_TO_START : 0,\r
2662 START_TO_END : 1,\r
2663 END_TO_END : 2,\r
2664 END_TO_START : 3,\r
2665\r
2666 // Public methods\r
2667 setStart : setStart,\r
2668 setEnd : setEnd,\r
2669 setStartBefore : setStartBefore,\r
2670 setStartAfter : setStartAfter,\r
2671 setEndBefore : setEndBefore,\r
2672 setEndAfter : setEndAfter,\r
2673 collapse : collapse,\r
2674 selectNode : selectNode,\r
2675 selectNodeContents : selectNodeContents,\r
2676 compareBoundaryPoints : compareBoundaryPoints,\r
2677 deleteContents : deleteContents,\r
2678 extractContents : extractContents,\r
2679 cloneContents : cloneContents,\r
2680 insertNode : insertNode,\r
2681 surroundContents : surroundContents,\r
2682 cloneRange : cloneRange\r
2683 });\r
2684\r
2685 function setStart(n, o) {\r
2686 _setEndPoint(TRUE, n, o);\r
2687 };\r
2688\r
2689 function setEnd(n, o) {\r
2690 _setEndPoint(FALSE, n, o);\r
2691 };\r
2692\r
2693 function setStartBefore(n) {\r
2694 setStart(n.parentNode, nodeIndex(n));\r
2695 };\r
2696\r
2697 function setStartAfter(n) {\r
2698 setStart(n.parentNode, nodeIndex(n) + 1);\r
2699 };\r
2700\r
2701 function setEndBefore(n) {\r
2702 setEnd(n.parentNode, nodeIndex(n));\r
2703 };\r
2704\r
2705 function setEndAfter(n) {\r
2706 setEnd(n.parentNode, nodeIndex(n) + 1);\r
2707 };\r
2708\r
2709 function collapse(ts) {\r
2710 if (ts) {\r
2711 t[END_CONTAINER] = t[START_CONTAINER];\r
2712 t[END_OFFSET] = t[START_OFFSET];\r
2713 } else {\r
2714 t[START_CONTAINER] = t[END_CONTAINER];\r
2715 t[START_OFFSET] = t[END_OFFSET];\r
2716 }\r
2717\r
2718 t.collapsed = TRUE;\r
2719 };\r
2720\r
2721 function selectNode(n) {\r
2722 setStartBefore(n);\r
2723 setEndAfter(n);\r
2724 };\r
2725\r
2726 function selectNodeContents(n) {\r
2727 setStart(n, 0);\r
2728 setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2729 };\r
2730\r
2731 function compareBoundaryPoints(h, r) {\r
2732 var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],\r
2733 rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;\r
2734\r
2735 // Check START_TO_START\r
2736 if (h === 0)\r
2737 return _compareBoundaryPoints(sc, so, rsc, rso);\r
2738 \r
2739 // Check START_TO_END\r
2740 if (h === 1)\r
2741 return _compareBoundaryPoints(ec, eo, rsc, rso);\r
2742 \r
2743 // Check END_TO_END\r
2744 if (h === 2)\r
2745 return _compareBoundaryPoints(ec, eo, rec, reo);\r
2746 \r
2747 // Check END_TO_START\r
2748 if (h === 3) \r
2749 return _compareBoundaryPoints(sc, so, rec, reo);\r
2750 };\r
2751\r
2752 function deleteContents() {\r
2753 _traverse(DELETE);\r
2754 };\r
2755\r
2756 function extractContents() {\r
2757 return _traverse(EXTRACT);\r
2758 };\r
2759\r
2760 function cloneContents() {\r
2761 return _traverse(CLONE);\r
2762 };\r
2763\r
2764 function insertNode(n) {\r
2765 var startContainer = this[START_CONTAINER],\r
2766 startOffset = this[START_OFFSET], nn, o;\r
2767\r
2768 // Node is TEXT_NODE or CDATA\r
2769 if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2770 if (!startOffset) {\r
2771 // At the start of text\r
2772 startContainer.parentNode.insertBefore(n, startContainer);\r
2773 } else if (startOffset >= startContainer.nodeValue.length) {\r
2774 // At the end of text\r
2775 dom.insertAfter(n, startContainer);\r
2776 } else {\r
2777 // Middle, need to split\r
2778 nn = startContainer.splitText(startOffset);\r
2779 startContainer.parentNode.insertBefore(n, nn);\r
2780 }\r
2781 } else {\r
2782 // Insert element node\r
2783 if (startContainer.childNodes.length > 0)\r
2784 o = startContainer.childNodes[startOffset];\r
2785\r
2786 if (o)\r
2787 startContainer.insertBefore(n, o);\r
2788 else\r
2789 startContainer.appendChild(n);\r
2790 }\r
2791 };\r
2792\r
2793 function surroundContents(n) {\r
2794 var f = t.extractContents();\r
2795\r
2796 t.insertNode(n);\r
2797 n.appendChild(f);\r
2798 t.selectNode(n);\r
2799 };\r
2800\r
2801 function cloneRange() {\r
2802 return extend(new Range(dom), {\r
2803 startContainer : t[START_CONTAINER],\r
2804 startOffset : t[START_OFFSET],\r
2805 endContainer : t[END_CONTAINER],\r
2806 endOffset : t[END_OFFSET],\r
2807 collapsed : t.collapsed,\r
2808 commonAncestorContainer : t.commonAncestorContainer\r
2809 });\r
2810 };\r
2811\r
2812 // Private methods\r
2813\r
2814 function _getSelectedNode(container, offset) {\r
2815 var child;\r
2816\r
2817 if (container.nodeType == 3 /* TEXT_NODE */)\r
2818 return container;\r
2819\r
2820 if (offset < 0)\r
2821 return container;\r
2822\r
2823 child = container.firstChild;\r
2824 while (child && offset > 0) {\r
2825 --offset;\r
2826 child = child.nextSibling;\r
2827 }\r
2828\r
2829 if (child)\r
2830 return child;\r
2831\r
2832 return container;\r
2833 };\r
2834\r
2835 function _isCollapsed() {\r
2836 return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
2837 };\r
2838\r
2839 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
2840 var c, offsetC, n, cmnRoot, childA, childB;\r
2841 \r
2842 // In the first case the boundary-points have the same container. A is before B\r
2843 // if its offset is less than the offset of B, A is equal to B if its offset is\r
2844 // equal to the offset of B, and A is after B if its offset is greater than the\r
2845 // offset of B.\r
2846 if (containerA == containerB) {\r
2847 if (offsetA == offsetB)\r
2848 return 0; // equal\r
2849\r
2850 if (offsetA < offsetB)\r
2851 return -1; // before\r
2852\r
2853 return 1; // after\r
2854 }\r
2855\r
2856 // In the second case a child node C of the container of A is an ancestor\r
2857 // container of B. In this case, A is before B if the offset of A is less than or\r
2858 // equal to the index of the child node C and A is after B otherwise.\r
2859 c = containerB;\r
2860 while (c && c.parentNode != containerA)\r
2861 c = c.parentNode;\r
2862\r
2863 if (c) {\r
2864 offsetC = 0;\r
2865 n = containerA.firstChild;\r
2866\r
2867 while (n != c && offsetC < offsetA) {\r
2868 offsetC++;\r
2869 n = n.nextSibling;\r
2870 }\r
2871\r
2872 if (offsetA <= offsetC)\r
2873 return -1; // before\r
2874\r
2875 return 1; // after\r
2876 }\r
2877\r
2878 // In the third case a child node C of the container of B is an ancestor container\r
2879 // of A. In this case, A is before B if the index of the child node C is less than\r
2880 // the offset of B and A is after B otherwise.\r
2881 c = containerA;\r
2882 while (c && c.parentNode != containerB) {\r
2883 c = c.parentNode;\r
2884 }\r
2885\r
2886 if (c) {\r
2887 offsetC = 0;\r
2888 n = containerB.firstChild;\r
2889\r
2890 while (n != c && offsetC < offsetB) {\r
2891 offsetC++;\r
2892 n = n.nextSibling;\r
2893 }\r
2894\r
2895 if (offsetC < offsetB)\r
2896 return -1; // before\r
2897\r
2898 return 1; // after\r
2899 }\r
2900\r
2901 // In the fourth case, none of three other cases hold: the containers of A and B\r
2902 // are siblings or descendants of sibling nodes. In this case, A is before B if\r
2903 // the container of A is before the container of B in a pre-order traversal of the\r
2904 // Ranges' context tree and A is after B otherwise.\r
2905 cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
2906 childA = containerA;\r
2907\r
2908 while (childA && childA.parentNode != cmnRoot)\r
2909 childA = childA.parentNode;\r
2910\r
2911 if (!childA)\r
2912 childA = cmnRoot;\r
2913\r
2914 childB = containerB;\r
2915 while (childB && childB.parentNode != cmnRoot)\r
2916 childB = childB.parentNode;\r
2917\r
2918 if (!childB)\r
2919 childB = cmnRoot;\r
2920\r
2921 if (childA == childB)\r
2922 return 0; // equal\r
2923\r
2924 n = cmnRoot.firstChild;\r
2925 while (n) {\r
2926 if (n == childA)\r
2927 return -1; // before\r
2928\r
2929 if (n == childB)\r
2930 return 1; // after\r
2931\r
2932 n = n.nextSibling;\r
2933 }\r
2934 };\r
2935\r
2936 function _setEndPoint(st, n, o) {\r
2937 var ec, sc;\r
2938\r
2939 if (st) {\r
2940 t[START_CONTAINER] = n;\r
2941 t[START_OFFSET] = o;\r
2942 } else {\r
2943 t[END_CONTAINER] = n;\r
2944 t[END_OFFSET] = o;\r
2945 }\r
2946\r
2947 // If one boundary-point of a Range is set to have a root container\r
2948 // other than the current one for the Range, the Range is collapsed to\r
2949 // the new position. This enforces the restriction that both boundary-\r
2950 // points of a Range must have the same root container.\r
2951 ec = t[END_CONTAINER];\r
2952 while (ec.parentNode)\r
2953 ec = ec.parentNode;\r
2954\r
2955 sc = t[START_CONTAINER];\r
2956 while (sc.parentNode)\r
2957 sc = sc.parentNode;\r
2958\r
2959 if (sc == ec) {\r
2960 // The start position of a Range is guaranteed to never be after the\r
2961 // end position. To enforce this restriction, if the start is set to\r
2962 // be at a position after the end, the Range is collapsed to that\r
2963 // position.\r
2964 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
2965 t.collapse(st);\r
2966 } else\r
2967 t.collapse(st);\r
2968\r
2969 t.collapsed = _isCollapsed();\r
2970 t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
2971 };\r
2972\r
2973 function _traverse(how) {\r
2974 var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2975\r
2976 if (t[START_CONTAINER] == t[END_CONTAINER])\r
2977 return _traverseSameContainer(how);\r
2978\r
2979 for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2980 if (p == t[START_CONTAINER])\r
2981 return _traverseCommonStartContainer(c, how);\r
2982\r
2983 ++endContainerDepth;\r
2984 }\r
2985\r
2986 for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2987 if (p == t[END_CONTAINER])\r
2988 return _traverseCommonEndContainer(c, how);\r
2989\r
2990 ++startContainerDepth;\r
2991 }\r
2992\r
2993 depthDiff = startContainerDepth - endContainerDepth;\r
2994\r
2995 startNode = t[START_CONTAINER];\r
2996 while (depthDiff > 0) {\r
2997 startNode = startNode.parentNode;\r
2998 depthDiff--;\r
2999 }\r
3000\r
3001 endNode = t[END_CONTAINER];\r
3002 while (depthDiff < 0) {\r
3003 endNode = endNode.parentNode;\r
3004 depthDiff++;\r
3005 }\r
3006\r
3007 // ascend the ancestor hierarchy until we have a common parent.\r
3008 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
3009 startNode = sp;\r
3010 endNode = ep;\r
3011 }\r
3012\r
3013 return _traverseCommonAncestors(startNode, endNode, how);\r
3014 };\r
3015\r
3016 function _traverseSameContainer(how) {\r
3017 var frag, s, sub, n, cnt, sibling, xferNode;\r
3018\r
3019 if (how != DELETE)\r
3020 frag = doc.createDocumentFragment();\r
3021\r
3022 // If selection is empty, just return the fragment\r
3023 if (t[START_OFFSET] == t[END_OFFSET])\r
3024 return frag;\r
3025\r
3026 // Text node needs special case handling\r
3027 if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
3028 // get the substring\r
3029 s = t[START_CONTAINER].nodeValue;\r
3030 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
3031\r
3032 // set the original text node to its new value\r
3033 if (how != CLONE) {\r
3034 t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
3035\r
3036 // Nothing is partially selected, so collapse to start point\r
3037 t.collapse(TRUE);\r
3038 }\r
3039\r
3040 if (how == DELETE)\r
3041 return;\r
3042\r
3043 frag.appendChild(doc.createTextNode(sub));\r
3044 return frag;\r
3045 }\r
3046\r
3047 // Copy nodes between the start/end offsets.\r
3048 n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
3049 cnt = t[END_OFFSET] - t[START_OFFSET];\r
3050\r
3051 while (cnt > 0) {\r
3052 sibling = n.nextSibling;\r
3053 xferNode = _traverseFullySelected(n, how);\r
3054\r
3055 if (frag)\r
3056 frag.appendChild( xferNode );\r
3057\r
3058 --cnt;\r
3059 n = sibling;\r
3060 }\r
3061\r
3062 // Nothing is partially selected, so collapse to start point\r
3063 if (how != CLONE)\r
3064 t.collapse(TRUE);\r
3065\r
3066 return frag;\r
3067 };\r
3068\r
3069 function _traverseCommonStartContainer(endAncestor, how) {\r
3070 var frag, n, endIdx, cnt, sibling, xferNode;\r
3071\r
3072 if (how != DELETE)\r
3073 frag = doc.createDocumentFragment();\r
3074\r
3075 n = _traverseRightBoundary(endAncestor, how);\r
3076\r
3077 if (frag)\r
3078 frag.appendChild(n);\r
3079\r
3080 endIdx = nodeIndex(endAncestor);\r
3081 cnt = endIdx - t[START_OFFSET];\r
3082\r
3083 if (cnt <= 0) {\r
3084 // Collapse to just before the endAncestor, which\r
3085 // is partially selected.\r
3086 if (how != CLONE) {\r
3087 t.setEndBefore(endAncestor);\r
3088 t.collapse(FALSE);\r
3089 }\r
3090\r
3091 return frag;\r
3092 }\r
3093\r
3094 n = endAncestor.previousSibling;\r
3095 while (cnt > 0) {\r
3096 sibling = n.previousSibling;\r
3097 xferNode = _traverseFullySelected(n, how);\r
3098\r
3099 if (frag)\r
3100 frag.insertBefore(xferNode, frag.firstChild);\r
3101\r
3102 --cnt;\r
3103 n = sibling;\r
3104 }\r
3105\r
3106 // Collapse to just before the endAncestor, which\r
3107 // is partially selected.\r
3108 if (how != CLONE) {\r
3109 t.setEndBefore(endAncestor);\r
3110 t.collapse(FALSE);\r
3111 }\r
3112\r
3113 return frag;\r
3114 };\r
3115\r
3116 function _traverseCommonEndContainer(startAncestor, how) {\r
3117 var frag, startIdx, n, cnt, sibling, xferNode;\r
3118\r
3119 if (how != DELETE)\r
3120 frag = doc.createDocumentFragment();\r
3121\r
3122 n = _traverseLeftBoundary(startAncestor, how);\r
3123 if (frag)\r
3124 frag.appendChild(n);\r
3125\r
3126 startIdx = nodeIndex(startAncestor);\r
3127 ++startIdx; // Because we already traversed it....\r
3128\r
3129 cnt = t[END_OFFSET] - startIdx;\r
3130 n = startAncestor.nextSibling;\r
3131 while (cnt > 0) {\r
3132 sibling = n.nextSibling;\r
3133 xferNode = _traverseFullySelected(n, how);\r
3134\r
3135 if (frag)\r
3136 frag.appendChild(xferNode);\r
3137\r
3138 --cnt;\r
3139 n = sibling;\r
3140 }\r
3141\r
3142 if (how != CLONE) {\r
3143 t.setStartAfter(startAncestor);\r
3144 t.collapse(TRUE);\r
3145 }\r
3146\r
3147 return frag;\r
3148 };\r
3149\r
3150 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3151 var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3152\r
3153 if (how != DELETE)\r
3154 frag = doc.createDocumentFragment();\r
3155\r
3156 n = _traverseLeftBoundary(startAncestor, how);\r
3157 if (frag)\r
3158 frag.appendChild(n);\r
3159\r
3160 commonParent = startAncestor.parentNode;\r
3161 startOffset = nodeIndex(startAncestor);\r
3162 endOffset = nodeIndex(endAncestor);\r
3163 ++startOffset;\r
3164\r
3165 cnt = endOffset - startOffset;\r
3166 sibling = startAncestor.nextSibling;\r
3167\r
3168 while (cnt > 0) {\r
3169 nextSibling = sibling.nextSibling;\r
3170 n = _traverseFullySelected(sibling, how);\r
3171\r
3172 if (frag)\r
3173 frag.appendChild(n);\r
3174\r
3175 sibling = nextSibling;\r
3176 --cnt;\r
3177 }\r
3178\r
3179 n = _traverseRightBoundary(endAncestor, how);\r
3180\r
3181 if (frag)\r
3182 frag.appendChild(n);\r
3183\r
3184 if (how != CLONE) {\r
3185 t.setStartAfter(startAncestor);\r
3186 t.collapse(TRUE);\r
3187 }\r
3188\r
3189 return frag;\r
3190 };\r
3191\r
3192 function _traverseRightBoundary(root, how) {\r
3193 var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3194\r
3195 if (next == root)\r
3196 return _traverseNode(next, isFullySelected, FALSE, how);\r
3197\r
3198 parent = next.parentNode;\r
3199 clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3200\r
3201 while (parent) {\r
3202 while (next) {\r
3203 prevSibling = next.previousSibling;\r
3204 clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3205\r
3206 if (how != DELETE)\r
3207 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3208\r
3209 isFullySelected = TRUE;\r
3210 next = prevSibling;\r
3211 }\r
3212\r
3213 if (parent == root)\r
3214 return clonedParent;\r
3215\r
3216 next = parent.previousSibling;\r
3217 parent = parent.parentNode;\r
3218\r
3219 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3220\r
3221 if (how != DELETE)\r
3222 clonedGrandParent.appendChild(clonedParent);\r
3223\r
3224 clonedParent = clonedGrandParent;\r
3225 }\r
3226 };\r
3227\r
3228 function _traverseLeftBoundary(root, how) {\r
3229 var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3230\r
3231 if (next == root)\r
3232 return _traverseNode(next, isFullySelected, TRUE, how);\r
3233\r
3234 parent = next.parentNode;\r
3235 clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3236\r
3237 while (parent) {\r
3238 while (next) {\r
3239 nextSibling = next.nextSibling;\r
3240 clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3241\r
3242 if (how != DELETE)\r
3243 clonedParent.appendChild(clonedChild);\r
3244\r
3245 isFullySelected = TRUE;\r
3246 next = nextSibling;\r
3247 }\r
3248\r
3249 if (parent == root)\r
3250 return clonedParent;\r
3251\r
3252 next = parent.nextSibling;\r
3253 parent = parent.parentNode;\r
3254\r
3255 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3256\r
3257 if (how != DELETE)\r
3258 clonedGrandParent.appendChild(clonedParent);\r
3259\r
3260 clonedParent = clonedGrandParent;\r
3261 }\r
3262 };\r
3263\r
3264 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3265 var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3266\r
3267 if (isFullySelected)\r
3268 return _traverseFullySelected(n, how);\r
3269\r
3270 if (n.nodeType == 3 /* TEXT_NODE */) {\r
3271 txtValue = n.nodeValue;\r
3272\r
3273 if (isLeft) {\r
3274 offset = t[START_OFFSET];\r
3275 newNodeValue = txtValue.substring(offset);\r
3276 oldNodeValue = txtValue.substring(0, offset);\r
3277 } else {\r
3278 offset = t[END_OFFSET];\r
3279 newNodeValue = txtValue.substring(0, offset);\r
3280 oldNodeValue = txtValue.substring(offset);\r
3281 }\r
3282\r
3283 if (how != CLONE)\r
3284 n.nodeValue = oldNodeValue;\r
3285\r
3286 if (how == DELETE)\r
3287 return;\r
3288\r
3289 newNode = n.cloneNode(FALSE);\r
3290 newNode.nodeValue = newNodeValue;\r
3291\r
3292 return newNode;\r
3293 }\r
3294\r
3295 if (how == DELETE)\r
3296 return;\r
3297\r
3298 return n.cloneNode(FALSE);\r
3299 };\r
3300\r
3301 function _traverseFullySelected(n, how) {\r
3302 if (how != DELETE)\r
3303 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3304\r
3305 n.parentNode.removeChild(n);\r
3306 };\r
3307 };\r
3308\r
3309 ns.Range = Range;\r
3310})(tinymce.dom);\r
3311\r
3312(function() {\r
3313 function Selection(selection) {\r
3314 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3315\r
3316 // Returns a W3C DOM compatible range object by using the IE Range API\r
3317 function getRange() {\r
3318 var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
3319\r
3320 // If selection is outside the current document just return an empty range\r
3321 element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3322 if (element.ownerDocument != dom.doc)\r
3323 return domRange;\r
3324\r
3325 // Handle control selection or text selection of a image\r
3326 if (ieRange.item || !element.hasChildNodes()) {\r
3327 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3328 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3329\r
3330 return domRange;\r
3331 }\r
3332\r
3333 collapsed = selection.isCollapsed();\r
3334\r
3335 function findEndPoint(start) {\r
3336 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
3337\r
3338 // Setup temp range and collapse it\r
3339 checkRng = ieRange.duplicate();\r
3340 checkRng.collapse(start);\r
3341\r
3342 // Create marker and insert it at the end of the endpoints parent\r
3343 marker = dom.create('a');\r
3344 parent = checkRng.parentElement();\r
3345\r
3346 // If parent doesn't have any children then set the container to that parent and the index to 0\r
3347 if (!parent.hasChildNodes()) {\r
3348 domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
3349 return;\r
3350 }\r
3351\r
3352 parent.appendChild(marker);\r
3353 checkRng.moveToElementText(marker);\r
3354 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3355 if (position > 0) {\r
3356 // The position is after the end of the parent element.\r
3357 // This is the case where IE puts the caret to the left edge of a table.\r
3358 domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
3359 dom.remove(marker);\r
3360 return;\r
3361 }\r
3362\r
3363 // Setup node list and endIndex\r
3364 nodes = tinymce.grep(parent.childNodes);\r
3365 endIndex = nodes.length - 1;\r
3366 // Perform a binary search for the position\r
3367 while (startIndex <= endIndex) {\r
3368 index = Math.floor((startIndex + endIndex) / 2);\r
3369\r
3370 // Insert marker and check it's position relative to the selection\r
3371 parent.insertBefore(marker, nodes[index]);\r
3372 checkRng.moveToElementText(marker);\r
3373 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3374 if (position > 0) {\r
3375 // Marker is to the right\r
3376 startIndex = index + 1;\r
3377 } else if (position < 0) {\r
3378 // Marker is to the left\r
3379 endIndex = index - 1;\r
3380 } else {\r
3381 // Maker is where we are\r
3382 found = true;\r
3383 break;\r
3384 }\r
3385 }\r
3386\r
3387 // Setup container\r
3388 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
3389\r
3390 // Handle element selection\r
3391 if (container.nodeType == 1) {\r
3392 dom.remove(marker);\r
3393\r
3394 // Find offset and container\r
3395 offset = dom.nodeIndex(container);\r
3396 container = container.parentNode;\r
3397\r
3398 // Move the offset if we are setting the end or the position is after an element\r
3399 if (!start || index > 0)\r
3400 offset++;\r
3401 } else {\r
3402 // Calculate offset within text node\r
3403 if (position > 0 || index == 0) {\r
3404 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3405 offset = checkRng.text.length;\r
3406 } else {\r
3407 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3408 offset = container.nodeValue.length - checkRng.text.length;\r
3409 }\r
3410\r
3411 dom.remove(marker);\r
3412 }\r
3413\r
3414 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
3415 };\r
3416\r
3417 // Find start point\r
3418 findEndPoint(true);\r
3419\r
3420 // Find end point if needed\r
3421 if (!collapsed)\r
3422 findEndPoint();\r
3423\r
3424 return domRange;\r
3425 };\r
3426\r
3427 this.addRange = function(rng) {\r
3428 var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
3429\r
3430 function setEndPoint(start) {\r
3431 var container, offset, marker, tmpRng, nodes;\r
3432\r
3433 marker = dom.create('a');\r
3434 container = start ? startContainer : endContainer;\r
3435 offset = start ? startOffset : endOffset;\r
3436 tmpRng = ieRng.duplicate();\r
3437\r
3438 if (container == doc) {\r
3439 container = body;\r
3440 offset = 0;\r
3441 }\r
3442\r
3443 if (container.nodeType == 3) {\r
3444 container.parentNode.insertBefore(marker, container);\r
3445 tmpRng.moveToElementText(marker);\r
3446 tmpRng.moveStart('character', offset);\r
3447 dom.remove(marker);\r
3448 ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3449 } else {\r
3450 nodes = container.childNodes;\r
3451\r
3452 if (nodes.length) {\r
3453 if (offset >= nodes.length) {\r
3454 dom.insertAfter(marker, nodes[nodes.length - 1]);\r
3455 } else {\r
3456 container.insertBefore(marker, nodes[offset]);\r
3457 }\r
3458\r
3459 tmpRng.moveToElementText(marker);\r
3460 } else {\r
3461 // Empty node selection for example <div>|</div>\r
3462 marker = doc.createTextNode(invisibleChar);\r
3463 container.appendChild(marker);\r
3464 tmpRng.moveToElementText(marker.parentNode);\r
3465 tmpRng.collapse(TRUE);\r
3466 }\r
3467\r
3468 ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3469 dom.remove(marker);\r
3470 }\r
3471 }\r
3472\r
3473 // Destroy cached range\r
3474 this.destroy();\r
3475\r
3476 // Setup some shorter versions\r
3477 startContainer = rng.startContainer;\r
3478 startOffset = rng.startOffset;\r
3479 endContainer = rng.endContainer;\r
3480 endOffset = rng.endOffset;\r
3481 ieRng = body.createTextRange();\r
3482\r
3483 // If single element selection then try making a control selection out of it\r
3484 if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
3485 if (startOffset == endOffset - 1) {\r
3486 try {\r
3487 ctrlRng = body.createControlRange();\r
3488 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
3489 ctrlRng.select();\r
3490 return;\r
3491 } catch (ex) {\r
3492 // Ignore\r
3493 }\r
3494 }\r
3495 }\r
3496\r
3497 // Set start/end point of selection\r
3498 setEndPoint(true);\r
3499 setEndPoint();\r
3500\r
3501 // Select the new range and scroll it into view\r
3502 ieRng.select();\r
3503 };\r
3504