| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 var tinymce = { 2 majorVersion : '3', 3 minorVersion : '2.6', 4 releaseDate : '2009-08-19', 5 6 _init : function() { 7 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; 8 9 t.isOpera = w.opera && opera.buildNumber; 10 11 t.isWebKit = /WebKit/.test(ua); 12 13 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName); 14 15 t.isIE6 = t.isIE && /MSIE [56]/.test(ua); 16 17 t.isGecko = !t.isWebKit && /Gecko/.test(ua); 18 19 t.isMac = ua.indexOf('Mac') != -1; 20 21 t.isAir = /adobeair/i.test(ua); 22 23 // TinyMCE .NET webcontrol might be setting the values for TinyMCE 24 if (w.tinyMCEPreInit) { 25 t.suffix = tinyMCEPreInit.suffix; 26 t.baseURL = tinyMCEPreInit.base; 27 t.query = tinyMCEPreInit.query; 28 return; 29 } 30 31 // Get suffix and base 32 t.suffix = ''; 33 34 // If base element found, add that infront of baseURL 35 nl = d.getElementsByTagName('base'); 36 for (i=0; i<nl.length; i++) { 37 if (v = nl[i].href) { 38 // Host only value like http://site.com or http://site.com:8008 39 if (/^https?:\/\/[^\/]+$/.test(v)) 40 v += '/'; 41 42 base = v ? v.match(/.*\//)[0] : ''; // Get only directory 43 } 44 } 45 46 function getBase(n) { 47 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) { 48 if (/_(src|dev)\.js/g.test(n.src)) 49 t.suffix = '_src'; 50 51 if ((p = n.src.indexOf('?')) != -1) 52 t.query = n.src.substring(p + 1); 53 54 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/')); 55 56 // If path to script is relative and a base href was found add that one infront 57 // the src property will always be an absolute one on non IE browsers and IE 8 58 // so this logic will basically only be executed on older IE versions 59 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0) 60 t.baseURL = base + t.baseURL; 61 62 return t.baseURL; 63 } 64 65 return null; 66 }; 67 68 // Check document 69 nl = d.getElementsByTagName('script'); 70 for (i=0; i<nl.length; i++) { 71 if (getBase(nl[i])) 72 return; 73 } 74 75 // Check head 76 n = d.getElementsByTagName('head')[0]; 77 if (n) { 78 nl = n.getElementsByTagName('script'); 79 for (i=0; i<nl.length; i++) { 80 if (getBase(nl[i])) 81 return; 82 } 83 } 84 85 return; 86 }, 87 88 is : function(o, t) { 89 var n = typeof(o); 90 91 if (!t) 92 return n != 'undefined'; 93 94 if (t == 'array' && (o.hasOwnProperty && o instanceof Array)) 95 return true; 96 97 return n == t; 98 }, 99 100 each : function(o, cb, s) { 101 var n, l; 102 103 if (!o) 104 return 0; 105 106 s = s || o; 107 108 if (typeof(o.length) != 'undefined') { 109 // Indexed arrays, needed for Safari 110 for (n=0, l = o.length; n<l; n++) { 111 if (cb.call(s, o[n], n, o) === false) 112 return 0; 113 } 114 } else { 115 // Hashtables 116 for (n in o) { 117 if (o.hasOwnProperty(n)) { 118 if (cb.call(s, o[n], n, o) === false) 119 return 0; 120 } 121 } 122 } 123 124 return 1; 125 }, 126 127 128 map : function(a, f) { 129 var o = []; 130 131 tinymce.each(a, function(v) { 132 o.push(f(v)); 133 }); 134 135 return o; 136 }, 137 138 grep : function(a, f) { 139 var o = []; 140 141 tinymce.each(a, function(v) { 142 if (!f || f(v)) 143 o.push(v); 144 }); 145 146 return o; 147 }, 148 149 inArray : function(a, v) { 150 var i, l; 151 152 if (a) { 153 for (i = 0, l = a.length; i < l; i++) { 154 if (a[i] === v) 155 return i; 156 } 157 } 158 159 return -1; 160 }, 161 162 extend : function(o, e) { 163 var i, a = arguments; 164 165 for (i=1; i<a.length; i++) { 166 e = a[i]; 167 168 tinymce.each(e, function(v, n) { 169 if (typeof(v) !== 'undefined') 170 o[n] = v; 171 }); 172 } 173 174 return o; 175 }, 176 177 178 trim : function(s) { 179 return (s ? '' + s : '').replace(/^\s*|\s*$/g, ''); 180 }, 181 182 create : function(s, p) { 183 var t = this, sp, ns, cn, scn, c, de = 0; 184 185 // Parse : <prefix> <class>:<super class> 186 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); 187 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name 188 189 // Create namespace for new class 190 ns = t.createNS(s[3].replace(/\.\w+$/, '')); 191 192 // Class already exists 193 if (ns[cn]) 194 return; 195 196 // Make pure static class 197 if (s[2] == 'static') { 198 ns[cn] = p; 199 200 if (this.onCreate) 201 this.onCreate(s[2], s[3], ns[cn]); 202 203 return; 204 } 205 206 // Create default constructor 207 if (!p[cn]) { 208 p[cn] = function() {}; 209 de = 1; 210 } 211 212 // Add constructor and methods 213 ns[cn] = p[cn]; 214 t.extend(ns[cn].prototype, p); 215 216 // Extend 217 if (s[5]) { 218 sp = t.resolve(s[5]).prototype; 219 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name 220 221 // Extend constructor 222 c = ns[cn]; 223 if (de) { 224 // Add passthrough constructor 225 ns[cn] = function() { 226 return sp[scn].apply(this, arguments); 227 }; 228 } else { 229 // Add inherit constructor 230 ns[cn] = function() { 231 this.parent = sp[scn]; 232 return c.apply(this, arguments); 233 }; 234 } 235 ns[cn].prototype[cn] = ns[cn]; 236 237 // Add super methods 238 t.each(sp, function(f, n) { 239 ns[cn].prototype[n] = sp[n]; 240 }); 241 242 // Add overridden methods 243 t.each(p, function(f, n) { 244 // Extend methods if needed 245 if (sp[n]) { 246 ns[cn].prototype[n] = function() { 247 this.parent = sp[n]; 248 return f.apply(this, arguments); 249 }; 250 } else { 251 if (n != cn) 252 ns[cn].prototype[n] = f; 253 } 254 }); 255 } 256 257 // Add static methods 258 t.each(p['static'], function(f, n) { 259 ns[cn][n] = f; 260 }); 261 262 if (this.onCreate) 263 this.onCreate(s[2], s[3], ns[cn].prototype); 264 }, 265 266 walk : function(o, f, n, s) { 267 s = s || this; 268 269 if (o) { 270 if (n) 271 o = o[n]; 272 273 tinymce.each(o, function(o, i) { 274 if (f.call(s, o, i, n) === false) 275 return false; 276 277 tinymce.walk(o, f, n, s); 278 }); 279 } 280 }, 281 282 createNS : function(n, o) { 283 var i, v; 284 285 o = o || window; 286 287 n = n.split('.'); 288 for (i=0; i<n.length; i++) { 289 v = n[i]; 290 291 if (!o[v]) 292 o[v] = {}; 293 294 o = o[v]; 295 } 296 297 return o; 298 }, 299 300 resolve : function(n, o) { 301 var i, l; 302 303 o = o || window; 304 305 n = n.split('.'); 306 for (i = 0, l = n.length; i < l; i++) { 307 o = o[n[i]]; 308 309 if (!o) 310 break; 311 } 312 313 return o; 314 }, 315 316 addUnload : function(f, s) { 317 var t = this, w = window; 318 319 f = {func : f, scope : s || this}; 320 321 if (!t.unloads) { 322 function unload() { 323 var li = t.unloads, o, n; 324 325 if (li) { 326 // Call unload handlers 327 for (n in li) { 328 o = li[n]; 329 330 if (o && o.func) 331 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy 332 } 333 334 // Detach unload function 335 if (w.detachEvent) { 336 w.detachEvent('onbeforeunload', fakeUnload); 337 w.detachEvent('onunload', unload); 338 } else if (w.removeEventListener) 339 w.removeEventListener('unload', unload, false); 340 341 // Destroy references 342 t.unloads = o = li = w = unload = 0; 343 344 // Run garbarge collector on IE 345 if (window.CollectGarbage) 346 window.CollectGarbage(); 347 } 348 }; 349 350 function fakeUnload() { 351 var d = document; 352 353 // Is there things still loading, then do some magic 354 if (d.readyState == 'interactive') { 355 function stop() { 356 // Prevent memory leak 357 d.detachEvent('onstop', stop); 358 359 // Call unload handler 360 if (unload) 361 unload(); 362 363 d = 0; 364 }; 365 366 // Fire unload when the currently loading page is stopped 367 if (d) 368 d.attachEvent('onstop', stop); 369 370 // Remove onstop listener after a while to prevent the unload function 371 // to execute if the user presses cancel in an onbeforeunload 372 // confirm dialog and then presses the browser stop button 373 window.setTimeout(function() { 374 if (d) 375 d.detachEvent('onstop', stop); 376 }, 0); 377 } 378 }; 379 380 // Attach unload handler 381 if (w.attachEvent) { 382 w.attachEvent('onunload', unload); 383 w.attachEvent('onbeforeunload', fakeUnload); 384 } else if (w.addEventListener) 385 w.addEventListener('unload', unload, false); 386 387 // Setup initial unload handler array 388 t.unloads = [f]; 389 } else 390 t.unloads.push(f); 391 392 return f; 393 }, 394 395 removeUnload : function(f) { 396 var u = this.unloads, r = null; 397 398 tinymce.each(u, function(o, i) { 399 if (o && o.func == f) { 400 u.splice(i, 1); 401 r = f; 402 return false; 403 } 404 }); 405 406 return r; 407 }, 408 409 explode : function(s, d) { 410 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s; 411 }, 412 413 _addVer : function(u) { 414 var v; 415 416 if (!this.query) 417 return u; 418 419 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query; 420 421 if (u.indexOf('#') == -1) 422 return u + v; 423 424 return u.replace('#', v + '#'); 425 } 426 427 }; 428 429 // Required for GZip AJAX loading 430 window.tinymce = tinymce; 431 432 // Initialize the API 433 tinymce._init(); 434 tinymce.create('tinymce.util.Dispatcher', { 435 scope : null, 436 listeners : null, 437 438 Dispatcher : function(s) { 439 this.scope = s || this; 440 this.listeners = []; 441 }, 442 443 add : function(cb, s) { 444 this.listeners.push({cb : cb, scope : s || this.scope}); 445 446 return cb; 447 }, 448 449 addToTop : function(cb, s) { 450 this.listeners.unshift({cb : cb, scope : s || this.scope}); 451 452 return cb; 453 }, 454 455 remove : function(cb) { 456 var l = this.listeners, o = null; 457 458 tinymce.each(l, function(c, i) { 459 if (cb == c.cb) { 460 o = cb; 461 l.splice(i, 1); 462 return false; 463 } 464 }); 465 466 return o; 467 }, 468 469 dispatch : function() { 470 var s, a = arguments, i, li = this.listeners, c; 471 472 // Needs to be a real loop since the listener count might change while looping 473 // And this is also more efficient 474 for (i = 0; i<li.length; i++) { 475 c = li[i]; 476 s = c.cb.apply(c.scope, a); 477 478 if (s === false) 479 break; 480 } 481 482 return s; 483 } 484 485 }); 486 (function() { 487 var each = tinymce.each; 488 489 tinymce.create('tinymce.util.URI', { 490 URI : function(u, s) { 491 var t = this, o, a, b; 492 493 // Trim whitespace 494 u = tinymce.trim(u); 495 496 // Default settings 497 s = t.settings = s || {}; 498 499 // Strange app protocol or local anchor 500 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) { 501 t.source = u; 502 return; 503 } 504 505 // Absolute path with no host, fake host and protocol 506 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0) 507 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u; 508 509 // Relative path http:// or protocol relative //path 510 if (!/^\w*:?\/\//.test(u)) 511 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u); 512 513 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) 514 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something 515 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u); 516 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) { 517 var s = u[i]; 518 519 // Zope 3 workaround, they use @@something 520 if (s) 521 s = s.replace(/\(mce_at\)/g, '@@'); 522 523 t[v] = s; 524 }); 525 526 if (b = s.base_uri) { 527 if (!t.protocol) 528 t.protocol = b.protocol; 529 530 if (!t.userInfo) 531 t.userInfo = b.userInfo; 532 533 if (!t.port && t.host == 'mce_host') 534 t.port = b.port; 535 536 if (!t.host || t.host == 'mce_host') 537 t.host = b.host; 538 539 t.source = ''; 540 } 541 542 //t.path = t.path || '/'; 543 }, 544 545 setPath : function(p) { 546 var t = this; 547 548 p = /^(.*?)\/?(\w+)?$/.exec(p); 549 550 // Update path parts 551 t.path = p[0]; 552 t.directory = p[1]; 553 t.file = p[2]; 554 555 // Rebuild source 556 t.source = ''; 557 t.getURI(); 558 }, 559 560 toRelative : function(u) { 561 var t = this, o; 562 563 if (u === "./") 564 return u; 565 566 u = new tinymce.util.URI(u, {base_uri : t}); 567 568 // Not on same domain/port or protocol 569 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol) 570 return u.getURI(); 571 572 o = t.toRelPath(t.path, u.path); 573 574 // Add query 575 if (u.query) 576 o += '?' + u.query; 577 578 // Add anchor 579 if (u.anchor) 580 o += '#' + u.anchor; 581 582 return o; 583 }, 584 585 toAbsolute : function(u, nh) { 586 var u = new tinymce.util.URI(u, {base_uri : this}); 587 588 return u.getURI(this.host == u.host ? nh : 0); 589 }, 590 591 toRelPath : function(base, path) { 592 var items, bp = 0, out = '', i, l; 593 594 // Split the paths 595 base = base.substring(0, base.lastIndexOf('/')); 596 base = base.split('/'); 597 items = path.split('/'); 598 599 if (base.length >= items.length) { 600 for (i = 0, l = base.length; i < l; i++) { 601 if (i >= items.length || base[i] != items[i]) { 602 bp = i + 1; 603 break; 604 } 605 } 606 } 607 608 if (base.length < items.length) { 609 for (i = 0, l = items.length; i < l; i++) { 610 if (i >= base.length || base[i] != items[i]) { 611 bp = i + 1; 612 break; 613 } 614 } 615 } 616 617 if (bp == 1) 618 return path; 619 620 for (i = 0, l = base.length - (bp - 1); i < l; i++) 621 out += "../"; 622 623 for (i = bp - 1, l = items.length; i < l; i++) { 624 if (i != bp - 1) 625 out += "/" + items[i]; 626 else 627 out += items[i]; 628 } 629 630 return out; 631 }, 632 633 toAbsPath : function(base, path) { 634 var i, nb = 0, o = [], tr; 635 636 // Split paths 637 tr = /\/$/.test(path) ? '/' : ''; 638 base = base.split('/'); 639 path = path.split('/'); 640 641 // Remove empty chunks 642 each(base, function(k) { 643 if (k) 644 o.push(k); 645 }); 646 647 base = o; 648 649 // Merge relURLParts chunks 650 for (i = path.length - 1, o = []; i >= 0; i--) { 651 // Ignore empty or . 652 if (path[i].length == 0 || path[i] == ".") 653 continue; 654 655 // Is parent 656 if (path[i] == '..') { 657 nb++; 658 continue; 659 } 660 661 // Move up 662 if (nb > 0) { 663 nb--; 664 continue; 665 } 666 667 o.push(path[i]); 668 } 669 670 i = base.length - nb; 671 672 // If /a/b/c or / 673 if (i <= 0) 674 return '/' + o.reverse().join('/') + tr; 675 676 return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr; 677 }, 678 679 getURI : function(nh) { 680 var s, t = this; 681 682 // Rebuild source 683 if (!t.source || nh) { 684 s = ''; 685 686 if (!nh) { 687 if (t.protocol) 688 s += t.protocol + '://'; 689 690 if (t.userInfo) 691 s += t.userInfo + '@'; 692 693 if (t.host) 694 s += t.host; 695 696 if (t.port) 697 s += ':' + t.port; 698 } 699 700 if (t.path) 701 s += t.path; 702 703 if (t.query) 704 s += '?' + t.query; 705 706 if (t.anchor) 707 s += '#' + t.anchor; 708 709 t.source = s; 710 } 711 712 return t.source; 713 } 714 }); 715 })(); 716 (function() { 717 var each = tinymce.each; 718 719 tinymce.create('static tinymce.util.Cookie', { 720 getHash : function(n) { 721 var v = this.get(n), h; 722 723 if (v) { 724 each(v.split('&'), function(v) { 725 v = v.split('='); 726 h = h || {}; 727 h[unescape(v[0])] = unescape(v[1]); 728 }); 729 } 730 731 return h; 732 }, 733 734 setHash : function(n, v, e, p, d, s) { 735 var o = ''; 736 737 each(v, function(v, k) { 738 o += (!o ? '' : '&') + escape(k) + '=' + escape(v); 739 }); 740 741 this.set(n, o, e, p, d, s); 742 }, 743 744 get : function(n) { 745 var c = document.cookie, e, p = n + "=", b; 746 747 // Strict mode 748 if (!c) 749 return; 750 751 b = c.indexOf("; " + p); 752 753 if (b == -1) { 754 b = c.indexOf(p); 755 756 if (b != 0) 757 return null; 758 } else 759 b += 2; 760 761 e = c.indexOf(";", b); 762 763 if (e == -1) 764 e = c.length; 765 766 return unescape(c.substring(b + p.length, e)); 767 }, 768 769 set : function(n, v, e, p, d, s) { 770 document.cookie = n + "=" + escape(v) + 771 ((e) ? "; expires=" + e.toGMTString() : "") + 772 ((p) ? "; path=" + escape(p) : "") + 773 ((d) ? "; domain=" + d : "") + 774 ((s) ? "; secure" : ""); 775 }, 776 777 remove : function(n, p) { 778 var d = new Date(); 779 780 d.setTime(d.getTime() - 1000); 781 782 this.set(n, '', d, p, d); 783 } 784 }); 785 })(); 786 tinymce.create('static tinymce.util.JSON', { 787 serialize : function(o) { 788 var i, v, s = tinymce.util.JSON.serialize, t; 789 790 if (o == null) 791 return 'null'; 792 793 t = typeof o; 794 795 if (t == 'string') { 796 v = '\bb\tt\nn\ff\rr\""\'\'\\\\'; 797 798 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) { 799 i = v.indexOf(b); 800 801 if (i + 1) 802 return '\\' + v.charAt(i + 1); 803 804 a = b.charCodeAt().toString(16); 805 806 return '\\u' + '0000'.substring(a.length) + a; 807 }) + '"'; 808 } 809 810 if (t == 'object') { 811 if (o.hasOwnProperty && o instanceof Array) { 812 for (i=0, v = '['; i<o.length; i++) 813 v += (i > 0 ? ',' : '') + s(o[i]); 814 815 return v + ']'; 816 } 817 818 v = '{'; 819 820 for (i in o) 821 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : ''; 822 823 return v + '}'; 824 } 825 826 return '' + o; 827 }, 828 829 parse : function(s) { 830 try { 831 return eval('(' + s + ')'); 832 } catch (ex) { 833 // Ignore 834 } 835 } 836 837 }); 838 tinymce.create('static tinymce.util.XHR', { 839 send : function(o) { 840 var x, t, w = window, c = 0; 841 842 // Default settings 843 o.scope = o.scope || this; 844 o.success_scope = o.success_scope || o.scope; 845 o.error_scope = o.error_scope || o.scope; 846 o.async = o.async === false ? false : true; 847 o.data = o.data || ''; 848 849 function get(s) { 850 x = 0; 851 852 try { 853 x = new ActiveXObject(s); 854 } catch (ex) { 855 } 856 857 return x; 858 }; 859 860 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP'); 861 862 if (x) { 863 if (x.overrideMimeType) 864 x.overrideMimeType(o.content_type); 865 866 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async); 867 868 if (o.content_type) 869 x.setRequestHeader('Content-Type', o.content_type); 870 871 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 872 873 x.send(o.data); 874 875 function ready() { 876 if (!o.async || x.readyState == 4 || c++ > 10000) { 877 if (o.success && c < 10000 && x.status == 200) 878 o.success.call(o.success_scope, '' + x.responseText, x, o); 879 else if (o.error) 880 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o); 881 882 x = null; 883 } else 884 w.setTimeout(ready, 10); 885 }; 886 887 // Syncronous request 888 if (!o.async) 889 return ready(); 890 891 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE 892 t = w.setTimeout(ready, 10); 893 } 894 } 895 }); 896 (function() { 897 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR; 898 899 tinymce.create('tinymce.util.JSONRequest', { 900 JSONRequest : function(s) { 901 this.settings = extend({ 902 }, s); 903 this.count = 0; 904 }, 905 906 send : function(o) { 907 var ecb = o.error, scb = o.success; 908 909 o = extend(this.settings, o); 910 911 o.success = function(c, x) { 912 c = JSON.parse(c); 913 914 if (typeof(c) == 'undefined') { 915 c = { 916 error : 'JSON Parse error.' 917 }; 918 } 919 920 if (c.error) 921 ecb.call(o.error_scope || o.scope, c.error, x); 922 else 923 scb.call(o.success_scope || o.scope, c.result); 924 }; 925 926 o.error = function(ty, x) { 927 ecb.call(o.error_scope || o.scope, ty, x); 928 }; 929 930 o.data = JSON.serialize({ 931 id : o.id || 'c' + (this.count++), 932 method : o.method, 933 params : o.params 934 }); 935 936 // JSON content type for Ruby on rails. Bug: #1883287 937 o.content_type = 'application/json'; 938 939 XHR.send(o); 940 }, 941 942 'static' : { 943 sendRPC : function(o) { 944 return new tinymce.util.JSONRequest().send(o); 945 } 946 } 947 }); 948 }());(function(tinymce) { 949 // Shorten names 950 var each = tinymce.each, is = tinymce.is; 951 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE; 952 953 tinymce.create('tinymce.dom.DOMUtils', { 954 doc : null, 955 root : null, 956 files : null, 957 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/, 958 props : { 959 "for" : "htmlFor", 960 "class" : "className", 961 className : "className", 962 checked : "checked", 963 disabled : "disabled", 964 maxlength : "maxLength", 965 readonly : "readOnly", 966 selected : "selected", 967 value : "value", 968 id : "id", 969 name : "name", 970 type : "type" 971 }, 972 973 DOMUtils : function(d, s) { 974 var t = this; 975 976 t.doc = d; 977 t.win = window; 978 t.files = {}; 979 t.cssFlicker = false; 980 t.counter = 0; 981 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; 982 t.stdMode = d.documentMode === 8; 983 984 t.settings = s = tinymce.extend({ 985 keep_values : false, 986 hex_colors : 1, 987 process_html : 1 988 }, s); 989 990 // Fix IE6SP2 flicker and check it failed for pre SP2 991 if (tinymce.isIE6) { 992 try { 993 d.execCommand('BackgroundImageCache', false, true); 994 } catch (e) { 995 t.cssFlicker = true; 996 } 997 } 998 999 tinymce.addUnload(t.destroy, t); 1000 }, 1001 1002 getRoot : function() { 1003 var t = this, s = t.settings; 1004 1005 return (s && t.get(s.root_element)) || t.doc.body; 1006 }, 1007 1008 getViewPort : function(w) { 1009 var d, b; 1010 1011 w = !w ? this.win : w; 1012 d = w.document; 1013 b = this.boxModel ? d.documentElement : d.body; 1014 1015 // Returns viewport size excluding scrollbars 1016 return { 1017 x : w.pageXOffset || b.scrollLeft, 1018 y : w.pageYOffset || b.scrollTop, 1019 w : w.innerWidth || b.clientWidth, 1020 h : w.innerHeight || b.clientHeight 1021 }; 1022 }, 1023 1024 getRect : function(e) { 1025 var p, t = this, sr; 1026 1027 e = t.get(e); 1028 p = t.getPos(e); 1029 sr = t.getSize(e); 1030 1031 return { 1032 x : p.x, 1033 y : p.y, 1034 w : sr.w, 1035 h : sr.h 1036 }; 1037 }, 1038 1039 getSize : function(e) { 1040 var t = this, w, h; 1041 1042 e = t.get(e); 1043 w = t.getStyle(e, 'width'); 1044 h = t.getStyle(e, 'height'); 1045 1046 // Non pixel value, then force offset/clientWidth 1047 if (w.indexOf('px') === -1) 1048 w = 0; 1049 1050 // Non pixel value, then force offset/clientWidth 1051 if (h.indexOf('px') === -1) 1052 h = 0; 1053 1054 return { 1055 w : parseInt(w) || e.offsetWidth || e.clientWidth, 1056 h : parseInt(h) || e.offsetHeight || e.clientHeight 1057 }; 1058 }, 1059 1060 getParent : function(n, f, r) { 1061 return this.getParents(n, f, r, false); 1062 }, 1063 1064 getParents : function(n, f, r, c) { 1065 var t = this, na, se = t.settings, o = []; 1066 1067 n = t.get(n); 1068 c = c === undefined; 1069 1070 if (se.strict_root) 1071 r = r || t.getRoot(); 1072 1073 // Wrap node name as func 1074 if (is(f, 'string')) { 1075 na = f; 1076 1077 if (f === '*') { 1078 f = function(n) {return n.nodeType == 1;}; 1079 } else { 1080 f = function(n) { 1081 return t.is(n, na); 1082 }; 1083 } 1084 } 1085 1086 while (n) { 1087 if (n == r || !n.nodeType || n.nodeType === 9) 1088 break; 1089 1090 if (!f || f(n)) { 1091 if (c) 1092 o.push(n); 1093 else 1094 return n; 1095 } 1096 1097 n = n.parentNode; 1098 } 1099 1100 return c ? o : null; 1101 }, 1102 1103 get : function(e) { 1104 var n; 1105 1106 if (e && this.doc && typeof(e) == 'string') { 1107 n = e; 1108 e = this.doc.getElementById(e); 1109 1110 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick 1111 if (e && e.id !== n) 1112 return this.doc.getElementsByName(n)[1]; 1113 } 1114 1115 return e; 1116 }, 1117 1118 getNext : function(node, selector) { 1119 return this._findSib(node, selector, 'nextSibling'); 1120 }, 1121 1122 getPrev : function(node, selector) { 1123 return this._findSib(node, selector, 'previousSibling'); 1124 }, 1125 1126 1127 select : function(pa, s) { 1128 var t = this; 1129 1130 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []); 1131 }, 1132 1133 is : function(n, patt) { 1134 return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0; 1135 }, 1136 1137 1138 add : function(p, n, a, h, c) { 1139 var t = this; 1140 1141 return this.run(p, function(p) { 1142 var e, k; 1143 1144 e = is(n, 'string') ? t.doc.createElement(n) : n; 1145 t.setAttribs(e, a); 1146 1147 if (h) { 1148 if (h.nodeType) 1149 e.appendChild(h); 1150 else 1151 t.setHTML(e, h); 1152 } 1153 1154 return !c ? p.appendChild(e) : e; 1155 }); 1156 }, 1157 1158 create : function(n, a, h) { 1159 return this.add(this.doc.createElement(n), n, a, h, 1); 1160 }, 1161 1162 createHTML : function(n, a, h) { 1163 var o = '', t = this, k; 1164 1165 o += '<' + n; 1166 1167 for (k in a) { 1168 if (a.hasOwnProperty(k)) 1169 o += ' ' + k + '="' + t.encode(a[k]) + '"'; 1170 } 1171 1172 if (tinymce.is(h)) 1173 return o + '>' + h + '</' + n + '>'; 1174 1175 return o + ' />'; 1176 }, 1177 1178 remove : function(n, k) { 1179 var t = this; 1180 1181 return this.run(n, function(n) { 1182 var p, g, i; 1183 1184 p = n.parentNode; 1185 1186 if (!p) 1187 return null; 1188 1189 if (k) { 1190 for (i = n.childNodes.length - 1; i >= 0; i--) 1191 t.insertAfter(n.childNodes[i], n); 1192 1193 //each(n.childNodes, function(c) { 1194 // p.insertBefore(c.cloneNode(true), n); 1195 //}); 1196 } 1197 1198 // Fix IE psuedo leak 1199 if (t.fixPsuedoLeaks) { 1200 p = n.cloneNode(true); 1201 k = 'IELeakGarbageBin'; 1202 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'}); 1203 g.appendChild(n); 1204 g.innerHTML = ''; 1205 1206 return p; 1207 } 1208 1209 return p.removeChild(n); 1210 }); 1211 }, 1212 1213 setStyle : function(n, na, v) { 1214 var t = this; 1215 1216 return t.run(n, function(e) { 1217 var s, i; 1218 1219 s = e.style; 1220 1221 // Camelcase it, if needed 1222 na = na.replace(/-(\D)/g, function(a, b){ 1223 return b.toUpperCase(); 1224 }); 1225 1226 // Default px suffix on these 1227 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v))) 1228 v += 'px'; 1229 1230 switch (na) { 1231 case 'opacity': 1232 // IE specific opacity 1233 if (isIE) { 1234 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")"; 1235 1236 if (!n.currentStyle || !n.currentStyle.hasLayout) 1237 s.display = 'inline-block'; 1238 } 1239 1240 // Fix for older browsers 1241 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || ''; 1242 break; 1243 1244 case 'float': 1245 isIE ? s.styleFloat = v : s.cssFloat = v; 1246 break; 1247 1248 default: 1249 s[na] = v || ''; 1250 } 1251 1252 // Force update of the style data 1253 if (t.settings.update_styles) 1254 t.setAttrib(e, 'mce_style'); 1255 }); 1256 }, 1257 1258 getStyle : function(n, na, c) { 1259 n = this.get(n); 1260 1261 if (!n) 1262 return false; 1263 1264 // Gecko 1265 if (this.doc.defaultView && c) { 1266 // Remove camelcase 1267 na = na.replace(/[A-Z]/g, function(a){ 1268 return '-' + a; 1269 }); 1270 1271 try { 1272 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na); 1273 } catch (ex) { 1274 // Old safari might fail 1275 return null; 1276 } 1277 } 1278 1279 // Camelcase it, if needed 1280 na = na.replace(/-(\D)/g, function(a, b){ 1281 return b.toUpperCase(); 1282 }); 1283 1284 if (na == 'float') 1285 na = isIE ? 'styleFloat' : 'cssFloat'; 1286 1287 // IE & Opera 1288 if (n.currentStyle && c) 1289 return n.currentStyle[na]; 1290 1291 return n.style[na]; 1292 }, 1293 1294 setStyles : function(e, o) { 1295 var t = this, s = t.settings, ol; 1296 1297 ol = s.update_styles; 1298 s.update_styles = 0; 1299 1300 each(o, function(v, n) { 1301 t.setStyle(e, n, v); 1302 }); 1303 1304 // Update style info 1305 s.update_styles = ol; 1306 if (s.update_styles) 1307 t.setAttrib(e, s.cssText); 1308 }, 1309 1310 setAttrib : function(e, n, v) { 1311 var t = this; 1312 1313 // Whats the point 1314 if (!e || !n) 1315 return; 1316 1317 // Strict XML mode 1318 if (t.settings.strict) 1319 n = n.toLowerCase(); 1320 1321 return this.run(e, function(e) { 1322 var s = t.settings; 1323 1324 switch (n) { 1325 case "style": 1326 if (!is(v, 'string')) { 1327 each(v, function(v, n) { 1328 t.setStyle(e, n, v); 1329 }); 1330 1331 return; 1332 } 1333 1334 // No mce_style for elements with these since they might get resized by the user 1335 if (s.keep_values) { 1336 if (v && !t._isRes(v)) 1337 e.setAttribute('mce_style', v, 2); 1338 else 1339 e.removeAttribute('mce_style', 2); 1340 } 1341 1342 e.style.cssText = v; 1343 break; 1344 1345 case "class": 1346 e.className = v || ''; // Fix IE null bug 1347 break; 1348 1349 case "src": 1350 case "href": 1351 if (s.keep_values) { 1352 if (s.url_converter) 1353 v = s.url_converter.call(s.url_converter_scope || t, v, n, e); 1354 1355 t.setAttrib(e, 'mce_' + n, v, 2); 1356 } 1357 1358 break; 1359 1360 case "shape": 1361 e.setAttribute('mce_style', v); 1362 break; 1363 } 1364 1365 if (is(v) && v !== null && v.length !== 0) 1366 e.setAttribute(n, '' + v, 2); 1367 else 1368 e.removeAttribute(n, 2); 1369 }); 1370 }, 1371 1372 setAttribs : function(e, o) { 1373 var t = this; 1374 1375 return this.run(e, function(e) { 1376 each(o, function(v, n) { 1377 t.setAttrib(e, n, v); 1378 }); 1379 }); 1380 }, 1381 1382 getAttrib : function(e, n, dv) { 1383 var v, t = this; 1384 1385 e = t.get(e); 1386 1387 if (!e || e.nodeType !== 1) 1388 return false; 1389 1390 if (!is(dv)) 1391 dv = ''; 1392 1393 // Try the mce variant for these 1394 if (/^(src|href|style|coords|shape)$/.test(n)) { 1395 v = e.getAttribute("mce_" + n); 1396 1397 if (v) 1398 return v; 1399 } 1400 1401 if (isIE && t.props[n]) { 1402 v = e[t.props[n]]; 1403 v = v && v.nodeValue ? v.nodeValue : v; 1404 } 1405 1406 if (!v) 1407 v = e.getAttribute(n, 2); 1408 1409 if (n === 'style') { 1410 v = v || e.style.cssText; 1411 1412 if (v) { 1413 v = t.serializeStyle(t.parseStyle(v)); 1414 1415 if (t.settings.keep_values && !t._isRes(v)) 1416 e.setAttribute('mce_style', v); 1417 } 1418 } 1419 1420 // Remove Apple and WebKit stuff 1421 if (isWebKit && n === "class" && v) 1422 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, ''); 1423 1424 // Handle IE issues 1425 if (isIE) { 1426 switch (n) { 1427 case 'rowspan': 1428 case 'colspan': 1429 // IE returns 1 as default value 1430 if (v === 1) 1431 v = ''; 1432 1433 break; 1434 1435 case 'size': 1436 // IE returns +0 as default value for size 1437 if (v === '+0' || v === 20 || v === 0) 1438 v = ''; 1439 1440 break; 1441 1442 case 'width': 1443 case 'height': 1444 case 'vspace': 1445 case 'checked': 1446 case 'disabled': 1447 case 'readonly': 1448 if (v === 0) 1449 v = ''; 1450 1451 break; 1452 1453 case 'hspace': 1454 // IE returns -1 as default value 1455 if (v === -1) 1456 v = ''; 1457 1458 break; 1459 1460 case 'maxlength': 1461 case 'tabindex': 1462 // IE returns default value 1463 if (v === 32768 || v === 2147483647 || v === '32768') 1464 v = ''; 1465 1466 break; 1467 1468 case 'multiple': 1469 case 'compact': 1470 case 'noshade': 1471 case 'nowrap': 1472 if (v === 65535) 1473 return n; 1474 1475 return dv; 1476 1477 case 'shape': 1478 v = v.toLowerCase(); 1479 break; 1480 1481 default: 1482 // IE has odd anonymous function for event attributes 1483 if (n.indexOf('on') === 0 && v) 1484 v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1'); 1485 } 1486 } 1487 1488 return (v !== undefined && v !== null && v !== '') ? '' + v : dv; 1489 }, 1490 1491 getPos : function(n, ro) { 1492 var t = this, x = 0, y = 0, e, d = t.doc, r; 1493 1494 n = t.get(n); 1495 ro = ro || d.body; 1496 1497 if (n) { 1498 // Use getBoundingClientRect on IE, Opera has it but it's not perfect 1499 if (isIE && !t.stdMode) { 1500 n = n.getBoundingClientRect(); 1501 e = t.boxModel ? d.documentElement : d.body; 1502 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border 1503 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x; 1504 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset 1505 1506 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x}; 1507 } 1508 1509 r = n; 1510 while (r && r != ro && r.nodeType) { 1511 x += r.offsetLeft || 0; 1512 y += r.offsetTop || 0; 1513 r = r.offsetParent; 1514 } 1515 1516 r = n.parentNode; 1517 while (r && r != ro && r.nodeType) { 1518 x -= r.scrollLeft || 0; 1519 y -= r.scrollTop || 0; 1520 r = r.parentNode; 1521 } 1522 } 1523 1524 return {x : x, y : y}; 1525 }, 1526 1527 parseStyle : function(st) { 1528 var t = this, s = t.settings, o = {}; 1529 1530 if (!st) 1531 return o; 1532 1533 function compress(p, s, ot) { 1534 var t, r, b, l; 1535 1536 // Get values and check it it needs compressing 1537 t = o[p + '-top' + s]; 1538 if (!t) 1539 return; 1540 1541 r = o[p + '-right' + s]; 1542 if (t != r) 1543 return; 1544 1545 b = o[p + '-bottom' + s]; 1546 if (r != b) 1547 return; 1548 1549 l = o[p + '-left' + s]; 1550 if (b != l) 1551 return; 1552 1553 // Compress 1554 o[ot] = l; 1555 delete o[p + '-top' + s]; 1556 delete o[p + '-right' + s]; 1557 delete o[p + '-bottom' + s]; 1558 delete o[p + '-left' + s]; 1559 }; 1560 1561 function compress2(ta, a, b, c) { 1562 var t; 1563 1564 t = o[a]; 1565 if (!t) 1566 return; 1567 1568 t = o[b]; 1569 if (!t) 1570 return; 1571 1572 t = o[c]; 1573 if (!t) 1574 return; 1575 1576 // Compress 1577 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c]; 1578 delete o[a]; 1579 delete o[b]; 1580 delete o[c]; 1581 }; 1582 1583 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities 1584 1585 each(st.split(';'), function(v) { 1586 var sv, ur = []; 1587 1588 if (v) { 1589 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities 1590 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';}); 1591 v = v.split(':'); 1592 sv = tinymce.trim(v[1]); 1593 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];}); 1594 1595 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) { 1596 return t.toHex(v); 1597 }); 1598 1599 if (s.url_converter) { 1600 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) { 1601 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')'; 1602 }); 1603 } 1604 1605 o[tinymce.trim(v[0]).toLowerCase()] = sv; 1606 } 1607 }); 1608 1609 compress("border", "", "border"); 1610 compress("border", "-width", "border-width"); 1611 compress("border", "-color", "border-color"); 1612 compress("border", "-style", "border-style"); 1613 compress("padding", "", "padding"); 1614 compress("margin", "", "margin"); 1615 compress2('border', 'border-width', 'border-style', 'border-color'); 1616 1617 if (isIE) { 1618 // Remove pointless border 1619 if (o.border == 'medium none') 1620 o.border = ''; 1621 } 1622 1623 return o; 1624 }, 1625 1626 serializeStyle : function(o) { 1627 var s = ''; 1628 1629 each(o, function(v, k) { 1630 if (k && v) { 1631 if (tinymce.isGecko && k.indexOf('-moz-') === 0) 1632 return; 1633 1634 switch (k) { 1635 case 'color': 1636 case 'background-color': 1637 v = v.toLowerCase(); 1638 break; 1639 } 1640 1641 s += (s ? ' ' : '') + k + ': ' + v + ';'; 1642 } 1643 }); 1644 1645 return s; 1646 }, 1647 1648 loadCSS : function(u) { 1649 var t = this, d = t.doc, head; 1650 1651 if (!u) 1652 u = ''; 1653 1654 head = t.select('head')[0]; 1655 1656 each(u.split(','), function(u) { 1657 var link; 1658 1659 if (t.files[u]) 1660 return; 1661 1662 t.files[u] = true; 1663 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)}); 1664 1665 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug 1666 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading 1667 // It's ugly but it seems to work fine. 1668 if (isIE && d.documentMode) { 1669 link.onload = function() { 1670 d.recalc(); 1671 link.onload = null; 1672 }; 1673 } 1674 1675 head.appendChild(link); 1676 }); 1677 }, 1678 1679 addClass : function(e, c) { 1680 return this.run(e, function(e) { 1681 var o; 1682 1683 if (!c) 1684 return 0; 1685 1686 if (this.hasClass(e, c)) 1687 return e.className; 1688 1689 o = this.removeClass(e, c); 1690 1691 return e.className = (o != '' ? (o + ' ') : '') + c; 1692 }); 1693 }, 1694 1695 removeClass : function(e, c) { 1696 var t = this, re; 1697 1698 return t.run(e, function(e) { 1699 var v; 1700 1701 if (t.hasClass(e, c)) { 1702 if (!re) 1703 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g"); 1704 1705 v = e.className.replace(re, ' '); 1706 1707 return e.className = tinymce.trim(v != ' ' ? v : ''); 1708 } 1709 1710 return e.className; 1711 }); 1712 }, 1713 1714 hasClass : function(n, c) { 1715 n = this.get(n); 1716 1717 if (!n || !c) 1718 return false; 1719 1720 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1; 1721 }, 1722 1723 show : function(e) { 1724 return this.setStyle(e, 'display', 'block'); 1725 }, 1726 1727 hide : function(e) { 1728 return this.setStyle(e, 'display', 'none'); 1729 }, 1730 1731 isHidden : function(e) { 1732 e = this.get(e); 1733 1734 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none'; 1735 }, 1736 1737 uniqueId : function(p) { 1738 return (!p ? 'mce_' : p) + (this.counter++); 1739 }, 1740 1741 setHTML : function(e, h) { 1742 var t = this; 1743 1744 return this.run(e, function(e) { 1745 var x, i, nl, n, p, x; 1746 1747 h = t.processHTML(h); 1748 1749 if (isIE) { 1750 function set() { 1751 try { 1752 // IE will remove comments from the beginning 1753 // unless you padd the contents with something 1754 e.innerHTML = '<br />' + h; 1755 e.removeChild(e.firstChild); 1756 } catch (ex) { 1757 // 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 1758 // This seems to fix this problem 1759 1760 // Remove all child nodes 1761 while (e.firstChild) 1762 e.firstChild.removeNode(); 1763 1764 // Create new div with HTML contents and a BR infront to keep comments 1765 x = t.create('div'); 1766 x.innerHTML = '<br />' + h; 1767 1768 // Add all children from div to target 1769 each (x.childNodes, function(n, i) { 1770 // Skip br element 1771 if (i) 1772 e.appendChild(n); 1773 }); 1774 } 1775 }; 1776 1777 // IE has a serious bug when it comes to paragraphs it can produce an invalid 1778 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted 1779 // It seems to be that IE doesn't like a root block element placed inside another root block element 1780 if (t.settings.fix_ie_paragraphs) 1781 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true"> </p>'); 1782 1783 set(); 1784 1785 if (t.settings.fix_ie_paragraphs) { 1786 // Check for odd paragraphs this is a sign of a broken DOM 1787 nl = e.getElementsByTagName("p"); 1788 for (i = nl.length - 1, x = 0; i >= 0; i--) { 1789 n = nl[i]; 1790 1791 if (!n.hasChildNodes()) { 1792 if (!n.mce_keep) { 1793 x = 1; // Is broken 1794 break; 1795 } 1796 1797 n.removeAttribute('mce_keep'); 1798 } 1799 } 1800 } 1801 1802 // Time to fix the madness IE left us 1803 if (x) { 1804 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs 1805 // after we use innerHTML we can fix the DOM tree 1806 h = h.replace(/<p ([^>]+)>|<p>/g, '<div $1 mce_tmp="1">'); 1807 h = h.replace(/<\/p>/g, '</div>'); 1808 1809 // Set the new HTML with DIVs 1810 set(); 1811 1812 // Replace all DIV elements with he mce_tmp attibute back to paragraphs 1813 // This is needed since IE has a annoying bug see above for details 1814 // This is a slow process but it has to be done. :( 1815 if (t.settings.fix_ie_paragraphs) { 1816 nl = e.getElementsByTagName("DIV"); 1817 for (i = nl.length - 1; i >= 0; i--) { 1818 n = nl[i]; 1819 1820 // Is it a temp div 1821 if (n.mce_tmp) { 1822 // Create new paragraph 1823 p = t.doc.createElement('p'); 1824 1825 // Copy all attributes 1826 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) { 1827 var v; 1828 1829 if (b !== 'mce_tmp') { 1830 v = n.getAttribute(b); 1831 1832 if (!v && b === 'class') 1833 v = n.className; 1834 1835 p.setAttribute(b, v); 1836 } 1837 }); 1838 1839 // Append all children to new paragraph 1840 for (x = 0; x<n.childNodes.length; x++) 1841 p.appendChild(n.childNodes[x].cloneNode(true)); 1842 1843 // Replace div with new paragraph 1844 n.swapNode(p); 1845 } 1846 } 1847 } 1848 } 1849 } else 1850 e.innerHTML = h; 1851 1852 return h; 1853 }); 1854 }, 1855 1856 processHTML : function(h) { 1857 var t = this, s = t.settings; 1858 1859 if (!s.process_html) 1860 return h; 1861 1862 // Convert strong and em to b and i in FF since it can't handle them 1863 if (tinymce.isGecko) { 1864 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>'); 1865 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>'); 1866 } else if (isIE) { 1867 h = h.replace(/'/g, '''); // IE can't handle apos 1868 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct 1869 } 1870 1871 // Fix some issues 1872 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open 1873 1874 // Store away src and href in mce_src and mce_href since browsers mess them up 1875 if (s.keep_values) { 1876 // Wrap scripts and styles in comments for serialization purposes 1877 if (/<script|noscript|style/i.test(h)) { 1878 function trim(s) { 1879 // Remove prefix and suffix code for element 1880 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n'); 1881 s = s.replace(/^[\r\n]*|[\r\n]*$/g, ''); 1882 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, ''); 1883 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, ''); 1884 1885 return s; 1886 }; 1887 1888 // Wrap the script contents in CDATA and keep them from executing 1889 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) { 1890 // Force type attribute 1891 if (!attribs) 1892 attribs = ' type="text/javascript"'; 1893 1894 // Convert the src attribute of the scripts 1895 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) { 1896 if (s.url_converter) 1897 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script')); 1898 1899 return 'mce_src="' + url + '"'; 1900 }); 1901 1902 // Wrap text contents 1903 if (tinymce.trim(text)) 1904 text = '<!--\n' + trim(text) + '\n// -->'; 1905 1906 return '<mce:script' + attribs + '>' + text + '</mce:script>'; 1907 }); 1908 1909 // Wrap style elements 1910 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) { 1911 // Wrap text contents 1912 if (text) 1913 text = '<!--\n' + trim(text) + '\n-->'; 1914 1915 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' mce_bogus="1">' + text + '</style>'; 1916 }); 1917 1918 // Wrap noscript elements 1919 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) { 1920 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '--') + '--></mce:noscript>'; 1921 }); 1922 } 1923 1924 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->'); 1925 1926 // Process all tags with src, href or style 1927 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) { 1928 function handle(m, b, c) { 1929 var u = c; 1930 1931 // Tag already got a mce_ version 1932 if (a.indexOf('mce_' + b) != -1) 1933 return m; 1934 1935 if (b == 'style') { 1936 // No mce_style for elements with these since they might get resized by the user 1937 if (t._isRes(c)) 1938 return m; 1939 1940 if (s.hex_colors) { 1941 u = u.replace(/rgb\([^\)]+\)/g, function(v) { 1942 return t.toHex(v); 1943 }); 1944 } 1945 1946 if (s.url_converter) { 1947 u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) { 1948 return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')'; 1949 }); 1950 } 1951 } else if (b != 'coords' && b != 'shape') { 1952 if (s.url_converter) 1953 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)); 1954 } 1955 1956 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"'; 1957 }; 1958 1959 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C 1960 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C 1961 1962 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE 1963 }); 1964 } 1965 1966 return h; 1967 }, 1968 1969 getOuterHTML : function(e) { 1970 var d; 1971 1972 e = this.get(e); 1973 1974 if (!e) 1975 return null; 1976 1977 if (e.outerHTML !== undefined) 1978 return e.outerHTML; 1979 1980 d = (e.ownerDocument || this.doc).createElement("body"); 1981 d.appendChild(e.cloneNode(true)); 1982 1983 return d.innerHTML; 1984 }, 1985 1986 setOuterHTML : function(e, h, d) { 1987 var t = this; 1988 1989 function setHTML(e, h, d) { 1990 var n, tp; 1991 1992 tp = d.createElement("body"); 1993 tp.innerHTML = h; 1994 1995 n = tp.lastChild; 1996 while (n) { 1997 t.insertAfter(n.cloneNode(true), e); 1998 n = n.previousSibling; 1999 } 2000 2001 t.remove(e); 2002 }; 2003 2004 return this.run(e, function(e) { 2005 e = t.get(e); 2006 2007 // Only set HTML on elements 2008 if (e.nodeType == 1) { 2009 d = d || e.ownerDocument || t.doc; 2010 2011 if (isIE) { 2012 try { 2013 // Try outerHTML for IE it sometimes produces an unknown runtime error 2014 if (isIE && e.nodeType == 1) 2015 e.outerHTML = h; 2016 else 2017 setHTML(e, h, d); 2018 } catch (ex) { 2019 // Fix for unknown runtime error 2020 setHTML(e, h, d); 2021 } 2022 } else 2023 setHTML(e, h, d); 2024 } 2025 }); 2026 }, 2027 2028 decode : function(s) { 2029 var e, n, v; 2030 2031 // Look for entities to decode 2032 if (/&[^;]+;/.test(s)) { 2033 // Decode the entities using a div element not super efficient but less code 2034 e = this.doc.createElement("div"); 2035 e.innerHTML = s; 2036 n = e.firstChild; 2037 v = ''; 2038 2039 if (n) { 2040 do { 2041 v += n.nodeValue; 2042 } while (n.nextSibling); 2043 } 2044 2045 return v || s; 2046 } 2047 2048 return s; 2049 }, 2050 2051 encode : function(s) { 2052 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) { 2053 switch (c) { 2054 case '&': 2055 return '&'; 2056 2057 case '"': 2058 return '"'; 2059 2060 case '<': 2061 return '<'; 2062 2063 case '>': 2064 return '>'; 2065 } 2066 2067 return c; 2068 }) : s; 2069 }, 2070 2071 insertAfter : function(n, r) { 2072 var t = this; 2073 2074 r = t.get(r); 2075 2076 return this.run(n, function(n) { 2077 var p, ns; 2078 2079 p = r.parentNode; 2080 ns = r.nextSibling; 2081 2082 if (ns) 2083 p.insertBefore(n, ns); 2084 else 2085 p.appendChild(n); 2086 2087 return n; 2088 }); 2089 }, 2090 2091 isBlock : function(n) { 2092 if (n.nodeType && n.nodeType !== 1) 2093 return false; 2094 2095 n = n.nodeName || n; 2096 2097 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n); 2098 }, 2099 2100 replace : function(n, o, k) { 2101 var t = this; 2102 2103 if (is(o, 'array')) 2104 n = n.cloneNode(true); 2105 2106 return t.run(o, function(o) { 2107 if (k) { 2108 each(o.childNodes, function(c) { 2109 n.appendChild(c.cloneNode(true)); 2110 }); 2111 } 2112 2113 // Fix IE psuedo leak for elements since replacing elements if fairly common 2114 // Will break parentNode for some unknown reason 2115 if (t.fixPsuedoLeaks && o.nodeType === 1) { 2116 o.parentNode.insertBefore(n, o); 2117 t.remove(o); 2118 return n; 2119 } 2120 2121 return o.parentNode.replaceChild(n, o); 2122 }); 2123 }, 2124 2125 findCommonAncestor : function(a, b) { 2126 var ps = a, pe; 2127 2128 while (ps) { 2129 pe = b; 2130 2131 while (pe && ps != pe) 2132 pe = pe.parentNode; 2133 2134 if (ps == pe) 2135 break; 2136 2137 ps = ps.parentNode; 2138 } 2139 2140 if (!ps && a.ownerDocument) 2141 return a.ownerDocument.documentElement; 2142 2143 return ps; 2144 }, 2145 2146 toHex : function(s) { 2147 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s); 2148 2149 function hex(s) { 2150 s = parseInt(s).toString(16); 2151 2152 return s.length > 1 ? s : '0' + s; // 0 -> 00 2153 }; 2154 2155 if (c) { 2156 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]); 2157 2158 return s; 2159 } 2160 2161 return s; 2162 }, 2163 2164 getClasses : function() { 2165 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov; 2166 2167 if (t.classes) 2168 return t.classes; 2169 2170 function addClasses(s) { 2171 // IE style imports 2172 each(s.imports, function(r) { 2173 addClasses(r); 2174 }); 2175 2176 each(s.cssRules || s.rules, function(r) { 2177 // Real type or fake it on IE 2178 switch (r.type || 1) { 2179 // Rule 2180 case 1: 2181 if (r.selectorText) { 2182 each(r.selectorText.split(','), function(v) { 2183 v = v.replace(/^\s*|\s*$|^\s\./g, ""); 2184 2185 // Is internal or it doesn't contain a class 2186 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v)) 2187 return; 2188 2189 // Remove everything but class name 2190 ov = v; 2191 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1'); 2192 2193 // Filter classes 2194 if (f && !(v = f(v, ov))) 2195 return; 2196 2197 if (!lo[v]) { 2198 cl.push({'class' : v}); 2199 lo[v] = 1; 2200 } 2201 }); 2202 } 2203 break; 2204 2205 // Import 2206 case 3: 2207 addClasses(r.styleSheet); 2208 break; 2209 } 2210 }); 2211 }; 2212 2213 try { 2214 each(t.doc.styleSheets, addClasses); 2215 } catch (ex) { 2216 // Ignore 2217 } 2218 2219 if (cl.length > 0) 2220 t.classes = cl; 2221 2222 return cl; 2223 }, 2224 2225 run : function(e, f, s) { 2226 var t = this, o; 2227 2228 if (t.doc && typeof(e) === 'string') 2229 e = t.get(e); 2230 2231 if (!e) 2232 return false; 2233 2234 s = s || this; 2235 if (!e.nodeType && (e.length || e.length === 0)) { 2236 o = []; 2237 2238 each(e, function(e, i) { 2239 if (e) { 2240 if (typeof(e) == 'string') 2241 e = t.doc.getElementById(e); 2242 2243 o.push(f.call(s, e, i)); 2244 } 2245 }); 2246 2247 return o; 2248 } 2249 2250 return f.call(s, e); 2251 }, 2252 2253 getAttribs : function(n) { 2254 var o; 2255 2256 n = this.get(n); 2257 2258 if (!n) 2259 return []; 2260 2261 if (isIE) { 2262 o = []; 2263 2264 // Object will throw exception in IE 2265 if (n.nodeName == 'OBJECT') 2266 return n.attributes; 2267 2268 // It's crazy that this is faster in IE but it's because it returns all attributes all the time 2269 n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) { 2270 o.push({specified : 1, nodeName : b}); 2271 }); 2272 2273 return o; 2274 } 2275 2276 return n.attributes; 2277 }, 2278 2279 destroy : function(s) { 2280 var t = this; 2281 2282 if (t.events) 2283 t.events.destroy(); 2284 2285 t.win = t.doc = t.root = t.events = null; 2286 2287 // Manual destroy then remove unload handler 2288 if (!s) 2289 tinymce.removeUnload(t.destroy); 2290 }, 2291 2292 createRng : function() { 2293 var d = this.doc; 2294 2295 return d.createRange ? d.createRange() : new tinymce.dom.Range(this); 2296 }, 2297 2298 split : function(pe, e, re) { 2299 var t = this, r = t.createRng(), bef, aft, pa; 2300 2301 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence 2302 // but we don't want that in our code since it serves no purpose 2303 // For example if this is chopped: 2304 // <p>text 1<span><b>CHOP</b></span>text 2</p> 2305 // would produce: 2306 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p> 2307 // this function will then trim of empty edges and produce: 2308 // <p>text 1</p><b>CHOP</b><p>text 2</p> 2309 function trimEdge(n, na) { 2310 n = n[na]; 2311 2312 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na])) 2313 t.remove(n[na]); 2314 }; 2315 2316 function isEmpty(n) { 2317 n = t.getOuterHTML(n); 2318 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars 2319 n = n.replace(/<[^>]+>/g, ''); // Remove all tags 2320 2321 return n.replace(/[ \t\r\n]+| | /g, '') == ''; 2322 }; 2323 2324 if (pe && e) { 2325 // Get before chunk 2326 r.setStartBefore(pe); 2327 r.setEndBefore(e); 2328 bef = r.extractContents(); 2329 2330 // Get after chunk 2331 r = t.createRng(); 2332 r.setStartAfter(e); 2333 r.setEndAfter(pe); 2334 aft = r.extractContents(); 2335 2336 // Insert chunks and remove parent 2337 pa = pe.parentNode; 2338 2339 // Remove right side edge of the before contents 2340 trimEdge(bef, 'lastChild'); 2341 2342 if (!isEmpty(bef)) 2343 pa.insertBefore(bef, pe); 2344 2345 if (re) 2346 pa.replaceChild(re, e); 2347 else 2348 pa.insertBefore(e, pe); 2349 2350 // Remove left site edge of the after contents 2351 trimEdge(aft, 'firstChild'); 2352 2353 if (!isEmpty(aft)) 2354 pa.insertBefore(aft, pe); 2355 2356 t.remove(pe); 2357 2358 return re || e; 2359 } 2360 }, 2361 2362 bind : function(target, name, func, scope) { 2363 var t = this; 2364 2365 if (!t.events) 2366 t.events = new tinymce.dom.EventUtils(); 2367 2368 return t.events.add(target, name, func, scope || this); 2369 }, 2370 2371 unbind : function(target, name, func) { 2372 var t = this; 2373 2374 if (!t.events) 2375 t.events = new tinymce.dom.EventUtils(); 2376 2377 return t.events.remove(target, name, func); 2378 }, 2379 2380 2381 _findSib : function(node, selector, name) { 2382 var t = this, f = selector; 2383 2384 if (node) { 2385 // If expression make a function of it using is 2386 if (is(f, 'string')) { 2387 f = function(node) { 2388 return t.is(node, selector); 2389 }; 2390 } 2391 2392 // Loop all siblings 2393 for (node = node[name]; node; node = node[name]) { 2394 if (f(node)) 2395 return node; 2396 } 2397 } 2398 2399 return null; 2400 }, 2401 2402 _isRes : function(c) { 2403 // Is live resizble element 2404 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c); 2405 } 2406 2407 /* 2408 walk : function(n, f, s) { 2409 var d = this.doc, w; 2410 2411 if (d.createTreeWalker) { 2412 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); 2413 2414 while ((n = w.nextNode()) != null) 2415 f.call(s || this, n); 2416 } else 2417 tinymce.walk(n, f, 'childNodes', s); 2418 } 2419 */ 2420 2421 /* 2422 toRGB : function(s) { 2423 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s); 2424 2425 if (c) { 2426 // #FFF -> #FFFFFF 2427 if (!is(c[3])) 2428 c[3] = c[2] = c[1]; 2429 2430 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")"; 2431 } 2432 2433 return s; 2434 } 2435 */ 2436 }); 2437 2438 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0}); 2439 })(tinymce); 2440 (function(ns) { 2441 // Traverse constants 2442 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend; 2443 2444 function indexOf(child, parent) { 2445 var i, node; 2446 2447 if (child.parentNode != parent) 2448 return -1; 2449 2450 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling) 2451 i++; 2452 2453 return i; 2454 }; 2455 2456 function nodeIndex(n) { 2457 var i = 0; 2458 2459 while (n.previousSibling) { 2460 i++; 2461 n = n.previousSibling; 2462 } 2463 2464 return i; 2465 }; 2466 2467 function getSelectedNode(container, offset) { 2468 var child; 2469 2470 if (container.nodeType == 3 /* TEXT_NODE */) 2471 return container; 2472 2473 if (offset < 0) 2474 return container; 2475 2476 child = container.firstChild; 2477 while (child != null && offset > 0) { 2478 --offset; 2479 child = child.nextSibling; 2480 } 2481 2482 if (child != null) 2483 return child; 2484 2485 return container; 2486 }; 2487 2488 // Range constructor 2489 function Range(dom) { 2490 var d = dom.doc; 2491 2492 extend(this, { 2493 dom : dom, 2494 2495 // Inital states 2496 startContainer : d, 2497 startOffset : 0, 2498 endContainer : d, 2499 endOffset : 0, 2500 collapsed : true, 2501 commonAncestorContainer : d, 2502 2503 // Range constants 2504 START_TO_START : 0, 2505 START_TO_END : 1, 2506 END_TO_END : 2, 2507 END_TO_START : 3 2508 }); 2509 }; 2510 2511 // Add range methods 2512 extend(Range.prototype, { 2513 setStart : function(n, o) { 2514 this._setEndPoint(true, n, o); 2515 }, 2516 2517 setEnd : function(n, o) { 2518 this._setEndPoint(false, n, o); 2519 }, 2520 2521 setStartBefore : function(n) { 2522 this.setStart(n.parentNode, nodeIndex(n)); 2523 }, 2524 2525 setStartAfter : function(n) { 2526 this.setStart(n.parentNode, nodeIndex(n) + 1); 2527 }, 2528 2529 setEndBefore : function(n) { 2530 this.setEnd(n.parentNode, nodeIndex(n)); 2531 }, 2532 2533 setEndAfter : function(n) { 2534 this.setEnd(n.parentNode, nodeIndex(n) + 1); 2535 }, 2536 2537 collapse : function(ts) { 2538 var t = this; 2539 2540 if (ts) { 2541 t.endContainer = t.startContainer; 2542 t.endOffset = t.startOffset; 2543 } else { 2544 t.startContainer = t.endContainer; 2545 t.startOffset = t.endOffset; 2546 } 2547 2548 t.collapsed = true; 2549 }, 2550 2551 selectNode : function(n) { 2552 this.setStartBefore(n); 2553 this.setEndAfter(n); 2554 }, 2555 2556 selectNodeContents : function(n) { 2557 this.setStart(n, 0); 2558 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); 2559 }, 2560 2561 compareBoundaryPoints : function(h, r) { 2562 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset; 2563 2564 // Check START_TO_START 2565 if (h === 0) 2566 return t._compareBoundaryPoints(sc, so, sc, so); 2567 2568 // Check START_TO_END 2569 if (h === 1) 2570 return t._compareBoundaryPoints(sc, so, ec, eo); 2571 2572 // Check END_TO_END 2573 if (h === 2) 2574 return t._compareBoundaryPoints(ec, eo, ec, eo); 2575 2576 // Check END_TO_START 2577 if (h === 3) 2578 return t._compareBoundaryPoints(ec, eo, sc, so); 2579 }, 2580 2581 deleteContents : function() { 2582 this._traverse(DELETE); 2583 }, 2584 2585 extractContents : function() { 2586 return this._traverse(EXTRACT); 2587 }, 2588 2589 cloneContents : function() { 2590 return this._traverse(CLONE); 2591 }, 2592 2593 insertNode : function(n) { 2594 var t = this, nn, o; 2595 2596 // Node is TEXT_NODE or CDATA 2597 if (n.nodeType === 3 || n.nodeType === 4) { 2598 nn = t.startContainer.splitText(t.startOffset); 2599 t.startContainer.parentNode.insertBefore(n, nn); 2600 } else { 2601 // Insert element node 2602 if (t.startContainer.childNodes.length > 0) 2603 o = t.startContainer.childNodes[t.startOffset]; 2604 2605 t.startContainer.insertBefore(n, o); 2606 } 2607 }, 2608 2609 surroundContents : function(n) { 2610 var t = this, f = t.extractContents(); 2611 2612 t.insertNode(n); 2613 n.appendChild(f); 2614 t.selectNode(n); 2615 }, 2616 2617 cloneRange : function() { 2618 var t = this; 2619 2620 return extend(new Range(t.dom), { 2621 startContainer : t.startContainer, 2622 startOffset : t.startOffset, 2623 endContainer : t.endContainer, 2624 endOffset : t.endOffset, 2625 collapsed : t.collapsed, 2626 commonAncestorContainer : t.commonAncestorContainer 2627 }); 2628 }, 2629 2630 /* 2631 detach : function() { 2632 // Not implemented 2633 }, 2634 */ 2635 // Internal methods 2636 2637 _isCollapsed : function() { 2638 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset); 2639 }, 2640 2641 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) { 2642 var c, offsetC, n, cmnRoot, childA, childB; 2643 2644 // In the first case the boundary-points have the same container. A is before B 2645 // if its offset is less than the offset of B, A is equal to B if its offset is 2646 // equal to the offset of B, and A is after B if its offset is greater than the 2647 // offset of B. 2648 if (containerA == containerB) { 2649 if (offsetA == offsetB) { 2650 return 0; // equal 2651 } else if (offsetA < offsetB) { 2652 return -1; // before 2653 } else { 2654 return 1; // after 2655 } 2656 } 2657 2658 // In the second case a child node C of the container of A is an ancestor 2659 // container of B. In this case, A is before B if the offset of A is less than or 2660 // equal to the index of the child node C and A is after B otherwise. 2661 c = containerB; 2662 while (c && c.parentNode != containerA) { 2663 c = c.parentNode; 2664 } 2665 if (c) { 2666 offsetC = 0; 2667 n = containerA.firstChild; 2668 2669 while (n != c && offsetC < offsetA) { 2670 offsetC++; 2671 n = n.nextSibling; 2672 } 2673 2674 if (offsetA <= offsetC) { 2675 return -1; // before 2676 } else { 2677 return 1; // after 2678 } 2679 } 2680 2681 // In the third case a child node C of the container of B is an ancestor container 2682 // of A. In this case, A is before B if the index of the child node C is less than 2683 // the offset of B and A is after B otherwise. 2684 c = containerA; 2685 while (c && c.parentNode != containerB) { 2686 c = c.parentNode; 2687 } 2688 2689 if (c) { 2690 offsetC = 0; 2691 n = containerB.firstChild; 2692 2693 while (n != c && offsetC < offsetB) { 2694 offsetC++; 2695 n = n.nextSibling; 2696 } 2697 2698 if (offsetC < offsetB) { 2699 return -1; // before 2700 } else { 2701 return 1; // after 2702 } 2703 } 2704 2705 // In the fourth case, none of three other cases hold: the containers of A and B 2706 // are siblings or descendants of sibling nodes. In this case, A is before B if 2707 // the container of A is before the container of B in a pre-order traversal of the 2708 // Ranges' context tree and A is after B otherwise. 2709 cmnRoot = this.dom.findCommonAncestor(containerA, containerB); 2710 childA = containerA; 2711 2712 while (childA && childA.parentNode != cmnRoot) { 2713 childA = childA.parentNode; 2714 } 2715 2716 if (!childA) { 2717 childA = cmnRoot; 2718 } 2719 2720 childB = containerB; 2721 while (childB && childB.parentNode != cmnRoot) { 2722 childB = childB.parentNode; 2723 } 2724 2725 if (!childB) { 2726 childB = cmnRoot; 2727 } 2728 2729 if (childA == childB) { 2730 return 0; // equal 2731 } 2732 2733 n = cmnRoot.firstChild; 2734 while (n) { 2735 if (n == childA) { 2736 return -1; // before 2737 } 2738 2739 if (n == childB) { 2740 return 1; // after 2741 } 2742 2743 n = n.nextSibling; 2744 } 2745 }, 2746 2747 _setEndPoint : function(st, n, o) { 2748 var t = this, ec, sc; 2749 2750 if (st) { 2751 t.startContainer = n; 2752 t.startOffset = o; 2753 } else { 2754 t.endContainer = n; 2755 t.endOffset = o; 2756 } 2757 2758 // If one boundary-point of a Range is set to have a root container 2759 // other than the current one for the Range, the Range is collapsed to 2760 // the new position. This enforces the restriction that both boundary- 2761 // points of a Range must have the same root container. 2762 ec = t.endContainer; 2763 while (ec.parentNode) 2764 ec = ec.parentNode; 2765 2766 sc = t.startContainer; 2767 while (sc.parentNode) 2768 sc = sc.parentNode; 2769 2770 if (sc != ec) { 2771 t.collapse(st); 2772 } else { 2773 // The start position of a Range is guaranteed to never be after the 2774 // end position. To enforce this restriction, if the start is set to 2775 // be at a position after the end, the Range is collapsed to that 2776 // position. 2777 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0) 2778 t.collapse(st); 2779 } 2780 2781 t.collapsed = t._isCollapsed(); 2782 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer); 2783 }, 2784 2785 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :) 2786 2787 _traverse : function(how) { 2788 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; 2789 2790 if (t.startContainer == t.endContainer) 2791 return t._traverseSameContainer(how); 2792 2793 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { 2794 if (p == t.startContainer) 2795 return t._traverseCommonStartContainer(c, how); 2796 2797 ++endContainerDepth; 2798 } 2799 2800 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { 2801 if (p == t.endContainer) 2802 return t._traverseCommonEndContainer(c, how); 2803 2804 ++startContainerDepth; 2805 } 2806 2807 depthDiff = startContainerDepth - endContainerDepth; 2808 2809 startNode = t.startContainer; 2810 while (depthDiff > 0) { 2811 startNode = startNode.parentNode; 2812 depthDiff--; 2813 } 2814 2815 endNode = t.endContainer; 2816 while (depthDiff < 0) { 2817 endNode = endNode.parentNode; 2818 depthDiff++; 2819 } 2820 2821 // ascend the ancestor hierarchy until we have a common parent. 2822 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { 2823 startNode = sp; 2824 endNode = ep; 2825 } 2826 2827 return t._traverseCommonAncestors(startNode, endNode, how); 2828 }, 2829 2830 _traverseSameContainer : function(how) { 2831 var t = this, frag, s, sub, n, cnt, sibling, xferNode; 2832 2833 if (how != DELETE) 2834 frag = t.dom.doc.createDocumentFragment(); 2835 2836 // If selection is empty, just return the fragment 2837 if (t.startOffset == t.endOffset) 2838 return frag; 2839 2840 // Text node needs special case handling 2841 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) { 2842 // get the substring 2843 s = t.startContainer.nodeValue; 2844 sub = s.substring(t.startOffset, t.endOffset); 2845 2846 // set the original text node to its new value 2847 if (how != CLONE) { 2848 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset); 2849 2850 // Nothing is partially selected, so collapse to start point 2851 t.collapse(true); 2852 } 2853 2854 if (how == DELETE) 2855 return null; 2856 2857 frag.appendChild(t.dom.doc.createTextNode(sub)); 2858 return frag; 2859 } 2860 2861 // Copy nodes between the start/end offsets. 2862 n = getSelectedNode(t.startContainer, t.startOffset); 2863 cnt = t.endOffset - t.startOffset; 2864 2865 while (cnt > 0) { 2866 sibling = n.nextSibling; 2867 xferNode = t._traverseFullySelected(n, how); 2868 2869 if (frag) 2870 frag.appendChild( xferNode ); 2871 2872 --cnt; 2873 n = sibling; 2874 } 2875 2876 // Nothing is partially selected, so collapse to start point 2877 if (how != CLONE) 2878 t.collapse(true); 2879 2880 return frag; 2881 }, 2882 2883 _traverseCommonStartContainer : function(endAncestor, how) { 2884 var t = this, frag, n, endIdx, cnt, sibling, xferNode; 2885 2886 if (how != DELETE) 2887 frag = t.dom.doc.createDocumentFragment(); 2888 2889 n = t._traverseRightBoundary(endAncestor, how); 2890 2891 if (frag) 2892 frag.appendChild(n); 2893 2894 endIdx = indexOf(endAncestor, t.startContainer); 2895 cnt = endIdx - t.startOffset; 2896 2897 if (cnt <= 0) { 2898 // Collapse to just before the endAncestor, which 2899 // is partially selected. 2900 if (how != CLONE) { 2901 t.setEndBefore(endAncestor); 2902 t.collapse(false); 2903 } 2904 2905 return frag; 2906 } 2907 2908 n = endAncestor.previousSibling; 2909 while (cnt > 0) { 2910 sibling = n.previousSibling; 2911 xferNode = t._traverseFullySelected(n, how); 2912 2913 if (frag) 2914 frag.insertBefore(xferNode, frag.firstChild); 2915 2916 --cnt; 2917 n = sibling; 2918 } 2919 2920 // Collapse to just before the endAncestor, which 2921 // is partially selected. 2922 if (how != CLONE) { 2923 t.setEndBefore(endAncestor); 2924 t.collapse(false); 2925 } 2926 2927 return frag; 2928 }, 2929 2930 _traverseCommonEndContainer : function(startAncestor, how) { 2931 var t = this, frag, startIdx, n, cnt, sibling, xferNode; 2932 2933 if (how != DELETE) 2934 frag = t.dom.doc.createDocumentFragment(); 2935 2936 n = t._traverseLeftBoundary(startAncestor, how); 2937 if (frag) 2938 frag.appendChild(n); 2939 2940 startIdx = indexOf(startAncestor, t.endContainer); 2941 ++startIdx; // Because we already traversed it.... 2942 2943 cnt = t.endOffset - startIdx; 2944 n = startAncestor.nextSibling; 2945 while (cnt > 0) { 2946 sibling = n.nextSibling; 2947 xferNode = t._traverseFullySelected(n, how); 2948 2949 if (frag) 2950 frag.appendChild(xferNode); 2951 2952 --cnt; 2953 n = sibling; 2954 } 2955 2956 if (how != CLONE) { 2957 t.setStartAfter(startAncestor); 2958 t.collapse(true); 2959 } 2960 2961 return frag; 2962 }, 2963 2964 _traverseCommonAncestors : function(startAncestor, endAncestor, how) { 2965 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; 2966 2967 if (how != DELETE) 2968 frag = t.dom.doc.createDocumentFragment(); 2969 2970 n = t._traverseLeftBoundary(startAncestor, how); 2971 if (frag) 2972 frag.appendChild(n); 2973 2974 commonParent = startAncestor.parentNode; 2975 startOffset = indexOf(startAncestor, commonParent); 2976 endOffset = indexOf(endAncestor, commonParent); 2977 ++startOffset; 2978 2979 cnt = endOffset - startOffset; 2980 sibling = startAncestor.nextSibling; 2981 2982 while (cnt > 0) { 2983 nextSibling = sibling.nextSibling; 2984 n = t._traverseFullySelected(sibling, how); 2985 2986 if (frag) 2987 frag.appendChild(n); 2988 2989 sibling = nextSibling; 2990 --cnt; 2991 } 2992 2993 n = t._traverseRightBoundary(endAncestor, how); 2994 2995 if (frag) 2996 frag.appendChild(n); 2997 2998 if (how != CLONE) { 2999 t.setStartAfter(startAncestor); 3000 t.collapse(true); 3001 } 3002 3003 return frag; 3004 }, 3005 3006 _traverseRightBoundary : function(root, how) { 3007 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent; 3008 var isFullySelected = next != t.endContainer; 3009 3010 if (next == root) 3011 return t._traverseNode(next, isFullySelected, false, how); 3012 3013 parent = next.parentNode; 3014 clonedParent = t._traverseNode(parent, false, false, how); 3015 3016 while (parent != null) { 3017 while (next != null) { 3018 prevSibling = next.previousSibling; 3019 clonedChild = t._traverseNode(next, isFullySelected, false, how); 3020 3021 if (how != DELETE) 3022 clonedParent.insertBefore(clonedChild, clonedParent.firstChild); 3023 3024 isFullySelected = true; 3025 next = prevSibling; 3026 } 3027 3028 if (parent == root) 3029 return clonedParent; 3030 3031 next = parent.previousSibling; 3032 parent = parent.parentNode; 3033 3034 clonedGrandParent = t._traverseNode(parent, false, false, how); 3035 3036 if (how != DELETE) 3037 clonedGrandParent.appendChild(clonedParent); 3038 3039 clonedParent = clonedGrandParent; 3040 } 3041 3042 // should never occur 3043 return null; 3044 }, 3045 3046 _traverseLeftBoundary : function(root, how) { 3047 var t = this, next = getSelectedNode(t.startContainer, t.startOffset); 3048 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; 3049 3050 if (next == root) 3051 return t._traverseNode(next, isFullySelected, true, how); 3052 3053 parent = next.parentNode; 3054 clonedParent = t._traverseNode(parent, false, true, how); 3055 3056 while (parent != null) { 3057 while (next != null) { 3058 nextSibling = next.nextSibling; 3059 clonedChild = t._traverseNode(next, isFullySelected, true, how); 3060 3061 if (how != DELETE) 3062 clonedParent.appendChild(clonedChild); 3063 3064 isFullySelected = true; 3065 next = nextSibling; 3066 } 3067 3068 if (parent == root) 3069 return clonedParent; 3070 3071 next = parent.nextSibling; 3072 parent = parent.parentNode; 3073 3074 clonedGrandParent = t._traverseNode(parent, false, true, how); 3075 3076 if (how != DELETE) 3077 clonedGrandParent.appendChild(clonedParent); 3078 3079 clonedParent = clonedGrandParent; 3080 } 3081 3082 // should never occur 3083 return null; 3084 }, 3085 3086 _traverseNode : function(n, isFullySelected, isLeft, how) { 3087 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode; 3088 3089 if (isFullySelected) 3090 return t._traverseFullySelected(n, how); 3091 3092 if (n.nodeType == 3 /* TEXT_NODE */) { 3093 txtValue = n.nodeValue; 3094 3095 if (isLeft) { 3096 offset = t.startOffset; 3097 newNodeValue = txtValue.substring(offset); 3098 oldNodeValue = txtValue.substring(0, offset); 3099 } else { 3100 offset = t.endOffset; 3101 newNodeValue = txtValue.substring(0, offset); 3102 oldNodeValue = txtValue.substring(offset); 3103 } 3104 3105 if (how != CLONE) 3106 n.nodeValue = oldNodeValue; 3107 3108 if (how == DELETE) 3109 return null; 3110 3111 newNode = n.cloneNode(false); 3112 newNode.nodeValue = newNodeValue; 3113 3114 return newNode; 3115 } 3116 3117 if (how == DELETE) 3118 return null; 3119 3120 return n.cloneNode(false); 3121 }, 3122 3123 _traverseFullySelected : function(n, how) { 3124 var t = this; 3125 3126 if (how != DELETE) 3127 return how == CLONE ? n.cloneNode(true) : n; 3128 3129 n.parentNode.removeChild(n); 3130 return null; 3131 } 3132 }); 3133 3134 ns.Range = Range; 3135 })(tinymce.dom); 3136 (function() { 3137 function Selection(selection) { 3138 var t = this, invisibleChar = '\uFEFF', range, lastIERng; 3139 3140 function compareRanges(rng1, rng2) { 3141 if (rng1 && rng2) { 3142 // Both are control ranges and the selected element matches 3143 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) 3144 return 1; 3145 3146 // Both are text ranges and the range matches 3147 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) 3148 return 1; 3149 } 3150 3151 return 0; 3152 }; 3153 3154 function getRange() { 3155 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos, endPos, element, sc, ec, collapsed; 3156 3157 function findIndex(element) { 3158 var nl = element.parentNode.childNodes, i; 3159 3160 for (i = nl.length - 1; i >= 0; i--) { 3161 if (nl[i] == element) 3162 return i; 3163 } 3164 3165 return -1; 3166 }; 3167 3168 function findEndPoint(start) { 3169 var rng = ieRange.duplicate(), parent, i, nl, n, offset = 0, index = 0, pos, tmpRng; 3170 3171 // Insert marker character 3172 rng.collapse(start); 3173 parent = rng.parentElement(); 3174 rng.pasteHTML(invisibleChar); // Needs to be a pasteHTML instead of .text = since IE has a bug with nodeValue 3175 3176 // Find marker character 3177 nl = parent.childNodes; 3178 for (i = 0; i < nl.length; i++) { 3179 n = nl[i]; 3180 3181 // Calculate node index excluding text node fragmentation 3182 if (i > 0 && (n.nodeType !== 3 || nl[i - 1].nodeType !== 3)) 3183 index++; 3184 3185 // If text node then calculate offset 3186 if (n.nodeType === 3) { 3187 // Look for marker 3188 pos = n.nodeValue.indexOf(invisibleChar); 3189 if (pos !== -1) { 3190 offset += pos; 3191 break; 3192 } 3193 3194 offset += n.nodeValue.length; 3195 } else 3196 offset = 0; 3197 } 3198 3199 // Remove marker character 3200 rng.moveStart('character', -1); 3201 rng.text = ''; 3202 3203 return {index : index, offset : offset, parent : parent}; 3204 }; 3205 3206 // If selection is outside the current document just return an empty range 3207 element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); 3208 if (element.ownerDocument != dom.doc) 3209 return domRange; 3210 3211 // Handle control selection or text selection of a image 3212 if (ieRange.item || !element.hasChildNodes()) { 3213 domRange.setStart(element.parentNode, findIndex(element)); 3214 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1); 3215 3216 return domRange; 3217 } 3218 3219 // Check collapsed state 3220 collapsed = selection.isCollapsed(); 3221 3222 // Find start and end pos index and offset 3223 startPos = findEndPoint(true); 3224 endPos = findEndPoint(false); 3225 3226 // Normalize the elements to avoid fragmented dom 3227 startPos.parent.normalize(); 3228 endPos.parent.normalize(); 3229 3230 // Set start container and offset 3231 sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)]; 3232 3233 if (sc.nodeType != 3) 3234 domRange.setStart(startPos.parent, startPos.index); 3235 else 3236 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset); 3237 3238 // Set end container and offset 3239 ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)]; 3240 3241 if (ec.nodeType != 3) { 3242 if (!collapsed) 3243 endPos.index++; 3244 3245 domRange.setEnd(endPos.parent, endPos.index); 3246 } else 3247 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset); 3248 3249 // If not collapsed then make sure offsets are valid 3250 if (!collapsed) { 3251 sc = domRange.startContainer; 3252 if (sc.nodeType == 1) 3253 domRange.setStart(sc, Math.min(domRange.startOffset, sc.childNodes.length)); 3254 3255 ec = domRange.endContainer; 3256 if (ec.nodeType == 1) 3257 domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length)); 3258 } 3259 3260 // Restore selection to new range 3261 t.addRange(domRange); 3262 3263 return domRange; 3264 }; 3265 3266 this.addRange = function(rng) { 3267 var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo; 3268 3269 // Setup some shorter versions 3270 sc = rng.startContainer; 3271 so = rng.startOffset; 3272 ec = rng.endContainer; 3273 eo = rng.endOffset; 3274 ieRng = body.createTextRange(); 3275 3276 // Find element 3277 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc; 3278 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec; 3279 3280 // Single element selection 3281 if (sc == ec && sc.nodeType == 1) { 3282 // Make control selection for some elements 3283 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) { 3284 ieRng = body.createControlRange(); 3285 ieRng.addElement(sc); 3286 } else { 3287 ieRng = body.createTextRange(); 3288 3289 // Padd empty elements with invisible character 3290 if (!sc.hasChildNodes() && sc.canHaveHTML) 3291 sc.innerHTML = invisibleChar; 3292 3293 // Select element contents 3294 ieRng.moveToElementText(sc); 3295 3296 // If it's only containing a padding remove it so the caret remains 3297 if (sc.innerHTML == invisibleChar) { 3298 ieRng.collapse(true); 3299 sc.removeChild(sc.firstChild); 3300 } 3301 } 3302 3303 if (so == eo) 3304 ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1); 3305 3306 ieRng.select(); 3307 3308 return; 3309 } 3310 3311 function getCharPos(container, offset) { 3312 var nodeVal, rng, pos; 3313 3314 if (container.nodeType != 3) 3315 return -1; 3316 3317 nodeVal = container.nodeValue; 3318 rng = body.createTextRange(); 3319 3320 // Insert marker at offset position 3321 container.nodeValue = nodeVal.substring(0, offset) + invisibleChar + nodeVal.substring(offset); 3322 3323 // Find char pos of marker and remove it 3324 rng.moveToElementText(container.parentNode); 3325 rng.findText(invisibleChar); 3326 pos = Math.abs(rng.moveStart('character', -0xFFFFF)); 3327 container.nodeValue = nodeVal; 3328 3329 return pos; 3330 }; 3331 3332 // Collapsed range 3333 if (rng.collapsed) { 3334 pos = getCharPos(sc, so); 3335 3336 ieRng = body.createTextRange(); 3337 ieRng.move('character', pos); 3338 ieRng.select(); 3339 3340 return; 3341 } else { 3342 // If same text container 3343 if (sc == ec && sc.nodeType == 3) { 3344 startPos = getCharPos(sc, so); 3345 3346 ieRng.move('character', startPos); 3347 ieRng.moveEnd('character', eo - so); 3348 ieRng.select(); 3349 3350 return; 3351 } 3352 3353 // Get caret positions 3354 startPos = getCharPos(sc, so); 3355 endPos = getCharPos(ec, eo); 3356 ieRng = body.createTextRange(); 3357 3358 // Move start of range to start character position or start element 3359 if (startPos == -1) { 3360 ieRng.moveToElementText(sc); 3361 startPos = 0; 3362 } else 3363 ieRng.move('character', startPos); 3364 3365 // Move end of range to end character position or end element 3366 tmpRng = body.createTextRange(); 3367 3368 if (endPos == -1) 3369 tmpRng.moveToElementText(ec); 3370 else 3371 tmpRng.move('character', endPos); 3372 3373 ieRng.setEndPoint('EndToEnd', tmpRng); 3374 ieRng.select(); 3375 3376 return; 3377 } 3378 }; 3379 3380 this.getRangeAt = function() { 3381 // Setup new range if the cache is empty 3382 if (!range || !compareRanges(lastIERng, selection.getRng())) { 3383 range = getRange(); 3384 3385 // Store away text range for next call 3386 lastIERng = selection.getRng(); 3387 } 3388 3389 // Return cached range 3390 return range; 3391 }; 3392 3393 this.destroy = function() { 3394 // Destroy cached range and last IE range to avoid memory leaks 3395 lastIERng = range = null; 3396 }; 3397 }; 3398 3399 // Expose the selection object 3400 tinymce.dom.TridentSelection = Selection; 3401 })(); 3402 3403 /* 3404 * Sizzle CSS Selector Engine - v1.0 3405 * Copyright 2009, The Dojo Foundation 3406 * Released under the MIT, BSD, and GPL Licenses. 3407 * More information: http://sizzlejs.com/ 3408 */ 3409 (function(){ 3410 3411 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, 3412 done = 0, 3413 toString = Object.prototype.toString, 3414 hasDuplicate = false; 3415 3416 var Sizzle = function(selector, context, results, seed) { 3417 results = results || []; 3418 var origContext = context = context || document; 3419 3420 if ( context.nodeType !== 1 && context.nodeType !== 9 ) { 3421 return []; 3422 } 3423 3424 if ( !selector || typeof selector !== "string" ) { 3425 return results; 3426 } 3427 3428 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context); 3429 3430 // Reset the position of the chunker regexp (start from head) 3431 chunker.lastIndex = 0; 3432 3433 while ( (m = chunker.exec(selector)) !== null ) { 3434 parts.push( m[1] ); 3435 3436 if ( m[2] ) { 3437 extra = RegExp.rightContext; 3438 break; 3439 } 3440 } 3441 3442 if ( parts.length > 1 && origPOS.exec( selector ) ) { 3443 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { 3444 set = posProcess( parts[0] + parts[1], context ); 3445 } else { 3446 set = Expr.relative[ parts[0] ] ? 3447 [ context ] : 3448 Sizzle( parts.shift(), context ); 3449 3450 while ( parts.length ) { 3451 selector = parts.shift(); 3452 3453 if ( Expr.relative[ selector ] ) 3454 selector += parts.shift(); 3455 3456 set = posProcess( selector, set ); 3457 } 3458 } 3459 } else { 3460 // Take a shortcut and set the context if the root selector is an ID 3461 // (but not if it'll be faster if the inner selector is an ID) 3462 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && 3463 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { 3464 var ret = Sizzle.find( parts.shift(), context, contextXML ); 3465 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; 3466 } 3467 3468 if ( context ) { 3469 var ret = seed ? 3470 { expr: parts.pop(), set: makeArray(seed) } : 3471 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); 3472 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; 3473 3474 if ( parts.length > 0 ) { 3475 checkSet = makeArray(set); 3476 } else { 3477 prune = false; 3478 } 3479 3480 while ( parts.length ) { 3481 var cur = parts.pop(), pop = cur; 3482 3483 if ( !Expr.relative[ cur ] ) { 3484 cur = ""; 3485 } else { 3486 pop = parts.pop(); 3487 } 3488 3489 if ( pop == null ) { 3490 pop = context; 3491 } 3492 3493 Expr.relative[ cur ]( checkSet, pop, contextXML ); 3494 } 3495 } else { 3496 checkSet = parts = []; 3497 } 3498 } 3499 3500 if ( !checkSet ) { 3501 checkSet = set; 3502 } 3503 3504 if ( !checkSet ) { 3505 throw "Syntax error, unrecognized expression: " + (cur || selector); 3506 } 3507 3508 if ( toString.call(checkSet) === "[object Array]" ) { 3509 if ( !prune ) { 3510 results.push.apply( results, checkSet ); 3511 } else if ( context && context.nodeType === 1 ) { 3512 for ( var i = 0; checkSet[i] != null; i++ ) { 3513 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { 3514 results.push( set[i] ); 3515 } 3516 } 3517 } else { 3518 for ( var i = 0; checkSet[i] != null; i++ ) { 3519 if ( checkSet[i] && checkSet[i].nodeType === 1 ) { 3520 results.push( set[i] ); 3521 } 3522 } 3523 } 3524 } else { 3525 makeArray( checkSet, results ); 3526 } 3527 3528 if ( extra ) { 3529 Sizzle( extra, origContext, results, seed ); 3530 Sizzle.uniqueSort( results ); 3531 } 3532 3533 return results; 3534 }; 3535 3536 Sizzle.uniqueSort = function(results){ 3537 if ( sortOrder ) { 3538 hasDuplicate = false; 3539 results.sort(sortOrder); 3540 3541 if ( hasDuplicate ) { 3542 for ( var i = 1; i < results.length; i++ ) { 3543 if ( results[i] === results[i-1] ) { 3544 results.splice(i--, 1); 3545 } 3546 } 3547 } 3548 } 3549 }; 3550 3551 Sizzle.matches = function(expr, set){ 3552 return Sizzle(expr, null, null, set); 3553 }; 3554 3555 Sizzle.find = function(expr, context, isXML){ 3556 var set, match; 3557 3558 if ( !expr ) { 3559 return []; 3560 } 3561 3562 for ( var i = 0, l = Expr.order.length; i < l; i++ ) { 3563 var type = Expr.order[i], match; 3564 3565 if ( (match = Expr.match[ type ].exec( expr )) ) { 3566 var left = RegExp.leftContext; 3567 3568 if ( left.substr( left.length - 1 ) !== "\\" ) { 3569 match[1] = (match[1] || "").replace(/\\/g, ""); 3570 set = Expr.find[ type ]( match, context, isXML ); 3571 if ( set != null ) { 3572 expr = expr.replace( Expr.match[ type ], "" ); 3573 break; 3574 } 3575 } 3576 } 3577 } 3578 3579 if ( !set ) { 3580 set = context.getElementsByTagName("*"); 3581 } 3582 3583 return {set: set, expr: expr}; 3584 }; 3585 3586 Sizzle.filter = function(expr, set, inplace, not){ 3587 var old = expr, result = [], curLoop = set, match, anyFound, 3588 isXMLFilter = set && set[0] && isXML(set[0]); 3589 3590 while ( expr && set.length ) { 3591 for ( var type in Expr.filter ) { 3592 if ( (match = Expr.match[ type ].exec( expr )) != null ) { 3593 var filter = Expr.filter[ type ], found, item; 3594 anyFound = false; 3595 3596 if ( curLoop == result ) { 3597 result = []; 3598 } 3599 3600 if ( Expr.preFilter[ type ] ) { 3601 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); 3602 3603 if ( !match ) { 3604 anyFound = found = true; 3605 } else if ( match === true ) { 3606 continue; 3607 } 3608 } 3609 3610 if ( match ) { 3611 for ( var i = 0; (item = curLoop[i]) != null; i++ ) { 3612 if ( item ) { 3613 found = filter( item, match, i, curLoop ); 3614 var pass = not ^ !!found; 3615 3616 if ( inplace && found != null ) { 3617 if ( pass ) { 3618 anyFound = true; 3619 } else { 3620 curLoop[i] = false; 3621 } 3622 } else if ( pass ) { 3623 result.push( item ); 3624 anyFound = true; 3625 } 3626 } 3627 } 3628 } 3629 3630 if ( found !== undefined ) { 3631 if ( !inplace ) { 3632 curLoop = result; 3633 } 3634 3635 expr = expr.replace( Expr.match[ type ], "" ); 3636 3637 if ( !anyFound ) { 3638 return []; 3639 } 3640 3641 break; 3642 } 3643 } 3644 } 3645 3646 // Improper expression 3647 if ( expr == old ) { 3648 if ( anyFound == null ) { 3649 throw "Syntax error, unrecognized expression: " + expr; 3650 } else { 3651 break; 3652 } 3653 } 3654 3655 old = expr; 3656 } 3657 3658 return curLoop; 3659 }; 3660 3661 var Expr = Sizzle.selectors = { 3662 order: [ "ID", "NAME", "TAG" ], 3663 match: { 3664 ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, 3665 CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, 3666 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/, 3667 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, 3668 TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/, 3669 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, 3670 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, 3671 PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ 3672 }, 3673 attrMap: { 3674 "class": "className", 3675 "for": "htmlFor" 3676 }, 3677 attrHandle: { 3678 href: function(elem){ 3679 return elem.getAttribute("href"); 3680 } 3681 }, 3682 relative: { 3683 "+": function(checkSet, part, isXML){ 3684 var isPartStr = typeof part === "string", 3685 isTag = isPartStr && !/\W/.test(part), 3686 isPartStrNotTag = isPartStr && !isTag; 3687 3688 if ( isTag && !isXML ) { 3689 part = part.toUpperCase(); 3690 } 3691 3692 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { 3693 if ( (elem = checkSet[i]) ) { 3694 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} 3695 3696 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? 3697 elem || false : 3698 elem === part; 3699 } 3700 } 3701 3702 if ( isPartStrNotTag ) { 3703 Sizzle.filter( part, checkSet, true ); 3704 } 3705 }, 3706 ">": function(checkSet, part, isXML){ 3707 var isPartStr = typeof part === "string"; 3708 3709 if ( isPartStr && !/\W/.test(part) ) { 3710 part = isXML ? part : part.toUpperCase(); 3711 3712 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 3713 var elem = checkSet[i]; 3714 if ( elem ) { 3715 var parent = elem.parentNode; 3716 checkSet[i] = parent.nodeName === part ? parent : false; 3717 } 3718 } 3719 } else { 3720 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 3721 var elem = checkSet[i]; 3722 if ( elem ) { 3723 checkSet[i] = isPartStr ? 3724 elem.parentNode : 3725 elem.parentNode === part; 3726 } 3727 } 3728 3729 if ( isPartStr ) { 3730 Sizzle.filter( part, checkSet, true ); 3731 } 3732 } 3733 }, 3734 "": function(checkSet, part, isXML){ 3735 var doneName = done++, checkFn = dirCheck; 3736 3737 if ( !part.match(/\W/) ) { 3738 var nodeCheck = part = isXML ? part : part.toUpperCase(); 3739 checkFn = dirNodeCheck; 3740 } 3741 3742 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); 3743 }, 3744 "~": function(checkSet, part, isXML){ 3745 var doneName = done++, checkFn = dirCheck; 3746 3747 if ( typeof part === "string" && !part.match(/\W/) ) { 3748 var nodeCheck = part = isXML ? part : part.toUpperCase(); 3749 checkFn = dirNodeCheck; 3750 } 3751 3752 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 3753 } 3754 }, 3755 find: { 3756 ID: function(match, context, isXML){ 3757 if ( typeof context.getElementById !== "undefined" && !isXML ) { 3758 var m = context.getElementById(match[1]); 3759 return m ? [m] : []; 3760 } 3761 }, 3762 NAME: function(match, context, isXML){ 3763 if ( typeof context.getElementsByName !== "undefined" ) { 3764 var ret = [], results = context.getElementsByName(match[1]); 3765 3766 for ( var i = 0, l = results.length; i < l; i++ ) { 3767 if ( results[i].getAttribute("name") === match[1] ) { 3768 ret.push( results[i] ); 3769 } 3770 } 3771 3772 return ret.length === 0 ? null : ret; 3773 } 3774 }, 3775 TAG: function(match, context){ 3776 return context.getElementsByTagName(match[1]); 3777 } 3778 }, 3779 preFilter: { 3780 CLASS: function(match, curLoop, inplace, result, not, isXML){ 3781 match = " " + match[1].replace(/\\/g, "") + " "; 3782 3783 if ( isXML ) { 3784 return match; 3785 } 3786 3787 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { 3788 if ( elem ) { 3789 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { 3790 if ( !inplace ) 3791 result.push( elem ); 3792 } else if ( inplace ) { 3793 curLoop[i] = false; 3794 } 3795 } 3796 } 3797 3798 return false; 3799 }, 3800 ID: function(match){ 3801 return match[1].replace(/\\/g, ""); 3802 }, 3803 TAG: function(match, curLoop){ 3804 for ( var i = 0; curLoop[i] === false; i++ ){} 3805 return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); 3806 }, 3807 CHILD: function(match){ 3808 if ( match[1] == "nth" ) { 3809 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' 3810 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( 3811 match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || 3812 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); 3813 3814 // calculate the numbers (first)n+(last) including if they are negative 3815 match[2] = (test[1] + (test[2] || 1)) - 0; 3816 match[3] = test[3] - 0; 3817 } 3818 3819 // TODO: Move to normal caching system 3820 match[0] = done++; 3821 3822 return match; 3823 }, 3824 ATTR: function(match, curLoop, inplace, result, not, isXML){ 3825 var name = match[1].replace(/\\/g, ""); 3826 3827 if ( !isXML && Expr.attrMap[name] ) { 3828 match[1] = Expr.attrMap[name]; 3829 } 3830 3831 if ( match[2] === "~=" ) { 3832 match[4] = " " + match[4] + " "; 3833 } 3834 3835 return match; 3836 }, 3837 PSEUDO: function(match, curLoop, inplace, result, not){ 3838 if ( match[1] === "not" ) { 3839 // If we're dealing with a complex expression, or a simple one 3840 if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) { 3841 match[3] = Sizzle(match[3], null, null, curLoop); 3842 } else { 3843 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); 3844 if ( !inplace ) { 3845 result.push.apply( result, ret ); 3846 } 3847 return false; 3848 } 3849 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { 3850 return true; 3851 } 3852 3853 return match; 3854 }, 3855 POS: function(match){ 3856 match.unshift( true ); 3857 return match; 3858 } 3859 }, 3860 filters: { 3861 enabled: function(elem){ 3862 return elem.disabled === false && elem.type !== "hidden"; 3863 }, 3864 disabled: function(elem){ 3865 return elem.disabled === true; 3866 }, 3867 checked: function(elem){ 3868 return elem.checked === true; 3869 }, 3870 selected: function(elem){ 3871 // Accessing this property makes selected-by-default 3872 // options in Safari work properly 3873 elem.parentNode.selectedIndex; 3874 return elem.selected === true; 3875 }, 3876 parent: function(elem){ 3877 return !!elem.firstChild; 3878 }, 3879 empty: function(elem){ 3880 return !elem.firstChild; 3881 }, 3882 has: function(elem, i, match){ 3883 return !!Sizzle( match[3], elem ).length; 3884 }, 3885 header: function(elem){ 3886 return /h\d/i.test( elem.nodeName ); 3887 }, 3888 text: function(elem){ 3889 return "text" === elem.type; 3890 }, 3891 radio: function(elem){ 3892 return "radio" === elem.type; 3893 }, 3894 checkbox: function(elem){ 3895 return "checkbox" === elem.type; 3896 }, 3897 file: function(elem){ 3898 return "file" === elem.type; 3899 }, 3900 password: function(elem){ 3901 return "password" === elem.type; 3902 }, 3903 submit: function(elem){ 3904 return "submit" === elem.type; 3905 }, 3906 image: function(elem){ 3907 return "image" === elem.type; 3908 }, 3909 reset: function(elem){ 3910 return "reset" === elem.type; 3911 }, 3912 button: function(elem){ 3913 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; 3914 }, 3915 input: function(elem){ 3916 return /input|select|textarea|button/i.test(elem.nodeName); 3917 } 3918 }, 3919 setFilters: { 3920 first: function(elem, i){ 3921 return i === 0; 3922 }, 3923 last: function(elem, i, match, array){ 3924 return i === array.length - 1; 3925 }, 3926 even: function(elem, i){ 3927 return i % 2 === 0; 3928 }, 3929 odd: function(elem, i){ 3930 return i % 2 === 1; 3931 }, 3932 lt: function(elem, i, match){ 3933 return i < match[3] - 0; 3934 }, 3935 gt: function(elem, i, match){ 3936 return i > match[3] - 0; 3937 }, 3938 nth: function(elem, i, match){ 3939 return match[3] - 0 == i; 3940 }, 3941 eq: function(elem, i, match){ 3942 return match[3] - 0 == i; 3943 } 3944 }, 3945 filter: { 3946 PSEUDO: function(elem, match, i, array){ 3947 var name = match[1], filter = Expr.filters[ name ]; 3948 3949 if ( filter ) { 3950 return filter( elem, i, match, array ); 3951 } else if ( name === "contains" ) { 3952 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; 3953 } else if ( name === "not" ) { 3954 var not = match[3]; 3955 3956 for ( var i = 0, l = not.length; i < l; i++ ) { 3957 if ( not[i] === elem ) { 3958 return false; 3959 } 3960 } 3961 3962 return true; 3963 } 3964 }, 3965 CHILD: function(elem, match){ 3966 var type = match[1], node = elem; 3967 switch (type) { 3968 case 'only': 3969 case 'first': 3970 while (node = node.previousSibling) { 3971 if ( node.nodeType === 1 ) return false; 3972 } 3973 if ( type == 'first') return true; 3974 node = elem; 3975 case 'last': 3976 while (node = node.nextSibling) { 3977 if ( node.nodeType === 1 ) return false; 3978 } 3979 return true; 3980 case 'nth': 3981 var first = match[2], last = match[3]; 3982 3983 if ( first == 1 && last == 0 ) { 3984 return true; 3985 } 3986 3987 var doneName = match[0], 3988 parent = elem.parentNode; 3989 3990 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { 3991 var count = 0; 3992 for ( node = parent.firstChild; node; node = node.nextSibling ) { 3993 if ( node.nodeType === 1 ) { 3994 node.nodeIndex = ++count; 3995 } 3996 } 3997 parent.sizcache = doneName; 3998 } 3999 4000 var diff = elem.nodeIndex - last; 4001 if ( first == 0 ) { 4002 return diff == 0; 4003 } else { 4004 return ( diff % first == 0 && diff / first >= 0 ); 4005 } 4006 } 4007 }, 4008 ID: function(elem, match){ 4009 return elem.nodeType === 1 && elem.getAttribute("id") === match; 4010 }, 4011 TAG: function(elem, match){ 4012 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; 4013 }, 4014 CLASS: function(elem, match){ 4015 return (" " + (elem.className || elem.getAttribute("class")) + " ") 4016 .indexOf( match ) > -1; 4017 }, 4018 ATTR: function(elem, match){ 4019 var name = match[1], 4020 result = Expr.attrHandle[ name ] ? 4021 Expr.attrHandle[ name ]( elem ) : 4022 elem[ name ] != null ? 4023 elem[ name ] : 4024 elem.getAttribute( name ), 4025 value = result + "", 4026 type = match[2], 4027 check = match[4]; 4028 4029 return result == null ? 4030 type === "!=" : 4031 type === "=" ? 4032 value === check : 4033 type === "*=" ? 4034 value.indexOf(check) >= 0 : 4035 type === "~=" ? 4036 (" " + value + " ").indexOf(check) >= 0 : 4037 !check ? 4038 value && result !== false : 4039 type === "!=" ? 4040 value != check : 4041 type === "^=" ? 4042 value.indexOf(check) === 0 : 4043 type === "$=" ? 4044 value.substr(value.length - check.length) === check : 4045 type === "|=" ? 4046 value === check || value.substr(0, check.length + 1) === check + "-" : 4047 false; 4048 }, 4049 POS: function(elem, match, i, array){ 4050 var name = match[2], filter = Expr.setFilters[ name ]; 4051 4052 if ( filter ) { 4053 return filter( elem, i, match, array ); 4054 } 4055 } 4056 } 4057 }; 4058 4059 var origPOS = Expr.match.POS; 4060 4061 for ( var type in Expr.match ) { 4062 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); 4063 } 4064 4065 var makeArray = function(array, results) { 4066 array = Array.prototype.slice.call( array ); 4067 4068 if ( results ) { 4069 results.push.apply( results, array ); 4070 return results; 4071 } 4072 4073 return array; 4074 }; 4075 4076 // Perform a simple check to determine if the browser is capable of 4077 // converting a NodeList to an array using builtin methods. 4078 try { 4079 Array.prototype.slice.call( document.documentElement.childNodes ); 4080 4081 // Provide a fallback method if it does not work 4082 } catch(e){ 4083 makeArray = function(array, results) { 4084 var ret = results || []; 4085 4086 if ( toString.call(array) === "[object Array]" ) { 4087 Array.prototype.push.apply( ret, array ); 4088 } else { 4089 if ( typeof array.length === "number" ) { 4090 for ( var i = 0, l = array.length; i < l; i++ ) { 4091 ret.push( array[i] ); 4092 } 4093 } else { 4094 for ( var i = 0; array[i]; i++ ) { 4095 ret.push( array[i] ); 4096 } 4097 } 4098 } 4099 4100 return ret; 4101 }; 4102 } 4103 4104 var sortOrder; 4105 4106 if ( document.documentElement.compareDocumentPosition ) { 4107 sortOrder = function( a, b ) { 4108 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; 4109 if ( ret === 0 ) { 4110 hasDuplicate = true; 4111 } 4112 return ret; 4113 }; 4114 } else if ( "sourceIndex" in document.documentElement ) { 4115 sortOrder = function( a, b ) { 4116 var ret = a.sourceIndex - b.sourceIndex; 4117 if ( ret === 0 ) { 4118 hasDuplicate = true; 4119 } 4120 return ret; 4121 }; 4122 } else if ( document.createRange ) { 4123 sortOrder = function( a, b ) { 4124 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); 4125 aRange.setStart(a, 0); 4126 aRange.setEnd(a, 0); 4127 bRange.setStart(b, 0); 4128 bRange.setEnd(b, 0); 4129 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); 4130 if ( ret === 0 ) { 4131 hasDuplicate = true; 4132 } 4133 return ret; 4134 }; 4135 } 4136 4137 // Check to see if the browser returns elements by name when 4138 // querying by getElementById (and provide a workaround) 4139 (function(){ 4140 // We're going to inject a fake input element with a specified name 4141 var form = document.createElement("div"), 4142 id = "script" + (new Date).getTime(); 4143 form.innerHTML = "<a name='" + id + "'/>"; 4144 4145 // Inject it into the root element, check its status, and remove it quickly 4146 var root = document.documentElement; 4147 root.insertBefore( form, root.firstChild ); 4148 4149 // The workaround has to do additional checks after a getElementById 4150 // Which slows things down for other browsers (hence the branching) 4151 if ( !!document.getElementById( id ) ) { 4152 Expr.find.ID = function(match, context, isXML){ 4153 if ( typeof context.getElementById !== "undefined" && !isXML ) { 4154 var m = context.getElementById(match[1]); 4155 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; 4156 } 4157 }; 4158 4159 Expr.filter.ID = function(elem, match){ 4160 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); 4161 return elem.nodeType === 1 && node && node.nodeValue === match; 4162 }; 4163 } 4164 4165 root.removeChild( form ); 4166 })(); 4167 4168 (function(){ 4169 // Check to see if the browser returns only elements 4170 // when doing getElementsByTagName("*") 4171 4172 // Create a fake element 4173 var div = document.createElement("div"); 4174 div.appendChild( document.createComment("") ); 4175 4176 // Make sure no comments are found 4177 if ( div.getElementsByTagName("*").length > 0 ) { 4178 Expr.find.TAG = function(match, context){ 4179 var results = context.getElementsByTagName(match[1]); 4180 4181 // Filter out possible comments 4182 if ( match[1] === "*" ) { 4183 var tmp = []; 4184 4185 for ( var i = 0; results[i]; i++ ) { 4186 if ( results[i].nodeType === 1 ) { 4187 tmp.push( results[i] ); 4188 } 4189 } 4190 4191 results = tmp; 4192 } 4193 4194 return results; 4195 }; 4196 } 4197 4198 // Check to see if an attribute returns normalized href attributes 4199 div.innerHTML = "<a href='#'></a>"; 4200 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && 4201 div.firstChild.getAttribute("href") !== "#" ) { 4202 Expr.attrHandle.href = function(elem){ 4203 return elem.getAttribute("href", 2); 4204 }; 4205 } 4206 })(); 4207 4208 if ( document.querySelectorAll ) (function(){ 4209 var oldSizzle = Sizzle, div = document.createElement("div"); 4210 div.innerHTML = "<p class='TEST'></p>"; 4211 4212 // Safari can't handle uppercase or unicode characters when 4213 // in quirks mode. 4214 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { 4215 return; 4216 } 4217 4218 Sizzle = function(query, context, extra, seed){ 4219 context = context || document; 4220 4221 // Only use querySelectorAll on non-XML documents 4222 // (ID selectors don't work in non-HTML documents) 4223 if ( !seed && context.nodeType === 9 && !isXML(context) ) { 4224 try { 4225 return makeArray( context.querySelectorAll(query), extra ); 4226 } catch(e){} 4227 } 4228 4229 return oldSizzle(query, context, extra, seed); 4230 }; 4231 4232 for ( var prop in oldSizzle ) { 4233 Sizzle[ prop ] = oldSizzle[ prop ]; 4234 } 4235 })(); 4236 4237 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ 4238 var div = document.createElement("div"); 4239 div.innerHTML = "<div class='test e'></div><div class='test'></div>"; 4240 4241 // Opera can't find a second classname (in 9.6) 4242 if ( div.getElementsByClassName("e").length === 0 ) 4243 return; 4244 4245 // Safari caches class attributes, doesn't catch changes (in 3.2) 4246 div.lastChild.className = "e"; 4247 4248 if ( div.getElementsByClassName("e").length === 1 ) 4249 return; 4250 4251 Expr.order.splice(1, 0, "CLASS"); 4252 Expr.find.CLASS = function(match, context, isXML) { 4253 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { 4254 return context.getElementsByClassName(match[1]); 4255 } 4256 }; 4257 })(); 4258 4259 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 4260 var sibDir = dir == "previousSibling" && !isXML; 4261 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 4262 var elem = checkSet[i]; 4263 if ( elem ) { 4264 if ( sibDir && elem.nodeType === 1 ){ 4265 elem.sizcache = doneName; 4266 elem.sizset = i; 4267 } 4268 elem = elem[dir]; 4269 var match = false; 4270 4271 while ( elem ) { 4272 if ( elem.sizcache === doneName ) { 4273 match = checkSet[elem.sizset]; 4274 break; 4275 } 4276 4277 if ( elem.nodeType === 1 && !isXML ){ 4278 elem.sizcache = doneName; 4279 elem.sizset = i; 4280 } 4281 4282 if ( elem.nodeName === cur ) { 4283 match = elem; 4284 break; 4285 } 4286 4287 elem = elem[dir]; 4288 } 4289 4290 checkSet[i] = match; 4291 } 4292 } 4293 } 4294 4295 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 4296 var sibDir = dir == "previousSibling" && !isXML; 4297 for ( var i = 0, l = checkSet.length; i < l; i++ ) { 4298 var elem = checkSet[i]; 4299 if ( elem ) { 4300 if ( sibDir && elem.nodeType === 1 ) { 4301 elem.sizcache = doneName; 4302 elem.sizset = i; 4303 } 4304 elem = elem[dir]; 4305 var match = false; 4306 4307 while ( elem ) { 4308 if ( elem.sizcache === doneName ) { 4309 match = checkSet[elem.sizset]; 4310 break; 4311 } 4312 4313 if ( elem.nodeType === 1 ) { 4314 if ( !isXML ) { 4315 elem.sizcache = doneName; 4316 elem.sizset = i; 4317 } 4318 if ( typeof cur !== "string" ) { 4319 if ( elem === cur ) { 4320 match = true; 4321 break; 4322 } 4323 4324 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { 4325 match = elem; 4326 break; 4327 } 4328 } 4329 4330 elem = elem[dir]; 4331 } 4332 4333 checkSet[i] = match; 4334 } 4335 } 4336 } 4337 4338 var contains = document.compareDocumentPosition ? function(a, b){ 4339 return a.compareDocumentPosition(b) & 16; 4340 } : function(a, b){ 4341 return a !== b && (a.contains ? a.contains(b) : true); 4342 }; 4343 4344 var isXML = function(elem){ 4345 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || 4346 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; 4347 }; 4348 4349 var posProcess = function(selector, context){ 4350 var tmpSet = [], later = "", match, 4351 root = context.nodeType ? [context] : context; 4352 4353 // Position selectors must be done after the filter 4354 // And so must :not(positional) so we move all PSEUDOs to the end 4355 while ( (match = Expr.match.PSEUDO.exec( selector )) ) { 4356 later += match[0]; 4357 selector = selector.replace( Expr.match.PSEUDO, "" ); 4358 } 4359 4360 selector = Expr.relative[selector] ? selector + "*" : selector; 4361 4362 for ( var i = 0, l = root.length; i < l; i++ ) { 4363 Sizzle( selector, root[i], tmpSet ); 4364 } 4365 4366 return Sizzle.filter( later, tmpSet ); 4367 }; 4368 4369 // EXPOSE 4370 4371 window.tinymce.dom.Sizzle = Sizzle; 4372 4373 })(); 4374 4375 (function(tinymce) { 4376 // Shorten names 4377 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; 4378 4379 tinymce.create('tinymce.dom.EventUtils', { 4380 EventUtils : function() { 4381 this.inits = []; 4382 this.events = []; 4383 }, 4384 4385 add : function(o, n, f, s) { 4386 var cb, t = this, el = t.events, r; 4387 4388 if (n instanceof Array) { 4389 r = []; 4390 4391 each(n, function(n) { 4392 r.push(t.add(o, n, f, s)); 4393 }); 4394 4395 return r; 4396 } 4397 4398 // Handle array 4399 if (o && o.hasOwnProperty && o instanceof Array) { 4400 r = []; 4401 4402 each(o, function(o) { 4403 o = DOM.get(o); 4404 r.push(t.add(o, n, f, s)); 4405 }); 4406 4407 return r; 4408 } 4409 4410 o = DOM.get(o); 4411 4412 if (!o) 4413 return; 4414 4415 // Setup event callback 4416 cb = function(e) { 4417 // Is all events disabled 4418 if (t.disabled) 4419 return; 4420 4421 e = e || window.event; 4422 4423 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid 4424 if (e && isIE) { 4425 if (!e.target) 4426 e.target = e.srcElement; 4427 4428 // Patch in preventDefault, stopPropagation methods for W3C compatibility 4429 tinymce.extend(e, t._stoppers); 4430 } 4431 4432 if (!s) 4433 return f(e); 4434 4435 return f.call(s, e); 4436 }; 4437 4438 if (n == 'unload') { 4439 tinymce.unloads.unshift({func : cb}); 4440 return cb; 4441 } 4442 4443 if (n == 'init') { 4444 if (t.domLoaded) 4445 cb(); 4446 else 4447 t.inits.push(cb); 4448 4449 return cb; 4450 } 4451 4452 // Store away listener reference 4453 el.push({ 4454 obj : o, 4455 name : n, 4456 func : f, 4457 cfunc : cb, 4458 scope : s 4459 }); 4460 4461 t._add(o, n, cb); 4462 4463 return f; 4464 }, 4465 4466 remove : function(o, n, f) { 4467 var t = this, a = t.events, s = false, r; 4468 4469 // Handle array 4470 if (o && o.hasOwnProperty && o instanceof Array) { 4471 r = []; 4472 4473 each(o, function(o) { 4474 o = DOM.get(o); 4475 r.push(t.remove(o, n, f)); 4476 }); 4477 4478 return r; 4479 } 4480 4481 o = DOM.get(o); 4482 4483 each(a, function(e, i) { 4484 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) { 4485 a.splice(i, 1); 4486 t._remove(o, n, e.cfunc); 4487 s = true; 4488 return false; 4489 } 4490 }); 4491 4492 return s; 4493 }, 4494 4495 clear : function(o) { 4496 var t = this, a = t.events, i, e; 4497 4498 if (o) { 4499 o = DOM.get(o); 4500 4501 for (i = a.length - 1; i >= 0; i--) { 4502 e = a[i]; 4503 4504 if (e.obj === o) { 4505 t._remove(e.obj, e.name, e.cfunc); 4506 e.obj = e.cfunc = null; 4507 a.splice(i, 1); 4508 } 4509 } 4510 } 4511 }, 4512 4513 cancel : function(e) { 4514 if (!e) 4515 return false; 4516 4517 this.stop(e); 4518 4519 return this.prevent(e); 4520 }, 4521 4522 stop : function(e) { 4523 if (e.stopPropagation) 4524 e.stopPropagation(); 4525 else 4526 e.cancelBubble = true; 4527 4528 return false; 4529 }, 4530 4531 prevent : function(e) { 4532 if (e.preventDefault) 4533 e.preventDefault(); 4534 else 4535 e.returnValue = false; 4536 4537 return false; 4538 }, 4539 4540 destroy : function() { 4541 var t = this; 4542 4543 each(t.events, function(e, i) { 4544 t._remove(e.obj, e.name, e.cfunc); 4545 e.obj = e.cfunc = null; 4546 }); 4547 4548 t.events = []; 4549 t = null; 4550 }, 4551 4552 _add : function(o, n, f) { 4553 if (o.attachEvent) 4554 o.attachEvent('on' + n, f); 4555 else if (o.addEventListener) 4556 o.addEventListener(n, f, false); 4557 else 4558 o['on' + n] = f; 4559 }, 4560 4561 _remove : function(o, n, f) { 4562 if (o) { 4563 try { 4564 if (o.detachEvent) 4565 o.detachEvent('on' + n, f); 4566 else if (o.removeEventListener) 4567 o.removeEventListener(n, f, false); 4568 else 4569 o['on' + n] = null; 4570 } catch (ex) { 4571 // Might fail with permission denined on IE so we just ignore that 4572 } 4573 } 4574 }, 4575 4576 _pageInit : function(win) { 4577 var t = this; 4578 4579 // Keep it from running more than once 4580 if (t.domLoaded) 4581 return; 4582 4583 t.domLoaded = true; 4584 4585 each(t.inits, function(c) { 4586 c(); 4587 }); 4588 4589 t.inits = []; 4590 }, 4591 4592 _wait : function(win) { 4593 var t = this, doc = win.document; 4594 4595 // No need since the document is already loaded 4596 if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) { 4597 t.domLoaded = 1; 4598 return; 4599 } 4600 4601 // Use IE method 4602 if (doc.attachEvent) { 4603 doc.attachEvent("onreadystatechange", function() { 4604 if (doc.readyState === "complete") { 4605 doc.detachEvent("onreadystatechange", arguments.callee); 4606 t._pageInit(win); 4607 } 4608 }); 4609 4610 if (doc.documentElement.doScroll && win == win.top) { 4611 (function() { 4612 if (t.domLoaded) 4613 return; 4614 4615 try { 4616 // If IE is used, use the trick by Diego Perini 4617 // http://javascript.nwbox.com/IEContentLoaded/ 4618 doc.documentElement.doScroll("left"); 4619 } catch (ex) { 4620 setTimeout(arguments.callee, 0); 4621 return; 4622 } 4623 4624 t._pageInit(win); 4625 })(); 4626 } 4627 } else if (doc.addEventListener) { 4628 t._add(win, 'DOMContentLoaded', function() { 4629 t._pageInit(win); 4630 }); 4631 } 4632 4633 t._add(win, 'load', function() { 4634 t._pageInit(win); 4635 }); 4636 }, 4637 4638 _stoppers : { 4639 preventDefault : function() { 4640 this.returnValue = false; 4641 }, 4642 4643 stopPropagation : function() { 4644 this.cancelBubble = true; 4645 } 4646 } 4647 }); 4648 4649 Event = tinymce.dom.Event = new tinymce.dom.EventUtils(); 4650 4651 // Dispatch DOM content loaded event for IE and Safari 4652 Event._wait(window); 4653 4654 tinymce.addUnload(function() { 4655 Event.destroy(); 4656 }); 4657 })(tinymce); 4658 (function(tinymce) { 4659 var each = tinymce.each; 4660 4661 tinymce.create('tinymce.dom.Element', { 4662 Element : function(id, s) { 4663 var t = this, dom, el; 4664 4665 s = s || {}; 4666 t.id = id; 4667 t.dom = dom = s.dom || tinymce.DOM; 4668 t.settings = s; 4669 4670 // Only IE leaks DOM references, this is a lot faster 4671 if (!tinymce.isIE) 4672 el = t.dom.get(t.id); 4673 4674 each([ 4675 'getPos', 4676 'getRect', 4677 'getParent', 4678 'add', 4679 'setStyle', 4680 'getStyle', 4681 'setStyles', 4682 'setAttrib', 4683 'setAttribs', 4684 'getAttrib', 4685 'addClass', 4686 'removeClass', 4687 'hasClass', 4688 'getOuterHTML', 4689 'setOuterHTML', 4690 'remove', 4691 'show', 4692 'hide', 4693 'isHidden', 4694 'setHTML', 4695 'get' 4696 ], function(k) { 4697 t[k] = function() { 4698 var a = [id], i; 4699 4700 for (i = 0; i < arguments.length; i++) 4701 a.push(arguments[i]); 4702 4703 a = dom[k].apply(dom, a); 4704 t.update(k); 4705 4706 return a; 4707 }; 4708 }); 4709 }, 4710 4711 on : function(n, f, s) { 4712 return tinymce.dom.Event.add(this.id, n, f, s); 4713 }, 4714 4715 getXY : function() { 4716 return { 4717 x : parseInt(this.getStyle('left')), 4718 y : parseInt(this.getStyle('top')) 4719 }; 4720 }, 4721 4722 getSize : function() { 4723 var n = this.dom.get(this.id); 4724 4725 return { 4726 w : parseInt(this.getStyle('width') || n.clientWidth), 4727 h : parseInt(this.getStyle('height') || n.clientHeight) 4728 }; 4729 }, 4730 4731 moveTo : function(x, y) { 4732 this.setStyles({left : x, top : y}); 4733 }, 4734 4735 moveBy : function(x, y) { 4736 var p = this.getXY(); 4737 4738 this.moveTo(p.x + x, p.y + y); 4739 }, 4740 4741 resizeTo : function(w, h) { 4742 this.setStyles({width : w, height : h}); 4743 }, 4744 4745 resizeBy : function(w, h) { 4746 var s = this.getSize(); 4747 4748 this.resizeTo(s.w + w, s.h + h); 4749 }, 4750 4751 update : function(k) { 4752 var t = this, b, dom = t.dom; 4753 4754 if (tinymce.isIE6 && t.settings.blocker) { 4755 k = k || ''; 4756 4757 // Ignore getters 4758 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0) 4759 return; 4760 4761 // Remove blocker on remove 4762 if (k == 'remove') { 4763 dom.remove(t.blocker); 4764 return; 4765 } 4766 4767 if (!t.blocker) { 4768 t.blocker = dom.uniqueId(); 4769 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'}); 4770 dom.setStyle(b, 'opacity', 0); 4771 } else 4772 b = dom.get(t.blocker); 4773 4774 dom.setStyle(b, 'left', t.getStyle('left', 1)); 4775 dom.setStyle(b, 'top', t.getStyle('top', 1)); 4776 dom.setStyle(b, 'width', t.getStyle('width', 1)); 4777 dom.setStyle(b, 'height', t.getStyle('height', 1)); 4778 dom.setStyle(b, 'display', t.getStyle('display', 1)); 4779 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1); 4780 } 4781 } 4782 }); 4783 })(tinymce); 4784 (function(tinymce) { 4785 function trimNl(s) { 4786 return s.replace(/[\n\r]+/g, ''); 4787 }; 4788 4789 // Shorten names 4790 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each; 4791 4792 tinymce.create('tinymce.dom.Selection', { 4793 Selection : function(dom, win, serializer) { 4794 var t = this; 4795 4796 t.dom = dom; 4797 t.win = win; 4798 t.serializer = serializer; 4799 4800 // Add events 4801 each([ 4802 'onBeforeSetContent', 4803 'onBeforeGetContent', 4804 'onSetContent', 4805 'onGetContent' 4806 ], function(e) { 4807 t[e] = new tinymce.util.Dispatcher(t); 4808 }); 4809 4810 // No W3C Range support 4811 if (!t.win.getSelection) 4812 t.tridentSel = new tinymce.dom.TridentSelection(t); 4813 4814 // Prevent leaks 4815 tinymce.addUnload(t.destroy, t); 4816 }, 4817 4818 getContent : function(s) { 4819 var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n; 4820 4821 s = s || {}; 4822 wb = wa = ''; 4823 s.get = true; 4824 s.format = s.format || 'html'; 4825 t.onBeforeGetContent.dispatch(t, s); 4826 4827 if (s.format == 'text') 4828 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : '')); 4829 4830 if (r.cloneContents) { 4831 n = r.cloneContents(); 4832 4833 if (n) 4834 e.appendChild(n); 4835 } else if (is(r.item) || is(r.htmlText)) 4836 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText; 4837 else 4838 e.innerHTML = r.toString(); 4839 4840 // Keep whitespace before and after 4841 if (/^\s/.test(e.innerHTML)) 4842 wb = ' '; 4843 4844 if (/\s+$/.test(e.innerHTML)) 4845 wa = ' '; 4846 4847 s.getInner = true; 4848 4849 s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa; 4850 t.onGetContent.dispatch(t, s); 4851 4852 return s.content; 4853 }, 4854 4855 setContent : function(h, s) { 4856 var t = this, r = t.getRng(), c, d = t.win.document; 4857 4858 s = s || {format : 'html'}; 4859 s.set = true; 4860 h = s.content = t.dom.processHTML(h); 4861 4862 // Dispatch before set content event 4863 t.onBeforeSetContent.dispatch(t, s); 4864 h = s.content; 4865 4866 if (r.insertNode) { 4867 // Make caret marker since insertNode places the caret in the beginning of text after insert 4868 h += '<span id="__caret">_</span>'; 4869 4870 // Delete and insert new node 4871 r.deleteContents(); 4872 r.insertNode(t.getRng().createContextualFragment(h)); 4873 4874 // Move to caret marker 4875 c = t.dom.get('__caret'); 4876 4877 // Make sure we wrap it compleatly, Opera fails with a simple select call 4878 r = d.createRange(); 4879 r.setStartBefore(c); 4880 r.setEndAfter(c); 4881 t.setRng(r); 4882 4883 // Delete the marker, and hopefully the caret gets placed in the right location 4884 // Removed this since it seems to remove in FF and simply deleting it 4885 // doesn't seem to affect the caret position in any browser 4886 //d.execCommand('Delete', false, null); 4887 4888 // Remove the caret position 4889 t.dom.remove('__caret'); 4890 } else { 4891 if (r.item) { 4892 // Delete content and get caret text selection 4893 d.execCommand('Delete', false, null); 4894 r = t.getRng(); 4895 } 4896 4897 r.pasteHTML(h); 4898 } 4899 4900 // Dispatch set content event 4901 t.onSetContent.dispatch(t, s); 4902 }, 4903 4904 getStart : function() { 4905 var t = this, r = t.getRng(), e; 4906 4907 if (isIE) { 4908 if (r.item) 4909 return r.item(0); 4910 4911 r = r.duplicate(); 4912 r.collapse(1); 4913 e = r.parentElement(); 4914 4915 if (e && e.nodeName == 'BODY') 4916 return e.firstChild; 4917 4918 return e; 4919 } else { 4920 e = r.startContainer; 4921 4922 if (e.nodeName == 'BODY') 4923 return e.firstChild; 4924 4925 return t.dom.getParent(e, '*'); 4926 } 4927 }, 4928 4929 getEnd : function() { 4930 var t = this, r = t.getRng(), e; 4931 4932 if (isIE) { 4933 if (r.item) 4934 return r.item(0); 4935 4936 r = r.duplicate(); 4937 r.collapse(0); 4938 e = r.parentElement(); 4939 4940 if (e && e.nodeName == 'BODY') 4941 return e.lastChild; 4942 4943 return e; 4944 } else { 4945 e = r.endContainer; 4946 4947 if (e.nodeName == 'BODY') 4948 return e.lastChild; 4949 4950 return t.dom.getParent(e, '*'); 4951 } 4952 }, 4953 4954 getBookmark : function(si) { 4955 var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv; 4956 sx = vp.x; 4957 sy = vp.y; 4958 4959 // Simple bookmark fast but not as persistent 4960 if (si) 4961 return {rng : r, scrollX : sx, scrollY : sy}; 4962 4963 // Handle IE 4964 if (isIE) { 4965 // Control selection 4966 if (r.item) { 4967 e = r.item(0); 4968 4969 each(t.dom.select(e.nodeName), function(n, i) { 4970 if (e == n) { 4971 sp = i; 4972 return false; 4973 } 4974 }); 4975 4976 return { 4977 tag : e.nodeName, 4978 index : sp, 4979 scrollX : sx, 4980 scrollY : sy 4981 }; 4982 } 4983 4984 // Text selection 4985 tr = t.dom.doc.body.createTextRange(); 4986 tr.moveToElementText(ro); 4987 tr.collapse(true); 4988 bp = Math.abs(tr.move('character', c)); 4989 4990 tr = r.duplicate(); 4991 tr.collapse(true); 4992 sp = Math.abs(tr.move('character', c)); 4993 4994 tr = r.duplicate(); 4995 tr.collapse(false); 4996 le = Math.abs(tr.move('character', c)) - sp; 4997 4998 return { 4999 start : sp - bp, 5000 length : le, 5001 scrollX : sx, 5002 scrollY : sy 5003 }; 5004 } 5005 5006 // Handle W3C 5007 e = t.getNode(); 5008 s = t.getSel(); 5009 5010 if (!s) 5011 return null; 5012 5013 // Image selection 5014 if (e && e.nodeName == 'IMG') { 5015 return { 5016 scrollX : sx, 5017 scrollY : sy 5018 }; 5019 } 5020 5021 // Text selection 5022 5023 function getPos(r, sn, en) { 5024 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}; 5025 5026 while ((n = w.nextNode()) != null) { 5027 if (n == sn) 5028 d.start = p; 5029 5030 if (n == en) { 5031 d.end = p; 5032 return d; 5033 } 5034 5035 p += trimNl(n.nodeValue || '').length; 5036 } 5037 5038 return null; 5039 }; 5040 5041 // Caret or selection 5042 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) { 5043 e = getPos(ro, s.anchorNode, s.focusNode); 5044 5045 if (!e) 5046 return {scrollX : sx, scrollY : sy}; 5047 5048 // Count whitespace before 5049 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); 5050 5051 return { 5052 start : Math.max(e.start + s.anchorOffset - wb, 0), 5053 end : Math.max(e.end + s.focusOffset - wb, 0), 5054 scrollX : sx, 5055 scrollY : sy, 5056 beg : s.anchorOffset - wb == 0 5057 }; 5058 } else { 5059 e = getPos(ro, r.startContainer, r.endContainer); 5060 5061 // Count whitespace before start and end container 5062 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); 5063 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;}); 5064 5065 if (!e) 5066 return {scrollX : sx, scrollY : sy}; 5067 5068 return { 5069 start : Math.max(e.start + r.startOffset - wb, 0), 5070 end : Math.max(e.end + r.endOffset - wa, 0), 5071 scrollX : sx, 5072 scrollY : sy, 5073 beg : r.startOffset - wb == 0 5074 }; 5075 } 5076 }, 5077 5078 moveToBookmark : function(b) { 5079 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv; 5080 5081 function getPos(r, sp, ep) { 5082 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb; 5083 5084 while ((n = w.nextNode()) != null) { 5085 wa = wb = 0; 5086 5087 nv = n.nodeValue || ''; 5088 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;}); 5089 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;}); 5090 5091 nvl = trimNl(nv).length; 5092 p += nvl; 5093 5094 if (p >= sp && !d.startNode) { 5095 o = sp - (p - nvl); 5096 5097 // Fix for odd quirk in FF 5098 if (b.beg && o >= nvl) 5099 continue; 5100 5101 d.startNode = n; 5102 d.startOffset = o + wb; 5103 } 5104 5105 if (p >= ep) { 5106 d.endNode = n; 5107 d.endOffset = ep - (p - nvl) + wb; 5108 return d; 5109 } 5110 } 5111 5112 return null; 5113 }; 5114 5115 if (!b) 5116 return false; 5117 5118 t.win.scrollTo(b.scrollX, b.scrollY); 5119 5120 // Handle explorer 5121 if (isIE) { 5122 // Handle simple 5123 if (r = b.rng) { 5124 try { 5125 r.select(); 5126 } catch (ex) { 5127 // Ignore 5128 } 5129 5130 return true; 5131 } 5132 5133 t.win.focus(); 5134 5135 // Handle control bookmark 5136 if (b.tag) { 5137 r = ro.createControlRange(); 5138 5139 each(t.dom.select(b.tag), function(n, i) { 5140 if (i == b.index) 5141 r.addElement(n); 5142 }); 5143 } else { 5144 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs 5145 try { 5146 // Incorrect bookmark 5147 if (b.start < 0) 5148 return true; 5149 5150 r = s.createRange(); 5151 r.moveToElementText(ro); 5152 r.collapse(true); 5153 r.moveStart('character', b.start); 5154 r.moveEnd('character', b.length); 5155 } catch (ex2) { 5156 return true; 5157 } 5158 } 5159 5160 try { 5161 r.select(); 5162 } catch (ex) { 5163 // Needed for some odd IE bug #1843306 5164 } 5165 5166 return true; 5167 } 5168 5169 // Handle W3C 5170 if (!s) 5171 return false; 5172 5173 // Handle simple 5174 if (b.rng) { 5175 s.removeAllRanges(); 5176 s.addRange(b.rng); 5177 } else { 5178 if (is(b.start) && is(b.end)) { 5179 try { 5180 sd = getPos(ro, b.start, b.end); 5181 5182 if (sd) { 5183 r = t.dom.doc.createRange(); 5184 r.setStart(sd.startNode, sd.startOffset); 5185 r.setEnd(sd.endNode, sd.endOffset); 5186 s.removeAllRanges(); 5187 s.addRange(r); 5188 } 5189 5190 if (!tinymce.isOpera) 5191 t.win.focus(); 5192 } catch (ex) { 5193 // Ignore 5194 } 5195 } 5196 } 5197 }, 5198 5199 select : function(n, c) { 5200 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document; 5201 5202 function find(n, start) { 5203 var walker, o; 5204 5205 if (n) { 5206 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); 5207 5208 // Find first/last non empty text node 5209 while (n = walker.nextNode()) { 5210 o = n; 5211 5212 if (tinymce.trim(n.nodeValue).length != 0) { 5213 if (start) 5214 return n; 5215 else 5216 o = n; 5217 } 5218 } 5219 } 5220 5221 return o; 5222 }; 5223 5224 if (isIE) { 5225 try { 5226 b = d.body; 5227 5228 if (/^(IMG|TABLE)$/.test(n.nodeName)) { 5229 r = b.createControlRange(); 5230 r.addElement(n); 5231 } else { 5232 r = b.createTextRange(); 5233 r.moveToElementText(n); 5234 } 5235 5236 r.select(); 5237 } catch (ex) { 5238 // Throws illigal agrument in IE some times 5239 } 5240 } else { 5241 if (c) { 5242 fn = find(n, 1) || t.dom.select('br:first', n)[0]; 5243 ln = find(n, 0) || t.dom.select('br:last', n)[0]; 5244 5245 if (fn && ln) { 5246 r = d.createRange(); 5247 5248 if (fn.nodeName == 'BR') 5249 r.setStartBefore(fn); 5250 else 5251 r.setStart(fn, 0); 5252 5253 if (ln.nodeName == 'BR') 5254 r.setEndBefore(ln); 5255 else 5256 r.setEnd(ln, ln.nodeValue.length); 5257 } else 5258 r.selectNode(n); 5259 } else 5260 r.selectNode(n); 5261 5262 t.setRng(r); 5263 } 5264 5265 return n; 5266 }, 5267 5268 isCollapsed : function() { 5269 var t = this, r = t.getRng(), s = t.getSel(); 5270 5271 if (!r || r.item) 5272 return false; 5273 5274 return !s || r.boundingWidth == 0 || r.collapsed; 5275 }, 5276 5277 collapse : function(b) { 5278 var t = this, r = t.getRng(), n; 5279 5280 // Control range on IE 5281 if (r.item) { 5282 n = r.item(0); 5283 r = this.win.document.body.createTextRange(); 5284 r.moveToElementText(n); 5285 } 5286 5287 r.collapse(!!b); 5288 t.setRng(r); 5289 }, 5290 5291 getSel : function() { 5292 var t = this, w = this.win; 5293 5294 return w.getSelection ? w.getSelection() : w.document.selection; 5295 }, 5296 5297 getRng : function(w3c) { 5298 var t = this, s, r; 5299 5300 // Found tridentSel object then we need to use that one 5301 if (w3c && t.tridentSel) 5302 return t.tridentSel.getRangeAt(0); 5303 5304 try { 5305 if (s = t.getSel()) 5306 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange()); 5307 } catch (ex) { 5308 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe 5309 } 5310 5311 // No range found then create an empty one 5312 // This can occur when the editor is placed in a hidden container element on Gecko 5313 // Or on IE when there was an exception 5314 if (!r) 5315 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange(); 5316 5317 return r; 5318 }, 5319 5320 setRng : function(r) { 5321 var s, t = this; 5322 5323 if (!t.tridentSel) { 5324 s = t.getSel(); 5325 5326 if (s) { 5327 s.removeAllRanges(); 5328 s.addRange(r); 5329 } 5330 } else { 5331 // Is W3C Range 5332 if (r.cloneRange) { 5333 t.tridentSel.addRange(r); 5334 return; 5335 } 5336 5337 // Is IE specific range 5338 try { 5339 r.select(); 5340 } catch (ex) { 5341 // Needed for some odd IE bug #1843306 5342 } 5343 } 5344 }, 5345 5346 setNode : function(n) { 5347 var t = this; 5348 5349 t.setContent(t.dom.getOuterHTML(n)); 5350 5351 return n; 5352 }, 5353 5354 getNode : function() { 5355 var t = this, r = t.getRng(), s = t.getSel(), e; 5356 5357 if (!isIE) { 5358 // Range maybe lost after the editor is made visible again 5359 if (!r) 5360 return t.dom.getRoot(); 5361 5362 e = r.commonAncestorContainer; 5363 5364 // Handle selection a image or other control like element such as anchors 5365 if (!r.collapsed) { 5366 // If the anchor node is a element instead of a text node then return this element 5367 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) 5368 return s.anchorNode.childNodes[s.anchorOffset]; 5369 5370 if (r.startContainer == r.endContainer) { 5371 if (r.startOffset - r.endOffset < 2) { 5372 if (r.startContainer.hasChildNodes()) 5373 e = r.startContainer.childNodes[r.startOffset]; 5374 } 5375 } 5376 } 5377 5378 return t.dom.getParent(e, '*'); 5379 } 5380 5381 return r.item ? r.item(0) : r.parentElement(); 5382 }, 5383 5384 getSelectedBlocks : function(st, en) { 5385 var t = this, dom = t.dom, sb, eb, n, bl = []; 5386 5387 sb = dom.getParent(st || t.getStart(), dom.isBlock); 5388 eb = dom.getParent(en || t.getEnd(), dom.isBlock); 5389 5390 if (sb) 5391 bl.push(sb); 5392 5393 if (sb && eb && sb != eb) { 5394 n = sb; 5395 5396 while ((n = n.nextSibling) && n != eb) { 5397 if (dom.isBlock(n)) 5398 bl.push(n); 5399 } 5400 } 5401 5402 if (eb && sb != eb) 5403 bl.push(eb); 5404 5405 return bl; 5406 }, 5407 5408 destroy : function(s) { 5409 var t = this; 5410 5411 t.win = null; 5412 5413 if (t.tridentSel) 5414 t.tridentSel.destroy(); 5415 5416 // Manual destroy then remove unload handler 5417 if (!s) 5418 tinymce.removeUnload(t.destroy); 5419 } 5420 }); 5421 })(tinymce); 5422 (function(tinymce) { 5423 tinymce.create('tinymce.dom.XMLWriter', { 5424 node : null, 5425 5426 XMLWriter : function(s) { 5427 // Get XML document 5428 function getXML() { 5429 var i = document.implementation; 5430 5431 if (!i || !i.createDocument) { 5432 // Try IE objects 5433 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {} 5434 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {} 5435 } else 5436 return i.createDocument('', '', null); 5437 }; 5438 5439 this.doc = getXML(); 5440 5441 // Since Opera and WebKit doesn't escape > into > we need to do it our self to normalize the output for all browsers 5442 this.valid = tinymce.isOpera || tinymce.isWebKit; 5443 5444 this.reset(); 5445 }, 5446 5447 reset : function() { 5448 var t = this, d = t.doc; 5449 5450 if (d.firstChild) 5451 d.removeChild(d.firstChild); 5452 5453 t.node = d.appendChild(d.createElement("html")); 5454 }, 5455 5456 writeStartElement : function(n) { 5457 var t = this; 5458 5459 t.node = t.node.appendChild(t.doc.createElement(n)); 5460 }, 5461 5462 writeAttribute : function(n, v) { 5463 if (this.valid) 5464 v = v.replace(/>/g, '%MCGT%'); 5465 5466 this.node.setAttribute(n, v); 5467 }, 5468 5469 writeEndElement : function() { 5470 this.node = this.node.parentNode; 5471 }, 5472 5473 writeFullEndElement : function() { 5474 var t = this, n = t.node; 5475 5476 n.appendChild(t.doc.createTextNode("")); 5477 t.node = n.parentNode; 5478 }, 5479 5480 writeText : function(v) { 5481 if (this.valid) 5482 v = v.replace(/>/g, '%MCGT%'); 5483 5484 this.node.appendChild(this.doc.createTextNode(v)); 5485 }, 5486 5487 writeCDATA : function(v) { 5488 this.node.appendChild(this.doc.createCDATASection(v)); 5489 }, 5490 5491 writeComment : function(v) { 5492 // Fix for bug #2035694 5493 if (tinymce.isIE) 5494 v = v.replace(/^\-|\-$/g, ' '); 5495 5496 this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' '))); 5497 }, 5498 5499 getContent : function() { 5500 var h; 5501 5502 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc); 5503 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, ''); 5504 h = h.replace(/ ?\/>/g, ' />'); 5505 5506 if (this.valid) 5507 h = h.replace(/\%MCGT%/g, '>'); 5508 5509 return h; 5510 } 5511 }); 5512 })(tinymce); 5513 (function(tinymce) { 5514 tinymce.create('tinymce.dom.StringWriter', { 5515 str : null, 5516 tags : null, 5517 count : 0, 5518 settings : null, 5519 indent : null, 5520 5521 StringWriter : function(s) { 5522 this.settings = tinymce.extend({ 5523 indent_char : ' ', 5524 indentation : 0 5525 }, s); 5526 5527 this.reset(); 5528 }, 5529 5530 reset : function() { 5531 this.indent = ''; 5532 this.str = ""; 5533 this.tags = []; 5534 this.count = 0; 5535 }, 5536 5537 writeStartElement : function(n) { 5538 this._writeAttributesEnd(); 5539 this.writeRaw('<' + n); 5540 this.tags.push(n); 5541 this.inAttr = true; 5542 this.count++; 5543 this.elementCount = this.count; 5544 }, 5545 5546 writeAttribute : function(n, v) { 5547 var t = this; 5548 5549 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"'); 5550 }, 5551 5552 writeEndElement : function() { 5553 var n; 5554 5555 if (this.tags.length > 0) { 5556 n = this.tags.pop(); 5557 5558 if (this._writeAttributesEnd(1)) 5559 this.writeRaw('</' + n + '>'); 5560 5561 if (this.settings.indentation > 0) 5562 this.writeRaw('\n'); 5563 } 5564 }, 5565 5566 writeFullEndElement : function() { 5567 if (this.tags.length > 0) { 5568 this._writeAttributesEnd(); 5569 this.writeRaw('</' + this.tags.pop() + '>'); 5570 5571 if (this.settings.indentation > 0) 5572 this.writeRaw('\n'); 5573 } 5574 }, 5575 5576 writeText : function(v) { 5577 this._writeAttributesEnd(); 5578 this.writeRaw(this.encode(v)); 5579 this.count++; 5580 }, 5581 5582 writeCDATA : function(v) { 5583 this._writeAttributesEnd(); 5584 this.writeRaw('<![CDATA[' + v + ']]>'); 5585 this.count++; 5586 }, 5587 5588 writeComment : function(v) { 5589 this._writeAttributesEnd(); 5590 this.writeRaw('<!-- ' + v + '-->'); 5591 this.count++; 5592 }, 5593 5594 writeRaw : function(v) { 5595 this.str += v; 5596 }, 5597 5598 encode : function(s) { 5599 return s.replace(/[<>&"]/g, function(v) { 5600 switch (v) { 5601 case '<': 5602 return '<'; 5603 5604 case '>': 5605 return '>'; 5606 5607 case '&': 5608 return '&'; 5609 5610 case '"': 5611 return '"'; 5612 } 5613 5614 return v; 5615 }); 5616 }, 5617 5618 getContent : function() { 5619 return this.str; 5620 }, 5621 5622 _writeAttributesEnd : function(s) { 5623 if (!this.inAttr) 5624 return; 5625 5626 this.inAttr = false; 5627 5628 if (s && this.elementCount == this.count) { 5629 this.writeRaw(' />'); 5630 return false; 5631 } 5632 5633 this.writeRaw('>'); 5634 5635 return true; 5636 } 5637 }); 5638 })(tinymce); 5639 (function(tinymce) { 5640 // Shorten names 5641 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko; 5642 5643 function wildcardToRE(s) { 5644 return s.replace(/([?+*])/g, '.$1'); 5645 }; 5646 5647 tinymce.create('tinymce.dom.Serializer', { 5648 Serializer : function(s) { 5649 var t = this; 5650 5651 t.key = 0; 5652 t.onPreProcess = new Dispatcher(t); 5653 t.onPostProcess = new Dispatcher(t); 5654 5655 try { 5656 t.writer = new tinymce.dom.XMLWriter(); 5657 } catch (ex) { 5658 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter 5659 t.writer = new tinymce.dom.StringWriter(); 5660 } 5661 5662 // Default settings 5663 t.settings = s = extend({ 5664 dom : tinymce.DOM, 5665 valid_nodes : 0, 5666 node_filter : 0, 5667 attr_filter : 0, 5668 invalid_attrs : /^(mce_|_moz_|sizset|sizcache)/, 5669 closed : /^(br|hr|input|meta|img|link|param|area)$/, 5670 entity_encoding : 'named', 5671 entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro', 5672 bool_attrs : /(checked|disabled|readonly|selected|nowrap)/, 5673 valid_elements : '*[*]', 5674 extended_valid_elements : 0, 5675 valid_child_elements : 0, 5676 invalid_elements : 0, 5677 fix_table_elements : 1, 5678 fix_list_elements : true, 5679 fix_content_duplication : true, 5680 convert_fonts_to_spans : false, 5681 font_size_classes : 0, 5682 font_size_style_values : 0, 5683 apply_source_formatting : 0, 5684 indent_mode : 'simple', 5685 indent_char : '\t', 5686 indent_levels : 1, 5687 remove_linebreaks : 1, 5688 remove_redundant_brs : 1, 5689 element_format : 'xhtml' 5690 }, s); 5691 5692 t.dom = s.dom; 5693 5694 if (s.remove_redundant_brs) { 5695 t.onPostProcess.add(function(se, o) { 5696 // Remove single BR at end of block elements since they get rendered 5697 o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) { 5698 // Check if it's a single element 5699 if (/^<br \/>\s*<\//.test(a)) 5700 return '</' + c + '>'; 5701 5702 return a; 5703 }); 5704 }); 5705 } 5706 5707 // Remove XHTML element endings i.e. produce crap :) XHTML is better 5708 if (s.element_format == 'html') { 5709 t.onPostProcess.add(function(se, o) { 5710 o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>'); 5711 }); 5712 } 5713 5714 if (s.fix_list_elements) { 5715 t.onPreProcess.add(function(se, o) { 5716 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np; 5717 5718 function prevNode(e, n) { 5719 var a = n.split(','), i; 5720 5721 while ((e = e.previousSibling) != null) { 5722 for (i=0; i<a.length; i++) { 5723 if (e.nodeName == a[i]) 5724 return e; 5725 } 5726 } 5727 5728 return null; 5729 }; 5730 5731 for (x=0; x<a.length; x++) { 5732 nl = t.dom.select(a[x], o.node); 5733 5734 for (i=0; i<nl.length; i++) { 5735 n = nl[i]; 5736 p = n.parentNode; 5737 5738 if (r.test(p.nodeName)) { 5739 np = prevNode(n, 'LI'); 5740 5741 if (!np) { 5742 np = t.dom.create('li'); 5743 np.innerHTML = ' '; 5744 np.appendChild(n); 5745 p.insertBefore(np, p.firstChild); 5746 } else 5747 np.appendChild(n); 5748 } 5749 } 5750 } 5751 }); 5752 } 5753 5754 if (s.fix_table_elements) { 5755 t.onPreProcess.add(function(se, o) { 5756 each(t.dom.select('p table', o.node).reverse(), function(n) { 5757 var parent = t.dom.getParent(n.parentNode, 'table,p'); 5758 5759 if (parent.nodeName != 'TABLE') { 5760 // IE has a odd bug where tables inside paragraphs sometimes gets wrapped in a BODY and documentFragement element 5761 // This hack seems to resolve that issue. This will normally not happed since your contents should be valid in the first place 5762 if (isIE) 5763 t.dom.setOuterHTML(n, n.outerHTML); 5764 5765 t.dom.split(parent, n); 5766 } 5767 }); 5768 }); 5769 } 5770 }, 5771 5772 setEntities : function(s) { 5773 var t = this, a, i, l = {}, re = '', v; 5774 5775 // No need to setup more than once 5776 if (t.entityLookup) 5777 return; 5778 5779 // Build regex and lookup array 5780 a = s.split(','); 5781 for (i = 0; i < a.length; i += 2) { 5782 v = a[i]; 5783 5784 // Don't add default & " etc. 5785 if (v == 34 || v == 38 || v == 60 || v == 62) 5786 continue; 5787 5788 l[String.fromCharCode(a[i])] = a[i + 1]; 5789 5790 v = parseInt(a[i]).toString(16); 5791 re += '\\u' + '0000'.substring(v.length) + v; 5792 } 5793 5794 if (!re) { 5795 t.settings.entity_encoding = 'raw'; 5796 return; 5797 } 5798 5799 t.entitiesRE = new RegExp('[' + re + ']', 'g'); 5800 t.entityLookup = l; 5801 }, 5802 5803 setValidChildRules : function(s) { 5804 this.childRules = null; 5805 this.addValidChildRules(s); 5806 }, 5807 5808 addValidChildRules : function(s) { 5809 var t = this, inst, intr, bloc; 5810 5811 if (!s) 5812 return; 5813 5814 inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment'; 5815 intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment'; 5816 bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP'; 5817 5818 each(s.split(','), function(s) { 5819 var p = s.split(/\[|\]/), re; 5820 5821 s = ''; 5822 each(p[1].split('|'), function(v) { 5823 if (s) 5824 s += '|'; 5825 5826 switch (v) { 5827 case '%itrans': 5828 v = intr; 5829 break; 5830 5831 case '%itrans_na': 5832 v = intr.substring(2); 5833 break; 5834 5835 case '%istrict': 5836 v = inst; 5837 break; 5838 5839 case '%istrict_na': 5840 v = inst.substring(2); 5841 break; 5842 5843 case '%btrans': 5844 v = bloc; 5845 break; 5846 5847 case '%bstrict': 5848 v = bloc; 5849 break; 5850 } 5851 5852 s += v; 5853 }); 5854 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); 5855 5856 each(p[0].split('/'), function(s) { 5857 t.childRules = t.childRules || {}; 5858 t.childRules[s] = re; 5859 }); 5860 }); 5861 5862 // Build regex 5863 s = ''; 5864 each(t.childRules, function(v, k) { 5865 if (s) 5866 s += '|'; 5867 5868 s += k; 5869 }); 5870 5871 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); 5872 5873 /*console.debug(t.parentElementsRE.toString()); 5874 each(t.childRules, function(v) { 5875 console.debug(v.toString()); 5876 });*/ 5877 }, 5878 5879 setRules : function(s) { 5880 var t = this; 5881 5882 t._setup(); 5883 t.rules = {}; 5884 t.wildRules = []; 5885 t.validElements = {}; 5886 5887 return t.addRules(s); 5888 }, 5889 5890 addRules : function(s) { 5891 var t = this, dr; 5892 5893 if (!s) 5894 return; 5895 5896 t._setup(); 5897 5898 each(s.split(','), function(s) { 5899 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = []; 5900 5901 // Extend with default rules 5902 if (dr) 5903 at = tinymce.extend([], dr.attribs); 5904 5905 // Parse attributes 5906 if (p.length > 1) { 5907 each(p[1].split('|'), function(s) { 5908 var ar = {}, i; 5909 5910 at = at || []; 5911 5912 // Parse attribute rule 5913 s = s.replace(/::/g, '~'); 5914 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s); 5915 s[2] = s[2].replace(/~/g, ':'); 5916 5917 // Add required attributes 5918 if (s[1] == '!') { 5919 ra = ra || []; 5920 ra.push(s[2]); 5921 } 5922 5923 // Remove inherited attributes 5924 if (s[1] == '-') { 5925 for (i = 0; i <at.length; i++) { 5926 if (at[i].name == s[2]) { 5927 at.splice(i, 1); 5928 return; 5929 } 5930 } 5931 } 5932 5933 switch (s[3]) { 5934 // Add default attrib values 5935 case '=': 5936 ar.defaultVal = s[4] || ''; 5937 break; 5938 5939 // Add forced attrib values 5940 case ':': 5941 ar.forcedVal = s[4]; 5942 break; 5943 5944 // Add validation values 5945 case '<': 5946 ar.validVals = s[4].split('?'); 5947 break; 5948 } 5949 5950 if (/[*.?]/.test(s[2])) { 5951 wat = wat || []; 5952 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$'); 5953 wat.push(ar); 5954 } else { 5955 ar.name = s[2]; 5956 at.push(ar); 5957 } 5958 5959 va.push(s[2]); 5960 }); 5961 } 5962 5963 // Handle element names 5964 each(tn, function(s, i) { 5965 var pr = s.charAt(0), x = 1, ru = {}; 5966 5967 // Extend with default rule data 5968 if (dr) { 5969 if (dr.noEmpty) 5970 ru.noEmpty = dr.noEmpty; 5971 5972 if (dr.fullEnd) 5973 ru.fullEnd = dr.fullEnd; 5974 5975 if (dr.padd) 5976 ru.padd = dr.padd; 5977 } 5978 5979 // Handle prefixes 5980 switch (pr) { 5981 case '-': 5982 ru.noEmpty = true; 5983 break; 5984 5985 case '+': 5986 ru.fullEnd = true; 5987 break; 5988 5989 case '#': 5990 ru.padd = true; 5991 break; 5992 5993 default: 5994 x = 0; 5995 } 5996 5997 tn[i] = s = s.substring(x); 5998 t.validElements[s] = 1; 5999 6000 // Add element name or element regex 6001 if (/[*.?]/.test(tn[0])) { 6002 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$'); 6003 t.wildRules = t.wildRules || {}; 6004 t.wildRules.push(ru); 6005 } else { 6006 ru.name = tn[0]; 6007 6008 // Store away default rule 6009 if (tn[0] == '@') 6010 dr = ru; 6011 6012 t.rules[s] = ru; 6013 } 6014 6015 ru.attribs = at; 6016 6017 if (ra) 6018 ru.requiredAttribs = ra; 6019 6020 if (wat) { 6021 // Build valid attributes regexp 6022 s = ''; 6023 each(va, function(v) { 6024 if (s) 6025 s += '|'; 6026 6027 s += '(' + wildcardToRE(v) + ')'; 6028 }); 6029 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$'); 6030 ru.wildAttribs = wat; 6031 } 6032 }); 6033 }); 6034 6035 // Build valid elements regexp 6036 s = ''; 6037 each(t.validElements, function(v, k) { 6038 if (s) 6039 s += '|'; 6040 6041 if (k != '@') 6042 s += k; 6043 }); 6044 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$'); 6045 6046 //console.debug(t.validElementsRE.toString()); 6047 //console.dir(t.rules); 6048 //console.dir(t.wildRules); 6049 }, 6050 6051 findRule : function(n) { 6052 var t = this, rl = t.rules, i, r; 6053 6054 t._setup(); 6055 6056 // Exact match 6057 r = rl[n]; 6058 if (r) 6059 return r; 6060 6061 // Try wildcards 6062 rl = t.wildRules; 6063 for (i = 0; i < rl.length; i++) { 6064 if (rl[i].nameRE.test(n)) 6065 return rl[i]; 6066 } 6067 6068 return null; 6069 }, 6070 6071 findAttribRule : function(ru, n) { 6072 var i, wa = ru.wildAttribs; 6073 6074 for (i = 0; i < wa.length; i++) { 6075 if (wa[i].nameRE.test(n)) 6076 return wa[i]; 6077 } 6078 6079 return null; 6080 }, 6081 6082 serialize : function(n, o) { 6083 var h, t = this, doc; 6084 6085 t._setup(); 6086 o = o || {}; 6087 o.format = o.format || 'html'; 6088 n = n.cloneNode(true); 6089 t.processObj = o; 6090 6091 // Nodes needs to be attached to something in WebKit due to a bug https://bugs.webkit.org/show_bug.cgi?id=25571 6092 if (tinymce.isWebKit) { 6093 doc = n.ownerDocument.implementation.createHTMLDocument(""); 6094 doc.body.appendChild(doc.importNode(n)); 6095 } 6096 6097 t.key = '' + (parseInt(t.key) + 1); 6098 6099 // Pre process 6100 if (!o.no_events) { 6101 o.node = n; 6102 t.onPreProcess.dispatch(t, o); 6103 } 6104 6105 // Serialize HTML DOM into a string 6106 t.writer.reset(); 6107 t._serializeNode(n, o.getInner); 6108 6109 // Post process 6110 o.content = t.writer.getContent(); 6111 6112 if (!o.no_events) 6113 t.onPostProcess.dispatch(t, o); 6114 6115 t._postProcess(o); 6116 o.node = null; 6117 6118 return tinymce.trim(o.content); 6119 }, 6120 6121 // Internal functions 6122 6123 _postProcess : function(o) { 6124 var t = this, s = t.settings, h = o.content, sc = [], p; 6125 6126 if (o.format == 'html') { 6127 // Protect some elements 6128 p = t._protect({ 6129 content : h, 6130 patterns : [ 6131 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g}, 6132 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g}, 6133 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g}, 6134 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1}, 6135 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g} 6136 ] 6137 }); 6138 6139 h = p.content; 6140 6141 // Entity encode 6142 if (s.entity_encoding !== 'raw') 6143 h = t._encode(h); 6144 6145 // Use BR instead of padded P elements inside editor and use <p> </p> outside editor 6146 /* if (o.set) 6147 h = h.replace(/<p>\s+( | |\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>'); 6148 else 6149 h = h.replace(/<p>\s+( | |\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/ 6150 6151 // Since Gecko and Safari keeps whitespace in the DOM we need to 6152 // remove it inorder to match other browsers. But I think Gecko and Safari is right. 6153 // This process is only done when getting contents out from the editor. 6154 if (!o.set) { 6155 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char 6156 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1> </p>' : '<p$1> </p>'); 6157 6158 if (s.remove_linebreaks) { 6159 h = h.replace(/\r?\n|\r/g, ' '); 6160 h = h.replace(/(<[^>]+>)\s+/g, '$1 '); 6161 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1'); 6162 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start 6163 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start 6164 h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>'); // Trim block end 6165 } 6166 6167 // Simple indentation 6168 if (s.apply_source_formatting && s.indent_mode == 'simple') { 6169 // Add line breaks before and after block elements 6170 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n'); 6171 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>'); 6172 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n'); 6173 h = h.replace(/\n\n/g, '\n'); 6174 } 6175 } 6176 6177 h = t._unprotect(h, p); 6178 6179 // Restore CDATA sections 6180 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>'); 6181 6182 // Restore the \u00a0 character if raw mode is enabled 6183 if (s.entity_encoding == 'raw') 6184 h = h.replace(/<p> <\/p>|<p([^>]+)> <\/p>/g, '<p$1>\u00a0</p>'); 6185 6186 // Restore noscript elements 6187 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) { 6188 return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>'; 6189 }); 6190 } 6191 6192 o.content = h; 6193 }, 6194 6195 _serializeNode : function(n, inn) { 6196 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed; 6197 6198 if (!s.node_filter || s.node_filter(n)) { 6199 switch (n.nodeType) { 6200 case 1: // Element 6201 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus')) 6202 return; 6203 6204 iv = false; 6205 hc = n.hasChildNodes(); 6206 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase(); 6207 6208 // Add correct prefix on IE 6209 if (isIE) { 6210 if (n.scopeName !== 'HTML' && n.scopeName !== 'html') 6211 nn = n.scopeName + ':' + nn; 6212 } 6213 6214 // Remove mce prefix on IE needed for the abbr element 6215 if (nn.indexOf('mce:') === 0) 6216 nn = nn.substring(4); 6217 6218 // Check if valid 6219 if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) { 6220 iv = true; 6221 break; 6222 } 6223 6224 if (isIE) { 6225 // Fix IE content duplication (DOM can have multiple copies of the same node) 6226 if (s.fix_content_duplication) { 6227 if (n.mce_serialized == t.key) 6228 return; 6229 6230 n.mce_serialized = t.key; 6231 } 6232 6233 // IE sometimes adds a / infront of the node name 6234 if (nn.charAt(0) == '/') 6235 nn = nn.substring(1); 6236 } else if (isGecko) { 6237 // Ignore br elements 6238 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz') 6239 return; 6240 } 6241 6242 // Check if valid child 6243 if (t.childRules) { 6244 if (t.parentElementsRE.test(t.elementName)) { 6245 if (!t.childRules[t.elementName].test(nn)) { 6246 iv = true; 6247 break; 6248 } 6249 } 6250 6251 t.elementName = nn; 6252 } 6253 6254 ru = t.findRule(nn); 6255 nn = ru.name || nn; 6256 closed = s.closed.test(nn); 6257 6258 // Skip empty nodes or empty node name in IE 6259 if ((!hc && ru.noEmpty) || (isIE && !nn)) { 6260 iv = true; 6261 break; 6262 } 6263 6264 // Check required 6265 if (ru.requiredAttribs) { 6266 a = ru.requiredAttribs; 6267 6268 for (i = a.length - 1; i >= 0; i--) { 6269 if (this.dom.getAttrib(n, a[i]) !== '') 6270 break; 6271 } 6272 6273 // None of the required was there 6274 if (i == -1) { 6275 iv = true; 6276 break; 6277 } 6278 } 6279 6280 w.writeStartElement(nn); 6281 6282 // Add ordered attributes 6283 if (ru.attribs) { 6284 for (i=0, at = ru.attribs, l = at.length; i<l; i++) { 6285 a = at[i]; 6286 v = t._getAttrib(n, a); 6287 6288 if (v !== null) 6289 w.writeAttribute(a.name, v); 6290 } 6291 } 6292 6293 // Add wild attributes 6294 if (ru.validAttribsRE) { 6295 at = t.dom.getAttribs(n); 6296 for (i=at.length-1; i>-1; i--) { 6297 no = at[i]; 6298 6299 if (no.specified) { 6300 a = no.nodeName.toLowerCase(); 6301 6302 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a)) 6303 continue; 6304 6305 ar = t.findAttribRule(ru, a); 6306 v = t._getAttrib(n, ar, a); 6307 6308 if (v !== null) 6309 w.writeAttribute(a, v); 6310 } 6311 } 6312 } 6313 6314 // Write text from script 6315 if (nn === 'script' && tinymce.trim(n.innerHTML)) { 6316 w.writeText('// '); // Padd it with a comment so it will parse on older browsers 6317 w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures 6318 hc = false; 6319 break; 6320 } 6321 6322 // Padd empty nodes with a 6323 if (ru.padd) { 6324 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug 6325 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) { 6326 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus')) 6327 w.writeText('\u00a0'); 6328 } else if (!hc) 6329 w.writeText('\u00a0'); // No children then padd it 6330 } 6331 6332 break; 6333 6334 case 3: // Text 6335 // Check if valid child 6336 if (t.childRules && t.parentElementsRE.test(t.elementName)) { 6337 if (!t.childRules[t.elementName].test(n.nodeName)) 6338 return; 6339 } 6340 6341 return w.writeText(n.nodeValue); 6342 6343 case 4: // CDATA 6344 return w.writeCDATA(n.nodeValue); 6345 6346 case 8: // Comment 6347 return w.writeComment(n.nodeValue); 6348 } 6349 } else if (n.nodeType == 1) 6350 hc = n.hasChildNodes(); 6351 6352 if (hc && !closed) { 6353 cn = n.firstChild; 6354 6355 while (cn) { 6356 t._serializeNode(cn); 6357 t.elementName = nn; 6358 cn = cn.nextSibling; 6359 } 6360 } 6361 6362 // Write element end 6363 if (!iv) { 6364 if (!closed) 6365 w.writeFullEndElement(); 6366 else 6367 w.writeEndElement(); 6368 } 6369 }, 6370 6371 _protect : function(o) { 6372 var t = this; 6373 6374 o.items = o.items || []; 6375 6376 function enc(s) { 6377 return s.replace(/[\r\n\\]/g, function(c) { 6378 if (c === '\n') 6379 return '\\n'; 6380 else if (c === '\\') 6381 return '\\\\'; 6382 6383 return '\\r'; 6384 }); 6385 }; 6386 6387 function dec(s) { 6388 return s.replace(/\\[\\rn]/g, function(c) { 6389 if (c === '\\n') 6390 return '\n'; 6391 else if (c === '\\\\') 6392 return '\\'; 6393 6394 return '\r'; 6395 }); 6396 }; 6397 6398 each(o.patterns, function(p) { 6399 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) { 6400 b = dec(b); 6401 6402 if (p.encode) 6403 b = t._encode(b); 6404 6405 o.items.push(b); 6406 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c; 6407 })); 6408 }); 6409 6410 return o; 6411 }, 6412 6413 _unprotect : function(h, o) { 6414 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) { 6415 return o.items[parseInt(b)]; 6416 }); 6417 6418 o.items = []; 6419 6420 return h; 6421 }, 6422 6423 _encode : function(h) { 6424 var t = this, s = t.settings, l; 6425 6426 // Entity encode 6427 if (s.entity_encoding !== 'raw') { 6428 if (s.entity_encoding.indexOf('named') != -1) { 6429 t.setEntities(s.entities); 6430 l = t.entityLookup; 6431 6432 h = h.replace(t.entitiesRE, function(a) { 6433 var v; 6434 6435 if (v = l[a]) 6436 a = '&' + v + ';'; 6437 6438 return a; 6439 }); 6440 } 6441 6442 if (s.entity_encoding.indexOf('numeric') != -1) { 6443 h = h.replace(/[\u007E-\uFFFF]/g, function(a) { 6444 return '&#' + a.charCodeAt(0) + ';'; 6445 }); 6446 } 6447 } 6448 6449 return h; 6450 }, 6451 6452 _setup : function() { 6453 var t = this, s = this.settings; 6454 6455 if (t.done) 6456 return; 6457 6458 t.done = 1; 6459 6460 t.setRules(s.valid_elements); 6461 t.addRules(s.extended_valid_elements); 6462 t.addValidChildRules(s.valid_child_elements); 6463 6464 if (s.invalid_elements) 6465 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$'); 6466 6467 if (s.attrib_value_filter) 6468 t.attribValueFilter = s.attribValueFilter; 6469 }, 6470 6471 _getAttrib : function(n, a, na) { 6472 var i, v; 6473 6474 na = na || a.name; 6475 6476 if (a.forcedVal && (v = a.forcedVal)) { 6477 if (v === '{$uid}') 6478 return this.dom.uniqueId(); 6479 6480 return v; 6481 } 6482 6483 v = this.dom.getAttrib(n, na); 6484 6485 // Bool attr 6486 if (this.settings.bool_attrs.test(na) && v) { 6487 v = ('' + v).toLowerCase(); 6488 6489 if (v === 'false' || v === '0') 6490 return null; 6491 6492 v = na; 6493 } 6494 6495 switch (na) { 6496 case 'rowspan': 6497 case 'colspan': 6498 // Whats the point? Remove usless attribute value 6499 if (v == '1') 6500 v = ''; 6501 6502 break; 6503 } 6504 6505 if (this.attribValueFilter) 6506 v = this.attribValueFilter(na, v, n); 6507 6508 if (a.validVals) { 6509 for (i = a.validVals.length - 1; i >= 0; i--) { 6510 if (v == a.validVals[i]) 6511 break; 6512 } 6513 6514 if (i == -1) 6515 return null; 6516 } 6517 6518 if (v === '' && typeof(a.defaultVal) != 'undefined') { 6519 v = a.defaultVal; 6520 6521 if (v === '{$uid}') 6522 return this.dom.uniqueId(); 6523 6524 return v; 6525 } else { 6526 // Remove internal mceItemXX classes when content is extracted from editor 6527 if (na == 'class' && this.processObj.get) 6528 v = v.replace(/\s?mceItem\w+\s?/g, ''); 6529 } 6530 6531 if (v === '') 6532 return null; 6533 6534 6535 return v; 6536 } 6537 }); 6538 })(tinymce); 6539 (function(tinymce) { 6540 var each = tinymce.each, Event = tinymce.dom.Event; 6541 6542 tinymce.create('tinymce.dom.ScriptLoader', { 6543 ScriptLoader : function(s) { 6544 this.settings = s || {}; 6545 this.queue = []; 6546 this.lookup = {}; 6547 }, 6548 6549 isDone : function(u) { 6550 return this.lookup[u] ? this.lookup[u].state == 2 : 0; 6551 }, 6552 6553 markDone : function(u) { 6554 this.lookup[u] = {state : 2, url : u}; 6555 }, 6556 6557 add : function(u, cb, s, pr) { 6558 var t = this, lo = t.lookup, o; 6559 6560 if (o = lo[u]) { 6561 // Is loaded fire callback 6562 if (cb && o.state == 2) 6563 cb.call(s || this); 6564 6565 return o; 6566 } 6567 6568 o = {state : 0, url : u, func : cb, scope : s || this}; 6569 6570 if (pr) 6571 t.queue.unshift(o); 6572 else 6573 t.queue.push(o); 6574 6575 lo[u] = o; 6576 6577 return o; 6578 }, 6579 6580 load : function(u, cb, s) { 6581 var t = this, o; 6582 6583 if (o = t.lookup[u]) { 6584 // Is loaded fire callback 6585 if (cb && o.state == 2) 6586 cb.call(s || t); 6587 6588 return o; 6589 } 6590 6591 function loadScript(u) { 6592 if (Event.domLoaded || t.settings.strict_mode) { 6593 tinymce.util.XHR.send({ 6594 url : tinymce._addVer(u), 6595 error : t.settings.error, 6596 async : false, 6597 success : function(co) { 6598 t.eval(co); 6599 } 6600 }); 6601 } else 6602 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>'); 6603 }; 6604 6605 if (!tinymce.is(u, 'string')) { 6606 each(u, function(u) { 6607 loadScript(u); 6608 }); 6609 6610 if (cb) 6611 cb.call(s || t); 6612 } else { 6613 loadScript(u); 6614 6615 if (cb) 6616 cb.call(s || t); 6617 } 6618 }, 6619 6620 loadQueue : function(cb, s) { 6621 var t = this; 6622 6623 if (!t.queueLoading) { 6624 t.queueLoading = 1; 6625 t.queueCallbacks = []; 6626 6627 t.loadScripts(t.queue, function() { 6628 t.queueLoading = 0; 6629 6630 if (cb) 6631 cb.call(s || t); 6632 6633 each(t.queueCallbacks, function(o) { 6634 o.func.call(o.scope); 6635 }); 6636 }); 6637 } else if (cb) 6638 t.queueCallbacks.push({func : cb, scope : s || t}); 6639 }, 6640 6641 eval : function(co) { 6642 var w = window; 6643 6644 // Evaluate script 6645 if (!w.execScript) { 6646 try { 6647 eval.call(w, co); 6648 } catch (ex) { 6649 eval(co, w); // Firefox 3.0a8 6650 } 6651 } else 6652 w.execScript(co); // IE 6653 }, 6654 6655 loadScripts : function(sc, cb, s) { 6656 var t = this, lo = t.lookup; 6657 6658 function done(o) { 6659 o.state = 2; // Has been loaded 6660 6661 // Run callback 6662 if (o.func) 6663 o.func.call(o.scope || t); 6664 }; 6665 6666 function allDone() { 6667 var l; 6668 6669 // Check if all files are loaded 6670 l = sc.length; 6671 each(sc, function(o) { 6672 o = lo[o.url]; 6673 6674 if (o.state === 2) {// It has finished loading 6675 done(o); 6676 l--; 6677 } else 6678 load(o); 6679 }); 6680 6681 // They are all loaded 6682 if (l === 0 && cb) { 6683 cb.call(s || t); 6684 cb = 0; 6685 } 6686 }; 6687 6688 function load(o) { 6689 if (o.state > 0) 6690 return; 6691 6692 o.state = 1; // Is loading 6693 6694 tinymce.dom.ScriptLoader.loadScript(o.url, function() { 6695 done(o); 6696 allDone(); 6697 }); 6698 6699 /* 6700 tinymce.util.XHR.send({ 6701 url : o.url, 6702 error : t.settings.error, 6703 success : function(co) { 6704 t.eval(co); 6705 done(o); 6706 allDone(); 6707 } 6708 }); 6709 */ 6710 }; 6711 6712 each(sc, function(o) { 6713 var u = o.url; 6714 6715 // Add to queue if needed 6716 if (!lo[u]) { 6717 lo[u] = o; 6718 t.queue.push(o); 6719 } else 6720 o = lo[u]; 6721 6722 // Is already loading or has been loaded 6723 if (o.state > 0) 6724 return; 6725 6726 if (!Event.domLoaded && !t.settings.strict_mode) { 6727 var ix, ol = ''; 6728 6729 // Add onload events 6730 if (cb || o.func) { 6731 o.state = 1; // Is loading 6732 6733 ix = tinymce.dom.ScriptLoader._addOnLoad(function() { 6734 done(o); 6735 allDone(); 6736 }); 6737 6738 if (tinymce.isIE) 6739 ol = ' onreadystatechange="'; 6740 else 6741 ol = ' onload="'; 6742 6743 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"'; 6744 } 6745 6746 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>'); 6747 6748 if (!o.func) 6749 done(o); 6750 } else 6751 load(o); 6752 }); 6753 6754 allDone(); 6755 }, 6756 6757 // Static methods 6758 'static' : { 6759 _addOnLoad : function(f) { 6760 var t = this; 6761 6762 t._funcs = t._funcs || []; 6763 t._funcs.push(f); 6764 6765 return t._funcs.length - 1; 6766 }, 6767 6768 _onLoad : function(e, u, ix) { 6769 if (!tinymce.isIE || e.readyState == 'complete') 6770 this._funcs[ix].call(this); 6771 }, 6772 6773 loadScript : function(u, cb) { 6774 var id = tinymce.DOM.uniqueId(), e; 6775 6776 function done() { 6777 Event.clear(id); 6778 tinymce.DOM.remove(id); 6779 6780 if (cb) { 6781 cb.call(document, u); 6782 cb = 0; 6783 } 6784 }; 6785 6786 if (tinymce.isIE) { 6787 /* Event.add(e, 'readystatechange', function(e) { 6788 if (e.target && e.target.readyState == 'complete') 6789 done(); 6790 });*/ 6791 6792 tinymce.util.XHR.send({ 6793 url : tinymce._addVer(u), 6794 async : false, 6795 success : function(co) { 6796 window.execScript(co); 6797 done(); 6798 } 6799 }); 6800 } else { 6801 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)}); 6802 Event.add(e, 'load', done); 6803 6804 // Check for head or body 6805 (document.getElementsByTagName('head')[0] || document.body).appendChild(e); 6806 } 6807 } 6808 } 6809 }); 6810 6811 // Global script loader 6812 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader(); 6813 })(tinymce); 6814 (function(tinymce) { 6815 // Shorten class names 6816 var DOM = tinymce.DOM, is = tinymce.is; 6817 6818 tinymce.create('tinymce.ui.Control', { 6819 Control : function(id, s) { 6820 this.id = id; 6821 this.settings = s = s || {}; 6822 this.rendered = false; 6823 this.onRender = new tinymce.util.Dispatcher(this); 6824 this.classPrefix = ''; 6825 this.scope = s.scope || this; 6826 this.disabled = 0; 6827 this.active = 0; 6828 }, 6829 6830 setDisabled : function(s) { 6831 var e; 6832 6833 if (s != this.disabled) { 6834 e = DOM.get(this.id); 6835 6836 // Add accessibility title for unavailable actions 6837 if (e && this.settings.unavailable_prefix) { 6838 if (s) { 6839 this.prevTitle = e.title; 6840 e.title = this.settings.unavailable_prefix + ": " + e.title; 6841 } else 6842 e.title = this.prevTitle; 6843 } 6844 6845 this.setState('Disabled', s); 6846 this.setState('Enabled', !s); 6847 this.disabled = s; 6848 } 6849 }, 6850 6851 isDisabled : function() { 6852 return this.disabled; 6853 }, 6854 6855 setActive : function(s) { 6856 if (s != this.active) { 6857 this.setState('Active', s); 6858 this.active = s; 6859 } 6860 }, 6861 6862 isActive : function() { 6863 return this.active; 6864 }, 6865 6866 setState : function(c, s) { 6867 var n = DOM.get(this.id); 6868 6869 c = this.classPrefix + c; 6870 6871 if (s) 6872 DOM.addClass(n, c); 6873 else 6874 DOM.removeClass(n, c); 6875 }, 6876 6877 isRendered : function() { 6878 return this.rendered; 6879 }, 6880 6881 renderHTML : function() { 6882 }, 6883 6884 renderTo : function(n) { 6885 DOM.setHTML(n, this.renderHTML()); 6886 }, 6887 6888 postRender : function() { 6889 var t = this, b; 6890 6891 // Set pending states 6892 if (is(t.disabled)) { 6893 b = t.disabled; 6894 t.disabled = -1; 6895 t.setDisabled(b); 6896 } 6897 6898 if (is(t.active)) { 6899 b = t.active; 6900 t.active = -1; 6901 t.setActive(b); 6902 } 6903 }, 6904 6905 remove : function() { 6906 DOM.remove(this.id); 6907 this.destroy(); 6908 }, 6909 6910 destroy : function() { 6911 tinymce.dom.Event.clear(this.id); 6912 } 6913 }); 6914 })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', { 6915 Container : function(id, s) { 6916 this.parent(id, s); 6917 6918 this.controls = []; 6919 6920 this.lookup = {}; 6921 }, 6922 6923 add : function(c) { 6924 this.lookup[c.id] = c; 6925 this.controls.push(c); 6926 6927 return c; 6928 }, 6929 6930 get : function(n) { 6931 return this.lookup[n]; 6932 } 6933 }); 6934 6935 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { 6936 Separator : function(id, s) { 6937 this.parent(id, s); 6938 this.classPrefix = 'mceSeparator'; 6939 }, 6940 6941 renderHTML : function() { 6942 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix}); 6943 } 6944 }); 6945 (function(tinymce) { 6946 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; 6947 6948 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', { 6949 MenuItem : function(id, s) { 6950 this.parent(id, s); 6951 this.classPrefix = 'mceMenuItem'; 6952 }, 6953 6954 setSelected : function(s) { 6955 this.setState('Selected', s); 6956 this.selected = s; 6957 }, 6958 6959 isSelected : function() { 6960 return this.selected; 6961 }, 6962 6963 postRender : function() { 6964 var t = this; 6965 6966 t.parent(); 6967 6968 // Set pending state 6969 if (is(t.selected)) 6970 t.setSelected(t.selected); 6971 } 6972 }); 6973 })(tinymce); 6974 (function(tinymce) { 6975 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; 6976 6977 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', { 6978 Menu : function(id, s) { 6979 var t = this; 6980 6981 t.parent(id, s); 6982 t.items = {}; 6983 t.collapsed = false; 6984 t.menuCount = 0; 6985 t.onAddItem = new tinymce.util.Dispatcher(this); 6986 }, 6987 6988 expand : function(d) { 6989 var t = this; 6990 6991 if (d) { 6992 walk(t, function(o) { 6993 if (o.expand) 6994 o.expand(); 6995 }, 'items', t); 6996 } 6997 6998 t.collapsed = false; 6999 }, 7000 7001 collapse : function(d) { 7002 var t = this; 7003 7004 if (d) { 7005 walk(t, function(o) { 7006 if (o.collapse) 7007 o.collapse(); 7008 }, 'items', t); 7009 } 7010 7011 t.collapsed = true; 7012 }, 7013 7014 isCollapsed : function() { 7015 return this.collapsed; 7016 }, 7017 7018 add : function(o) { 7019 if (!o.settings) 7020 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o); 7021 7022 this.onAddItem.dispatch(this, o); 7023 7024 return this.items[o.id] = o; 7025 }, 7026 7027 addSeparator : function() { 7028 return this.add({separator : true}); 7029 }, 7030 7031 addMenu : function(o) { 7032 if (!o.collapse) 7033 o = this.createMenu(o); 7034 7035 this.menuCount++; 7036 7037 return this.add(o); 7038 }, 7039 7040 hasMenus : function() { 7041 return this.menuCount !== 0; 7042 }, 7043 7044 remove : function(o) { 7045 delete this.items[o.id]; 7046 }, 7047 7048 removeAll : function() { 7049 var t = this; 7050 7051 walk(t, function(o) { 7052 if (o.removeAll) 7053 o.removeAll(); 7054 else 7055 o.remove(); 7056 7057 o.destroy(); 7058 }, 'items', t); 7059 7060 t.items = {}; 7061 }, 7062 7063 createMenu : function(o) { 7064 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o); 7065 7066 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem); 7067 7068 return m; 7069 } 7070 }); 7071 })(tinymce);(function(tinymce) { 7072 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element; 7073 7074 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', { 7075 DropMenu : function(id, s) { 7076 s = s || {}; 7077 s.container = s.container || DOM.doc.body; 7078 s.offset_x = s.offset_x || 0; 7079 s.offset_y = s.offset_y || 0; 7080 s.vp_offset_x = s.vp_offset_x || 0; 7081 s.vp_offset_y = s.vp_offset_y || 0; 7082 7083 if (is(s.icons) && !s.icons) 7084 s['class'] += ' mceNoIcons'; 7085 7086 this.parent(id, s); 7087 this.onShowMenu = new tinymce.util.Dispatcher(this); 7088 this.onHideMenu = new tinymce.util.Dispatcher(this); 7089 this.classPrefix = 'mceMenu'; 7090 }, 7091 7092 createMenu : function(s) { 7093 var t = this, cs = t.settings, m; 7094 7095 s.container = s.container || cs.container; 7096 s.parent = t; 7097 s.constrain = s.constrain || cs.constrain; 7098 s['class'] = s['class'] || cs['class']; 7099 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x; 7100 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y; 7101 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s); 7102 7103 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem); 7104 7105 return m; 7106 }, 7107 7108 update : function() { 7109 var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th; 7110 7111 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth; 7112 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight; 7113 7114 if (!DOM.boxModel) 7115 t.element.setStyles({width : tw + 2, height : th + 2}); 7116 else 7117 t.element.setStyles({width : tw, height : th}); 7118 7119 if (s.max_width) 7120 DOM.setStyle(co, 'width', tw); 7121 7122 if (s.max_height) { 7123 DOM.setStyle(co, 'height', th); 7124 7125 if (tb.clientHeight < s.max_height) 7126 DOM.setStyle(co, 'overflow', 'hidden'); 7127 } 7128 }, 7129 7130 showMenu : function(x, y, px) { 7131 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix; 7132 7133 t.collapse(1); 7134 7135 if (t.isMenuVisible) 7136 return; 7137 7138 if (!t.rendered) { 7139 co = DOM.add(t.settings.container, t.renderNode()); 7140 7141 each(t.items, function(o) { 7142 o.postRender(); 7143 }); 7144 7145 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container}); 7146 } else 7147 co = DOM.get('menu_' + t.id); 7148 7149 // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug 7150 if (!tinymce.isOpera) 7151 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF}); 7152 7153 DOM.show(co); 7154 t.update(); 7155 7156 x += s.offset_x || 0; 7157 y += s.offset_y || 0; 7158 vp.w -= 4; 7159 vp.h -= 4; 7160 7161 // Move inside viewport if not submenu 7162 if (s.constrain) { 7163 w = co.clientWidth - ot; 7164 h = co.clientHeight - ot; 7165 mx = vp.x + vp.w; 7166 my = vp.y + vp.h; 7167 7168 if ((x + s.vp_offset_x + w) > mx) 7169 x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w); 7170 7171 if ((y + s.vp_offset_y + h) > my) 7172 y = Math.max(0, (my - s.vp_offset_y) - h); 7173 } 7174 7175 DOM.setStyles(co, {left : x , top : y}); 7176 t.element.update(); 7177 7178 t.isMenuVisible = 1; 7179 t.mouseClickFunc = Event.add(co, 'click', function(e) { 7180 var m; 7181 7182 e = e.target; 7183 7184 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) { 7185 m = t.items[e.id]; 7186 7187 if (m.isDisabled()) 7188 return; 7189 7190 dm = t; 7191 7192 while (dm) { 7193 if (dm.hideMenu) 7194 dm.hideMenu(); 7195 7196 dm = dm.settings.parent; 7197 } 7198 7199 if (m.settings.onclick) 7200 m.settings.onclick(e); 7201 7202 return Event.cancel(e); // Cancel to fix onbeforeunload problem 7203 } 7204 }); 7205 7206 if (t.hasMenus()) { 7207 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) { 7208 var m, r, mi; 7209 7210 e = e.target; 7211 if (e && (e = DOM.getParent(e, 'tr'))) { 7212 m = t.items[e.id]; 7213 7214 if (t.lastMenu) 7215 t.lastMenu.collapse(1); 7216 7217 if (m.isDisabled()) 7218 return; 7219 7220 if (e && DOM.hasClass(e, cp + 'ItemSub')) { 7221 //p = DOM.getPos(s.container); 7222 r = DOM.getRect(e); 7223 m.showMenu((r.x + r.w - ot), r.y - ot, r.x); 7224 t.lastMenu = m; 7225 DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive'); 7226 } 7227 } 7228 }); 7229 } 7230 7231 t.onShowMenu.dispatch(t); 7232 7233 if (s.keyboard_focus) { 7234 Event.add(co, 'keydown', t._keyHandler, t); 7235 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link 7236 t._focusIdx = 0; 7237 } 7238 }, 7239 7240 hideMenu : function(c) { 7241 var t = this, co = DOM.get('menu_' + t.id), e; 7242 7243 if (!t.isMenuVisible) 7244 return; 7245 7246 Event.remove(co, 'mouseover', t.mouseOverFunc); 7247 Event.remove(co, 'click', t.mouseClickFunc); 7248 Event.remove(co, 'keydown', t._keyHandler); 7249 DOM.hide(co); 7250 t.isMenuVisible = 0; 7251 7252 if (!c) 7253 t.collapse(1); 7254 7255 if (t.element) 7256 t.element.hide(); 7257 7258 if (e = DOM.get(t.id)) 7259 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive'); 7260 7261 t.onHideMenu.dispatch(t); 7262 }, 7263 7264 add : function(o) { 7265 var t = this, co; 7266 7267 o = t.parent(o); 7268 7269 if (t.isRendered && (co = DOM.get('menu_' + t.id))) 7270 t._add(DOM.select('tbody', co)[0], o); 7271 7272 return o; 7273 }, 7274 7275 collapse : function(d) { 7276 this.parent(d); 7277 this.hideMenu(1); 7278 }, 7279 7280 remove : function(o) { 7281 DOM.remove(o.id); 7282 this.destroy(); 7283 7284 return this.parent(o); 7285 }, 7286 7287 destroy : function() { 7288 var t = this, co = DOM.get('menu_' + t.id); 7289 7290 Event.remove(co, 'mouseover', t.mouseOverFunc); 7291 Event.remove(co, 'click', t.mouseClickFunc); 7292 7293 if (t.element) 7294 t.element.remove(); 7295 7296 DOM.remove(co); 7297 }, 7298 7299 renderNode : function() { 7300 var t = this, s = t.settings, n, tb, co, w; 7301 7302 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'}); 7303 co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')}); 7304 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container}); 7305 7306 if (s.menu_line) 7307 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'}); 7308 7309 // n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'}); 7310 n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0}); 7311 tb = DOM.add(n, 'tbody'); 7312 7313 each(t.items, function(o) { 7314 t._add(tb, o); 7315 }); 7316 7317 t.rendered = true; 7318 7319 return w; 7320 }, 7321 7322 // Internal functions 7323 7324 _keyHandler : function(e) { 7325 var t = this, kc = e.keyCode; 7326 7327 function focus(d) { 7328 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i]; 7329 7330 if (e) { 7331 t._focusIdx = i; 7332 e.focus(); 7333 } 7334 }; 7335 7336 switch (kc) { 7337 case 38: 7338 focus(-1); // Select first link 7339 return; 7340 7341 case 40: 7342 focus(1); 7343 return; 7344 7345 case 13: 7346 return; 7347 7348 case 27: 7349 return this.hideMenu(); 7350 } 7351 }, 7352 7353 _add : function(tb, o) { 7354 var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic; 7355 7356 if (s.separator) { 7357 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'}); 7358 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'}); 7359 7360 if (n = ro.previousSibling) 7361 DOM.addClass(n, 'mceLast'); 7362 7363 return; 7364 } 7365 7366 n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'}); 7367 n = it = DOM.add(n, 'td'); 7368 n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'}); 7369 7370 DOM.addClass(it, s['class']); 7371 // n = DOM.add(n, 'span', {'class' : 'item'}); 7372 7373 ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')}); 7374 7375 if (s.icon_src) 7376 DOM.add(ic, 'img', {src : s.icon_src}); 7377 7378 n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title); 7379 7380 if (o.settings.style) 7381 DOM.setAttrib(n, 'style', o.settings.style); 7382 7383 if (tb.childNodes.length == 1) 7384 DOM.addClass(ro, 'mceFirst'); 7385 7386 if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator')) 7387 DOM.addClass(ro, 'mceFirst'); 7388 7389 if (o.collapse) 7390 DOM.addClass(ro, cp + 'ItemSub'); 7391 7392 if (n = ro.previousSibling) 7393 DOM.removeClass(n, 'mceLast'); 7394 7395 DOM.addClass(ro, 'mceLast'); 7396 } 7397 }); 7398 })(tinymce);(function(tinymce) { 7399 var DOM = tinymce.DOM; 7400 7401 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', { 7402 Button : function(id, s) { 7403 this.parent(id, s); 7404 this.classPrefix = 'mceButton'; 7405 }, 7406 7407 renderHTML : function() { 7408 var cp = this.classPrefix, s = this.settings, h, l; 7409 7410 l = DOM.encode(s.label || ''); 7411 h = '<a id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM.encode(s.title) + '">'; 7412 7413 if (s.image) 7414 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>'; 7415 else 7416 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>'; 7417 7418 return h; 7419 }, 7420 7421 postRender : function() { 7422 var t = this, s = t.settings; 7423 7424 tinymce.dom.Event.add(t.id, 'click', function(e) { 7425 if (!t.isDisabled()) 7426 return s.onclick.call(s.scope, e); 7427 }); 7428 } 7429 }); 7430 })(tinymce); 7431 (function(tinymce) { 7432 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; 7433 7434 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', { 7435 ListBox : function(id, s) { 7436 var t = this; 7437 7438 t.parent(id, s); 7439 7440 t.items = []; 7441 7442 t.onChange = new Dispatcher(t); 7443 7444 t.onPostRender = new Dispatcher(t); 7445 7446 t.onAdd = new Dispatcher(t); 7447 7448 t.onRenderMenu = new tinymce.util.Dispatcher(this); 7449 7450 t.classPrefix = 'mceListBox'; 7451 }, 7452 7453 select : function(va) { 7454 var t = this, fv, f; 7455 7456 if (va == undefined) 7457 return t.selectByIndex(-1); 7458 7459 // Is string or number make function selector 7460 if (va && va.call) 7461 f = va; 7462 else { 7463 f = function(v) { 7464 return v == va; 7465 }; 7466 } 7467 7468 // Do we need to do something? 7469 if (va != t.selectedValue) { 7470 // Find item 7471 each(t.items, function(o, i) { 7472 if (f(o.value)) { 7473 fv = 1; 7474 t.selectByIndex(i); 7475 return false; 7476 } 7477 }); 7478 7479 if (!fv) 7480 t.selectByIndex(-1); 7481 } 7482 }, 7483 7484 selectByIndex : function(idx) { 7485 var t = this, e, o; 7486 7487 if (idx != t.selectedIndex) { 7488 e = DOM.get(t.id + '_text'); 7489 o = t.items[idx]; 7490 7491 if (o) { 7492 t.selectedValue = o.value; 7493 t.selectedIndex = idx; 7494 DOM.setHTML(e, DOM.encode(o.title)); 7495 DOM.removeClass(e, 'mceTitle'); 7496 } else { 7497 DOM.setHTML(e, DOM.encode(t.settings.title)); 7498 DOM.addClass(e, 'mceTitle'); 7499 t.selectedValue = t.selectedIndex = null; 7500 } 7501 7502 e = 0; 7503 } 7504 }, 7505 7506 add : function(n, v, o) { 7507 var t = this; 7508 7509 o = o || {}; 7510 o = tinymce.extend(o, { 7511 title : n, 7512 value : v 7513 }); 7514 7515 t.items.push(o); 7516 t.onAdd.dispatch(t, o); 7517 }, 7518 7519 getLength : function() { 7520 return this.items.length; 7521 }, 7522 7523 renderHTML : function() { 7524 var h = '', t = this, s = t.settings, cp = t.classPrefix; 7525 7526 h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>'; 7527 h += '<td>' + DOM.createHTML('a', {id : t.id + '_text', href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>'; 7528 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>'; 7529 h += '</tr></tbody></table>'; 7530 7531 return h; 7532 }, 7533 7534 showMenu : function() { 7535 var t = this, p1, p2, e = DOM.get(this.id), m; 7536 7537 if (t.isDisabled() || t.items.length == 0) 7538 return; 7539 7540 if (t.menu && t.menu.isMenuVisible) 7541 return t.hideMenu(); 7542 7543 if (!t.isMenuRendered) { 7544 t.renderMenu(); 7545 t.isMenuRendered = true; 7546 } 7547 7548 p1 = DOM.getPos(this.settings.menu_container); 7549 p2 = DOM.getPos(e); 7550 7551 m = t.menu; 7552 m.settings.offset_x = p2.x; 7553 m.settings.offset_y = p2.y; 7554 m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus 7555 7556 // Select in menu 7557 if (t.oldID) 7558 m.items[t.oldID].setSelected(0); 7559 7560 each(t.items, function(o) { 7561 if (o.value === t.selectedValue) { 7562 m.items[o.id].setSelected(1); 7563 t.oldID = o.id; 7564 } 7565 }); 7566 7567 m.showMenu(0, e.clientHeight); 7568 7569 Event.add(DOM.doc, 'mousedown', t.hideMenu, t); 7570 DOM.addClass(t.id, t.classPrefix + 'Selected'); 7571 7572 //DOM.get(t.id + '_text').focus(); 7573 }, 7574 7575 hideMenu : function(e) { 7576 var t = this; 7577 7578 // Prevent double toogles by canceling the mouse click event to the button 7579 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open')) 7580 return; 7581 7582 if (!e || !DOM.getParent(e.target, '.mceMenu')) { 7583 DOM.removeClass(t.id, t.classPrefix + 'Selected'); 7584 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); 7585 7586 if (t.menu) 7587 t.menu.hideMenu(); 7588 } 7589 }, 7590 7591 renderMenu : function() { 7592 var t = this, m; 7593 7594 m = t.settings.control_manager.createDropMenu(t.id + '_menu', { 7595 menu_line : 1, 7596 'class' : t.classPrefix + 'Menu mceNoIcons', 7597 max_width : 150, 7598 max_height : 150 7599 }); 7600 7601 m.onHideMenu.add(t.hideMenu, t); 7602 7603 m.add({ 7604 title : t.settings.title, 7605 'class' : 'mceMenuItemTitle', 7606 onclick : function() { 7607 if (t.settings.onselect('') !== false) 7608 t.select(''); // Must be runned after 7609 } 7610 }); 7611 7612 each(t.items, function(o) { 7613 o.id = DOM.uniqueId(); 7614 o.onclick = function() { 7615 if (t.settings.onselect(o.value) !== false) 7616 t.select(o.value); // Must be runned after 7617 }; 7618 7619 m.add(o); 7620 }); 7621 7622 t.onRenderMenu.dispatch(t, m); 7623 t.menu = m; 7624 }, 7625 7626 postRender : function() { 7627 var t = this, cp = t.classPrefix; 7628 7629 Event.add(t.id, 'click', t.showMenu, t); 7630 Event.add(t.id + '_text', 'focus', function(e) { 7631 if (!t._focused) { 7632 t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) { 7633 var idx = -1, v, kc = e.keyCode; 7634 7635 // Find current index 7636 each(t.items, function(v, i) { 7637 if (t.selectedValue == v.value) 7638 idx = i; 7639 }); 7640 7641 // Move up/down 7642 if (kc == 38) 7643 v = t.items[idx - 1]; 7644 else if (kc == 40) 7645 v = t.items[idx + 1]; 7646 else if (kc == 13) { 7647 // Fake select on enter 7648 v = t.selectedValue; 7649 t.selectedValue = null; // Needs to be null to fake change 7650 t.settings.onselect(v); 7651 return Event.cancel(e); 7652 } 7653 7654 if (v) { 7655 t.hideMenu(); 7656 t.select(v.value); 7657 } 7658 }); 7659 } 7660 7661 t._focused = 1; 7662 }); 7663 Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;}); 7664 7665 // Old IE doesn't have hover on all elements 7666 if (tinymce.isIE6 || !DOM.boxModel) { 7667 Event.add(t.id, 'mouseover', function() { 7668 if (!DOM.hasClass(t.id, cp + 'Disabled')) 7669 DOM.addClass(t.id, cp + 'Hover'); 7670 }); 7671 7672 Event.add(t.id, 'mouseout', function() { 7673 if (!DOM.hasClass(t.id, cp + 'Disabled')) 7674 DOM.removeClass(t.id, cp + 'Hover'); 7675 }); 7676 } 7677 7678 t.onPostRender.dispatch(t, DOM.get(t.id)); 7679 }, 7680 7681 destroy : function() { 7682 this.parent(); 7683 7684 Event.clear(this.id + '_text'); 7685 Event.clear(this.id + '_open'); 7686 } 7687 }); 7688 })(tinymce);(function(tinymce) { 7689 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; 7690 7691 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', { 7692 NativeListBox : function(id, s) { 7693 this.parent(id, s); 7694 this.classPrefix = 'mceNativeListBox'; 7695 }, 7696 7697 setDisabled : function(s) { 7698 DOM.get(this.id).disabled = s; 7699 }, 7700 7701 isDisabled : function() { 7702 return DOM.get(this.id).disabled; 7703 }, 7704 7705 select : function(va) { 7706 var t = this, fv, f; 7707 7708 if (va == undefined) 7709 return t.selectByIndex(-1); 7710 7711 // Is string or number make function selector 7712 if (va && va.call) 7713 f = va; 7714 else { 7715 f = function(v) { 7716 return v == va; 7717 }; 7718 } 7719 7720 // Do we need to do something? 7721 if (va != t.selectedValue) { 7722 // Find item 7723 each(t.items, function(o, i) { 7724 if (f(o.value)) { 7725 fv = 1; 7726 t.selectByIndex(i); 7727 return false; 7728 } 7729 }); 7730 7731 if (!fv) 7732 t.selectByIndex(-1); 7733 } 7734 }, 7735 7736 selectByIndex : function(idx) { 7737 DOM.get(this.id).selectedIndex = idx + 1; 7738 this.selectedValue = this.items[idx] ? this.items[idx].value : null; 7739 }, 7740 7741 add : function(n, v, a) { 7742 var o, t = this; 7743 7744 a = a || {}; 7745 a.value = v; 7746 7747 if (t.isRendered()) 7748 DOM.add(DOM.get(this.id), 'option', a, n); 7749 7750 o = { 7751 title : n, 7752 value : v, 7753 attribs : a 7754 }; 7755 7756 t.items.push(o); 7757 t.onAdd.dispatch(t, o); 7758 }, 7759 7760 getLength : function() { 7761 return DOM.get(this.id).options.length - 1; 7762 }, 7763 7764 renderHTML : function() { 7765 var h, t = this; 7766 7767 h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --'); 7768 7769 each(t.items, function(it) { 7770 h += DOM.createHTML('option', {value : it.value}, it.title); 7771 }); 7772 7773 h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h); 7774 7775 return h; 7776 }, 7777 7778 postRender : function() { 7779 var t = this, ch; 7780 7781 t.rendered = true; 7782 7783 function onChange(e) { 7784 var v = t.items[e.target.selectedIndex - 1]; 7785 7786 if (v && (v = v.value)) { 7787 t.onChange.dispatch(t, v); 7788 7789 if (t.settings.onselect) 7790 t.settings.onselect(v); 7791 } 7792 }; 7793 7794 Event.add(t.id, 'change', onChange); 7795 7796 // Accessibility keyhandler 7797 Event.add(t.id, 'keydown', function(e) { 7798 var bf; 7799 7800 Event.remove(t.id, 'change', ch); 7801 7802 bf = Event.add(t.id, 'blur', function() { 7803 Event.add(t.id, 'change', onChange); 7804 Event.remove(t.id, 'blur', bf); 7805 }); 7806 7807 if (e.keyCode == 13 || e.keyCode == 32) { 7808 onChange(e); 7809 return Event.cancel(e); 7810 } 7811 }); 7812 7813 t.onPostRender.dispatch(t, DOM.get(t.id)); 7814 } 7815 }); 7816 })(tinymce);(function(tinymce) { 7817 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; 7818 7819 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', { 7820 MenuButton : function(id, s) { 7821 this.parent(id, s); 7822 7823 this.onRenderMenu = new tinymce.util.Dispatcher(this); 7824 7825 s.menu_container = s.menu_container || DOM.doc.body; 7826 }, 7827 7828 showMenu : function() { 7829 var t = this, p1, p2, e = DOM.get(t.id), m; 7830 7831 if (t.isDisabled()) 7832 return; 7833 7834 if (!t.isMenuRendered) { 7835 t.renderMenu(); 7836 t.isMenuRendered = true; 7837 } 7838 7839 if (t.isMenuVisible) 7840 return t.hideMenu(); 7841 7842 p1 = DOM.getPos(t.settings.menu_container); 7843 p2 = DOM.getPos(e); 7844 7845 m = t.menu; 7846 m.settings.offset_x = p2.x; 7847 m.settings.offset_y = p2.y; 7848 m.settings.vp_offset_x = p2.x; 7849 m.settings.vp_offset_y = p2.y; 7850 m.settings.keyboard_focus = t._focused; 7851 m.showMenu(0, e.clientHeight); 7852 7853 Event.add(DOM.doc, 'mousedown', t.hideMenu, t); 7854 t.setState('Selected', 1); 7855 7856 t.isMenuVisible = 1; 7857 }, 7858 7859 renderMenu : function() { 7860 var t = this, m; 7861 7862 m = t.settings.control_manager.createDropMenu(t.id + '_menu', { 7863 menu_line : 1, 7864 'class' : this.classPrefix + 'Menu', 7865 icons : t.settings.icons 7866 }); 7867 7868 m.onHideMenu.add(t.hideMenu, t); 7869 7870 t.onRenderMenu.dispatch(t, m); 7871 t.menu = m; 7872 }, 7873 7874 hideMenu : function(e) { 7875 var t = this; 7876 7877 // Prevent double toogles by canceling the mouse click event to the button 7878 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';})) 7879 return; 7880 7881 if (!e || !DOM.getParent(e.target, '.mceMenu')) { 7882 t.setState('Selected', 0); 7883 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); 7884 if (t.menu) 7885 t.menu.hideMenu(); 7886 } 7887 7888 t.isMenuVisible = 0; 7889 }, 7890 7891 postRender : function() { 7892 var t = this, s = t.settings; 7893 7894 Event.add(t.id, 'click', function() { 7895 if (!t.isDisabled()) { 7896 if (s.onclick) 7897 s.onclick(t.value); 7898 7899 t.showMenu(); 7900 } 7901 }); 7902 } 7903 }); 7904 })(tinymce); 7905 (function(tinymce) { 7906 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; 7907 7908 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', { 7909 SplitButton : function(id, s) { 7910 this.parent(id, s); 7911 this.classPrefix = 'mceSplitButton'; 7912 }, 7913 7914 renderHTML : function() { 7915 var h, t = this, s = t.settings, h1; 7916 7917 h = '<tbody><tr>'; 7918 7919 if (s.image) 7920 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']}); 7921 else 7922 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, ''); 7923 7924 h += '<td>' + DOM.createHTML('a', {id : t.id + '_action', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>'; 7925 7926 h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}); 7927 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>'; 7928 7929 h += '</tr></tbody>'; 7930 7931 return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h); 7932 }, 7933 7934 postRender : function() { 7935 var t = this, s = t.settings; 7936 7937 if (s.onclick) { 7938 Event.add(t.id + '_action', 'click', function() { 7939 if (!t.isDisabled()) 7940 s.onclick(t.value); 7941 }); 7942 } 7943 7944 Event.add(t.id + '_open', 'click', t.showMenu, t); 7945 Event.add(t.id + '_open', 'focus', function() {t._focused = 1;}); 7946 Event.add(t.id + '_open', 'blur', function() {t._focused = 0;}); 7947 7948 // Old IE doesn't have hover on all elements 7949 if (tinymce.isIE6 || !DOM.boxModel) { 7950 Event.add(t.id, 'mouseover', function() { 7951 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled')) 7952 DOM.addClass(t.id, 'mceSplitButtonHover'); 7953 }); 7954 7955 Event.add(t.id, 'mouseout', function() { 7956 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled')) 7957 DOM.removeClass(t.id, 'mceSplitButtonHover'); 7958 }); 7959 } 7960 }, 7961 7962 destroy : function() { 7963 this.parent(); 7964 7965 Event.clear(this.id + '_action'); 7966 Event.clear(this.id + '_open'); 7967 } 7968 }); 7969 })(tinymce); 7970 (function(tinymce) { 7971 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each; 7972 7973 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', { 7974 ColorSplitButton : function(id, s) { 7975 var t = this; 7976 7977 t.parent(id, s); 7978 7979 t.settings = s = tinymce.extend({ 7980 colors : '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF', 7981 grid_width : 8, 7982 default_color : '#888888' 7983 }, t.settings); 7984 7985 t.onShowMenu = new tinymce.util.Dispatcher(t); 7986 7987 t.onHideMenu = new tinymce.util.Dispatcher(t); 7988 7989 t.value = s.default_color; 7990 }, 7991 7992 showMenu : function() { 7993 var t = this, r, p, e, p2; 7994 7995 if (t.isDisabled()) 7996 return; 7997 7998 if (!t.isMenuRendered) { 7999 t.renderMenu(); 8000 t.isMenuRendered = true; 8001 } 8002 8003 if (t.isMenuVisible) 8004 return t.hideMenu(); 8005 8006 e = DOM.get(t.id); 8007 DOM.show(t.id + '_menu'); 8008 DOM.addClass(e, 'mceSplitButtonSelected'); 8009 p2 = DOM.getPos(e); 8010 DOM.setStyles(t.id + '_menu', { 8011 left : p2.x, 8012 top : p2.y + e.clientHeight, 8013 zIndex : 200000 8014 }); 8015 e = 0; 8016 8017 Event.add(DOM.doc, 'mousedown', t.hideMenu, t); 8018 t.onShowMenu.dispatch(t); 8019 8020 if (t._focused) { 8021 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) { 8022 if (e.keyCode == 27) 8023 t.hideMenu(); 8024 }); 8025 8026 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link 8027 } 8028 8029 t.isMenuVisible = 1; 8030 }, 8031 8032 hideMenu : function(e) { 8033 var t = this; 8034 8035 // Prevent double toogles by canceling the mouse click event to the button 8036 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';})) 8037 return; 8038 8039 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) { 8040 DOM.removeClass(t.id, 'mceSplitButtonSelected'); 8041 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); 8042 Event.remove(t.id + '_menu', 'keydown', t._keyHandler); 8043 DOM.hide(t.id + '_menu'); 8044 } 8045 8046 t.onHideMenu.dispatch(t); 8047 8048 t.isMenuVisible = 0; 8049 }, 8050 8051 renderMenu : function() { 8052 var t = this, m, i = 0, s = t.settings, n, tb, tr, w; 8053 8054 w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'}); 8055 m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'}); 8056 DOM.add(m, 'span', {'class' : 'mceMenuLine'}); 8057 8058 n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'}); 8059 tb = DOM.add(n, 'tbody'); 8060 8061 // Generate color grid 8062 i = 0; 8063 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) { 8064 c = c.replace(/^#/, ''); 8065 8066 if (!i--) { 8067 tr = DOM.add(tb, 'tr'); 8068 i = s.grid_width - 1; 8069 } 8070 8071 n = DOM.add(tr, 'td'); 8072 8073 n = DOM.add(n, 'a', { 8074 href : 'javascript:;', 8075 style : { 8076 backgroundColor : '#' + c 8077 }, 8078 mce_color : '#' + c 8079 }); 8080 }); 8081 8082 if (s.more_colors_func) { 8083 n = DOM.add(tb, 'tr'); 8084 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'}); 8085 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title); 8086 8087 Event.add(n, 'click', function(e) { 8088 s.more_colors_func.call(s.more_colors_scope || this); 8089 return Event.cancel(e); // Cancel to fix onbeforeunload problem 8090 }); 8091 } 8092 8093 DOM.addClass(m, 'mceColorSplitMenu'); 8094 8095 Event.add(t.id + '_menu', 'click', function(e) { 8096 var c; 8097 8098 e = e.target; 8099 8100 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color'))) 8101 t.setColor(c); 8102 8103 return Event.cancel(e); // Prevent IE auto save warning 8104 }); 8105 8106 return w; 8107 }, 8108 8109 setColor : function(c) { 8110 var t = this; 8111 8112 DOM.setStyle(t.id + '_preview', 'backgroundColor', c); 8113 8114 t.value = c; 8115 t.hideMenu(); 8116 t.settings.onselect(c); 8117 }, 8118 8119 postRender : function() { 8120 var t = this, id = t.id; 8121 8122 t.parent(); 8123 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'}); 8124 DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value); 8125 }, 8126 8127 destroy : function() { 8128 this.parent(); 8129 8130 Event.clear(this.id + '_menu'); 8131 Event.clear(this.id + '_more'); 8132 DOM.remove(this.id + '_menu'); 8133 } 8134 }); 8135 })(tinymce); 8136 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { 8137 renderHTML : function() { 8138 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl; 8139 8140 cl = t.controls; 8141 for (i=0; i<cl.length; i++) { 8142 // Get current control, prev control, next control and if the control is a list box or not 8143 co = cl[i]; 8144 pr = cl[i - 1]; 8145 nx = cl[i + 1]; 8146 8147 // Add toolbar start 8148 if (i === 0) { 8149 c = 'mceToolbarStart'; 8150 8151 if (co.Button) 8152 c += ' mceToolbarStartButton'; 8153 else if (co.SplitButton) 8154 c += ' mceToolbarStartSplitButton'; 8155 else if (co.ListBox) 8156 c += ' mceToolbarStartListBox'; 8157 8158 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->')); 8159 } 8160 8161 // Add toolbar end before list box and after the previous button 8162 // This is to fix the o2k7 editor skins 8163 if (pr && co.ListBox) { 8164 if (pr.Button || pr.SplitButton) 8165 h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->')); 8166 } 8167 8168 // Render control HTML 8169 8170 // IE 8 quick fix, needed to propertly generate a hit area for anchors 8171 if (dom.stdMode) 8172 h += '<td style="position: relative">' + co.renderHTML() + '</td>'; 8173 else 8174 h += '<td>' + co.renderHTML() + '</td>'; 8175 8176 // Add toolbar start after list box and before the next button 8177 // This is to fix the o2k7 editor skins 8178 if (nx && co.ListBox) { 8179 if (nx.Button || nx.SplitButton) 8180 h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->')); 8181 } 8182 } 8183 8184 c = 'mceToolbarEnd'; 8185 8186 if (co.Button) 8187 c += ' mceToolbarEndButton'; 8188 else if (co.SplitButton) 8189 c += ' mceToolbarEndSplitButton'; 8190 else if (co.ListBox) 8191 c += ' mceToolbarEndListBox'; 8192 8193 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->')); 8194 8195 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>'); 8196 } 8197 }); 8198 (function(tinymce) { 8199 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each; 8200 8201 tinymce.create('tinymce.AddOnManager', { 8202 items : [], 8203 urls : {}, 8204 lookup : {}, 8205 8206 onAdd : new Dispatcher(this), 8207 8208 get : function(n) { 8209 return this.lookup[n]; 8210 }, 8211 8212 requireLangPack : function(n) { 8213 var u, s = tinymce.EditorManager.settings; 8214 8215 if (s && s.language) { 8216 u = this.urls[n] + '/langs/' + s.language + '.js'; 8217 8218 if (!tinymce.dom.Event.domLoaded && !s.strict_mode) 8219 tinymce.ScriptLoader.load(u); 8220 else 8221 tinymce.ScriptLoader.add(u); 8222 } 8223 }, 8224 8225 add : function(id, o) { 8226 this.items.push(o); 8227 this.lookup[id] = o; 8228 this.onAdd.dispatch(this, id, o); 8229 8230 return o; 8231 }, 8232 8233 load : function(n, u, cb, s) { 8234 var t = this; 8235 8236 if (t.urls[n]) 8237 return; 8238 8239 if (u.indexOf('/') != 0 && u.indexOf('://') == -1) 8240 u = tinymce.baseURL + '/' + u; 8241 8242 t.urls[n] = u.substring(0, u.lastIndexOf('/')); 8243 tinymce.ScriptLoader.add(u, cb, s); 8244 } 8245 }); 8246 8247 // Create plugin and theme managers 8248 tinymce.PluginManager = new tinymce.AddOnManager(); 8249 tinymce.ThemeManager = new tinymce.AddOnManager(); 8250 }(tinymce)); 8251 8252 (function(tinymce) { 8253 // Shorten names 8254 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode; 8255 8256 tinymce.create('static tinymce.EditorManager', { 8257 editors : {}, 8258 8259 i18n : {}, 8260 8261 activeEditor : null, 8262 8263 preInit : function() { 8264 var t = this, lo = window.location; 8265 8266 // Setup some URLs where the editor API is located and where the document is 8267 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); 8268 if (!/[\/\\]$/.test(tinymce.documentBaseURL)) 8269 tinymce.documentBaseURL += '/'; 8270 8271 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL); 8272 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL); 8273 8274 // Add before unload listener 8275 // This was required since IE was leaking memory if you added and removed beforeunload listeners 8276 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event 8277 t.onBeforeUnload = new tinymce.util.Dispatcher(t); 8278 8279 // Must be on window or IE will leak if the editor is placed in frame or iframe 8280 Event.add(window, 'beforeunload', function(e) { 8281 t.onBeforeUnload.dispatch(t, e); 8282 }); 8283 }, 8284 8285 init : function(s) { 8286 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed; 8287 8288 function execCallback(se, n, s) { 8289 var f = se[n]; 8290 8291 if (!f) 8292 return; 8293 8294 if (tinymce.is(f, 'string')) { 8295 s = f.replace(/\.\w+$/, ''); 8296 s = s ? tinymce.resolve(s) : 0; 8297 f = tinymce.resolve(f); 8298 } 8299 8300 return f.apply(s || this, Array.prototype.slice.call(arguments, 2)); 8301 }; 8302 8303 s = extend({ 8304 theme : "simple", 8305 language : "en", 8306 strict_loading_mode : document.contentType == 'application/xhtml+xml' 8307 }, s); 8308 8309 t.settings = s; 8310 8311 // If page not loaded and strict mode isn't enabled then load them 8312 if (!Event.domLoaded && !s.strict_loading_mode) { 8313 // Load language 8314 if (s.language) 8315 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js'); 8316 8317 // Load theme 8318 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) 8319 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); 8320 8321 // Load plugins 8322 if (s.plugins) { 8323 pl = explode(s.plugins); 8324 8325 // Load rest if plugins 8326 each(pl, function(v) { 8327 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) { 8328 // Skip safari plugin for other browsers 8329 if (!tinymce.isWebKit && v == 'safari') 8330 return; 8331 8332 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js'); 8333 } 8334 }); 8335 } 8336 8337 sl.loadQueue(); 8338 } 8339 8340 // Legacy call 8341 Event.add(document, 'init', function() { 8342 var l, co; 8343 8344 execCallback(s, 'onpageload'); 8345 8346 // Verify that it's a valid browser 8347 if (s.browsers) { 8348 l = false; 8349 8350 each(explode(s.browsers), function(v) { 8351 switch (v) { 8352 case 'ie': 8353 case 'msie': 8354 if (tinymce.isIE) 8355 l = true; 8356 break; 8357 8358 case 'gecko': 8359 if (tinymce.isGecko) 8360 l = true; 8361 break; 8362 8363 case 'safari': 8364 case 'webkit': 8365 if (tinymce.isWebKit) 8366 l = true; 8367 break; 8368 8369 case 'opera': 8370 if (tinymce.isOpera) 8371 l = true; 8372 8373 break; 8374 } 8375 }); 8376 8377 // Not a valid one 8378 if (!l) 8379 return; 8380 } 8381 8382 switch (s.mode) { 8383 case "exact": 8384 l = s.elements || ''; 8385 8386 if(l.length > 0) { 8387 each(explode(l), function(v) { 8388 if (DOM.get(v)) { 8389 ed = new tinymce.Editor(v, s); 8390 el.push(ed); 8391 ed.render(1); 8392 } else { 8393 c = 0; 8394 8395 each(document.forms, function(f) { 8396 each(f.elements, function(e) { 8397 if (e.name === v) { 8398 v = 'mce_editor_' + c; 8399 DOM.setAttrib(e, 'id', v); 8400 8401 ed = new tinymce.Editor(v, s); 8402 el.push(ed); 8403 ed.render(1); 8404 } 8405 }); 8406 }); 8407 } 8408 }); 8409 } 8410 break; 8411 8412 case "textareas": 8413 case "specific_textareas": 8414 function hasClass(n, c) { 8415 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c); 8416 }; 8417 8418 each(DOM.select('textarea'), function(v) { 8419 if (s.editor_deselector && hasClass(v, s.editor_deselector)) 8420 return; 8421 8422 if (!s.editor_selector || hasClass(v, s.editor_selector)) { 8423 // Can we use the name 8424 e = DOM.get(v.name); 8425 if (!v.id && !e) 8426 v.id = v.name; 8427 8428 // Generate unique name if missing or already exists 8429 if (!v.id || t.get(v.id)) 8430 v.id = DOM.uniqueId(); 8431 8432 ed = new tinymce.Editor(v.id, s); 8433 el.push(ed); 8434 ed.render(1); 8435 } 8436 }); 8437 break; 8438 } 8439 8440 // Call onInit when all editors are initialized 8441 if (s.oninit) { 8442 l = co = 0; 8443 8444 each (el, function(ed) { 8445 co++; 8446 8447 if (!ed.initialized) { 8448 // Wait for it 8449 ed.onInit.add(function() { 8450 l++; 8451 8452 // All done 8453 if (l == co) 8454 execCallback(s, 'oninit'); 8455 }); 8456 } else 8457 l++; 8458 8459 // All done 8460 if (l == co) 8461 execCallback(s, 'oninit'); 8462 }); 8463 } 8464 }); 8465 }, 8466 8467 get : function(id) { 8468 return this.editors[id]; 8469 }, 8470 8471 getInstanceById : function(id) { 8472 return this.get(id); 8473 }, 8474 8475 add : function(e) { 8476 this.editors[e.id] = e; 8477 this._setActive(e); 8478 8479 return e; 8480 }, 8481 8482 remove : function(e) { 8483 var t = this; 8484 8485 // Not in the collection 8486 if (!t.editors[e.id]) 8487 return null; 8488 8489 delete t.editors[e.id]; 8490 8491 // Select another editor since the active one was removed 8492 if (t.activeEditor == e) { 8493 t._setActive(null); 8494 8495 each(t.editors, function(e) { 8496 t._setActive(e); 8497 return false; // Break 8498 }); 8499 } 8500 8501 e.destroy(); 8502 8503 return e; 8504 }, 8505 8506 execCommand : function(c, u, v) { 8507 var t = this, ed = t.get(v), w; 8508 8509 // Manager commands 8510 switch (c) { 8511 case "mceFocus": 8512 ed.focus(); 8513 return true; 8514 8515 case "mceAddEditor": 8516 case "mceAddControl": 8517 if (!t.get(v)) 8518 new tinymce.Editor(v, t.settings).render(); 8519 8520 return true; 8521 8522 case "mceAddFrameControl": 8523 w = v.window; 8524 8525 // Add tinyMCE global instance and tinymce namespace to specified window 8526 w.tinyMCE = tinyMCE; 8527 w.tinymce = tinymce; 8528 8529 tinymce.DOM.doc = w.document; 8530 tinymce.DOM.win = w; 8531 8532 ed = new tinymce.Editor(v.element_id, v); 8533 ed.render(); 8534 8535 // Fix IE memory leaks 8536 if (tinymce.isIE) { 8537 function clr() { 8538 ed.destroy(); 8539 w.detachEvent('onunload', clr); 8540 w = w.tinyMCE = w.tinymce = null; // IE leak 8541 }; 8542 8543 w.attachEvent('onunload', clr); 8544 } 8545 8546 v.page_window = null; 8547 8548 return true; 8549 8550 case "mceRemoveEditor": 8551 case "mceRemoveControl": 8552 if (ed) 8553 ed.remove(); 8554 8555 return true; 8556 8557 case 'mceToggleEditor': 8558 if (!ed) { 8559 t.execCommand('mceAddControl', 0, v); 8560 return true; 8561 } 8562 8563 if (ed.isHidden()) 8564 ed.show(); 8565 else 8566 ed.hide(); 8567 8568 return true; 8569 } 8570 8571 // Run command on active editor 8572 if (t.activeEditor) 8573 return t.activeEditor.execCommand(c, u, v); 8574 8575 return false; 8576 }, 8577 8578 execInstanceCommand : function(id, c, u, v) { 8579 var ed = this.get(id); 8580 8581 if (ed) 8582 return ed.execCommand(c, u, v); 8583 8584 return false; 8585 }, 8586 8587 triggerSave : function() { 8588 each(this.editors, function(e) { 8589 e.save(); 8590 }); 8591 }, 8592 8593 addI18n : function(p, o) { 8594 var lo, i18n = this.i18n; 8595 8596 if (!tinymce.is(p, 'string')) { 8597 each(p, function(o, lc) { 8598 each(o, function(o, g) { 8599 each(o, function(o, k) { 8600 if (g === 'common') 8601 i18n[lc + '.' + k] = o; 8602 else 8603 i18n[lc + '.' + g + '.' + k] = o; 8604 }); 8605 }); 8606 }); 8607 } else { 8608 each(o, function(o, k) { 8609 i18n[p + '.' + k] = o; 8610 }); 8611 } 8612 }, 8613 8614 // Private methods 8615 8616 _setActive : function(e) { 8617 this.selectedInstance = this.activeEditor = e; 8618 } 8619 }); 8620 8621 tinymce.EditorManager.preInit(); 8622 })(tinymce); 8623 8624 var tinyMCE = window.tinyMCE = tinymce.EditorManager; 8625 (function(tinymce) { 8626 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher; 8627 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit; 8628 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager; 8629 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode; 8630 8631 tinymce.create('tinymce.Editor', { 8632 Editor : function(id, s) { 8633 var t = this; 8634 8635 t.id = t.editorId = id; 8636 8637 t.execCommands = {}; 8638 t.queryStateCommands = {}; 8639 t.queryValueCommands = {}; 8640 8641 t.isNotDirty = false; 8642 8643 t.plugins = {}; 8644 8645 // Add events to the editor 8646 each([ 8647 'onPreInit', 8648 8649 'onBeforeRenderUI', 8650 8651 'onPostRender', 8652 8653 'onInit', 8654 8655 'onRemove', 8656 8657 'onActivate', 8658 8659 'onDeactivate', 8660 8661 'onClick', 8662 8663 'onEvent', 8664 8665 'onMouseUp', 8666 8667 'onMouseDown', 8668 8669 'onDblClick', 8670 8671 'onKeyDown', 8672 8673 'onKeyUp', 8674 8675 'onKeyPress', 8676 8677 'onContextMenu', 8678 8679 'onSubmit', 8680 8681 'onReset', 8682 8683 'onPaste', 8684 8685 'onPreProcess', 8686 8687 'onPostProcess', 8688 8689 'onBeforeSetContent', 8690 8691 'onBeforeGetContent', 8692 8693 'onSetContent', 8694 8695 'onGetContent', 8696 8697 'onLoadContent', 8698 8699 'onSaveContent', 8700 8701 'onNodeChange', 8702 8703 'onChange', 8704 8705 'onBeforeExecCommand', 8706 8707 'onExecCommand', 8708 8709 'onUndo', 8710 8711 'onRedo', 8712 8713 'onVisualAid', 8714 8715 'onSetProgressState' 8716 ], function(e) { 8717 t[e] = new Dispatcher(t); 8718 }); 8719 8720 t.settings = s = extend({ 8721 id : id, 8722 language : 'en', 8723 docs_language : 'en', 8724 theme : 'simple', 8725 skin : 'default', 8726 delta_width : 0, 8727 delta_height : 0, 8728 popup_css : '', 8729 plugins : '', 8730 document_base_url : tinymce.documentBaseURL, 8731 add_form_submit_trigger : 1, 8732 submit_patch : 1, 8733 add_unload_trigger : 1, 8734 convert_urls : 1, 8735 relative_urls : 1, 8736 remove_script_host : 1, 8737 table_inline_editing : 0, 8738 object_resizing : 1, 8739 cleanup : 1, 8740 accessibility_focus : 1, 8741 custom_shortcuts : 1, 8742 custom_undo_redo_keyboard_shortcuts : 1, 8743 custom_undo_redo_restore_selection : 1, 8744 custom_undo_redo : 1, 8745 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', 8746 visual_table_class : 'mceItemTable', 8747 visual : 1, 8748 inline_styles : true, 8749 convert_fonts_to_spans : true, 8750 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large', 8751 apply_source_formatting : 1, 8752 directionality : 'ltr', 8753 forced_root_block : 'p', 8754 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big', 8755 hidden_input : 1, 8756 padd_empty_editor : 1, 8757 render_ui : 1, 8758 init_theme : 1, 8759 force_p_newlines : 1, 8760 indentation : '30px', 8761 keep_styles : 1, 8762 fix_table_elements : 1, 8763 removeformat_selector : 'span,b,strong,em,i,font,u,strike' 8764 }, s); 8765 8766 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, { 8767 base_uri : tinyMCE.baseURI 8768 }); 8769 8770 t.baseURI = EditorManager.baseURI; 8771 8772 // Call setup 8773 t.execCallback('setup', t); 8774 }, 8775 8776 render : function(nst) { 8777 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader; 8778 8779 // Page is not loaded yet, wait for it 8780 if (!Event.domLoaded) { 8781 Event.add(document, 'init', function() { 8782 t.render(); 8783 }); 8784 return; 8785 } 8786 8787 // Force strict loading mode if render us called by user and not internally 8788 if (!nst) { 8789 s.strict_loading_mode = 1; 8790 tinyMCE.settings = s; 8791 } 8792 8793 // Element not found, then skip initialization 8794 if (!t.getElement()) 8795 return; 8796 8797 if (s.strict_loading_mode) { 8798 sl.settings.strict_mode = s.strict_loading_mode; 8799 tinymce.DOM.settings.strict = 1; 8800 } 8801 8802 // Add hidden input for non input elements inside form elements 8803 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form')) 8804 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id); 8805 8806 if (tinymce.WindowManager) 8807 t.windowManager = new tinymce.WindowManager(t); 8808 8809 if (s.encoding == 'xml') { 8810 t.onGetContent.add(function(ed, o) { 8811 if (o.save) 8812 o.content = DOM.encode(o.content); 8813 }); 8814 } 8815 8816 if (s.add_form_submit_trigger) { 8817 t.onSubmit.addToTop(function() { 8818 if (t.initialized) { 8819 t.save(); 8820 t.isNotDirty = 1; 8821 } 8822 }); 8823 } 8824 8825 if (s.add_unload_trigger) { 8826 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() { 8827 if (t.initialized && !t.destroyed && !t.isHidden()) 8828 t.save({format : 'raw', no_events : true}); 8829 }); 8830 } 8831 8832 tinymce.addUnload(t.destroy, t); 8833 8834 if (s.submit_patch) { 8835 t.onBeforeRenderUI.add(function() { 8836 var n = t.getElement().form; 8837 8838 if (!n) 8839 return; 8840 8841 // Already patched 8842 if (n._mceOldSubmit) 8843 return; 8844 8845 // Check page uses id="submit" or name="submit" for it's submit button 8846 if (!n.submit.nodeType && !n.submit.length) { 8847 t.formElement = n; 8848 n._mceOldSubmit = n.submit; 8849 n.submit = function() { 8850 // Save all instances 8851 EditorManager.triggerSave(); 8852 t.isNotDirty = 1; 8853 8854 return t.formElement._mceOldSubmit(t.formElement); 8855 }; 8856 } 8857 8858 n = null; 8859 }); 8860 } 8861 8862 // Load scripts 8863 function loadScripts() { 8864 if (s.language) 8865 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js'); 8866 8867 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) 8868 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); 8869 8870 each(explode(s.plugins), function(p) { 8871 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) { 8872 // Skip safari plugin for other browsers 8873 if (!isWebKit && p == 'safari') 8874 return; 8875 8876 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js'); 8877 } 8878 }); 8879 8880 // Init when que is loaded 8881 sl.loadQueue(function() { 8882 if (!t.removed) 8883 t.init(); 8884 }); 8885 }; 8886 8887 loadScripts(); 8888 }, 8889 8890 init : function() { 8891 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re; 8892 8893 EditorManager.add(t); 8894 8895 if (s.theme) { 8896 s.theme = s.theme.replace(/-/, ''); 8897 o = ThemeManager.get(s.theme); 8898 t.theme = new o(); 8899 8900 if (t.theme.init && s.init_theme) 8901 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, '')); 8902 } 8903 8904 // Create all plugins 8905 each(explode(s.plugins.replace(/\-/g, '')), function(p) { 8906 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po; 8907 8908 if (c) { 8909 po = new c(t, u); 8910 8911 t.plugins[p] = po; 8912 8913 if (po.init) 8914 po.init(t, u); 8915 } 8916 }); 8917 8918 // Setup popup CSS path(s) 8919 if (s.popup_css !== false) { 8920 if (s.popup_css) 8921 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css); 8922 else 8923 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css"); 8924 } 8925 8926 if (s.popup_css_add) 8927 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add); 8928 8929 t.controlManager = new tinymce.ControlManager(t); 8930 8931 t.undoManager = new tinymce.UndoManager(t); 8932 8933 // Pass through 8934 t.undoManager.onAdd.add(function(um, l) { 8935 if (!l.initial) 8936 return t.onChange.dispatch(t, l, um); 8937 }); 8938 8939 t.undoManager.onUndo.add(function(um, l) { 8940 return t.onUndo.dispatch(t, l, um); 8941 }); 8942 8943 t.undoManager.onRedo.add(function(um, l) { 8944 return t.onRedo.dispatch(t, l, um); 8945 }); 8946 8947 if (s.custom_undo_redo) { 8948 t.onExecCommand.add(function(ed, cmd, ui, val, a) { 8949 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) 8950 t.undoManager.add(); 8951 }); 8952 } 8953 8954 t.onExecCommand.add(function(ed, c) { 8955 // Don't refresh the select lists until caret move 8956 if (!/^(FontName|FontSize)$/.test(c)) 8957 t.nodeChanged(); 8958 }); 8959 8960 // Remove ghost selections on images and tables in Gecko 8961 if (isGecko) { 8962 function repaint(a, o) { 8963 if (!o || !o.initial) 8964 t.execCommand('mceRepaint'); 8965 }; 8966 8967 t.onUndo.add(repaint); 8968 t.onRedo.add(repaint); 8969 t.onSetContent.add(repaint); 8970 } 8971 8972 // Enables users to override the control factory 8973 t.onBeforeRenderUI.dispatch(t, t.controlManager); 8974 8975 // Measure box 8976 if (s.render_ui) { 8977 w = s.width || e.style.width || e.offsetWidth; 8978 h = s.height || e.style.height || e.offsetHeight; 8979 t.orgDisplay = e.style.display; 8980 re = /^[0-9\.]+(|px)$/i; 8981 8982 if (re.test('' + w)) 8983 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100); 8984 8985 if (re.test('' + h)) 8986 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100); 8987 8988 // Render UI 8989 o = t.theme.renderUI({ 8990 targetNode : e, 8991 width : w, 8992 height : h, 8993 deltaWidth : s.delta_width, 8994 deltaHeight : s.delta_height 8995 }); 8996 8997 t.editorContainer = o.editorContainer; 8998 } 8999 9000 9001 // User specified a document.domain value 9002 if (document.domain && location.hostname != document.domain) 9003 tinymce.relaxedDomain = document.domain; 9004 9005 // Resize editor 9006 DOM.setStyles(o.sizeContainer || o.editorContainer, { 9007 width : w, 9008 height : h 9009 }); 9010 9011 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : ''); 9012 if (h < 100) 9013 h = 100; 9014 9015 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">'; 9016 9017 // We only need to override paths if we have to 9018 // IE has a bug where it remove site absolute urls to relative ones if this is specified 9019 if (s.document_base_url != tinymce.documentBaseURL) 9020 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />'; 9021 9022 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; 9023 9024 if (tinymce.relaxedDomain) 9025 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>'; 9026 9027 bi = s.body_id || 'tinymce'; 9028 if (bi.indexOf('=') != -1) { 9029 bi = t.getParam('body_id', '', 'hash'); 9030 bi = bi[t.id] || bi; 9031 } 9032 9033 bc = s.body_class || ''; 9034 if (bc.indexOf('=') != -1) { 9035 bc = t.getParam('body_class', '', 'hash'); 9036 bc = bc[t.id] || ''; 9037 } 9038 9039 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>'; 9040 9041 // Domain relaxing enabled, then set document domain 9042 if (tinymce.relaxedDomain) { 9043 // We need to write the contents here in IE since multiple writes messes up refresh button and back button 9044 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5)) 9045 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; 9046 else if (tinymce.isOpera) 9047 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()'; 9048 } 9049 9050 // Create iframe 9051 n = DOM.add(o.iframeContainer, 'iframe', { 9052 id : t.id + "_ifr", 9053 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7 9054 frameBorder : '0', 9055 style : { 9056 width : '100%', 9057 height : h 9058 } 9059 }); 9060 9061 t.contentAreaContainer = o.iframeContainer; 9062 DOM.get(o.editorContainer).style.display = t.orgDisplay; 9063 DOM.get(t.id).style.display = 'none'; 9064 9065 if (!isIE || !tinymce.relaxedDomain) 9066 t.setupIframe(); 9067 9068 e = n = o = null; // Cleanup 9069 }, 9070 9071 setupIframe : function() { 9072 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b; 9073 9074 // Setup iframe body 9075 if (!isIE || !tinymce.relaxedDomain) { 9076 d.open(); 9077 d.write(t.iframeHTML); 9078 d.close(); 9079 } 9080 9081 // Design mode needs to be added here Ctrl+A will fail otherwise 9082 if (!isIE) { 9083 try { 9084 if (!s.readonly) 9085 d.designMode = 'On'; 9086 } catch (ex) { 9087 // Will fail on Gecko if the editor is placed in an hidden container element 9088 // The design mode will be set ones the editor is focused 9089 } 9090 } 9091 9092 // IE needs to use contentEditable or it will display non secure items for HTTPS 9093 if (isIE) { 9094 // It will not steal focus if we hide it while setting contentEditable 9095 b = t.getBody(); 9096 DOM.hide(b); 9097 9098 if (!s.readonly) 9099 b.contentEditable = true; 9100 9101 DOM.show(b); 9102 } 9103 9104 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), { 9105 keep_values : true, 9106 url_converter : t.convertURL, 9107 url_converter_scope : t, 9108 hex_colors : s.force_hex_style_colors, 9109 class_filter : s.class_filter, 9110 update_styles : 1, 9111 fix_ie_paragraphs : 1 9112 }); 9113 9114 t.serializer = new tinymce.dom.Serializer(extend(s, { 9115 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements, 9116 dom : t.dom 9117 })); 9118 9119 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer); 9120 9121 t.forceBlocks = new tinymce.ForceBlocks(t, { 9122 forced_root_block : s.forced_root_block 9123 }); 9124 t.editorCommands = new tinymce.EditorCommands(t); 9125 9126 // Pass through 9127 t.serializer.onPreProcess.add(function(se, o) { 9128 return t.onPreProcess.dispatch(t, o, se); 9129 }); 9130 9131 t.serializer.onPostProcess.add(function(se, o) { 9132 return t.onPostProcess.dispatch(t, o, se); 9133 }); 9134 9135 t.onPreInit.dispatch(t); 9136 9137 if (!s.gecko_spellcheck) 9138 t.getBody().spellcheck = 0; 9139 9140 if (!s.readonly) 9141 t._addEvents(); 9142 9143 t.controlManager.onPostRender.dispatch(t, t.controlManager); 9144 t.onPostRender.dispatch(t); 9145 9146 if (s.directionality) 9147 t.getBody().dir = s.directionality; 9148 9149 if (s.nowrap) 9150 t.getBody().style.whiteSpace = "nowrap"; 9151 9152 if (s.custom_elements) { 9153 function handleCustom(ed, o) { 9154 each(explode(s.custom_elements), function(v) { 9155 var n; 9156 9157 if (v.indexOf('~') === 0) { 9158 v = v.substring(1); 9159 n = 'span'; 9160 } else 9161 n = 'div'; 9162 9163 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>'); 9164 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>'); 9165 }); 9166 }; 9167 9168 t.onBeforeSetContent.add(handleCustom); 9169 t.onPostProcess.add(function(ed, o) { 9170 if (o.set) 9171 handleCustom(ed, o); 9172 }); 9173 } 9174 9175 if (s.handle_node_change_callback) { 9176 t.onNodeChange.add(function(ed, cm, n) { 9177 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed()); 9178 }); 9179 } 9180 9181 if (s.save_callback) { 9182 t.onSaveContent.add(function(ed, o) { 9183 var h = t.execCallback('save_callback', t.id, o.content, t.getBody()); 9184 9185 if (h) 9186 o.content = h; 9187 }); 9188 } 9189 9190 if (s.onchange_callback) { 9191 t.onChange.add(function(ed, l) { 9192 t.execCallback('onchange_callback', t, l); 9193 }); 9194 } 9195 9196 if (s.convert_newlines_to_brs) { 9197 t.onBeforeSetContent.add(function(ed, o) { 9198 if (o.initial) 9199 o.content = o.content.replace(/\r?\n/g, '<br />'); 9200 }); 9201 } 9202 9203 if (s.fix_nesting && isIE) { 9204 t.onBeforeSetContent.add(function(ed, o) { 9205 o.content = t._fixNesting(o.content); 9206 }); 9207 } 9208 9209 if (s.preformatted) { 9210 t.onPostProcess.add(function(ed, o) { 9211 o.content = o.content.replace(/^\s*<pre.*?>/, ''); 9212 o.content = o.content.replace(/<\/pre>\s*$/, ''); 9213 9214 if (o.set) 9215 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>'; 9216 }); 9217 } 9218 9219 if (s.verify_css_classes) { 9220 t.serializer.attribValueFilter = function(n, v) { 9221 var s, cl; 9222 9223 if (n == 'class') { 9224 // Build regexp for classes 9225 if (!t.classesRE) { 9226 cl = t.dom.getClasses(); 9227 9228 if (cl.length > 0) { 9229 s = ''; 9230 9231 each (cl, function(o) { 9232 s += (s ? '|' : '') + o['class']; 9233 }); 9234 9235 t.classesRE = new RegExp('(' + s + ')', 'gi'); 9236 } 9237 } 9238 9239 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : ''; 9240 } 9241 9242 return v; 9243 }; 9244 } 9245 9246 if (s.convert_fonts_to_spans) 9247 t._convertFonts(); 9248 9249 if (s.inline_styles) 9250 t._convertInlineElements(); 9251 9252 if (s.cleanup_callback) { 9253 t.onBeforeSetContent.add(function(ed, o) { 9254 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); 9255 }); 9256 9257 t.onPreProcess.add(function(ed, o) { 9258 if (o.set) 9259 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o); 9260 9261 if (o.get) 9262 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o); 9263 }); 9264 9265 t.onPostProcess.add(function(ed, o) { 9266 if (o.set) 9267 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); 9268 9269 if (o.get) 9270 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o); 9271 }); 9272 } 9273 9274 if (s.save_callback) { 9275 t.onGetContent.add(function(ed, o) { 9276 if (o.save) 9277 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody()); 9278 }); 9279 } 9280 9281 if (s.handle_event_callback) { 9282 t.onEvent.add(function(ed, e, o) { 9283 if (t.execCallback('handle_event_callback', e, ed, o) === false) 9284 Event.cancel(e); 9285 }); 9286 } 9287 9288 // Add visual aids when new contents is added 9289 t.onSetContent.add(function() { 9290 t.addVisual(t.getBody()); 9291 }); 9292 9293 // Remove empty contents 9294 if (s.padd_empty_editor) { 9295 t.onPostProcess.add(function(ed, o) { 9296 o.content = o.content.replace(/^(<p[^>]*>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); 9297 }); 9298 } 9299 9300 if (isGecko) { 9301 // Fix gecko link bug, when a link is placed at the end of block elements there is 9302 // no way to move the caret behind the link. This fix adds a bogus br element after the link 9303 function fixLinks(ed, o) { 9304 each(ed.dom.select('a'), function(n) { 9305 var pn = n.parentNode; 9306 9307 if (ed.dom.isBlock(pn) && pn.lastChild === n) 9308 ed.dom.add(pn, 'br', {'mce_bogus' : 1}); 9309 }); 9310 }; 9311 9312 t.onExecCommand.add(function(ed, cmd) { 9313 if (cmd === 'CreateLink') 9314 fixLinks(ed); 9315 }); 9316 9317 t.onSetContent.add(t.selection.onSetContent.add(fixLinks)); 9318 9319 if (!s.readonly) { 9320 try { 9321 // Design mode must be set here once again to fix a bug where 9322 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again 9323 d.designMode = 'Off'; 9324 d.designMode = 'On'; 9325 } catch (ex) { 9326 // Will fail on Gecko if the editor is placed in an hidden container element 9327 // The design mode will be set ones the editor is focused 9328 } 9329 } 9330 } 9331 9332 // A small timeout was needed since firefox will remove. Bug: #1838304 9333 setTimeout(function () { 9334 if (t.removed) 9335 return; 9336 9337 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')}); 9338 t.startContent = t.getContent({format : 'raw'}); 9339 t.undoManager.add({initial : true}); 9340 t.initialized = true; 9341 9342 t.onInit.dispatch(t); 9343 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc()); 9344 t.execCallback('init_instance_callback', t); 9345 t.focus(true); 9346 t.nodeChanged({initial : 1}); 9347 9348 // Load specified content CSS last 9349 if (s.content_css) { 9350 tinymce.each(explode(s.content_css), function(u) { 9351 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u)); 9352 }); 9353 } 9354 9355 // Handle auto focus 9356 if (s.auto_focus) { 9357 setTimeout(function () { 9358 var ed = EditorManager.get(s.auto_focus); 9359 9360 ed.selection.select(ed.getBody(), 1); 9361 ed.selection.collapse(1); 9362 ed.getWin().focus(); 9363 }, 100); 9364 } 9365 }, 1); 9366 9367 e = null; 9368 }, 9369 9370 9371 focus : function(sf) { 9372 var oed, t = this, ce = t.settings.content_editable; 9373 9374 if (!sf) { 9375 // Is not content editable or the selection is outside the area in IE 9376 // the IE statement is needed to avoid bluring if element selections inside layers since 9377 // the layer is like it's own document in IE 9378 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc())) 9379 t.getWin().focus(); 9380 9381 } 9382 9383 if (EditorManager.activeEditor != t) { 9384 if ((oed = EditorManager.activeEditor) != null) 9385 oed.onDeactivate.dispatch(oed, t); 9386 9387 t.onActivate.dispatch(t, oed); 9388 } 9389 9390 EditorManager._setActive(t); 9391 }, 9392 9393 execCallback : function(n) { 9394 var t = this, f = t.settings[n], s; 9395 9396 if (!f) 9397 return; 9398 9399 // Look through lookup 9400 if (t.callbackLookup && (s = t.callbackLookup[n])) { 9401 f = s.func; 9402 s = s.scope; 9403 } 9404 9405 if (is(f, 'string')) { 9406 s = f.replace(/\.\w+$/, ''); 9407 s = s ? tinymce.resolve(s) : 0; 9408 f = tinymce.resolve(f); 9409 t.callbackLookup = t.callbackLookup || {}; 9410 t.callbackLookup[n] = {func : f, scope : s}; 9411 } 9412 9413 return f.apply(s || t, Array.prototype.slice.call(arguments, 1)); 9414 }, 9415 9416 translate : function(s) { 9417 var c = this.settings.language || 'en', i18n = EditorManager.i18n; 9418 9419 if (!s) 9420 return ''; 9421 9422 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) { 9423 return i18n[c + '.' + b] || '{#' + b + '}'; 9424 }); 9425 }, 9426 9427 getLang : function(n, dv) { 9428 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}'); 9429 }, 9430 9431 getParam : function(n, dv, ty) { 9432 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o; 9433 9434 if (ty === 'hash') { 9435 o = {}; 9436 9437 if (is(v, 'string')) { 9438 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) { 9439 v = v.split('='); 9440 9441 if (v.length > 1) 9442 o[tr(v[0])] = tr(v[1]); 9443 else 9444 o[tr(v[0])] = tr(v); 9445 }); 9446 } else 9447 o = v; 9448 9449 return o; 9450 } 9451 9452 return v; 9453 }, 9454 9455 nodeChanged : function(o) { 9456 var t = this, s = t.selection, n = s.getNode() || t.getBody(); 9457 9458 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading 9459 if (t.initialized) { 9460 t.onNodeChange.dispatch( 9461 t, 9462 o ? o.controlManager || t.controlManager : t.controlManager, 9463 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state 9464 s.isCollapsed(), 9465 o 9466 ); 9467 } 9468 }, 9469 9470 addButton : function(n, s) { 9471 var t = this; 9472 9473 t.buttons = t.buttons || {}; 9474 t.buttons[n] = s; 9475 }, 9476 9477 addCommand : function(n, f, s) { 9478 this.execCommands[n] = {func : f, scope : s || this}; 9479 }, 9480 9481 addQueryStateHandler : function(n, f, s) { 9482 this.queryStateCommands[n] = {func : f, scope : s || this}; 9483 }, 9484 9485 addQueryValueHandler : function(n, f, s) { 9486 this.queryValueCommands[n] = {func : f, scope : s || this}; 9487 }, 9488 9489 addShortcut : function(pa, desc, cmd_func, sc) { 9490 var t = this, c; 9491 9492 if (!t.settings.custom_shortcuts) 9493 return false; 9494 9495 t.shortcuts = t.shortcuts || {}; 9496 9497 if (is(cmd_func, 'string')) { 9498 c = cmd_func; 9499 9500 cmd_func = function() { 9501 t.execCommand(c, false, null); 9502 }; 9503 } 9504 9505 if (is(cmd_func, 'object')) { 9506 c = cmd_func; 9507 9508 cmd_func = function() { 9509 t.execCommand(c[0], c[1], c[2]); 9510 }; 9511 } 9512 9513 each(explode(pa), function(pa) { 9514 var o = { 9515 func : cmd_func, 9516 scope : sc || this, 9517 desc : desc, 9518 alt : false, 9519 ctrl : false, 9520 shift : false 9521 }; 9522 9523 each(explode(pa, '+'), function(v) { 9524 switch (v) { 9525 case 'alt': 9526 case 'ctrl': 9527 case 'shift': 9528 o[v] = true; 9529 break; 9530 9531 default: 9532 o.charCode = v.charCodeAt(0); 9533 o.keyCode = v.toUpperCase().charCodeAt(0); 9534 } 9535 }); 9536 9537 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o; 9538 }); 9539 9540 return true; 9541 }, 9542 9543 execCommand : function(cmd, ui, val, a) { 9544 var t = this, s = 0, o, st; 9545 9546 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus)) 9547 t.focus(); 9548 9549 o = {}; 9550 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o); 9551 if (o.terminate) 9552 return false; 9553 9554 // Command callback 9555 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) { 9556 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9557 return true; 9558 } 9559 9560 // Registred commands 9561 if (o = t.execCommands[cmd]) { 9562 st = o.func.call(o.scope, ui, val); 9563 9564 // Fall through on true 9565 if (st !== true) { 9566 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9567 return st; 9568 } 9569 } 9570 9571 // Plugin commands 9572 each(t.plugins, function(p) { 9573 if (p.execCommand && p.execCommand(cmd, ui, val)) { 9574 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9575 s = 1; 9576 return false; 9577 } 9578 }); 9579 9580 if (s) 9581 return true; 9582 9583 // Theme commands 9584 if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) { 9585 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9586 return true; 9587 } 9588 9589 // Execute global commands 9590 if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) { 9591 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9592 return true; 9593 } 9594 9595 // Editor commands 9596 if (t.editorCommands.execCommand(cmd, ui, val)) { 9597 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9598 return true; 9599 } 9600 9601 // Browser commands 9602 t.getDoc().execCommand(cmd, ui, val); 9603 t.onExecCommand.dispatch(t, cmd, ui, val, a); 9604 }, 9605 9606 queryCommandState : function(c) { 9607 var t = this, o, s; 9608 9609 // Is hidden then return undefined 9610 if (t._isHidden()) 9611 return; 9612 9613 // Registred commands 9614 if (o = t.queryStateCommands[c]) { 9615 s = o.func.call(o.scope); 9616 9617 // Fall though on true 9618 if (s !== true) 9619 return s; 9620 } 9621 9622 // Registred commands 9623 o = t.editorCommands.queryCommandState(c); 9624 if (o !== -1) 9625 return o; 9626 9627 // Browser commands 9628 try { 9629 return this.getDoc().queryCommandState(c); 9630 } catch (ex) { 9631 // Fails sometimes see bug: 1896577 9632 } 9633 }, 9634 9635 queryCommandValue : function(c) { 9636 var t = this, o, s; 9637 9638 // Is hidden then return undefined 9639 if (t._isHidden()) 9640 return; 9641 9642 // Registred commands 9643 if (o = t.queryValueCommands[c]) { 9644 s = o.func.call(o.scope); 9645 9646 // Fall though on true 9647 if (s !== true) 9648 return s; 9649 } 9650 9651 // Registred commands 9652 o = t.editorCommands.queryCommandValue(c); 9653 if (is(o)) 9654 return o; 9655 9656 // Browser commands 9657 try { 9658 return this.getDoc().queryCommandValue(c); 9659 } catch (ex) { 9660 // Fails sometimes see bug: 1896577 9661 } 9662 }, 9663 9664 show : function() { 9665 var t = this; 9666 9667 DOM.show(t.getContainer()); 9668 DOM.hide(t.id); 9669 t.load(); 9670 }, 9671 9672 hide : function() { 9673 var t = this, d = t.getDoc(); 9674 9675 // Fixed bug where IE has a blinking cursor left from the editor 9676 if (isIE && d) 9677 d.execCommand('SelectAll'); 9678 9679 // We must save before we hide so Safari doesn't crash 9680 t.save(); 9681 DOM.hide(t.getContainer()); 9682 DOM.setStyle(t.id, 'display', t.orgDisplay); 9683 }, 9684 9685 isHidden : function() { 9686 return !DOM.isHidden(this.id); 9687 }, 9688 9689 setProgressState : function(b, ti, o) { 9690 this.onSetProgressState.dispatch(this, b, ti, o); 9691 9692 return b; 9693 }, 9694 9695 load : function(o) { 9696 var t = this, e = t.getElement(), h; 9697 9698 if (e) { 9699 o = o || {}; 9700 o.load = true; 9701 9702 // Double encode existing entities in the value 9703 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o); 9704 o.element = e; 9705 9706 if (!o.no_events) 9707 t.onLoadContent.dispatch(t, o); 9708 9709 o.element = e = null; 9710 9711 return h; 9712 } 9713 }, 9714 9715 save : function(o) { 9716 var t = this, e = t.getElement(), h, f; 9717 9718 if (!e || !t.initialized) 9719 return; 9720 9721 o = o || {}; 9722 o.save = true; 9723 9724 // Add undo level will trigger onchange event 9725 if (!o.no_events) { 9726 t.undoManager.typing = 0; 9727 t.undoManager.add(); 9728 } 9729 9730 o.element = e; 9731 h = o.content = t.getContent(o); 9732 9733 if (!o.no_events) 9734 t.onSaveContent.dispatch(t, o); 9735 9736 h = o.content; 9737 9738 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) { 9739 e.innerHTML = h; 9740 9741 // Update hidden form element 9742 if (f = DOM.getParent(t.id, 'form')) { 9743 each(f.elements, function(e) { 9744 if (e.name == t.id) { 9745 e.value = h; 9746 return false; 9747 } 9748 }); 9749 } 9750 } else 9751 e.value = h; 9752 9753 o.element = e = null; 9754 9755 return h; 9756 }, 9757 9758 setContent : function(h, o) { 9759 var t = this; 9760 9761 o = o || {}; 9762 o.format = o.format || 'html'; 9763 o.set = true; 9764 o.content = h; 9765 9766 if (!o.no_events) 9767 t.onBeforeSetContent.dispatch(t, o); 9768 9769 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content 9770 // It will also be impossible to place the caret in the editor unless there is a BR element present 9771 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) { 9772 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />'); 9773 o.format = 'raw'; 9774 } 9775 9776 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content)); 9777 9778 if (o.format != 'raw' && t.settings.cleanup) { 9779 o.getInner = true; 9780 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o)); 9781 } 9782 9783 if (!o.no_events) 9784 t.onSetContent.dispatch(t, o); 9785 9786 return o.content; 9787 }, 9788 9789 getContent : function(o) { 9790 var t = this, h; 9791 9792 o = o || {}; 9793 o.format = o.format || 'html'; 9794 o.get = true; 9795 9796 if (!o.no_events) 9797 t.onBeforeGetContent.dispatch(t, o); 9798 9799 if (o.format != 'raw' && t.settings.cleanup) { 9800 o.getInner = true; 9801 h = t.serializer.serialize(t.getBody(), o); 9802 } else 9803 h = t.getBody().innerHTML; 9804 9805 h = h.replace(/^\s*|\s*$/g, ''); 9806 o.content = h; 9807 9808 if (!o.no_events) 9809 t.onGetContent.dispatch(t, o); 9810 9811 return o.content; 9812 }, 9813 9814 isDirty : function() { 9815 var t = this; 9816 9817 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty; 9818 }, 9819 9820 getContainer : function() { 9821 var t = this; 9822 9823 if (!t.container) 9824 t.container = DOM.get(t.editorContainer || t.id + '_parent'); 9825 9826 return t.container; 9827 }, 9828 9829 getContentAreaContainer : function() { 9830 return this.contentAreaContainer; 9831 }, 9832 9833 getElement : function() { 9834 return DOM.get(this.settings.content_element || this.id); 9835 }, 9836 9837 getWin : function() { 9838 var t = this, e; 9839 9840 if (!t.contentWindow) { 9841 e = DOM.get(t.id + "_ifr"); 9842 9843 if (e) 9844 t.contentWindow = e.contentWindow; 9845 } 9846 9847 return t.contentWindow; 9848 }, 9849 9850 getDoc : function() { 9851 var t = this, w; 9852 9853 if (!t.contentDocument) { 9854 w = t.getWin(); 9855 9856 if (w) 9857 t.contentDocument = w.document; 9858 } 9859 9860 return t.contentDocument; 9861 }, 9862 9863 getBody : function() { 9864 return this.bodyElement || this.getDoc().body; 9865 }, 9866 9867 convertURL : function(u, n, e) { 9868 var t = this, s = t.settings; 9869 9870 // Use callback instead 9871 if (s.urlconverter_callback) 9872 return t.execCallback('urlconverter_callback', u, e, true, n); 9873 9874 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs 9875 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0) 9876 return u; 9877 9878 // Convert to relative 9879 if (s.relative_urls) 9880 return t.documentBaseURI.toRelative(u); 9881 9882 // Convert to absolute 9883 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host); 9884 9885 return u; 9886 }, 9887 9888 addVisual : function(e) { 9889 var t = this, s = t.settings; 9890 9891 e = e || t.getBody(); 9892 9893 if (!is(t.hasVisual)) 9894 t.hasVisual = s.visual; 9895 9896 each(t.dom.select('table,a', e), function(e) { 9897 var v; 9898 9899 switch (e.nodeName) { 9900 case 'TABLE': 9901 v = t.dom.getAttrib(e, 'border'); 9902 9903 if (!v || v == '0') { 9904 if (t.hasVisual) 9905 t.dom.addClass(e, s.visual_table_class); 9906 else 9907 t.dom.removeClass(e, s.visual_table_class); 9908 } 9909 9910 return; 9911 9912 case 'A': 9913 v = t.dom.getAttrib(e, 'name'); 9914 9915 if (v) { 9916 if (t.hasVisual) 9917 t.dom.addClass(e, 'mceItemAnchor'); 9918 else 9919 t.dom.removeClass(e, 'mceItemAnchor'); 9920 } 9921 9922 return; 9923 } 9924 }); 9925 9926 t.onVisualAid.dispatch(t, e, t.hasVisual); 9927 }, 9928 9929 remove : function() { 9930 var t = this, e = t.getContainer(); 9931 9932 t.removed = 1; // Cancels post remove event execution 9933 t.hide(); 9934 9935 t.execCallback('remove_instance_callback', t); 9936 t.onRemove.dispatch(t); 9937 9938 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command 9939 t.onExecCommand.listeners = []; 9940 9941 EditorManager.remove(t); 9942 DOM.remove(e); 9943 }, 9944 9945 destroy : function(s) { 9946 var t = this; 9947 9948 // One time is enough 9949 if (t.destroyed) 9950 return; 9951 9952 if (!s) { 9953 tinymce.removeUnload(t.destroy); 9954 tinyMCE.onBeforeUnload.remove(t._beforeUnload); 9955 9956 // Manual destroy 9957 if (t.theme && t.theme.destroy) 9958 t.theme.destroy(); 9959 9960 // Destroy controls, selection and dom 9961 t.controlManager.destroy(); 9962 t.selection.destroy(); 9963 t.dom.destroy(); 9964 9965 // Remove all events 9966 9967 // Don't clear the window or document if content editable 9968 // is enabled since other instances might still be present 9969 if (!t.settings.content_editable) { 9970 Event.clear(t.getWin()); 9971 Event.clear(t.getDoc()); 9972 } 9973 9974 Event.clear(t.getBody()); 9975 Event.clear(t.formElement); 9976 } 9977 9978 if (t.formElement) { 9979 t.formElement.submit = t.formElement._mceOldSubmit; 9980 t.formElement._mceOldSubmit = null; 9981 } 9982 9983 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null; 9984 9985 if (t.selection) 9986 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null; 9987 9988 t.destroyed = 1; 9989 }, 9990 9991 // Internal functions 9992 9993 _addEvents : function() { 9994 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset 9995 var t = this, i, s = t.settings, lo = { 9996 mouseup : 'onMouseUp', 9997 mousedown : 'onMouseDown', 9998 click : 'onClick', 9999 keyup : 'onKeyUp', 10000 keydown : 'onKeyDown', 10001 keypress : 'onKeyPress', 10002 submit : 'onSubmit', 10003 reset : 'onReset', 10004 contextmenu : 'onContextMenu', 10005 dblclick : 'onDblClick', 10006 paste : 'onPaste' // Doesn't work in all browsers yet 10007 }; 10008 10009 function eventHandler(e, o) { 10010 var ty = e.type; 10011 10012 // Don't fire events when it's removed 10013 if (t.removed) 10014 return; 10015 10016 // Generic event handler 10017 if (t.onEvent.dispatch(t, e, o) !== false) { 10018 // Specific event handler 10019 t[lo[e.fakeType || e.type]].dispatch(t, e, o); 10020 } 10021 }; 10022 10023 // Add DOM events 10024 each(lo, function(v, k) { 10025 switch (k) { 10026 case 'contextmenu': 10027 if (tinymce.isOpera) { 10028 // Fake contextmenu on Opera 10029 t.dom.bind(t.getBody(), 'mousedown', function(e) { 10030 if (e.ctrlKey) { 10031 e.fakeType = 'contextmenu'; 10032 eventHandler(e); 10033 } 10034 }); 10035 } else 10036 t.dom.bind(t.getBody(), k, eventHandler); 10037 break; 10038 10039 case 'paste': 10040 t.dom.bind(t.getBody(), k, function(e) { 10041 eventHandler(e); 10042 }); 10043 break; 10044 10045 case 'submit': 10046 case 'reset': 10047 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler); 10048 break; 10049 10050 default: 10051 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler); 10052 } 10053 }); 10054 10055 t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) { 10056 t.focus(true); 10057 }); 10058 10059 10060 // Fixes bug where a specified document_base_uri could result in broken images 10061 // This will also fix drag drop of images in Gecko 10062 if (tinymce.isGecko) { 10063 // Convert all images to absolute URLs 10064 /* t.onSetContent.add(function(ed, o) { 10065 each(ed.dom.select('img'), function(e) { 10066 var v; 10067 10068 if (v = e.getAttribute('mce_src')) 10069 e.src = t.documentBaseURI.toAbsolute(v); 10070 }) 10071 });*/ 10072 10073 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) { 10074 var v; 10075 10076 e = e.target; 10077 10078 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src'))) 10079 e.src = t.documentBaseURI.toAbsolute(v); 10080 }); 10081 } 10082 10083 // Set various midas options in Gecko 10084 if (isGecko) { 10085 function setOpts() { 10086 var t = this, d = t.getDoc(), s = t.settings; 10087 10088 if (isGecko && !s.readonly) { 10089 if (t._isHidden()) { 10090 try { 10091 if (!s.content_editable) 10092 d.designMode = 'On'; 10093 } catch (ex) { 10094 // Fails if it's hidden 10095 } 10096 } 10097 10098 try { 10099 // Try new Gecko method 10100 d.execCommand("styleWithCSS", 0, false); 10101 } catch (ex) { 10102 // Use old method 10103 if (!t._isHidden()) 10104 try {d.execCommand("useCSS", 0, true);} catch (ex) {} 10105 } 10106 10107 if (!s.table_inline_editing) 10108 try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {} 10109 10110 if (!s.object_resizing) 10111 try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {} 10112 } 10113 }; 10114 10115 t.onBeforeExecCommand.add(setOpts); 10116 t.onMouseDown.add(setOpts); 10117 } 10118 10119 // Add node change handlers 10120 t.onMouseUp.add(t.nodeChanged); 10121 t.onClick.add(t.nodeChanged); 10122 t.onKeyUp.add(function(ed, e) { 10123 var c = e.keyCode; 10124 10125 if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey) 10126 t.nodeChanged(); 10127 }); 10128 10129 // Add reset handler 10130 t.onReset.add(function() { 10131 t.setContent(t.startContent, {format : 'raw'}); 10132 }); 10133 10134 // Add shortcuts 10135 if (s.custom_shortcuts) { 10136 if (s.custom_undo_redo_keyboard_shortcuts) { 10137 t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo'); 10138 t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo'); 10139 } 10140 10141 // Add default shortcuts for gecko 10142 if (isGecko) { 10143 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold'); 10144 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic'); 10145 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline'); 10146 } 10147 10148 // BlockFormat shortcuts keys 10149 for (i=1; i<=6; i++) 10150 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']); 10151 10152 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']); 10153 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']); 10154 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']); 10155 10156 function find(e) { 10157 var v = null; 10158 10159 if (!e.altKey && !e.ctrlKey && !e.metaKey) 10160 return v; 10161 10162 each(t.shortcuts, function(o) { 10163 if (tinymce.isMac && o.ctrl != e.metaKey) 10164 return; 10165 else if (!tinymce.isMac && o.ctrl != e.ctrlKey) 10166 return; 10167 10168 if (o.alt != e.altKey) 10169 return; 10170 10171 if (o.shift != e.shiftKey) 10172 return; 10173 10174 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) { 10175 v = o; 10176 return false; 10177 } 10178 }); 10179 10180 return v; 10181 }; 10182 10183 t.onKeyUp.add(function(ed, e) { 10184 var o = find(e); 10185 10186 if (o) 10187 return Event.cancel(e); 10188 }); 10189 10190 t.onKeyPress.add(function(ed, e) { 10191 var o = find(e); 10192 10193 if (o) 10194 return Event.cancel(e); 10195 }); 10196 10197 t.onKeyDown.add(function(ed, e) { 10198 var o = find(e); 10199 10200 if (o) { 10201 o.func.call(o.scope); 10202 return Event.cancel(e); 10203 } 10204 }); 10205 } 10206 10207 if (tinymce.isIE) { 10208 // Fix so resize will only update the width and height attributes not the styles of an image 10209 // It will also block mceItemNoResize items 10210 t.dom.bind(t.getDoc(), 'controlselect', function(e) { 10211 var re = t.resizeInfo, cb; 10212 10213 e = e.target; 10214 10215 // Don't do this action for non image elements 10216 if (e.nodeName !== 'IMG') 10217 return; 10218 10219 if (re) 10220 t.dom.unbind(re.node, re.ev, re.cb); 10221 10222 if (!t.dom.hasClass(e, 'mceItemNoResize')) { 10223 ev = 'resizeend'; 10224 cb = t.dom.bind(e, ev, function(e) { 10225 var v; 10226 10227 e = e.target; 10228 10229 if (v = t.dom.getStyle(e, 'width')) { 10230 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, '')); 10231 t.dom.setStyle(e, 'width', ''); 10232 } 10233 10234 if (v = t.dom.getStyle(e, 'height')) { 10235 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, '')); 10236 t.dom.setStyle(e, 'height', ''); 10237 } 10238 }); 10239 } else { 10240 ev = 'resizestart'; 10241 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event); 10242 } 10243 10244 re = t.resizeInfo = { 10245 node : e, 10246 ev : ev, 10247 cb : cb 10248 }; 10249 }); 10250 10251 t.onKeyDown.add(function(ed, e) { 10252 switch (e.keyCode) { 10253 case 8: 10254 // Fix IE control + backspace browser bug 10255 if (t.selection.getRng().item) { 10256 t.selection.getRng().item(0).removeNode(); 10257 return Event.cancel(e); 10258 } 10259 } 10260 }); 10261 10262 /*if (t.dom.boxModel) { 10263 t.getBody().style.height = '100%'; 10264 10265 Event.add(t.getWin(), 'resize', function(e) { 10266 var docElm = t.getDoc().documentElement; 10267 10268 docElm.style.height = (docElm.offsetHeight - 10) + 'px'; 10269 }); 10270 }*/ 10271 } 10272 10273 if (tinymce.isOpera) { 10274 t.onClick.add(function(ed, e) { 10275 Event.prevent(e); 10276 }); 10277 } 10278 10279 // Add custom undo/redo handlers 10280 if (s.custom_undo_redo) { 10281 function addUndo() { 10282 t.undoManager.typing = 0; 10283 t.undoManager.add(); 10284 }; 10285 10286 // Add undo level on editor blur 10287 if (tinymce.isIE) { 10288 t.dom.bind(t.getWin(), 'blur', function(e) { 10289 var n; 10290 10291 // Check added for fullscreen bug 10292 if (t.selection) { 10293 n = t.selection.getNode(); 10294 10295 // Add undo level is selection was lost to another document 10296 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc()) 10297 addUndo(); 10298 } 10299 }); 10300 } else { 10301 t.dom.bind(t.getDoc(), 'blur', function() { 10302 if (t.selection && !t.removed) 10303 addUndo(); 10304 }); 10305 } 10306 10307 t.onMouseDown.add(addUndo); 10308 10309 t.onKeyUp.add(function(ed, e) { 10310 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) { 10311 t.undoManager.typing = 0; 10312 t.undoManager.add(); 10313 } 10314 }); 10315 10316 t.onKeyDown.add(function(ed, e) { 10317 // Is caracter positon keys 10318 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) { 10319 if (t.undoManager.typing) { 10320 t.undoManager.add(); 10321 t.undoManager.typing = 0; 10322 } 10323 10324 return; 10325 } 10326 10327 if (!t.undoManager.typing) { 10328 t.undoManager.add(); 10329 t.undoManager.typing = 1; 10330 } 10331 }); 10332 } 10333 }, 10334 10335 _convertInlineElements : function() { 10336 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp; 10337 10338 function convert(ed, o) { 10339 if (!s.inline_styles) 10340 return; 10341 10342 if (o.get) { 10343 each(t.dom.select('table,u,strike', o.node), function(n) { 10344 switch (n.nodeName) { 10345 case 'TABLE': 10346 if (v = dom.getAttrib(n, 'height')) { 10347 dom.setStyle(n, 'height', v); 10348 dom.setAttrib(n, 'height', ''); 10349 } 10350 break; 10351 10352 case 'U': 10353 case 'STRIKE': 10354 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')}); 10355 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through'; 10356 dom.setAttrib(n, 'mce_style', ''); 10357 dom.setAttrib(n, 'mce_name', 'span'); 10358 break; 10359 } 10360 }); 10361 } else if (o.set) { 10362 each(t.dom.select('table,span', o.node).reverse(), function(n) { 10363 if (n.nodeName == 'TABLE') { 10364 if (v = dom.getStyle(n, 'height')) 10365 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, '')); 10366 } else { 10367 // Convert spans to elements 10368 if (n.style.textDecoration == 'underline') 10369 na = 'u'; 10370 else if (n.style.textDecoration == 'line-through') 10371 na = 'strike'; 10372 else 10373 na = ''; 10374 10375 if (na) { 10376 n.style.textDecoration = ''; 10377 dom.setAttrib(n, 'mce_style', ''); 10378 10379 e = dom.create(na, { 10380 style : dom.getAttrib(n, 'style') 10381 }); 10382 10383 dom.replace(e, n, 1); 10384 } 10385 } 10386 }); 10387 } 10388 }; 10389 10390 t.onPreProcess.add(convert); 10391 10392 if (!s.cleanup_on_startup) { 10393 t.onSetContent.add(function(ed, o) { 10394 if (o.initial) 10395 convert(t, {node : t.getBody(), set : 1}); 10396 }); 10397 } 10398 }, 10399 10400 _convertFonts : function() { 10401 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl; 10402 10403 // No need 10404 if (!s.inline_styles) 10405 return; 10406 10407 // Font pt values and font size names 10408 fz = [8, 10, 12, 14, 18, 24, 36]; 10409 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large']; 10410 10411 if (sl = s.font_size_style_values) 10412 sl = explode(sl); 10413 10414 if (cl = s.font_size_classes) 10415 cl = explode(cl); 10416 10417 function process(no) { 10418 var n, sp, nl, x; 10419 10420 // Keep unit tests happy 10421 if (!s.inline_styles) 10422 return; 10423 10424 nl = t.dom.select('font', no); 10425 for (x = nl.length - 1; x >= 0; x--) { 10426 n = nl[x]; 10427 10428 sp = dom.create('span', { 10429 style : dom.getAttrib(n, 'style'), 10430 'class' : dom.getAttrib(n, 'class') 10431 }); 10432 10433 dom.setStyles(sp, { 10434 fontFamily : dom.getAttrib(n, 'face'), 10435 color : dom.getAttrib(n, 'color'), 10436 backgroundColor : n.style.backgroundColor 10437 }); 10438 10439 if (n.size) { 10440 if (sl) 10441 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]); 10442 else 10443 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]); 10444 } 10445 10446 dom.setAttrib(sp, 'mce_style', ''); 10447 dom.replace(sp, n, 1); 10448 } 10449 }; 10450 10451 // Run on cleanup 10452 t.onPreProcess.add(function(ed, o) { 10453 if (o.get) 10454 process(o.node); 10455 }); 10456 10457 t.onSetContent.add(function(ed, o) { 10458 if (o.initial) 10459 process(o.node); 10460 }); 10461 }, 10462 10463 _isHidden : function() { 10464 var s; 10465 10466 if (!isGecko) 10467 return 0; 10468 10469 // Weird, wheres that cursor selection? 10470 s = this.selection.getSel(); 10471 return (!s || !s.rangeCount || s.rangeCount == 0); 10472 }, 10473 10474 // Fix for bug #1867292 10475 _fixNesting : function(s) { 10476 var d = [], i; 10477 10478 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) { 10479 var e; 10480 10481 // Handle end element 10482 if (b === '/') { 10483 if (!d.length) 10484 return ''; 10485 10486 if (c !== d[d.length - 1].tag) { 10487 for (i=d.length - 1; i>=0; i--) { 10488 if (d[i].tag === c) { 10489 d[i].close = 1; 10490 break; 10491 } 10492 } 10493 10494 return ''; 10495 } else { 10496 d.pop(); 10497 10498 if (d.length && d[d.length - 1].close) { 10499 a = a + '</' + d[d.length - 1].tag + '>'; 10500 d.pop(); 10501 } 10502 } 10503 } else { 10504 // Ignore these 10505 if (/^(br|hr|input|meta|img|link|param)$/i.test(c)) 10506 return a; 10507 10508 // Ignore closed ones 10509 if (/\/>$/.test(a)) 10510 return a; 10511 10512 d.push({tag : c}); // Push start element 10513 } 10514 10515 return a; 10516 }); 10517 10518 // End all open tags 10519 for (i=d.length - 1; i>=0; i--) 10520 s += '</' + d[i].tag + '>'; 10521 10522 return s; 10523 } 10524 }); 10525 })(tinymce); 10526 (function(tinymce) { 10527 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit; 10528 10529 tinymce.create('tinymce.EditorCommands', { 10530 EditorCommands : function(ed) { 10531 this.editor = ed; 10532 }, 10533 10534 execCommand : function(cmd, ui, val) { 10535 var t = this, ed = t.editor, f; 10536 10537 switch (cmd) { 10538 // Ignore these 10539 case 'mceResetDesignMode': 10540 case 'mceBeginUndoLevel': 10541 return true; 10542 10543 // Ignore these 10544 case 'unlink': 10545 t.UnLink(); 10546 return true; 10547 10548 // Bundle these together 10549 case 'JustifyLeft': 10550 case 'JustifyCenter': 10551 case 'JustifyRight': 10552 case 'JustifyFull': 10553 t.mceJustify(cmd, cmd.substring(7).toLowerCase()); 10554 return true; 10555 10556 default: 10557 f = this[cmd]; 10558 10559 if (f) { 10560 f.call(this, ui, val); 10561 return true; 10562 } 10563 } 10564 10565 return false; 10566 }, 10567 10568 Indent : function() { 10569 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu; 10570 10571 // Setup indent level 10572 iv = ed.settings.indentation; 10573 iu = /[a-z%]+$/i.exec(iv); 10574 iv = parseInt(iv); 10575 10576 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) { 10577 each(s.getSelectedBlocks(), function(e) { 10578 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu); 10579 }); 10580 10581 return; 10582 } 10583 10584 ed.getDoc().execCommand('Indent', false, null); 10585 10586 if (isIE) { 10587 d.getParent(s.getNode(), function(n) { 10588 if (n.nodeName == 'BLOCKQUOTE') { 10589 n.dir = n.style.cssText = ''; 10590 } 10591 }); 10592 } 10593 }, 10594 10595 Outdent : function() { 10596 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu; 10597 10598 // Setup indent level 10599 iv = ed.settings.indentation; 10600 iu = /[a-z%]+$/i.exec(iv); 10601 iv = parseInt(iv); 10602 10603 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) { 10604 each(s.getSelectedBlocks(), function(e) { 10605 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv); 10606 d.setStyle(e, 'paddingLeft', v ? v + iu : ''); 10607 }); 10608 10609 return; 10610 } 10611 10612 ed.getDoc().execCommand('Outdent', false, null); 10613 }, 10614 10615 /* 10616 mceSetAttribute : function(u, v) { 10617 var ed = this.editor, d = ed.dom, e; 10618 10619 if (e = d.getParent(ed.selection.getNode(), d.isBlock)) 10620 d.setAttrib(e, v.name, v.value); 10621 }, 10622 */ 10623 mceSetContent : function(u, v) { 10624 this.editor.setContent(v); 10625 }, 10626 10627 mceToggleVisualAid : function() { 10628 var ed = this.editor; 10629 10630 ed.hasVisual = !ed.hasVisual; 10631 ed.addVisual(); 10632 }, 10633 10634 mceReplaceContent : function(u, v) { 10635 var s = this.editor.selection; 10636 10637 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'}))); 10638 }, 10639 10640 mceInsertLink : function(u, v) { 10641 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'a'); 10642 10643 if (tinymce.is(v, 'string')) 10644 v = {href : v}; 10645 10646 function set(e) { 10647 each(v, function(v, k) { 10648 ed.dom.setAttrib(e, k, v); 10649 }); 10650 }; 10651 10652 if (!e) { 10653 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);'); 10654 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) { 10655 set(e); 10656 }); 10657 } else { 10658 if (v.href) 10659 set(e); 10660 else 10661 ed.dom.remove(e, 1); 10662 } 10663 }, 10664 10665 UnLink : function() { 10666 var ed = this.editor, s = ed.selection; 10667 10668 if (s.isCollapsed()) 10669 s.select(s.getNode()); 10670 10671 ed.getDoc().execCommand('unlink', false, null); 10672 s.collapse(0); 10673 }, 10674 10675 FontName : function(u, v) { 10676 var t = this, ed = t.editor, s = ed.selection, e; 10677 10678 if (!v) { 10679 if (s.isCollapsed()) 10680 s.select(s.getNode()); 10681 } else { 10682 if (ed.settings.convert_fonts_to_spans) 10683 t._applyInlineStyle('span', {style : {fontFamily : v}}); 10684 else 10685 ed.getDoc().execCommand('FontName', false, v); 10686 } 10687 }, 10688 10689 FontSize : function(u, v) { 10690 var ed = this.editor, s = ed.settings, fc, fs; 10691 10692 // Use style options instead 10693 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) { 10694 fs = tinymce.explode(s.font_size_style_values); 10695 fc = tinymce.explode(s.font_size_classes); 10696 10697 if (fc) 10698 v = fc[v - 1] || v; 10699 else 10700 v = fs[v - 1] || v; 10701 } 10702 10703 if (v >= 1 && v <= 7) 10704 ed.getDoc().execCommand('FontSize', false, v); 10705 else 10706 this._applyInlineStyle('span', {style : {fontSize : v}}); 10707 }, 10708 10709 queryCommandValue : function(c) { 10710 var f = this['queryValue' + c]; 10711 10712 if (f) 10713 return f.call(this, c); 10714 10715 return false; 10716 }, 10717 10718 queryCommandState : function(cmd) { 10719 var f; 10720 10721 switch (cmd) { 10722 // Bundle these together 10723 case 'JustifyLeft': 10724 case 'JustifyCenter': 10725 case 'JustifyRight': 10726 case 'JustifyFull': 10727 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase()); 10728 10729 default: 10730 if (f = this['queryState' + cmd]) 10731 return f.call(this, cmd); 10732 } 10733 10734 return -1; 10735 }, 10736 10737 _queryState : function(c) { 10738 try { 10739 return this.editor.getDoc().queryCommandState(c); 10740 } catch (ex) { 10741 // Ignore exception 10742 } 10743 }, 10744 10745 _queryVal : function(c) { 10746 try { 10747 return this.editor.getDoc().queryCommandValue(c); 10748 } catch (ex) { 10749 // Ignore exception 10750 } 10751 }, 10752 10753 queryValueFontSize : function() { 10754 var ed = this.editor, v = 0, p; 10755 10756 if (p = ed.dom.getParent(ed.selection.getNode(), 'span')) 10757 v = p.style.fontSize; 10758 10759 if (!v && (isOpera || isWebKit)) { 10760 if (p = ed.dom.getParent(ed.selection.getNode(), 'font')) 10761 v = p.size; 10762 10763 return v; 10764 } 10765 10766 return v || this._queryVal('FontSize'); 10767 }, 10768 10769 queryValueFontName : function() { 10770 var ed = this.editor, v = 0, p; 10771 10772 if (p = ed.dom.getParent(ed.selection.getNode(), 'font')) 10773 v = p.face; 10774 10775 if (p = ed.dom.getParent(ed.selection.getNode(), 'span')) 10776 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); 10777 10778 if (!v) 10779 v = this._queryVal('FontName'); 10780 10781 return v; 10782 }, 10783 10784 mceJustify : function(c, v) { 10785 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm; 10786 10787 if (ed.settings.inline_styles && this.queryStateJustify(c, v)) 10788 rm = 1; 10789 10790 bl = dom.getParent(n, ed.dom.isBlock); 10791 10792 if (nn == 'IMG') { 10793 if (v == 'full') 10794 return; 10795 10796 if (rm) { 10797 if (v == 'center') 10798 dom.setStyle(bl || n.parentNode, 'textAlign', ''); 10799 10800 dom.setStyle(n, 'float', ''); 10801 this.mceRepaint(); 10802 return; 10803 } 10804 10805 if (v == 'center') { 10806 // Do not change table elements 10807 if (bl && /^(TD|TH)$/.test(bl.nodeName)) 10808 bl = 0; 10809 10810 if (!bl || bl.childNodes.length > 1) { 10811 nb = dom.create('p'); 10812 nb.appendChild(n.cloneNode(false)); 10813 10814 if (bl) 10815 dom.insertAfter(nb, bl); 10816 else 10817 dom.insertAfter(nb, n); 10818 10819 dom.remove(n); 10820 n = nb.firstChild; 10821 bl = nb; 10822 } 10823 10824 dom.setStyle(bl, 'textAlign', v); 10825 dom.setStyle(n, 'float', ''); 10826 } else { 10827 dom.setStyle(n, 'float', v); 10828 dom.setStyle(bl || n.parentNode, 'textAlign', ''); 10829 } 10830 10831 this.mceRepaint(); 10832 return; 10833 } 10834 10835 // Handle the alignment outselfs, less quirks in all browsers 10836 if (ed.settings.inline_styles && ed.settings.forced_root_block) { 10837 if (rm) 10838 v = ''; 10839 10840 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) { 10841 dom.setAttrib(e, 'align', ''); 10842 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v); 10843 }); 10844 10845 return; 10846 } else if (!rm) 10847 ed.getDoc().execCommand(c, false, null); 10848 10849 if (ed.settings.inline_styles) { 10850 if (rm) { 10851 dom.getParent(ed.selection.getNode(), function(n) { 10852 if (n.style && n.style.textAlign) 10853 dom.setStyle(n, 'textAlign', ''); 10854 }); 10855 10856 return; 10857 } 10858 10859 each(dom.select('*'), function(n) { 10860 var v = n.align; 10861 10862 if (v) { 10863 if (v == 'full') 10864 v = 'justify'; 10865 10866 dom.setStyle(n, 'textAlign', v); 10867 dom.setAttrib(n, 'align', ''); 10868 } 10869 }); 10870 } 10871 }, 10872 10873 mceSetCSSClass : function(u, v) { 10874 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v}); 10875 }, 10876 10877 getSelectedElement : function() { 10878 var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re; 10879 10880 if (se.isCollapsed() || r.item) 10881 return se.getNode(); 10882 10883 // Setup regexp 10884 re = ed.settings.merge_styles_invalid_parents; 10885 if (tinymce.is(re, 'string')) 10886 re = new RegExp(re, 'i'); 10887 10888 if (isIE) { 10889 r1 = r.duplicate(); 10890 r1.collapse(true); 10891 sc = r1.parentElement(); 10892 10893 r2 = r.duplicate(); 10894 r2.collapse(false); 10895 ec = r2.parentElement(); 10896 10897 if (sc != ec) { 10898 r1.move('character', 1); 10899 sc = r1.parentElement(); 10900 } 10901 10902 if (sc == ec) { 10903 r1 = r.duplicate(); 10904 r1.moveToElementText(sc); 10905 10906 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0) 10907 return re && re.test(sc.nodeName) ? null : sc; 10908 } 10909 } else { 10910 function getParent(n) { 10911 return dom.getParent(n, '*'); 10912 }; 10913 10914 sc = r.startContainer; 10915 ec = r.endContainer; 10916 so = r.startOffset; 10917 eo = r.endOffset; 10918 10919 if (!r.collapsed) { 10920 if (sc == ec) { 10921 if (so - eo < 2) { 10922 if (sc.hasChildNodes()) { 10923 sp = sc.childNodes[so]; 10924 return re && re.test(sp.nodeName) ? null : sp; 10925 } 10926 } 10927 } 10928 } 10929 10930 if (sc.nodeType != 3 || ec.nodeType != 3) 10931 return null; 10932 10933 if (so == 0) { 10934 sp = getParent(sc); 10935 10936 if (sp && sp.firstChild != sc) 10937 sp = null; 10938 } 10939 10940 if (so == sc.nodeValue.length) { 10941 e = sc.nextSibling; 10942 10943 if (e && e.nodeType == 1) 10944 sp = sc.nextSibling; 10945 } 10946 10947 if (eo == 0) { 10948 e = ec.previousSibling; 10949 10950 if (e && e.nodeType == 1) 10951 ep = e; 10952 } 10953 10954 if (eo == ec.nodeValue.length) { 10955 ep = getParent(ec); 10956 10957 if (ep && ep.lastChild != ec) 10958 ep = null; 10959 } 10960 10961 // Same element 10962 if (sp == ep) 10963 return re && sp && re.test(sp.nodeName) ? null : sp; 10964 } 10965 10966 return null; 10967 }, 10968 10969 mceSetStyleInfo : function(u, v) { 10970 var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re; 10971 10972 function set(n, e) { 10973 if (n.nodeType == 1) { 10974 switch (v.command) { 10975 case 'setattrib': 10976 return dom.setAttrib(n, v.name, v.value); 10977 10978 case 'setstyle': 10979 return dom.setStyle(n, v.name, v.value); 10980 10981 case 'removeformat': 10982 return dom.setAttrib(n, 'class', ''); 10983 } 10984 } 10985 }; 10986 10987 // Setup regexp 10988 re = ed.settings.merge_styles_invalid_parents; 10989 if (tinymce.is(re, 'string')) 10990 re = new RegExp(re, 'i'); 10991 10992 // Set style info on selected element 10993 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers) 10994 set(e, 1); 10995 else { 10996 // Generate wrappers and set styles on them 10997 d.execCommand('FontName', false, '__'); 10998 each(dom.select('span,font'), function(n) { 10999 var sp, e; 11000 11001 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') { 11002 sp = dom.create(nn, {mce_new : '1'}); 11003 11004 set(sp); 11005 11006 each (n.childNodes, function(n) { 11007 sp.appendChild(n.cloneNode(true)); 11008 }); 11009 11010 dom.replace(sp, n); 11011 } 11012 }); 11013 } 11014 11015 // Remove wrappers inside new ones 11016 each(dom.select(nn).reverse(), function(n) { 11017 var p = n.parentNode; 11018 11019 // Check if it's an old span in a new wrapper 11020 if (!dom.getAttrib(n, 'mce_new')) { 11021 // Find new wrapper 11022 p = dom.getParent(n, '*[mce_new]'); 11023 11024 if (p) 11025 dom.remove(n, 1); 11026 } 11027 }); 11028 11029 // Merge wrappers with parent wrappers 11030 each(dom.select(nn).reverse(), function(n) { 11031 var p = n.parentNode; 11032 11033 if (!p || !dom.getAttrib(n, 'mce_new')) 11034 return; 11035 11036 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN') 11037 return; 11038 11039 // Has parent of the same type and only child 11040 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1) 11041 return dom.remove(p, 1); 11042 11043 // Has parent that is more suitable to have the class and only child 11044 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) { 11045 set(p); // Set style info on parent instead 11046 dom.setAttrib(n, 'class', ''); 11047 } 11048 }); 11049 11050 // Remove empty wrappers 11051 each(dom.select(nn).reverse(), function(n) { 11052 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) { 11053 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style')) 11054 return dom.remove(n, 1); 11055 11056 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker 11057 } 11058 }); 11059 11060 s.moveToBookmark(b); 11061 }, 11062 11063 queryStateJustify : function(c, v) { 11064 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom; 11065 11066 if (n && n.nodeName == 'IMG') { 11067 if (dom.getStyle(n, 'float') == v) 11068 return 1; 11069 11070 return n.parentNode.style.textAlign == v; 11071 } 11072 11073 n = dom.getParent(ed.selection.getStart(), function(n) { 11074 return n.nodeType == 1 && n.style.textAlign; 11075 }); 11076 11077 if (v == 'full') 11078 v = 'justify'; 11079 11080 if (ed.settings.inline_styles) 11081 return (n && n.style.textAlign == v); 11082 11083 return this._queryState(c); 11084 }, 11085 11086 ForeColor : function(ui, v) { 11087 var ed = this.editor; 11088 11089 if (ed.settings.convert_fonts_to_spans) { 11090 this._applyInlineStyle('span', {style : {color : v}}); 11091 return; 11092 } else 11093 ed.getDoc().execCommand('ForeColor', false, v); 11094 }, 11095 11096 HiliteColor : function(ui, val) { 11097 var t = this, ed = t.editor, d = ed.getDoc(); 11098 11099 if (ed.settings.convert_fonts_to_spans) { 11100 this._applyInlineStyle('span', {style : {backgroundColor : val}}); 11101 return; 11102 } 11103 11104 function set(s) { 11105 if (!isGecko) 11106 return; 11107 11108 try { 11109 // Try new Gecko method 11110 d.execCommand("styleWithCSS", 0, s); 11111 } catch (ex) { 11112 // Use old 11113 d.execCommand("useCSS", 0, !s); 11114 } 11115 }; 11116 11117 if (isGecko || isOpera) { 11118 set(true); 11119 d.execCommand('hilitecolor', false, val); 11120 set(false); 11121 } else 11122 d.execCommand('BackColor', false, val); 11123 }, 11124 11125 FormatBlock : function(ui, val) { 11126 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b; 11127 11128 function isBlock(n) { 11129 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName); 11130 }; 11131 11132 bl = dom.getParent(s.getNode(), function(n) { 11133 return isBlock(n); 11134 }); 11135 11136 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div> 11137 // FF and Opera doesn't change parent DIV elements if you switch format 11138 if (bl) { 11139 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') { 11140 // Rename block element 11141 nb = ed.dom.create(val); 11142 11143 each(dom.getAttribs(bl), function(v) { 11144 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName)); 11145 }); 11146 11147 b = s.getBookmark(); 11148 dom.replace(nb, bl, 1); 11149 s.moveToBookmark(b); 11150 ed.nodeChanged(); 11151 return; 11152 } 11153 } 11154 11155 val = ed.settings.forced_root_block ? (val || '<p>') : val; 11156 11157 if (val.indexOf('<') == -1) 11158 val = '<' + val + '>'; 11159 11160 if (tinymce.isGecko) 11161 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1'); 11162 11163 ed.getDoc().execCommand('FormatBlock', false, val); 11164 }, 11165 11166 mceCleanup : function() { 11167 var ed = this.editor, s = ed.selection, b = s.getBookmark(); 11168 ed.setContent(ed.getContent()); 11169 s.moveToBookmark(b); 11170 }, 11171 11172 mceRemoveNode : function(ui, val) { 11173 var ed = this.editor, s = ed.selection, b, n = val || s.getNode(); 11174 11175 // Make sure that the body node isn't removed 11176 if (n == ed.getBody()) 11177 return; 11178 11179 b = s.getBookmark(); 11180 ed.dom.remove(n, 1); 11181 s.moveToBookmark(b); 11182 ed.nodeChanged(); 11183 }, 11184 11185 mceSelectNodeDepth : function(ui, val) { 11186 var ed = this.editor, s = ed.selection, c = 0; 11187 11188 ed.dom.getParent(s.getNode(), function(n) { 11189 if (n.nodeType == 1 && c++ == val) { 11190 s.select(n); 11191 ed.nodeChanged(); 11192 return false; 11193 } 11194 }, ed.getBody()); 11195 }, 11196 11197 mceSelectNode : function(u, v) { 11198 this.editor.selection.select(v); 11199 }, 11200 11201 mceInsertContent : function(ui, val) { 11202 this.editor.selection.setContent(val); 11203 }, 11204 11205 mceInsertRawHTML : function(ui, val) { 11206 var ed = this.editor; 11207 11208 ed.selection.setContent('tiny_mce_marker'); 11209 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val)); 11210 }, 11211 11212 mceRepaint : function() { 11213 var s, b, e = this.editor; 11214 11215 if (tinymce.isGecko) { 11216 try { 11217 s = e.selection; 11218 b = s.getBookmark(true); 11219 11220 if (s.getSel()) 11221 s.getSel().selectAllChildren(e.getBody()); 11222 11223 s.collapse(true); 11224 s.moveToBookmark(b); 11225 } catch (ex) { 11226 // Ignore 11227 } 11228 } 11229 }, 11230 11231 queryStateUnderline : function() { 11232 var ed = this.editor, n = ed.selection.getNode(); 11233 11234 if (n && n.nodeName == 'A') 11235 return false; 11236 11237 return this._queryState('Underline'); 11238 }, 11239 11240 queryStateOutdent : function() { 11241 var ed = this.editor, n; 11242 11243 if (ed.settings.inline_styles) { 11244 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0) 11245 return true; 11246 11247 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0) 11248 return true; 11249 } 11250 11251 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE')); 11252 }, 11253 11254 queryStateInsertUnorderedList : function() { 11255 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL'); 11256 }, 11257 11258 queryStateInsertOrderedList : function() { 11259 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL'); 11260 }, 11261 11262 queryStatemceBlockQuote : function() { 11263 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';}); 11264 }, 11265 11266 _applyInlineStyle : function(na, at, op) { 11267 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found; 11268 11269 na = na.toUpperCase(); 11270 11271 if (op && op.check_classes && at['class']) 11272 op.check_classes.push(at['class']); 11273 11274 function removeEmpty() { 11275 each(dom.select(na).reverse(), function(n) { 11276 var c = 0; 11277 11278 // Check if there is any attributes 11279 each(dom.getAttribs(n), function(an) { 11280 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') { 11281 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName)); 11282 c++; 11283 } 11284 }); 11285 11286 // No attributes then remove the element and keep the children 11287 if (c == 0) 11288 dom.remove(n, 1); 11289 }); 11290 }; 11291 11292 function replaceFonts() { 11293 var bm; 11294 11295 each(dom.select('span,font'), function(n) { 11296 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') { 11297 if (!bm) 11298 bm = ed.selection.getBookmark(); 11299 11300 at._mce_new = '1'; 11301 dom.replace(dom.create(na, at), n, 1); 11302 } 11303 }); 11304 11305 // Remove redundant elements 11306 each(dom.select(na + '[_mce_new]'), function(n) { 11307 function removeStyle(n) { 11308 if (n.nodeType == 1) { 11309 each(at.style, function(v, k) { 11310 dom.setStyle(n, k, ''); 11311 }); 11312 11313 // Remove spans with the same class or marked classes 11314 if (at['class'] && n.className && op) { 11315 each(op.check_classes, function(c) { 11316 if (dom.hasClass(n, c)) 11317 dom.removeClass(n, c); 11318 }); 11319 } 11320 } 11321 }; 11322 11323 // Remove specified style information from child elements 11324 each(dom.select(na, n), removeStyle); 11325 11326 // Remove the specified style information on parent if current node is only child (IE) 11327 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1) 11328 removeStyle(n.parentNode); 11329 11330 // Remove the child elements style info if a parent already has it 11331 dom.getParent(n.parentNode, function(pn) { 11332 if (pn.nodeType == 1) { 11333 if (at.style) { 11334 each(at.style, function(v, k) { 11335 var sv; 11336 11337 if (!lo[k] && (sv = dom.getStyle(pn, k))) { 11338 if (sv === v) 11339 dom.setStyle(n, k, ''); 11340 11341 lo[k] = 1; 11342 } 11343 }); 11344 } 11345 11346 // Remove spans with the same class or marked classes 11347 if (at['class'] && pn.className && op) { 11348 each(op.check_classes, function(c) { 11349 if (dom.hasClass(pn, c)) 11350 dom.removeClass(n, c); 11351 }); 11352 } 11353 } 11354 11355 return false; 11356 }); 11357 11358 n.removeAttribute('_mce_new'); 11359 }); 11360 11361 removeEmpty(); 11362 ed.selection.moveToBookmark(bm); 11363 11364 return !!bm; 11365 }; 11366 11367 // Create inline elements 11368 ed.focus(); 11369 ed.getDoc().execCommand('FontName', false, 'mceinline'); 11370 replaceFonts(); 11371 11372 if (kh = t._applyInlineStyle.keyhandler) { 11373 ed.onKeyUp.remove(kh); 11374 ed.onKeyPress.remove(kh); 11375 ed.onKeyDown.remove(kh); 11376 ed.onSetContent.remove(t._applyInlineStyle.chandler); 11377 } 11378 11379 if (ed.selection.isCollapsed()) { 11380 // IE will format the current word so this code can't be executed on that browser 11381 if (!isIE) { 11382 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) { 11383 each(at.style, function(v, k) { 11384 var kv; 11385 11386 if (kv = dom.getStyle(n, k)) { 11387 if (kv == v) { 11388 dom.setStyle(n, k, ''); 11389 found = 2; 11390 return false; 11391 } 11392 11393 found = 1; 11394 return false; 11395 } 11396 }); 11397 11398 if (found) 11399 return false; 11400 }); 11401 11402 if (found == 2) { 11403 bm = ed.selection.getBookmark(); 11404 11405 removeEmpty(); 11406 11407 ed.selection.moveToBookmark(bm); 11408 11409 // Node change needs to be detached since the onselect event 11410 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix! 11411 window.setTimeout(function() { 11412 ed.nodeChanged(); 11413 }, 1); 11414 11415 return; 11416 } 11417 } 11418 11419 // Start collecting styles 11420 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style); 11421 11422 t._applyInlineStyle.chandler = ed.onSetContent.add(function() { 11423 delete t._pendingStyles; 11424 }); 11425 11426 t._applyInlineStyle.keyhandler = kh = function(e) { 11427 // Use pending styles 11428 if (t._pendingStyles) { 11429 at.style = t._pendingStyles; 11430 delete t._pendingStyles; 11431 } 11432 11433 if (replaceFonts()) { 11434 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler); 11435 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler); 11436 } 11437 11438 if (e.type == 'keyup') 11439 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler); 11440 }; 11441 11442 ed.onKeyDown.add(kh); 11443 ed.onKeyPress.add(kh); 11444 ed.onKeyUp.add(kh); 11445 } else 11446 t._pendingStyles = 0; 11447 } 11448 }); 11449 })(tinymce);(function(tinymce) { 11450 tinymce.create('tinymce.UndoManager', { 11451 index : 0, 11452 data : null, 11453 typing : 0, 11454 11455 UndoManager : function(ed) { 11456 var t = this, Dispatcher = tinymce.util.Dispatcher; 11457 11458 t.editor = ed; 11459 t.data = []; 11460 t.onAdd = new Dispatcher(this); 11461 t.onUndo = new Dispatcher(this); 11462 t.onRedo = new Dispatcher(this); 11463 }, 11464 11465 add : function(l) { 11466 var t = this, i, ed = t.editor, b, s = ed.settings, la; 11467 11468 l = l || {}; 11469 l.content = l.content || ed.getContent({format : 'raw', no_events : 1}); 11470 11471 // Add undo level if needed 11472 l.content = l.content.replace(/^\s*|\s*$/g, ''); 11473 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index]; 11474 if (!l.initial && la && l.content == la.content) 11475 return null; 11476 11477 // Time to compress 11478 if (s.custom_undo_redo_levels) { 11479 if (t.data.length > s.custom_undo_redo_levels) { 11480 for (i = 0; i < t.data.length - 1; i++) 11481 t.data[i] = t.data[i + 1]; 11482 11483 t.data.length--; 11484 t.index = t.data.length; 11485 } 11486 } 11487 11488 if (s.custom_undo_redo_restore_selection && !l.initial) 11489 l.bookmark = b = l.bookmark || ed.selection.getBookmark(); 11490 11491 if (t.index < t.data.length) 11492 t.index++; 11493 11494 // Only initial marked undo levels should be allowed as first item 11495 // This to workaround a bug with Firefox and the blur event 11496 if (t.data.length === 0 && !l.initial) 11497 return null; 11498 11499 // Add level 11500 t.data.length = t.index + 1; 11501 t.data[t.index++] = l; 11502 11503 if (l.initial) 11504 t.index = 0; 11505 11506 // Set initial bookmark use first real undo level 11507 if (t.data.length == 2 && t.data[0].initial) 11508 t.data[0].bookmark = b; 11509 11510 t.onAdd.dispatch(t, l); 11511 ed.isNotDirty = 0; 11512 11513 //console.dir(t.data); 11514 11515 return l; 11516 }, 11517 11518 undo : function() { 11519 var t = this, ed = t.editor, l = l, i; 11520 11521 if (t.typing) { 11522 t.add(); 11523 t.typing = 0; 11524 } 11525 11526 if (t.index > 0) { 11527 // If undo on last index then take snapshot 11528 if (t.index == t.data.length && t.index > 1) { 11529 i = t.index; 11530 t.typing = 0; 11531 11532 if (!t.add()) 11533 t.index = i; 11534 11535 --t.index; 11536 } 11537 11538 l = t.data[--t.index]; 11539 ed.setContent(l.content, {format : 'raw'}); 11540 ed.selection.moveToBookmark(l.bookmark); 11541 11542 t.onUndo.dispatch(t, l); 11543 } 11544 11545 return l; 11546 }, 11547 11548 redo : function() { 11549 var t = this, ed = t.editor, l = null; 11550 11551 if (t.index < t.data.length - 1) { 11552 l = t.data[++t.index]; 11553 ed.setContent(l.content, {format : 'raw'}); 11554 ed.selection.moveToBookmark(l.bookmark); 11555 11556 t.onRedo.dispatch(t, l); 11557 } 11558 11559 return l; 11560 }, 11561 11562 clear : function() { 11563 var t = this; 11564 11565 t.data = []; 11566 t.index = 0; 11567 t.typing = 0; 11568 t.add({initial : true}); 11569 }, 11570 11571 hasUndo : function() { 11572 return this.index != 0 || this.typing; 11573 }, 11574 11575 hasRedo : function() { 11576 return this.index < this.data.length - 1; 11577 } 11578 }); 11579 })(tinymce); 11580 (function(tinymce) { 11581 // Shorten names 11582 var Event, isIE, isGecko, isOpera, each, extend; 11583 11584 Event = tinymce.dom.Event; 11585 isIE = tinymce.isIE; 11586 isGecko = tinymce.isGecko; 11587 isOpera = tinymce.isOpera; 11588 each = tinymce.each; 11589 extend = tinymce.extend; 11590 11591 // Checks if the selection/caret is at the end of the specified block element 11592 function isAtEnd(rng, par) { 11593 var rng2 = par.ownerDocument.createRange(); 11594 11595 rng2.setStart(rng.endContainer, rng.endOffset); 11596 rng2.setEndAfter(par); 11597 11598 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element 11599 return rng2.cloneContents().textContent.length == 0; 11600 }; 11601 11602 function isEmpty(n) { 11603 n = n.innerHTML; 11604 11605 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars 11606 n = n.replace(/<[^>]+>/g, ''); // Remove all tags 11607 11608 return n.replace(/[ \t\r\n]+/g, '') == ''; 11609 }; 11610 11611 tinymce.create('tinymce.ForceBlocks', { 11612 ForceBlocks : function(ed) { 11613 var t = this, s = ed.settings, elm; 11614 11615 t.editor = ed; 11616 t.dom = ed.dom; 11617 elm = (s.forced_root_block || 'p').toLowerCase(); 11618 s.element = elm.toUpperCase(); 11619 11620 ed.onPreInit.add(t.setup, t); 11621 11622 t.reOpera = new RegExp('(\\u00a0| | )<\/' + elm + '>', 'gi'); 11623 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi'); 11624 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi'); 11625 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>( | )<\\\/%p>|<%p>( | )<\\\/%p>'.replace(/%p/g, elm), 'gi'); 11626 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi'); 11627 11628 function padd(ed, o) { 11629 if (isOpera) 11630 o.content = o.content.replace(t.reOpera, '</' + elm + '>'); 11631 11632 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>'); 11633 11634 if (!isIE && !isOpera && o.set) { 11635 // Use instead of BR in padded paragraphs 11636 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>'); 11637 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>'); 11638 } else 11639 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>'); 11640 }; 11641 11642 ed.onBeforeSetContent.add(padd); 11643 ed.onPostProcess.add(padd); 11644 11645 if (s.forced_root_block) { 11646 ed.onInit.add(t.forceRoots, t); 11647 ed.onSetContent.add(t.forceRoots, t); 11648 ed.onBeforeGetContent.add(t.forceRoots, t); 11649 } 11650 }, 11651 11652 setup : function() { 11653 var t = this, ed = t.editor, s = ed.settings; 11654 11655 // Force root blocks when typing and when getting output 11656 if (s.forced_root_block) { 11657 ed.onKeyUp.add(t.forceRoots, t); 11658 ed.onPreProcess.add(t.forceRoots, t); 11659 } 11660 11661 if (s.force_br_newlines) { 11662 // Force IE to produce BRs on enter 11663 if (isIE) { 11664 ed.onKeyPress.add(function(ed, e) { 11665 var n, s = ed.selection; 11666 11667 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') { 11668 s.setContent('<br id="__" /> ', {format : 'raw'}); 11669 n = ed.dom.get('__'); 11670 n.removeAttribute('id'); 11671 s.select(n); 11672 s.collapse(); 11673 return Event.cancel(e); 11674 } 11675 }); 11676 } 11677 11678 return; 11679 } 11680 11681 if (!isIE && s.force_p_newlines) { 11682 /* ed.onPreProcess.add(function(ed, o) { 11683 each(ed.dom.select('br', o.node), function(n) { 11684 var p = n.parentNode; 11685 11686 // Replace <p><br /></p> with <p> </p> 11687 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) { 11688 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n); 11689 } 11690 }); 11691 });*/ 11692 11693 ed.onKeyPress.add(function(ed, e) { 11694 if (e.keyCode == 13 && !e.shiftKey) { 11695 if (!t.insertPara(e)) 11696 Event.cancel(e); 11697 } 11698 }); 11699 11700 if (isGecko) { 11701 ed.onKeyDown.add(function(ed, e) { 11702 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) 11703 t.backspaceDelete(e, e.keyCode == 8); 11704 }); 11705 } 11706 } 11707 11708 function ren(rn, na) { 11709 var ne = ed.dom.create(na); 11710 11711 each(rn.attributes, function(a) { 11712 if (a.specified && a.nodeValue) 11713 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue); 11714 }); 11715 11716 each(rn.childNodes, function(n) { 11717 ne.appendChild(n.cloneNode(true)); 11718 }); 11719 11720 rn.parentNode.replaceChild(ne, rn); 11721 11722 return ne; 11723 }; 11724 11725 // Padd empty inline elements within block elements 11726 // For example: <p><strong><em></em></strong></p> becomes <p><strong><em> </em></strong></p> 11727 ed.onPreProcess.add(function(ed, o) { 11728 each(ed.dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) { 11729 if (isEmpty(p)) { 11730 each(ed.dom.select('span,em,strong,b,i', o.node), function(n) { 11731 if (!n.hasChildNodes()) { 11732 n.appendChild(ed.getDoc().createTextNode('\u00a0')); 11733 return false; // Break the loop one padding is enough 11734 } 11735 }); 11736 } 11737 }); 11738 }); 11739 11740 // IE specific fixes 11741 if (isIE) { 11742 // Replaces IE:s auto generated paragraphs with the specified element name 11743 if (s.element != 'P') { 11744 ed.onKeyPress.add(function(ed, e) { 11745 t.lastElm = ed.selection.getNode().nodeName; 11746 }); 11747 11748 ed.onKeyUp.add(function(ed, e) { 11749 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody(); 11750 11751 if (b.childNodes.length === 1 && n.nodeName == 'P') { 11752 n = ren(n, s.element); 11753 sel.select(n); 11754 sel.collapse(); 11755 ed.nodeChanged(); 11756 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') { 11757 bl = ed.dom.getParent(n, 'p'); 11758 11759 if (bl) { 11760 ren(bl, s.element); 11761 ed.nodeChanged(); 11762 } 11763 } 11764 }); 11765 } 11766 } 11767 }, 11768 11769 find : function(n, t, s) { 11770 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1; 11771 11772 while (n = w.nextNode()) { 11773 c++; 11774 11775 // Index by node 11776 if (t == 0 && n == s) 11777 return c; 11778 11779 // Node by index 11780 if (t == 1 && c == s) 11781 return n; 11782 } 11783 11784 return -1; 11785 }, 11786 11787 forceRoots : function(ed, e) { 11788 var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF; 11789 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid; 11790 11791 // Fix for bug #1863847 11792 //if (e && e.keyCode == 13) 11793 // return true; 11794 11795 // Wrap non blocks into blocks 11796 for (i = nl.length - 1; i >= 0; i--) { 11797 nx = nl[i]; 11798 11799 // Is text or non block element 11800 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) { 11801 if (!bl) { 11802 // Create new block but ignore whitespace 11803 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) { 11804 // Store selection 11805 if (si == -2 && r) { 11806 if (!isIE) { 11807 // If selection is element then mark it 11808 if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) { 11809 // Save the id of the selected element 11810 eid = n.getAttribute("id"); 11811 n.setAttribute("id", "__mce"); 11812 } else { 11813 // If element is inside body, might not be the case in contentEdiable mode 11814 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) { 11815 so = r.startOffset; 11816 eo = r.endOffset; 11817 si = t.find(b, 0, r.startContainer); 11818 ei = t.find(b, 0, r.endContainer); 11819 } 11820 } 11821 } else { 11822 tr = d.body.createTextRange(); 11823 tr.moveToElementText(b); 11824 tr.collapse(1); 11825 bp = tr.move('character', c) * -1; 11826 11827 tr = r.duplicate(); 11828 tr.collapse(1); 11829 sp = tr.move('character', c) * -1; 11830 11831 tr = r.duplicate(); 11832 tr.collapse(0); 11833 le = (tr.move('character', c) * -1) - sp; 11834 11835 si = sp - bp; 11836 ei = le; 11837 } 11838 } 11839 11840 bl = ed.dom.create(ed.settings.forced_root_block); 11841 bl.appendChild(nx.cloneNode(1)); 11842 nx.parentNode.replaceChild(bl, nx); 11843 } 11844 } else { 11845 if (bl.hasChildNodes()) 11846 bl.insertBefore(nx, bl.firstChild); 11847 else 11848 bl.appendChild(nx); 11849 } 11850 } else 11851 bl = null; // Time to create new block 11852 } 11853 11854 // Restore selection 11855 if (si != -2) { 11856 if (!isIE) { 11857 bl = b.getElementsByTagName(ed.settings.element)[0]; 11858 r = d.createRange(); 11859 11860 // Select last location or generated block 11861 if (si != -1) 11862 r.setStart(t.find(b, 1, si), so); 11863 else 11864 r.setStart(bl, 0); 11865 11866 // Select last location or generated block 11867 if (ei != -1) 11868 r.setEnd(t.find(b, 1, ei), eo); 11869 else 11870 r.setEnd(bl, 0); 11871 11872 if (s) { 11873 s.removeAllRanges(); 11874 s.addRange(r); 11875 } 11876 } else { 11877 try { 11878 r = s.createRange(); 11879 r.moveToElementText(b); 11880 r.collapse(1); 11881 r.moveStart('character', si); 11882 r.moveEnd('character', ei); 11883 r.select(); 11884 } catch (ex) { 11885 // Ignore 11886 } 11887 } 11888 } else if (!isIE && (n = ed.dom.get('__mce'))) { 11889 // Restore the id of the selected element 11890 if (eid) 11891 n.setAttribute('id', eid); 11892 else 11893 n.removeAttribute('id'); 11894 11895 // Move caret before selected element 11896 r = d.createRange(); 11897 r.setStartBefore(n); 11898 r.setEndBefore(n); 11899 se.setRng(r); 11900 } 11901 }, 11902 11903 getParentBlock : function(n) { 11904 var d = this.dom; 11905 11906 return d.getParent(n, d.isBlock); 11907 }, 11908 11909 insertPara : function(e) { 11910 var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body; 11911 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car; 11912 11913 // If root blocks are forced then use Operas default behavior since it's really good 11914 // Removed due to bug: #1853816 11915 // if (se.forced_root_block && isOpera) 11916 // return true; 11917 11918 // Setup before range 11919 rb = d.createRange(); 11920 11921 // If is before the first block element and in body, then move it into first block element 11922 rb.setStart(s.anchorNode, s.anchorOffset); 11923 rb.collapse(true); 11924 11925 // Setup after range 11926 ra = d.createRange(); 11927 11928 // If is before the first block element and in body, then move it into first block element 11929 ra.setStart(s.focusNode, s.focusOffset); 11930 ra.collapse(true); 11931 11932 // Setup start/end points 11933 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; 11934 sn = dir ? s.anchorNode : s.focusNode; 11935 so = dir ? s.anchorOffset : s.focusOffset; 11936 en = dir ? s.focusNode : s.anchorNode; 11937 eo = dir ? s.focusOffset : s.anchorOffset; 11938 11939 // If selection is in empty table cell 11940 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) { 11941 if (sn.firstChild.nodeName == 'BR') 11942 dom.remove(sn.firstChild); // Remove BR 11943 11944 // Create two new block elements 11945 if (sn.childNodes.length == 0) { 11946 ed.dom.add(sn, se.element, null, '<br />'); 11947 aft = ed.dom.add(sn, se.element, null, '<br />'); 11948 } else { 11949 n = sn.innerHTML; 11950 sn.innerHTML = ''; 11951 ed.dom.add(sn, se.element, null, n); 11952 aft = ed.dom.add(sn, se.element, null, '<br />'); 11953 } 11954 11955 // Move caret into the last one 11956 r = d.createRange(); 11957 r.selectNodeContents(aft); 11958 r.collapse(1); 11959 ed.selection.setRng(r); 11960 11961 return false; 11962 } 11963 11964 // If the caret is in an invalid location in FF we need to move it into the first block 11965 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) { 11966 sn = en = sn.firstChild; 11967 so = eo = 0; 11968 rb = d.createRange(); 11969 rb.setStart(sn, 0); 11970 ra = d.createRange(); 11971 ra.setStart(en, 0); 11972 } 11973 11974 // Never use body as start or end node 11975 sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes 11976 sn = sn.nodeName == "BODY" ? sn.firstChild : sn; 11977 en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes 11978 en = en.nodeName == "BODY" ? en.firstChild : en; 11979 11980 // Get start and end blocks 11981 sb = t.getParentBlock(sn); 11982 eb = t.getParentBlock(en); 11983 bn = sb ? sb.nodeName : se.element; // Get block name to create 11984 11985 // Return inside list use default browser behavior 11986 if (t.dom.getParent(sb, 'ol,ul,pre')) 11987 return true; 11988 11989 // If caption or absolute layers then always generate new blocks within 11990 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { 11991 bn = se.element; 11992 sb = null; 11993 } 11994 11995 // If caption or absolute layers then always generate new blocks within 11996 if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { 11997 bn = se.element; 11998 eb = null; 11999 } 12000 12001 // Use P instead 12002 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) { 12003 bn = se.element; 12004 sb = eb = null; 12005 } 12006 12007 // Setup new before and after blocks 12008 bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn); 12009 aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn); 12010 12011 // Remove id from after clone 12012 aft.removeAttribute('id'); 12013 12014 // Is header and cursor is at the end, then force paragraph under 12015 if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb)) 12016 aft = ed.dom.create(se.element); 12017 12018 // Find start chop node 12019 n = sc = sn; 12020 do { 12021 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName)) 12022 break; 12023 12024 sc = n; 12025 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode)); 12026 12027 // Find end chop node 12028 n = ec = en; 12029 do { 12030 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName)) 12031 break; 12032 12033 ec = n; 12034 } while ((n = n.nextSibling ? n.nextSibling : n.parentNode)); 12035 12036 // Place first chop part into before block element 12037 if (sc.nodeName == bn) 12038 rb.setStart(sc, 0); 12039 else 12040 rb.setStartBefore(sc); 12041 12042 rb.setEnd(sn, so); 12043 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari 12044 12045 // Place secnd chop part within new block element 12046 try { 12047 ra.setEndAfter(ec); 12048 } catch(ex) { 12049 //console.debug(s.focusNode, s.focusOffset); 12050 } 12051 12052 ra.setStart(en, eo); 12053 aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari 12054 12055 // Create range around everything 12056 r = d.createRange(); 12057 if (!sc.previousSibling && sc.parentNode.nodeName == bn) { 12058 r.setStartBefore(sc.parentNode); 12059 } else { 12060 if (rb.startContainer.nodeName == bn && rb.startOffset == 0) 12061 r.setStartBefore(rb.startContainer); 12062 else 12063 r.setStart(rb.startContainer, rb.startOffset); 12064 } 12065 12066 if (!ec.nextSibling && ec.parentNode.nodeName == bn) 12067 r.setEndAfter(ec.parentNode); 12068 else 12069 r.setEnd(ra.endContainer, ra.endOffset); 12070 12071 // Delete and replace it with new block elements 12072 r.deleteContents(); 12073 12074 if (isOpera) 12075 ed.getWin().scrollTo(0, vp.y); 12076 12077 // Never wrap blocks in blocks 12078 if (bef.firstChild && bef.firstChild.nodeName == bn) 12079 bef.innerHTML = bef.firstChild.innerHTML; 12080 12081 if (aft.firstChild && aft.firstChild.nodeName == bn) 12082 aft.innerHTML = aft.firstChild.innerHTML; 12083 12084 // Padd empty blocks 12085 if (isEmpty(bef)) 12086 bef.innerHTML = '<br />'; 12087 12088 function appendStyles(e, en) { 12089 var nl = [], nn, n, i; 12090 12091 e.innerHTML = ''; 12092 12093 // Make clones of style elements 12094 if (se.keep_styles) { 12095 n = en; 12096 do { 12097 // We only want style specific elements 12098 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) { 12099 nn = n.cloneNode(false); 12100 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique 12101 nl.push(nn); 12102 } 12103 } while (n = n.parentNode); 12104 } 12105 12106 // Append style elements to aft 12107 if (nl.length > 0) { 12108 for (i = nl.length - 1, nn = e; i >= 0; i--) 12109 nn = nn.appendChild(nl[i]); 12110 12111 // Padd most inner style element 12112 nl[0].innerHTML = isOpera ? ' ' : '<br />'; // Extra space for Opera so that the caret can move there 12113 return nl[0]; // Move caret to most inner element 12114 } else 12115 e.innerHTML = isOpera ? ' ' : '<br />'; // Extra space for Opera so that the caret can move there 12116 }; 12117 12118 // Fill empty afterblook with current style 12119 if (isEmpty(aft)) 12120 car = appendStyles(aft, en); 12121 12122 // Opera needs this one backwards for older versions 12123 if (isOpera && parseFloat(opera.version()) < 9.5) { 12124 r.insertNode(bef); 12125 r.insertNode(aft); 12126 } else { 12127 r.insertNode(aft); 12128 r.insertNode(bef); 12129 } 12130 12131 // Normalize 12132 aft.normalize(); 12133 bef.normalize(); 12134 12135 function first(n) { 12136 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n; 12137 }; 12138 12139 // Move cursor and scroll into view 12140 r = d.createRange(); 12141 r.selectNodeContents(isGecko ? first(car || aft) : car || aft); 12142 r.collapse(1); 12143 s.removeAllRanges(); 12144 s.addRange(r); 12145 12146 // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs 12147 y = ed.dom.getPos(aft).y; 12148 ch = aft.clientHeight; 12149 12150 // Is element within viewport 12151 if (y < vp.y || y + ch > vp.y + vp.h) { 12152 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks 12153 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight)); 12154 } 12155 12156 return false; 12157 }, 12158 12159 backspaceDelete : function(e, bs) { 12160 var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn; 12161 12162 /* 12163 var par, rng, nextBlock; 12164 12165 // Delete key will not merge paragraphs on Gecko so we need to do this manually 12166 // Hitting the delete key at the following caret position doesn't merge the elements <p>A|</p><p>B</p> 12167 // This logic will merge them into this: <p>A|B</p> 12168 if (e.keyCode == 46) { 12169 if (r.collapsed) { 12170 par = dom.getParent(sc, 'p,h1,h2,h3,h4,h5,h6,div'); 12171 12172 if (par) { 12173 rng = dom.createRng(); 12174 12175 rng.setStart(sc, r.startOffset); 12176 rng.setEndAfter(par); 12177 12178 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element 12179 if (dom.getOuterHTML(rng.cloneContents()).replace(/<[^>]+>/g, '').length == 0) { 12180 nextBlock = dom.getNext(par, 'p,h1,h2,h3,h4,h5,h6,div'); 12181 12182 // Copy all children from next sibling block and remove it 12183 if (nextBlock) { 12184 each(nextBlock.childNodes, function(node) { 12185 par.appendChild(node.cloneNode(true)); 12186 }); 12187 12188 dom.remove(nextBlock); 12189 } 12190 12191 // Block the default even since the Gecko team might eventually fix this 12192 // We will remove this logic once they do we can't feature detect this one 12193 e.preventDefault(); 12194 return; 12195 } 12196 } 12197 } 12198 } 12199 */ 12200 12201 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs 12202 // This workaround removes the element by hand and moves the caret to the previous element 12203 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) { 12204 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) { 12205 // Find previous block element 12206 n = sc; 12207 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ; 12208 12209 if (n) { 12210 if (sc != b.firstChild) { 12211 // Find last text node 12212 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); 12213 while (tn = w.nextNode()) 12214 n = tn; 12215 12216 // Place caret at the end of last text node 12217 r = ed.getDoc().createRange(); 12218 r.setStart(n, n.nodeValue ? n.nodeValue.length : 0); 12219 r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0); 12220 se.setRng(r); 12221 12222 // Remove the target container 12223 ed.dom.remove(sc); 12224 } 12225 12226 return Event.cancel(e); 12227 } 12228 } 12229 } 12230 12231 // Gecko generates BR elements here and there, we don't like those so lets remove them 12232 function handler(e) { 12233 var pr; 12234 12235 e = e.target; 12236 12237 // A new BR was created in a block element, remove it 12238 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) { 12239 pr = e.previousSibling; 12240 12241 Event.remove(b, 'DOMNodeInserted', handler); 12242 12243 // Is there whitespace at the end of the node before then we might need the pesky BR 12244 // to place the caret at a correct location see bug: #2013943 12245 if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue)) 12246 return; 12247 12248 // Only remove BR elements that got inserted in the middle of the text 12249 if (e.previousSibling || e.nextSibling) 12250 ed.dom.remove(e); 12251 } 12252 }; 12253 12254 // Listen for new nodes 12255 Event._add(b, 'DOMNodeInserted', handler); 12256 12257 // Remove listener 12258 window.setTimeout(function() { 12259 Event._remove(b, 'DOMNodeInserted', handler); 12260 }, 1); 12261 } 12262 }); 12263 })(tinymce); 12264 (function(tinymce) { 12265 // Shorten names 12266 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend; 12267 12268 tinymce.create('tinymce.ControlManager', { 12269 ControlManager : function(ed, s) { 12270 var t = this, i; 12271 12272 s = s || {}; 12273 t.editor = ed; 12274 t.controls = {}; 12275 t.onAdd = new tinymce.util.Dispatcher(t); 12276 t.onPostRender = new tinymce.util.Dispatcher(t); 12277 t.prefix = s.prefix || ed.id + '_'; 12278 t._cls = {}; 12279 12280 t.onPostRender.add(function() { 12281 each(t.controls, function(c) { 12282 c.postRender(); 12283 }); 12284 }); 12285 }, 12286 12287 get : function(id) { 12288 return this.controls[this.prefix + id] || this.controls[id]; 12289 }, 12290 12291 setActive : function(id, s) { 12292 var c = null; 12293 12294 if (c = this.get(id)) 12295 c.setActive(s); 12296 12297 return c; 12298 }, 12299 12300 setDisabled : function(id, s) { 12301 var c = null; 12302 12303 if (c = this.get(id)) 12304 c.setDisabled(s); 12305 12306 return c; 12307 }, 12308 12309 add : function(c) { 12310 var t = this; 12311 12312 if (c) { 12313 t.controls[c.id] = c; 12314 t.onAdd.dispatch(c, t); 12315 } 12316 12317 return c; 12318 }, 12319 12320 createControl : function(n) { 12321 var c, t = this, ed = t.editor; 12322 12323 each(ed.plugins, function(p) { 12324 if (p.createControl) { 12325 c = p.createControl(n, t); 12326 12327 if (c) 12328 return false; 12329 } 12330 }); 12331 12332 switch (n) { 12333 case "|": 12334 case "separator": 12335 return t.createSeparator(); 12336 } 12337 12338 if (!c && ed.buttons && (c = ed.buttons[n])) 12339 return t.createButton(n, c); 12340 12341 return t.add(c); 12342 }, 12343 12344 createDropMenu : function(id, s, cc) { 12345 var t = this, ed = t.editor, c, bm, v, cls; 12346 12347 s = extend({ 12348 'class' : 'mceDropDown', 12349 constrain : ed.settings.constrain_menus 12350 }, s); 12351 12352 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin'; 12353 if (v = ed.getParam('skin_variant')) 12354 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1); 12355 12356 id = t.prefix + id; 12357 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu; 12358 c = t.controls[id] = new cls(id, s); 12359 c.onAddItem.add(function(c, o) { 12360 var s = o.settings; 12361 12362 s.title = ed.getLang(s.title, s.title); 12363 12364 if (!s.onclick) { 12365 s.onclick = function(v) { 12366 ed.execCommand(s.cmd, s.ui || false, s.value); 12367 }; 12368 } 12369 }); 12370 12371 ed.onRemove.add(function() { 12372 c.destroy(); 12373 }); 12374 12375 // Fix for bug #1897785, #1898007 12376 if (tinymce.isIE) { 12377 c.onShowMenu.add(function() { 12378 // IE 8 needs focus in order to store away a range with the current collapsed caret location 12379 ed.focus(); 12380 12381 bm = ed.selection.getBookmark(1); 12382 }); 12383 12384 c.onHideMenu.add(function() { 12385 if (bm) { 12386 ed.selection.moveToBookmark(bm); 12387 bm = 0; 12388 } 12389 }); 12390 } 12391 12392 return t.add(c); 12393 }, 12394 12395 createListBox : function(id, s, cc) { 12396 var t = this, ed = t.editor, cmd, c, cls; 12397 12398 if (t.get(id)) 12399 return null; 12400 12401 s.title = ed.translate(s.title); 12402 s.scope = s.scope || ed; 12403 12404 if (!s.onselect) { 12405 s.onselect = function(v) { 12406 ed.execCommand(s.cmd, s.ui || false, v || s.value); 12407 }; 12408 } 12409 12410 s = extend({ 12411 title : s.title, 12412 'class' : 'mce_' + id, 12413 scope : s.scope, 12414 control_manager : t 12415 }, s); 12416 12417 id = t.prefix + id; 12418 12419 if (ed.settings.use_native_selects) 12420 c = new tinymce.ui.NativeListBox(id, s); 12421 else { 12422 cls = cc || t._cls.listbox || tinymce.ui.ListBox; 12423 c = new cls(id, s); 12424 } 12425 12426 t.controls[id] = c; 12427 12428 // Fix focus problem in Safari 12429 if (tinymce.isWebKit) { 12430 c.onPostRender.add(function(c, n) { 12431 // Store bookmark on mousedown 12432 Event.add(n, 'mousedown', function() { 12433 ed.bookmark = ed.selection.getBookmark(1); 12434 }); 12435 12436 // Restore on focus, since it might be lost 12437 Event.add(n, 'focus', function() { 12438 ed.selection.moveToBookmark(ed.bookmark); 12439 ed.bookmark = null; 12440 }); 12441 }); 12442 } 12443 12444 if (c.hideMenu) 12445 ed.onMouseDown.add(c.hideMenu, c); 12446 12447 return t.add(c); 12448 }, 12449 12450 createButton : function(id, s, cc) { 12451 var t = this, ed = t.editor, o, c, cls; 12452 12453 if (t.get(id)) 12454 return null; 12455 12456 s.title = ed.translate(s.title); 12457 s.label = ed.translate(s.label); 12458 s.scope = s.scope || ed; 12459 12460 if (!s.onclick && !s.menu_button) { 12461 s.onclick = function() { 12462 ed.execCommand(s.cmd, s.ui || false, s.value); 12463 }; 12464 } 12465 12466 s = extend({ 12467 title : s.title, 12468 'class' : 'mce_' + id, 12469 unavailable_prefix : ed.getLang('unavailable', ''), 12470 scope : s.scope, 12471 control_manager : t 12472 }, s); 12473 12474 id = t.prefix + id; 12475 12476 if (s.menu_button) { 12477 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton; 12478 c = new cls(id, s); 12479 ed.onMouseDown.add(c.hideMenu, c); 12480 } else { 12481 cls = t._cls.button || tinymce.ui.Button; 12482 c = new cls(id, s); 12483 } 12484 12485 return t.add(c); 12486 }, 12487 12488 createMenuButton : function(id, s, cc) { 12489 s = s || {}; 12490 s.menu_button = 1; 12491 12492 return this.createButton(id, s, cc); 12493 }, 12494 12495 createSplitButton : function(id, s, cc) { 12496 var t = this, ed = t.editor, cmd, c, cls; 12497 12498 if (t.get(id)) 12499 return null; 12500 12501 s.title = ed.translate(s.title); 12502 s.scope = s.scope || ed; 12503 12504 if (!s.onclick) { 12505 s.onclick = function(v) { 12506 ed.execCommand(s.cmd, s.ui || false, v || s.value); 12507 }; 12508 } 12509 12510 if (!s.onselect) { 12511 s.onselect = function(v) { 12512 ed.execCommand(s.cmd, s.ui || false, v || s.value); 12513 }; 12514 } 12515 12516 s = extend({ 12517 title : s.title, 12518 'class' : 'mce_' + id, 12519 scope : s.scope, 12520 control_manager : t 12521 }, s); 12522 12523 id = t.prefix + id; 12524 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton; 12525 c = t.add(new cls(id, s)); 12526 ed.onMouseDown.add(c.hideMenu, c); 12527 12528 return c; 12529 }, 12530 12531 createColorSplitButton : function(id, s, cc) { 12532 var t = this, ed = t.editor, cmd, c, cls, bm; 12533 12534 if (t.get(id)) 12535 return null; 12536 12537 s.title = ed.translate(s.title); 12538 s.scope = s.scope || ed; 12539 12540 if (!s.onclick) { 12541 s.onclick = function(v) { 12542 if (tinymce.isIE) 12543 bm = ed.selection.getBookmark(1); 12544 12545 ed.execCommand(s.cmd, s.ui || false, v || s.value); 12546 }; 12547 } 12548 12549 if (!s.onselect) { 12550 s.onselect = function(v) { 12551 ed.execCommand(s.cmd, s.ui || false, v || s.value); 12552 }; 12553 } 12554 12555 s = extend({ 12556 title : s.title, 12557 'class' : 'mce_' + id, 12558 'menu_class' : ed.getParam('skin') + 'Skin', 12559 scope : s.scope, 12560 more_colors_title : ed.getLang('more_colors') 12561 }, s); 12562 12563 id = t.prefix + id; 12564 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton; 12565 c = new cls(id, s); 12566 ed.onMouseDown.add(c.hideMenu, c); 12567 12568 // Remove the menu element when the editor is removed 12569 ed.onRemove.add(function() { 12570 c.destroy(); 12571 }); 12572 12573 // Fix for bug #1897785, #1898007 12574 if (tinymce.isIE) { 12575 c.onShowMenu.add(function() { 12576 // IE 8 needs focus in order to store away a range with the current collapsed caret location 12577 ed.focus(); 12578 bm = ed.selection.getBookmark(1); 12579 }); 12580 12581 c.onHideMenu.add(function() { 12582 if (bm) { 12583 ed.selection.moveToBookmark(bm); 12584 bm = 0; 12585 } 12586 }); 12587 } 12588 12589 return t.add(c); 12590 }, 12591 12592 createToolbar : function(id, s, cc) { 12593 var c, t = this, cls; 12594 12595 id = t.prefix + id; 12596 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar; 12597 c = new cls(id, s); 12598 12599 if (t.get(id)) 12600 return null; 12601 12602 return t.add(c); 12603 }, 12604 12605 createSeparator : function(cc) { 12606 var cls = cc || this._cls.separator || tinymce.ui.Separator; 12607 12608 return new cls(); 12609 }, 12610 12611 setControlType : function(n, c) { 12612 return this._cls[n.toLowerCase()] = c; 12613 }, 12614 12615 destroy : function() { 12616 each(this.controls, function(c) { 12617 c.destroy(); 12618 }); 12619 12620 this.controls = null; 12621 } 12622 }); 12623 })(tinymce); 12624 (function(tinymce) { 12625 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera; 12626 12627 tinymce.create('tinymce.WindowManager', { 12628 WindowManager : function(ed) { 12629 var t = this; 12630 12631 t.editor = ed; 12632 t.onOpen = new Dispatcher(t); 12633 t.onClose = new Dispatcher(t); 12634 t.params = {}; 12635 t.features = {}; 12636 }, 12637 12638 open : function(s, p) { 12639 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u; 12640 12641 // Default some options 12642 s = s || {}; 12643 p = p || {}; 12644 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window 12645 sh = isOpera ? vp.h : screen.height; 12646 s.name = s.name || 'mc_' + new Date().getTime(); 12647 s.width = parseInt(s.width || 320); 12648 s.height = parseInt(s.height || 240); 12649 s.resizable = true; 12650 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0); 12651 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0); 12652 p.inline = false; 12653 p.mce_width = s.width; 12654 p.mce_height = s.height; 12655 p.mce_auto_focus = s.auto_focus; 12656 12657 if (mo) { 12658 if (isIE) { 12659 s.center = true; 12660 s.help = false; 12661 s.dialogWidth = s.width + 'px'; 12662 s.dialogHeight = s.height + 'px'; 12663 s.scroll = s.scrollbars || false; 12664 } 12665 } 12666 12667 // Build features string 12668 each(s, function(v, k) { 12669 if (tinymce.is(v, 'boolean')) 12670 v = v ? 'yes' : 'no'; 12671 12672 if (!/^(name|url)$/.test(k)) { 12673 if (isIE && mo) 12674 f += (f ? ';' : '') + k + ':' + v; 12675 else 12676 f += (f ? ',' : '') + k + '=' + v; 12677 } 12678 }); 12679 12680 t.features = s; 12681 t.params = p; 12682 t.onOpen.dispatch(t, s, p); 12683 12684 u = s.url || s.file; 12685 u = tinymce._addVer(u); 12686 12687 try { 12688 if (isIE && mo) { 12689 w = 1; 12690 window.showModalDialog(u, window, f); 12691 } else 12692 w = window.open(u, s.name, f); 12693 } catch (ex) { 12694 // Ignore 12695 } 12696 12697 if (!w) 12698 alert(t.editor.getLang('popup_blocked')); 12699 }, 12700 12701 close : function(w) { 12702 w.close(); 12703 this.onClose.dispatch(this); 12704 }, 12705 12706 createInstance : function(cl, a, b, c, d, e) { 12707 var f = tinymce.resolve(cl); 12708 12709 return new f(a, b, c, d, e); 12710 }, 12711 12712 confirm : function(t, cb, s, w) { 12713 w = w || window; 12714 12715 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t)))); 12716 }, 12717 12718 alert : function(tx, cb, s, w) { 12719 var t = this; 12720 12721 w = w || window; 12722 w.alert(t._decode(t.editor.getLang(tx, tx))); 12723 12724 if (cb) 12725 cb.call(s || t); 12726 }, 12727 12728 // Internal functions 12729 12730 _decode : function(s) { 12731 return tinymce.DOM.decode(s).replace(/\\n/g, '\n'); 12732 } 12733 }); 12734 }(tinymce));(function(tinymce) { 12735 tinymce.CommandManager = function() { 12736 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {}; 12737 12738 function add(collection, cmd, func, scope) { 12739 if (typeof(cmd) == 'string') 12740 cmd = [cmd]; 12741 12742 tinymce.each(cmd, function(cmd) { 12743 collection[cmd.toLowerCase()] = {func : func, scope : scope}; 12744 }); 12745 }; 12746 12747 tinymce.extend(this, { 12748 add : function(cmd, func, scope) { 12749 add(execCommands, cmd, func, scope); 12750 }, 12751 12752 addQueryStateHandler : function(cmd, func, scope) { 12753 add(queryStateCommands, cmd, func, scope); 12754 }, 12755 12756 addQueryValueHandler : function(cmd, func, scope) { 12757 add(queryValueCommands, cmd, func, scope); 12758 }, 12759 12760 execCommand : function(scope, cmd, ui, value, args) { 12761 if (cmd = execCommands[cmd.toLowerCase()]) { 12762 if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false) 12763 return true; 12764 } 12765 }, 12766 12767 queryCommandValue : function() { 12768 if (cmd = queryValueCommands[cmd.toLowerCase()]) 12769 return cmd.func.call(scope || cmd.scope, ui, value, args); 12770 }, 12771 12772 queryCommandState : function() { 12773 if (cmd = queryStateCommands[cmd.toLowerCase()]) 12774 return cmd.func.call(scope || cmd.scope, ui, value, args); 12775 } 12776 }); 12777 }; 12778 12779 tinymce.GlobalCommands = new tinymce.CommandManager(); 12780 })(tinymce);(function(tinymce) { 12781 function processRange(dom, start, end, callback) { 12782 var ancestor, n, startPoint, endPoint, sib; 12783 12784 function findEndPoint(n, c) { 12785 do { 12786 if (n.parentNode == c) 12787 return n; 12788 12789 n = n.parentNode; 12790 } while(n); 12791 }; 12792 12793 function process(n) { 12794 callback(n); 12795 tinymce.walk(n, callback, 'childNodes'); 12796 }; 12797 12798 // Find common ancestor and end points 12799 ancestor = dom.findCommonAncestor(start, end); 12800 startPoint = findEndPoint(start, ancestor) || start; 12801 endPoint = findEndPoint(end, ancestor) || end; 12802 12803 // Process left leaf 12804 for (n = start; n && n != startPoint; n = n.parentNode) { 12805 for (sib = n.nextSibling; sib; sib = sib.nextSibling) 12806 process(sib); 12807 } 12808 12809 // Process middle from start to end point 12810 if (startPoint != endPoint) { 12811 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling) 12812 process(n); 12813 } else 12814 process(startPoint); 12815 12816 // Process right leaf 12817 for (n = end; n && n != endPoint; n = n.parentNode) { 12818 for (sib = n.previousSibling; sib; sib = sib.previousSibling) 12819 process(sib); 12820 } 12821 }; 12822 12823 tinymce.GlobalCommands.add('RemoveFormat', function() { 12824 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n; 12825 12826 function findFormatRoot(n) { 12827 var sp; 12828 12829 dom.getParent(n, function(n) { 12830 if (dom.is(n, ed.getParam('removeformat_selector'))) 12831 sp = n; 12832 12833 return dom.isBlock(n); 12834 }, ed.getBody()); 12835 12836 return sp; 12837 }; 12838 12839 function collect(n) { 12840 if (dom.is(n, ed.getParam('removeformat_selector'))) 12841 nodes.push(n); 12842 }; 12843 12844 function walk(n) { 12845 collect(n); 12846 tinymce.walk(n, collect, 'childNodes'); 12847 }; 12848 12849 bm = s.getBookmark(); 12850 sc = r.startContainer; 12851 ec = r.endContainer; 12852 so = r.startOffset; 12853 eo = r.endOffset; 12854 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc; 12855 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec; 12856 12857 // Same container 12858 if (sc == ec) { // TEXT_NODE 12859 start = findFormatRoot(sc); 12860 12861 // Handle single text node 12862 if (sc.nodeType == 3) { 12863 if (start && start.nodeType == 1) { // ELEMENT 12864 n = sc.splitText(so); 12865 n.splitText(eo - so); 12866 dom.split(start, n); 12867 12868 s.moveToBookmark(bm); 12869 } 12870 12871 return; 12872 } 12873 12874 // Handle single element 12875 walk(dom.split(start, sc) || sc); 12876 } else { 12877 // Find start/end format root 12878 start = findFormatRoot(sc); 12879 end = findFormatRoot(ec); 12880 12881 // Split start text node 12882 if (start) { 12883 if (sc.nodeType == 3) { // TEXT 12884 // Since IE doesn't support white space nodes in the DOM we need to 12885 // add this invisible character so that the splitText function can split the contents 12886 if (so == sc.nodeValue.length) 12887 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix 12888 12889 sc = sc.splitText(so); 12890 } 12891 } 12892 12893 // Split end text node 12894 if (end) { 12895 if (ec.nodeType == 3) // TEXT 12896 ec.splitText(eo); 12897 } 12898 12899 // If the start and end format root is the same then we need to wrap 12900 // the end node in a span since the split calls might change the reference 12901 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p> 12902 if (start && start == end) 12903 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec); 12904 12905 // Split all start containers down to the format root 12906 if (start) 12907 start = dom.split(start, sc); 12908 else 12909 start = sc; 12910 12911 // If there is a span wrapper use that one instead 12912 if (n = dom.get('__end')) { 12913 ec = n; 12914 end = findFormatRoot(ec); 12915 } 12916 12917 // Split all end containers down to the format root 12918 if (end) 12919 end = dom.split(end, ec); 12920 else 12921 end = ec; 12922 12923 // Collect nodes in between 12924 processRange(dom, start, end, collect); 12925 12926 // Remove invisible character for IE workaround if we find it 12927 if (sc.nodeValue == '\uFEFF') 12928 sc.nodeValue = ''; 12929 12930 // Process start/end container elements 12931 walk(ec); 12932 walk(sc); 12933 } 12934 12935 // Remove all collected nodes 12936 tinymce.each(nodes, function(n) { 12937 dom.remove(n, 1); 12938 }); 12939 12940 // Remove leftover wrapper 12941 dom.remove('__end', 1); 12942 12943 s.moveToBookmark(bm); 12944 }); 12945 })(tinymce); 12946 (function(tinymce) { 12947 tinymce.GlobalCommands.add('mceBlockQuote', function() { 12948 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl; 12949 12950 function getBQ(e) { 12951 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';}); 12952 }; 12953 12954 // Get start/end block 12955 sb = dom.getParent(s.getStart(), dom.isBlock); 12956 eb = dom.getParent(s.getEnd(), dom.isBlock); 12957 12958 // Remove blockquote(s) 12959 if (bq = getBQ(sb)) { 12960 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR')) 12961 bm = s.getBookmark(); 12962 12963 // Move all elements after the end block into new bq 12964 if (getBQ(eb)) { 12965 bq2 = bq.cloneNode(false); 12966 12967 while (n = eb.nextSibling) 12968 bq2.appendChild(n.parentNode.removeChild(n)); 12969 } 12970 12971 // Add new bq after 12972 if (bq2) 12973 dom.insertAfter(bq2, bq); 12974 12975 // Move all selected blocks after the current bq 12976 nl = s.getSelectedBlocks(sb, eb); 12977 for (i = nl.length - 1; i >= 0; i--) { 12978 dom.insertAfter(nl[i], bq); 12979 } 12980 12981 // Empty bq, then remove it 12982 if (/^\s*$/.test(bq.innerHTML)) 12983 dom.remove(bq, 1); // Keep children so boomark restoration works correctly 12984 12985 // Empty bq, then remote it 12986 if (bq2 && /^\s*$/.test(bq2.innerHTML)) 12987 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly 12988 12989 if (!bm) { 12990 // Move caret inside empty block element 12991 if (!tinymce.isIE) { 12992 r = ed.getDoc().createRange(); 12993 r.setStart(sb, 0); 12994 r.setEnd(sb, 0); 12995 s.setRng(r); 12996 } else { 12997 s.select(sb); 12998 s.collapse(0); 12999 13000 // IE misses the empty block some times element so we must move back the caret 13001 if (dom.getParent(s.getStart(), dom.isBlock) != sb) { 13002 r = s.getRng(); 13003 r.move('character', -1); 13004 r.select(); 13005 } 13006 } 13007 } else 13008 ed.selection.moveToBookmark(bm); 13009 13010 return; 13011 } 13012 13013 // Since IE can start with a totally empty document we need to add the first bq and paragraph 13014 if (tinymce.isIE && !sb && !eb) { 13015 ed.getDoc().execCommand('Indent'); 13016 n = getBQ(s.getNode()); 13017 n.style.margin = n.dir = ''; // IE adds margin and dir to bq 13018 return; 13019 } 13020 13021 if (!sb || !eb) 13022 return; 13023 13024 // If empty paragraph node then do not use bookmark 13025 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR')) 13026 bm = s.getBookmark(); 13027 13028 // Move selected block elements into a bq 13029 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) { 13030 // Found existing BQ add to this one 13031 if (e.nodeName == 'BLOCKQUOTE' && !bq) { 13032 bq = e; 13033 return; 13034 } 13035 13036 // No BQ found, create one 13037 if (!bq) { 13038 bq = dom.create('blockquote'); 13039 e.parentNode.insertBefore(bq, e); 13040 } 13041 13042 // Add children from existing BQ 13043 if (e.nodeName == 'BLOCKQUOTE' && bq) { 13044 n = e.firstChild; 13045 13046 while (n) { 13047 bq.appendChild(n.cloneNode(true)); 13048 n = n.nextSibling; 13049 } 13050 13051 dom.remove(e); 13052 return; 13053 } 13054 13055 // Add non BQ element to BQ 13056 bq.appendChild(dom.remove(e)); 13057 }); 13058 13059 if (!bm) { 13060 // Move caret inside empty block element 13061 if (!tinymce.isIE) { 13062 r = ed.getDoc().createRange(); 13063 r.setStart(sb, 0); 13064 r.setEnd(sb, 0); 13065 s.setRng(r); 13066 } else { 13067 s.select(sb); 13068 s.collapse(1); 13069 } 13070 } else 13071 s.moveToBookmark(bm); 13072 }); 13073 })(tinymce); 13074 (function(tinymce) { 13075 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) { 13076 tinymce.GlobalCommands.add(cmd, function() { 13077 var ed = this, doc = ed.getDoc(); 13078 13079 try { 13080 doc.execCommand(cmd, false, null); 13081 13082 // On WebKit the command will just be ignored if it's not enabled 13083 if (!doc.queryCommandSupported(cmd)) 13084 throw 'Error'; 13085 } catch (ex) { 13086 if (tinymce.isGecko) { 13087 ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) { 13088 if (s) 13089 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank'); 13090 }); 13091 } else 13092 ed.windowManager.alert(ed.getLang('clipboard_no_support')); 13093 } 13094 }); 13095 }); 13096 })(tinymce); 13097 (function(tinymce) { 13098 tinymce.GlobalCommands.add('InsertHorizontalRule', function() { 13099 if (tinymce.isOpera) 13100 return this.getDoc().execCommand('InsertHorizontalRule', false, ''); 13101 13102 this.selection.setContent('<hr />'); 13103 }); 13104 })(tinymce); 13105 (function() { 13106 var cmds = tinymce.GlobalCommands; 13107 13108 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() { 13109 this.undoManager.add(); 13110 }); 13111 13112 cmds.add('Undo', function() { 13113 var ed = this; 13114 13115 if (ed.settings.custom_undo_redo) { 13116 ed.undoManager.undo(); 13117 ed.nodeChanged(); 13118 return true; 13119 } 13120 13121 return false; // Run browser command 13122 }); 13123 13124 cmds.add('Redo', function() { 13125 var ed = this; 13126 13127 if (ed.settings.custom_undo_redo) { 13128 ed.undoManager.redo(); 13129 ed.nodeChanged(); 13130 return true; 13131 } 13132 13133 return false; // Run browser command 13134 }); 13135 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |