annotate media/js/tiny_mce/tiny_mce_src.js @ 133:c515b7401078

Use the new common way to apply markItUp to textareas and to get the smiley and markdown help dialogs for all the remaining apps except for forums and comments.
author Brian Neal <bgneal@gmail.com>
date Fri, 27 Nov 2009 00:21:47 +0000
parents a5b4c5ce0658
children 149c3567fec1
rev   line source
bgneal@45 1 var tinymce = {
bgneal@45 2 majorVersion : '3',
bgneal@45 3 minorVersion : '2.2.3',
bgneal@45 4 releaseDate : '2009-03-26',
bgneal@45 5
bgneal@45 6 _init : function() {
bgneal@45 7 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
bgneal@45 8
bgneal@45 9 // Browser checks
bgneal@45 10 t.isOpera = w.opera && opera.buildNumber;
bgneal@45 11 t.isWebKit = /WebKit/.test(ua);
bgneal@45 12 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
bgneal@45 13 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
bgneal@45 14 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
bgneal@45 15 t.isMac = ua.indexOf('Mac') != -1;
bgneal@45 16 t.isAir = /adobeair/i.test(ua);
bgneal@45 17
bgneal@45 18 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
bgneal@45 19 if (w.tinyMCEPreInit) {
bgneal@45 20 t.suffix = tinyMCEPreInit.suffix;
bgneal@45 21 t.baseURL = tinyMCEPreInit.base;
bgneal@45 22 t.query = tinyMCEPreInit.query;
bgneal@45 23 return;
bgneal@45 24 }
bgneal@45 25
bgneal@45 26 // Get suffix and base
bgneal@45 27 t.suffix = '';
bgneal@45 28
bgneal@45 29 // If base element found, add that infront of baseURL
bgneal@45 30 nl = d.getElementsByTagName('base');
bgneal@45 31 for (i=0; i<nl.length; i++) {
bgneal@45 32 if (v = nl[i].href) {
bgneal@45 33 // Host only value like http://site.com or http://site.com:8008
bgneal@45 34 if (/^https?:\/\/[^\/]+$/.test(v))
bgneal@45 35 v += '/';
bgneal@45 36
bgneal@45 37 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
bgneal@45 38 }
bgneal@45 39 }
bgneal@45 40
bgneal@45 41 function getBase(n) {
bgneal@45 42 if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) {
bgneal@45 43 if (/_(src|dev)\.js/g.test(n.src))
bgneal@45 44 t.suffix = '_src';
bgneal@45 45
bgneal@45 46 if ((p = n.src.indexOf('?')) != -1)
bgneal@45 47 t.query = n.src.substring(p + 1);
bgneal@45 48
bgneal@45 49 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
bgneal@45 50
bgneal@45 51 // If path to script is relative and a base href was found add that one infront
bgneal@45 52 if (base && t.baseURL.indexOf('://') == -1)
bgneal@45 53 t.baseURL = base + t.baseURL;
bgneal@45 54
bgneal@45 55 return t.baseURL;
bgneal@45 56 }
bgneal@45 57
bgneal@45 58 return null;
bgneal@45 59 };
bgneal@45 60
bgneal@45 61 // Check document
bgneal@45 62 nl = d.getElementsByTagName('script');
bgneal@45 63 for (i=0; i<nl.length; i++) {
bgneal@45 64 if (getBase(nl[i]))
bgneal@45 65 return;
bgneal@45 66 }
bgneal@45 67
bgneal@45 68 // Check head
bgneal@45 69 n = d.getElementsByTagName('head')[0];
bgneal@45 70 if (n) {
bgneal@45 71 nl = n.getElementsByTagName('script');
bgneal@45 72 for (i=0; i<nl.length; i++) {
bgneal@45 73 if (getBase(nl[i]))
bgneal@45 74 return;
bgneal@45 75 }
bgneal@45 76 }
bgneal@45 77
bgneal@45 78 return;
bgneal@45 79 },
bgneal@45 80
bgneal@45 81 is : function(o, t) {
bgneal@45 82 var n = typeof(o);
bgneal@45 83
bgneal@45 84 if (!t)
bgneal@45 85 return n != 'undefined';
bgneal@45 86
bgneal@45 87 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
bgneal@45 88 return true;
bgneal@45 89
bgneal@45 90 return n == t;
bgneal@45 91 },
bgneal@45 92
bgneal@45 93
bgneal@45 94 each : function(o, cb, s) {
bgneal@45 95 var n, l;
bgneal@45 96
bgneal@45 97 if (!o)
bgneal@45 98 return 0;
bgneal@45 99
bgneal@45 100 s = s || o;
bgneal@45 101
bgneal@45 102 if (typeof(o.length) != 'undefined') {
bgneal@45 103 // Indexed arrays, needed for Safari
bgneal@45 104 for (n=0, l = o.length; n<l; n++) {
bgneal@45 105 if (cb.call(s, o[n], n, o) === false)
bgneal@45 106 return 0;
bgneal@45 107 }
bgneal@45 108 } else {
bgneal@45 109 // Hashtables
bgneal@45 110 for (n in o) {
bgneal@45 111 if (o.hasOwnProperty(n)) {
bgneal@45 112 if (cb.call(s, o[n], n, o) === false)
bgneal@45 113 return 0;
bgneal@45 114 }
bgneal@45 115 }
bgneal@45 116 }
bgneal@45 117
bgneal@45 118 return 1;
bgneal@45 119 },
bgneal@45 120
bgneal@45 121 map : function(a, f) {
bgneal@45 122 var o = [];
bgneal@45 123
bgneal@45 124 tinymce.each(a, function(v) {
bgneal@45 125 o.push(f(v));
bgneal@45 126 });
bgneal@45 127
bgneal@45 128 return o;
bgneal@45 129 },
bgneal@45 130
bgneal@45 131 grep : function(a, f) {
bgneal@45 132 var o = [];
bgneal@45 133
bgneal@45 134 tinymce.each(a, function(v) {
bgneal@45 135 if (!f || f(v))
bgneal@45 136 o.push(v);
bgneal@45 137 });
bgneal@45 138
bgneal@45 139 return o;
bgneal@45 140 },
bgneal@45 141
bgneal@45 142 inArray : function(a, v) {
bgneal@45 143 var i, l;
bgneal@45 144
bgneal@45 145 if (a) {
bgneal@45 146 for (i = 0, l = a.length; i < l; i++) {
bgneal@45 147 if (a[i] === v)
bgneal@45 148 return i;
bgneal@45 149 }
bgneal@45 150 }
bgneal@45 151
bgneal@45 152 return -1;
bgneal@45 153 },
bgneal@45 154
bgneal@45 155 extend : function(o, e) {
bgneal@45 156 var i, a = arguments;
bgneal@45 157
bgneal@45 158 for (i=1; i<a.length; i++) {
bgneal@45 159 e = a[i];
bgneal@45 160
bgneal@45 161 tinymce.each(e, function(v, n) {
bgneal@45 162 if (typeof(v) !== 'undefined')
bgneal@45 163 o[n] = v;
bgneal@45 164 });
bgneal@45 165 }
bgneal@45 166
bgneal@45 167 return o;
bgneal@45 168 },
bgneal@45 169
bgneal@45 170 trim : function(s) {
bgneal@45 171 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
bgneal@45 172 },
bgneal@45 173
bgneal@45 174
bgneal@45 175 create : function(s, p) {
bgneal@45 176 var t = this, sp, ns, cn, scn, c, de = 0;
bgneal@45 177
bgneal@45 178 // Parse : <prefix> <class>:<super class>
bgneal@45 179 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
bgneal@45 180 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
bgneal@45 181
bgneal@45 182 // Create namespace for new class
bgneal@45 183 ns = t.createNS(s[3].replace(/\.\w+$/, ''));
bgneal@45 184
bgneal@45 185 // Class already exists
bgneal@45 186 if (ns[cn])
bgneal@45 187 return;
bgneal@45 188
bgneal@45 189 // Make pure static class
bgneal@45 190 if (s[2] == 'static') {
bgneal@45 191 ns[cn] = p;
bgneal@45 192
bgneal@45 193 if (this.onCreate)
bgneal@45 194 this.onCreate(s[2], s[3], ns[cn]);
bgneal@45 195
bgneal@45 196 return;
bgneal@45 197 }
bgneal@45 198
bgneal@45 199 // Create default constructor
bgneal@45 200 if (!p[cn]) {
bgneal@45 201 p[cn] = function() {};
bgneal@45 202 de = 1;
bgneal@45 203 }
bgneal@45 204
bgneal@45 205 // Add constructor and methods
bgneal@45 206 ns[cn] = p[cn];
bgneal@45 207 t.extend(ns[cn].prototype, p);
bgneal@45 208
bgneal@45 209 // Extend
bgneal@45 210 if (s[5]) {
bgneal@45 211 sp = t.resolve(s[5]).prototype;
bgneal@45 212 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
bgneal@45 213
bgneal@45 214 // Extend constructor
bgneal@45 215 c = ns[cn];
bgneal@45 216 if (de) {
bgneal@45 217 // Add passthrough constructor
bgneal@45 218 ns[cn] = function() {
bgneal@45 219 return sp[scn].apply(this, arguments);
bgneal@45 220 };
bgneal@45 221 } else {
bgneal@45 222 // Add inherit constructor
bgneal@45 223 ns[cn] = function() {
bgneal@45 224 this.parent = sp[scn];
bgneal@45 225 return c.apply(this, arguments);
bgneal@45 226 };
bgneal@45 227 }
bgneal@45 228 ns[cn].prototype[cn] = ns[cn];
bgneal@45 229
bgneal@45 230 // Add super methods
bgneal@45 231 t.each(sp, function(f, n) {
bgneal@45 232 ns[cn].prototype[n] = sp[n];
bgneal@45 233 });
bgneal@45 234
bgneal@45 235 // Add overridden methods
bgneal@45 236 t.each(p, function(f, n) {
bgneal@45 237 // Extend methods if needed
bgneal@45 238 if (sp[n]) {
bgneal@45 239 ns[cn].prototype[n] = function() {
bgneal@45 240 this.parent = sp[n];
bgneal@45 241 return f.apply(this, arguments);
bgneal@45 242 };
bgneal@45 243 } else {
bgneal@45 244 if (n != cn)
bgneal@45 245 ns[cn].prototype[n] = f;
bgneal@45 246 }
bgneal@45 247 });
bgneal@45 248 }
bgneal@45 249
bgneal@45 250 // Add static methods
bgneal@45 251 t.each(p['static'], function(f, n) {
bgneal@45 252 ns[cn][n] = f;
bgneal@45 253 });
bgneal@45 254
bgneal@45 255 if (this.onCreate)
bgneal@45 256 this.onCreate(s[2], s[3], ns[cn].prototype);
bgneal@45 257 },
bgneal@45 258
bgneal@45 259 walk : function(o, f, n, s) {
bgneal@45 260 s = s || this;
bgneal@45 261
bgneal@45 262 if (o) {
bgneal@45 263 if (n)
bgneal@45 264 o = o[n];
bgneal@45 265
bgneal@45 266 tinymce.each(o, function(o, i) {
bgneal@45 267 if (f.call(s, o, i, n) === false)
bgneal@45 268 return false;
bgneal@45 269
bgneal@45 270 tinymce.walk(o, f, n, s);
bgneal@45 271 });
bgneal@45 272 }
bgneal@45 273 },
bgneal@45 274
bgneal@45 275 createNS : function(n, o) {
bgneal@45 276 var i, v;
bgneal@45 277
bgneal@45 278 o = o || window;
bgneal@45 279
bgneal@45 280 n = n.split('.');
bgneal@45 281 for (i=0; i<n.length; i++) {
bgneal@45 282 v = n[i];
bgneal@45 283
bgneal@45 284 if (!o[v])
bgneal@45 285 o[v] = {};
bgneal@45 286
bgneal@45 287 o = o[v];
bgneal@45 288 }
bgneal@45 289
bgneal@45 290 return o;
bgneal@45 291 },
bgneal@45 292
bgneal@45 293 resolve : function(n, o) {
bgneal@45 294 var i, l;
bgneal@45 295
bgneal@45 296 o = o || window;
bgneal@45 297
bgneal@45 298 n = n.split('.');
bgneal@45 299 for (i=0, l = n.length; i<l; i++) {
bgneal@45 300 o = o[n[i]];
bgneal@45 301
bgneal@45 302 if (!o)
bgneal@45 303 break;
bgneal@45 304 }
bgneal@45 305
bgneal@45 306 return o;
bgneal@45 307 },
bgneal@45 308
bgneal@45 309 addUnload : function(f, s) {
bgneal@45 310 var t = this, w = window;
bgneal@45 311
bgneal@45 312 f = {func : f, scope : s || this};
bgneal@45 313
bgneal@45 314 if (!t.unloads) {
bgneal@45 315 function unload() {
bgneal@45 316 var li = t.unloads, o, n;
bgneal@45 317
bgneal@45 318 if (li) {
bgneal@45 319 // Call unload handlers
bgneal@45 320 for (n in li) {
bgneal@45 321 o = li[n];
bgneal@45 322
bgneal@45 323 if (o && o.func)
bgneal@45 324 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
bgneal@45 325 }
bgneal@45 326
bgneal@45 327 // Detach unload function
bgneal@45 328 if (w.detachEvent) {
bgneal@45 329 w.detachEvent('onbeforeunload', fakeUnload);
bgneal@45 330 w.detachEvent('onunload', unload);
bgneal@45 331 } else if (w.removeEventListener)
bgneal@45 332 w.removeEventListener('unload', unload, false);
bgneal@45 333
bgneal@45 334 // Destroy references
bgneal@45 335 t.unloads = o = li = w = unload = null;
bgneal@45 336
bgneal@45 337 // Run garbarge collector on IE
bgneal@45 338 if (window.CollectGarbage)
bgneal@45 339 window.CollectGarbage();
bgneal@45 340 }
bgneal@45 341 };
bgneal@45 342
bgneal@45 343 function fakeUnload() {
bgneal@45 344 var d = document;
bgneal@45 345
bgneal@45 346 // Is there things still loading, then do some magic
bgneal@45 347 if (d.readyState == 'interactive') {
bgneal@45 348 function stop() {
bgneal@45 349 // Prevent memory leak
bgneal@45 350 d.detachEvent('onstop', stop);
bgneal@45 351
bgneal@45 352 // Call unload handler
bgneal@45 353 unload();
bgneal@45 354
bgneal@45 355 d = null;
bgneal@45 356 };
bgneal@45 357
bgneal@45 358 // Fire unload when the currently loading page is stopped
bgneal@45 359 d.attachEvent('onstop', stop);
bgneal@45 360
bgneal@45 361 // Remove onstop listener after a while to prevent the unload function
bgneal@45 362 // to execute if the user presses cancel in an onbeforeunload
bgneal@45 363 // confirm dialog and then presses the browser stop button
bgneal@45 364 window.setTimeout(function() {
bgneal@45 365 d.detachEvent('onstop', stop);
bgneal@45 366 }, 0);
bgneal@45 367 }
bgneal@45 368 };
bgneal@45 369
bgneal@45 370 // Attach unload handler
bgneal@45 371 if (w.attachEvent) {
bgneal@45 372 w.attachEvent('onunload', unload);
bgneal@45 373 w.attachEvent('onbeforeunload', fakeUnload);
bgneal@45 374 } else if (w.addEventListener)
bgneal@45 375 w.addEventListener('unload', unload, false);
bgneal@45 376
bgneal@45 377 // Setup initial unload handler array
bgneal@45 378 t.unloads = [f];
bgneal@45 379 } else
bgneal@45 380 t.unloads.push(f);
bgneal@45 381
bgneal@45 382 return f;
bgneal@45 383 },
bgneal@45 384
bgneal@45 385 removeUnload : function(f) {
bgneal@45 386 var u = this.unloads, r = null;
bgneal@45 387
bgneal@45 388 tinymce.each(u, function(o, i) {
bgneal@45 389 if (o && o.func == f) {
bgneal@45 390 u.splice(i, 1);
bgneal@45 391 r = f;
bgneal@45 392 return false;
bgneal@45 393 }
bgneal@45 394 });
bgneal@45 395
bgneal@45 396 return r;
bgneal@45 397 },
bgneal@45 398
bgneal@45 399 explode : function(s, d) {
bgneal@45 400 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
bgneal@45 401 },
bgneal@45 402
bgneal@45 403 _addVer : function(u) {
bgneal@45 404 var v;
bgneal@45 405
bgneal@45 406 if (!this.query)
bgneal@45 407 return u;
bgneal@45 408
bgneal@45 409 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
bgneal@45 410
bgneal@45 411 if (u.indexOf('#') == -1)
bgneal@45 412 return u + v;
bgneal@45 413
bgneal@45 414 return u.replace('#', v + '#');
bgneal@45 415 }
bgneal@45 416
bgneal@45 417 };
bgneal@45 418
bgneal@45 419 // Required for GZip AJAX loading
bgneal@45 420 window.tinymce = tinymce;
bgneal@45 421
bgneal@45 422 // Initialize the API
bgneal@45 423 tinymce._init();
bgneal@45 424 tinymce.create('tinymce.util.Dispatcher', {
bgneal@45 425 scope : null,
bgneal@45 426 listeners : null,
bgneal@45 427
bgneal@45 428 Dispatcher : function(s) {
bgneal@45 429 this.scope = s || this;
bgneal@45 430 this.listeners = [];
bgneal@45 431 },
bgneal@45 432
bgneal@45 433 add : function(cb, s) {
bgneal@45 434 this.listeners.push({cb : cb, scope : s || this.scope});
bgneal@45 435
bgneal@45 436 return cb;
bgneal@45 437 },
bgneal@45 438
bgneal@45 439 addToTop : function(cb, s) {
bgneal@45 440 this.listeners.unshift({cb : cb, scope : s || this.scope});
bgneal@45 441
bgneal@45 442 return cb;
bgneal@45 443 },
bgneal@45 444
bgneal@45 445 remove : function(cb) {
bgneal@45 446 var l = this.listeners, o = null;
bgneal@45 447
bgneal@45 448 tinymce.each(l, function(c, i) {
bgneal@45 449 if (cb == c.cb) {
bgneal@45 450 o = cb;
bgneal@45 451 l.splice(i, 1);
bgneal@45 452 return false;
bgneal@45 453 }
bgneal@45 454 });
bgneal@45 455
bgneal@45 456 return o;
bgneal@45 457 },
bgneal@45 458
bgneal@45 459 dispatch : function() {
bgneal@45 460 var s, a = arguments, i, li = this.listeners, c;
bgneal@45 461
bgneal@45 462 // Needs to be a real loop since the listener count might change while looping
bgneal@45 463 // And this is also more efficient
bgneal@45 464 for (i = 0; i<li.length; i++) {
bgneal@45 465 c = li[i];
bgneal@45 466 s = c.cb.apply(c.scope, a);
bgneal@45 467
bgneal@45 468 if (s === false)
bgneal@45 469 break;
bgneal@45 470 }
bgneal@45 471
bgneal@45 472 return s;
bgneal@45 473 }
bgneal@45 474
bgneal@45 475 });
bgneal@45 476 (function() {
bgneal@45 477 var each = tinymce.each;
bgneal@45 478
bgneal@45 479 tinymce.create('tinymce.util.URI', {
bgneal@45 480 URI : function(u, s) {
bgneal@45 481 var t = this, o, a, b;
bgneal@45 482
bgneal@45 483 // Default settings
bgneal@45 484 s = t.settings = s || {};
bgneal@45 485
bgneal@45 486 // Strange app protocol or local anchor
bgneal@45 487 if (/^(mailto|tel|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) {
bgneal@45 488 t.source = u;
bgneal@45 489 return;
bgneal@45 490 }
bgneal@45 491
bgneal@45 492 // Absolute path with no host, fake host and protocol
bgneal@45 493 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
bgneal@45 494 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
bgneal@45 495
bgneal@45 496 // Relative path
bgneal@45 497 if (u.indexOf(':/') === -1 && u.indexOf('//') !== 0)
bgneal@45 498 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
bgneal@45 499
bgneal@45 500 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
bgneal@45 501 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
bgneal@45 502 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
bgneal@45 503 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
bgneal@45 504 var s = u[i];
bgneal@45 505
bgneal@45 506 // Zope 3 workaround, they use @@something
bgneal@45 507 if (s)
bgneal@45 508 s = s.replace(/\(mce_at\)/g, '@@');
bgneal@45 509
bgneal@45 510 t[v] = s;
bgneal@45 511 });
bgneal@45 512
bgneal@45 513 if (b = s.base_uri) {
bgneal@45 514 if (!t.protocol)
bgneal@45 515 t.protocol = b.protocol;
bgneal@45 516
bgneal@45 517 if (!t.userInfo)
bgneal@45 518 t.userInfo = b.userInfo;
bgneal@45 519
bgneal@45 520 if (!t.port && t.host == 'mce_host')
bgneal@45 521 t.port = b.port;
bgneal@45 522
bgneal@45 523 if (!t.host || t.host == 'mce_host')
bgneal@45 524 t.host = b.host;
bgneal@45 525
bgneal@45 526 t.source = '';
bgneal@45 527 }
bgneal@45 528
bgneal@45 529 //t.path = t.path || '/';
bgneal@45 530 },
bgneal@45 531
bgneal@45 532 setPath : function(p) {
bgneal@45 533 var t = this;
bgneal@45 534
bgneal@45 535 p = /^(.*?)\/?(\w+)?$/.exec(p);
bgneal@45 536
bgneal@45 537 // Update path parts
bgneal@45 538 t.path = p[0];
bgneal@45 539 t.directory = p[1];
bgneal@45 540 t.file = p[2];
bgneal@45 541
bgneal@45 542 // Rebuild source
bgneal@45 543 t.source = '';
bgneal@45 544 t.getURI();
bgneal@45 545 },
bgneal@45 546
bgneal@45 547 toRelative : function(u) {
bgneal@45 548 var t = this, o;
bgneal@45 549
bgneal@45 550 if (u === "./")
bgneal@45 551 return u;
bgneal@45 552
bgneal@45 553 u = new tinymce.util.URI(u, {base_uri : t});
bgneal@45 554
bgneal@45 555 // Not on same domain/port or protocol
bgneal@45 556 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
bgneal@45 557 return u.getURI();
bgneal@45 558
bgneal@45 559 o = t.toRelPath(t.path, u.path);
bgneal@45 560
bgneal@45 561 // Add query
bgneal@45 562 if (u.query)
bgneal@45 563 o += '?' + u.query;
bgneal@45 564
bgneal@45 565 // Add anchor
bgneal@45 566 if (u.anchor)
bgneal@45 567 o += '#' + u.anchor;
bgneal@45 568
bgneal@45 569 return o;
bgneal@45 570 },
bgneal@45 571
bgneal@45 572 toAbsolute : function(u, nh) {
bgneal@45 573 var u = new tinymce.util.URI(u, {base_uri : this});
bgneal@45 574
bgneal@45 575 return u.getURI(this.host == u.host ? nh : 0);
bgneal@45 576 },
bgneal@45 577
bgneal@45 578 toRelPath : function(base, path) {
bgneal@45 579 var items, bp = 0, out = '', i, l;
bgneal@45 580
bgneal@45 581 // Split the paths
bgneal@45 582 base = base.substring(0, base.lastIndexOf('/'));
bgneal@45 583 base = base.split('/');
bgneal@45 584 items = path.split('/');
bgneal@45 585
bgneal@45 586 if (base.length >= items.length) {
bgneal@45 587 for (i = 0, l = base.length; i < l; i++) {
bgneal@45 588 if (i >= items.length || base[i] != items[i]) {
bgneal@45 589 bp = i + 1;
bgneal@45 590 break;
bgneal@45 591 }
bgneal@45 592 }
bgneal@45 593 }
bgneal@45 594
bgneal@45 595 if (base.length < items.length) {
bgneal@45 596 for (i = 0, l = items.length; i < l; i++) {
bgneal@45 597 if (i >= base.length || base[i] != items[i]) {
bgneal@45 598 bp = i + 1;
bgneal@45 599 break;
bgneal@45 600 }
bgneal@45 601 }
bgneal@45 602 }
bgneal@45 603
bgneal@45 604 if (bp == 1)
bgneal@45 605 return path;
bgneal@45 606
bgneal@45 607 for (i = 0, l = base.length - (bp - 1); i < l; i++)
bgneal@45 608 out += "../";
bgneal@45 609
bgneal@45 610 for (i = bp - 1, l = items.length; i < l; i++) {
bgneal@45 611 if (i != bp - 1)
bgneal@45 612 out += "/" + items[i];
bgneal@45 613 else
bgneal@45 614 out += items[i];
bgneal@45 615 }
bgneal@45 616
bgneal@45 617 return out;
bgneal@45 618 },
bgneal@45 619
bgneal@45 620 toAbsPath : function(base, path) {
bgneal@45 621 var i, nb = 0, o = [], tr;
bgneal@45 622
bgneal@45 623 // Split paths
bgneal@45 624 tr = /\/$/.test(path) ? '/' : '';
bgneal@45 625 base = base.split('/');
bgneal@45 626 path = path.split('/');
bgneal@45 627
bgneal@45 628 // Remove empty chunks
bgneal@45 629 each(base, function(k) {
bgneal@45 630 if (k)
bgneal@45 631 o.push(k);
bgneal@45 632 });
bgneal@45 633
bgneal@45 634 base = o;
bgneal@45 635
bgneal@45 636 // Merge relURLParts chunks
bgneal@45 637 for (i = path.length - 1, o = []; i >= 0; i--) {
bgneal@45 638 // Ignore empty or .
bgneal@45 639 if (path[i].length == 0 || path[i] == ".")
bgneal@45 640 continue;
bgneal@45 641
bgneal@45 642 // Is parent
bgneal@45 643 if (path[i] == '..') {
bgneal@45 644 nb++;
bgneal@45 645 continue;
bgneal@45 646 }
bgneal@45 647
bgneal@45 648 // Move up
bgneal@45 649 if (nb > 0) {
bgneal@45 650 nb--;
bgneal@45 651 continue;
bgneal@45 652 }
bgneal@45 653
bgneal@45 654 o.push(path[i]);
bgneal@45 655 }
bgneal@45 656
bgneal@45 657 i = base.length - nb;
bgneal@45 658
bgneal@45 659 // If /a/b/c or /
bgneal@45 660 if (i <= 0)
bgneal@45 661 return '/' + o.reverse().join('/') + tr;
bgneal@45 662
bgneal@45 663 return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr;
bgneal@45 664 },
bgneal@45 665
bgneal@45 666 getURI : function(nh) {
bgneal@45 667 var s, t = this;
bgneal@45 668
bgneal@45 669 // Rebuild source
bgneal@45 670 if (!t.source || nh) {
bgneal@45 671 s = '';
bgneal@45 672
bgneal@45 673 if (!nh) {
bgneal@45 674 if (t.protocol)
bgneal@45 675 s += t.protocol + '://';
bgneal@45 676
bgneal@45 677 if (t.userInfo)
bgneal@45 678 s += t.userInfo + '@';
bgneal@45 679
bgneal@45 680 if (t.host)
bgneal@45 681 s += t.host;
bgneal@45 682
bgneal@45 683 if (t.port)
bgneal@45 684 s += ':' + t.port;
bgneal@45 685 }
bgneal@45 686
bgneal@45 687 if (t.path)
bgneal@45 688 s += t.path;
bgneal@45 689
bgneal@45 690 if (t.query)
bgneal@45 691 s += '?' + t.query;
bgneal@45 692
bgneal@45 693 if (t.anchor)
bgneal@45 694 s += '#' + t.anchor;
bgneal@45 695
bgneal@45 696 t.source = s;
bgneal@45 697 }
bgneal@45 698
bgneal@45 699 return t.source;
bgneal@45 700 }
bgneal@45 701
bgneal@45 702 });
bgneal@45 703 })();
bgneal@45 704 (function() {
bgneal@45 705 var each = tinymce.each;
bgneal@45 706
bgneal@45 707 tinymce.create('static tinymce.util.Cookie', {
bgneal@45 708 getHash : function(n) {
bgneal@45 709 var v = this.get(n), h;
bgneal@45 710
bgneal@45 711 if (v) {
bgneal@45 712 each(v.split('&'), function(v) {
bgneal@45 713 v = v.split('=');
bgneal@45 714 h = h || {};
bgneal@45 715 h[unescape(v[0])] = unescape(v[1]);
bgneal@45 716 });
bgneal@45 717 }
bgneal@45 718
bgneal@45 719 return h;
bgneal@45 720 },
bgneal@45 721
bgneal@45 722 setHash : function(n, v, e, p, d, s) {
bgneal@45 723 var o = '';
bgneal@45 724
bgneal@45 725 each(v, function(v, k) {
bgneal@45 726 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
bgneal@45 727 });
bgneal@45 728
bgneal@45 729 this.set(n, o, e, p, d, s);
bgneal@45 730 },
bgneal@45 731
bgneal@45 732 get : function(n) {
bgneal@45 733 var c = document.cookie, e, p = n + "=", b;
bgneal@45 734
bgneal@45 735 // Strict mode
bgneal@45 736 if (!c)
bgneal@45 737 return;
bgneal@45 738
bgneal@45 739 b = c.indexOf("; " + p);
bgneal@45 740
bgneal@45 741 if (b == -1) {
bgneal@45 742 b = c.indexOf(p);
bgneal@45 743
bgneal@45 744 if (b != 0)
bgneal@45 745 return null;
bgneal@45 746 } else
bgneal@45 747 b += 2;
bgneal@45 748
bgneal@45 749 e = c.indexOf(";", b);
bgneal@45 750
bgneal@45 751 if (e == -1)
bgneal@45 752 e = c.length;
bgneal@45 753
bgneal@45 754 return unescape(c.substring(b + p.length, e));
bgneal@45 755 },
bgneal@45 756
bgneal@45 757 set : function(n, v, e, p, d, s) {
bgneal@45 758 document.cookie = n + "=" + escape(v) +
bgneal@45 759 ((e) ? "; expires=" + e.toGMTString() : "") +
bgneal@45 760 ((p) ? "; path=" + escape(p) : "") +
bgneal@45 761 ((d) ? "; domain=" + d : "") +
bgneal@45 762 ((s) ? "; secure" : "");
bgneal@45 763 },
bgneal@45 764
bgneal@45 765 remove : function(n, p) {
bgneal@45 766 var d = new Date();
bgneal@45 767
bgneal@45 768 d.setTime(d.getTime() - 1000);
bgneal@45 769
bgneal@45 770 this.set(n, '', d, p, d);
bgneal@45 771 }
bgneal@45 772
bgneal@45 773 });
bgneal@45 774 })();
bgneal@45 775 tinymce.create('static tinymce.util.JSON', {
bgneal@45 776 serialize : function(o) {
bgneal@45 777 var i, v, s = tinymce.util.JSON.serialize, t;
bgneal@45 778
bgneal@45 779 if (o == null)
bgneal@45 780 return 'null';
bgneal@45 781
bgneal@45 782 t = typeof o;
bgneal@45 783
bgneal@45 784 if (t == 'string') {
bgneal@45 785 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
bgneal@45 786
bgneal@45 787 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {
bgneal@45 788 i = v.indexOf(b);
bgneal@45 789
bgneal@45 790 if (i + 1)
bgneal@45 791 return '\\' + v.charAt(i + 1);
bgneal@45 792
bgneal@45 793 a = b.charCodeAt().toString(16);
bgneal@45 794
bgneal@45 795 return '\\u' + '0000'.substring(a.length) + a;
bgneal@45 796 }) + '"';
bgneal@45 797 }
bgneal@45 798
bgneal@45 799 if (t == 'object') {
bgneal@45 800 if (o.hasOwnProperty && o instanceof Array) {
bgneal@45 801 for (i=0, v = '['; i<o.length; i++)
bgneal@45 802 v += (i > 0 ? ',' : '') + s(o[i]);
bgneal@45 803
bgneal@45 804 return v + ']';
bgneal@45 805 }
bgneal@45 806
bgneal@45 807 v = '{';
bgneal@45 808
bgneal@45 809 for (i in o)
bgneal@45 810 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
bgneal@45 811
bgneal@45 812 return v + '}';
bgneal@45 813 }
bgneal@45 814
bgneal@45 815 return '' + o;
bgneal@45 816 },
bgneal@45 817
bgneal@45 818 parse : function(s) {
bgneal@45 819 try {
bgneal@45 820 return eval('(' + s + ')');
bgneal@45 821 } catch (ex) {
bgneal@45 822 // Ignore
bgneal@45 823 }
bgneal@45 824 }
bgneal@45 825
bgneal@45 826 });
bgneal@45 827 tinymce.create('static tinymce.util.XHR', {
bgneal@45 828 send : function(o) {
bgneal@45 829 var x, t, w = window, c = 0;
bgneal@45 830
bgneal@45 831 // Default settings
bgneal@45 832 o.scope = o.scope || this;
bgneal@45 833 o.success_scope = o.success_scope || o.scope;
bgneal@45 834 o.error_scope = o.error_scope || o.scope;
bgneal@45 835 o.async = o.async === false ? false : true;
bgneal@45 836 o.data = o.data || '';
bgneal@45 837
bgneal@45 838 function get(s) {
bgneal@45 839 x = 0;
bgneal@45 840
bgneal@45 841 try {
bgneal@45 842 x = new ActiveXObject(s);
bgneal@45 843 } catch (ex) {
bgneal@45 844 }
bgneal@45 845
bgneal@45 846 return x;
bgneal@45 847 };
bgneal@45 848
bgneal@45 849 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
bgneal@45 850
bgneal@45 851 if (x) {
bgneal@45 852 if (x.overrideMimeType)
bgneal@45 853 x.overrideMimeType(o.content_type);
bgneal@45 854
bgneal@45 855 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
bgneal@45 856
bgneal@45 857 if (o.content_type)
bgneal@45 858 x.setRequestHeader('Content-Type', o.content_type);
bgneal@45 859
bgneal@45 860 x.send(o.data);
bgneal@45 861
bgneal@45 862 function ready() {
bgneal@45 863 if (!o.async || x.readyState == 4 || c++ > 10000) {
bgneal@45 864 if (o.success && c < 10000 && x.status == 200)
bgneal@45 865 o.success.call(o.success_scope, '' + x.responseText, x, o);
bgneal@45 866 else if (o.error)
bgneal@45 867 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
bgneal@45 868
bgneal@45 869 x = null;
bgneal@45 870 } else
bgneal@45 871 w.setTimeout(ready, 10);
bgneal@45 872 };
bgneal@45 873
bgneal@45 874 // Syncronous request
bgneal@45 875 if (!o.async)
bgneal@45 876 return ready();
bgneal@45 877
bgneal@45 878 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
bgneal@45 879 t = w.setTimeout(ready, 10);
bgneal@45 880 }
bgneal@45 881
bgneal@45 882 }
bgneal@45 883 });
bgneal@45 884 (function() {
bgneal@45 885 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
bgneal@45 886
bgneal@45 887 tinymce.create('tinymce.util.JSONRequest', {
bgneal@45 888 JSONRequest : function(s) {
bgneal@45 889 this.settings = extend({
bgneal@45 890 }, s);
bgneal@45 891 this.count = 0;
bgneal@45 892 },
bgneal@45 893
bgneal@45 894 send : function(o) {
bgneal@45 895 var ecb = o.error, scb = o.success;
bgneal@45 896
bgneal@45 897 o = extend(this.settings, o);
bgneal@45 898
bgneal@45 899 o.success = function(c, x) {
bgneal@45 900 c = JSON.parse(c);
bgneal@45 901
bgneal@45 902 if (typeof(c) == 'undefined') {
bgneal@45 903 c = {
bgneal@45 904 error : 'JSON Parse error.'
bgneal@45 905 };
bgneal@45 906 }
bgneal@45 907
bgneal@45 908 if (c.error)
bgneal@45 909 ecb.call(o.error_scope || o.scope, c.error, x);
bgneal@45 910 else
bgneal@45 911 scb.call(o.success_scope || o.scope, c.result);
bgneal@45 912 };
bgneal@45 913
bgneal@45 914 o.error = function(ty, x) {
bgneal@45 915 ecb.call(o.error_scope || o.scope, ty, x);
bgneal@45 916 };
bgneal@45 917
bgneal@45 918 o.data = JSON.serialize({
bgneal@45 919 id : o.id || 'c' + (this.count++),
bgneal@45 920 method : o.method,
bgneal@45 921 params : o.params
bgneal@45 922 });
bgneal@45 923
bgneal@45 924 // JSON content type for Ruby on rails. Bug: #1883287
bgneal@45 925 o.content_type = 'application/json';
bgneal@45 926
bgneal@45 927 XHR.send(o);
bgneal@45 928 },
bgneal@45 929
bgneal@45 930 'static' : {
bgneal@45 931 sendRPC : function(o) {
bgneal@45 932 return new tinymce.util.JSONRequest().send(o);
bgneal@45 933 }
bgneal@45 934 }
bgneal@45 935
bgneal@45 936 });
bgneal@45 937 }());(function(tinymce) {
bgneal@45 938 // Shorten names
bgneal@45 939 var each = tinymce.each, is = tinymce.is;
bgneal@45 940 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;
bgneal@45 941
bgneal@45 942 tinymce.create('tinymce.dom.DOMUtils', {
bgneal@45 943 doc : null,
bgneal@45 944 root : null,
bgneal@45 945 files : null,
bgneal@45 946 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
bgneal@45 947 props : {
bgneal@45 948 "for" : "htmlFor",
bgneal@45 949 "class" : "className",
bgneal@45 950 className : "className",
bgneal@45 951 checked : "checked",
bgneal@45 952 disabled : "disabled",
bgneal@45 953 maxlength : "maxLength",
bgneal@45 954 readonly : "readOnly",
bgneal@45 955 selected : "selected",
bgneal@45 956 value : "value",
bgneal@45 957 id : "id",
bgneal@45 958 name : "name",
bgneal@45 959 type : "type"
bgneal@45 960 },
bgneal@45 961
bgneal@45 962 DOMUtils : function(d, s) {
bgneal@45 963 var t = this;
bgneal@45 964
bgneal@45 965 t.doc = d;
bgneal@45 966 t.win = window;
bgneal@45 967 t.files = {};
bgneal@45 968 t.cssFlicker = false;
bgneal@45 969 t.counter = 0;
bgneal@45 970 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat";
bgneal@45 971 t.stdMode = d.documentMode === 8;
bgneal@45 972
bgneal@45 973 this.settings = s = tinymce.extend({
bgneal@45 974 keep_values : false,
bgneal@45 975 hex_colors : 1,
bgneal@45 976 process_html : 1
bgneal@45 977 }, s);
bgneal@45 978
bgneal@45 979 // Fix IE6SP2 flicker and check it failed for pre SP2
bgneal@45 980 if (tinymce.isIE6) {
bgneal@45 981 try {
bgneal@45 982 d.execCommand('BackgroundImageCache', false, true);
bgneal@45 983 } catch (e) {
bgneal@45 984 t.cssFlicker = true;
bgneal@45 985 }
bgneal@45 986 }
bgneal@45 987
bgneal@45 988 tinymce.addUnload(t.destroy, t);
bgneal@45 989 },
bgneal@45 990
bgneal@45 991 getRoot : function() {
bgneal@45 992 var t = this, s = t.settings;
bgneal@45 993
bgneal@45 994 return (s && t.get(s.root_element)) || t.doc.body;
bgneal@45 995 },
bgneal@45 996
bgneal@45 997 getViewPort : function(w) {
bgneal@45 998 var d, b;
bgneal@45 999
bgneal@45 1000 w = !w ? this.win : w;
bgneal@45 1001 d = w.document;
bgneal@45 1002 b = this.boxModel ? d.documentElement : d.body;
bgneal@45 1003
bgneal@45 1004 // Returns viewport size excluding scrollbars
bgneal@45 1005 return {
bgneal@45 1006 x : w.pageXOffset || b.scrollLeft,
bgneal@45 1007 y : w.pageYOffset || b.scrollTop,
bgneal@45 1008 w : w.innerWidth || b.clientWidth,
bgneal@45 1009 h : w.innerHeight || b.clientHeight
bgneal@45 1010 };
bgneal@45 1011 },
bgneal@45 1012
bgneal@45 1013 getRect : function(e) {
bgneal@45 1014 var p, t = this, sr;
bgneal@45 1015
bgneal@45 1016 e = t.get(e);
bgneal@45 1017 p = t.getPos(e);
bgneal@45 1018 sr = t.getSize(e);
bgneal@45 1019
bgneal@45 1020 return {
bgneal@45 1021 x : p.x,
bgneal@45 1022 y : p.y,
bgneal@45 1023 w : sr.w,
bgneal@45 1024 h : sr.h
bgneal@45 1025 };
bgneal@45 1026 },
bgneal@45 1027
bgneal@45 1028 getSize : function(e) {
bgneal@45 1029 var t = this, w, h;
bgneal@45 1030
bgneal@45 1031 e = t.get(e);
bgneal@45 1032 w = t.getStyle(e, 'width');
bgneal@45 1033 h = t.getStyle(e, 'height');
bgneal@45 1034
bgneal@45 1035 // Non pixel value, then force offset/clientWidth
bgneal@45 1036 if (w.indexOf('px') === -1)
bgneal@45 1037 w = 0;
bgneal@45 1038
bgneal@45 1039 // Non pixel value, then force offset/clientWidth
bgneal@45 1040 if (h.indexOf('px') === -1)
bgneal@45 1041 h = 0;
bgneal@45 1042
bgneal@45 1043 return {
bgneal@45 1044 w : parseInt(w) || e.offsetWidth || e.clientWidth,
bgneal@45 1045 h : parseInt(h) || e.offsetHeight || e.clientHeight
bgneal@45 1046 };
bgneal@45 1047 },
bgneal@45 1048
bgneal@45 1049 is : function(n, patt) {
bgneal@45 1050 return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0;
bgneal@45 1051 },
bgneal@45 1052
bgneal@45 1053 getParent : function(n, f, r) {
bgneal@45 1054 return this.getParents(n, f, r, false);
bgneal@45 1055 },
bgneal@45 1056
bgneal@45 1057 getParents : function(n, f, r, c) {
bgneal@45 1058 var t = this, na, se = t.settings, o = [];
bgneal@45 1059
bgneal@45 1060 n = t.get(n);
bgneal@45 1061 c = c === undefined;
bgneal@45 1062
bgneal@45 1063 if (se.strict_root)
bgneal@45 1064 r = r || t.getRoot();
bgneal@45 1065
bgneal@45 1066 // Wrap node name as func
bgneal@45 1067 if (is(f, 'string')) {
bgneal@45 1068 na = f;
bgneal@45 1069
bgneal@45 1070 if (f === '*') {
bgneal@45 1071 f = function(n) {return n.nodeType == 1;};
bgneal@45 1072 } else {
bgneal@45 1073 f = function(n) {
bgneal@45 1074 return t.is(n, na);
bgneal@45 1075 };
bgneal@45 1076 }
bgneal@45 1077 }
bgneal@45 1078
bgneal@45 1079 while (n) {
bgneal@45 1080 if (n == r)
bgneal@45 1081 break;
bgneal@45 1082
bgneal@45 1083 if (!f || f(n)) {
bgneal@45 1084 if (c)
bgneal@45 1085 o.push(n);
bgneal@45 1086 else
bgneal@45 1087 return n;
bgneal@45 1088 }
bgneal@45 1089
bgneal@45 1090 n = n.parentNode;
bgneal@45 1091 }
bgneal@45 1092
bgneal@45 1093 return c ? o : null;
bgneal@45 1094 },
bgneal@45 1095
bgneal@45 1096 get : function(e) {
bgneal@45 1097 var n;
bgneal@45 1098
bgneal@45 1099 if (e && this.doc && typeof(e) == 'string') {
bgneal@45 1100 n = e;
bgneal@45 1101 e = this.doc.getElementById(e);
bgneal@45 1102
bgneal@45 1103 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
bgneal@45 1104 if (e && e.id !== n)
bgneal@45 1105 return this.doc.getElementsByName(n)[1];
bgneal@45 1106 }
bgneal@45 1107
bgneal@45 1108 return e;
bgneal@45 1109 },
bgneal@45 1110
bgneal@45 1111
bgneal@45 1112 select : function(pa, s) {
bgneal@45 1113 var t = this;
bgneal@45 1114
bgneal@45 1115 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);
bgneal@45 1116 },
bgneal@45 1117
bgneal@45 1118
bgneal@45 1119 add : function(p, n, a, h, c) {
bgneal@45 1120 var t = this;
bgneal@45 1121
bgneal@45 1122 return this.run(p, function(p) {
bgneal@45 1123 var e, k;
bgneal@45 1124
bgneal@45 1125 e = is(n, 'string') ? t.doc.createElement(n) : n;
bgneal@45 1126 t.setAttribs(e, a);
bgneal@45 1127
bgneal@45 1128 if (h) {
bgneal@45 1129 if (h.nodeType)
bgneal@45 1130 e.appendChild(h);
bgneal@45 1131 else
bgneal@45 1132 t.setHTML(e, h);
bgneal@45 1133 }
bgneal@45 1134
bgneal@45 1135 return !c ? p.appendChild(e) : e;
bgneal@45 1136 });
bgneal@45 1137 },
bgneal@45 1138
bgneal@45 1139 create : function(n, a, h) {
bgneal@45 1140 return this.add(this.doc.createElement(n), n, a, h, 1);
bgneal@45 1141 },
bgneal@45 1142
bgneal@45 1143 createHTML : function(n, a, h) {
bgneal@45 1144 var o = '', t = this, k;
bgneal@45 1145
bgneal@45 1146 o += '<' + n;
bgneal@45 1147
bgneal@45 1148 for (k in a) {
bgneal@45 1149 if (a.hasOwnProperty(k))
bgneal@45 1150 o += ' ' + k + '="' + t.encode(a[k]) + '"';
bgneal@45 1151 }
bgneal@45 1152
bgneal@45 1153 if (tinymce.is(h))
bgneal@45 1154 return o + '>' + h + '</' + n + '>';
bgneal@45 1155
bgneal@45 1156 return o + ' />';
bgneal@45 1157 },
bgneal@45 1158
bgneal@45 1159 remove : function(n, k) {
bgneal@45 1160 var t = this;
bgneal@45 1161
bgneal@45 1162 return this.run(n, function(n) {
bgneal@45 1163 var p, g, i;
bgneal@45 1164
bgneal@45 1165 p = n.parentNode;
bgneal@45 1166
bgneal@45 1167 if (!p)
bgneal@45 1168 return null;
bgneal@45 1169
bgneal@45 1170 if (k) {
bgneal@45 1171 for (i = n.childNodes.length - 1; i >= 0; i--)
bgneal@45 1172 t.insertAfter(n.childNodes[i], n);
bgneal@45 1173
bgneal@45 1174 //each(n.childNodes, function(c) {
bgneal@45 1175 // p.insertBefore(c.cloneNode(true), n);
bgneal@45 1176 //});
bgneal@45 1177 }
bgneal@45 1178
bgneal@45 1179 // Fix IE psuedo leak
bgneal@45 1180 if (t.fixPsuedoLeaks) {
bgneal@45 1181 p = n.cloneNode(true);
bgneal@45 1182 k = 'IELeakGarbageBin';
bgneal@45 1183 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});
bgneal@45 1184 g.appendChild(n);
bgneal@45 1185 g.innerHTML = '';
bgneal@45 1186
bgneal@45 1187 return p;
bgneal@45 1188 }
bgneal@45 1189
bgneal@45 1190 return p.removeChild(n);
bgneal@45 1191 });
bgneal@45 1192 },
bgneal@45 1193
bgneal@45 1194
bgneal@45 1195 setStyle : function(n, na, v) {
bgneal@45 1196 var t = this;
bgneal@45 1197
bgneal@45 1198 return t.run(n, function(e) {
bgneal@45 1199 var s, i;
bgneal@45 1200
bgneal@45 1201 s = e.style;
bgneal@45 1202
bgneal@45 1203 // Camelcase it, if needed
bgneal@45 1204 na = na.replace(/-(\D)/g, function(a, b){
bgneal@45 1205 return b.toUpperCase();
bgneal@45 1206 });
bgneal@45 1207
bgneal@45 1208 // Default px suffix on these
bgneal@45 1209 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
bgneal@45 1210 v += 'px';
bgneal@45 1211
bgneal@45 1212 switch (na) {
bgneal@45 1213 case 'opacity':
bgneal@45 1214 // IE specific opacity
bgneal@45 1215 if (isIE) {
bgneal@45 1216 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
bgneal@45 1217
bgneal@45 1218 if (!n.currentStyle || !n.currentStyle.hasLayout)
bgneal@45 1219 s.display = 'inline-block';
bgneal@45 1220 }
bgneal@45 1221
bgneal@45 1222 // Fix for older browsers
bgneal@45 1223 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
bgneal@45 1224 break;
bgneal@45 1225
bgneal@45 1226 case 'float':
bgneal@45 1227 isIE ? s.styleFloat = v : s.cssFloat = v;
bgneal@45 1228 break;
bgneal@45 1229
bgneal@45 1230 default:
bgneal@45 1231 s[na] = v || '';
bgneal@45 1232 }
bgneal@45 1233
bgneal@45 1234 // Force update of the style data
bgneal@45 1235 if (t.settings.update_styles)
bgneal@45 1236 t.setAttrib(e, 'mce_style');
bgneal@45 1237 });
bgneal@45 1238 },
bgneal@45 1239
bgneal@45 1240 getStyle : function(n, na, c) {
bgneal@45 1241 n = this.get(n);
bgneal@45 1242
bgneal@45 1243 if (!n)
bgneal@45 1244 return false;
bgneal@45 1245
bgneal@45 1246 // Gecko
bgneal@45 1247 if (this.doc.defaultView && c) {
bgneal@45 1248 // Remove camelcase
bgneal@45 1249 na = na.replace(/[A-Z]/g, function(a){
bgneal@45 1250 return '-' + a;
bgneal@45 1251 });
bgneal@45 1252
bgneal@45 1253 try {
bgneal@45 1254 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
bgneal@45 1255 } catch (ex) {
bgneal@45 1256 // Old safari might fail
bgneal@45 1257 return null;
bgneal@45 1258 }
bgneal@45 1259 }
bgneal@45 1260
bgneal@45 1261 // Camelcase it, if needed
bgneal@45 1262 na = na.replace(/-(\D)/g, function(a, b){
bgneal@45 1263 return b.toUpperCase();
bgneal@45 1264 });
bgneal@45 1265
bgneal@45 1266 if (na == 'float')
bgneal@45 1267 na = isIE ? 'styleFloat' : 'cssFloat';
bgneal@45 1268
bgneal@45 1269 // IE & Opera
bgneal@45 1270 if (n.currentStyle && c)
bgneal@45 1271 return n.currentStyle[na];
bgneal@45 1272
bgneal@45 1273 return n.style[na];
bgneal@45 1274 },
bgneal@45 1275
bgneal@45 1276 setStyles : function(e, o) {
bgneal@45 1277 var t = this, s = t.settings, ol;
bgneal@45 1278
bgneal@45 1279 ol = s.update_styles;
bgneal@45 1280 s.update_styles = 0;
bgneal@45 1281
bgneal@45 1282 each(o, function(v, n) {
bgneal@45 1283 t.setStyle(e, n, v);
bgneal@45 1284 });
bgneal@45 1285
bgneal@45 1286 // Update style info
bgneal@45 1287 s.update_styles = ol;
bgneal@45 1288 if (s.update_styles)
bgneal@45 1289 t.setAttrib(e, s.cssText);
bgneal@45 1290 },
bgneal@45 1291
bgneal@45 1292 setAttrib : function(e, n, v) {
bgneal@45 1293 var t = this;
bgneal@45 1294
bgneal@45 1295 // Whats the point
bgneal@45 1296 if (!e || !n)
bgneal@45 1297 return;
bgneal@45 1298
bgneal@45 1299 // Strict XML mode
bgneal@45 1300 if (t.settings.strict)
bgneal@45 1301 n = n.toLowerCase();
bgneal@45 1302
bgneal@45 1303 return this.run(e, function(e) {
bgneal@45 1304 var s = t.settings;
bgneal@45 1305
bgneal@45 1306 switch (n) {
bgneal@45 1307 case "style":
bgneal@45 1308 if (!is(v, 'string')) {
bgneal@45 1309 each(v, function(v, n) {
bgneal@45 1310 t.setStyle(e, n, v);
bgneal@45 1311 });
bgneal@45 1312
bgneal@45 1313 return;
bgneal@45 1314 }
bgneal@45 1315
bgneal@45 1316 // No mce_style for elements with these since they might get resized by the user
bgneal@45 1317 if (s.keep_values) {
bgneal@45 1318 if (v && !t._isRes(v))
bgneal@45 1319 e.setAttribute('mce_style', v, 2);
bgneal@45 1320 else
bgneal@45 1321 e.removeAttribute('mce_style', 2);
bgneal@45 1322 }
bgneal@45 1323
bgneal@45 1324 e.style.cssText = v;
bgneal@45 1325 break;
bgneal@45 1326
bgneal@45 1327 case "class":
bgneal@45 1328 e.className = v || ''; // Fix IE null bug
bgneal@45 1329 break;
bgneal@45 1330
bgneal@45 1331 case "src":
bgneal@45 1332 case "href":
bgneal@45 1333 if (s.keep_values) {
bgneal@45 1334 if (s.url_converter)
bgneal@45 1335 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
bgneal@45 1336
bgneal@45 1337 t.setAttrib(e, 'mce_' + n, v, 2);
bgneal@45 1338 }
bgneal@45 1339
bgneal@45 1340 break;
bgneal@45 1341
bgneal@45 1342 case "shape":
bgneal@45 1343 e.setAttribute('mce_style', v);
bgneal@45 1344 break;
bgneal@45 1345 }
bgneal@45 1346
bgneal@45 1347 if (is(v) && v !== null && v.length !== 0)
bgneal@45 1348 e.setAttribute(n, '' + v, 2);
bgneal@45 1349 else
bgneal@45 1350 e.removeAttribute(n, 2);
bgneal@45 1351 });
bgneal@45 1352 },
bgneal@45 1353
bgneal@45 1354 setAttribs : function(e, o) {
bgneal@45 1355 var t = this;
bgneal@45 1356
bgneal@45 1357 return this.run(e, function(e) {
bgneal@45 1358 each(o, function(v, n) {
bgneal@45 1359 t.setAttrib(e, n, v);
bgneal@45 1360 });
bgneal@45 1361 });
bgneal@45 1362 },
bgneal@45 1363
bgneal@45 1364
bgneal@45 1365 getAttrib : function(e, n, dv) {
bgneal@45 1366 var v, t = this;
bgneal@45 1367
bgneal@45 1368 e = t.get(e);
bgneal@45 1369
bgneal@45 1370 if (!e || e.nodeType !== 1)
bgneal@45 1371 return false;
bgneal@45 1372
bgneal@45 1373 if (!is(dv))
bgneal@45 1374 dv = '';
bgneal@45 1375
bgneal@45 1376 // Try the mce variant for these
bgneal@45 1377 if (/^(src|href|style|coords|shape)$/.test(n)) {
bgneal@45 1378 v = e.getAttribute("mce_" + n);
bgneal@45 1379
bgneal@45 1380 if (v)
bgneal@45 1381 return v;
bgneal@45 1382 }
bgneal@45 1383
bgneal@45 1384 if (isIE && t.props[n]) {
bgneal@45 1385 v = e[t.props[n]];
bgneal@45 1386 v = v && v.nodeValue ? v.nodeValue : v;
bgneal@45 1387 }
bgneal@45 1388
bgneal@45 1389 if (!v)
bgneal@45 1390 v = e.getAttribute(n, 2);
bgneal@45 1391
bgneal@45 1392 if (n === 'style') {
bgneal@45 1393 v = v || e.style.cssText;
bgneal@45 1394
bgneal@45 1395 if (v) {
bgneal@45 1396 v = t.serializeStyle(t.parseStyle(v));
bgneal@45 1397
bgneal@45 1398 if (t.settings.keep_values && !t._isRes(v))
bgneal@45 1399 e.setAttribute('mce_style', v);
bgneal@45 1400 }
bgneal@45 1401 }
bgneal@45 1402
bgneal@45 1403 // Remove Apple and WebKit stuff
bgneal@45 1404 if (isWebKit && n === "class" && v)
bgneal@45 1405 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
bgneal@45 1406
bgneal@45 1407 // Handle IE issues
bgneal@45 1408 if (isIE) {
bgneal@45 1409 switch (n) {
bgneal@45 1410 case 'rowspan':
bgneal@45 1411 case 'colspan':
bgneal@45 1412 // IE returns 1 as default value
bgneal@45 1413 if (v === 1)
bgneal@45 1414 v = '';
bgneal@45 1415
bgneal@45 1416 break;
bgneal@45 1417
bgneal@45 1418 case 'size':
bgneal@45 1419 // IE returns +0 as default value for size
bgneal@45 1420 if (v === '+0' || v === 20 || v === 0)
bgneal@45 1421 v = '';
bgneal@45 1422
bgneal@45 1423 break;
bgneal@45 1424
bgneal@45 1425 case 'width':
bgneal@45 1426 case 'height':
bgneal@45 1427 case 'vspace':
bgneal@45 1428 case 'checked':
bgneal@45 1429 case 'disabled':
bgneal@45 1430 case 'readonly':
bgneal@45 1431 if (v === 0)
bgneal@45 1432 v = '';
bgneal@45 1433
bgneal@45 1434 break;
bgneal@45 1435
bgneal@45 1436 case 'hspace':
bgneal@45 1437 // IE returns -1 as default value
bgneal@45 1438 if (v === -1)
bgneal@45 1439 v = '';
bgneal@45 1440
bgneal@45 1441 break;
bgneal@45 1442
bgneal@45 1443 case 'maxlength':
bgneal@45 1444 case 'tabindex':
bgneal@45 1445 // IE returns default value
bgneal@45 1446 if (v === 32768 || v === 2147483647 || v === '32768')
bgneal@45 1447 v = '';
bgneal@45 1448
bgneal@45 1449 break;
bgneal@45 1450
bgneal@45 1451 case 'multiple':
bgneal@45 1452 case 'compact':
bgneal@45 1453 case 'noshade':
bgneal@45 1454 case 'nowrap':
bgneal@45 1455 if (v === 65535)
bgneal@45 1456 return n;
bgneal@45 1457
bgneal@45 1458 return dv;
bgneal@45 1459
bgneal@45 1460 case 'shape':
bgneal@45 1461 v = v.toLowerCase();
bgneal@45 1462 break;
bgneal@45 1463
bgneal@45 1464 default:
bgneal@45 1465 // IE has odd anonymous function for event attributes
bgneal@45 1466 if (n.indexOf('on') === 0 && v)
bgneal@45 1467 v = ('' + v).replace(/^function\s+anonymous\(\)\s+\{\s+(.*)\s+\}$/, '$1');
bgneal@45 1468 }
bgneal@45 1469 }
bgneal@45 1470
bgneal@45 1471 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;
bgneal@45 1472 },
bgneal@45 1473
bgneal@45 1474 getPos : function(n) {
bgneal@45 1475 var t = this, x = 0, y = 0, e, d = t.doc, r;
bgneal@45 1476
bgneal@45 1477 n = t.get(n);
bgneal@45 1478
bgneal@45 1479 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
bgneal@45 1480 if (n && isIE && !t.stdMode) {
bgneal@45 1481 n = n.getBoundingClientRect();
bgneal@45 1482 e = t.boxModel ? d.documentElement : d.body;
bgneal@45 1483 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
bgneal@45 1484 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
bgneal@45 1485 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset
bgneal@45 1486
bgneal@45 1487 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
bgneal@45 1488 }
bgneal@45 1489
bgneal@45 1490 r = n;
bgneal@45 1491 while (r) {
bgneal@45 1492 x += r.offsetLeft || 0;
bgneal@45 1493 y += r.offsetTop || 0;
bgneal@45 1494 r = r.offsetParent;
bgneal@45 1495 }
bgneal@45 1496
bgneal@45 1497 r = n;
bgneal@45 1498 while (r) {
bgneal@45 1499 // Opera 9.25 bug fix, fixed in 9.50
bgneal@45 1500 if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) {
bgneal@45 1501 x -= r.scrollLeft || 0;
bgneal@45 1502 y -= r.scrollTop || 0;
bgneal@45 1503 }
bgneal@45 1504
bgneal@45 1505 r = r.parentNode;
bgneal@45 1506
bgneal@45 1507 // No node type or document type
bgneal@45 1508 if (!r.nodeType || r.nodeType == 9 || r.nodeName.toLowerCase() == 'body')
bgneal@45 1509 break;
bgneal@45 1510 }
bgneal@45 1511
bgneal@45 1512 return {x : x, y : y};
bgneal@45 1513 },
bgneal@45 1514
bgneal@45 1515 parseStyle : function(st) {
bgneal@45 1516 var t = this, s = t.settings, o = {};
bgneal@45 1517
bgneal@45 1518 if (!st)
bgneal@45 1519 return o;
bgneal@45 1520
bgneal@45 1521 function compress(p, s, ot) {
bgneal@45 1522 var t, r, b, l;
bgneal@45 1523
bgneal@45 1524 // Get values and check it it needs compressing
bgneal@45 1525 t = o[p + '-top' + s];
bgneal@45 1526 if (!t)
bgneal@45 1527 return;
bgneal@45 1528
bgneal@45 1529 r = o[p + '-right' + s];
bgneal@45 1530 if (t != r)
bgneal@45 1531 return;
bgneal@45 1532
bgneal@45 1533 b = o[p + '-bottom' + s];
bgneal@45 1534 if (r != b)
bgneal@45 1535 return;
bgneal@45 1536
bgneal@45 1537 l = o[p + '-left' + s];
bgneal@45 1538 if (b != l)
bgneal@45 1539 return;
bgneal@45 1540
bgneal@45 1541 // Compress
bgneal@45 1542 o[ot] = l;
bgneal@45 1543 delete o[p + '-top' + s];
bgneal@45 1544 delete o[p + '-right' + s];
bgneal@45 1545 delete o[p + '-bottom' + s];
bgneal@45 1546 delete o[p + '-left' + s];
bgneal@45 1547 };
bgneal@45 1548
bgneal@45 1549 function compress2(ta, a, b, c) {
bgneal@45 1550 var t;
bgneal@45 1551
bgneal@45 1552 t = o[a];
bgneal@45 1553 if (!t)
bgneal@45 1554 return;
bgneal@45 1555
bgneal@45 1556 t = o[b];
bgneal@45 1557 if (!t)
bgneal@45 1558 return;
bgneal@45 1559
bgneal@45 1560 t = o[c];
bgneal@45 1561 if (!t)
bgneal@45 1562 return;
bgneal@45 1563
bgneal@45 1564 // Compress
bgneal@45 1565 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
bgneal@45 1566 delete o[a];
bgneal@45 1567 delete o[b];
bgneal@45 1568 delete o[c];
bgneal@45 1569 };
bgneal@45 1570
bgneal@45 1571 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
bgneal@45 1572
bgneal@45 1573 each(st.split(';'), function(v) {
bgneal@45 1574 var sv, ur = [];
bgneal@45 1575
bgneal@45 1576 if (v) {
bgneal@45 1577 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
bgneal@45 1578 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
bgneal@45 1579 v = v.split(':');
bgneal@45 1580 sv = tinymce.trim(v[1]);
bgneal@45 1581 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});
bgneal@45 1582
bgneal@45 1583 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
bgneal@45 1584 return t.toHex(v);
bgneal@45 1585 });
bgneal@45 1586
bgneal@45 1587 if (s.url_converter) {
bgneal@45 1588 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
bgneal@45 1589 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
bgneal@45 1590 });
bgneal@45 1591 }
bgneal@45 1592
bgneal@45 1593 o[tinymce.trim(v[0]).toLowerCase()] = sv;
bgneal@45 1594 }
bgneal@45 1595 });
bgneal@45 1596
bgneal@45 1597 compress("border", "", "border");
bgneal@45 1598 compress("border", "-width", "border-width");
bgneal@45 1599 compress("border", "-color", "border-color");
bgneal@45 1600 compress("border", "-style", "border-style");
bgneal@45 1601 compress("padding", "", "padding");
bgneal@45 1602 compress("margin", "", "margin");
bgneal@45 1603 compress2('border', 'border-width', 'border-style', 'border-color');
bgneal@45 1604
bgneal@45 1605 if (isIE) {
bgneal@45 1606 // Remove pointless border
bgneal@45 1607 if (o.border == 'medium none')
bgneal@45 1608 o.border = '';
bgneal@45 1609 }
bgneal@45 1610
bgneal@45 1611 return o;
bgneal@45 1612 },
bgneal@45 1613
bgneal@45 1614 serializeStyle : function(o) {
bgneal@45 1615 var s = '';
bgneal@45 1616
bgneal@45 1617 each(o, function(v, k) {
bgneal@45 1618 if (k && v) {
bgneal@45 1619 if (tinymce.isGecko && k.indexOf('-moz-') === 0)
bgneal@45 1620 return;
bgneal@45 1621
bgneal@45 1622 switch (k) {
bgneal@45 1623 case 'color':
bgneal@45 1624 case 'background-color':
bgneal@45 1625 v = v.toLowerCase();
bgneal@45 1626 break;
bgneal@45 1627 }
bgneal@45 1628
bgneal@45 1629 s += (s ? ' ' : '') + k + ': ' + v + ';';
bgneal@45 1630 }
bgneal@45 1631 });
bgneal@45 1632
bgneal@45 1633 return s;
bgneal@45 1634 },
bgneal@45 1635
bgneal@45 1636 loadCSS : function(u) {
bgneal@45 1637 var t = this, d = t.doc;
bgneal@45 1638
bgneal@45 1639 if (!u)
bgneal@45 1640 u = '';
bgneal@45 1641
bgneal@45 1642 each(u.split(','), function(u) {
bgneal@45 1643 if (t.files[u])
bgneal@45 1644 return;
bgneal@45 1645
bgneal@45 1646 t.files[u] = true;
bgneal@45 1647 t.add(t.select('head')[0], 'link', {rel : 'stylesheet', href : tinymce._addVer(u)});
bgneal@45 1648 });
bgneal@45 1649 },
bgneal@45 1650
bgneal@45 1651
bgneal@45 1652 addClass : function(e, c) {
bgneal@45 1653 return this.run(e, function(e) {
bgneal@45 1654 var o;
bgneal@45 1655
bgneal@45 1656 if (!c)
bgneal@45 1657 return 0;
bgneal@45 1658
bgneal@45 1659 if (this.hasClass(e, c))
bgneal@45 1660 return e.className;
bgneal@45 1661
bgneal@45 1662 o = this.removeClass(e, c);
bgneal@45 1663
bgneal@45 1664 return e.className = (o != '' ? (o + ' ') : '') + c;
bgneal@45 1665 });
bgneal@45 1666 },
bgneal@45 1667
bgneal@45 1668 removeClass : function(e, c) {
bgneal@45 1669 var t = this, re;
bgneal@45 1670
bgneal@45 1671 return t.run(e, function(e) {
bgneal@45 1672 var v;
bgneal@45 1673
bgneal@45 1674 if (t.hasClass(e, c)) {
bgneal@45 1675 if (!re)
bgneal@45 1676 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
bgneal@45 1677
bgneal@45 1678 v = e.className.replace(re, ' ');
bgneal@45 1679
bgneal@45 1680 return e.className = tinymce.trim(v != ' ' ? v : '');
bgneal@45 1681 }
bgneal@45 1682
bgneal@45 1683 return e.className;
bgneal@45 1684 });
bgneal@45 1685 },
bgneal@45 1686
bgneal@45 1687 hasClass : function(n, c) {
bgneal@45 1688 n = this.get(n);
bgneal@45 1689
bgneal@45 1690 if (!n || !c)
bgneal@45 1691 return false;
bgneal@45 1692
bgneal@45 1693 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
bgneal@45 1694 },
bgneal@45 1695
bgneal@45 1696 show : function(e) {
bgneal@45 1697 return this.setStyle(e, 'display', 'block');
bgneal@45 1698 },
bgneal@45 1699
bgneal@45 1700 hide : function(e) {
bgneal@45 1701 return this.setStyle(e, 'display', 'none');
bgneal@45 1702 },
bgneal@45 1703
bgneal@45 1704 isHidden : function(e) {
bgneal@45 1705 e = this.get(e);
bgneal@45 1706
bgneal@45 1707 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
bgneal@45 1708 },
bgneal@45 1709
bgneal@45 1710
bgneal@45 1711 uniqueId : function(p) {
bgneal@45 1712 return (!p ? 'mce_' : p) + (this.counter++);
bgneal@45 1713 },
bgneal@45 1714
bgneal@45 1715 setHTML : function(e, h) {
bgneal@45 1716 var t = this;
bgneal@45 1717
bgneal@45 1718 return this.run(e, function(e) {
bgneal@45 1719 var x, i, nl, n, p, x;
bgneal@45 1720
bgneal@45 1721 h = t.processHTML(h);
bgneal@45 1722
bgneal@45 1723 if (isIE) {
bgneal@45 1724 function set() {
bgneal@45 1725 try {
bgneal@45 1726 // IE will remove comments from the beginning
bgneal@45 1727 // unless you padd the contents with something
bgneal@45 1728 e.innerHTML = '<br />' + h;
bgneal@45 1729 e.removeChild(e.firstChild);
bgneal@45 1730 } catch (ex) {
bgneal@45 1731 // 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
bgneal@45 1732 // This seems to fix this problem
bgneal@45 1733
bgneal@45 1734 // Remove all child nodes
bgneal@45 1735 while (e.firstChild)
bgneal@45 1736 e.firstChild.removeNode();
bgneal@45 1737
bgneal@45 1738 // Create new div with HTML contents and a BR infront to keep comments
bgneal@45 1739 x = t.create('div');
bgneal@45 1740 x.innerHTML = '<br />' + h;
bgneal@45 1741
bgneal@45 1742 // Add all children from div to target
bgneal@45 1743 each (x.childNodes, function(n, i) {
bgneal@45 1744 // Skip br element
bgneal@45 1745 if (i)
bgneal@45 1746 e.appendChild(n);
bgneal@45 1747 });
bgneal@45 1748 }
bgneal@45 1749 };
bgneal@45 1750
bgneal@45 1751 // IE has a serious bug when it comes to paragraphs it can produce an invalid
bgneal@45 1752 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
bgneal@45 1753 // It seems to be that IE doesn't like a root block element placed inside another root block element
bgneal@45 1754 if (t.settings.fix_ie_paragraphs)
bgneal@45 1755 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');
bgneal@45 1756
bgneal@45 1757 set();
bgneal@45 1758
bgneal@45 1759 if (t.settings.fix_ie_paragraphs) {
bgneal@45 1760 // Check for odd paragraphs this is a sign of a broken DOM
bgneal@45 1761 nl = e.getElementsByTagName("p");
bgneal@45 1762 for (i = nl.length - 1, x = 0; i >= 0; i--) {
bgneal@45 1763 n = nl[i];
bgneal@45 1764
bgneal@45 1765 if (!n.hasChildNodes()) {
bgneal@45 1766 if (!n.mce_keep) {
bgneal@45 1767 x = 1; // Is broken
bgneal@45 1768 break;
bgneal@45 1769 }
bgneal@45 1770
bgneal@45 1771 n.removeAttribute('mce_keep');
bgneal@45 1772 }
bgneal@45 1773 }
bgneal@45 1774 }
bgneal@45 1775
bgneal@45 1776 // Time to fix the madness IE left us
bgneal@45 1777 if (x) {
bgneal@45 1778 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
bgneal@45 1779 // after we use innerHTML we can fix the DOM tree
bgneal@45 1780 h = h.replace(/<p ([^>]+)>|<p>/g, '<div $1 mce_tmp="1">');
bgneal@45 1781 h = h.replace(/<\/p>/g, '</div>');
bgneal@45 1782
bgneal@45 1783 // Set the new HTML with DIVs
bgneal@45 1784 set();
bgneal@45 1785
bgneal@45 1786 // Replace all DIV elements with he mce_tmp attibute back to paragraphs
bgneal@45 1787 // This is needed since IE has a annoying bug see above for details
bgneal@45 1788 // This is a slow process but it has to be done. :(
bgneal@45 1789 if (t.settings.fix_ie_paragraphs) {
bgneal@45 1790 nl = e.getElementsByTagName("DIV");
bgneal@45 1791 for (i = nl.length - 1; i >= 0; i--) {
bgneal@45 1792 n = nl[i];
bgneal@45 1793
bgneal@45 1794 // Is it a temp div
bgneal@45 1795 if (n.mce_tmp) {
bgneal@45 1796 // Create new paragraph
bgneal@45 1797 p = t.doc.createElement('p');
bgneal@45 1798
bgneal@45 1799 // Copy all attributes
bgneal@45 1800 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
bgneal@45 1801 var v;
bgneal@45 1802
bgneal@45 1803 if (b !== 'mce_tmp') {
bgneal@45 1804 v = n.getAttribute(b);
bgneal@45 1805
bgneal@45 1806 if (!v && b === 'class')
bgneal@45 1807 v = n.className;
bgneal@45 1808
bgneal@45 1809 p.setAttribute(b, v);
bgneal@45 1810 }
bgneal@45 1811 });
bgneal@45 1812
bgneal@45 1813 // Append all children to new paragraph
bgneal@45 1814 for (x = 0; x<n.childNodes.length; x++)
bgneal@45 1815 p.appendChild(n.childNodes[x].cloneNode(true));
bgneal@45 1816
bgneal@45 1817 // Replace div with new paragraph
bgneal@45 1818 n.swapNode(p);
bgneal@45 1819 }
bgneal@45 1820 }
bgneal@45 1821 }
bgneal@45 1822 }
bgneal@45 1823 } else
bgneal@45 1824 e.innerHTML = h;
bgneal@45 1825
bgneal@45 1826 return h;
bgneal@45 1827 });
bgneal@45 1828 },
bgneal@45 1829
bgneal@45 1830 processHTML : function(h) {
bgneal@45 1831 var t = this, s = t.settings;
bgneal@45 1832
bgneal@45 1833 if (!s.process_html)
bgneal@45 1834 return h;
bgneal@45 1835
bgneal@45 1836 // Convert strong and em to b and i in FF since it can't handle them
bgneal@45 1837 if (tinymce.isGecko) {
bgneal@45 1838 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
bgneal@45 1839 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
bgneal@45 1840 } else if (isIE) {
bgneal@45 1841 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
bgneal@45 1842 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct
bgneal@45 1843 }
bgneal@45 1844
bgneal@45 1845 // Fix some issues
bgneal@45 1846 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open
bgneal@45 1847
bgneal@45 1848 // Store away src and href in mce_src and mce_href since browsers mess them up
bgneal@45 1849 if (s.keep_values) {
bgneal@45 1850 // Wrap scripts and styles in comments for serialization purposes
bgneal@45 1851 if (/<script|style/.test(h)) {
bgneal@45 1852 function trim(s) {
bgneal@45 1853 // Remove prefix and suffix code for element
bgneal@45 1854 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');
bgneal@45 1855 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
bgneal@45 1856 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');
bgneal@45 1857 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
bgneal@45 1858
bgneal@45 1859 return s;
bgneal@45 1860 };
bgneal@45 1861
bgneal@45 1862 // Preserve script elements
bgneal@45 1863 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/g, function(v, a, b) {
bgneal@45 1864 // Remove prefix and suffix code for script element
bgneal@45 1865 b = trim(b);
bgneal@45 1866
bgneal@45 1867 // Force type attribute
bgneal@45 1868 if (!a)
bgneal@45 1869 a = ' type="text/javascript"';
bgneal@45 1870
bgneal@45 1871 // Wrap contents in a comment
bgneal@45 1872 if (b)
bgneal@45 1873 b = '<!--\n' + b + '\n// -->';
bgneal@45 1874
bgneal@45 1875 // Output fake element
bgneal@45 1876 return '<mce:script' + a + '>' + b + '</mce:script>';
bgneal@45 1877 });
bgneal@45 1878
bgneal@45 1879 // Preserve style elements
bgneal@45 1880 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/g, function(v, a, b) {
bgneal@45 1881 b = trim(b);
bgneal@45 1882 return '<mce:style' + a + '><!--\n' + b + '\n--></mce:style><style' + a + ' mce_bogus="1">' + b + '</style>';
bgneal@45 1883 });
bgneal@45 1884 }
bgneal@45 1885
bgneal@45 1886 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');
bgneal@45 1887
bgneal@45 1888 // Process all tags with src, href or style
bgneal@45 1889 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {
bgneal@45 1890 function handle(m, b, c) {
bgneal@45 1891 var u = c;
bgneal@45 1892
bgneal@45 1893 // Tag already got a mce_ version
bgneal@45 1894 if (a.indexOf('mce_' + b) != -1)
bgneal@45 1895 return m;
bgneal@45 1896
bgneal@45 1897 if (b == 'style') {
bgneal@45 1898 // Why did I need this one?
bgneal@45 1899 //if (isIE)
bgneal@45 1900 // u = t.serializeStyle(t.parseStyle(u));
bgneal@45 1901
bgneal@45 1902 // No mce_style for elements with these since they might get resized by the user
bgneal@45 1903 if (t._isRes(c))
bgneal@45 1904 return m;
bgneal@45 1905
bgneal@45 1906 if (s.hex_colors) {
bgneal@45 1907 u = u.replace(/rgb\([^\)]+\)/g, function(v) {
bgneal@45 1908 return t.toHex(v);
bgneal@45 1909 });
bgneal@45 1910 }
bgneal@45 1911
bgneal@45 1912 if (s.url_converter) {
bgneal@45 1913 u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) {
bgneal@45 1914 return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')';
bgneal@45 1915 });
bgneal@45 1916 }
bgneal@45 1917 } else if (b != 'coords' && b != 'shape') {
bgneal@45 1918 if (s.url_converter)
bgneal@45 1919 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
bgneal@45 1920 }
bgneal@45 1921
bgneal@45 1922 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
bgneal@45 1923 };
bgneal@45 1924
bgneal@45 1925 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C
bgneal@45 1926 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C
bgneal@45 1927
bgneal@45 1928 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE
bgneal@45 1929 });
bgneal@45 1930 }
bgneal@45 1931
bgneal@45 1932 return h;
bgneal@45 1933 },
bgneal@45 1934
bgneal@45 1935 getOuterHTML : function(e) {
bgneal@45 1936 var d;
bgneal@45 1937
bgneal@45 1938 e = this.get(e);
bgneal@45 1939
bgneal@45 1940 if (!e)
bgneal@45 1941 return null;
bgneal@45 1942
bgneal@45 1943 if (e.outerHTML !== undefined)
bgneal@45 1944 return e.outerHTML;
bgneal@45 1945
bgneal@45 1946 d = (e.ownerDocument || this.doc).createElement("body");
bgneal@45 1947 d.appendChild(e.cloneNode(true));
bgneal@45 1948
bgneal@45 1949 return d.innerHTML;
bgneal@45 1950 },
bgneal@45 1951
bgneal@45 1952 setOuterHTML : function(e, h, d) {
bgneal@45 1953 var t = this;
bgneal@45 1954
bgneal@45 1955 return this.run(e, function(e) {
bgneal@45 1956 var n, tp;
bgneal@45 1957
bgneal@45 1958 e = t.get(e);
bgneal@45 1959 d = d || e.ownerDocument || t.doc;
bgneal@45 1960
bgneal@45 1961 if (isIE && e.nodeType == 1)
bgneal@45 1962 e.outerHTML = h;
bgneal@45 1963 else {
bgneal@45 1964 tp = d.createElement("body");
bgneal@45 1965 tp.innerHTML = h;
bgneal@45 1966
bgneal@45 1967 n = tp.lastChild;
bgneal@45 1968 while (n) {
bgneal@45 1969 t.insertAfter(n.cloneNode(true), e);
bgneal@45 1970 n = n.previousSibling;
bgneal@45 1971 }
bgneal@45 1972
bgneal@45 1973 t.remove(e);
bgneal@45 1974 }
bgneal@45 1975 });
bgneal@45 1976 },
bgneal@45 1977
bgneal@45 1978 decode : function(s) {
bgneal@45 1979 var e, n, v;
bgneal@45 1980
bgneal@45 1981 // Look for entities to decode
bgneal@45 1982 if (/&[^;]+;/.test(s)) {
bgneal@45 1983 // Decode the entities using a div element not super efficient but less code
bgneal@45 1984 e = this.doc.createElement("div");
bgneal@45 1985 e.innerHTML = s;
bgneal@45 1986 n = e.firstChild;
bgneal@45 1987 v = '';
bgneal@45 1988
bgneal@45 1989 if (n) {
bgneal@45 1990 do {
bgneal@45 1991 v += n.nodeValue;
bgneal@45 1992 } while (n.nextSibling);
bgneal@45 1993 }
bgneal@45 1994
bgneal@45 1995 return v || s;
bgneal@45 1996 }
bgneal@45 1997
bgneal@45 1998 return s;
bgneal@45 1999 },
bgneal@45 2000
bgneal@45 2001 encode : function(s) {
bgneal@45 2002 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
bgneal@45 2003 switch (c) {
bgneal@45 2004 case '&':
bgneal@45 2005 return '&amp;';
bgneal@45 2006
bgneal@45 2007 case '"':
bgneal@45 2008 return '&quot;';
bgneal@45 2009
bgneal@45 2010 case '<':
bgneal@45 2011 return '&lt;';
bgneal@45 2012
bgneal@45 2013 case '>':
bgneal@45 2014 return '&gt;';
bgneal@45 2015 }
bgneal@45 2016
bgneal@45 2017 return c;
bgneal@45 2018 }) : s;
bgneal@45 2019 },
bgneal@45 2020
bgneal@45 2021
bgneal@45 2022 insertAfter : function(n, r) {
bgneal@45 2023 var t = this;
bgneal@45 2024
bgneal@45 2025 r = t.get(r);
bgneal@45 2026
bgneal@45 2027 return this.run(n, function(n) {
bgneal@45 2028 var p, ns;
bgneal@45 2029
bgneal@45 2030 p = r.parentNode;
bgneal@45 2031 ns = r.nextSibling;
bgneal@45 2032
bgneal@45 2033 if (ns)
bgneal@45 2034 p.insertBefore(n, ns);
bgneal@45 2035 else
bgneal@45 2036 p.appendChild(n);
bgneal@45 2037
bgneal@45 2038 return n;
bgneal@45 2039 });
bgneal@45 2040 },
bgneal@45 2041
bgneal@45 2042
bgneal@45 2043 isBlock : function(n) {
bgneal@45 2044 if (n.nodeType && n.nodeType !== 1)
bgneal@45 2045 return false;
bgneal@45 2046
bgneal@45 2047 n = n.nodeName || n;
bgneal@45 2048
bgneal@45 2049 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);
bgneal@45 2050 },
bgneal@45 2051
bgneal@45 2052
bgneal@45 2053 replace : function(n, o, k) {
bgneal@45 2054 var t = this;
bgneal@45 2055
bgneal@45 2056 if (is(o, 'array'))
bgneal@45 2057 n = n.cloneNode(true);
bgneal@45 2058
bgneal@45 2059 return t.run(o, function(o) {
bgneal@45 2060 if (k) {
bgneal@45 2061 each(o.childNodes, function(c) {
bgneal@45 2062 n.appendChild(c.cloneNode(true));
bgneal@45 2063 });
bgneal@45 2064 }
bgneal@45 2065
bgneal@45 2066 // Fix IE psuedo leak for elements since replacing elements if fairly common
bgneal@45 2067 // Will break parentNode for some unknown reason
bgneal@45 2068 if (t.fixPsuedoLeaks && o.nodeType === 1) {
bgneal@45 2069 o.parentNode.insertBefore(n, o);
bgneal@45 2070 t.remove(o);
bgneal@45 2071 return n;
bgneal@45 2072 }
bgneal@45 2073
bgneal@45 2074 return o.parentNode.replaceChild(n, o);
bgneal@45 2075 });
bgneal@45 2076 },
bgneal@45 2077
bgneal@45 2078
bgneal@45 2079 findCommonAncestor : function(a, b) {
bgneal@45 2080 var ps = a, pe;
bgneal@45 2081
bgneal@45 2082 while (ps) {
bgneal@45 2083 pe = b;
bgneal@45 2084
bgneal@45 2085 while (pe && ps != pe)
bgneal@45 2086 pe = pe.parentNode;
bgneal@45 2087
bgneal@45 2088 if (ps == pe)
bgneal@45 2089 break;
bgneal@45 2090
bgneal@45 2091 ps = ps.parentNode;
bgneal@45 2092 }
bgneal@45 2093
bgneal@45 2094 if (!ps && a.ownerDocument)
bgneal@45 2095 return a.ownerDocument.documentElement;
bgneal@45 2096
bgneal@45 2097 return ps;
bgneal@45 2098 },
bgneal@45 2099
bgneal@45 2100 toHex : function(s) {
bgneal@45 2101 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
bgneal@45 2102
bgneal@45 2103 function hex(s) {
bgneal@45 2104 s = parseInt(s).toString(16);
bgneal@45 2105
bgneal@45 2106 return s.length > 1 ? s : '0' + s; // 0 -> 00
bgneal@45 2107 };
bgneal@45 2108
bgneal@45 2109 if (c) {
bgneal@45 2110 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
bgneal@45 2111
bgneal@45 2112 return s;
bgneal@45 2113 }
bgneal@45 2114
bgneal@45 2115 return s;
bgneal@45 2116 },
bgneal@45 2117
bgneal@45 2118 getClasses : function() {
bgneal@45 2119 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
bgneal@45 2120
bgneal@45 2121 if (t.classes)
bgneal@45 2122 return t.classes;
bgneal@45 2123
bgneal@45 2124 function addClasses(s) {
bgneal@45 2125 // IE style imports
bgneal@45 2126 each(s.imports, function(r) {
bgneal@45 2127 addClasses(r);
bgneal@45 2128 });
bgneal@45 2129
bgneal@45 2130 each(s.cssRules || s.rules, function(r) {
bgneal@45 2131 // Real type or fake it on IE
bgneal@45 2132 switch (r.type || 1) {
bgneal@45 2133 // Rule
bgneal@45 2134 case 1:
bgneal@45 2135 if (r.selectorText) {
bgneal@45 2136 each(r.selectorText.split(','), function(v) {
bgneal@45 2137 v = v.replace(/^\s*|\s*$|^\s\./g, "");
bgneal@45 2138
bgneal@45 2139 // Is internal or it doesn't contain a class
bgneal@45 2140 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
bgneal@45 2141 return;
bgneal@45 2142
bgneal@45 2143 // Remove everything but class name
bgneal@45 2144 ov = v;
bgneal@45 2145 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
bgneal@45 2146
bgneal@45 2147 // Filter classes
bgneal@45 2148 if (f && !(v = f(v, ov)))
bgneal@45 2149 return;
bgneal@45 2150
bgneal@45 2151 if (!lo[v]) {
bgneal@45 2152 cl.push({'class' : v});
bgneal@45 2153 lo[v] = 1;
bgneal@45 2154 }
bgneal@45 2155 });
bgneal@45 2156 }
bgneal@45 2157 break;
bgneal@45 2158
bgneal@45 2159 // Import
bgneal@45 2160 case 3:
bgneal@45 2161 addClasses(r.styleSheet);
bgneal@45 2162 break;
bgneal@45 2163 }
bgneal@45 2164 });
bgneal@45 2165 };
bgneal@45 2166
bgneal@45 2167 try {
bgneal@45 2168 each(t.doc.styleSheets, addClasses);
bgneal@45 2169 } catch (ex) {
bgneal@45 2170 // Ignore
bgneal@45 2171 }
bgneal@45 2172
bgneal@45 2173 if (cl.length > 0)
bgneal@45 2174 t.classes = cl;
bgneal@45 2175
bgneal@45 2176 return cl;
bgneal@45 2177 },
bgneal@45 2178
bgneal@45 2179 run : function(e, f, s) {
bgneal@45 2180 var t = this, o;
bgneal@45 2181
bgneal@45 2182 if (t.doc && typeof(e) === 'string')
bgneal@45 2183 e = t.get(e);
bgneal@45 2184
bgneal@45 2185 if (!e)
bgneal@45 2186 return false;
bgneal@45 2187
bgneal@45 2188 s = s || this;
bgneal@45 2189 if (!e.nodeType && (e.length || e.length === 0)) {
bgneal@45 2190 o = [];
bgneal@45 2191
bgneal@45 2192 each(e, function(e, i) {
bgneal@45 2193 if (e) {
bgneal@45 2194 if (typeof(e) == 'string')
bgneal@45 2195 e = t.doc.getElementById(e);
bgneal@45 2196
bgneal@45 2197 o.push(f.call(s, e, i));
bgneal@45 2198 }
bgneal@45 2199 });
bgneal@45 2200
bgneal@45 2201 return o;
bgneal@45 2202 }
bgneal@45 2203
bgneal@45 2204 return f.call(s, e);
bgneal@45 2205 },
bgneal@45 2206
bgneal@45 2207 getAttribs : function(n) {
bgneal@45 2208 var o;
bgneal@45 2209
bgneal@45 2210 n = this.get(n);
bgneal@45 2211
bgneal@45 2212 if (!n)
bgneal@45 2213 return [];
bgneal@45 2214
bgneal@45 2215 if (isIE) {
bgneal@45 2216 o = [];
bgneal@45 2217
bgneal@45 2218 // Object will throw exception in IE
bgneal@45 2219 if (n.nodeName == 'OBJECT')
bgneal@45 2220 return n.attributes;
bgneal@45 2221
bgneal@45 2222 // It's crazy that this is faster in IE but it's because it returns all attributes all the time
bgneal@45 2223 n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
bgneal@45 2224 o.push({specified : 1, nodeName : b});
bgneal@45 2225 });
bgneal@45 2226
bgneal@45 2227 return o;
bgneal@45 2228 }
bgneal@45 2229
bgneal@45 2230 return n.attributes;
bgneal@45 2231 },
bgneal@45 2232
bgneal@45 2233 destroy : function(s) {
bgneal@45 2234 var t = this;
bgneal@45 2235
bgneal@45 2236 t.win = t.doc = t.root = null;
bgneal@45 2237
bgneal@45 2238 // Manual destroy then remove unload handler
bgneal@45 2239 if (!s)
bgneal@45 2240 tinymce.removeUnload(t.destroy);
bgneal@45 2241 },
bgneal@45 2242
bgneal@45 2243 createRng : function() {
bgneal@45 2244 var d = this.doc;
bgneal@45 2245
bgneal@45 2246 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
bgneal@45 2247 },
bgneal@45 2248
bgneal@45 2249 split : function(pe, e, re) {
bgneal@45 2250 var t = this, r = t.createRng(), bef, aft, pa;
bgneal@45 2251
bgneal@45 2252 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence
bgneal@45 2253 // but we don't want that in our code since it serves no purpose
bgneal@45 2254 // For example if this is chopped:
bgneal@45 2255 // <p>text 1<span><b>CHOP</b></span>text 2</p>
bgneal@45 2256 // would produce:
bgneal@45 2257 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
bgneal@45 2258 // this function will then trim of empty edges and produce:
bgneal@45 2259 // <p>text 1</p><b>CHOP</b><p>text 2</p>
bgneal@45 2260 function trimEdge(n, na) {
bgneal@45 2261 n = n[na];
bgneal@45 2262
bgneal@45 2263 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na]))
bgneal@45 2264 t.remove(n[na]);
bgneal@45 2265 };
bgneal@45 2266
bgneal@45 2267 function isEmpty(n) {
bgneal@45 2268 n = t.getOuterHTML(n);
bgneal@45 2269 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
bgneal@45 2270 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
bgneal@45 2271
bgneal@45 2272 return n.replace(/[ \t\r\n]+|&nbsp;|&#160;/g, '') == '';
bgneal@45 2273 };
bgneal@45 2274
bgneal@45 2275 if (pe && e) {
bgneal@45 2276 // Get before chunk
bgneal@45 2277 r.setStartBefore(pe);
bgneal@45 2278 r.setEndBefore(e);
bgneal@45 2279 bef = r.extractContents();
bgneal@45 2280
bgneal@45 2281 // Get after chunk
bgneal@45 2282 r = t.createRng();
bgneal@45 2283 r.setStartAfter(e);
bgneal@45 2284 r.setEndAfter(pe);
bgneal@45 2285 aft = r.extractContents();
bgneal@45 2286
bgneal@45 2287 // Insert chunks and remove parent
bgneal@45 2288 pa = pe.parentNode;
bgneal@45 2289
bgneal@45 2290 // Remove right side edge of the before contents
bgneal@45 2291 trimEdge(bef, 'lastChild');
bgneal@45 2292
bgneal@45 2293 if (!isEmpty(bef))
bgneal@45 2294 pa.insertBefore(bef, pe);
bgneal@45 2295
bgneal@45 2296 if (re)
bgneal@45 2297 pa.replaceChild(re, e);
bgneal@45 2298 else
bgneal@45 2299 pa.insertBefore(e, pe);
bgneal@45 2300
bgneal@45 2301 // Remove left site edge of the after contents
bgneal@45 2302 trimEdge(aft, 'firstChild');
bgneal@45 2303
bgneal@45 2304 if (!isEmpty(aft))
bgneal@45 2305 pa.insertBefore(aft, pe);
bgneal@45 2306
bgneal@45 2307 t.remove(pe);
bgneal@45 2308
bgneal@45 2309 return re || e;
bgneal@45 2310 }
bgneal@45 2311 },
bgneal@45 2312
bgneal@45 2313 _isRes : function(c) {
bgneal@45 2314 // Is live resizble element
bgneal@45 2315 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
bgneal@45 2316 }
bgneal@45 2317
bgneal@45 2318 /*
bgneal@45 2319 walk : function(n, f, s) {
bgneal@45 2320 var d = this.doc, w;
bgneal@45 2321
bgneal@45 2322 if (d.createTreeWalker) {
bgneal@45 2323 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
bgneal@45 2324
bgneal@45 2325 while ((n = w.nextNode()) != null)
bgneal@45 2326 f.call(s || this, n);
bgneal@45 2327 } else
bgneal@45 2328 tinymce.walk(n, f, 'childNodes', s);
bgneal@45 2329 }
bgneal@45 2330 */
bgneal@45 2331
bgneal@45 2332 /*
bgneal@45 2333 toRGB : function(s) {
bgneal@45 2334 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
bgneal@45 2335
bgneal@45 2336 if (c) {
bgneal@45 2337 // #FFF -> #FFFFFF
bgneal@45 2338 if (!is(c[3]))
bgneal@45 2339 c[3] = c[2] = c[1];
bgneal@45 2340
bgneal@45 2341 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
bgneal@45 2342 }
bgneal@45 2343
bgneal@45 2344 return s;
bgneal@45 2345 }
bgneal@45 2346 */
bgneal@45 2347
bgneal@45 2348 });
bgneal@45 2349
bgneal@45 2350 // Setup page DOM
bgneal@45 2351 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
bgneal@45 2352 })(tinymce);
bgneal@45 2353 (function(ns) {
bgneal@45 2354 // Traverse constants
bgneal@45 2355 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend;
bgneal@45 2356
bgneal@45 2357 function indexOf(child, parent) {
bgneal@45 2358 var i, node;
bgneal@45 2359
bgneal@45 2360 if (child.parentNode != parent)
bgneal@45 2361 return -1;
bgneal@45 2362
bgneal@45 2363 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling)
bgneal@45 2364 i++;
bgneal@45 2365
bgneal@45 2366 return i;
bgneal@45 2367 };
bgneal@45 2368
bgneal@45 2369 function nodeIndex(n) {
bgneal@45 2370 var i = 0;
bgneal@45 2371
bgneal@45 2372 while (n.previousSibling) {
bgneal@45 2373 i++;
bgneal@45 2374 n = n.previousSibling;
bgneal@45 2375 }
bgneal@45 2376
bgneal@45 2377 return i;
bgneal@45 2378 };
bgneal@45 2379
bgneal@45 2380 function getSelectedNode(container, offset) {
bgneal@45 2381 var child;
bgneal@45 2382
bgneal@45 2383 if (container.nodeType == 3 /* TEXT_NODE */)
bgneal@45 2384 return container;
bgneal@45 2385
bgneal@45 2386 if (offset < 0)
bgneal@45 2387 return container;
bgneal@45 2388
bgneal@45 2389 child = container.firstChild;
bgneal@45 2390 while (child != null && offset > 0) {
bgneal@45 2391 --offset;
bgneal@45 2392 child = child.nextSibling;
bgneal@45 2393 }
bgneal@45 2394
bgneal@45 2395 if (child != null)
bgneal@45 2396 return child;
bgneal@45 2397
bgneal@45 2398 return container;
bgneal@45 2399 };
bgneal@45 2400
bgneal@45 2401 // Range constructor
bgneal@45 2402 function Range(dom) {
bgneal@45 2403 var d = dom.doc;
bgneal@45 2404
bgneal@45 2405 extend(this, {
bgneal@45 2406 dom : dom,
bgneal@45 2407
bgneal@45 2408 // Inital states
bgneal@45 2409 startContainer : d,
bgneal@45 2410 startOffset : 0,
bgneal@45 2411 endContainer : d,
bgneal@45 2412 endOffset : 0,
bgneal@45 2413 collapsed : true,
bgneal@45 2414 commonAncestorContainer : d,
bgneal@45 2415
bgneal@45 2416 // Range constants
bgneal@45 2417 START_TO_START : 0,
bgneal@45 2418 START_TO_END : 1,
bgneal@45 2419 END_TO_END : 2,
bgneal@45 2420 END_TO_START : 3
bgneal@45 2421 });
bgneal@45 2422 };
bgneal@45 2423
bgneal@45 2424 // Add range methods
bgneal@45 2425 extend(Range.prototype, {
bgneal@45 2426 setStart : function(n, o) {
bgneal@45 2427 this._setEndPoint(true, n, o);
bgneal@45 2428 },
bgneal@45 2429
bgneal@45 2430 setEnd : function(n, o) {
bgneal@45 2431 this._setEndPoint(false, n, o);
bgneal@45 2432 },
bgneal@45 2433
bgneal@45 2434 setStartBefore : function(n) {
bgneal@45 2435 this.setStart(n.parentNode, nodeIndex(n));
bgneal@45 2436 },
bgneal@45 2437
bgneal@45 2438 setStartAfter : function(n) {
bgneal@45 2439 this.setStart(n.parentNode, nodeIndex(n) + 1);
bgneal@45 2440 },
bgneal@45 2441
bgneal@45 2442 setEndBefore : function(n) {
bgneal@45 2443 this.setEnd(n.parentNode, nodeIndex(n));
bgneal@45 2444 },
bgneal@45 2445
bgneal@45 2446 setEndAfter : function(n) {
bgneal@45 2447 this.setEnd(n.parentNode, nodeIndex(n) + 1);
bgneal@45 2448 },
bgneal@45 2449
bgneal@45 2450 collapse : function(ts) {
bgneal@45 2451 var t = this;
bgneal@45 2452
bgneal@45 2453 if (ts) {
bgneal@45 2454 t.endContainer = t.startContainer;
bgneal@45 2455 t.endOffset = t.startOffset;
bgneal@45 2456 } else {
bgneal@45 2457 t.startContainer = t.endContainer;
bgneal@45 2458 t.startOffset = t.endOffset;
bgneal@45 2459 }
bgneal@45 2460
bgneal@45 2461 t.collapsed = true;
bgneal@45 2462 },
bgneal@45 2463
bgneal@45 2464 selectNode : function(n) {
bgneal@45 2465 this.setStartBefore(n);
bgneal@45 2466 this.setEndAfter(n);
bgneal@45 2467 },
bgneal@45 2468
bgneal@45 2469 selectNodeContents : function(n) {
bgneal@45 2470 this.setStart(n, 0);
bgneal@45 2471 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
bgneal@45 2472 },
bgneal@45 2473
bgneal@45 2474 compareBoundaryPoints : function(h, r) {
bgneal@45 2475 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset;
bgneal@45 2476
bgneal@45 2477 // Check START_TO_START
bgneal@45 2478 if (h === 0)
bgneal@45 2479 return t._compareBoundaryPoints(sc, so, sc, so);
bgneal@45 2480
bgneal@45 2481 // Check START_TO_END
bgneal@45 2482 if (h === 1)
bgneal@45 2483 return t._compareBoundaryPoints(sc, so, ec, eo);
bgneal@45 2484
bgneal@45 2485 // Check END_TO_END
bgneal@45 2486 if (h === 2)
bgneal@45 2487 return t._compareBoundaryPoints(ec, eo, ec, eo);
bgneal@45 2488
bgneal@45 2489 // Check END_TO_START
bgneal@45 2490 if (h === 3)
bgneal@45 2491 return t._compareBoundaryPoints(ec, eo, sc, so);
bgneal@45 2492 },
bgneal@45 2493
bgneal@45 2494 deleteContents : function() {
bgneal@45 2495 this._traverse(DELETE);
bgneal@45 2496 },
bgneal@45 2497
bgneal@45 2498 extractContents : function() {
bgneal@45 2499 return this._traverse(EXTRACT);
bgneal@45 2500 },
bgneal@45 2501
bgneal@45 2502 cloneContents : function() {
bgneal@45 2503 return this._traverse(CLONE);
bgneal@45 2504 },
bgneal@45 2505
bgneal@45 2506 insertNode : function(n) {
bgneal@45 2507 var t = this, nn, o;
bgneal@45 2508
bgneal@45 2509 // Node is TEXT_NODE or CDATA
bgneal@45 2510 if (n.nodeType === 3 || n.nodeType === 4) {
bgneal@45 2511 nn = t.startContainer.splitText(t.startOffset);
bgneal@45 2512 t.startContainer.parentNode.insertBefore(n, nn);
bgneal@45 2513 } else {
bgneal@45 2514 // Insert element node
bgneal@45 2515 if (t.startContainer.childNodes.length > 0)
bgneal@45 2516 o = t.startContainer.childNodes[t.startOffset];
bgneal@45 2517
bgneal@45 2518 t.startContainer.insertBefore(n, o);
bgneal@45 2519 }
bgneal@45 2520 },
bgneal@45 2521
bgneal@45 2522 surroundContents : function(n) {
bgneal@45 2523 var t = this, f = t.extractContents();
bgneal@45 2524
bgneal@45 2525 t.insertNode(n);
bgneal@45 2526 n.appendChild(f);
bgneal@45 2527 t.selectNode(n);
bgneal@45 2528 },
bgneal@45 2529
bgneal@45 2530 cloneRange : function() {
bgneal@45 2531 var t = this;
bgneal@45 2532
bgneal@45 2533 return extend(new Range(t.dom), {
bgneal@45 2534 startContainer : t.startContainer,
bgneal@45 2535 startOffset : t.startOffset,
bgneal@45 2536 endContainer : t.endContainer,
bgneal@45 2537 endOffset : t.endOffset,
bgneal@45 2538 collapsed : t.collapsed,
bgneal@45 2539 commonAncestorContainer : t.commonAncestorContainer
bgneal@45 2540 });
bgneal@45 2541 },
bgneal@45 2542
bgneal@45 2543 /*
bgneal@45 2544 toString : function() {
bgneal@45 2545 // Not implemented
bgneal@45 2546 },
bgneal@45 2547
bgneal@45 2548 detach : function() {
bgneal@45 2549 // Not implemented
bgneal@45 2550 },
bgneal@45 2551 */
bgneal@45 2552 // Internal methods
bgneal@45 2553
bgneal@45 2554 _isCollapsed : function() {
bgneal@45 2555 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
bgneal@45 2556 },
bgneal@45 2557
bgneal@45 2558 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) {
bgneal@45 2559 var c, offsetC, n, cmnRoot, childA, childB;
bgneal@45 2560
bgneal@45 2561 // In the first case the boundary-points have the same container. A is before B
bgneal@45 2562 // if its offset is less than the offset of B, A is equal to B if its offset is
bgneal@45 2563 // equal to the offset of B, and A is after B if its offset is greater than the
bgneal@45 2564 // offset of B.
bgneal@45 2565 if (containerA == containerB) {
bgneal@45 2566 if (offsetA == offsetB) {
bgneal@45 2567 return 0; // equal
bgneal@45 2568 } else if (offsetA < offsetB) {
bgneal@45 2569 return -1; // before
bgneal@45 2570 } else {
bgneal@45 2571 return 1; // after
bgneal@45 2572 }
bgneal@45 2573 }
bgneal@45 2574
bgneal@45 2575 // In the second case a child node C of the container of A is an ancestor
bgneal@45 2576 // container of B. In this case, A is before B if the offset of A is less than or
bgneal@45 2577 // equal to the index of the child node C and A is after B otherwise.
bgneal@45 2578 c = containerB;
bgneal@45 2579 while (c && c.parentNode != containerA) {
bgneal@45 2580 c = c.parentNode;
bgneal@45 2581 }
bgneal@45 2582 if (c) {
bgneal@45 2583 offsetC = 0;
bgneal@45 2584 n = containerA.firstChild;
bgneal@45 2585
bgneal@45 2586 while (n != c && offsetC < offsetA) {
bgneal@45 2587 offsetC++;
bgneal@45 2588 n = n.nextSibling;
bgneal@45 2589 }
bgneal@45 2590
bgneal@45 2591 if (offsetA <= offsetC) {
bgneal@45 2592 return -1; // before
bgneal@45 2593 } else {
bgneal@45 2594 return 1; // after
bgneal@45 2595 }
bgneal@45 2596 }
bgneal@45 2597
bgneal@45 2598 // In the third case a child node C of the container of B is an ancestor container
bgneal@45 2599 // of A. In this case, A is before B if the index of the child node C is less than
bgneal@45 2600 // the offset of B and A is after B otherwise.
bgneal@45 2601 c = containerA;
bgneal@45 2602 while (c && c.parentNode != containerB) {
bgneal@45 2603 c = c.parentNode;
bgneal@45 2604 }
bgneal@45 2605
bgneal@45 2606 if (c) {
bgneal@45 2607 offsetC = 0;
bgneal@45 2608 n = containerB.firstChild;
bgneal@45 2609
bgneal@45 2610 while (n != c && offsetC < offsetB) {
bgneal@45 2611 offsetC++;
bgneal@45 2612 n = n.nextSibling;
bgneal@45 2613 }
bgneal@45 2614
bgneal@45 2615 if (offsetC < offsetB) {
bgneal@45 2616 return -1; // before
bgneal@45 2617 } else {
bgneal@45 2618 return 1; // after
bgneal@45 2619 }
bgneal@45 2620 }
bgneal@45 2621
bgneal@45 2622 // In the fourth case, none of three other cases hold: the containers of A and B
bgneal@45 2623 // are siblings or descendants of sibling nodes. In this case, A is before B if
bgneal@45 2624 // the container of A is before the container of B in a pre-order traversal of the
bgneal@45 2625 // Ranges' context tree and A is after B otherwise.
bgneal@45 2626 cmnRoot = this.dom.findCommonAncestor(containerA, containerB);
bgneal@45 2627 childA = containerA;
bgneal@45 2628
bgneal@45 2629 while (childA && childA.parentNode != cmnRoot) {
bgneal@45 2630 childA = childA.parentNode;
bgneal@45 2631 }
bgneal@45 2632
bgneal@45 2633 if (!childA) {
bgneal@45 2634 childA = cmnRoot;
bgneal@45 2635 }
bgneal@45 2636
bgneal@45 2637 childB = containerB;
bgneal@45 2638 while (childB && childB.parentNode != cmnRoot) {
bgneal@45 2639 childB = childB.parentNode;
bgneal@45 2640 }
bgneal@45 2641
bgneal@45 2642 if (!childB) {
bgneal@45 2643 childB = cmnRoot;
bgneal@45 2644 }
bgneal@45 2645
bgneal@45 2646 if (childA == childB) {
bgneal@45 2647 return 0; // equal
bgneal@45 2648 }
bgneal@45 2649
bgneal@45 2650 n = cmnRoot.firstChild;
bgneal@45 2651 while (n) {
bgneal@45 2652 if (n == childA) {
bgneal@45 2653 return -1; // before
bgneal@45 2654 }
bgneal@45 2655
bgneal@45 2656 if (n == childB) {
bgneal@45 2657 return 1; // after
bgneal@45 2658 }
bgneal@45 2659
bgneal@45 2660 n = n.nextSibling;
bgneal@45 2661 }
bgneal@45 2662 },
bgneal@45 2663
bgneal@45 2664 _setEndPoint : function(st, n, o) {
bgneal@45 2665 var t = this, ec, sc;
bgneal@45 2666
bgneal@45 2667 if (st) {
bgneal@45 2668 t.startContainer = n;
bgneal@45 2669 t.startOffset = o;
bgneal@45 2670 } else {
bgneal@45 2671 t.endContainer = n;
bgneal@45 2672 t.endOffset = o;
bgneal@45 2673 }
bgneal@45 2674
bgneal@45 2675 // If one boundary-point of a Range is set to have a root container
bgneal@45 2676 // other than the current one for the Range, the Range is collapsed to
bgneal@45 2677 // the new position. This enforces the restriction that both boundary-
bgneal@45 2678 // points of a Range must have the same root container.
bgneal@45 2679 ec = t.endContainer;
bgneal@45 2680 while (ec.parentNode)
bgneal@45 2681 ec = ec.parentNode;
bgneal@45 2682
bgneal@45 2683 sc = t.startContainer;
bgneal@45 2684 while (sc.parentNode)
bgneal@45 2685 sc = sc.parentNode;
bgneal@45 2686
bgneal@45 2687 if (sc != ec) {
bgneal@45 2688 t.collapse(st);
bgneal@45 2689 } else {
bgneal@45 2690 // The start position of a Range is guaranteed to never be after the
bgneal@45 2691 // end position. To enforce this restriction, if the start is set to
bgneal@45 2692 // be at a position after the end, the Range is collapsed to that
bgneal@45 2693 // position.
bgneal@45 2694 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0)
bgneal@45 2695 t.collapse(st);
bgneal@45 2696 }
bgneal@45 2697
bgneal@45 2698 t.collapsed = t._isCollapsed();
bgneal@45 2699 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer);
bgneal@45 2700 },
bgneal@45 2701
bgneal@45 2702 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :)
bgneal@45 2703
bgneal@45 2704 _traverse : function(how) {
bgneal@45 2705 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
bgneal@45 2706
bgneal@45 2707 if (t.startContainer == t.endContainer)
bgneal@45 2708 return t._traverseSameContainer(how);
bgneal@45 2709
bgneal@45 2710 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
bgneal@45 2711 if (p == t.startContainer)
bgneal@45 2712 return t._traverseCommonStartContainer(c, how);
bgneal@45 2713
bgneal@45 2714 ++endContainerDepth;
bgneal@45 2715 }
bgneal@45 2716
bgneal@45 2717 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
bgneal@45 2718 if (p == t.endContainer)
bgneal@45 2719 return t._traverseCommonEndContainer(c, how);
bgneal@45 2720
bgneal@45 2721 ++startContainerDepth;
bgneal@45 2722 }
bgneal@45 2723
bgneal@45 2724 depthDiff = startContainerDepth - endContainerDepth;
bgneal@45 2725
bgneal@45 2726 startNode = t.startContainer;
bgneal@45 2727 while (depthDiff > 0) {
bgneal@45 2728 startNode = startNode.parentNode;
bgneal@45 2729 depthDiff--;
bgneal@45 2730 }
bgneal@45 2731
bgneal@45 2732 endNode = t.endContainer;
bgneal@45 2733 while (depthDiff < 0) {
bgneal@45 2734 endNode = endNode.parentNode;
bgneal@45 2735 depthDiff++;
bgneal@45 2736 }
bgneal@45 2737
bgneal@45 2738 // ascend the ancestor hierarchy until we have a common parent.
bgneal@45 2739 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
bgneal@45 2740 startNode = sp;
bgneal@45 2741 endNode = ep;
bgneal@45 2742 }
bgneal@45 2743
bgneal@45 2744 return t._traverseCommonAncestors(startNode, endNode, how);
bgneal@45 2745 },
bgneal@45 2746
bgneal@45 2747 _traverseSameContainer : function(how) {
bgneal@45 2748 var t = this, frag, s, sub, n, cnt, sibling, xferNode;
bgneal@45 2749
bgneal@45 2750 if (how != DELETE)
bgneal@45 2751 frag = t.dom.doc.createDocumentFragment();
bgneal@45 2752
bgneal@45 2753 // If selection is empty, just return the fragment
bgneal@45 2754 if (t.startOffset == t.endOffset)
bgneal@45 2755 return frag;
bgneal@45 2756
bgneal@45 2757 // Text node needs special case handling
bgneal@45 2758 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) {
bgneal@45 2759 // get the substring
bgneal@45 2760 s = t.startContainer.nodeValue;
bgneal@45 2761 sub = s.substring(t.startOffset, t.endOffset);
bgneal@45 2762
bgneal@45 2763 // set the original text node to its new value
bgneal@45 2764 if (how != CLONE) {
bgneal@45 2765 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset);
bgneal@45 2766
bgneal@45 2767 // Nothing is partially selected, so collapse to start point
bgneal@45 2768 t.collapse(true);
bgneal@45 2769 }
bgneal@45 2770
bgneal@45 2771 if (how == DELETE)
bgneal@45 2772 return null;
bgneal@45 2773
bgneal@45 2774 frag.appendChild(t.dom.doc.createTextNode(sub));
bgneal@45 2775 return frag;
bgneal@45 2776 }
bgneal@45 2777
bgneal@45 2778 // Copy nodes between the start/end offsets.
bgneal@45 2779 n = getSelectedNode(t.startContainer, t.startOffset);
bgneal@45 2780 cnt = t.endOffset - t.startOffset;
bgneal@45 2781
bgneal@45 2782 while (cnt > 0) {
bgneal@45 2783 sibling = n.nextSibling;
bgneal@45 2784 xferNode = t._traverseFullySelected(n, how);
bgneal@45 2785
bgneal@45 2786 if (frag)
bgneal@45 2787 frag.appendChild( xferNode );
bgneal@45 2788
bgneal@45 2789 --cnt;
bgneal@45 2790 n = sibling;
bgneal@45 2791 }
bgneal@45 2792
bgneal@45 2793 // Nothing is partially selected, so collapse to start point
bgneal@45 2794 if (how != CLONE)
bgneal@45 2795 t.collapse(true);
bgneal@45 2796
bgneal@45 2797 return frag;
bgneal@45 2798 },
bgneal@45 2799
bgneal@45 2800 _traverseCommonStartContainer : function(endAncestor, how) {
bgneal@45 2801 var t = this, frag, n, endIdx, cnt, sibling, xferNode;
bgneal@45 2802
bgneal@45 2803 if (how != DELETE)
bgneal@45 2804 frag = t.dom.doc.createDocumentFragment();
bgneal@45 2805
bgneal@45 2806 n = t._traverseRightBoundary(endAncestor, how);
bgneal@45 2807
bgneal@45 2808 if (frag)
bgneal@45 2809 frag.appendChild(n);
bgneal@45 2810
bgneal@45 2811 endIdx = indexOf(endAncestor, t.startContainer);
bgneal@45 2812 cnt = endIdx - t.startOffset;
bgneal@45 2813
bgneal@45 2814 if (cnt <= 0) {
bgneal@45 2815 // Collapse to just before the endAncestor, which
bgneal@45 2816 // is partially selected.
bgneal@45 2817 if (how != CLONE) {
bgneal@45 2818 t.setEndBefore(endAncestor);
bgneal@45 2819 t.collapse(false);
bgneal@45 2820 }
bgneal@45 2821
bgneal@45 2822 return frag;
bgneal@45 2823 }
bgneal@45 2824
bgneal@45 2825 n = endAncestor.previousSibling;
bgneal@45 2826 while (cnt > 0) {
bgneal@45 2827 sibling = n.previousSibling;
bgneal@45 2828 xferNode = t._traverseFullySelected(n, how);
bgneal@45 2829
bgneal@45 2830 if (frag)
bgneal@45 2831 frag.insertBefore(xferNode, frag.firstChild);
bgneal@45 2832
bgneal@45 2833 --cnt;
bgneal@45 2834 n = sibling;
bgneal@45 2835 }
bgneal@45 2836
bgneal@45 2837 // Collapse to just before the endAncestor, which
bgneal@45 2838 // is partially selected.
bgneal@45 2839 if (how != CLONE) {
bgneal@45 2840 t.setEndBefore(endAncestor);
bgneal@45 2841 t.collapse(false);
bgneal@45 2842 }
bgneal@45 2843
bgneal@45 2844 return frag;
bgneal@45 2845 },
bgneal@45 2846
bgneal@45 2847 _traverseCommonEndContainer : function(startAncestor, how) {
bgneal@45 2848 var t = this, frag, startIdx, n, cnt, sibling, xferNode;
bgneal@45 2849
bgneal@45 2850 if (how != DELETE)
bgneal@45 2851 frag = t.dom.doc.createDocumentFragment();
bgneal@45 2852
bgneal@45 2853 n = t._traverseLeftBoundary(startAncestor, how);
bgneal@45 2854 if (frag)
bgneal@45 2855 frag.appendChild(n);
bgneal@45 2856
bgneal@45 2857 startIdx = indexOf(startAncestor, t.endContainer);
bgneal@45 2858 ++startIdx; // Because we already traversed it....
bgneal@45 2859
bgneal@45 2860 cnt = t.endOffset - startIdx;
bgneal@45 2861 n = startAncestor.nextSibling;
bgneal@45 2862 while (cnt > 0) {
bgneal@45 2863 sibling = n.nextSibling;
bgneal@45 2864 xferNode = t._traverseFullySelected(n, how);
bgneal@45 2865
bgneal@45 2866 if (frag)
bgneal@45 2867 frag.appendChild(xferNode);
bgneal@45 2868
bgneal@45 2869 --cnt;
bgneal@45 2870 n = sibling;
bgneal@45 2871 }
bgneal@45 2872
bgneal@45 2873 if (how != CLONE) {
bgneal@45 2874 t.setStartAfter(startAncestor);
bgneal@45 2875 t.collapse(true);
bgneal@45 2876 }
bgneal@45 2877
bgneal@45 2878 return frag;
bgneal@45 2879 },
bgneal@45 2880
bgneal@45 2881 _traverseCommonAncestors : function(startAncestor, endAncestor, how) {
bgneal@45 2882 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
bgneal@45 2883
bgneal@45 2884 if (how != DELETE)
bgneal@45 2885 frag = t.dom.doc.createDocumentFragment();
bgneal@45 2886
bgneal@45 2887 n = t._traverseLeftBoundary(startAncestor, how);
bgneal@45 2888 if (frag)
bgneal@45 2889 frag.appendChild(n);
bgneal@45 2890
bgneal@45 2891 commonParent = startAncestor.parentNode;
bgneal@45 2892 startOffset = indexOf(startAncestor, commonParent);
bgneal@45 2893 endOffset = indexOf(endAncestor, commonParent);
bgneal@45 2894 ++startOffset;
bgneal@45 2895
bgneal@45 2896 cnt = endOffset - startOffset;
bgneal@45 2897 sibling = startAncestor.nextSibling;
bgneal@45 2898
bgneal@45 2899 while (cnt > 0) {
bgneal@45 2900 nextSibling = sibling.nextSibling;
bgneal@45 2901 n = t._traverseFullySelected(sibling, how);
bgneal@45 2902
bgneal@45 2903 if (frag)
bgneal@45 2904 frag.appendChild(n);
bgneal@45 2905
bgneal@45 2906 sibling = nextSibling;
bgneal@45 2907 --cnt;
bgneal@45 2908 }
bgneal@45 2909
bgneal@45 2910 n = t._traverseRightBoundary(endAncestor, how);
bgneal@45 2911
bgneal@45 2912 if (frag)
bgneal@45 2913 frag.appendChild(n);
bgneal@45 2914
bgneal@45 2915 if (how != CLONE) {
bgneal@45 2916 t.setStartAfter(startAncestor);
bgneal@45 2917 t.collapse(true);
bgneal@45 2918 }
bgneal@45 2919
bgneal@45 2920 return frag;
bgneal@45 2921 },
bgneal@45 2922
bgneal@45 2923 _traverseRightBoundary : function(root, how) {
bgneal@45 2924 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent;
bgneal@45 2925 var isFullySelected = next != t.endContainer;
bgneal@45 2926
bgneal@45 2927 if (next == root)
bgneal@45 2928 return t._traverseNode(next, isFullySelected, false, how);
bgneal@45 2929
bgneal@45 2930 parent = next.parentNode;
bgneal@45 2931 clonedParent = t._traverseNode(parent, false, false, how);
bgneal@45 2932
bgneal@45 2933 while (parent != null) {
bgneal@45 2934 while (next != null) {
bgneal@45 2935 prevSibling = next.previousSibling;
bgneal@45 2936 clonedChild = t._traverseNode(next, isFullySelected, false, how);
bgneal@45 2937
bgneal@45 2938 if (how != DELETE)
bgneal@45 2939 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
bgneal@45 2940
bgneal@45 2941 isFullySelected = true;
bgneal@45 2942 next = prevSibling;
bgneal@45 2943 }
bgneal@45 2944
bgneal@45 2945 if (parent == root)
bgneal@45 2946 return clonedParent;
bgneal@45 2947
bgneal@45 2948 next = parent.previousSibling;
bgneal@45 2949 parent = parent.parentNode;
bgneal@45 2950
bgneal@45 2951 clonedGrandParent = t._traverseNode(parent, false, false, how);
bgneal@45 2952
bgneal@45 2953 if (how != DELETE)
bgneal@45 2954 clonedGrandParent.appendChild(clonedParent);
bgneal@45 2955
bgneal@45 2956 clonedParent = clonedGrandParent;
bgneal@45 2957 }
bgneal@45 2958
bgneal@45 2959 // should never occur
bgneal@45 2960 return null;
bgneal@45 2961 },
bgneal@45 2962
bgneal@45 2963 _traverseLeftBoundary : function(root, how) {
bgneal@45 2964 var t = this, next = getSelectedNode(t.startContainer, t.startOffset);
bgneal@45 2965 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
bgneal@45 2966
bgneal@45 2967 if (next == root)
bgneal@45 2968 return t._traverseNode(next, isFullySelected, true, how);
bgneal@45 2969
bgneal@45 2970 parent = next.parentNode;
bgneal@45 2971 clonedParent = t._traverseNode(parent, false, true, how);
bgneal@45 2972
bgneal@45 2973 while (parent != null) {
bgneal@45 2974 while (next != null) {
bgneal@45 2975 nextSibling = next.nextSibling;
bgneal@45 2976 clonedChild = t._traverseNode(next, isFullySelected, true, how);
bgneal@45 2977
bgneal@45 2978 if (how != DELETE)
bgneal@45 2979 clonedParent.appendChild(clonedChild);
bgneal@45 2980
bgneal@45 2981 isFullySelected = true;
bgneal@45 2982 next = nextSibling;
bgneal@45 2983 }
bgneal@45 2984
bgneal@45 2985 if (parent == root)
bgneal@45 2986 return clonedParent;
bgneal@45 2987
bgneal@45 2988 next = parent.nextSibling;
bgneal@45 2989 parent = parent.parentNode;
bgneal@45 2990
bgneal@45 2991 clonedGrandParent = t._traverseNode(parent, false, true, how);
bgneal@45 2992
bgneal@45 2993 if (how != DELETE)
bgneal@45 2994 clonedGrandParent.appendChild(clonedParent);
bgneal@45 2995
bgneal@45 2996 clonedParent = clonedGrandParent;
bgneal@45 2997 }
bgneal@45 2998
bgneal@45 2999 // should never occur
bgneal@45 3000 return null;
bgneal@45 3001 },
bgneal@45 3002
bgneal@45 3003 _traverseNode : function(n, isFullySelected, isLeft, how) {
bgneal@45 3004 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode;
bgneal@45 3005
bgneal@45 3006 if (isFullySelected)
bgneal@45 3007 return t._traverseFullySelected(n, how);
bgneal@45 3008
bgneal@45 3009 if (n.nodeType == 3 /* TEXT_NODE */) {
bgneal@45 3010 txtValue = n.nodeValue;
bgneal@45 3011
bgneal@45 3012 if (isLeft) {
bgneal@45 3013 offset = t.startOffset;
bgneal@45 3014 newNodeValue = txtValue.substring(offset);
bgneal@45 3015 oldNodeValue = txtValue.substring(0, offset);
bgneal@45 3016 } else {
bgneal@45 3017 offset = t.endOffset;
bgneal@45 3018 newNodeValue = txtValue.substring(0, offset);
bgneal@45 3019 oldNodeValue = txtValue.substring(offset);
bgneal@45 3020 }
bgneal@45 3021
bgneal@45 3022 if (how != CLONE)
bgneal@45 3023 n.nodeValue = oldNodeValue;
bgneal@45 3024
bgneal@45 3025 if (how == DELETE)
bgneal@45 3026 return null;
bgneal@45 3027
bgneal@45 3028 newNode = n.cloneNode(false);
bgneal@45 3029 newNode.nodeValue = newNodeValue;
bgneal@45 3030
bgneal@45 3031 return newNode;
bgneal@45 3032 }
bgneal@45 3033
bgneal@45 3034 if (how == DELETE)
bgneal@45 3035 return null;
bgneal@45 3036
bgneal@45 3037 return n.cloneNode(false);
bgneal@45 3038 },
bgneal@45 3039
bgneal@45 3040 _traverseFullySelected : function(n, how) {
bgneal@45 3041 var t = this;
bgneal@45 3042
bgneal@45 3043 if (how != DELETE)
bgneal@45 3044 return how == CLONE ? n.cloneNode(true) : n;
bgneal@45 3045
bgneal@45 3046 n.parentNode.removeChild(n);
bgneal@45 3047 return null;
bgneal@45 3048 }
bgneal@45 3049 });
bgneal@45 3050
bgneal@45 3051 ns.Range = Range;
bgneal@45 3052 })(tinymce.dom);
bgneal@45 3053 (function() {
bgneal@45 3054 function Selection(selection) {
bgneal@45 3055 var t = this;
bgneal@45 3056
bgneal@45 3057 function getRange() {
bgneal@45 3058 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos = {}, endPos = {};
bgneal@45 3059
bgneal@45 3060 // Handle control selection
bgneal@45 3061 if (ieRange.item) {
bgneal@45 3062 domRange.setStartBefore(ieRange.item(0));
bgneal@45 3063 domRange.setEndAfter(ieRange.item(0));
bgneal@45 3064
bgneal@45 3065 return domRange;
bgneal@45 3066 }
bgneal@45 3067
bgneal@45 3068 function findEndPoint(ie_rng, start, pos) {
bgneal@45 3069 var rng, rng2, startElement;
bgneal@45 3070
bgneal@45 3071 rng = ie_rng.duplicate();
bgneal@45 3072 rng.collapse(start);
bgneal@45 3073 element = rng.parentElement();
bgneal@45 3074
bgneal@45 3075 // If element is block then we need to move one character
bgneal@45 3076 // since the selection has a extra invisible character
bgneal@45 3077 if (element.currentStyle.display == 'block') {
bgneal@45 3078 rng = ie_rng.duplicate();
bgneal@45 3079 rng2 = ie_rng.duplicate();
bgneal@45 3080
bgneal@45 3081 // Move one character at beginning/end of selection
bgneal@45 3082 if (start)
bgneal@45 3083 rng.moveStart('character', 1);
bgneal@45 3084 else
bgneal@45 3085 rng.moveEnd('character', -1);
bgneal@45 3086
bgneal@45 3087 // The range shouldn't have been changed so lets restore it
bgneal@45 3088 if (rng.text != rng2.text)
bgneal@45 3089 rng = rng2;
bgneal@45 3090
bgneal@45 3091 rng.collapse(start);
bgneal@45 3092 element = rng.parentElement();
bgneal@45 3093 }
bgneal@45 3094
bgneal@45 3095 pos.parent = element;
bgneal@45 3096 pos.range = rng;
bgneal@45 3097 };
bgneal@45 3098
bgneal@45 3099 function findIndexAndOffset(pos) {
bgneal@45 3100 var rng = pos.range, i, nl, marker, sibling, idx = 0;
bgneal@45 3101
bgneal@45 3102 // Set parent and offset
bgneal@45 3103 pos.offset = 0;
bgneal@45 3104 pos.parent = rng.parentElement();
bgneal@45 3105
bgneal@45 3106 // Insert marker
bgneal@45 3107 rng.pasteHTML('<span id="_mce"></span>');
bgneal@45 3108 marker = dom.get('_mce');
bgneal@45 3109
bgneal@45 3110 // Find the makers node index excluding text node fragmentation
bgneal@45 3111 nl = pos.parent.childNodes;
bgneal@45 3112 for (i = 0; i < nl.length; i++) {
bgneal@45 3113 if (nl[i] == marker) {
bgneal@45 3114 pos.index = idx;
bgneal@45 3115 break;
bgneal@45 3116 }
bgneal@45 3117
bgneal@45 3118 if (i > 0 && (nl[i].nodeType != 3 || nl[i - 1].nodeType != 3))
bgneal@45 3119 idx++;
bgneal@45 3120 }
bgneal@45 3121
bgneal@45 3122 // Figure out the character offset excluding text node fragmentation
bgneal@45 3123 sibling = marker.previousSibling;
bgneal@45 3124 if (sibling) {
bgneal@45 3125 if (sibling.nodeType === 3) {
bgneal@45 3126 do {
bgneal@45 3127 pos.offset += sibling.nodeValue.length;
bgneal@45 3128 } while ((sibling = sibling.previousSibling) && sibling.nodeType == 3);
bgneal@45 3129 } else
bgneal@45 3130 pos.index++;
bgneal@45 3131 }
bgneal@45 3132
bgneal@45 3133 // Remove the marker
bgneal@45 3134 dom.remove(marker);
bgneal@45 3135
bgneal@45 3136 return pos;
bgneal@45 3137 };
bgneal@45 3138
bgneal@45 3139 // Find end points
bgneal@45 3140 findEndPoint(ieRange, true, startPos);
bgneal@45 3141 findEndPoint(ieRange, false, endPos);
bgneal@45 3142
bgneal@45 3143 // Find start and end positions
bgneal@45 3144 findIndexAndOffset(startPos);
bgneal@45 3145 findIndexAndOffset(endPos);
bgneal@45 3146
bgneal@45 3147 // Normalize the elements to avoid fragmented dom
bgneal@45 3148 startPos.parent.normalize();
bgneal@45 3149 endPos.parent.normalize();
bgneal@45 3150
bgneal@45 3151 // Set start and end points of the domRange
bgneal@45 3152 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset);
bgneal@45 3153 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset);
bgneal@45 3154
bgneal@45 3155 // Restore selection to new range
bgneal@45 3156 t.addRange(domRange);
bgneal@45 3157
bgneal@45 3158 return domRange;
bgneal@45 3159 };
bgneal@45 3160
bgneal@45 3161 this.addRange = function(rng) {
bgneal@45 3162 var ieRng, startPos, endPos, body = selection.dom.doc.body;
bgneal@45 3163
bgneal@45 3164 // Element selection, then make a control range
bgneal@45 3165 if (rng.startContainer.nodeType == 1) {
bgneal@45 3166 ieRng = body.createControlRange();
bgneal@45 3167 ieRng.addElement(rng.startContainer.childNodes[rng.startOffset]);
bgneal@45 3168 return;
bgneal@45 3169 }
bgneal@45 3170
bgneal@45 3171 function findPos(start) {
bgneal@45 3172 var container, offset, rng2, pos;
bgneal@45 3173
bgneal@45 3174 // Get container and offset
bgneal@45 3175 container = start ? rng.startContainer : rng.endContainer;
bgneal@45 3176 offset = start ? rng.startOffset : rng.endOffset;
bgneal@45 3177
bgneal@45 3178 // Insert marker character
bgneal@45 3179 container.nodeValue = container.nodeValue.substring(0, offset) + '\uFEFF' + container.nodeValue.substring(offset);
bgneal@45 3180
bgneal@45 3181 // Create range for whole parent element
bgneal@45 3182 rng2 = body.createTextRange();
bgneal@45 3183 rng2.moveToElementText(container.parentNode);
bgneal@45 3184 pos = rng2.text.indexOf('\uFEFF');
bgneal@45 3185 container.nodeValue = container.nodeValue.replace(/\uFEFF/, '');
bgneal@45 3186
bgneal@45 3187 if (start)
bgneal@45 3188 startPos = pos;
bgneal@45 3189 else
bgneal@45 3190 endPos = pos;
bgneal@45 3191 };
bgneal@45 3192
bgneal@45 3193 function setPos(start) {
bgneal@45 3194 var rng2, container = start ? rng.startContainer : rng.endContainer;
bgneal@45 3195
bgneal@45 3196 rng2 = body.createTextRange();
bgneal@45 3197 rng2.moveToElementText(container.parentNode);
bgneal@45 3198 rng2.collapse(true);
bgneal@45 3199 rng2.move('character', start ? startPos : endPos);
bgneal@45 3200
bgneal@45 3201 if (start)
bgneal@45 3202 ieRng.setEndPoint('StartToStart', rng2);
bgneal@45 3203 else
bgneal@45 3204 ieRng.setEndPoint('EndToStart', rng2);
bgneal@45 3205 };
bgneal@45 3206
bgneal@45 3207 // Create IE specific range
bgneal@45 3208 ieRng = body.createTextRange();
bgneal@45 3209
bgneal@45 3210 // Find start/end pos
bgneal@45 3211 findPos(true);
bgneal@45 3212 findPos(false);
bgneal@45 3213
bgneal@45 3214 // Set start/end pos
bgneal@45 3215 setPos(true);
bgneal@45 3216 setPos(false);
bgneal@45 3217
bgneal@45 3218 ieRng.select();
bgneal@45 3219 };
bgneal@45 3220
bgneal@45 3221 this.getRangeAt = function() {
bgneal@45 3222 // todo: Implement range caching here later
bgneal@45 3223 return getRange();
bgneal@45 3224 };
bgneal@45 3225 };
bgneal@45 3226
bgneal@45 3227 // Expose the selection object
bgneal@45 3228 tinymce.dom.TridentSelection = Selection;
bgneal@45 3229 })();
bgneal@45 3230
bgneal@45 3231 /*
bgneal@45 3232 * Sizzle CSS Selector Engine - v1.0
bgneal@45 3233 * Copyright 2009, The Dojo Foundation
bgneal@45 3234 * Released under the MIT, BSD, and GPL Licenses.
bgneal@45 3235 * More information: http://sizzlejs.com/
bgneal@45 3236 */
bgneal@45 3237 (function(){
bgneal@45 3238
bgneal@45 3239 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
bgneal@45 3240 done = 0,
bgneal@45 3241 toString = Object.prototype.toString,
bgneal@45 3242 arraySplice = Array.prototype.splice,
bgneal@45 3243 arrayPush = Array.prototype.push,
bgneal@45 3244 arraySort = Array.prototype.sort;
bgneal@45 3245
bgneal@45 3246 var Sizzle = function(selector, context, results, seed) {
bgneal@45 3247 results = results || [];
bgneal@45 3248 var origContext = context = context || document;
bgneal@45 3249
bgneal@45 3250 if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
bgneal@45 3251 return [];
bgneal@45 3252 }
bgneal@45 3253
bgneal@45 3254 if ( !selector || typeof selector !== "string" ) {
bgneal@45 3255 return results;
bgneal@45 3256 }
bgneal@45 3257
bgneal@45 3258 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
bgneal@45 3259
bgneal@45 3260 // Reset the position of the chunker regexp (start from head)
bgneal@45 3261 chunker.lastIndex = 0;
bgneal@45 3262
bgneal@45 3263 while ( (m = chunker.exec(selector)) !== null ) {
bgneal@45 3264 parts.push( m[1] );
bgneal@45 3265
bgneal@45 3266 if ( m[2] ) {
bgneal@45 3267 extra = RegExp.rightContext;
bgneal@45 3268 break;
bgneal@45 3269 }
bgneal@45 3270 }
bgneal@45 3271
bgneal@45 3272 if ( parts.length > 1 && origPOS.exec( selector ) ) {
bgneal@45 3273 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
bgneal@45 3274 set = posProcess( parts[0] + parts[1], context );
bgneal@45 3275 } else {
bgneal@45 3276 set = Expr.relative[ parts[0] ] ?
bgneal@45 3277 [ context ] :
bgneal@45 3278 Sizzle( parts.shift(), context );
bgneal@45 3279
bgneal@45 3280 while ( parts.length ) {
bgneal@45 3281 selector = parts.shift();
bgneal@45 3282
bgneal@45 3283 if ( Expr.relative[ selector ] )
bgneal@45 3284 selector += parts.shift();
bgneal@45 3285
bgneal@45 3286 set = posProcess( selector, set );
bgneal@45 3287 }
bgneal@45 3288 }
bgneal@45 3289 } else {
bgneal@45 3290 // Take a shortcut and set the context if the root selector is an ID
bgneal@45 3291 // (but not if it'll be faster if the inner selector is an ID)
bgneal@45 3292 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
bgneal@45 3293 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
bgneal@45 3294 var ret = Sizzle.find( parts.shift(), context, contextXML );
bgneal@45 3295 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
bgneal@45 3296 }
bgneal@45 3297
bgneal@45 3298 if ( context ) {
bgneal@45 3299 var ret = seed ?
bgneal@45 3300 { expr: parts.pop(), set: makeArray(seed) } :
bgneal@45 3301 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
bgneal@45 3302 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
bgneal@45 3303
bgneal@45 3304 if ( parts.length > 0 ) {
bgneal@45 3305 checkSet = makeArray(set);
bgneal@45 3306 } else {
bgneal@45 3307 prune = false;
bgneal@45 3308 }
bgneal@45 3309
bgneal@45 3310 while ( parts.length ) {
bgneal@45 3311 var cur = parts.pop(), pop = cur;
bgneal@45 3312
bgneal@45 3313 if ( !Expr.relative[ cur ] ) {
bgneal@45 3314 cur = "";
bgneal@45 3315 } else {
bgneal@45 3316 pop = parts.pop();
bgneal@45 3317 }
bgneal@45 3318
bgneal@45 3319 if ( pop == null ) {
bgneal@45 3320 pop = context;
bgneal@45 3321 }
bgneal@45 3322
bgneal@45 3323 Expr.relative[ cur ]( checkSet, pop, contextXML );
bgneal@45 3324 }
bgneal@45 3325 } else {
bgneal@45 3326 checkSet = parts = [];
bgneal@45 3327 }
bgneal@45 3328 }
bgneal@45 3329
bgneal@45 3330 if ( !checkSet ) {
bgneal@45 3331 checkSet = set;
bgneal@45 3332 }
bgneal@45 3333
bgneal@45 3334 if ( !checkSet ) {
bgneal@45 3335 throw "Syntax error, unrecognized expression: " + (cur || selector);
bgneal@45 3336 }
bgneal@45 3337
bgneal@45 3338 if ( toString.call(checkSet) === "[object Array]" ) {
bgneal@45 3339 if ( !prune ) {
bgneal@45 3340 arrayPush.apply( results, checkSet );
bgneal@45 3341 } else if ( context && context.nodeType === 1 ) {
bgneal@45 3342 for ( var i = 0; checkSet[i] != null; i++ ) {
bgneal@45 3343 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
bgneal@45 3344 arrayPush.call( results, set[i] );
bgneal@45 3345 }
bgneal@45 3346 }
bgneal@45 3347 } else {
bgneal@45 3348 for ( var i = 0; checkSet[i] != null; i++ ) {
bgneal@45 3349 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
bgneal@45 3350 arrayPush.call( results, set[i] );
bgneal@45 3351 }
bgneal@45 3352 }
bgneal@45 3353 }
bgneal@45 3354 } else {
bgneal@45 3355 makeArray( checkSet, results );
bgneal@45 3356 }
bgneal@45 3357
bgneal@45 3358 if ( extra ) {
bgneal@45 3359 Sizzle( extra, origContext, results, seed );
bgneal@45 3360 Sizzle.uniqueSort( results );
bgneal@45 3361 }
bgneal@45 3362
bgneal@45 3363 return results;
bgneal@45 3364 };
bgneal@45 3365
bgneal@45 3366 Sizzle.uniqueSort = function(results){
bgneal@45 3367 if ( sortOrder ) {
bgneal@45 3368 hasDuplicate = false;
bgneal@45 3369 arraySort.call(results, sortOrder);
bgneal@45 3370
bgneal@45 3371 if ( hasDuplicate ) {
bgneal@45 3372 for ( var i = 1; i < results.length; i++ ) {
bgneal@45 3373 if ( results[i] === results[i-1] ) {
bgneal@45 3374 arraySplice.call(results, i--, 1);
bgneal@45 3375 }
bgneal@45 3376 }
bgneal@45 3377 }
bgneal@45 3378 }
bgneal@45 3379 };
bgneal@45 3380
bgneal@45 3381 Sizzle.matches = function(expr, set){
bgneal@45 3382 return Sizzle(expr, null, null, set);
bgneal@45 3383 };
bgneal@45 3384
bgneal@45 3385 Sizzle.find = function(expr, context, isXML){
bgneal@45 3386 var set, match;
bgneal@45 3387
bgneal@45 3388 if ( !expr ) {
bgneal@45 3389 return [];
bgneal@45 3390 }
bgneal@45 3391
bgneal@45 3392 for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
bgneal@45 3393 var type = Expr.order[i], match;
bgneal@45 3394
bgneal@45 3395 if ( (match = Expr.match[ type ].exec( expr )) ) {
bgneal@45 3396 var left = RegExp.leftContext;
bgneal@45 3397
bgneal@45 3398 if ( left.substr( left.length - 1 ) !== "\\" ) {
bgneal@45 3399 match[1] = (match[1] || "").replace(/\\/g, "");
bgneal@45 3400 set = Expr.find[ type ]( match, context, isXML );
bgneal@45 3401 if ( set != null ) {
bgneal@45 3402 expr = expr.replace( Expr.match[ type ], "" );
bgneal@45 3403 break;
bgneal@45 3404 }
bgneal@45 3405 }
bgneal@45 3406 }
bgneal@45 3407 }
bgneal@45 3408
bgneal@45 3409 if ( !set ) {
bgneal@45 3410 set = context.getElementsByTagName("*");
bgneal@45 3411 }
bgneal@45 3412
bgneal@45 3413 return {set: set, expr: expr};
bgneal@45 3414 };
bgneal@45 3415
bgneal@45 3416 Sizzle.filter = function(expr, set, inplace, not){
bgneal@45 3417 var old = expr, result = [], curLoop = set, match, anyFound,
bgneal@45 3418 isXMLFilter = set && set[0] && isXML(set[0]);
bgneal@45 3419
bgneal@45 3420 while ( expr && set.length ) {
bgneal@45 3421 for ( var type in Expr.filter ) {
bgneal@45 3422 if ( (match = Expr.match[ type ].exec( expr )) != null ) {
bgneal@45 3423 var filter = Expr.filter[ type ], found, item;
bgneal@45 3424 anyFound = false;
bgneal@45 3425
bgneal@45 3426 if ( curLoop == result ) {
bgneal@45 3427 result = [];
bgneal@45 3428 }
bgneal@45 3429
bgneal@45 3430 if ( Expr.preFilter[ type ] ) {
bgneal@45 3431 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
bgneal@45 3432
bgneal@45 3433 if ( !match ) {
bgneal@45 3434 anyFound = found = true;
bgneal@45 3435 } else if ( match === true ) {
bgneal@45 3436 continue;
bgneal@45 3437 }
bgneal@45 3438 }
bgneal@45 3439
bgneal@45 3440 if ( match ) {
bgneal@45 3441 for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
bgneal@45 3442 if ( item ) {
bgneal@45 3443 found = filter( item, match, i, curLoop );
bgneal@45 3444 var pass = not ^ !!found;
bgneal@45 3445
bgneal@45 3446 if ( inplace && found != null ) {
bgneal@45 3447 if ( pass ) {
bgneal@45 3448 anyFound = true;
bgneal@45 3449 } else {
bgneal@45 3450 curLoop[i] = false;
bgneal@45 3451 }
bgneal@45 3452 } else if ( pass ) {
bgneal@45 3453 result.push( item );
bgneal@45 3454 anyFound = true;
bgneal@45 3455 }
bgneal@45 3456 }
bgneal@45 3457 }
bgneal@45 3458 }
bgneal@45 3459
bgneal@45 3460 if ( found !== undefined ) {
bgneal@45 3461 if ( !inplace ) {
bgneal@45 3462 curLoop = result;
bgneal@45 3463 }
bgneal@45 3464
bgneal@45 3465 expr = expr.replace( Expr.match[ type ], "" );
bgneal@45 3466
bgneal@45 3467 if ( !anyFound ) {
bgneal@45 3468 return [];
bgneal@45 3469 }
bgneal@45 3470
bgneal@45 3471 break;
bgneal@45 3472 }
bgneal@45 3473 }
bgneal@45 3474 }
bgneal@45 3475
bgneal@45 3476 // Improper expression
bgneal@45 3477 if ( expr == old ) {
bgneal@45 3478 if ( anyFound == null ) {
bgneal@45 3479 throw "Syntax error, unrecognized expression: " + expr;
bgneal@45 3480 } else {
bgneal@45 3481 break;
bgneal@45 3482 }
bgneal@45 3483 }
bgneal@45 3484
bgneal@45 3485 old = expr;
bgneal@45 3486 }
bgneal@45 3487
bgneal@45 3488 return curLoop;
bgneal@45 3489 };
bgneal@45 3490
bgneal@45 3491 var Expr = Sizzle.selectors = {
bgneal@45 3492 order: [ "ID", "NAME", "TAG" ],
bgneal@45 3493 match: {
bgneal@45 3494 ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
bgneal@45 3495 CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
bgneal@45 3496 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
bgneal@45 3497 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
bgneal@45 3498 TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
bgneal@45 3499 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
bgneal@45 3500 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
bgneal@45 3501 PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
bgneal@45 3502 },
bgneal@45 3503 attrMap: {
bgneal@45 3504 "class": "className",
bgneal@45 3505 "for": "htmlFor"
bgneal@45 3506 },
bgneal@45 3507 attrHandle: {
bgneal@45 3508 href: function(elem){
bgneal@45 3509 return elem.getAttribute("href");
bgneal@45 3510 }
bgneal@45 3511 },
bgneal@45 3512 relative: {
bgneal@45 3513 "+": function(checkSet, part, isXML){
bgneal@45 3514 var isPartStr = typeof part === "string",
bgneal@45 3515 isTag = isPartStr && !/\W/.test(part),
bgneal@45 3516 isPartStrNotTag = isPartStr && !isTag;
bgneal@45 3517
bgneal@45 3518 if ( isTag && !isXML ) {
bgneal@45 3519 part = part.toUpperCase();
bgneal@45 3520 }
bgneal@45 3521
bgneal@45 3522 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
bgneal@45 3523 if ( (elem = checkSet[i]) ) {
bgneal@45 3524 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
bgneal@45 3525
bgneal@45 3526 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
bgneal@45 3527 elem || false :
bgneal@45 3528 elem === part;
bgneal@45 3529 }
bgneal@45 3530 }
bgneal@45 3531
bgneal@45 3532 if ( isPartStrNotTag ) {
bgneal@45 3533 Sizzle.filter( part, checkSet, true );
bgneal@45 3534 }
bgneal@45 3535 },
bgneal@45 3536 ">": function(checkSet, part, isXML){
bgneal@45 3537 var isPartStr = typeof part === "string";
bgneal@45 3538
bgneal@45 3539 if ( isPartStr && !/\W/.test(part) ) {
bgneal@45 3540 part = isXML ? part : part.toUpperCase();
bgneal@45 3541
bgneal@45 3542 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
bgneal@45 3543 var elem = checkSet[i];
bgneal@45 3544 if ( elem ) {
bgneal@45 3545 var parent = elem.parentNode;
bgneal@45 3546 checkSet[i] = parent.nodeName === part ? parent : false;
bgneal@45 3547 }
bgneal@45 3548 }
bgneal@45 3549 } else {
bgneal@45 3550 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
bgneal@45 3551 var elem = checkSet[i];
bgneal@45 3552 if ( elem ) {
bgneal@45 3553 checkSet[i] = isPartStr ?
bgneal@45 3554 elem.parentNode :
bgneal@45 3555 elem.parentNode === part;
bgneal@45 3556 }
bgneal@45 3557 }
bgneal@45 3558
bgneal@45 3559 if ( isPartStr ) {
bgneal@45 3560 Sizzle.filter( part, checkSet, true );
bgneal@45 3561 }
bgneal@45 3562 }
bgneal@45 3563 },
bgneal@45 3564 "": function(checkSet, part, isXML){
bgneal@45 3565 var doneName = done++, checkFn = dirCheck;
bgneal@45 3566
bgneal@45 3567 if ( !part.match(/\W/) ) {
bgneal@45 3568 var nodeCheck = part = isXML ? part : part.toUpperCase();
bgneal@45 3569 checkFn = dirNodeCheck;
bgneal@45 3570 }
bgneal@45 3571
bgneal@45 3572 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
bgneal@45 3573 },
bgneal@45 3574 "~": function(checkSet, part, isXML){
bgneal@45 3575 var doneName = done++, checkFn = dirCheck;
bgneal@45 3576
bgneal@45 3577 if ( typeof part === "string" && !part.match(/\W/) ) {
bgneal@45 3578 var nodeCheck = part = isXML ? part : part.toUpperCase();
bgneal@45 3579 checkFn = dirNodeCheck;
bgneal@45 3580 }
bgneal@45 3581
bgneal@45 3582 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
bgneal@45 3583 }
bgneal@45 3584 },
bgneal@45 3585 find: {
bgneal@45 3586 ID: function(match, context, isXML){
bgneal@45 3587 if ( typeof context.getElementById !== "undefined" && !isXML ) {
bgneal@45 3588 var m = context.getElementById(match[1]);
bgneal@45 3589 return m ? [m] : [];
bgneal@45 3590 }
bgneal@45 3591 },
bgneal@45 3592 NAME: function(match, context, isXML){
bgneal@45 3593 if ( typeof context.getElementsByName !== "undefined" ) {
bgneal@45 3594 var ret = [], results = context.getElementsByName(match[1]);
bgneal@45 3595
bgneal@45 3596 for ( var i = 0, l = results.length; i < l; i++ ) {
bgneal@45 3597 if ( results[i].getAttribute("name") === match[1] ) {
bgneal@45 3598 ret.push( results[i] );
bgneal@45 3599 }
bgneal@45 3600 }
bgneal@45 3601
bgneal@45 3602 return ret.length === 0 ? null : ret;
bgneal@45 3603 }
bgneal@45 3604 },
bgneal@45 3605 TAG: function(match, context){
bgneal@45 3606 return context.getElementsByTagName(match[1]);
bgneal@45 3607 }
bgneal@45 3608 },
bgneal@45 3609 preFilter: {
bgneal@45 3610 CLASS: function(match, curLoop, inplace, result, not, isXML){
bgneal@45 3611 match = " " + match[1].replace(/\\/g, "") + " ";
bgneal@45 3612
bgneal@45 3613 if ( isXML ) {
bgneal@45 3614 return match;
bgneal@45 3615 }
bgneal@45 3616
bgneal@45 3617 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
bgneal@45 3618 if ( elem ) {
bgneal@45 3619 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
bgneal@45 3620 if ( !inplace )
bgneal@45 3621 result.push( elem );
bgneal@45 3622 } else if ( inplace ) {
bgneal@45 3623 curLoop[i] = false;
bgneal@45 3624 }
bgneal@45 3625 }
bgneal@45 3626 }
bgneal@45 3627
bgneal@45 3628 return false;
bgneal@45 3629 },
bgneal@45 3630 ID: function(match){
bgneal@45 3631 return match[1].replace(/\\/g, "");
bgneal@45 3632 },
bgneal@45 3633 TAG: function(match, curLoop){
bgneal@45 3634 for ( var i = 0; curLoop[i] === false; i++ ){}
bgneal@45 3635 return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
bgneal@45 3636 },
bgneal@45 3637 CHILD: function(match){
bgneal@45 3638 if ( match[1] == "nth" ) {
bgneal@45 3639 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
bgneal@45 3640 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
bgneal@45 3641 match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
bgneal@45 3642 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
bgneal@45 3643
bgneal@45 3644 // calculate the numbers (first)n+(last) including if they are negative
bgneal@45 3645 match[2] = (test[1] + (test[2] || 1)) - 0;
bgneal@45 3646 match[3] = test[3] - 0;
bgneal@45 3647 }
bgneal@45 3648
bgneal@45 3649 // TODO: Move to normal caching system
bgneal@45 3650 match[0] = done++;
bgneal@45 3651
bgneal@45 3652 return match;
bgneal@45 3653 },
bgneal@45 3654 ATTR: function(match, curLoop, inplace, result, not, isXML){
bgneal@45 3655 var name = match[1].replace(/\\/g, "");
bgneal@45 3656
bgneal@45 3657 if ( !isXML && Expr.attrMap[name] ) {
bgneal@45 3658 match[1] = Expr.attrMap[name];
bgneal@45 3659 }
bgneal@45 3660
bgneal@45 3661 if ( match[2] === "~=" ) {
bgneal@45 3662 match[4] = " " + match[4] + " ";
bgneal@45 3663 }
bgneal@45 3664
bgneal@45 3665 return match;
bgneal@45 3666 },
bgneal@45 3667 PSEUDO: function(match, curLoop, inplace, result, not){
bgneal@45 3668 if ( match[1] === "not" ) {
bgneal@45 3669 // If we're dealing with a complex expression, or a simple one
bgneal@45 3670 if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
bgneal@45 3671 match[3] = Sizzle(match[3], null, null, curLoop);
bgneal@45 3672 } else {
bgneal@45 3673 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
bgneal@45 3674 if ( !inplace ) {
bgneal@45 3675 result.push.apply( result, ret );
bgneal@45 3676 }
bgneal@45 3677 return false;
bgneal@45 3678 }
bgneal@45 3679 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
bgneal@45 3680 return true;
bgneal@45 3681 }
bgneal@45 3682
bgneal@45 3683 return match;
bgneal@45 3684 },
bgneal@45 3685 POS: function(match){
bgneal@45 3686 match.unshift( true );
bgneal@45 3687 return match;
bgneal@45 3688 }
bgneal@45 3689 },
bgneal@45 3690 filters: {
bgneal@45 3691 enabled: function(elem){
bgneal@45 3692 return elem.disabled === false && elem.type !== "hidden";
bgneal@45 3693 },
bgneal@45 3694 disabled: function(elem){
bgneal@45 3695 return elem.disabled === true;
bgneal@45 3696 },
bgneal@45 3697 checked: function(elem){
bgneal@45 3698 return elem.checked === true;
bgneal@45 3699 },
bgneal@45 3700 selected: function(elem){
bgneal@45 3701 // Accessing this property makes selected-by-default
bgneal@45 3702 // options in Safari work properly
bgneal@45 3703 elem.parentNode.selectedIndex;
bgneal@45 3704 return elem.selected === true;
bgneal@45 3705 },
bgneal@45 3706 parent: function(elem){
bgneal@45 3707 return !!elem.firstChild;
bgneal@45 3708 },
bgneal@45 3709 empty: function(elem){
bgneal@45 3710 return !elem.firstChild;
bgneal@45 3711 },
bgneal@45 3712 has: function(elem, i, match){
bgneal@45 3713 return !!Sizzle( match[3], elem ).length;
bgneal@45 3714 },
bgneal@45 3715 header: function(elem){
bgneal@45 3716 return /h\d/i.test( elem.nodeName );
bgneal@45 3717 },
bgneal@45 3718 text: function(elem){
bgneal@45 3719 return "text" === elem.type;
bgneal@45 3720 },
bgneal@45 3721 radio: function(elem){
bgneal@45 3722 return "radio" === elem.type;
bgneal@45 3723 },
bgneal@45 3724 checkbox: function(elem){
bgneal@45 3725 return "checkbox" === elem.type;
bgneal@45 3726 },
bgneal@45 3727 file: function(elem){
bgneal@45 3728 return "file" === elem.type;
bgneal@45 3729 },
bgneal@45 3730 password: function(elem){
bgneal@45 3731 return "password" === elem.type;
bgneal@45 3732 },
bgneal@45 3733 submit: function(elem){
bgneal@45 3734 return "submit" === elem.type;
bgneal@45 3735 },
bgneal@45 3736 image: function(elem){
bgneal@45 3737 return "image" === elem.type;
bgneal@45 3738 },
bgneal@45 3739 reset: function(elem){
bgneal@45 3740 return "reset" === elem.type;
bgneal@45 3741 },
bgneal@45 3742 button: function(elem){
bgneal@45 3743 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
bgneal@45 3744 },
bgneal@45 3745 input: function(elem){
bgneal@45 3746 return /input|select|textarea|button/i.test(elem.nodeName);
bgneal@45 3747 }
bgneal@45 3748 },
bgneal@45 3749 setFilters: {
bgneal@45 3750 first: function(elem, i){
bgneal@45 3751 return i === 0;
bgneal@45 3752 },
bgneal@45 3753 last: function(elem, i, match, array){
bgneal@45 3754 return i === array.length - 1;
bgneal@45 3755 },
bgneal@45 3756 even: function(elem, i){
bgneal@45 3757 return i % 2 === 0;
bgneal@45 3758 },
bgneal@45 3759 odd: function(elem, i){
bgneal@45 3760 return i % 2 === 1;
bgneal@45 3761 },
bgneal@45 3762 lt: function(elem, i, match){
bgneal@45 3763 return i < match[3] - 0;
bgneal@45 3764 },
bgneal@45 3765 gt: function(elem, i, match){
bgneal@45 3766 return i > match[3] - 0;
bgneal@45 3767 },
bgneal@45 3768 nth: function(elem, i, match){
bgneal@45 3769 return match[3] - 0 == i;
bgneal@45 3770 },
bgneal@45 3771 eq: function(elem, i, match){
bgneal@45 3772 return match[3] - 0 == i;
bgneal@45 3773 }
bgneal@45 3774 },
bgneal@45 3775 filter: {
bgneal@45 3776 PSEUDO: function(elem, match, i, array){
bgneal@45 3777 var name = match[1], filter = Expr.filters[ name ];
bgneal@45 3778
bgneal@45 3779 if ( filter ) {
bgneal@45 3780 return filter( elem, i, match, array );
bgneal@45 3781 } else if ( name === "contains" ) {
bgneal@45 3782 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
bgneal@45 3783 } else if ( name === "not" ) {
bgneal@45 3784 var not = match[3];
bgneal@45 3785
bgneal@45 3786 for ( var i = 0, l = not.length; i < l; i++ ) {
bgneal@45 3787 if ( not[i] === elem ) {
bgneal@45 3788 return false;
bgneal@45 3789 }
bgneal@45 3790 }
bgneal@45 3791
bgneal@45 3792 return true;
bgneal@45 3793 }
bgneal@45 3794 },
bgneal@45 3795 CHILD: function(elem, match){
bgneal@45 3796 var type = match[1], node = elem;
bgneal@45 3797 switch (type) {
bgneal@45 3798 case 'only':
bgneal@45 3799 case 'first':
bgneal@45 3800 while (node = node.previousSibling) {
bgneal@45 3801 if ( node.nodeType === 1 ) return false;
bgneal@45 3802 }
bgneal@45 3803 if ( type == 'first') return true;
bgneal@45 3804 node = elem;
bgneal@45 3805 case 'last':
bgneal@45 3806 while (node = node.nextSibling) {
bgneal@45 3807 if ( node.nodeType === 1 ) return false;
bgneal@45 3808 }
bgneal@45 3809 return true;
bgneal@45 3810 case 'nth':
bgneal@45 3811 var first = match[2], last = match[3];
bgneal@45 3812
bgneal@45 3813 if ( first == 1 && last == 0 ) {
bgneal@45 3814 return true;
bgneal@45 3815 }
bgneal@45 3816
bgneal@45 3817 var doneName = match[0],
bgneal@45 3818 parent = elem.parentNode;
bgneal@45 3819
bgneal@45 3820 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
bgneal@45 3821 var count = 0;
bgneal@45 3822 for ( node = parent.firstChild; node; node = node.nextSibling ) {
bgneal@45 3823 if ( node.nodeType === 1 ) {
bgneal@45 3824 node.nodeIndex = ++count;
bgneal@45 3825 }
bgneal@45 3826 }
bgneal@45 3827 parent.sizcache = doneName;
bgneal@45 3828 }
bgneal@45 3829
bgneal@45 3830 var diff = elem.nodeIndex - last;
bgneal@45 3831 if ( first == 0 ) {
bgneal@45 3832 return diff == 0;
bgneal@45 3833 } else {
bgneal@45 3834 return ( diff % first == 0 && diff / first >= 0 );
bgneal@45 3835 }
bgneal@45 3836 }
bgneal@45 3837 },
bgneal@45 3838 ID: function(elem, match){
bgneal@45 3839 return elem.nodeType === 1 && elem.getAttribute("id") === match;
bgneal@45 3840 },
bgneal@45 3841 TAG: function(elem, match){
bgneal@45 3842 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
bgneal@45 3843 },
bgneal@45 3844 CLASS: function(elem, match){
bgneal@45 3845 return (" " + (elem.className || elem.getAttribute("class")) + " ")
bgneal@45 3846 .indexOf( match ) > -1;
bgneal@45 3847 },
bgneal@45 3848 ATTR: function(elem, match){
bgneal@45 3849 var name = match[1],
bgneal@45 3850 result = Expr.attrHandle[ name ] ?
bgneal@45 3851 Expr.attrHandle[ name ]( elem ) :
bgneal@45 3852 elem[ name ] != null ?
bgneal@45 3853 elem[ name ] :
bgneal@45 3854 elem.getAttribute( name ),
bgneal@45 3855 value = result + "",
bgneal@45 3856 type = match[2],
bgneal@45 3857 check = match[4];
bgneal@45 3858
bgneal@45 3859 return result == null ?
bgneal@45 3860 type === "!=" :
bgneal@45 3861 type === "=" ?
bgneal@45 3862 value === check :
bgneal@45 3863 type === "*=" ?
bgneal@45 3864 value.indexOf(check) >= 0 :
bgneal@45 3865 type === "~=" ?
bgneal@45 3866 (" " + value + " ").indexOf(check) >= 0 :
bgneal@45 3867 !check ?
bgneal@45 3868 value && result !== false :
bgneal@45 3869 type === "!=" ?
bgneal@45 3870 value != check :
bgneal@45 3871 type === "^=" ?
bgneal@45 3872 value.indexOf(check) === 0 :
bgneal@45 3873 type === "$=" ?
bgneal@45 3874 value.substr(value.length - check.length) === check :
bgneal@45 3875 type === "|=" ?
bgneal@45 3876 value === check || value.substr(0, check.length + 1) === check + "-" :
bgneal@45 3877 false;
bgneal@45 3878 },
bgneal@45 3879 POS: function(elem, match, i, array){
bgneal@45 3880 var name = match[2], filter = Expr.setFilters[ name ];
bgneal@45 3881
bgneal@45 3882 if ( filter ) {
bgneal@45 3883 return filter( elem, i, match, array );
bgneal@45 3884 }
bgneal@45 3885 }
bgneal@45 3886 }
bgneal@45 3887 };
bgneal@45 3888
bgneal@45 3889 var origPOS = Expr.match.POS;
bgneal@45 3890
bgneal@45 3891 for ( var type in Expr.match ) {
bgneal@45 3892 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
bgneal@45 3893 }
bgneal@45 3894
bgneal@45 3895 var makeArray = function(array, results) {
bgneal@45 3896 array = Array.prototype.slice.call( array );
bgneal@45 3897
bgneal@45 3898 if ( results ) {
bgneal@45 3899 arrayPush.apply( results, array );
bgneal@45 3900 return results;
bgneal@45 3901 }
bgneal@45 3902
bgneal@45 3903 return array;
bgneal@45 3904 };
bgneal@45 3905
bgneal@45 3906 // Perform a simple check to determine if the browser is capable of
bgneal@45 3907 // converting a NodeList to an array using builtin methods.
bgneal@45 3908 try {
bgneal@45 3909 Array.prototype.slice.call( document.documentElement.childNodes );
bgneal@45 3910
bgneal@45 3911 // Provide a fallback method if it does not work
bgneal@45 3912 } catch(e){
bgneal@45 3913 makeArray = function(array, results) {
bgneal@45 3914 var ret = results || [];
bgneal@45 3915
bgneal@45 3916 if ( toString.call(array) === "[object Array]" ) {
bgneal@45 3917 Array.prototype.push.apply( ret, array );
bgneal@45 3918 } else {
bgneal@45 3919 if ( typeof array.length === "number" ) {
bgneal@45 3920 for ( var i = 0, l = array.length; i < l; i++ ) {
bgneal@45 3921 ret.push( array[i] );
bgneal@45 3922 }
bgneal@45 3923 } else {
bgneal@45 3924 for ( var i = 0; array[i]; i++ ) {
bgneal@45 3925 ret.push( array[i] );
bgneal@45 3926 }
bgneal@45 3927 }
bgneal@45 3928 }
bgneal@45 3929
bgneal@45 3930 return ret;
bgneal@45 3931 };
bgneal@45 3932 }
bgneal@45 3933
bgneal@45 3934 var sortOrder;
bgneal@45 3935
bgneal@45 3936 if ( document.documentElement.compareDocumentPosition ) {
bgneal@45 3937 sortOrder = function( a, b ) {
bgneal@45 3938 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
bgneal@45 3939 if ( ret === 0 ) {
bgneal@45 3940 hasDuplicate = true;
bgneal@45 3941 }
bgneal@45 3942 return ret;
bgneal@45 3943 };
bgneal@45 3944 } else if ( "sourceIndex" in document.documentElement ) {
bgneal@45 3945 sortOrder = function( a, b ) {
bgneal@45 3946 var ret = a.sourceIndex - b.sourceIndex;
bgneal@45 3947 if ( ret === 0 ) {
bgneal@45 3948 hasDuplicate = true;
bgneal@45 3949 }
bgneal@45 3950 return ret;
bgneal@45 3951 };
bgneal@45 3952 } else if ( document.createRange ) {
bgneal@45 3953 sortOrder = function( a, b ) {
bgneal@45 3954 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
bgneal@45 3955 aRange.selectNode(a);
bgneal@45 3956 aRange.collapse(true);
bgneal@45 3957 bRange.selectNode(b);
bgneal@45 3958 bRange.collapse(true);
bgneal@45 3959 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
bgneal@45 3960 if ( ret === 0 ) {
bgneal@45 3961 hasDuplicate = true;
bgneal@45 3962 }
bgneal@45 3963 return ret;
bgneal@45 3964 };
bgneal@45 3965 }
bgneal@45 3966
bgneal@45 3967 // Check to see if the browser returns elements by name when
bgneal@45 3968 // querying by getElementById (and provide a workaround)
bgneal@45 3969 (function(){
bgneal@45 3970 // We're going to inject a fake input element with a specified name
bgneal@45 3971 var form = document.createElement("form"),
bgneal@45 3972 id = "script" + (new Date).getTime();
bgneal@45 3973 form.innerHTML = "<input name='" + id + "'/>";
bgneal@45 3974
bgneal@45 3975 // Inject it into the root element, check its status, and remove it quickly
bgneal@45 3976 var root = document.documentElement;
bgneal@45 3977 root.insertBefore( form, root.firstChild );
bgneal@45 3978
bgneal@45 3979 // The workaround has to do additional checks after a getElementById
bgneal@45 3980 // Which slows things down for other browsers (hence the branching)
bgneal@45 3981 if ( !!document.getElementById( id ) ) {
bgneal@45 3982 Expr.find.ID = function(match, context, isXML){
bgneal@45 3983 if ( typeof context.getElementById !== "undefined" && !isXML ) {
bgneal@45 3984 var m = context.getElementById(match[1]);
bgneal@45 3985 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
bgneal@45 3986 }
bgneal@45 3987 };
bgneal@45 3988
bgneal@45 3989 Expr.filter.ID = function(elem, match){
bgneal@45 3990 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
bgneal@45 3991 return elem.nodeType === 1 && node && node.nodeValue === match;
bgneal@45 3992 };
bgneal@45 3993 }
bgneal@45 3994
bgneal@45 3995 root.removeChild( form );
bgneal@45 3996 })();
bgneal@45 3997
bgneal@45 3998 (function(){
bgneal@45 3999 // Check to see if the browser returns only elements
bgneal@45 4000 // when doing getElementsByTagName("*")
bgneal@45 4001
bgneal@45 4002 // Create a fake element
bgneal@45 4003 var div = document.createElement("div");
bgneal@45 4004 div.appendChild( document.createComment("") );
bgneal@45 4005
bgneal@45 4006 // Make sure no comments are found
bgneal@45 4007 if ( div.getElementsByTagName("*").length > 0 ) {
bgneal@45 4008 Expr.find.TAG = function(match, context){
bgneal@45 4009 var results = context.getElementsByTagName(match[1]);
bgneal@45 4010
bgneal@45 4011 // Filter out possible comments
bgneal@45 4012 if ( match[1] === "*" ) {
bgneal@45 4013 var tmp = [];
bgneal@45 4014
bgneal@45 4015 for ( var i = 0; results[i]; i++ ) {
bgneal@45 4016 if ( results[i].nodeType === 1 ) {
bgneal@45 4017 tmp.push( results[i] );
bgneal@45 4018 }
bgneal@45 4019 }
bgneal@45 4020
bgneal@45 4021 results = tmp;
bgneal@45 4022 }
bgneal@45 4023
bgneal@45 4024 return results;
bgneal@45 4025 };
bgneal@45 4026 }
bgneal@45 4027
bgneal@45 4028 // Check to see if an attribute returns normalized href attributes
bgneal@45 4029 div.innerHTML = "<a href='#'></a>";
bgneal@45 4030 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
bgneal@45 4031 div.firstChild.getAttribute("href") !== "#" ) {
bgneal@45 4032 Expr.attrHandle.href = function(elem){
bgneal@45 4033 return elem.getAttribute("href", 2);
bgneal@45 4034 };
bgneal@45 4035 }
bgneal@45 4036 })();
bgneal@45 4037
bgneal@45 4038 if ( document.querySelectorAll ) (function(){
bgneal@45 4039 var oldSizzle = Sizzle, div = document.createElement("div");
bgneal@45 4040 div.innerHTML = "<p class='TEST'></p>";
bgneal@45 4041
bgneal@45 4042 // Safari can't handle uppercase or unicode characters when
bgneal@45 4043 // in quirks mode.
bgneal@45 4044 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
bgneal@45 4045 return;
bgneal@45 4046 }
bgneal@45 4047
bgneal@45 4048 Sizzle = function(query, context, extra, seed){
bgneal@45 4049 context = context || document;
bgneal@45 4050
bgneal@45 4051 // Only use querySelectorAll on non-XML documents
bgneal@45 4052 // (ID selectors don't work in non-HTML documents)
bgneal@45 4053 if ( !seed && context.nodeType === 9 && !isXML(context) ) {
bgneal@45 4054 try {
bgneal@45 4055 return makeArray( context.querySelectorAll(query), extra );
bgneal@45 4056 } catch(e){}
bgneal@45 4057 }
bgneal@45 4058
bgneal@45 4059 return oldSizzle(query, context, extra, seed);
bgneal@45 4060 };
bgneal@45 4061
bgneal@45 4062 for ( var prop in oldSizzle ) {
bgneal@45 4063 Sizzle[ prop ] = oldSizzle[ prop ];
bgneal@45 4064 }
bgneal@45 4065 })();
bgneal@45 4066
bgneal@45 4067 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
bgneal@45 4068 var div = document.createElement("div");
bgneal@45 4069 div.innerHTML = "<div class='test e'></div><div class='test'></div>";
bgneal@45 4070
bgneal@45 4071 // Opera can't find a second classname (in 9.6)
bgneal@45 4072 if ( div.getElementsByClassName("e").length === 0 )
bgneal@45 4073 return;
bgneal@45 4074
bgneal@45 4075 // Safari caches class attributes, doesn't catch changes (in 3.2)
bgneal@45 4076 div.lastChild.className = "e";
bgneal@45 4077
bgneal@45 4078 if ( div.getElementsByClassName("e").length === 1 )
bgneal@45 4079 return;
bgneal@45 4080
bgneal@45 4081 Expr.order.splice(1, 0, "CLASS");
bgneal@45 4082 Expr.find.CLASS = function(match, context, isXML) {
bgneal@45 4083 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
bgneal@45 4084 return context.getElementsByClassName(match[1]);
bgneal@45 4085 }
bgneal@45 4086 };
bgneal@45 4087 })();
bgneal@45 4088
bgneal@45 4089 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
bgneal@45 4090 var sibDir = dir == "previousSibling" && !isXML;
bgneal@45 4091 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
bgneal@45 4092 var elem = checkSet[i];
bgneal@45 4093 if ( elem ) {
bgneal@45 4094 if ( sibDir && elem.nodeType === 1 ){
bgneal@45 4095 elem.sizcache = doneName;
bgneal@45 4096 elem.sizset = i;
bgneal@45 4097 }
bgneal@45 4098 elem = elem[dir];
bgneal@45 4099 var match = false;
bgneal@45 4100
bgneal@45 4101 while ( elem ) {
bgneal@45 4102 if ( elem.sizcache === doneName ) {
bgneal@45 4103 match = checkSet[elem.sizset];
bgneal@45 4104 break;
bgneal@45 4105 }
bgneal@45 4106
bgneal@45 4107 if ( elem.nodeType === 1 && !isXML ){
bgneal@45 4108 elem.sizcache = doneName;
bgneal@45 4109 elem.sizset = i;
bgneal@45 4110 }
bgneal@45 4111
bgneal@45 4112 if ( elem.nodeName === cur ) {
bgneal@45 4113 match = elem;
bgneal@45 4114 break;
bgneal@45 4115 }
bgneal@45 4116
bgneal@45 4117 elem = elem[dir];
bgneal@45 4118 }
bgneal@45 4119
bgneal@45 4120 checkSet[i] = match;
bgneal@45 4121 }
bgneal@45 4122 }
bgneal@45 4123 }
bgneal@45 4124
bgneal@45 4125 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
bgneal@45 4126 var sibDir = dir == "previousSibling" && !isXML;
bgneal@45 4127 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
bgneal@45 4128 var elem = checkSet[i];
bgneal@45 4129 if ( elem ) {
bgneal@45 4130 if ( sibDir && elem.nodeType === 1 ) {
bgneal@45 4131 elem.sizcache = doneName;
bgneal@45 4132 elem.sizset = i;
bgneal@45 4133 }
bgneal@45 4134 elem = elem[dir];
bgneal@45 4135 var match = false;
bgneal@45 4136
bgneal@45 4137 while ( elem ) {
bgneal@45 4138 if ( elem.sizcache === doneName ) {
bgneal@45 4139 match = checkSet[elem.sizset];
bgneal@45 4140 break;
bgneal@45 4141 }
bgneal@45 4142
bgneal@45 4143 if ( elem.nodeType === 1 ) {
bgneal@45 4144 if ( !isXML ) {
bgneal@45 4145 elem.sizcache = doneName;
bgneal@45 4146 elem.sizset = i;
bgneal@45 4147 }
bgneal@45 4148 if ( typeof cur !== "string" ) {
bgneal@45 4149 if ( elem === cur ) {
bgneal@45 4150 match = true;
bgneal@45 4151 break;
bgneal@45 4152 }
bgneal@45 4153
bgneal@45 4154 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
bgneal@45 4155 match = elem;
bgneal@45 4156 break;
bgneal@45 4157 }
bgneal@45 4158 }
bgneal@45 4159
bgneal@45 4160 elem = elem[dir];
bgneal@45 4161 }
bgneal@45 4162
bgneal@45 4163 checkSet[i] = match;
bgneal@45 4164 }
bgneal@45 4165 }
bgneal@45 4166 }
bgneal@45 4167
bgneal@45 4168 var contains = document.compareDocumentPosition ? function(a, b){
bgneal@45 4169 return a.compareDocumentPosition(b) & 16;
bgneal@45 4170 } : function(a, b){
bgneal@45 4171 return a !== b && (a.contains ? a.contains(b) : true);
bgneal@45 4172 };
bgneal@45 4173
bgneal@45 4174 var isXML = function(elem){
bgneal@45 4175 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
bgneal@45 4176 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
bgneal@45 4177 };
bgneal@45 4178
bgneal@45 4179 var posProcess = function(selector, context){
bgneal@45 4180 var tmpSet = [], later = "", match,
bgneal@45 4181 root = context.nodeType ? [context] : context;
bgneal@45 4182
bgneal@45 4183 // Position selectors must be done after the filter
bgneal@45 4184 // And so must :not(positional) so we move all PSEUDOs to the end
bgneal@45 4185 while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
bgneal@45 4186 later += match[0];
bgneal@45 4187 selector = selector.replace( Expr.match.PSEUDO, "" );
bgneal@45 4188 }
bgneal@45 4189
bgneal@45 4190 selector = Expr.relative[selector] ? selector + "*" : selector;
bgneal@45 4191
bgneal@45 4192 for ( var i = 0, l = root.length; i < l; i++ ) {
bgneal@45 4193 Sizzle( selector, root[i], tmpSet );
bgneal@45 4194 }
bgneal@45 4195
bgneal@45 4196 return Sizzle.filter( later, tmpSet );
bgneal@45 4197 };
bgneal@45 4198
bgneal@45 4199 // EXPOSE
bgneal@45 4200
bgneal@45 4201 window.tinymce.dom.Sizzle = Sizzle;
bgneal@45 4202
bgneal@45 4203 })();
bgneal@45 4204 (function(tinymce) {
bgneal@45 4205 // Shorten names
bgneal@45 4206 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
bgneal@45 4207
bgneal@45 4208 tinymce.create('static tinymce.dom.Event', {
bgneal@45 4209 inits : [],
bgneal@45 4210 events : [],
bgneal@45 4211
bgneal@45 4212
bgneal@45 4213 add : function(o, n, f, s) {
bgneal@45 4214 var cb, t = this, el = t.events, r;
bgneal@45 4215
bgneal@45 4216 // Handle array
bgneal@45 4217 if (o && o.hasOwnProperty && o instanceof Array) {
bgneal@45 4218 r = [];
bgneal@45 4219
bgneal@45 4220 each(o, function(o) {
bgneal@45 4221 o = DOM.get(o);
bgneal@45 4222 r.push(t.add(o, n, f, s));
bgneal@45 4223 });
bgneal@45 4224
bgneal@45 4225 return r;
bgneal@45 4226 }
bgneal@45 4227
bgneal@45 4228 o = DOM.get(o);
bgneal@45 4229
bgneal@45 4230 if (!o)
bgneal@45 4231 return;
bgneal@45 4232
bgneal@45 4233 // Setup event callback
bgneal@45 4234 cb = function(e) {
bgneal@45 4235 e = e || window.event;
bgneal@45 4236
bgneal@45 4237 // Patch in target in IE it's W3C valid
bgneal@45 4238 if (e && !e.target && isIE)
bgneal@45 4239 e.target = e.srcElement;
bgneal@45 4240
bgneal@45 4241 if (!s)
bgneal@45 4242 return f(e);
bgneal@45 4243
bgneal@45 4244 return f.call(s, e);
bgneal@45 4245 };
bgneal@45 4246
bgneal@45 4247 if (n == 'unload') {
bgneal@45 4248 tinymce.unloads.unshift({func : cb});
bgneal@45 4249 return cb;
bgneal@45 4250 }
bgneal@45 4251
bgneal@45 4252 if (n == 'init') {
bgneal@45 4253 if (t.domLoaded)
bgneal@45 4254 cb();
bgneal@45 4255 else
bgneal@45 4256 t.inits.push(cb);
bgneal@45 4257
bgneal@45 4258 return cb;
bgneal@45 4259 }
bgneal@45 4260
bgneal@45 4261 // Store away listener reference
bgneal@45 4262 el.push({
bgneal@45 4263 obj : o,
bgneal@45 4264 name : n,
bgneal@45 4265 func : f,
bgneal@45 4266 cfunc : cb,
bgneal@45 4267 scope : s
bgneal@45 4268 });
bgneal@45 4269
bgneal@45 4270 t._add(o, n, cb);
bgneal@45 4271
bgneal@45 4272 return f;
bgneal@45 4273 },
bgneal@45 4274
bgneal@45 4275 remove : function(o, n, f) {
bgneal@45 4276 var t = this, a = t.events, s = false, r;
bgneal@45 4277
bgneal@45 4278 // Handle array
bgneal@45 4279 if (o && o.hasOwnProperty && o instanceof Array) {
bgneal@45 4280 r = [];
bgneal@45 4281
bgneal@45 4282 each(o, function(o) {
bgneal@45 4283 o = DOM.get(o);
bgneal@45 4284 r.push(t.remove(o, n, f));
bgneal@45 4285 });
bgneal@45 4286
bgneal@45 4287 return r;
bgneal@45 4288 }
bgneal@45 4289
bgneal@45 4290 o = DOM.get(o);
bgneal@45 4291
bgneal@45 4292 each(a, function(e, i) {
bgneal@45 4293 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
bgneal@45 4294 a.splice(i, 1);
bgneal@45 4295 t._remove(o, n, e.cfunc);
bgneal@45 4296 s = true;
bgneal@45 4297 return false;
bgneal@45 4298 }
bgneal@45 4299 });
bgneal@45 4300
bgneal@45 4301 return s;
bgneal@45 4302 },
bgneal@45 4303
bgneal@45 4304 clear : function(o) {
bgneal@45 4305 var t = this, a = t.events, i, e;
bgneal@45 4306
bgneal@45 4307 if (o) {
bgneal@45 4308 o = DOM.get(o);
bgneal@45 4309
bgneal@45 4310 for (i = a.length - 1; i >= 0; i--) {
bgneal@45 4311 e = a[i];
bgneal@45 4312
bgneal@45 4313 if (e.obj === o) {
bgneal@45 4314 t._remove(e.obj, e.name, e.cfunc);
bgneal@45 4315 e.obj = e.cfunc = null;
bgneal@45 4316 a.splice(i, 1);
bgneal@45 4317 }
bgneal@45 4318 }
bgneal@45 4319 }
bgneal@45 4320 },
bgneal@45 4321
bgneal@45 4322
bgneal@45 4323 cancel : function(e) {
bgneal@45 4324 if (!e)
bgneal@45 4325 return false;
bgneal@45 4326
bgneal@45 4327 this.stop(e);
bgneal@45 4328 return this.prevent(e);
bgneal@45 4329 },
bgneal@45 4330
bgneal@45 4331 stop : function(e) {
bgneal@45 4332 if (e.stopPropagation)
bgneal@45 4333 e.stopPropagation();
bgneal@45 4334 else
bgneal@45 4335 e.cancelBubble = true;
bgneal@45 4336
bgneal@45 4337 return false;
bgneal@45 4338 },
bgneal@45 4339
bgneal@45 4340 prevent : function(e) {
bgneal@45 4341 if (e.preventDefault)
bgneal@45 4342 e.preventDefault();
bgneal@45 4343 else
bgneal@45 4344 e.returnValue = false;
bgneal@45 4345
bgneal@45 4346 return false;
bgneal@45 4347 },
bgneal@45 4348
bgneal@45 4349 _unload : function() {
bgneal@45 4350 var t = Event;
bgneal@45 4351
bgneal@45 4352 each(t.events, function(e, i) {
bgneal@45 4353 t._remove(e.obj, e.name, e.cfunc);
bgneal@45 4354 e.obj = e.cfunc = null;
bgneal@45 4355 });
bgneal@45 4356
bgneal@45 4357 t.events = [];
bgneal@45 4358 t = null;
bgneal@45 4359 },
bgneal@45 4360
bgneal@45 4361 _add : function(o, n, f) {
bgneal@45 4362 if (o.attachEvent)
bgneal@45 4363 o.attachEvent('on' + n, f);
bgneal@45 4364 else if (o.addEventListener)
bgneal@45 4365 o.addEventListener(n, f, false);
bgneal@45 4366 else
bgneal@45 4367 o['on' + n] = f;
bgneal@45 4368 },
bgneal@45 4369
bgneal@45 4370 _remove : function(o, n, f) {
bgneal@45 4371 if (o) {
bgneal@45 4372 try {
bgneal@45 4373 if (o.detachEvent)
bgneal@45 4374 o.detachEvent('on' + n, f);
bgneal@45 4375 else if (o.removeEventListener)
bgneal@45 4376 o.removeEventListener(n, f, false);
bgneal@45 4377 else
bgneal@45 4378 o['on' + n] = null;
bgneal@45 4379 } catch (ex) {
bgneal@45 4380 // Might fail with permission denined on IE so we just ignore that
bgneal@45 4381 }
bgneal@45 4382 }
bgneal@45 4383 },
bgneal@45 4384
bgneal@45 4385 _pageInit : function() {
bgneal@45 4386 var e = Event;
bgneal@45 4387
bgneal@45 4388 // Safari on Mac fires this twice
bgneal@45 4389 if (e.domLoaded)
bgneal@45 4390 return;
bgneal@45 4391
bgneal@45 4392 e._remove(window, 'DOMContentLoaded', e._pageInit);
bgneal@45 4393 e.domLoaded = true;
bgneal@45 4394
bgneal@45 4395 each(e.inits, function(c) {
bgneal@45 4396 c();
bgneal@45 4397 });
bgneal@45 4398
bgneal@45 4399 e.inits = [];
bgneal@45 4400 },
bgneal@45 4401
bgneal@45 4402 _wait : function() {
bgneal@45 4403 var t;
bgneal@45 4404
bgneal@45 4405 // No need since the document is already loaded
bgneal@45 4406 if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) {
bgneal@45 4407 Event.domLoaded = 1;
bgneal@45 4408 return;
bgneal@45 4409 }
bgneal@45 4410
bgneal@45 4411 if (isIE && document.location.protocol != 'https:') {
bgneal@45 4412 // Fake DOMContentLoaded on IE
bgneal@45 4413 document.write('<script id=__ie_onload defer src=\'javascript:""\';><\/script>');
bgneal@45 4414 DOM.get("__ie_onload").onreadystatechange = function() {
bgneal@45 4415 if (this.readyState == "complete") {
bgneal@45 4416 Event._pageInit();
bgneal@45 4417 DOM.get("__ie_onload").onreadystatechange = null; // Prevent leak
bgneal@45 4418 }
bgneal@45 4419 };
bgneal@45 4420 } else {
bgneal@45 4421 Event._add(window, 'DOMContentLoaded', Event._pageInit, Event);
bgneal@45 4422
bgneal@45 4423 if (isIE || isWebKit) {
bgneal@45 4424 t = setInterval(function() {
bgneal@45 4425 if (/loaded|complete/.test(document.readyState)) {
bgneal@45 4426 clearInterval(t);
bgneal@45 4427 Event._pageInit();
bgneal@45 4428 }
bgneal@45 4429 }, 10);
bgneal@45 4430 }
bgneal@45 4431 }
bgneal@45 4432 }
bgneal@45 4433
bgneal@45 4434 });
bgneal@45 4435
bgneal@45 4436 // Shorten name
bgneal@45 4437 Event = tinymce.dom.Event;
bgneal@45 4438
bgneal@45 4439 // Dispatch DOM content loaded event for IE and Safari
bgneal@45 4440 Event._wait();
bgneal@45 4441 tinymce.addUnload(Event._unload);
bgneal@45 4442 })(tinymce);
bgneal@45 4443 (function(tinymce) {
bgneal@45 4444 var each = tinymce.each;
bgneal@45 4445
bgneal@45 4446 tinymce.create('tinymce.dom.Element', {
bgneal@45 4447 Element : function(id, s) {
bgneal@45 4448 var t = this, dom, el;
bgneal@45 4449
bgneal@45 4450 s = s || {};
bgneal@45 4451 t.id = id;
bgneal@45 4452 t.dom = dom = s.dom || tinymce.DOM;
bgneal@45 4453 t.settings = s;
bgneal@45 4454
bgneal@45 4455 // Only IE leaks DOM references, this is a lot faster
bgneal@45 4456 if (!tinymce.isIE)
bgneal@45 4457 el = t.dom.get(t.id);
bgneal@45 4458
bgneal@45 4459 each([
bgneal@45 4460 'getPos',
bgneal@45 4461 'getRect',
bgneal@45 4462 'getParent',
bgneal@45 4463 'add',
bgneal@45 4464 'setStyle',
bgneal@45 4465 'getStyle',
bgneal@45 4466 'setStyles',
bgneal@45 4467 'setAttrib',
bgneal@45 4468 'setAttribs',
bgneal@45 4469 'getAttrib',
bgneal@45 4470 'addClass',
bgneal@45 4471 'removeClass',
bgneal@45 4472 'hasClass',
bgneal@45 4473 'getOuterHTML',
bgneal@45 4474 'setOuterHTML',
bgneal@45 4475 'remove',
bgneal@45 4476 'show',
bgneal@45 4477 'hide',
bgneal@45 4478 'isHidden',
bgneal@45 4479 'setHTML',
bgneal@45 4480 'get'
bgneal@45 4481 ], function(k) {
bgneal@45 4482 t[k] = function() {
bgneal@45 4483 var a = [id], i;
bgneal@45 4484
bgneal@45 4485 for (i = 0; i < arguments.length; i++)
bgneal@45 4486 a.push(arguments[i]);
bgneal@45 4487
bgneal@45 4488 a = dom[k].apply(dom, a);
bgneal@45 4489 t.update(k);
bgneal@45 4490
bgneal@45 4491 return a;
bgneal@45 4492 };
bgneal@45 4493 });
bgneal@45 4494 },
bgneal@45 4495
bgneal@45 4496 on : function(n, f, s) {
bgneal@45 4497 return tinymce.dom.Event.add(this.id, n, f, s);
bgneal@45 4498 },
bgneal@45 4499
bgneal@45 4500 getXY : function() {
bgneal@45 4501 return {
bgneal@45 4502 x : parseInt(this.getStyle('left')),
bgneal@45 4503 y : parseInt(this.getStyle('top'))
bgneal@45 4504 };
bgneal@45 4505 },
bgneal@45 4506
bgneal@45 4507 getSize : function() {
bgneal@45 4508 var n = this.dom.get(this.id);
bgneal@45 4509
bgneal@45 4510 return {
bgneal@45 4511 w : parseInt(this.getStyle('width') || n.clientWidth),
bgneal@45 4512 h : parseInt(this.getStyle('height') || n.clientHeight)
bgneal@45 4513 };
bgneal@45 4514 },
bgneal@45 4515
bgneal@45 4516 moveTo : function(x, y) {
bgneal@45 4517 this.setStyles({left : x, top : y});
bgneal@45 4518 },
bgneal@45 4519
bgneal@45 4520 moveBy : function(x, y) {
bgneal@45 4521 var p = this.getXY();
bgneal@45 4522
bgneal@45 4523 this.moveTo(p.x + x, p.y + y);
bgneal@45 4524 },
bgneal@45 4525
bgneal@45 4526 resizeTo : function(w, h) {
bgneal@45 4527 this.setStyles({width : w, height : h});
bgneal@45 4528 },
bgneal@45 4529
bgneal@45 4530 resizeBy : function(w, h) {
bgneal@45 4531 var s = this.getSize();
bgneal@45 4532
bgneal@45 4533 this.resizeTo(s.w + w, s.h + h);
bgneal@45 4534 },
bgneal@45 4535
bgneal@45 4536 update : function(k) {
bgneal@45 4537 var t = this, b, dom = t.dom;
bgneal@45 4538
bgneal@45 4539 if (tinymce.isIE6 && t.settings.blocker) {
bgneal@45 4540 k = k || '';
bgneal@45 4541
bgneal@45 4542 // Ignore getters
bgneal@45 4543 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
bgneal@45 4544 return;
bgneal@45 4545
bgneal@45 4546 // Remove blocker on remove
bgneal@45 4547 if (k == 'remove') {
bgneal@45 4548 dom.remove(t.blocker);
bgneal@45 4549 return;
bgneal@45 4550 }
bgneal@45 4551
bgneal@45 4552 if (!t.blocker) {
bgneal@45 4553 t.blocker = dom.uniqueId();
bgneal@45 4554 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
bgneal@45 4555 dom.setStyle(b, 'opacity', 0);
bgneal@45 4556 } else
bgneal@45 4557 b = dom.get(t.blocker);
bgneal@45 4558
bgneal@45 4559 dom.setStyle(b, 'left', t.getStyle('left', 1));
bgneal@45 4560 dom.setStyle(b, 'top', t.getStyle('top', 1));
bgneal@45 4561 dom.setStyle(b, 'width', t.getStyle('width', 1));
bgneal@45 4562 dom.setStyle(b, 'height', t.getStyle('height', 1));
bgneal@45 4563 dom.setStyle(b, 'display', t.getStyle('display', 1));
bgneal@45 4564 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);
bgneal@45 4565 }
bgneal@45 4566 }
bgneal@45 4567
bgneal@45 4568 });
bgneal@45 4569 })(tinymce);
bgneal@45 4570 (function(tinymce) {
bgneal@45 4571 function trimNl(s) {
bgneal@45 4572 return s.replace(/[\n\r]+/g, '');
bgneal@45 4573 };
bgneal@45 4574
bgneal@45 4575 // Shorten names
bgneal@45 4576 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
bgneal@45 4577
bgneal@45 4578 tinymce.create('tinymce.dom.Selection', {
bgneal@45 4579 Selection : function(dom, win, serializer) {
bgneal@45 4580 var t = this;
bgneal@45 4581
bgneal@45 4582 t.dom = dom;
bgneal@45 4583 t.win = win;
bgneal@45 4584 t.serializer = serializer;
bgneal@45 4585
bgneal@45 4586 // Add events
bgneal@45 4587 each([
bgneal@45 4588 'onBeforeSetContent',
bgneal@45 4589 'onBeforeGetContent',
bgneal@45 4590 'onSetContent',
bgneal@45 4591 'onGetContent'
bgneal@45 4592 ], function(e) {
bgneal@45 4593 t[e] = new tinymce.util.Dispatcher(t);
bgneal@45 4594 });
bgneal@45 4595
bgneal@45 4596 // No W3C Range support
bgneal@45 4597 if (!t.win.getSelection)
bgneal@45 4598 t.tridentSel = new tinymce.dom.TridentSelection(t);
bgneal@45 4599
bgneal@45 4600 // Prevent leaks
bgneal@45 4601 tinymce.addUnload(t.destroy, t);
bgneal@45 4602 },
bgneal@45 4603
bgneal@45 4604 getContent : function(s) {
bgneal@45 4605 var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;
bgneal@45 4606
bgneal@45 4607 s = s || {};
bgneal@45 4608 wb = wa = '';
bgneal@45 4609 s.get = true;
bgneal@45 4610 s.format = s.format || 'html';
bgneal@45 4611 t.onBeforeGetContent.dispatch(t, s);
bgneal@45 4612
bgneal@45 4613 if (s.format == 'text')
bgneal@45 4614 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));
bgneal@45 4615
bgneal@45 4616 if (r.cloneContents) {
bgneal@45 4617 n = r.cloneContents();
bgneal@45 4618
bgneal@45 4619 if (n)
bgneal@45 4620 e.appendChild(n);
bgneal@45 4621 } else if (is(r.item) || is(r.htmlText))
bgneal@45 4622 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
bgneal@45 4623 else
bgneal@45 4624 e.innerHTML = r.toString();
bgneal@45 4625
bgneal@45 4626 // Keep whitespace before and after
bgneal@45 4627 if (/^\s/.test(e.innerHTML))
bgneal@45 4628 wb = ' ';
bgneal@45 4629
bgneal@45 4630 if (/\s+$/.test(e.innerHTML))
bgneal@45 4631 wa = ' ';
bgneal@45 4632
bgneal@45 4633 s.getInner = true;
bgneal@45 4634
bgneal@45 4635 s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
bgneal@45 4636 t.onGetContent.dispatch(t, s);
bgneal@45 4637
bgneal@45 4638 return s.content;
bgneal@45 4639 },
bgneal@45 4640
bgneal@45 4641 setContent : function(h, s) {
bgneal@45 4642 var t = this, r = t.getRng(), c, d = t.win.document;
bgneal@45 4643
bgneal@45 4644 s = s || {format : 'html'};
bgneal@45 4645 s.set = true;
bgneal@45 4646 h = s.content = t.dom.processHTML(h);
bgneal@45 4647
bgneal@45 4648 // Dispatch before set content event
bgneal@45 4649 t.onBeforeSetContent.dispatch(t, s);
bgneal@45 4650 h = s.content;
bgneal@45 4651
bgneal@45 4652 if (r.insertNode) {
bgneal@45 4653 // Make caret marker since insertNode places the caret in the beginning of text after insert
bgneal@45 4654 h += '<span id="__caret">_</span>';
bgneal@45 4655
bgneal@45 4656 // Delete and insert new node
bgneal@45 4657 r.deleteContents();
bgneal@45 4658 r.insertNode(t.getRng().createContextualFragment(h));
bgneal@45 4659
bgneal@45 4660 // Move to caret marker
bgneal@45 4661 c = t.dom.get('__caret');
bgneal@45 4662
bgneal@45 4663 // Make sure we wrap it compleatly, Opera fails with a simple select call
bgneal@45 4664 r = d.createRange();
bgneal@45 4665 r.setStartBefore(c);
bgneal@45 4666 r.setEndAfter(c);
bgneal@45 4667 t.setRng(r);
bgneal@45 4668
bgneal@45 4669 // Delete the marker, and hopefully the caret gets placed in the right location
bgneal@45 4670 // Removed this since it seems to remove &nbsp; in FF and simply deleting it
bgneal@45 4671 // doesn't seem to affect the caret position in any browser
bgneal@45 4672 //d.execCommand('Delete', false, null);
bgneal@45 4673
bgneal@45 4674 // Remove the caret position
bgneal@45 4675 t.dom.remove('__caret');
bgneal@45 4676 } else {
bgneal@45 4677 if (r.item) {
bgneal@45 4678 // Delete content and get caret text selection
bgneal@45 4679 d.execCommand('Delete', false, null);
bgneal@45 4680 r = t.getRng();
bgneal@45 4681 }
bgneal@45 4682
bgneal@45 4683 r.pasteHTML(h);
bgneal@45 4684 }
bgneal@45 4685
bgneal@45 4686 // Dispatch set content event
bgneal@45 4687 t.onSetContent.dispatch(t, s);
bgneal@45 4688 },
bgneal@45 4689
bgneal@45 4690 getStart : function() {
bgneal@45 4691 var t = this, r = t.getRng(), e;
bgneal@45 4692
bgneal@45 4693 if (isIE) {
bgneal@45 4694 if (r.item)
bgneal@45 4695 return r.item(0);
bgneal@45 4696
bgneal@45 4697 r = r.duplicate();
bgneal@45 4698 r.collapse(1);
bgneal@45 4699 e = r.parentElement();
bgneal@45 4700
bgneal@45 4701 if (e && e.nodeName == 'BODY')
bgneal@45 4702 return e.firstChild;
bgneal@45 4703
bgneal@45 4704 return e;
bgneal@45 4705 } else {
bgneal@45 4706 e = r.startContainer;
bgneal@45 4707
bgneal@45 4708 if (e.nodeName == 'BODY')
bgneal@45 4709 return e.firstChild;
bgneal@45 4710
bgneal@45 4711 return t.dom.getParent(e, '*');
bgneal@45 4712 }
bgneal@45 4713 },
bgneal@45 4714
bgneal@45 4715 getEnd : function() {
bgneal@45 4716 var t = this, r = t.getRng(), e;
bgneal@45 4717
bgneal@45 4718 if (isIE) {
bgneal@45 4719 if (r.item)
bgneal@45 4720 return r.item(0);
bgneal@45 4721
bgneal@45 4722 r = r.duplicate();
bgneal@45 4723 r.collapse(0);
bgneal@45 4724 e = r.parentElement();
bgneal@45 4725
bgneal@45 4726 if (e && e.nodeName == 'BODY')
bgneal@45 4727 return e.lastChild;
bgneal@45 4728
bgneal@45 4729 return e;
bgneal@45 4730 } else {
bgneal@45 4731 e = r.endContainer;
bgneal@45 4732
bgneal@45 4733 if (e.nodeName == 'BODY')
bgneal@45 4734 return e.lastChild;
bgneal@45 4735
bgneal@45 4736 return t.dom.getParent(e, '*');
bgneal@45 4737 }
bgneal@45 4738 },
bgneal@45 4739
bgneal@45 4740 getBookmark : function(si) {
bgneal@45 4741 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;
bgneal@45 4742 sx = vp.x;
bgneal@45 4743 sy = vp.y;
bgneal@45 4744
bgneal@45 4745 // Simple bookmark fast but not as persistent
bgneal@45 4746 if (si == 'simple')
bgneal@45 4747 return {rng : r, scrollX : sx, scrollY : sy};
bgneal@45 4748
bgneal@45 4749 // Handle IE
bgneal@45 4750 if (isIE) {
bgneal@45 4751 // Control selection
bgneal@45 4752 if (r.item) {
bgneal@45 4753 e = r.item(0);
bgneal@45 4754
bgneal@45 4755 each(t.dom.select(e.nodeName), function(n, i) {
bgneal@45 4756 if (e == n) {
bgneal@45 4757 sp = i;
bgneal@45 4758 return false;
bgneal@45 4759 }
bgneal@45 4760 });
bgneal@45 4761
bgneal@45 4762 return {
bgneal@45 4763 tag : e.nodeName,
bgneal@45 4764 index : sp,
bgneal@45 4765 scrollX : sx,
bgneal@45 4766 scrollY : sy
bgneal@45 4767 };
bgneal@45 4768 }
bgneal@45 4769
bgneal@45 4770 // Text selection
bgneal@45 4771 tr = t.dom.doc.body.createTextRange();
bgneal@45 4772 tr.moveToElementText(ro);
bgneal@45 4773 tr.collapse(true);
bgneal@45 4774 bp = Math.abs(tr.move('character', c));
bgneal@45 4775
bgneal@45 4776 tr = r.duplicate();
bgneal@45 4777 tr.collapse(true);
bgneal@45 4778 sp = Math.abs(tr.move('character', c));
bgneal@45 4779
bgneal@45 4780 tr = r.duplicate();
bgneal@45 4781 tr.collapse(false);
bgneal@45 4782 le = Math.abs(tr.move('character', c)) - sp;
bgneal@45 4783
bgneal@45 4784 return {
bgneal@45 4785 start : sp - bp,
bgneal@45 4786 length : le,
bgneal@45 4787 scrollX : sx,
bgneal@45 4788 scrollY : sy
bgneal@45 4789 };
bgneal@45 4790 }
bgneal@45 4791
bgneal@45 4792 // Handle W3C
bgneal@45 4793 e = t.getNode();
bgneal@45 4794 s = t.getSel();
bgneal@45 4795
bgneal@45 4796 if (!s)
bgneal@45 4797 return null;
bgneal@45 4798
bgneal@45 4799 // Image selection
bgneal@45 4800 if (e && e.nodeName == 'IMG') {
bgneal@45 4801 return {
bgneal@45 4802 scrollX : sx,
bgneal@45 4803 scrollY : sy
bgneal@45 4804 };
bgneal@45 4805 }
bgneal@45 4806
bgneal@45 4807 // Text selection
bgneal@45 4808
bgneal@45 4809 function getPos(r, sn, en) {
bgneal@45 4810 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
bgneal@45 4811
bgneal@45 4812 while ((n = w.nextNode()) != null) {
bgneal@45 4813 if (n == sn)
bgneal@45 4814 d.start = p;
bgneal@45 4815
bgneal@45 4816 if (n == en) {
bgneal@45 4817 d.end = p;
bgneal@45 4818 return d;
bgneal@45 4819 }
bgneal@45 4820
bgneal@45 4821 p += trimNl(n.nodeValue || '').length;
bgneal@45 4822 }
bgneal@45 4823
bgneal@45 4824 return null;
bgneal@45 4825 };
bgneal@45 4826
bgneal@45 4827 // Caret or selection
bgneal@45 4828 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
bgneal@45 4829 e = getPos(ro, s.anchorNode, s.focusNode);
bgneal@45 4830
bgneal@45 4831 if (!e)
bgneal@45 4832 return {scrollX : sx, scrollY : sy};
bgneal@45 4833
bgneal@45 4834 // Count whitespace before
bgneal@45 4835 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
bgneal@45 4836
bgneal@45 4837 return {
bgneal@45 4838 start : Math.max(e.start + s.anchorOffset - wb, 0),
bgneal@45 4839 end : Math.max(e.end + s.focusOffset - wb, 0),
bgneal@45 4840 scrollX : sx,
bgneal@45 4841 scrollY : sy,
bgneal@45 4842 beg : s.anchorOffset - wb == 0
bgneal@45 4843 };
bgneal@45 4844 } else {
bgneal@45 4845 e = getPos(ro, r.startContainer, r.endContainer);
bgneal@45 4846
bgneal@45 4847 // Count whitespace before start and end container
bgneal@45 4848 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
bgneal@45 4849 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});
bgneal@45 4850
bgneal@45 4851 if (!e)
bgneal@45 4852 return {scrollX : sx, scrollY : sy};
bgneal@45 4853
bgneal@45 4854 return {
bgneal@45 4855 start : Math.max(e.start + r.startOffset - wb, 0),
bgneal@45 4856 end : Math.max(e.end + r.endOffset - wa, 0),
bgneal@45 4857 scrollX : sx,
bgneal@45 4858 scrollY : sy,
bgneal@45 4859 beg : r.startOffset - wb == 0
bgneal@45 4860 };
bgneal@45 4861 }
bgneal@45 4862 },
bgneal@45 4863
bgneal@45 4864 moveToBookmark : function(b) {
bgneal@45 4865 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;
bgneal@45 4866
bgneal@45 4867 function getPos(r, sp, ep) {
bgneal@45 4868 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;
bgneal@45 4869
bgneal@45 4870 while ((n = w.nextNode()) != null) {
bgneal@45 4871 wa = wb = 0;
bgneal@45 4872
bgneal@45 4873 nv = n.nodeValue || '';
bgneal@45 4874 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
bgneal@45 4875 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});
bgneal@45 4876
bgneal@45 4877 nvl = trimNl(nv).length;
bgneal@45 4878 p += nvl;
bgneal@45 4879
bgneal@45 4880 if (p >= sp && !d.startNode) {
bgneal@45 4881 o = sp - (p - nvl);
bgneal@45 4882
bgneal@45 4883 // Fix for odd quirk in FF
bgneal@45 4884 if (b.beg && o >= nvl)
bgneal@45 4885 continue;
bgneal@45 4886
bgneal@45 4887 d.startNode = n;
bgneal@45 4888 d.startOffset = o + wb;
bgneal@45 4889 }
bgneal@45 4890
bgneal@45 4891 if (p >= ep) {
bgneal@45 4892 d.endNode = n;
bgneal@45 4893 d.endOffset = ep - (p - nvl) + wb;
bgneal@45 4894 return d;
bgneal@45 4895 }
bgneal@45 4896 }
bgneal@45 4897
bgneal@45 4898 return null;
bgneal@45 4899 };
bgneal@45 4900
bgneal@45 4901 if (!b)
bgneal@45 4902 return false;
bgneal@45 4903
bgneal@45 4904 t.win.scrollTo(b.scrollX, b.scrollY);
bgneal@45 4905
bgneal@45 4906 // Handle explorer
bgneal@45 4907 if (isIE) {
bgneal@45 4908 // Handle simple
bgneal@45 4909 if (r = b.rng) {
bgneal@45 4910 try {
bgneal@45 4911 r.select();
bgneal@45 4912 } catch (ex) {
bgneal@45 4913 // Ignore
bgneal@45 4914 }
bgneal@45 4915
bgneal@45 4916 return true;
bgneal@45 4917 }
bgneal@45 4918
bgneal@45 4919 t.win.focus();
bgneal@45 4920
bgneal@45 4921 // Handle control bookmark
bgneal@45 4922 if (b.tag) {
bgneal@45 4923 r = ro.createControlRange();
bgneal@45 4924
bgneal@45 4925 each(t.dom.select(b.tag), function(n, i) {
bgneal@45 4926 if (i == b.index)
bgneal@45 4927 r.addElement(n);
bgneal@45 4928 });
bgneal@45 4929 } else {
bgneal@45 4930 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
bgneal@45 4931 try {
bgneal@45 4932 // Incorrect bookmark
bgneal@45 4933 if (b.start < 0)
bgneal@45 4934 return true;
bgneal@45 4935
bgneal@45 4936 r = s.createRange();
bgneal@45 4937 r.moveToElementText(ro);
bgneal@45 4938 r.collapse(true);
bgneal@45 4939 r.moveStart('character', b.start);
bgneal@45 4940 r.moveEnd('character', b.length);
bgneal@45 4941 } catch (ex2) {
bgneal@45 4942 return true;
bgneal@45 4943 }
bgneal@45 4944 }
bgneal@45 4945
bgneal@45 4946 try {
bgneal@45 4947 r.select();
bgneal@45 4948 } catch (ex) {
bgneal@45 4949 // Needed for some odd IE bug #1843306
bgneal@45 4950 }
bgneal@45 4951
bgneal@45 4952 return true;
bgneal@45 4953 }
bgneal@45 4954
bgneal@45 4955 // Handle W3C
bgneal@45 4956 if (!s)
bgneal@45 4957 return false;
bgneal@45 4958
bgneal@45 4959 // Handle simple
bgneal@45 4960 if (b.rng) {
bgneal@45 4961 s.removeAllRanges();
bgneal@45 4962 s.addRange(b.rng);
bgneal@45 4963 } else {
bgneal@45 4964 if (is(b.start) && is(b.end)) {
bgneal@45 4965 try {
bgneal@45 4966 sd = getPos(ro, b.start, b.end);
bgneal@45 4967
bgneal@45 4968 if (sd) {
bgneal@45 4969 r = t.dom.doc.createRange();
bgneal@45 4970 r.setStart(sd.startNode, sd.startOffset);
bgneal@45 4971 r.setEnd(sd.endNode, sd.endOffset);
bgneal@45 4972 s.removeAllRanges();
bgneal@45 4973 s.addRange(r);
bgneal@45 4974 }
bgneal@45 4975
bgneal@45 4976 if (!tinymce.isOpera)
bgneal@45 4977 t.win.focus();
bgneal@45 4978 } catch (ex) {
bgneal@45 4979 // Ignore
bgneal@45 4980 }
bgneal@45 4981 }
bgneal@45 4982 }
bgneal@45 4983 },
bgneal@45 4984
bgneal@45 4985 select : function(n, c) {
bgneal@45 4986 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;
bgneal@45 4987
bgneal@45 4988 function find(n, start) {
bgneal@45 4989 var walker, o;
bgneal@45 4990
bgneal@45 4991 if (n) {
bgneal@45 4992 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
bgneal@45 4993
bgneal@45 4994 // Find first/last non empty text node
bgneal@45 4995 while (n = walker.nextNode()) {
bgneal@45 4996 o = n;
bgneal@45 4997
bgneal@45 4998 if (tinymce.trim(n.nodeValue).length != 0) {
bgneal@45 4999 if (start)
bgneal@45 5000 return n;
bgneal@45 5001 else
bgneal@45 5002 o = n;
bgneal@45 5003 }
bgneal@45 5004 }
bgneal@45 5005 }
bgneal@45 5006
bgneal@45 5007 return o;
bgneal@45 5008 };
bgneal@45 5009
bgneal@45 5010 if (isIE) {
bgneal@45 5011 try {
bgneal@45 5012 b = d.body;
bgneal@45 5013
bgneal@45 5014 if (/^(IMG|TABLE)$/.test(n.nodeName)) {
bgneal@45 5015 r = b.createControlRange();
bgneal@45 5016 r.addElement(n);
bgneal@45 5017 } else {
bgneal@45 5018 r = b.createTextRange();
bgneal@45 5019 r.moveToElementText(n);
bgneal@45 5020 }
bgneal@45 5021
bgneal@45 5022 r.select();
bgneal@45 5023 } catch (ex) {
bgneal@45 5024 // Throws illigal agrument in IE some times
bgneal@45 5025 }
bgneal@45 5026 } else {
bgneal@45 5027 if (c) {
bgneal@45 5028 fn = find(n, 1) || t.dom.select('br:first', n)[0];
bgneal@45 5029 ln = find(n, 0) || t.dom.select('br:last', n)[0];
bgneal@45 5030
bgneal@45 5031 if (fn && ln) {
bgneal@45 5032 r = d.createRange();
bgneal@45 5033
bgneal@45 5034 if (fn.nodeName == 'BR')
bgneal@45 5035 r.setStartBefore(fn);
bgneal@45 5036 else
bgneal@45 5037 r.setStart(fn, 0);
bgneal@45 5038
bgneal@45 5039 if (ln.nodeName == 'BR')
bgneal@45 5040 r.setEndBefore(ln);
bgneal@45 5041 else
bgneal@45 5042 r.setEnd(ln, ln.nodeValue.length);
bgneal@45 5043 } else
bgneal@45 5044 r.selectNode(n);
bgneal@45 5045 } else
bgneal@45 5046 r.selectNode(n);
bgneal@45 5047
bgneal@45 5048 t.setRng(r);
bgneal@45 5049 }
bgneal@45 5050
bgneal@45 5051 return n;
bgneal@45 5052 },
bgneal@45 5053
bgneal@45 5054 isCollapsed : function() {
bgneal@45 5055 var t = this, r = t.getRng(), s = t.getSel();
bgneal@45 5056
bgneal@45 5057 if (!r || r.item)
bgneal@45 5058 return false;
bgneal@45 5059
bgneal@45 5060 return !s || r.boundingWidth == 0 || r.collapsed;
bgneal@45 5061 },
bgneal@45 5062
bgneal@45 5063 collapse : function(b) {
bgneal@45 5064 var t = this, r = t.getRng(), n;
bgneal@45 5065
bgneal@45 5066 // Control range on IE
bgneal@45 5067 if (r.item) {
bgneal@45 5068 n = r.item(0);
bgneal@45 5069 r = this.win.document.body.createTextRange();
bgneal@45 5070 r.moveToElementText(n);
bgneal@45 5071 }
bgneal@45 5072
bgneal@45 5073 r.collapse(!!b);
bgneal@45 5074 t.setRng(r);
bgneal@45 5075 },
bgneal@45 5076
bgneal@45 5077 getSel : function() {
bgneal@45 5078 var t = this, w = this.win;
bgneal@45 5079
bgneal@45 5080 return w.getSelection ? w.getSelection() : w.document.selection;
bgneal@45 5081 },
bgneal@45 5082
bgneal@45 5083 getRng : function(w3c) {
bgneal@45 5084 var t = this, s, r;
bgneal@45 5085
bgneal@45 5086 // Found tridentSel object then we need to use that one
bgneal@45 5087 if (w3c && t.tridentSel)
bgneal@45 5088 return t.tridentSel.getRangeAt(0);
bgneal@45 5089
bgneal@45 5090 try {
bgneal@45 5091 if (s = t.getSel())
bgneal@45 5092 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
bgneal@45 5093 } catch (ex) {
bgneal@45 5094 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe
bgneal@45 5095 }
bgneal@45 5096
bgneal@45 5097 // No range found then create an empty one
bgneal@45 5098 // This can occur when the editor is placed in a hidden container element on Gecko
bgneal@45 5099 // Or on IE when there was an exception
bgneal@45 5100 if (!r)
bgneal@45 5101 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();
bgneal@45 5102
bgneal@45 5103 return r;
bgneal@45 5104 },
bgneal@45 5105
bgneal@45 5106 setRng : function(r) {
bgneal@45 5107 var s, t = this;
bgneal@45 5108
bgneal@45 5109 if (!t.tridentSel) {
bgneal@45 5110 s = t.getSel();
bgneal@45 5111
bgneal@45 5112 if (s) {
bgneal@45 5113 s.removeAllRanges();
bgneal@45 5114 s.addRange(r);
bgneal@45 5115 }
bgneal@45 5116 } else {
bgneal@45 5117 // Is W3C Range
bgneal@45 5118 if (r.cloneRange) {
bgneal@45 5119 t.tridentSel.addRange(r);
bgneal@45 5120 return;
bgneal@45 5121 }
bgneal@45 5122
bgneal@45 5123 // Is IE specific range
bgneal@45 5124 try {
bgneal@45 5125 r.select();
bgneal@45 5126 } catch (ex) {
bgneal@45 5127 // Needed for some odd IE bug #1843306
bgneal@45 5128 }
bgneal@45 5129 }
bgneal@45 5130 },
bgneal@45 5131
bgneal@45 5132 setNode : function(n) {
bgneal@45 5133 var t = this;
bgneal@45 5134
bgneal@45 5135 t.setContent(t.dom.getOuterHTML(n));
bgneal@45 5136
bgneal@45 5137 return n;
bgneal@45 5138 },
bgneal@45 5139
bgneal@45 5140 getNode : function() {
bgneal@45 5141 var t = this, r = t.getRng(), s = t.getSel(), e;
bgneal@45 5142
bgneal@45 5143 if (!isIE) {
bgneal@45 5144 // Range maybe lost after the editor is made visible again
bgneal@45 5145 if (!r)
bgneal@45 5146 return t.dom.getRoot();
bgneal@45 5147
bgneal@45 5148 e = r.commonAncestorContainer;
bgneal@45 5149
bgneal@45 5150 // Handle selection a image or other control like element such as anchors
bgneal@45 5151 if (!r.collapsed) {
bgneal@45 5152 // If the anchor node is a element instead of a text node then return this element
bgneal@45 5153 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1)
bgneal@45 5154 return s.anchorNode.childNodes[s.anchorOffset];
bgneal@45 5155
bgneal@45 5156 if (r.startContainer == r.endContainer) {
bgneal@45 5157 if (r.startOffset - r.endOffset < 2) {
bgneal@45 5158 if (r.startContainer.hasChildNodes())
bgneal@45 5159 e = r.startContainer.childNodes[r.startOffset];
bgneal@45 5160 }
bgneal@45 5161 }
bgneal@45 5162 }
bgneal@45 5163
bgneal@45 5164 return t.dom.getParent(e, '*');
bgneal@45 5165 }
bgneal@45 5166
bgneal@45 5167 return r.item ? r.item(0) : r.parentElement();
bgneal@45 5168 },
bgneal@45 5169
bgneal@45 5170 getSelectedBlocks : function(st, en) {
bgneal@45 5171 var t = this, dom = t.dom, sb, eb, n, bl = [];
bgneal@45 5172
bgneal@45 5173 sb = dom.getParent(st || t.getStart(), dom.isBlock);
bgneal@45 5174 eb = dom.getParent(en || t.getEnd(), dom.isBlock);
bgneal@45 5175
bgneal@45 5176 if (sb)
bgneal@45 5177 bl.push(sb);
bgneal@45 5178
bgneal@45 5179 if (sb && eb && sb != eb) {
bgneal@45 5180 n = sb;
bgneal@45 5181
bgneal@45 5182 while ((n = n.nextSibling) && n != eb) {
bgneal@45 5183 if (dom.isBlock(n))
bgneal@45 5184 bl.push(n);
bgneal@45 5185 }
bgneal@45 5186 }
bgneal@45 5187
bgneal@45 5188 if (eb && sb != eb)
bgneal@45 5189 bl.push(eb);
bgneal@45 5190
bgneal@45 5191 return bl;
bgneal@45 5192 },
bgneal@45 5193
bgneal@45 5194 destroy : function(s) {
bgneal@45 5195 var t = this;
bgneal@45 5196
bgneal@45 5197 t.win = null;
bgneal@45 5198
bgneal@45 5199 // Manual destroy then remove unload handler
bgneal@45 5200 if (!s)
bgneal@45 5201 tinymce.removeUnload(t.destroy);
bgneal@45 5202 }
bgneal@45 5203
bgneal@45 5204 });
bgneal@45 5205 })(tinymce);
bgneal@45 5206 (function(tinymce) {
bgneal@45 5207 tinymce.create('tinymce.dom.XMLWriter', {
bgneal@45 5208 node : null,
bgneal@45 5209
bgneal@45 5210 XMLWriter : function(s) {
bgneal@45 5211 // Get XML document
bgneal@45 5212 function getXML() {
bgneal@45 5213 var i = document.implementation;
bgneal@45 5214
bgneal@45 5215 if (!i || !i.createDocument) {
bgneal@45 5216 // Try IE objects
bgneal@45 5217 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
bgneal@45 5218 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
bgneal@45 5219 } else
bgneal@45 5220 return i.createDocument('', '', null);
bgneal@45 5221 };
bgneal@45 5222
bgneal@45 5223 this.doc = getXML();
bgneal@45 5224
bgneal@45 5225 // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
bgneal@45 5226 this.valid = tinymce.isOpera || tinymce.isWebKit;
bgneal@45 5227
bgneal@45 5228 this.reset();
bgneal@45 5229 },
bgneal@45 5230
bgneal@45 5231 reset : function() {
bgneal@45 5232 var t = this, d = t.doc;
bgneal@45 5233
bgneal@45 5234 if (d.firstChild)
bgneal@45 5235 d.removeChild(d.firstChild);
bgneal@45 5236
bgneal@45 5237 t.node = d.appendChild(d.createElement("html"));
bgneal@45 5238 },
bgneal@45 5239
bgneal@45 5240 writeStartElement : function(n) {
bgneal@45 5241 var t = this;
bgneal@45 5242
bgneal@45 5243 t.node = t.node.appendChild(t.doc.createElement(n));
bgneal@45 5244 },
bgneal@45 5245
bgneal@45 5246 writeAttribute : function(n, v) {
bgneal@45 5247 if (this.valid)
bgneal@45 5248 v = v.replace(/>/g, '%MCGT%');
bgneal@45 5249
bgneal@45 5250 this.node.setAttribute(n, v);
bgneal@45 5251 },
bgneal@45 5252
bgneal@45 5253 writeEndElement : function() {
bgneal@45 5254 this.node = this.node.parentNode;
bgneal@45 5255 },
bgneal@45 5256
bgneal@45 5257 writeFullEndElement : function() {
bgneal@45 5258 var t = this, n = t.node;
bgneal@45 5259
bgneal@45 5260 n.appendChild(t.doc.createTextNode(""));
bgneal@45 5261 t.node = n.parentNode;
bgneal@45 5262 },
bgneal@45 5263
bgneal@45 5264 writeText : function(v) {
bgneal@45 5265 if (this.valid)
bgneal@45 5266 v = v.replace(/>/g, '%MCGT%');
bgneal@45 5267
bgneal@45 5268 this.node.appendChild(this.doc.createTextNode(v));
bgneal@45 5269 },
bgneal@45 5270
bgneal@45 5271 writeCDATA : function(v) {
bgneal@45 5272 this.node.appendChild(this.doc.createCDATA(v));
bgneal@45 5273 },
bgneal@45 5274
bgneal@45 5275 writeComment : function(v) {
bgneal@45 5276 // Fix for bug #2035694
bgneal@45 5277 if (tinymce.isIE)
bgneal@45 5278 v = v.replace(/^\-|\-$/g, ' ');
bgneal@45 5279
bgneal@45 5280 this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));
bgneal@45 5281 },
bgneal@45 5282
bgneal@45 5283 getContent : function() {
bgneal@45 5284 var h;
bgneal@45 5285
bgneal@45 5286 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
bgneal@45 5287 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
bgneal@45 5288 h = h.replace(/ ?\/>/g, ' />');
bgneal@45 5289
bgneal@45 5290 if (this.valid)
bgneal@45 5291 h = h.replace(/\%MCGT%/g, '&gt;');
bgneal@45 5292
bgneal@45 5293 return h;
bgneal@45 5294 }
bgneal@45 5295
bgneal@45 5296 });
bgneal@45 5297 })(tinymce);
bgneal@45 5298 (function(tinymce) {
bgneal@45 5299 tinymce.create('tinymce.dom.StringWriter', {
bgneal@45 5300 str : null,
bgneal@45 5301 tags : null,
bgneal@45 5302 count : 0,
bgneal@45 5303 settings : null,
bgneal@45 5304 indent : null,
bgneal@45 5305
bgneal@45 5306 StringWriter : function(s) {
bgneal@45 5307 this.settings = tinymce.extend({
bgneal@45 5308 indent_char : ' ',
bgneal@45 5309 indentation : 1
bgneal@45 5310 }, s);
bgneal@45 5311
bgneal@45 5312 this.reset();
bgneal@45 5313 },
bgneal@45 5314
bgneal@45 5315 reset : function() {
bgneal@45 5316 this.indent = '';
bgneal@45 5317 this.str = "";
bgneal@45 5318 this.tags = [];
bgneal@45 5319 this.count = 0;
bgneal@45 5320 },
bgneal@45 5321
bgneal@45 5322 writeStartElement : function(n) {
bgneal@45 5323 this._writeAttributesEnd();
bgneal@45 5324 this.writeRaw('<' + n);
bgneal@45 5325 this.tags.push(n);
bgneal@45 5326 this.inAttr = true;
bgneal@45 5327 this.count++;
bgneal@45 5328 this.elementCount = this.count;
bgneal@45 5329 },
bgneal@45 5330
bgneal@45 5331 writeAttribute : function(n, v) {
bgneal@45 5332 var t = this;
bgneal@45 5333
bgneal@45 5334 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
bgneal@45 5335 },
bgneal@45 5336
bgneal@45 5337 writeEndElement : function() {
bgneal@45 5338 var n;
bgneal@45 5339
bgneal@45 5340 if (this.tags.length > 0) {
bgneal@45 5341 n = this.tags.pop();
bgneal@45 5342
bgneal@45 5343 if (this._writeAttributesEnd(1))
bgneal@45 5344 this.writeRaw('</' + n + '>');
bgneal@45 5345
bgneal@45 5346 if (this.settings.indentation > 0)
bgneal@45 5347 this.writeRaw('\n');
bgneal@45 5348 }
bgneal@45 5349 },
bgneal@45 5350
bgneal@45 5351 writeFullEndElement : function() {
bgneal@45 5352 if (this.tags.length > 0) {
bgneal@45 5353 this._writeAttributesEnd();
bgneal@45 5354 this.writeRaw('</' + this.tags.pop() + '>');
bgneal@45 5355
bgneal@45 5356 if (this.settings.indentation > 0)
bgneal@45 5357 this.writeRaw('\n');
bgneal@45 5358 }
bgneal@45 5359 },
bgneal@45 5360
bgneal@45 5361 writeText : function(v) {
bgneal@45 5362 this._writeAttributesEnd();
bgneal@45 5363 this.writeRaw(this.encode(v));
bgneal@45 5364 this.count++;
bgneal@45 5365 },
bgneal@45 5366
bgneal@45 5367 writeCDATA : function(v) {
bgneal@45 5368 this._writeAttributesEnd();
bgneal@45 5369 this.writeRaw('<![CDATA[' + v + ']]>');
bgneal@45 5370 this.count++;
bgneal@45 5371 },
bgneal@45 5372
bgneal@45 5373 writeComment : function(v) {
bgneal@45 5374 this._writeAttributesEnd();
bgneal@45 5375 this.writeRaw('<!-- ' + v + '-->');
bgneal@45 5376 this.count++;
bgneal@45 5377 },
bgneal@45 5378
bgneal@45 5379 writeRaw : function(v) {
bgneal@45 5380 this.str += v;
bgneal@45 5381 },
bgneal@45 5382
bgneal@45 5383 encode : function(s) {
bgneal@45 5384 return s.replace(/[<>&"]/g, function(v) {
bgneal@45 5385 switch (v) {
bgneal@45 5386 case '<':
bgneal@45 5387 return '&lt;';
bgneal@45 5388
bgneal@45 5389 case '>':
bgneal@45 5390 return '&gt;';
bgneal@45 5391
bgneal@45 5392 case '&':
bgneal@45 5393 return '&amp;';
bgneal@45 5394
bgneal@45 5395 case '"':
bgneal@45 5396 return '&quot;';
bgneal@45 5397 }
bgneal@45 5398
bgneal@45 5399 return v;
bgneal@45 5400 });
bgneal@45 5401 },
bgneal@45 5402
bgneal@45 5403 getContent : function() {
bgneal@45 5404 return this.str;
bgneal@45 5405 },
bgneal@45 5406
bgneal@45 5407 _writeAttributesEnd : function(s) {
bgneal@45 5408 if (!this.inAttr)
bgneal@45 5409 return;
bgneal@45 5410
bgneal@45 5411 this.inAttr = false;
bgneal@45 5412
bgneal@45 5413 if (s && this.elementCount == this.count) {
bgneal@45 5414 this.writeRaw(' />');
bgneal@45 5415 return false;
bgneal@45 5416 }
bgneal@45 5417
bgneal@45 5418 this.writeRaw('>');
bgneal@45 5419
bgneal@45 5420 return true;
bgneal@45 5421 }
bgneal@45 5422
bgneal@45 5423 });
bgneal@45 5424 })(tinymce);
bgneal@45 5425 (function(tinymce) {
bgneal@45 5426 // Shorten names
bgneal@45 5427 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;
bgneal@45 5428
bgneal@45 5429 function wildcardToRE(s) {
bgneal@45 5430 return s.replace(/([?+*])/g, '.$1');
bgneal@45 5431 };
bgneal@45 5432
bgneal@45 5433 tinymce.create('tinymce.dom.Serializer', {
bgneal@45 5434 Serializer : function(s) {
bgneal@45 5435 var t = this;
bgneal@45 5436
bgneal@45 5437 t.key = 0;
bgneal@45 5438 t.onPreProcess = new Dispatcher(t);
bgneal@45 5439 t.onPostProcess = new Dispatcher(t);
bgneal@45 5440
bgneal@45 5441 try {
bgneal@45 5442 t.writer = new tinymce.dom.XMLWriter();
bgneal@45 5443 } catch (ex) {
bgneal@45 5444 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
bgneal@45 5445 t.writer = new tinymce.dom.StringWriter();
bgneal@45 5446 }
bgneal@45 5447
bgneal@45 5448 // Default settings
bgneal@45 5449 t.settings = s = extend({
bgneal@45 5450 dom : tinymce.DOM,
bgneal@45 5451 valid_nodes : 0,
bgneal@45 5452 node_filter : 0,
bgneal@45 5453 attr_filter : 0,
bgneal@45 5454 invalid_attrs : /^(mce_|_moz_)/,
bgneal@45 5455 closed : /(br|hr|input|meta|img|link|param)/,
bgneal@45 5456 entity_encoding : 'named',
bgneal@45 5457 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',
bgneal@45 5458 bool_attrs : /(checked|disabled|readonly|selected|nowrap)/,
bgneal@45 5459 valid_elements : '*[*]',
bgneal@45 5460 extended_valid_elements : 0,
bgneal@45 5461 valid_child_elements : 0,
bgneal@45 5462 invalid_elements : 0,
bgneal@45 5463 fix_table_elements : 1,
bgneal@45 5464 fix_list_elements : true,
bgneal@45 5465 fix_content_duplication : true,
bgneal@45 5466 convert_fonts_to_spans : false,
bgneal@45 5467 font_size_classes : 0,
bgneal@45 5468 font_size_style_values : 0,
bgneal@45 5469 apply_source_formatting : 0,
bgneal@45 5470 indent_mode : 'simple',
bgneal@45 5471 indent_char : '\t',
bgneal@45 5472 indent_levels : 1,
bgneal@45 5473 remove_linebreaks : 1,
bgneal@45 5474 remove_redundant_brs : 1,
bgneal@45 5475 element_format : 'xhtml'
bgneal@45 5476 }, s);
bgneal@45 5477
bgneal@45 5478 t.dom = s.dom;
bgneal@45 5479
bgneal@45 5480 if (s.remove_redundant_brs) {
bgneal@45 5481 t.onPostProcess.add(function(se, o) {
bgneal@45 5482 // Remove BR elements at end of list elements since they get rendered in IE
bgneal@45 5483 o.content = o.content.replace(/<br \/>(\s*<\/li>)/g, '$1');
bgneal@45 5484 });
bgneal@45 5485 }
bgneal@45 5486
bgneal@45 5487 // Remove XHTML element endings i.e. produce crap :) XHTML is better
bgneal@45 5488 if (s.element_format == 'html') {
bgneal@45 5489 t.onPostProcess.add(function(se, o) {
bgneal@45 5490 o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');
bgneal@45 5491 });
bgneal@45 5492 }
bgneal@45 5493
bgneal@45 5494 if (s.fix_list_elements) {
bgneal@45 5495 t.onPreProcess.add(function(se, o) {
bgneal@45 5496 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;
bgneal@45 5497
bgneal@45 5498 function prevNode(e, n) {
bgneal@45 5499 var a = n.split(','), i;
bgneal@45 5500
bgneal@45 5501 while ((e = e.previousSibling) != null) {
bgneal@45 5502 for (i=0; i<a.length; i++) {
bgneal@45 5503 if (e.nodeName == a[i])
bgneal@45 5504 return e;
bgneal@45 5505 }
bgneal@45 5506 }
bgneal@45 5507
bgneal@45 5508 return null;
bgneal@45 5509 };
bgneal@45 5510
bgneal@45 5511 for (x=0; x<a.length; x++) {
bgneal@45 5512 nl = t.dom.select(a[x], o.node);
bgneal@45 5513
bgneal@45 5514 for (i=0; i<nl.length; i++) {
bgneal@45 5515 n = nl[i];
bgneal@45 5516 p = n.parentNode;
bgneal@45 5517
bgneal@45 5518 if (r.test(p.nodeName)) {
bgneal@45 5519 np = prevNode(n, 'LI');
bgneal@45 5520
bgneal@45 5521 if (!np) {
bgneal@45 5522 np = t.dom.create('li');
bgneal@45 5523 np.innerHTML = '&nbsp;';
bgneal@45 5524 np.appendChild(n);
bgneal@45 5525 p.insertBefore(np, p.firstChild);
bgneal@45 5526 } else
bgneal@45 5527 np.appendChild(n);
bgneal@45 5528 }
bgneal@45 5529 }
bgneal@45 5530 }
bgneal@45 5531 });
bgneal@45 5532 }
bgneal@45 5533
bgneal@45 5534 if (s.fix_table_elements) {
bgneal@45 5535 t.onPreProcess.add(function(se, o) {
bgneal@45 5536 each(t.dom.select('p table', o.node), function(n) {
bgneal@45 5537 t.dom.split(t.dom.getParent(n, 'p'), n);
bgneal@45 5538 });
bgneal@45 5539 });
bgneal@45 5540 }
bgneal@45 5541 },
bgneal@45 5542
bgneal@45 5543 setEntities : function(s) {
bgneal@45 5544 var t = this, a, i, l = {}, re = '', v;
bgneal@45 5545
bgneal@45 5546 // No need to setup more than once
bgneal@45 5547 if (t.entityLookup)
bgneal@45 5548 return;
bgneal@45 5549
bgneal@45 5550 // Build regex and lookup array
bgneal@45 5551 a = s.split(',');
bgneal@45 5552 for (i = 0; i < a.length; i += 2) {
bgneal@45 5553 v = a[i];
bgneal@45 5554
bgneal@45 5555 // Don't add default &amp; &quot; etc.
bgneal@45 5556 if (v == 34 || v == 38 || v == 60 || v == 62)
bgneal@45 5557 continue;
bgneal@45 5558
bgneal@45 5559 l[String.fromCharCode(a[i])] = a[i + 1];
bgneal@45 5560
bgneal@45 5561 v = parseInt(a[i]).toString(16);
bgneal@45 5562 re += '\\u' + '0000'.substring(v.length) + v;
bgneal@45 5563 }
bgneal@45 5564
bgneal@45 5565 if (!re) {
bgneal@45 5566 t.settings.entity_encoding = 'raw';
bgneal@45 5567 return;
bgneal@45 5568 }
bgneal@45 5569
bgneal@45 5570 t.entitiesRE = new RegExp('[' + re + ']', 'g');
bgneal@45 5571 t.entityLookup = l;
bgneal@45 5572 },
bgneal@45 5573
bgneal@45 5574 setValidChildRules : function(s) {
bgneal@45 5575 this.childRules = null;
bgneal@45 5576 this.addValidChildRules(s);
bgneal@45 5577 },
bgneal@45 5578
bgneal@45 5579 addValidChildRules : function(s) {
bgneal@45 5580 var t = this, inst, intr, bloc;
bgneal@45 5581
bgneal@45 5582 if (!s)
bgneal@45 5583 return;
bgneal@45 5584
bgneal@45 5585 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';
bgneal@45 5586 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';
bgneal@45 5587 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';
bgneal@45 5588
bgneal@45 5589 each(s.split(','), function(s) {
bgneal@45 5590 var p = s.split(/\[|\]/), re;
bgneal@45 5591
bgneal@45 5592 s = '';
bgneal@45 5593 each(p[1].split('|'), function(v) {
bgneal@45 5594 if (s)
bgneal@45 5595 s += '|';
bgneal@45 5596
bgneal@45 5597 switch (v) {
bgneal@45 5598 case '%itrans':
bgneal@45 5599 v = intr;
bgneal@45 5600 break;
bgneal@45 5601
bgneal@45 5602 case '%itrans_na':
bgneal@45 5603 v = intr.substring(2);
bgneal@45 5604 break;
bgneal@45 5605
bgneal@45 5606 case '%istrict':
bgneal@45 5607 v = inst;
bgneal@45 5608 break;
bgneal@45 5609
bgneal@45 5610 case '%istrict_na':
bgneal@45 5611 v = inst.substring(2);
bgneal@45 5612 break;
bgneal@45 5613
bgneal@45 5614 case '%btrans':
bgneal@45 5615 v = bloc;
bgneal@45 5616 break;
bgneal@45 5617
bgneal@45 5618 case '%bstrict':
bgneal@45 5619 v = bloc;
bgneal@45 5620 break;
bgneal@45 5621 }
bgneal@45 5622
bgneal@45 5623 s += v;
bgneal@45 5624 });
bgneal@45 5625 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
bgneal@45 5626
bgneal@45 5627 each(p[0].split('/'), function(s) {
bgneal@45 5628 t.childRules = t.childRules || {};
bgneal@45 5629 t.childRules[s] = re;
bgneal@45 5630 });
bgneal@45 5631 });
bgneal@45 5632
bgneal@45 5633 // Build regex
bgneal@45 5634 s = '';
bgneal@45 5635 each(t.childRules, function(v, k) {
bgneal@45 5636 if (s)
bgneal@45 5637 s += '|';
bgneal@45 5638
bgneal@45 5639 s += k;
bgneal@45 5640 });
bgneal@45 5641
bgneal@45 5642 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
bgneal@45 5643
bgneal@45 5644 /*console.debug(t.parentElementsRE.toString());
bgneal@45 5645 each(t.childRules, function(v) {
bgneal@45 5646 console.debug(v.toString());
bgneal@45 5647 });*/
bgneal@45 5648 },
bgneal@45 5649
bgneal@45 5650 setRules : function(s) {
bgneal@45 5651 var t = this;
bgneal@45 5652
bgneal@45 5653 t._setup();
bgneal@45 5654 t.rules = {};
bgneal@45 5655 t.wildRules = [];
bgneal@45 5656 t.validElements = {};
bgneal@45 5657
bgneal@45 5658 return t.addRules(s);
bgneal@45 5659 },
bgneal@45 5660
bgneal@45 5661 addRules : function(s) {
bgneal@45 5662 var t = this, dr;
bgneal@45 5663
bgneal@45 5664 if (!s)
bgneal@45 5665 return;
bgneal@45 5666
bgneal@45 5667 t._setup();
bgneal@45 5668
bgneal@45 5669 each(s.split(','), function(s) {
bgneal@45 5670 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
bgneal@45 5671
bgneal@45 5672 // Extend with default rules
bgneal@45 5673 if (dr)
bgneal@45 5674 at = tinymce.extend([], dr.attribs);
bgneal@45 5675
bgneal@45 5676 // Parse attributes
bgneal@45 5677 if (p.length > 1) {
bgneal@45 5678 each(p[1].split('|'), function(s) {
bgneal@45 5679 var ar = {}, i;
bgneal@45 5680
bgneal@45 5681 at = at || [];
bgneal@45 5682
bgneal@45 5683 // Parse attribute rule
bgneal@45 5684 s = s.replace(/::/g, '~');
bgneal@45 5685 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
bgneal@45 5686 s[2] = s[2].replace(/~/g, ':');
bgneal@45 5687
bgneal@45 5688 // Add required attributes
bgneal@45 5689 if (s[1] == '!') {
bgneal@45 5690 ra = ra || [];
bgneal@45 5691 ra.push(s[2]);
bgneal@45 5692 }
bgneal@45 5693
bgneal@45 5694 // Remove inherited attributes
bgneal@45 5695 if (s[1] == '-') {
bgneal@45 5696 for (i = 0; i <at.length; i++) {
bgneal@45 5697 if (at[i].name == s[2]) {
bgneal@45 5698 at.splice(i, 1);
bgneal@45 5699 return;
bgneal@45 5700 }
bgneal@45 5701 }
bgneal@45 5702 }
bgneal@45 5703
bgneal@45 5704 switch (s[3]) {
bgneal@45 5705 // Add default attrib values
bgneal@45 5706 case '=':
bgneal@45 5707 ar.defaultVal = s[4] || '';
bgneal@45 5708 break;
bgneal@45 5709
bgneal@45 5710 // Add forced attrib values
bgneal@45 5711 case ':':
bgneal@45 5712 ar.forcedVal = s[4];
bgneal@45 5713 break;
bgneal@45 5714
bgneal@45 5715 // Add validation values
bgneal@45 5716 case '<':
bgneal@45 5717 ar.validVals = s[4].split('?');
bgneal@45 5718 break;
bgneal@45 5719 }
bgneal@45 5720
bgneal@45 5721 if (/[*.?]/.test(s[2])) {
bgneal@45 5722 wat = wat || [];
bgneal@45 5723 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
bgneal@45 5724 wat.push(ar);
bgneal@45 5725 } else {
bgneal@45 5726 ar.name = s[2];
bgneal@45 5727 at.push(ar);
bgneal@45 5728 }
bgneal@45 5729
bgneal@45 5730 va.push(s[2]);
bgneal@45 5731 });
bgneal@45 5732 }
bgneal@45 5733
bgneal@45 5734 // Handle element names
bgneal@45 5735 each(tn, function(s, i) {
bgneal@45 5736 var pr = s.charAt(0), x = 1, ru = {};
bgneal@45 5737
bgneal@45 5738 // Extend with default rule data
bgneal@45 5739 if (dr) {
bgneal@45 5740 if (dr.noEmpty)
bgneal@45 5741 ru.noEmpty = dr.noEmpty;
bgneal@45 5742
bgneal@45 5743 if (dr.fullEnd)
bgneal@45 5744 ru.fullEnd = dr.fullEnd;
bgneal@45 5745
bgneal@45 5746 if (dr.padd)
bgneal@45 5747 ru.padd = dr.padd;
bgneal@45 5748 }
bgneal@45 5749
bgneal@45 5750 // Handle prefixes
bgneal@45 5751 switch (pr) {
bgneal@45 5752 case '-':
bgneal@45 5753 ru.noEmpty = true;
bgneal@45 5754 break;
bgneal@45 5755
bgneal@45 5756 case '+':
bgneal@45 5757 ru.fullEnd = true;
bgneal@45 5758 break;
bgneal@45 5759
bgneal@45 5760 case '#':
bgneal@45 5761 ru.padd = true;
bgneal@45 5762 break;
bgneal@45 5763
bgneal@45 5764 default:
bgneal@45 5765 x = 0;
bgneal@45 5766 }
bgneal@45 5767
bgneal@45 5768 tn[i] = s = s.substring(x);
bgneal@45 5769 t.validElements[s] = 1;
bgneal@45 5770
bgneal@45 5771 // Add element name or element regex
bgneal@45 5772 if (/[*.?]/.test(tn[0])) {
bgneal@45 5773 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
bgneal@45 5774 t.wildRules = t.wildRules || {};
bgneal@45 5775 t.wildRules.push(ru);
bgneal@45 5776 } else {
bgneal@45 5777 ru.name = tn[0];
bgneal@45 5778
bgneal@45 5779 // Store away default rule
bgneal@45 5780 if (tn[0] == '@')
bgneal@45 5781 dr = ru;
bgneal@45 5782
bgneal@45 5783 t.rules[s] = ru;
bgneal@45 5784 }
bgneal@45 5785
bgneal@45 5786 ru.attribs = at;
bgneal@45 5787
bgneal@45 5788 if (ra)
bgneal@45 5789 ru.requiredAttribs = ra;
bgneal@45 5790
bgneal@45 5791 if (wat) {
bgneal@45 5792 // Build valid attributes regexp
bgneal@45 5793 s = '';
bgneal@45 5794 each(va, function(v) {
bgneal@45 5795 if (s)
bgneal@45 5796 s += '|';
bgneal@45 5797
bgneal@45 5798 s += '(' + wildcardToRE(v) + ')';
bgneal@45 5799 });
bgneal@45 5800 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
bgneal@45 5801 ru.wildAttribs = wat;
bgneal@45 5802 }
bgneal@45 5803 });
bgneal@45 5804 });
bgneal@45 5805
bgneal@45 5806 // Build valid elements regexp
bgneal@45 5807 s = '';
bgneal@45 5808 each(t.validElements, function(v, k) {
bgneal@45 5809 if (s)
bgneal@45 5810 s += '|';
bgneal@45 5811
bgneal@45 5812 if (k != '@')
bgneal@45 5813 s += k;
bgneal@45 5814 });
bgneal@45 5815 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');
bgneal@45 5816
bgneal@45 5817 //console.debug(t.validElementsRE.toString());
bgneal@45 5818 //console.dir(t.rules);
bgneal@45 5819 //console.dir(t.wildRules);
bgneal@45 5820 },
bgneal@45 5821
bgneal@45 5822 findRule : function(n) {
bgneal@45 5823 var t = this, rl = t.rules, i, r;
bgneal@45 5824
bgneal@45 5825 t._setup();
bgneal@45 5826
bgneal@45 5827 // Exact match
bgneal@45 5828 r = rl[n];
bgneal@45 5829 if (r)
bgneal@45 5830 return r;
bgneal@45 5831
bgneal@45 5832 // Try wildcards
bgneal@45 5833 rl = t.wildRules;
bgneal@45 5834 for (i = 0; i < rl.length; i++) {
bgneal@45 5835 if (rl[i].nameRE.test(n))
bgneal@45 5836 return rl[i];
bgneal@45 5837 }
bgneal@45 5838
bgneal@45 5839 return null;
bgneal@45 5840 },
bgneal@45 5841
bgneal@45 5842 findAttribRule : function(ru, n) {
bgneal@45 5843 var i, wa = ru.wildAttribs;
bgneal@45 5844
bgneal@45 5845 for (i = 0; i < wa.length; i++) {
bgneal@45 5846 if (wa[i].nameRE.test(n))
bgneal@45 5847 return wa[i];
bgneal@45 5848 }
bgneal@45 5849
bgneal@45 5850 return null;
bgneal@45 5851 },
bgneal@45 5852
bgneal@45 5853 serialize : function(n, o) {
bgneal@45 5854 var h, t = this;
bgneal@45 5855
bgneal@45 5856 t._setup();
bgneal@45 5857 o = o || {};
bgneal@45 5858 o.format = o.format || 'html';
bgneal@45 5859 t.processObj = o;
bgneal@45 5860 n = n.cloneNode(true);
bgneal@45 5861 t.key = '' + (parseInt(t.key) + 1);
bgneal@45 5862
bgneal@45 5863 // Pre process
bgneal@45 5864 if (!o.no_events) {
bgneal@45 5865 o.node = n;
bgneal@45 5866 t.onPreProcess.dispatch(t, o);
bgneal@45 5867 }
bgneal@45 5868
bgneal@45 5869 // Serialize HTML DOM into a string
bgneal@45 5870 t.writer.reset();
bgneal@45 5871 t._serializeNode(n, o.getInner);
bgneal@45 5872
bgneal@45 5873 // Post process
bgneal@45 5874 o.content = t.writer.getContent();
bgneal@45 5875
bgneal@45 5876 if (!o.no_events)
bgneal@45 5877 t.onPostProcess.dispatch(t, o);
bgneal@45 5878
bgneal@45 5879 t._postProcess(o);
bgneal@45 5880 o.node = null;
bgneal@45 5881
bgneal@45 5882 return tinymce.trim(o.content);
bgneal@45 5883 },
bgneal@45 5884
bgneal@45 5885 // Internal functions
bgneal@45 5886
bgneal@45 5887 _postProcess : function(o) {
bgneal@45 5888 var t = this, s = t.settings, h = o.content, sc = [], p;
bgneal@45 5889
bgneal@45 5890 if (o.format == 'html') {
bgneal@45 5891 // Protect some elements
bgneal@45 5892 p = t._protect({
bgneal@45 5893 content : h,
bgneal@45 5894 patterns : [
bgneal@45 5895 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
bgneal@45 5896 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
bgneal@45 5897 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
bgneal@45 5898 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
bgneal@45 5899 ]
bgneal@45 5900 });
bgneal@45 5901
bgneal@45 5902 h = p.content;
bgneal@45 5903
bgneal@45 5904 // Entity encode
bgneal@45 5905 if (s.entity_encoding !== 'raw')
bgneal@45 5906 h = t._encode(h);
bgneal@45 5907
bgneal@45 5908 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
bgneal@45 5909 /* if (o.set)
bgneal@45 5910 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
bgneal@45 5911 else
bgneal@45 5912 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/
bgneal@45 5913
bgneal@45 5914 // Since Gecko and Safari keeps whitespace in the DOM we need to
bgneal@45 5915 // remove it inorder to match other browsers. But I think Gecko and Safari is right.
bgneal@45 5916 // This process is only done when getting contents out from the editor.
bgneal@45 5917 if (!o.set) {
bgneal@45 5918 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
bgneal@45 5919 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');
bgneal@45 5920
bgneal@45 5921 if (s.remove_linebreaks) {
bgneal@45 5922 h = h.replace(/\r?\n|\r/g, ' ');
bgneal@45 5923 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
bgneal@45 5924 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
bgneal@45 5925 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
bgneal@45 5926 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
bgneal@45 5927 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
bgneal@45 5928 }
bgneal@45 5929
bgneal@45 5930 // Simple indentation
bgneal@45 5931 if (s.apply_source_formatting && s.indent_mode == 'simple') {
bgneal@45 5932 // Add line breaks before and after block elements
bgneal@45 5933 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
bgneal@45 5934 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
bgneal@45 5935 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
bgneal@45 5936 h = h.replace(/\n\n/g, '\n');
bgneal@45 5937 }
bgneal@45 5938 }
bgneal@45 5939
bgneal@45 5940 h = t._unprotect(h, p);
bgneal@45 5941
bgneal@45 5942 // Restore CDATA sections
bgneal@45 5943 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');
bgneal@45 5944
bgneal@45 5945 // Restore the \u00a0 character if raw mode is enabled
bgneal@45 5946 if (s.entity_encoding == 'raw')
bgneal@45 5947 h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
bgneal@45 5948 }
bgneal@45 5949
bgneal@45 5950 o.content = h;
bgneal@45 5951 },
bgneal@45 5952
bgneal@45 5953 _serializeNode : function(n, inn) {
bgneal@45 5954 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv;
bgneal@45 5955
bgneal@45 5956 if (!s.node_filter || s.node_filter(n)) {
bgneal@45 5957 switch (n.nodeType) {
bgneal@45 5958 case 1: // Element
bgneal@45 5959 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
bgneal@45 5960 return;
bgneal@45 5961
bgneal@45 5962 iv = false;
bgneal@45 5963 hc = n.hasChildNodes();
bgneal@45 5964 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
bgneal@45 5965
bgneal@45 5966 // Add correct prefix on IE
bgneal@45 5967 if (isIE) {
bgneal@45 5968 if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
bgneal@45 5969 nn = n.scopeName + ':' + nn;
bgneal@45 5970 }
bgneal@45 5971
bgneal@45 5972 // Remove mce prefix on IE needed for the abbr element
bgneal@45 5973 if (nn.indexOf('mce:') === 0)
bgneal@45 5974 nn = nn.substring(4);
bgneal@45 5975
bgneal@45 5976 // Check if valid
bgneal@45 5977 if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
bgneal@45 5978 iv = true;
bgneal@45 5979 break;
bgneal@45 5980 }
bgneal@45 5981
bgneal@45 5982 if (isIE) {
bgneal@45 5983 // Fix IE content duplication (DOM can have multiple copies of the same node)
bgneal@45 5984 if (s.fix_content_duplication) {
bgneal@45 5985 if (n.mce_serialized == t.key)
bgneal@45 5986 return;
bgneal@45 5987
bgneal@45 5988 n.mce_serialized = t.key;
bgneal@45 5989 }
bgneal@45 5990
bgneal@45 5991 // IE sometimes adds a / infront of the node name
bgneal@45 5992 if (nn.charAt(0) == '/')
bgneal@45 5993 nn = nn.substring(1);
bgneal@45 5994 } else if (isGecko) {
bgneal@45 5995 // Ignore br elements
bgneal@45 5996 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
bgneal@45 5997 return;
bgneal@45 5998 }
bgneal@45 5999
bgneal@45 6000 // Check if valid child
bgneal@45 6001 if (t.childRules) {
bgneal@45 6002 if (t.parentElementsRE.test(t.elementName)) {
bgneal@45 6003 if (!t.childRules[t.elementName].test(nn)) {
bgneal@45 6004 iv = true;
bgneal@45 6005 break;
bgneal@45 6006 }
bgneal@45 6007 }
bgneal@45 6008
bgneal@45 6009 t.elementName = nn;
bgneal@45 6010 }
bgneal@45 6011
bgneal@45 6012 ru = t.findRule(nn);
bgneal@45 6013 nn = ru.name || nn;
bgneal@45 6014
bgneal@45 6015 // Skip empty nodes or empty node name in IE
bgneal@45 6016 if ((!hc && ru.noEmpty) || (isIE && !nn)) {
bgneal@45 6017 iv = true;
bgneal@45 6018 break;
bgneal@45 6019 }
bgneal@45 6020
bgneal@45 6021 // Check required
bgneal@45 6022 if (ru.requiredAttribs) {
bgneal@45 6023 a = ru.requiredAttribs;
bgneal@45 6024
bgneal@45 6025 for (i = a.length - 1; i >= 0; i--) {
bgneal@45 6026 if (this.dom.getAttrib(n, a[i]) !== '')
bgneal@45 6027 break;
bgneal@45 6028 }
bgneal@45 6029
bgneal@45 6030 // None of the required was there
bgneal@45 6031 if (i == -1) {
bgneal@45 6032 iv = true;
bgneal@45 6033 break;
bgneal@45 6034 }
bgneal@45 6035 }
bgneal@45 6036
bgneal@45 6037 w.writeStartElement(nn);
bgneal@45 6038
bgneal@45 6039 // Add ordered attributes
bgneal@45 6040 if (ru.attribs) {
bgneal@45 6041 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
bgneal@45 6042 a = at[i];
bgneal@45 6043 v = t._getAttrib(n, a);
bgneal@45 6044
bgneal@45 6045 if (v !== null)
bgneal@45 6046 w.writeAttribute(a.name, v);
bgneal@45 6047 }
bgneal@45 6048 }
bgneal@45 6049
bgneal@45 6050 // Add wild attributes
bgneal@45 6051 if (ru.validAttribsRE) {
bgneal@45 6052 at = t.dom.getAttribs(n);
bgneal@45 6053 for (i=at.length-1; i>-1; i--) {
bgneal@45 6054 no = at[i];
bgneal@45 6055
bgneal@45 6056 if (no.specified) {
bgneal@45 6057 a = no.nodeName.toLowerCase();
bgneal@45 6058
bgneal@45 6059 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
bgneal@45 6060 continue;
bgneal@45 6061
bgneal@45 6062 ar = t.findAttribRule(ru, a);
bgneal@45 6063 v = t._getAttrib(n, ar, a);
bgneal@45 6064
bgneal@45 6065 if (v !== null)
bgneal@45 6066 w.writeAttribute(a, v);
bgneal@45 6067 }
bgneal@45 6068 }
bgneal@45 6069 }
bgneal@45 6070
bgneal@45 6071 // Padd empty nodes with a &nbsp;
bgneal@45 6072 if (ru.padd) {
bgneal@45 6073 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug
bgneal@45 6074 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {
bgneal@45 6075 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus'))
bgneal@45 6076 w.writeText('\u00a0');
bgneal@45 6077 } else if (!hc)
bgneal@45 6078 w.writeText('\u00a0'); // No children then padd it
bgneal@45 6079 }
bgneal@45 6080
bgneal@45 6081 break;
bgneal@45 6082
bgneal@45 6083 case 3: // Text
bgneal@45 6084 // Check if valid child
bgneal@45 6085 if (t.childRules && t.parentElementsRE.test(t.elementName)) {
bgneal@45 6086 if (!t.childRules[t.elementName].test(n.nodeName))
bgneal@45 6087 return;
bgneal@45 6088 }
bgneal@45 6089
bgneal@45 6090 return w.writeText(n.nodeValue);
bgneal@45 6091
bgneal@45 6092 case 4: // CDATA
bgneal@45 6093 return w.writeCDATA(n.nodeValue);
bgneal@45 6094
bgneal@45 6095 case 8: // Comment
bgneal@45 6096 return w.writeComment(n.nodeValue);
bgneal@45 6097 }
bgneal@45 6098 } else if (n.nodeType == 1)
bgneal@45 6099 hc = n.hasChildNodes();
bgneal@45 6100
bgneal@45 6101 if (hc) {
bgneal@45 6102 cn = n.firstChild;
bgneal@45 6103
bgneal@45 6104 while (cn) {
bgneal@45 6105 t._serializeNode(cn);
bgneal@45 6106 t.elementName = nn;
bgneal@45 6107 cn = cn.nextSibling;
bgneal@45 6108 }
bgneal@45 6109 }
bgneal@45 6110
bgneal@45 6111 // Write element end
bgneal@45 6112 if (!iv) {
bgneal@45 6113 if (hc || !s.closed.test(nn))
bgneal@45 6114 w.writeFullEndElement();
bgneal@45 6115 else
bgneal@45 6116 w.writeEndElement();
bgneal@45 6117 }
bgneal@45 6118 },
bgneal@45 6119
bgneal@45 6120 _protect : function(o) {
bgneal@45 6121 var t = this;
bgneal@45 6122
bgneal@45 6123 o.items = o.items || [];
bgneal@45 6124
bgneal@45 6125 function enc(s) {
bgneal@45 6126 return s.replace(/[\r\n\\]/g, function(c) {
bgneal@45 6127 if (c === '\n')
bgneal@45 6128 return '\\n';
bgneal@45 6129 else if (c === '\\')
bgneal@45 6130 return '\\\\';
bgneal@45 6131
bgneal@45 6132 return '\\r';
bgneal@45 6133 });
bgneal@45 6134 };
bgneal@45 6135
bgneal@45 6136 function dec(s) {
bgneal@45 6137 return s.replace(/\\[\\rn]/g, function(c) {
bgneal@45 6138 if (c === '\\n')
bgneal@45 6139 return '\n';
bgneal@45 6140 else if (c === '\\\\')
bgneal@45 6141 return '\\';
bgneal@45 6142
bgneal@45 6143 return '\r';
bgneal@45 6144 });
bgneal@45 6145 };
bgneal@45 6146
bgneal@45 6147 each(o.patterns, function(p) {
bgneal@45 6148 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
bgneal@45 6149 b = dec(b);
bgneal@45 6150
bgneal@45 6151 if (p.encode)
bgneal@45 6152 b = t._encode(b);
bgneal@45 6153
bgneal@45 6154 o.items.push(b);
bgneal@45 6155 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
bgneal@45 6156 }));
bgneal@45 6157 });
bgneal@45 6158
bgneal@45 6159 return o;
bgneal@45 6160 },
bgneal@45 6161
bgneal@45 6162 _unprotect : function(h, o) {
bgneal@45 6163 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
bgneal@45 6164 return o.items[parseInt(b)];
bgneal@45 6165 });
bgneal@45 6166
bgneal@45 6167 o.items = [];
bgneal@45 6168
bgneal@45 6169 return h;
bgneal@45 6170 },
bgneal@45 6171
bgneal@45 6172 _encode : function(h) {
bgneal@45 6173 var t = this, s = t.settings, l;
bgneal@45 6174
bgneal@45 6175 // Entity encode
bgneal@45 6176 if (s.entity_encoding !== 'raw') {
bgneal@45 6177 if (s.entity_encoding.indexOf('named') != -1) {
bgneal@45 6178 t.setEntities(s.entities);
bgneal@45 6179 l = t.entityLookup;
bgneal@45 6180
bgneal@45 6181 h = h.replace(t.entitiesRE, function(a) {
bgneal@45 6182 var v;
bgneal@45 6183
bgneal@45 6184 if (v = l[a])
bgneal@45 6185 a = '&' + v + ';';
bgneal@45 6186
bgneal@45 6187 return a;
bgneal@45 6188 });
bgneal@45 6189 }
bgneal@45 6190
bgneal@45 6191 if (s.entity_encoding.indexOf('numeric') != -1) {
bgneal@45 6192 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
bgneal@45 6193 return '&#' + a.charCodeAt(0) + ';';
bgneal@45 6194 });
bgneal@45 6195 }
bgneal@45 6196 }
bgneal@45 6197
bgneal@45 6198 return h;
bgneal@45 6199 },
bgneal@45 6200
bgneal@45 6201 _setup : function() {
bgneal@45 6202 var t = this, s = this.settings;
bgneal@45 6203
bgneal@45 6204 if (t.done)
bgneal@45 6205 return;
bgneal@45 6206
bgneal@45 6207 t.done = 1;
bgneal@45 6208
bgneal@45 6209 t.setRules(s.valid_elements);
bgneal@45 6210 t.addRules(s.extended_valid_elements);
bgneal@45 6211 t.addValidChildRules(s.valid_child_elements);
bgneal@45 6212
bgneal@45 6213 if (s.invalid_elements)
bgneal@45 6214 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
bgneal@45 6215
bgneal@45 6216 if (s.attrib_value_filter)
bgneal@45 6217 t.attribValueFilter = s.attribValueFilter;
bgneal@45 6218 },
bgneal@45 6219
bgneal@45 6220 _getAttrib : function(n, a, na) {
bgneal@45 6221 var i, v;
bgneal@45 6222
bgneal@45 6223 na = na || a.name;
bgneal@45 6224
bgneal@45 6225 if (a.forcedVal && (v = a.forcedVal)) {
bgneal@45 6226 if (v === '{$uid}')
bgneal@45 6227 return this.dom.uniqueId();
bgneal@45 6228
bgneal@45 6229 return v;
bgneal@45 6230 }
bgneal@45 6231
bgneal@45 6232 v = this.dom.getAttrib(n, na);
bgneal@45 6233
bgneal@45 6234 // Bool attr
bgneal@45 6235 if (this.settings.bool_attrs.test(na) && v) {
bgneal@45 6236 v = ('' + v).toLowerCase();
bgneal@45 6237
bgneal@45 6238 if (v === 'false' || v === '0')
bgneal@45 6239 return null;
bgneal@45 6240
bgneal@45 6241 v = na;
bgneal@45 6242 }
bgneal@45 6243
bgneal@45 6244 switch (na) {
bgneal@45 6245 case 'rowspan':
bgneal@45 6246 case 'colspan':
bgneal@45 6247 // Whats the point? Remove usless attribute value
bgneal@45 6248 if (v == '1')
bgneal@45 6249 v = '';
bgneal@45 6250
bgneal@45 6251 break;
bgneal@45 6252 }
bgneal@45 6253
bgneal@45 6254 if (this.attribValueFilter)
bgneal@45 6255 v = this.attribValueFilter(na, v, n);
bgneal@45 6256
bgneal@45 6257 if (a.validVals) {
bgneal@45 6258 for (i = a.validVals.length - 1; i >= 0; i--) {
bgneal@45 6259 if (v == a.validVals[i])
bgneal@45 6260 break;
bgneal@45 6261 }
bgneal@45 6262
bgneal@45 6263 if (i == -1)
bgneal@45 6264 return null;
bgneal@45 6265 }
bgneal@45 6266
bgneal@45 6267 if (v === '' && typeof(a.defaultVal) != 'undefined') {
bgneal@45 6268 v = a.defaultVal;
bgneal@45 6269
bgneal@45 6270 if (v === '{$uid}')
bgneal@45 6271 return this.dom.uniqueId();
bgneal@45 6272
bgneal@45 6273 return v;
bgneal@45 6274 } else {
bgneal@45 6275 // Remove internal mceItemXX classes when content is extracted from editor
bgneal@45 6276 if (na == 'class' && this.processObj.get)
bgneal@45 6277 v = v.replace(/\s?mceItem\w+\s?/g, '');
bgneal@45 6278 }
bgneal@45 6279
bgneal@45 6280 if (v === '')
bgneal@45 6281 return null;
bgneal@45 6282
bgneal@45 6283
bgneal@45 6284 return v;
bgneal@45 6285 }
bgneal@45 6286
bgneal@45 6287 });
bgneal@45 6288 })(tinymce);
bgneal@45 6289 (function(tinymce) {
bgneal@45 6290 var each = tinymce.each, Event = tinymce.dom.Event;
bgneal@45 6291
bgneal@45 6292 tinymce.create('tinymce.dom.ScriptLoader', {
bgneal@45 6293 ScriptLoader : function(s) {
bgneal@45 6294 this.settings = s || {};
bgneal@45 6295 this.queue = [];
bgneal@45 6296 this.lookup = {};
bgneal@45 6297 },
bgneal@45 6298
bgneal@45 6299 isDone : function(u) {
bgneal@45 6300 return this.lookup[u] ? this.lookup[u].state == 2 : 0;
bgneal@45 6301 },
bgneal@45 6302
bgneal@45 6303 markDone : function(u) {
bgneal@45 6304 this.lookup[u] = {state : 2, url : u};
bgneal@45 6305 },
bgneal@45 6306
bgneal@45 6307 add : function(u, cb, s, pr) {
bgneal@45 6308 var t = this, lo = t.lookup, o;
bgneal@45 6309
bgneal@45 6310 if (o = lo[u]) {
bgneal@45 6311 // Is loaded fire callback
bgneal@45 6312 if (cb && o.state == 2)
bgneal@45 6313 cb.call(s || this);
bgneal@45 6314
bgneal@45 6315 return o;
bgneal@45 6316 }
bgneal@45 6317
bgneal@45 6318 o = {state : 0, url : u, func : cb, scope : s || this};
bgneal@45 6319
bgneal@45 6320 if (pr)
bgneal@45 6321 t.queue.unshift(o);
bgneal@45 6322 else
bgneal@45 6323 t.queue.push(o);
bgneal@45 6324
bgneal@45 6325 lo[u] = o;
bgneal@45 6326
bgneal@45 6327 return o;
bgneal@45 6328 },
bgneal@45 6329
bgneal@45 6330 load : function(u, cb, s) {
bgneal@45 6331 var t = this, o;
bgneal@45 6332
bgneal@45 6333 if (o = t.lookup[u]) {
bgneal@45 6334 // Is loaded fire callback
bgneal@45 6335 if (cb && o.state == 2)
bgneal@45 6336 cb.call(s || t);
bgneal@45 6337
bgneal@45 6338 return o;
bgneal@45 6339 }
bgneal@45 6340
bgneal@45 6341 function loadScript(u) {
bgneal@45 6342 if (Event.domLoaded || t.settings.strict_mode) {
bgneal@45 6343 tinymce.util.XHR.send({
bgneal@45 6344 url : tinymce._addVer(u),
bgneal@45 6345 error : t.settings.error,
bgneal@45 6346 async : false,
bgneal@45 6347 success : function(co) {
bgneal@45 6348 t.eval(co);
bgneal@45 6349 }
bgneal@45 6350 });
bgneal@45 6351 } else
bgneal@45 6352 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
bgneal@45 6353 };
bgneal@45 6354
bgneal@45 6355 if (!tinymce.is(u, 'string')) {
bgneal@45 6356 each(u, function(u) {
bgneal@45 6357 loadScript(u);
bgneal@45 6358 });
bgneal@45 6359
bgneal@45 6360 if (cb)
bgneal@45 6361 cb.call(s || t);
bgneal@45 6362 } else {
bgneal@45 6363 loadScript(u);
bgneal@45 6364
bgneal@45 6365 if (cb)
bgneal@45 6366 cb.call(s || t);
bgneal@45 6367 }
bgneal@45 6368 },
bgneal@45 6369
bgneal@45 6370 loadQueue : function(cb, s) {
bgneal@45 6371 var t = this;
bgneal@45 6372
bgneal@45 6373 if (!t.queueLoading) {
bgneal@45 6374 t.queueLoading = 1;
bgneal@45 6375 t.queueCallbacks = [];
bgneal@45 6376
bgneal@45 6377 t.loadScripts(t.queue, function() {
bgneal@45 6378 t.queueLoading = 0;
bgneal@45 6379
bgneal@45 6380 if (cb)
bgneal@45 6381 cb.call(s || t);
bgneal@45 6382
bgneal@45 6383 each(t.queueCallbacks, function(o) {
bgneal@45 6384 o.func.call(o.scope);
bgneal@45 6385 });
bgneal@45 6386 });
bgneal@45 6387 } else if (cb)
bgneal@45 6388 t.queueCallbacks.push({func : cb, scope : s || t});
bgneal@45 6389 },
bgneal@45 6390
bgneal@45 6391 eval : function(co) {
bgneal@45 6392 var w = window;
bgneal@45 6393
bgneal@45 6394 // Evaluate script
bgneal@45 6395 if (!w.execScript) {
bgneal@45 6396 try {
bgneal@45 6397 eval.call(w, co);
bgneal@45 6398 } catch (ex) {
bgneal@45 6399 eval(co, w); // Firefox 3.0a8
bgneal@45 6400 }
bgneal@45 6401 } else
bgneal@45 6402 w.execScript(co); // IE
bgneal@45 6403 },
bgneal@45 6404
bgneal@45 6405 loadScripts : function(sc, cb, s) {
bgneal@45 6406 var t = this, lo = t.lookup;
bgneal@45 6407
bgneal@45 6408 function done(o) {
bgneal@45 6409 o.state = 2; // Has been loaded
bgneal@45 6410
bgneal@45 6411 // Run callback
bgneal@45 6412 if (o.func)
bgneal@45 6413 o.func.call(o.scope || t);
bgneal@45 6414 };
bgneal@45 6415
bgneal@45 6416 function allDone() {
bgneal@45 6417 var l;
bgneal@45 6418
bgneal@45 6419 // Check if all files are loaded
bgneal@45 6420 l = sc.length;
bgneal@45 6421 each(sc, function(o) {
bgneal@45 6422 o = lo[o.url];
bgneal@45 6423
bgneal@45 6424 if (o.state === 2) {// It has finished loading
bgneal@45 6425 done(o);
bgneal@45 6426 l--;
bgneal@45 6427 } else
bgneal@45 6428 load(o);
bgneal@45 6429 });
bgneal@45 6430
bgneal@45 6431 // They are all loaded
bgneal@45 6432 if (l === 0 && cb) {
bgneal@45 6433 cb.call(s || t);
bgneal@45 6434 cb = 0;
bgneal@45 6435 }
bgneal@45 6436 };
bgneal@45 6437
bgneal@45 6438 function load(o) {
bgneal@45 6439 if (o.state > 0)
bgneal@45 6440 return;
bgneal@45 6441
bgneal@45 6442 o.state = 1; // Is loading
bgneal@45 6443
bgneal@45 6444 tinymce.dom.ScriptLoader.loadScript(o.url, function() {
bgneal@45 6445 done(o);
bgneal@45 6446 allDone();
bgneal@45 6447 });
bgneal@45 6448
bgneal@45 6449 /*
bgneal@45 6450 tinymce.util.XHR.send({
bgneal@45 6451 url : o.url,
bgneal@45 6452 error : t.settings.error,
bgneal@45 6453 success : function(co) {
bgneal@45 6454 t.eval(co);
bgneal@45 6455 done(o);
bgneal@45 6456 allDone();
bgneal@45 6457 }
bgneal@45 6458 });
bgneal@45 6459 */
bgneal@45 6460 };
bgneal@45 6461
bgneal@45 6462 each(sc, function(o) {
bgneal@45 6463 var u = o.url;
bgneal@45 6464
bgneal@45 6465 // Add to queue if needed
bgneal@45 6466 if (!lo[u]) {
bgneal@45 6467 lo[u] = o;
bgneal@45 6468 t.queue.push(o);
bgneal@45 6469 } else
bgneal@45 6470 o = lo[u];
bgneal@45 6471
bgneal@45 6472 // Is already loading or has been loaded
bgneal@45 6473 if (o.state > 0)
bgneal@45 6474 return;
bgneal@45 6475
bgneal@45 6476 if (!Event.domLoaded && !t.settings.strict_mode) {
bgneal@45 6477 var ix, ol = '';
bgneal@45 6478
bgneal@45 6479 // Add onload events
bgneal@45 6480 if (cb || o.func) {
bgneal@45 6481 o.state = 1; // Is loading
bgneal@45 6482
bgneal@45 6483 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
bgneal@45 6484 done(o);
bgneal@45 6485 allDone();
bgneal@45 6486 });
bgneal@45 6487
bgneal@45 6488 if (tinymce.isIE)
bgneal@45 6489 ol = ' onreadystatechange="';
bgneal@45 6490 else
bgneal@45 6491 ol = ' onload="';
bgneal@45 6492
bgneal@45 6493 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
bgneal@45 6494 }
bgneal@45 6495
bgneal@45 6496 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
bgneal@45 6497
bgneal@45 6498 if (!o.func)
bgneal@45 6499 done(o);
bgneal@45 6500 } else
bgneal@45 6501 load(o);
bgneal@45 6502 });
bgneal@45 6503
bgneal@45 6504 allDone();
bgneal@45 6505 },
bgneal@45 6506
bgneal@45 6507 // Static methods
bgneal@45 6508 'static' : {
bgneal@45 6509 _addOnLoad : function(f) {
bgneal@45 6510 var t = this;
bgneal@45 6511
bgneal@45 6512 t._funcs = t._funcs || [];
bgneal@45 6513 t._funcs.push(f);
bgneal@45 6514
bgneal@45 6515 return t._funcs.length - 1;
bgneal@45 6516 },
bgneal@45 6517
bgneal@45 6518 _onLoad : function(e, u, ix) {
bgneal@45 6519 if (!tinymce.isIE || e.readyState == 'complete')
bgneal@45 6520 this._funcs[ix].call(this);
bgneal@45 6521 },
bgneal@45 6522
bgneal@45 6523 loadScript : function(u, cb) {
bgneal@45 6524 var id = tinymce.DOM.uniqueId(), e;
bgneal@45 6525
bgneal@45 6526 function done() {
bgneal@45 6527 Event.clear(id);
bgneal@45 6528 tinymce.DOM.remove(id);
bgneal@45 6529
bgneal@45 6530 if (cb) {
bgneal@45 6531 cb.call(document, u);
bgneal@45 6532 cb = 0;
bgneal@45 6533 }
bgneal@45 6534 };
bgneal@45 6535
bgneal@45 6536 if (tinymce.isIE) {
bgneal@45 6537 /* Event.add(e, 'readystatechange', function(e) {
bgneal@45 6538 if (e.target && e.target.readyState == 'complete')
bgneal@45 6539 done();
bgneal@45 6540 });*/
bgneal@45 6541
bgneal@45 6542 tinymce.util.XHR.send({
bgneal@45 6543 url : tinymce._addVer(u),
bgneal@45 6544 async : false,
bgneal@45 6545 success : function(co) {
bgneal@45 6546 window.execScript(co);
bgneal@45 6547 done();
bgneal@45 6548 }
bgneal@45 6549 });
bgneal@45 6550 } else {
bgneal@45 6551 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)});
bgneal@45 6552 Event.add(e, 'load', done);
bgneal@45 6553
bgneal@45 6554 // Check for head or body
bgneal@45 6555 (document.getElementsByTagName('head')[0] || document.body).appendChild(e);
bgneal@45 6556 }
bgneal@45 6557 }
bgneal@45 6558 }
bgneal@45 6559
bgneal@45 6560 });
bgneal@45 6561
bgneal@45 6562 // Global script loader
bgneal@45 6563 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
bgneal@45 6564 })(tinymce);
bgneal@45 6565 (function(tinymce) {
bgneal@45 6566 // Shorten class names
bgneal@45 6567 var DOM = tinymce.DOM, is = tinymce.is;
bgneal@45 6568
bgneal@45 6569 tinymce.create('tinymce.ui.Control', {
bgneal@45 6570 Control : function(id, s) {
bgneal@45 6571 this.id = id;
bgneal@45 6572 this.settings = s = s || {};
bgneal@45 6573 this.rendered = false;
bgneal@45 6574 this.onRender = new tinymce.util.Dispatcher(this);
bgneal@45 6575 this.classPrefix = '';
bgneal@45 6576 this.scope = s.scope || this;
bgneal@45 6577 this.disabled = 0;
bgneal@45 6578 this.active = 0;
bgneal@45 6579 },
bgneal@45 6580
bgneal@45 6581 setDisabled : function(s) {
bgneal@45 6582 var e;
bgneal@45 6583
bgneal@45 6584 if (s != this.disabled) {
bgneal@45 6585 e = DOM.get(this.id);
bgneal@45 6586
bgneal@45 6587 // Add accessibility title for unavailable actions
bgneal@45 6588 if (e && this.settings.unavailable_prefix) {
bgneal@45 6589 if (s) {
bgneal@45 6590 this.prevTitle = e.title;
bgneal@45 6591 e.title = this.settings.unavailable_prefix + ": " + e.title;
bgneal@45 6592 } else
bgneal@45 6593 e.title = this.prevTitle;
bgneal@45 6594 }
bgneal@45 6595
bgneal@45 6596 this.setState('Disabled', s);
bgneal@45 6597 this.setState('Enabled', !s);
bgneal@45 6598 this.disabled = s;
bgneal@45 6599 }
bgneal@45 6600 },
bgneal@45 6601
bgneal@45 6602 isDisabled : function() {
bgneal@45 6603 return this.disabled;
bgneal@45 6604 },
bgneal@45 6605
bgneal@45 6606 setActive : function(s) {
bgneal@45 6607 if (s != this.active) {
bgneal@45 6608 this.setState('Active', s);
bgneal@45 6609 this.active = s;
bgneal@45 6610 }
bgneal@45 6611 },
bgneal@45 6612
bgneal@45 6613 isActive : function() {
bgneal@45 6614 return this.active;
bgneal@45 6615 },
bgneal@45 6616
bgneal@45 6617 setState : function(c, s) {
bgneal@45 6618 var n = DOM.get(this.id);
bgneal@45 6619
bgneal@45 6620 c = this.classPrefix + c;
bgneal@45 6621
bgneal@45 6622 if (s)
bgneal@45 6623 DOM.addClass(n, c);
bgneal@45 6624 else
bgneal@45 6625 DOM.removeClass(n, c);
bgneal@45 6626 },
bgneal@45 6627
bgneal@45 6628 isRendered : function() {
bgneal@45 6629 return this.rendered;
bgneal@45 6630 },
bgneal@45 6631
bgneal@45 6632 renderHTML : function() {
bgneal@45 6633 },
bgneal@45 6634
bgneal@45 6635 renderTo : function(n) {
bgneal@45 6636 DOM.setHTML(n, this.renderHTML());
bgneal@45 6637 },
bgneal@45 6638
bgneal@45 6639 postRender : function() {
bgneal@45 6640 var t = this, b;
bgneal@45 6641
bgneal@45 6642 // Set pending states
bgneal@45 6643 if (is(t.disabled)) {
bgneal@45 6644 b = t.disabled;
bgneal@45 6645 t.disabled = -1;
bgneal@45 6646 t.setDisabled(b);
bgneal@45 6647 }
bgneal@45 6648
bgneal@45 6649 if (is(t.active)) {
bgneal@45 6650 b = t.active;
bgneal@45 6651 t.active = -1;
bgneal@45 6652 t.setActive(b);
bgneal@45 6653 }
bgneal@45 6654 },
bgneal@45 6655
bgneal@45 6656 remove : function() {
bgneal@45 6657 DOM.remove(this.id);
bgneal@45 6658 this.destroy();
bgneal@45 6659 },
bgneal@45 6660
bgneal@45 6661 destroy : function() {
bgneal@45 6662 tinymce.dom.Event.clear(this.id);
bgneal@45 6663 }
bgneal@45 6664
bgneal@45 6665 });
bgneal@45 6666 })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
bgneal@45 6667 Container : function(id, s) {
bgneal@45 6668 this.parent(id, s);
bgneal@45 6669 this.controls = [];
bgneal@45 6670 this.lookup = {};
bgneal@45 6671 },
bgneal@45 6672
bgneal@45 6673 add : function(c) {
bgneal@45 6674 this.lookup[c.id] = c;
bgneal@45 6675 this.controls.push(c);
bgneal@45 6676
bgneal@45 6677 return c;
bgneal@45 6678 },
bgneal@45 6679
bgneal@45 6680 get : function(n) {
bgneal@45 6681 return this.lookup[n];
bgneal@45 6682 }
bgneal@45 6683
bgneal@45 6684 });
bgneal@45 6685
bgneal@45 6686 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
bgneal@45 6687 Separator : function(id, s) {
bgneal@45 6688 this.parent(id, s);
bgneal@45 6689 this.classPrefix = 'mceSeparator';
bgneal@45 6690 },
bgneal@45 6691
bgneal@45 6692 renderHTML : function() {
bgneal@45 6693 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});
bgneal@45 6694 }
bgneal@45 6695
bgneal@45 6696 });
bgneal@45 6697 (function(tinymce) {
bgneal@45 6698 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
bgneal@45 6699
bgneal@45 6700 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
bgneal@45 6701 MenuItem : function(id, s) {
bgneal@45 6702 this.parent(id, s);
bgneal@45 6703 this.classPrefix = 'mceMenuItem';
bgneal@45 6704 },
bgneal@45 6705
bgneal@45 6706 setSelected : function(s) {
bgneal@45 6707 this.setState('Selected', s);
bgneal@45 6708 this.selected = s;
bgneal@45 6709 },
bgneal@45 6710
bgneal@45 6711 isSelected : function() {
bgneal@45 6712 return this.selected;
bgneal@45 6713 },
bgneal@45 6714
bgneal@45 6715 postRender : function() {
bgneal@45 6716 var t = this;
bgneal@45 6717
bgneal@45 6718 t.parent();
bgneal@45 6719
bgneal@45 6720 // Set pending state
bgneal@45 6721 if (is(t.selected))
bgneal@45 6722 t.setSelected(t.selected);
bgneal@45 6723 }
bgneal@45 6724
bgneal@45 6725 });
bgneal@45 6726 })(tinymce);
bgneal@45 6727 (function(tinymce) {
bgneal@45 6728 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
bgneal@45 6729
bgneal@45 6730 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
bgneal@45 6731 Menu : function(id, s) {
bgneal@45 6732 var t = this;
bgneal@45 6733
bgneal@45 6734 t.parent(id, s);
bgneal@45 6735 t.items = {};
bgneal@45 6736 t.collapsed = false;
bgneal@45 6737 t.menuCount = 0;
bgneal@45 6738 t.onAddItem = new tinymce.util.Dispatcher(this);
bgneal@45 6739 },
bgneal@45 6740
bgneal@45 6741 expand : function(d) {
bgneal@45 6742 var t = this;
bgneal@45 6743
bgneal@45 6744 if (d) {
bgneal@45 6745 walk(t, function(o) {
bgneal@45 6746 if (o.expand)
bgneal@45 6747 o.expand();
bgneal@45 6748 }, 'items', t);
bgneal@45 6749 }
bgneal@45 6750
bgneal@45 6751 t.collapsed = false;
bgneal@45 6752 },
bgneal@45 6753
bgneal@45 6754 collapse : function(d) {
bgneal@45 6755 var t = this;
bgneal@45 6756
bgneal@45 6757 if (d) {
bgneal@45 6758 walk(t, function(o) {
bgneal@45 6759 if (o.collapse)
bgneal@45 6760 o.collapse();
bgneal@45 6761 }, 'items', t);
bgneal@45 6762 }
bgneal@45 6763
bgneal@45 6764 t.collapsed = true;
bgneal@45 6765 },
bgneal@45 6766
bgneal@45 6767 isCollapsed : function() {
bgneal@45 6768 return this.collapsed;
bgneal@45 6769 },
bgneal@45 6770
bgneal@45 6771 add : function(o) {
bgneal@45 6772 if (!o.settings)
bgneal@45 6773 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
bgneal@45 6774
bgneal@45 6775 this.onAddItem.dispatch(this, o);
bgneal@45 6776
bgneal@45 6777 return this.items[o.id] = o;
bgneal@45 6778 },
bgneal@45 6779
bgneal@45 6780 addSeparator : function() {
bgneal@45 6781 return this.add({separator : true});
bgneal@45 6782 },
bgneal@45 6783
bgneal@45 6784 addMenu : function(o) {
bgneal@45 6785 if (!o.collapse)
bgneal@45 6786 o = this.createMenu(o);
bgneal@45 6787
bgneal@45 6788 this.menuCount++;
bgneal@45 6789
bgneal@45 6790 return this.add(o);
bgneal@45 6791 },
bgneal@45 6792
bgneal@45 6793 hasMenus : function() {
bgneal@45 6794 return this.menuCount !== 0;
bgneal@45 6795 },
bgneal@45 6796
bgneal@45 6797 remove : function(o) {
bgneal@45 6798 delete this.items[o.id];
bgneal@45 6799 },
bgneal@45 6800
bgneal@45 6801 removeAll : function() {
bgneal@45 6802 var t = this;
bgneal@45 6803
bgneal@45 6804 walk(t, function(o) {
bgneal@45 6805 if (o.removeAll)
bgneal@45 6806 o.removeAll();
bgneal@45 6807 else
bgneal@45 6808 o.remove();
bgneal@45 6809
bgneal@45 6810 o.destroy();
bgneal@45 6811 }, 'items', t);
bgneal@45 6812
bgneal@45 6813 t.items = {};
bgneal@45 6814 },
bgneal@45 6815
bgneal@45 6816 createMenu : function(o) {
bgneal@45 6817 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
bgneal@45 6818
bgneal@45 6819 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
bgneal@45 6820
bgneal@45 6821 return m;
bgneal@45 6822 }
bgneal@45 6823
bgneal@45 6824 });
bgneal@45 6825 })(tinymce);(function(tinymce) {
bgneal@45 6826 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
bgneal@45 6827
bgneal@45 6828 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
bgneal@45 6829 DropMenu : function(id, s) {
bgneal@45 6830 s = s || {};
bgneal@45 6831 s.container = s.container || DOM.doc.body;
bgneal@45 6832 s.offset_x = s.offset_x || 0;
bgneal@45 6833 s.offset_y = s.offset_y || 0;
bgneal@45 6834 s.vp_offset_x = s.vp_offset_x || 0;
bgneal@45 6835 s.vp_offset_y = s.vp_offset_y || 0;
bgneal@45 6836
bgneal@45 6837 if (is(s.icons) && !s.icons)
bgneal@45 6838 s['class'] += ' mceNoIcons';
bgneal@45 6839
bgneal@45 6840 this.parent(id, s);
bgneal@45 6841 this.onShowMenu = new tinymce.util.Dispatcher(this);
bgneal@45 6842 this.onHideMenu = new tinymce.util.Dispatcher(this);
bgneal@45 6843 this.classPrefix = 'mceMenu';
bgneal@45 6844 },
bgneal@45 6845
bgneal@45 6846 createMenu : function(s) {
bgneal@45 6847 var t = this, cs = t.settings, m;
bgneal@45 6848
bgneal@45 6849 s.container = s.container || cs.container;
bgneal@45 6850 s.parent = t;
bgneal@45 6851 s.constrain = s.constrain || cs.constrain;
bgneal@45 6852 s['class'] = s['class'] || cs['class'];
bgneal@45 6853 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
bgneal@45 6854 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
bgneal@45 6855 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
bgneal@45 6856
bgneal@45 6857 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
bgneal@45 6858
bgneal@45 6859 return m;
bgneal@45 6860 },
bgneal@45 6861
bgneal@45 6862 update : function() {
bgneal@45 6863 var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
bgneal@45 6864
bgneal@45 6865 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
bgneal@45 6866 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
bgneal@45 6867
bgneal@45 6868 if (!DOM.boxModel)
bgneal@45 6869 t.element.setStyles({width : tw + 2, height : th + 2});
bgneal@45 6870 else
bgneal@45 6871 t.element.setStyles({width : tw, height : th});
bgneal@45 6872
bgneal@45 6873 if (s.max_width)
bgneal@45 6874 DOM.setStyle(co, 'width', tw);
bgneal@45 6875
bgneal@45 6876 if (s.max_height) {
bgneal@45 6877 DOM.setStyle(co, 'height', th);
bgneal@45 6878
bgneal@45 6879 if (tb.clientHeight < s.max_height)
bgneal@45 6880 DOM.setStyle(co, 'overflow', 'hidden');
bgneal@45 6881 }
bgneal@45 6882 },
bgneal@45 6883
bgneal@45 6884 showMenu : function(x, y, px) {
bgneal@45 6885 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
bgneal@45 6886
bgneal@45 6887 t.collapse(1);
bgneal@45 6888
bgneal@45 6889 if (t.isMenuVisible)
bgneal@45 6890 return;
bgneal@45 6891
bgneal@45 6892 if (!t.rendered) {
bgneal@45 6893 co = DOM.add(t.settings.container, t.renderNode());
bgneal@45 6894
bgneal@45 6895 each(t.items, function(o) {
bgneal@45 6896 o.postRender();
bgneal@45 6897 });
bgneal@45 6898
bgneal@45 6899 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
bgneal@45 6900 } else
bgneal@45 6901 co = DOM.get('menu_' + t.id);
bgneal@45 6902
bgneal@45 6903 // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
bgneal@45 6904 if (!tinymce.isOpera)
bgneal@45 6905 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});
bgneal@45 6906
bgneal@45 6907 DOM.show(co);
bgneal@45 6908 t.update();
bgneal@45 6909
bgneal@45 6910 x += s.offset_x || 0;
bgneal@45 6911 y += s.offset_y || 0;
bgneal@45 6912 vp.w -= 4;
bgneal@45 6913 vp.h -= 4;
bgneal@45 6914
bgneal@45 6915 // Move inside viewport if not submenu
bgneal@45 6916 if (s.constrain) {
bgneal@45 6917 w = co.clientWidth - ot;
bgneal@45 6918 h = co.clientHeight - ot;
bgneal@45 6919 mx = vp.x + vp.w;
bgneal@45 6920 my = vp.y + vp.h;
bgneal@45 6921
bgneal@45 6922 if ((x + s.vp_offset_x + w) > mx)
bgneal@45 6923 x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);
bgneal@45 6924
bgneal@45 6925 if ((y + s.vp_offset_y + h) > my)
bgneal@45 6926 y = Math.max(0, (my - s.vp_offset_y) - h);
bgneal@45 6927 }
bgneal@45 6928
bgneal@45 6929 DOM.setStyles(co, {left : x , top : y});
bgneal@45 6930 t.element.update();
bgneal@45 6931
bgneal@45 6932 t.isMenuVisible = 1;
bgneal@45 6933 t.mouseClickFunc = Event.add(co, 'click', function(e) {
bgneal@45 6934 var m;
bgneal@45 6935
bgneal@45 6936 e = e.target;
bgneal@45 6937
bgneal@45 6938 if (e && (e = DOM.getParent(e, 'TR')) && !DOM.hasClass(e, cp + 'ItemSub')) {
bgneal@45 6939 m = t.items[e.id];
bgneal@45 6940
bgneal@45 6941 if (m.isDisabled())
bgneal@45 6942 return;
bgneal@45 6943
bgneal@45 6944 dm = t;
bgneal@45 6945
bgneal@45 6946 while (dm) {
bgneal@45 6947 if (dm.hideMenu)
bgneal@45 6948 dm.hideMenu();
bgneal@45 6949
bgneal@45 6950 dm = dm.settings.parent;
bgneal@45 6951 }
bgneal@45 6952
bgneal@45 6953 if (m.settings.onclick)
bgneal@45 6954 m.settings.onclick(e);
bgneal@45 6955
bgneal@45 6956 return Event.cancel(e); // Cancel to fix onbeforeunload problem
bgneal@45 6957 }
bgneal@45 6958 });
bgneal@45 6959
bgneal@45 6960 if (t.hasMenus()) {
bgneal@45 6961 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
bgneal@45 6962 var m, r, mi;
bgneal@45 6963
bgneal@45 6964 e = e.target;
bgneal@45 6965 if (e && (e = DOM.getParent(e, 'TR'))) {
bgneal@45 6966 m = t.items[e.id];
bgneal@45 6967
bgneal@45 6968 if (t.lastMenu)
bgneal@45 6969 t.lastMenu.collapse(1);
bgneal@45 6970
bgneal@45 6971 if (m.isDisabled())
bgneal@45 6972 return;
bgneal@45 6973
bgneal@45 6974 if (e && DOM.hasClass(e, cp + 'ItemSub')) {
bgneal@45 6975 //p = DOM.getPos(s.container);
bgneal@45 6976 r = DOM.getRect(e);
bgneal@45 6977 m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
bgneal@45 6978 t.lastMenu = m;
bgneal@45 6979 DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
bgneal@45 6980 }
bgneal@45 6981 }
bgneal@45 6982 });
bgneal@45 6983 }
bgneal@45 6984
bgneal@45 6985 t.onShowMenu.dispatch(t);
bgneal@45 6986
bgneal@45 6987 if (s.keyboard_focus) {
bgneal@45 6988 Event.add(co, 'keydown', t._keyHandler, t);
bgneal@45 6989 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
bgneal@45 6990 t._focusIdx = 0;
bgneal@45 6991 }
bgneal@45 6992 },
bgneal@45 6993
bgneal@45 6994 hideMenu : function(c) {
bgneal@45 6995 var t = this, co = DOM.get('menu_' + t.id), e;
bgneal@45 6996
bgneal@45 6997 if (!t.isMenuVisible)
bgneal@45 6998 return;
bgneal@45 6999
bgneal@45 7000 Event.remove(co, 'mouseover', t.mouseOverFunc);
bgneal@45 7001 Event.remove(co, 'click', t.mouseClickFunc);
bgneal@45 7002 Event.remove(co, 'keydown', t._keyHandler);
bgneal@45 7003 DOM.hide(co);
bgneal@45 7004 t.isMenuVisible = 0;
bgneal@45 7005
bgneal@45 7006 if (!c)
bgneal@45 7007 t.collapse(1);
bgneal@45 7008
bgneal@45 7009 if (t.element)
bgneal@45 7010 t.element.hide();
bgneal@45 7011
bgneal@45 7012 if (e = DOM.get(t.id))
bgneal@45 7013 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
bgneal@45 7014
bgneal@45 7015 t.onHideMenu.dispatch(t);
bgneal@45 7016 },
bgneal@45 7017
bgneal@45 7018 add : function(o) {
bgneal@45 7019 var t = this, co;
bgneal@45 7020
bgneal@45 7021 o = t.parent(o);
bgneal@45 7022
bgneal@45 7023 if (t.isRendered && (co = DOM.get('menu_' + t.id)))
bgneal@45 7024 t._add(DOM.select('tbody', co)[0], o);
bgneal@45 7025
bgneal@45 7026 return o;
bgneal@45 7027 },
bgneal@45 7028
bgneal@45 7029 collapse : function(d) {
bgneal@45 7030 this.parent(d);
bgneal@45 7031 this.hideMenu(1);
bgneal@45 7032 },
bgneal@45 7033
bgneal@45 7034 remove : function(o) {
bgneal@45 7035 DOM.remove(o.id);
bgneal@45 7036 this.destroy();
bgneal@45 7037
bgneal@45 7038 return this.parent(o);
bgneal@45 7039 },
bgneal@45 7040
bgneal@45 7041 destroy : function() {
bgneal@45 7042 var t = this, co = DOM.get('menu_' + t.id);
bgneal@45 7043
bgneal@45 7044 Event.remove(co, 'mouseover', t.mouseOverFunc);
bgneal@45 7045 Event.remove(co, 'click', t.mouseClickFunc);
bgneal@45 7046
bgneal@45 7047 if (t.element)
bgneal@45 7048 t.element.remove();
bgneal@45 7049
bgneal@45 7050 DOM.remove(co);
bgneal@45 7051 },
bgneal@45 7052
bgneal@45 7053 renderNode : function() {
bgneal@45 7054 var t = this, s = t.settings, n, tb, co, w;
bgneal@45 7055
bgneal@45 7056 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
bgneal@45 7057 co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
bgneal@45 7058 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
bgneal@45 7059
bgneal@45 7060 if (s.menu_line)
bgneal@45 7061 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});
bgneal@45 7062
bgneal@45 7063 // n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
bgneal@45 7064 n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
bgneal@45 7065 tb = DOM.add(n, 'tbody');
bgneal@45 7066
bgneal@45 7067 each(t.items, function(o) {
bgneal@45 7068 t._add(tb, o);
bgneal@45 7069 });
bgneal@45 7070
bgneal@45 7071 t.rendered = true;
bgneal@45 7072
bgneal@45 7073 return w;
bgneal@45 7074 },
bgneal@45 7075
bgneal@45 7076 // Internal functions
bgneal@45 7077
bgneal@45 7078 _keyHandler : function(e) {
bgneal@45 7079 var t = this, kc = e.keyCode;
bgneal@45 7080
bgneal@45 7081 function focus(d) {
bgneal@45 7082 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
bgneal@45 7083
bgneal@45 7084 if (e) {
bgneal@45 7085 t._focusIdx = i;
bgneal@45 7086 e.focus();
bgneal@45 7087 }
bgneal@45 7088 };
bgneal@45 7089
bgneal@45 7090 switch (kc) {
bgneal@45 7091 case 38:
bgneal@45 7092 focus(-1); // Select first link
bgneal@45 7093 return;
bgneal@45 7094
bgneal@45 7095 case 40:
bgneal@45 7096 focus(1);
bgneal@45 7097 return;
bgneal@45 7098
bgneal@45 7099 case 13:
bgneal@45 7100 return;
bgneal@45 7101
bgneal@45 7102 case 27:
bgneal@45 7103 return this.hideMenu();
bgneal@45 7104 }
bgneal@45 7105 },
bgneal@45 7106
bgneal@45 7107 _add : function(tb, o) {
bgneal@45 7108 var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;
bgneal@45 7109
bgneal@45 7110 if (s.separator) {
bgneal@45 7111 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
bgneal@45 7112 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});
bgneal@45 7113
bgneal@45 7114 if (n = ro.previousSibling)
bgneal@45 7115 DOM.addClass(n, 'mceLast');
bgneal@45 7116
bgneal@45 7117 return;
bgneal@45 7118 }
bgneal@45 7119
bgneal@45 7120 n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
bgneal@45 7121 n = it = DOM.add(n, 'td');
bgneal@45 7122 n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});
bgneal@45 7123
bgneal@45 7124 DOM.addClass(it, s['class']);
bgneal@45 7125 // n = DOM.add(n, 'span', {'class' : 'item'});
bgneal@45 7126
bgneal@45 7127 ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
bgneal@45 7128
bgneal@45 7129 if (s.icon_src)
bgneal@45 7130 DOM.add(ic, 'img', {src : s.icon_src});
bgneal@45 7131
bgneal@45 7132 n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
bgneal@45 7133
bgneal@45 7134 if (o.settings.style)
bgneal@45 7135 DOM.setAttrib(n, 'style', o.settings.style);
bgneal@45 7136
bgneal@45 7137 if (tb.childNodes.length == 1)
bgneal@45 7138 DOM.addClass(ro, 'mceFirst');
bgneal@45 7139
bgneal@45 7140 if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
bgneal@45 7141 DOM.addClass(ro, 'mceFirst');
bgneal@45 7142
bgneal@45 7143 if (o.collapse)
bgneal@45 7144 DOM.addClass(ro, cp + 'ItemSub');
bgneal@45 7145
bgneal@45 7146 if (n = ro.previousSibling)
bgneal@45 7147 DOM.removeClass(n, 'mceLast');
bgneal@45 7148
bgneal@45 7149 DOM.addClass(ro, 'mceLast');
bgneal@45 7150 }
bgneal@45 7151
bgneal@45 7152 });
bgneal@45 7153 })(tinymce);(function(tinymce) {
bgneal@45 7154 var DOM = tinymce.DOM;
bgneal@45 7155
bgneal@45 7156 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
bgneal@45 7157 Button : function(id, s) {
bgneal@45 7158 this.parent(id, s);
bgneal@45 7159 this.classPrefix = 'mceButton';
bgneal@45 7160 },
bgneal@45 7161
bgneal@45 7162 renderHTML : function() {
bgneal@45 7163 var cp = this.classPrefix, s = this.settings, h, l;
bgneal@45 7164
bgneal@45 7165 l = DOM.encode(s.label || '');
bgneal@45 7166 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) + '">';
bgneal@45 7167
bgneal@45 7168 if (s.image)
bgneal@45 7169 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
bgneal@45 7170 else
bgneal@45 7171 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';
bgneal@45 7172
bgneal@45 7173 return h;
bgneal@45 7174 },
bgneal@45 7175
bgneal@45 7176 postRender : function() {
bgneal@45 7177 var t = this, s = t.settings;
bgneal@45 7178
bgneal@45 7179 tinymce.dom.Event.add(t.id, 'click', function(e) {
bgneal@45 7180 if (!t.isDisabled())
bgneal@45 7181 return s.onclick.call(s.scope, e);
bgneal@45 7182 });
bgneal@45 7183 }
bgneal@45 7184
bgneal@45 7185 });
bgneal@45 7186 })(tinymce);
bgneal@45 7187 (function(tinymce) {
bgneal@45 7188 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
bgneal@45 7189
bgneal@45 7190 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
bgneal@45 7191 ListBox : function(id, s) {
bgneal@45 7192 var t = this;
bgneal@45 7193
bgneal@45 7194 t.parent(id, s);
bgneal@45 7195 t.items = [];
bgneal@45 7196 t.onChange = new Dispatcher(t);
bgneal@45 7197 t.onPostRender = new Dispatcher(t);
bgneal@45 7198 t.onAdd = new Dispatcher(t);
bgneal@45 7199 t.onRenderMenu = new tinymce.util.Dispatcher(this);
bgneal@45 7200 t.classPrefix = 'mceListBox';
bgneal@45 7201 },
bgneal@45 7202
bgneal@45 7203 select : function(va) {
bgneal@45 7204 var t = this, fv, f;
bgneal@45 7205
bgneal@45 7206 if (va == undefined)
bgneal@45 7207 return t.selectByIndex(-1);
bgneal@45 7208
bgneal@45 7209 // Is string or number make function selector
bgneal@45 7210 if (va && va.call)
bgneal@45 7211 f = va;
bgneal@45 7212 else {
bgneal@45 7213 f = function(v) {
bgneal@45 7214 return v == va;
bgneal@45 7215 };
bgneal@45 7216 }
bgneal@45 7217
bgneal@45 7218 // Do we need to do something?
bgneal@45 7219 if (va != t.selectedValue) {
bgneal@45 7220 // Find item
bgneal@45 7221 each(t.items, function(o, i) {
bgneal@45 7222 if (f(o.value)) {
bgneal@45 7223 fv = 1;
bgneal@45 7224 t.selectByIndex(i);
bgneal@45 7225 return false;
bgneal@45 7226 }
bgneal@45 7227 });
bgneal@45 7228
bgneal@45 7229 if (!fv)
bgneal@45 7230 t.selectByIndex(-1);
bgneal@45 7231 }
bgneal@45 7232 },
bgneal@45 7233
bgneal@45 7234 selectByIndex : function(idx) {
bgneal@45 7235 var t = this, e, o;
bgneal@45 7236
bgneal@45 7237 if (idx != t.selectedIndex) {
bgneal@45 7238 e = DOM.get(t.id + '_text');
bgneal@45 7239 o = t.items[idx];
bgneal@45 7240
bgneal@45 7241 if (o) {
bgneal@45 7242 t.selectedValue = o.value;
bgneal@45 7243 t.selectedIndex = idx;
bgneal@45 7244 DOM.setHTML(e, DOM.encode(o.title));
bgneal@45 7245 DOM.removeClass(e, 'mceTitle');
bgneal@45 7246 } else {
bgneal@45 7247 DOM.setHTML(e, DOM.encode(t.settings.title));
bgneal@45 7248 DOM.addClass(e, 'mceTitle');
bgneal@45 7249 t.selectedValue = t.selectedIndex = null;
bgneal@45 7250 }
bgneal@45 7251
bgneal@45 7252 e = 0;
bgneal@45 7253 }
bgneal@45 7254 },
bgneal@45 7255
bgneal@45 7256 add : function(n, v, o) {
bgneal@45 7257 var t = this;
bgneal@45 7258
bgneal@45 7259 o = o || {};
bgneal@45 7260 o = tinymce.extend(o, {
bgneal@45 7261 title : n,
bgneal@45 7262 value : v
bgneal@45 7263 });
bgneal@45 7264
bgneal@45 7265 t.items.push(o);
bgneal@45 7266 t.onAdd.dispatch(t, o);
bgneal@45 7267 },
bgneal@45 7268
bgneal@45 7269 getLength : function() {
bgneal@45 7270 return this.items.length;
bgneal@45 7271 },
bgneal@45 7272
bgneal@45 7273 renderHTML : function() {
bgneal@45 7274 var h = '', t = this, s = t.settings, cp = t.classPrefix;
bgneal@45 7275
bgneal@45 7276 h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
bgneal@45 7277 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>';
bgneal@45 7278 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';
bgneal@45 7279 h += '</tr></tbody></table>';
bgneal@45 7280
bgneal@45 7281 return h;
bgneal@45 7282 },
bgneal@45 7283
bgneal@45 7284 showMenu : function() {
bgneal@45 7285 var t = this, p1, p2, e = DOM.get(this.id), m;
bgneal@45 7286
bgneal@45 7287 if (t.isDisabled() || t.items.length == 0)
bgneal@45 7288 return;
bgneal@45 7289
bgneal@45 7290 if (t.menu && t.menu.isMenuVisible)
bgneal@45 7291 return t.hideMenu();
bgneal@45 7292
bgneal@45 7293 if (!t.isMenuRendered) {
bgneal@45 7294 t.renderMenu();
bgneal@45 7295 t.isMenuRendered = true;
bgneal@45 7296 }
bgneal@45 7297
bgneal@45 7298 p1 = DOM.getPos(this.settings.menu_container);
bgneal@45 7299 p2 = DOM.getPos(e);
bgneal@45 7300
bgneal@45 7301 m = t.menu;
bgneal@45 7302 m.settings.offset_x = p2.x;
bgneal@45 7303 m.settings.offset_y = p2.y;
bgneal@45 7304 m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
bgneal@45 7305
bgneal@45 7306 // Select in menu
bgneal@45 7307 if (t.oldID)
bgneal@45 7308 m.items[t.oldID].setSelected(0);
bgneal@45 7309
bgneal@45 7310 each(t.items, function(o) {
bgneal@45 7311 if (o.value === t.selectedValue) {
bgneal@45 7312 m.items[o.id].setSelected(1);
bgneal@45 7313 t.oldID = o.id;
bgneal@45 7314 }
bgneal@45 7315 });
bgneal@45 7316
bgneal@45 7317 m.showMenu(0, e.clientHeight);
bgneal@45 7318
bgneal@45 7319 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7320 DOM.addClass(t.id, t.classPrefix + 'Selected');
bgneal@45 7321
bgneal@45 7322 //DOM.get(t.id + '_text').focus();
bgneal@45 7323 },
bgneal@45 7324
bgneal@45 7325 hideMenu : function(e) {
bgneal@45 7326 var t = this;
bgneal@45 7327
bgneal@45 7328 // Prevent double toogles by canceling the mouse click event to the button
bgneal@45 7329 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
bgneal@45 7330 return;
bgneal@45 7331
bgneal@45 7332 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
bgneal@45 7333 DOM.removeClass(t.id, t.classPrefix + 'Selected');
bgneal@45 7334 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7335
bgneal@45 7336 if (t.menu)
bgneal@45 7337 t.menu.hideMenu();
bgneal@45 7338 }
bgneal@45 7339 },
bgneal@45 7340
bgneal@45 7341 renderMenu : function() {
bgneal@45 7342 var t = this, m;
bgneal@45 7343
bgneal@45 7344 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
bgneal@45 7345 menu_line : 1,
bgneal@45 7346 'class' : t.classPrefix + 'Menu mceNoIcons',
bgneal@45 7347 max_width : 150,
bgneal@45 7348 max_height : 150
bgneal@45 7349 });
bgneal@45 7350
bgneal@45 7351 m.onHideMenu.add(t.hideMenu, t);
bgneal@45 7352
bgneal@45 7353 m.add({
bgneal@45 7354 title : t.settings.title,
bgneal@45 7355 'class' : 'mceMenuItemTitle',
bgneal@45 7356 onclick : function() {
bgneal@45 7357 if (t.settings.onselect('') !== false)
bgneal@45 7358 t.select(''); // Must be runned after
bgneal@45 7359 }
bgneal@45 7360 });
bgneal@45 7361
bgneal@45 7362 each(t.items, function(o) {
bgneal@45 7363 o.id = DOM.uniqueId();
bgneal@45 7364 o.onclick = function() {
bgneal@45 7365 if (t.settings.onselect(o.value) !== false)
bgneal@45 7366 t.select(o.value); // Must be runned after
bgneal@45 7367 };
bgneal@45 7368
bgneal@45 7369 m.add(o);
bgneal@45 7370 });
bgneal@45 7371
bgneal@45 7372 t.onRenderMenu.dispatch(t, m);
bgneal@45 7373 t.menu = m;
bgneal@45 7374 },
bgneal@45 7375
bgneal@45 7376 postRender : function() {
bgneal@45 7377 var t = this, cp = t.classPrefix;
bgneal@45 7378
bgneal@45 7379 Event.add(t.id, 'click', t.showMenu, t);
bgneal@45 7380 Event.add(t.id + '_text', 'focus', function(e) {
bgneal@45 7381 if (!t._focused) {
bgneal@45 7382 t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
bgneal@45 7383 var idx = -1, v, kc = e.keyCode;
bgneal@45 7384
bgneal@45 7385 // Find current index
bgneal@45 7386 each(t.items, function(v, i) {
bgneal@45 7387 if (t.selectedValue == v.value)
bgneal@45 7388 idx = i;
bgneal@45 7389 });
bgneal@45 7390
bgneal@45 7391 // Move up/down
bgneal@45 7392 if (kc == 38)
bgneal@45 7393 v = t.items[idx - 1];
bgneal@45 7394 else if (kc == 40)
bgneal@45 7395 v = t.items[idx + 1];
bgneal@45 7396 else if (kc == 13) {
bgneal@45 7397 // Fake select on enter
bgneal@45 7398 v = t.selectedValue;
bgneal@45 7399 t.selectedValue = null; // Needs to be null to fake change
bgneal@45 7400 t.settings.onselect(v);
bgneal@45 7401 return Event.cancel(e);
bgneal@45 7402 }
bgneal@45 7403
bgneal@45 7404 if (v) {
bgneal@45 7405 t.hideMenu();
bgneal@45 7406 t.select(v.value);
bgneal@45 7407 }
bgneal@45 7408 });
bgneal@45 7409 }
bgneal@45 7410
bgneal@45 7411 t._focused = 1;
bgneal@45 7412 });
bgneal@45 7413 Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});
bgneal@45 7414
bgneal@45 7415 // Old IE doesn't have hover on all elements
bgneal@45 7416 if (tinymce.isIE6 || !DOM.boxModel) {
bgneal@45 7417 Event.add(t.id, 'mouseover', function() {
bgneal@45 7418 if (!DOM.hasClass(t.id, cp + 'Disabled'))
bgneal@45 7419 DOM.addClass(t.id, cp + 'Hover');
bgneal@45 7420 });
bgneal@45 7421
bgneal@45 7422 Event.add(t.id, 'mouseout', function() {
bgneal@45 7423 if (!DOM.hasClass(t.id, cp + 'Disabled'))
bgneal@45 7424 DOM.removeClass(t.id, cp + 'Hover');
bgneal@45 7425 });
bgneal@45 7426 }
bgneal@45 7427
bgneal@45 7428 t.onPostRender.dispatch(t, DOM.get(t.id));
bgneal@45 7429 },
bgneal@45 7430
bgneal@45 7431 destroy : function() {
bgneal@45 7432 this.parent();
bgneal@45 7433
bgneal@45 7434 Event.clear(this.id + '_text');
bgneal@45 7435 }
bgneal@45 7436
bgneal@45 7437 });
bgneal@45 7438 })(tinymce);(function(tinymce) {
bgneal@45 7439 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
bgneal@45 7440
bgneal@45 7441 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
bgneal@45 7442 NativeListBox : function(id, s) {
bgneal@45 7443 this.parent(id, s);
bgneal@45 7444 this.classPrefix = 'mceNativeListBox';
bgneal@45 7445 },
bgneal@45 7446
bgneal@45 7447 setDisabled : function(s) {
bgneal@45 7448 DOM.get(this.id).disabled = s;
bgneal@45 7449 },
bgneal@45 7450
bgneal@45 7451 isDisabled : function() {
bgneal@45 7452 return DOM.get(this.id).disabled;
bgneal@45 7453 },
bgneal@45 7454
bgneal@45 7455 select : function(va) {
bgneal@45 7456 var t = this, fv, f;
bgneal@45 7457
bgneal@45 7458 if (va == undefined)
bgneal@45 7459 return t.selectByIndex(-1);
bgneal@45 7460
bgneal@45 7461 // Is string or number make function selector
bgneal@45 7462 if (va && va.call)
bgneal@45 7463 f = va;
bgneal@45 7464 else {
bgneal@45 7465 f = function(v) {
bgneal@45 7466 return v == va;
bgneal@45 7467 };
bgneal@45 7468 }
bgneal@45 7469
bgneal@45 7470 // Do we need to do something?
bgneal@45 7471 if (va != t.selectedValue) {
bgneal@45 7472 // Find item
bgneal@45 7473 each(t.items, function(o, i) {
bgneal@45 7474 if (f(o.value)) {
bgneal@45 7475 fv = 1;
bgneal@45 7476 t.selectByIndex(i);
bgneal@45 7477 return false;
bgneal@45 7478 }
bgneal@45 7479 });
bgneal@45 7480
bgneal@45 7481 if (!fv)
bgneal@45 7482 t.selectByIndex(-1);
bgneal@45 7483 }
bgneal@45 7484 },
bgneal@45 7485
bgneal@45 7486 selectByIndex : function(idx) {
bgneal@45 7487 DOM.get(this.id).selectedIndex = idx + 1;
bgneal@45 7488 this.selectedValue = this.items[idx] ? this.items[idx].value : null;
bgneal@45 7489 },
bgneal@45 7490
bgneal@45 7491 add : function(n, v, a) {
bgneal@45 7492 var o, t = this;
bgneal@45 7493
bgneal@45 7494 a = a || {};
bgneal@45 7495 a.value = v;
bgneal@45 7496
bgneal@45 7497 if (t.isRendered())
bgneal@45 7498 DOM.add(DOM.get(this.id), 'option', a, n);
bgneal@45 7499
bgneal@45 7500 o = {
bgneal@45 7501 title : n,
bgneal@45 7502 value : v,
bgneal@45 7503 attribs : a
bgneal@45 7504 };
bgneal@45 7505
bgneal@45 7506 t.items.push(o);
bgneal@45 7507 t.onAdd.dispatch(t, o);
bgneal@45 7508 },
bgneal@45 7509
bgneal@45 7510 getLength : function() {
bgneal@45 7511 return DOM.get(this.id).options.length - 1;
bgneal@45 7512 },
bgneal@45 7513
bgneal@45 7514 renderHTML : function() {
bgneal@45 7515 var h, t = this;
bgneal@45 7516
bgneal@45 7517 h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');
bgneal@45 7518
bgneal@45 7519 each(t.items, function(it) {
bgneal@45 7520 h += DOM.createHTML('option', {value : it.value}, it.title);
bgneal@45 7521 });
bgneal@45 7522
bgneal@45 7523 h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);
bgneal@45 7524
bgneal@45 7525 return h;
bgneal@45 7526 },
bgneal@45 7527
bgneal@45 7528 postRender : function() {
bgneal@45 7529 var t = this, ch;
bgneal@45 7530
bgneal@45 7531 t.rendered = true;
bgneal@45 7532
bgneal@45 7533 function onChange(e) {
bgneal@45 7534 var v = t.items[e.target.selectedIndex - 1];
bgneal@45 7535
bgneal@45 7536 if (v && (v = v.value)) {
bgneal@45 7537 t.onChange.dispatch(t, v);
bgneal@45 7538
bgneal@45 7539 if (t.settings.onselect)
bgneal@45 7540 t.settings.onselect(v);
bgneal@45 7541 }
bgneal@45 7542 };
bgneal@45 7543
bgneal@45 7544 Event.add(t.id, 'change', onChange);
bgneal@45 7545
bgneal@45 7546 // Accessibility keyhandler
bgneal@45 7547 Event.add(t.id, 'keydown', function(e) {
bgneal@45 7548 var bf;
bgneal@45 7549
bgneal@45 7550 Event.remove(t.id, 'change', ch);
bgneal@45 7551
bgneal@45 7552 bf = Event.add(t.id, 'blur', function() {
bgneal@45 7553 Event.add(t.id, 'change', onChange);
bgneal@45 7554 Event.remove(t.id, 'blur', bf);
bgneal@45 7555 });
bgneal@45 7556
bgneal@45 7557 if (e.keyCode == 13 || e.keyCode == 32) {
bgneal@45 7558 onChange(e);
bgneal@45 7559 return Event.cancel(e);
bgneal@45 7560 }
bgneal@45 7561 });
bgneal@45 7562
bgneal@45 7563 t.onPostRender.dispatch(t, DOM.get(t.id));
bgneal@45 7564 }
bgneal@45 7565
bgneal@45 7566 });
bgneal@45 7567 })(tinymce);(function(tinymce) {
bgneal@45 7568 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
bgneal@45 7569
bgneal@45 7570 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
bgneal@45 7571 MenuButton : function(id, s) {
bgneal@45 7572 this.parent(id, s);
bgneal@45 7573 this.onRenderMenu = new tinymce.util.Dispatcher(this);
bgneal@45 7574 s.menu_container = s.menu_container || DOM.doc.body;
bgneal@45 7575 },
bgneal@45 7576
bgneal@45 7577 showMenu : function() {
bgneal@45 7578 var t = this, p1, p2, e = DOM.get(t.id), m;
bgneal@45 7579
bgneal@45 7580 if (t.isDisabled())
bgneal@45 7581 return;
bgneal@45 7582
bgneal@45 7583 if (!t.isMenuRendered) {
bgneal@45 7584 t.renderMenu();
bgneal@45 7585 t.isMenuRendered = true;
bgneal@45 7586 }
bgneal@45 7587
bgneal@45 7588 if (t.isMenuVisible)
bgneal@45 7589 return t.hideMenu();
bgneal@45 7590
bgneal@45 7591 p1 = DOM.getPos(t.settings.menu_container);
bgneal@45 7592 p2 = DOM.getPos(e);
bgneal@45 7593
bgneal@45 7594 m = t.menu;
bgneal@45 7595 m.settings.offset_x = p2.x;
bgneal@45 7596 m.settings.offset_y = p2.y;
bgneal@45 7597 m.settings.vp_offset_x = p2.x;
bgneal@45 7598 m.settings.vp_offset_y = p2.y;
bgneal@45 7599 m.settings.keyboard_focus = t._focused;
bgneal@45 7600 m.showMenu(0, e.clientHeight);
bgneal@45 7601
bgneal@45 7602 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7603 t.setState('Selected', 1);
bgneal@45 7604
bgneal@45 7605 t.isMenuVisible = 1;
bgneal@45 7606 },
bgneal@45 7607
bgneal@45 7608 renderMenu : function() {
bgneal@45 7609 var t = this, m;
bgneal@45 7610
bgneal@45 7611 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
bgneal@45 7612 menu_line : 1,
bgneal@45 7613 'class' : this.classPrefix + 'Menu',
bgneal@45 7614 icons : t.settings.icons
bgneal@45 7615 });
bgneal@45 7616
bgneal@45 7617 m.onHideMenu.add(t.hideMenu, t);
bgneal@45 7618
bgneal@45 7619 t.onRenderMenu.dispatch(t, m);
bgneal@45 7620 t.menu = m;
bgneal@45 7621 },
bgneal@45 7622
bgneal@45 7623 hideMenu : function(e) {
bgneal@45 7624 var t = this;
bgneal@45 7625
bgneal@45 7626 // Prevent double toogles by canceling the mouse click event to the button
bgneal@45 7627 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
bgneal@45 7628 return;
bgneal@45 7629
bgneal@45 7630 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
bgneal@45 7631 t.setState('Selected', 0);
bgneal@45 7632 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7633 if (t.menu)
bgneal@45 7634 t.menu.hideMenu();
bgneal@45 7635 }
bgneal@45 7636
bgneal@45 7637 t.isMenuVisible = 0;
bgneal@45 7638 },
bgneal@45 7639
bgneal@45 7640 postRender : function() {
bgneal@45 7641 var t = this, s = t.settings;
bgneal@45 7642
bgneal@45 7643 Event.add(t.id, 'click', function() {
bgneal@45 7644 if (!t.isDisabled()) {
bgneal@45 7645 if (s.onclick)
bgneal@45 7646 s.onclick(t.value);
bgneal@45 7647
bgneal@45 7648 t.showMenu();
bgneal@45 7649 }
bgneal@45 7650 });
bgneal@45 7651 }
bgneal@45 7652
bgneal@45 7653 });
bgneal@45 7654 })(tinymce);
bgneal@45 7655 (function(tinymce) {
bgneal@45 7656 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
bgneal@45 7657
bgneal@45 7658 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
bgneal@45 7659 SplitButton : function(id, s) {
bgneal@45 7660 this.parent(id, s);
bgneal@45 7661 this.classPrefix = 'mceSplitButton';
bgneal@45 7662 },
bgneal@45 7663
bgneal@45 7664 renderHTML : function() {
bgneal@45 7665 var h, t = this, s = t.settings, h1;
bgneal@45 7666
bgneal@45 7667 h = '<tbody><tr>';
bgneal@45 7668
bgneal@45 7669 if (s.image)
bgneal@45 7670 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
bgneal@45 7671 else
bgneal@45 7672 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');
bgneal@45 7673
bgneal@45 7674 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>';
bgneal@45 7675
bgneal@45 7676 h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});
bgneal@45 7677 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>';
bgneal@45 7678
bgneal@45 7679 h += '</tr></tbody>';
bgneal@45 7680
bgneal@45 7681 return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);
bgneal@45 7682 },
bgneal@45 7683
bgneal@45 7684 postRender : function() {
bgneal@45 7685 var t = this, s = t.settings;
bgneal@45 7686
bgneal@45 7687 if (s.onclick) {
bgneal@45 7688 Event.add(t.id + '_action', 'click', function() {
bgneal@45 7689 if (!t.isDisabled())
bgneal@45 7690 s.onclick(t.value);
bgneal@45 7691 });
bgneal@45 7692 }
bgneal@45 7693
bgneal@45 7694 Event.add(t.id + '_open', 'click', t.showMenu, t);
bgneal@45 7695 Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});
bgneal@45 7696 Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});
bgneal@45 7697
bgneal@45 7698 // Old IE doesn't have hover on all elements
bgneal@45 7699 if (tinymce.isIE6 || !DOM.boxModel) {
bgneal@45 7700 Event.add(t.id, 'mouseover', function() {
bgneal@45 7701 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
bgneal@45 7702 DOM.addClass(t.id, 'mceSplitButtonHover');
bgneal@45 7703 });
bgneal@45 7704
bgneal@45 7705 Event.add(t.id, 'mouseout', function() {
bgneal@45 7706 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
bgneal@45 7707 DOM.removeClass(t.id, 'mceSplitButtonHover');
bgneal@45 7708 });
bgneal@45 7709 }
bgneal@45 7710 },
bgneal@45 7711
bgneal@45 7712 destroy : function() {
bgneal@45 7713 this.parent();
bgneal@45 7714
bgneal@45 7715 Event.clear(this.id + '_action');
bgneal@45 7716 Event.clear(this.id + '_open');
bgneal@45 7717 }
bgneal@45 7718
bgneal@45 7719 });
bgneal@45 7720 })(tinymce);
bgneal@45 7721 (function(tinymce) {
bgneal@45 7722 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;
bgneal@45 7723
bgneal@45 7724 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
bgneal@45 7725 ColorSplitButton : function(id, s) {
bgneal@45 7726 var t = this;
bgneal@45 7727
bgneal@45 7728 t.parent(id, s);
bgneal@45 7729
bgneal@45 7730 t.settings = s = tinymce.extend({
bgneal@45 7731 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',
bgneal@45 7732 grid_width : 8,
bgneal@45 7733 default_color : '#888888'
bgneal@45 7734 }, t.settings);
bgneal@45 7735
bgneal@45 7736 t.onShowMenu = new tinymce.util.Dispatcher(t);
bgneal@45 7737 t.onHideMenu = new tinymce.util.Dispatcher(t);
bgneal@45 7738
bgneal@45 7739 t.value = s.default_color;
bgneal@45 7740 },
bgneal@45 7741
bgneal@45 7742 showMenu : function() {
bgneal@45 7743 var t = this, r, p, e, p2;
bgneal@45 7744
bgneal@45 7745 if (t.isDisabled())
bgneal@45 7746 return;
bgneal@45 7747
bgneal@45 7748 if (!t.isMenuRendered) {
bgneal@45 7749 t.renderMenu();
bgneal@45 7750 t.isMenuRendered = true;
bgneal@45 7751 }
bgneal@45 7752
bgneal@45 7753 if (t.isMenuVisible)
bgneal@45 7754 return t.hideMenu();
bgneal@45 7755
bgneal@45 7756 e = DOM.get(t.id);
bgneal@45 7757 DOM.show(t.id + '_menu');
bgneal@45 7758 DOM.addClass(e, 'mceSplitButtonSelected');
bgneal@45 7759 p2 = DOM.getPos(e);
bgneal@45 7760 DOM.setStyles(t.id + '_menu', {
bgneal@45 7761 left : p2.x,
bgneal@45 7762 top : p2.y + e.clientHeight,
bgneal@45 7763 zIndex : 200000
bgneal@45 7764 });
bgneal@45 7765 e = 0;
bgneal@45 7766
bgneal@45 7767 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7768
bgneal@45 7769 if (t._focused) {
bgneal@45 7770 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
bgneal@45 7771 if (e.keyCode == 27)
bgneal@45 7772 t.hideMenu();
bgneal@45 7773 });
bgneal@45 7774
bgneal@45 7775 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
bgneal@45 7776 }
bgneal@45 7777
bgneal@45 7778 t.onShowMenu.dispatch(t);
bgneal@45 7779
bgneal@45 7780 t.isMenuVisible = 1;
bgneal@45 7781 },
bgneal@45 7782
bgneal@45 7783 hideMenu : function(e) {
bgneal@45 7784 var t = this;
bgneal@45 7785
bgneal@45 7786 // Prevent double toogles by canceling the mouse click event to the button
bgneal@45 7787 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
bgneal@45 7788 return;
bgneal@45 7789
bgneal@45 7790 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
bgneal@45 7791 DOM.removeClass(t.id, 'mceSplitButtonSelected');
bgneal@45 7792 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
bgneal@45 7793 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
bgneal@45 7794 DOM.hide(t.id + '_menu');
bgneal@45 7795 }
bgneal@45 7796
bgneal@45 7797 t.onHideMenu.dispatch(t);
bgneal@45 7798
bgneal@45 7799 t.isMenuVisible = 0;
bgneal@45 7800 },
bgneal@45 7801
bgneal@45 7802 renderMenu : function() {
bgneal@45 7803 var t = this, m, i = 0, s = t.settings, n, tb, tr, w;
bgneal@45 7804
bgneal@45 7805 w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
bgneal@45 7806 m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
bgneal@45 7807 DOM.add(m, 'span', {'class' : 'mceMenuLine'});
bgneal@45 7808
bgneal@45 7809 n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});
bgneal@45 7810 tb = DOM.add(n, 'tbody');
bgneal@45 7811
bgneal@45 7812 // Generate color grid
bgneal@45 7813 i = 0;
bgneal@45 7814 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
bgneal@45 7815 c = c.replace(/^#/, '');
bgneal@45 7816
bgneal@45 7817 if (!i--) {
bgneal@45 7818 tr = DOM.add(tb, 'tr');
bgneal@45 7819 i = s.grid_width - 1;
bgneal@45 7820 }
bgneal@45 7821
bgneal@45 7822 n = DOM.add(tr, 'td');
bgneal@45 7823
bgneal@45 7824 n = DOM.add(n, 'a', {
bgneal@45 7825 href : 'javascript:;',
bgneal@45 7826 style : {
bgneal@45 7827 backgroundColor : '#' + c
bgneal@45 7828 },
bgneal@45 7829 mce_color : '#' + c
bgneal@45 7830 });
bgneal@45 7831 });
bgneal@45 7832
bgneal@45 7833 if (s.more_colors_func) {
bgneal@45 7834 n = DOM.add(tb, 'tr');
bgneal@45 7835 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
bgneal@45 7836 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);
bgneal@45 7837
bgneal@45 7838 Event.add(n, 'click', function(e) {
bgneal@45 7839 s.more_colors_func.call(s.more_colors_scope || this);
bgneal@45 7840 return Event.cancel(e); // Cancel to fix onbeforeunload problem
bgneal@45 7841 });
bgneal@45 7842 }
bgneal@45 7843
bgneal@45 7844 DOM.addClass(m, 'mceColorSplitMenu');
bgneal@45 7845
bgneal@45 7846 Event.add(t.id + '_menu', 'click', function(e) {
bgneal@45 7847 var c;
bgneal@45 7848
bgneal@45 7849 e = e.target;
bgneal@45 7850
bgneal@45 7851 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))
bgneal@45 7852 t.setColor(c);
bgneal@45 7853
bgneal@45 7854 return Event.cancel(e); // Prevent IE auto save warning
bgneal@45 7855 });
bgneal@45 7856
bgneal@45 7857 return w;
bgneal@45 7858 },
bgneal@45 7859
bgneal@45 7860 setColor : function(c) {
bgneal@45 7861 var t = this;
bgneal@45 7862
bgneal@45 7863 DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
bgneal@45 7864
bgneal@45 7865 t.value = c;
bgneal@45 7866 t.hideMenu();
bgneal@45 7867 t.settings.onselect(c);
bgneal@45 7868 },
bgneal@45 7869
bgneal@45 7870 postRender : function() {
bgneal@45 7871 var t = this, id = t.id;
bgneal@45 7872
bgneal@45 7873 t.parent();
bgneal@45 7874 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
bgneal@45 7875 DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);
bgneal@45 7876 },
bgneal@45 7877
bgneal@45 7878 destroy : function() {
bgneal@45 7879 this.parent();
bgneal@45 7880
bgneal@45 7881 Event.clear(this.id + '_menu');
bgneal@45 7882 Event.clear(this.id + '_more');
bgneal@45 7883 DOM.remove(this.id + '_menu');
bgneal@45 7884 }
bgneal@45 7885
bgneal@45 7886 });
bgneal@45 7887 })(tinymce);
bgneal@45 7888 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
bgneal@45 7889 renderHTML : function() {
bgneal@45 7890 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;
bgneal@45 7891
bgneal@45 7892 cl = t.controls;
bgneal@45 7893 for (i=0; i<cl.length; i++) {
bgneal@45 7894 // Get current control, prev control, next control and if the control is a list box or not
bgneal@45 7895 co = cl[i];
bgneal@45 7896 pr = cl[i - 1];
bgneal@45 7897 nx = cl[i + 1];
bgneal@45 7898
bgneal@45 7899 // Add toolbar start
bgneal@45 7900 if (i === 0) {
bgneal@45 7901 c = 'mceToolbarStart';
bgneal@45 7902
bgneal@45 7903 if (co.Button)
bgneal@45 7904 c += ' mceToolbarStartButton';
bgneal@45 7905 else if (co.SplitButton)
bgneal@45 7906 c += ' mceToolbarStartSplitButton';
bgneal@45 7907 else if (co.ListBox)
bgneal@45 7908 c += ' mceToolbarStartListBox';
bgneal@45 7909
bgneal@45 7910 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
bgneal@45 7911 }
bgneal@45 7912
bgneal@45 7913 // Add toolbar end before list box and after the previous button
bgneal@45 7914 // This is to fix the o2k7 editor skins
bgneal@45 7915 if (pr && co.ListBox) {
bgneal@45 7916 if (pr.Button || pr.SplitButton)
bgneal@45 7917 h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
bgneal@45 7918 }
bgneal@45 7919
bgneal@45 7920 // Render control HTML
bgneal@45 7921
bgneal@45 7922 // IE 8 quick fix, needed to propertly generate a hit area for anchors
bgneal@45 7923 if (dom.stdMode)
bgneal@45 7924 h += '<td style="position: relative">' + co.renderHTML() + '</td>';
bgneal@45 7925 else
bgneal@45 7926 h += '<td>' + co.renderHTML() + '</td>';
bgneal@45 7927
bgneal@45 7928 // Add toolbar start after list box and before the next button
bgneal@45 7929 // This is to fix the o2k7 editor skins
bgneal@45 7930 if (nx && co.ListBox) {
bgneal@45 7931 if (nx.Button || nx.SplitButton)
bgneal@45 7932 h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
bgneal@45 7933 }
bgneal@45 7934 }
bgneal@45 7935
bgneal@45 7936 c = 'mceToolbarEnd';
bgneal@45 7937
bgneal@45 7938 if (co.Button)
bgneal@45 7939 c += ' mceToolbarEndButton';
bgneal@45 7940 else if (co.SplitButton)
bgneal@45 7941 c += ' mceToolbarEndSplitButton';
bgneal@45 7942 else if (co.ListBox)
bgneal@45 7943 c += ' mceToolbarEndListBox';
bgneal@45 7944
bgneal@45 7945 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
bgneal@45 7946
bgneal@45 7947 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>');
bgneal@45 7948 }
bgneal@45 7949
bgneal@45 7950 });
bgneal@45 7951 (function(tinymce) {
bgneal@45 7952 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
bgneal@45 7953
bgneal@45 7954 tinymce.create('tinymce.AddOnManager', {
bgneal@45 7955 items : [],
bgneal@45 7956 urls : {},
bgneal@45 7957 lookup : {},
bgneal@45 7958 onAdd : new Dispatcher(this),
bgneal@45 7959
bgneal@45 7960 get : function(n) {
bgneal@45 7961 return this.lookup[n];
bgneal@45 7962 },
bgneal@45 7963
bgneal@45 7964 requireLangPack : function(n) {
bgneal@45 7965 var u, s = tinymce.EditorManager.settings;
bgneal@45 7966
bgneal@45 7967 if (s && s.language) {
bgneal@45 7968 u = this.urls[n] + '/langs/' + s.language + '.js';
bgneal@45 7969
bgneal@45 7970 if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
bgneal@45 7971 tinymce.ScriptLoader.load(u);
bgneal@45 7972 else
bgneal@45 7973 tinymce.ScriptLoader.add(u);
bgneal@45 7974 }
bgneal@45 7975 },
bgneal@45 7976
bgneal@45 7977 add : function(id, o) {
bgneal@45 7978 this.items.push(o);
bgneal@45 7979 this.lookup[id] = o;
bgneal@45 7980 this.onAdd.dispatch(this, id, o);
bgneal@45 7981
bgneal@45 7982 return o;
bgneal@45 7983 },
bgneal@45 7984
bgneal@45 7985 load : function(n, u, cb, s) {
bgneal@45 7986 var t = this;
bgneal@45 7987
bgneal@45 7988 if (t.urls[n])
bgneal@45 7989 return;
bgneal@45 7990
bgneal@45 7991 if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
bgneal@45 7992 u = tinymce.baseURL + '/' + u;
bgneal@45 7993
bgneal@45 7994 t.urls[n] = u.substring(0, u.lastIndexOf('/'));
bgneal@45 7995 tinymce.ScriptLoader.add(u, cb, s);
bgneal@45 7996 }
bgneal@45 7997
bgneal@45 7998 });
bgneal@45 7999
bgneal@45 8000 // Create plugin and theme managers
bgneal@45 8001 tinymce.PluginManager = new tinymce.AddOnManager();
bgneal@45 8002 tinymce.ThemeManager = new tinymce.AddOnManager();
bgneal@45 8003 }(tinymce));(function(tinymce) {
bgneal@45 8004 // Shorten names
bgneal@45 8005 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;
bgneal@45 8006
bgneal@45 8007 tinymce.create('static tinymce.EditorManager', {
bgneal@45 8008 editors : {},
bgneal@45 8009 i18n : {},
bgneal@45 8010 activeEditor : null,
bgneal@45 8011
bgneal@45 8012 preInit : function() {
bgneal@45 8013 var t = this, lo = window.location;
bgneal@45 8014
bgneal@45 8015 // Setup some URLs where the editor API is located and where the document is
bgneal@45 8016 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
bgneal@45 8017 if (!/[\/\\]$/.test(tinymce.documentBaseURL))
bgneal@45 8018 tinymce.documentBaseURL += '/';
bgneal@45 8019
bgneal@45 8020 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
bgneal@45 8021 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);
bgneal@45 8022
bgneal@45 8023 // User specified a document.domain value
bgneal@45 8024 if (document.domain && lo.hostname != document.domain)
bgneal@45 8025 tinymce.relaxedDomain = document.domain;
bgneal@45 8026
bgneal@45 8027 // Add before unload listener
bgneal@45 8028 // This was required since IE was leaking memory if you added and removed beforeunload listeners
bgneal@45 8029 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
bgneal@45 8030 t.onBeforeUnload = new tinymce.util.Dispatcher(t);
bgneal@45 8031
bgneal@45 8032 // Must be on window or IE will leak if the editor is placed in frame or iframe
bgneal@45 8033 Event.add(window, 'beforeunload', function(e) {
bgneal@45 8034 t.onBeforeUnload.dispatch(t, e);
bgneal@45 8035 });
bgneal@45 8036 },
bgneal@45 8037
bgneal@45 8038 init : function(s) {
bgneal@45 8039 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;
bgneal@45 8040
bgneal@45 8041 function execCallback(se, n, s) {
bgneal@45 8042 var f = se[n];
bgneal@45 8043
bgneal@45 8044 if (!f)
bgneal@45 8045 return;
bgneal@45 8046
bgneal@45 8047 if (tinymce.is(f, 'string')) {
bgneal@45 8048 s = f.replace(/\.\w+$/, '');
bgneal@45 8049 s = s ? tinymce.resolve(s) : 0;
bgneal@45 8050 f = tinymce.resolve(f);
bgneal@45 8051 }
bgneal@45 8052
bgneal@45 8053 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
bgneal@45 8054 };
bgneal@45 8055
bgneal@45 8056 s = extend({
bgneal@45 8057 theme : "simple",
bgneal@45 8058 language : "en",
bgneal@45 8059 strict_loading_mode : document.contentType == 'application/xhtml+xml'
bgneal@45 8060 }, s);
bgneal@45 8061
bgneal@45 8062 t.settings = s;
bgneal@45 8063
bgneal@45 8064 // If page not loaded and strict mode isn't enabled then load them
bgneal@45 8065 if (!Event.domLoaded && !s.strict_loading_mode) {
bgneal@45 8066 // Load language
bgneal@45 8067 if (s.language)
bgneal@45 8068 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
bgneal@45 8069
bgneal@45 8070 // Load theme
bgneal@45 8071 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
bgneal@45 8072 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
bgneal@45 8073
bgneal@45 8074 // Load plugins
bgneal@45 8075 if (s.plugins) {
bgneal@45 8076 pl = explode(s.plugins);
bgneal@45 8077
bgneal@45 8078 // Load compat2x first
bgneal@45 8079 if (tinymce.inArray(pl, 'compat2x') != -1)
bgneal@45 8080 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
bgneal@45 8081
bgneal@45 8082 // Load rest if plugins
bgneal@45 8083 each(pl, function(v) {
bgneal@45 8084 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
bgneal@45 8085 // Skip safari plugin for other browsers
bgneal@45 8086 if (!tinymce.isWebKit && v == 'safari')
bgneal@45 8087 return;
bgneal@45 8088
bgneal@45 8089 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
bgneal@45 8090 }
bgneal@45 8091 });
bgneal@45 8092 }
bgneal@45 8093
bgneal@45 8094 sl.loadQueue();
bgneal@45 8095 }
bgneal@45 8096
bgneal@45 8097 // Legacy call
bgneal@45 8098 Event.add(document, 'init', function() {
bgneal@45 8099 var l, co;
bgneal@45 8100
bgneal@45 8101 execCallback(s, 'onpageload');
bgneal@45 8102
bgneal@45 8103 // Verify that it's a valid browser
bgneal@45 8104 if (s.browsers) {
bgneal@45 8105 l = false;
bgneal@45 8106
bgneal@45 8107 each(explode(s.browsers), function(v) {
bgneal@45 8108 switch (v) {
bgneal@45 8109 case 'ie':
bgneal@45 8110 case 'msie':
bgneal@45 8111 if (tinymce.isIE)
bgneal@45 8112 l = true;
bgneal@45 8113 break;
bgneal@45 8114
bgneal@45 8115 case 'gecko':
bgneal@45 8116 if (tinymce.isGecko)
bgneal@45 8117 l = true;
bgneal@45 8118 break;
bgneal@45 8119
bgneal@45 8120 case 'safari':
bgneal@45 8121 case 'webkit':
bgneal@45 8122 if (tinymce.isWebKit)
bgneal@45 8123 l = true;
bgneal@45 8124 break;
bgneal@45 8125
bgneal@45 8126 case 'opera':
bgneal@45 8127 if (tinymce.isOpera)
bgneal@45 8128 l = true;
bgneal@45 8129
bgneal@45 8130 break;
bgneal@45 8131 }
bgneal@45 8132 });
bgneal@45 8133
bgneal@45 8134 // Not a valid one
bgneal@45 8135 if (!l)
bgneal@45 8136 return;
bgneal@45 8137 }
bgneal@45 8138
bgneal@45 8139 switch (s.mode) {
bgneal@45 8140 case "exact":
bgneal@45 8141 l = s.elements || '';
bgneal@45 8142
bgneal@45 8143 if(l.length > 0) {
bgneal@45 8144 each(explode(l), function(v) {
bgneal@45 8145 if (DOM.get(v)) {
bgneal@45 8146 ed = new tinymce.Editor(v, s);
bgneal@45 8147 el.push(ed);
bgneal@45 8148 ed.render(1);
bgneal@45 8149 } else {
bgneal@45 8150 c = 0;
bgneal@45 8151
bgneal@45 8152 each(document.forms, function(f) {
bgneal@45 8153 each(f.elements, function(e) {
bgneal@45 8154 if (e.name === v) {
bgneal@45 8155 v = 'mce_editor_' + c;
bgneal@45 8156 DOM.setAttrib(e, 'id', v);
bgneal@45 8157
bgneal@45 8158 ed = new tinymce.Editor(v, s);
bgneal@45 8159 el.push(ed);
bgneal@45 8160 ed.render(1);
bgneal@45 8161 }
bgneal@45 8162 });
bgneal@45 8163 });
bgneal@45 8164 }
bgneal@45 8165 });
bgneal@45 8166 }
bgneal@45 8167 break;
bgneal@45 8168
bgneal@45 8169 case "textareas":
bgneal@45 8170 case "specific_textareas":
bgneal@45 8171 function hasClass(n, c) {
bgneal@45 8172 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
bgneal@45 8173 };
bgneal@45 8174
bgneal@45 8175 each(DOM.select('textarea'), function(v) {
bgneal@45 8176 if (s.editor_deselector && hasClass(v, s.editor_deselector))
bgneal@45 8177 return;
bgneal@45 8178
bgneal@45 8179 if (!s.editor_selector || hasClass(v, s.editor_selector)) {
bgneal@45 8180 // Can we use the name
bgneal@45 8181 e = DOM.get(v.name);
bgneal@45 8182 if (!v.id && !e)
bgneal@45 8183 v.id = v.name;
bgneal@45 8184
bgneal@45 8185 // Generate unique name if missing or already exists
bgneal@45 8186 if (!v.id || t.get(v.id))
bgneal@45 8187 v.id = DOM.uniqueId();
bgneal@45 8188
bgneal@45 8189 ed = new tinymce.Editor(v.id, s);
bgneal@45 8190 el.push(ed);
bgneal@45 8191 ed.render(1);
bgneal@45 8192 }
bgneal@45 8193 });
bgneal@45 8194 break;
bgneal@45 8195 }
bgneal@45 8196
bgneal@45 8197 // Call onInit when all editors are initialized
bgneal@45 8198 if (s.oninit) {
bgneal@45 8199 l = co = 0;
bgneal@45 8200
bgneal@45 8201 each (el, function(ed) {
bgneal@45 8202 co++;
bgneal@45 8203
bgneal@45 8204 if (!ed.initialized) {
bgneal@45 8205 // Wait for it
bgneal@45 8206 ed.onInit.add(function() {
bgneal@45 8207 l++;
bgneal@45 8208
bgneal@45 8209 // All done
bgneal@45 8210 if (l == co)
bgneal@45 8211 execCallback(s, 'oninit');
bgneal@45 8212 });
bgneal@45 8213 } else
bgneal@45 8214 l++;
bgneal@45 8215
bgneal@45 8216 // All done
bgneal@45 8217 if (l == co)
bgneal@45 8218 execCallback(s, 'oninit');
bgneal@45 8219 });
bgneal@45 8220 }
bgneal@45 8221 });
bgneal@45 8222 },
bgneal@45 8223
bgneal@45 8224 get : function(id) {
bgneal@45 8225 return this.editors[id];
bgneal@45 8226 },
bgneal@45 8227
bgneal@45 8228 getInstanceById : function(id) {
bgneal@45 8229 return this.get(id);
bgneal@45 8230 },
bgneal@45 8231
bgneal@45 8232 add : function(e) {
bgneal@45 8233 this.editors[e.id] = e;
bgneal@45 8234 this._setActive(e);
bgneal@45 8235
bgneal@45 8236 return e;
bgneal@45 8237 },
bgneal@45 8238
bgneal@45 8239 remove : function(e) {
bgneal@45 8240 var t = this;
bgneal@45 8241
bgneal@45 8242 // Not in the collection
bgneal@45 8243 if (!t.editors[e.id])
bgneal@45 8244 return null;
bgneal@45 8245
bgneal@45 8246 delete t.editors[e.id];
bgneal@45 8247
bgneal@45 8248 // Select another editor since the active one was removed
bgneal@45 8249 if (t.activeEditor == e) {
bgneal@45 8250 each(t.editors, function(e) {
bgneal@45 8251 t._setActive(e);
bgneal@45 8252 return false; // Break
bgneal@45 8253 });
bgneal@45 8254 }
bgneal@45 8255
bgneal@45 8256 e.destroy();
bgneal@45 8257
bgneal@45 8258 return e;
bgneal@45 8259 },
bgneal@45 8260
bgneal@45 8261 execCommand : function(c, u, v) {
bgneal@45 8262 var t = this, ed = t.get(v), w;
bgneal@45 8263
bgneal@45 8264 // Manager commands
bgneal@45 8265 switch (c) {
bgneal@45 8266 case "mceFocus":
bgneal@45 8267 ed.focus();
bgneal@45 8268 return true;
bgneal@45 8269
bgneal@45 8270 case "mceAddEditor":
bgneal@45 8271 case "mceAddControl":
bgneal@45 8272 if (!t.get(v))
bgneal@45 8273 new tinymce.Editor(v, t.settings).render();
bgneal@45 8274
bgneal@45 8275 return true;
bgneal@45 8276
bgneal@45 8277 case "mceAddFrameControl":
bgneal@45 8278 w = v.window;
bgneal@45 8279
bgneal@45 8280 // Add tinyMCE global instance and tinymce namespace to specified window
bgneal@45 8281 w.tinyMCE = tinyMCE;
bgneal@45 8282 w.tinymce = tinymce;
bgneal@45 8283
bgneal@45 8284 tinymce.DOM.doc = w.document;
bgneal@45 8285 tinymce.DOM.win = w;
bgneal@45 8286
bgneal@45 8287 ed = new tinymce.Editor(v.element_id, v);
bgneal@45 8288 ed.render();
bgneal@45 8289
bgneal@45 8290 // Fix IE memory leaks
bgneal@45 8291 if (tinymce.isIE) {
bgneal@45 8292 function clr() {
bgneal@45 8293 ed.destroy();
bgneal@45 8294 w.detachEvent('onunload', clr);
bgneal@45 8295 w = w.tinyMCE = w.tinymce = null; // IE leak
bgneal@45 8296 };
bgneal@45 8297
bgneal@45 8298 w.attachEvent('onunload', clr);
bgneal@45 8299 }
bgneal@45 8300
bgneal@45 8301 v.page_window = null;
bgneal@45 8302
bgneal@45 8303 return true;
bgneal@45 8304
bgneal@45 8305 case "mceRemoveEditor":
bgneal@45 8306 case "mceRemoveControl":
bgneal@45 8307 if (ed)
bgneal@45 8308 ed.remove();
bgneal@45 8309
bgneal@45 8310 return true;
bgneal@45 8311
bgneal@45 8312 case 'mceToggleEditor':
bgneal@45 8313 if (!ed) {
bgneal@45 8314 t.execCommand('mceAddControl', 0, v);
bgneal@45 8315 return true;
bgneal@45 8316 }
bgneal@45 8317
bgneal@45 8318 if (ed.isHidden())
bgneal@45 8319 ed.show();
bgneal@45 8320 else
bgneal@45 8321 ed.hide();
bgneal@45 8322
bgneal@45 8323 return true;
bgneal@45 8324 }
bgneal@45 8325
bgneal@45 8326 // Run command on active editor
bgneal@45 8327 if (t.activeEditor)
bgneal@45 8328 return t.activeEditor.execCommand(c, u, v);
bgneal@45 8329
bgneal@45 8330 return false;
bgneal@45 8331 },
bgneal@45 8332
bgneal@45 8333 execInstanceCommand : function(id, c, u, v) {
bgneal@45 8334 var ed = this.get(id);
bgneal@45 8335
bgneal@45 8336 if (ed)
bgneal@45 8337 return ed.execCommand(c, u, v);
bgneal@45 8338
bgneal@45 8339 return false;
bgneal@45 8340 },
bgneal@45 8341
bgneal@45 8342 triggerSave : function() {
bgneal@45 8343 each(this.editors, function(e) {
bgneal@45 8344 e.save();
bgneal@45 8345 });
bgneal@45 8346 },
bgneal@45 8347
bgneal@45 8348 addI18n : function(p, o) {
bgneal@45 8349 var lo, i18n = this.i18n;
bgneal@45 8350
bgneal@45 8351 if (!tinymce.is(p, 'string')) {
bgneal@45 8352 each(p, function(o, lc) {
bgneal@45 8353 each(o, function(o, g) {
bgneal@45 8354 each(o, function(o, k) {
bgneal@45 8355 if (g === 'common')
bgneal@45 8356 i18n[lc + '.' + k] = o;
bgneal@45 8357 else
bgneal@45 8358 i18n[lc + '.' + g + '.' + k] = o;
bgneal@45 8359 });
bgneal@45 8360 });
bgneal@45 8361 });
bgneal@45 8362 } else {
bgneal@45 8363 each(o, function(o, k) {
bgneal@45 8364 i18n[p + '.' + k] = o;
bgneal@45 8365 });
bgneal@45 8366 }
bgneal@45 8367 },
bgneal@45 8368
bgneal@45 8369 // Private methods
bgneal@45 8370
bgneal@45 8371 _setActive : function(e) {
bgneal@45 8372 this.selectedInstance = this.activeEditor = e;
bgneal@45 8373 }
bgneal@45 8374
bgneal@45 8375 });
bgneal@45 8376
bgneal@45 8377 tinymce.EditorManager.preInit();
bgneal@45 8378 })(tinymce);
bgneal@45 8379
bgneal@45 8380 // Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call
bgneal@45 8381 var tinyMCE = window.tinyMCE = tinymce.EditorManager;
bgneal@45 8382 (function(tinymce) {
bgneal@45 8383 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
bgneal@45 8384 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
bgneal@45 8385 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
bgneal@45 8386 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
bgneal@45 8387
bgneal@45 8388 tinymce.create('tinymce.Editor', {
bgneal@45 8389 Editor : function(id, s) {
bgneal@45 8390 var t = this;
bgneal@45 8391
bgneal@45 8392 t.id = t.editorId = id;
bgneal@45 8393 t.execCommands = {};
bgneal@45 8394 t.queryStateCommands = {};
bgneal@45 8395 t.queryValueCommands = {};
bgneal@45 8396 t.plugins = {};
bgneal@45 8397
bgneal@45 8398 // Add events to the editor
bgneal@45 8399 each([
bgneal@45 8400 'onPreInit',
bgneal@45 8401 'onBeforeRenderUI',
bgneal@45 8402 'onPostRender',
bgneal@45 8403 'onInit',
bgneal@45 8404 'onRemove',
bgneal@45 8405 'onActivate',
bgneal@45 8406 'onDeactivate',
bgneal@45 8407 'onClick',
bgneal@45 8408 'onEvent',
bgneal@45 8409 'onMouseUp',
bgneal@45 8410 'onMouseDown',
bgneal@45 8411 'onDblClick',
bgneal@45 8412 'onKeyDown',
bgneal@45 8413 'onKeyUp',
bgneal@45 8414 'onKeyPress',
bgneal@45 8415 'onContextMenu',
bgneal@45 8416 'onSubmit',
bgneal@45 8417 'onReset',
bgneal@45 8418 'onPaste',
bgneal@45 8419 'onPreProcess',
bgneal@45 8420 'onPostProcess',
bgneal@45 8421 'onBeforeSetContent',
bgneal@45 8422 'onBeforeGetContent',
bgneal@45 8423 'onSetContent',
bgneal@45 8424 'onGetContent',
bgneal@45 8425 'onLoadContent',
bgneal@45 8426 'onSaveContent',
bgneal@45 8427 'onNodeChange',
bgneal@45 8428 'onChange',
bgneal@45 8429 'onBeforeExecCommand',
bgneal@45 8430 'onExecCommand',
bgneal@45 8431 'onUndo',
bgneal@45 8432 'onRedo',
bgneal@45 8433 'onVisualAid',
bgneal@45 8434 'onSetProgressState'
bgneal@45 8435 ], function(e) {
bgneal@45 8436 t[e] = new Dispatcher(t);
bgneal@45 8437 });
bgneal@45 8438
bgneal@45 8439 // Default editor config
bgneal@45 8440 t.settings = s = extend({
bgneal@45 8441 id : id,
bgneal@45 8442 language : 'en',
bgneal@45 8443 docs_language : 'en',
bgneal@45 8444 theme : 'simple',
bgneal@45 8445 skin : 'default',
bgneal@45 8446 delta_width : 0,
bgneal@45 8447 delta_height : 0,
bgneal@45 8448 popup_css : '',
bgneal@45 8449 plugins : '',
bgneal@45 8450 document_base_url : tinymce.documentBaseURL,
bgneal@45 8451 add_form_submit_trigger : 1,
bgneal@45 8452 submit_patch : 1,
bgneal@45 8453 add_unload_trigger : 1,
bgneal@45 8454 convert_urls : 1,
bgneal@45 8455 relative_urls : 1,
bgneal@45 8456 remove_script_host : 1,
bgneal@45 8457 table_inline_editing : 0,
bgneal@45 8458 object_resizing : 1,
bgneal@45 8459 cleanup : 1,
bgneal@45 8460 accessibility_focus : 1,
bgneal@45 8461 custom_shortcuts : 1,
bgneal@45 8462 custom_undo_redo_keyboard_shortcuts : 1,
bgneal@45 8463 custom_undo_redo_restore_selection : 1,
bgneal@45 8464 custom_undo_redo : 1,
bgneal@45 8465 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
bgneal@45 8466 visual_table_class : 'mceItemTable',
bgneal@45 8467 visual : 1,
bgneal@45 8468 inline_styles : true,
bgneal@45 8469 convert_fonts_to_spans : true,
bgneal@45 8470 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
bgneal@45 8471 apply_source_formatting : 1,
bgneal@45 8472 directionality : 'ltr',
bgneal@45 8473 forced_root_block : 'p',
bgneal@45 8474 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',
bgneal@45 8475 hidden_input : 1,
bgneal@45 8476 padd_empty_editor : 1,
bgneal@45 8477 render_ui : 1,
bgneal@45 8478 init_theme : 1,
bgneal@45 8479 force_p_newlines : 1,
bgneal@45 8480 indentation : '30px',
bgneal@45 8481 keep_styles : 1,
bgneal@45 8482 fix_table_elements : 1,
bgneal@45 8483 removeformat_selector : 'span,b,strong,em,i,font,u,strike'
bgneal@45 8484 }, s);
bgneal@45 8485
bgneal@45 8486 // Setup URIs
bgneal@45 8487 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
bgneal@45 8488 base_uri : tinyMCE.baseURI
bgneal@45 8489 });
bgneal@45 8490 t.baseURI = EditorManager.baseURI;
bgneal@45 8491
bgneal@45 8492 // Call setup
bgneal@45 8493 t.execCallback('setup', t);
bgneal@45 8494 },
bgneal@45 8495
bgneal@45 8496 render : function(nst) {
bgneal@45 8497 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
bgneal@45 8498
bgneal@45 8499 // Page is not loaded yet, wait for it
bgneal@45 8500 if (!Event.domLoaded) {
bgneal@45 8501 Event.add(document, 'init', function() {
bgneal@45 8502 t.render();
bgneal@45 8503 });
bgneal@45 8504 return;
bgneal@45 8505 }
bgneal@45 8506
bgneal@45 8507 // Force strict loading mode if render us called by user and not internally
bgneal@45 8508 if (!nst) {
bgneal@45 8509 s.strict_loading_mode = 1;
bgneal@45 8510 tinyMCE.settings = s;
bgneal@45 8511 }
bgneal@45 8512
bgneal@45 8513 // Element not found, then skip initialization
bgneal@45 8514 if (!t.getElement())
bgneal@45 8515 return;
bgneal@45 8516
bgneal@45 8517 if (s.strict_loading_mode) {
bgneal@45 8518 sl.settings.strict_mode = s.strict_loading_mode;
bgneal@45 8519 tinymce.DOM.settings.strict = 1;
bgneal@45 8520 }
bgneal@45 8521
bgneal@45 8522 // Add hidden input for non input elements inside form elements
bgneal@45 8523 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
bgneal@45 8524 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
bgneal@45 8525
bgneal@45 8526 if (tinymce.WindowManager)
bgneal@45 8527 t.windowManager = new tinymce.WindowManager(t);
bgneal@45 8528
bgneal@45 8529 if (s.encoding == 'xml') {
bgneal@45 8530 t.onGetContent.add(function(ed, o) {
bgneal@45 8531 if (o.save)
bgneal@45 8532 o.content = DOM.encode(o.content);
bgneal@45 8533 });
bgneal@45 8534 }
bgneal@45 8535
bgneal@45 8536 if (s.add_form_submit_trigger) {
bgneal@45 8537 t.onSubmit.addToTop(function() {
bgneal@45 8538 if (t.initialized) {
bgneal@45 8539 t.save();
bgneal@45 8540 t.isNotDirty = 1;
bgneal@45 8541 }
bgneal@45 8542 });
bgneal@45 8543 }
bgneal@45 8544
bgneal@45 8545 if (s.add_unload_trigger) {
bgneal@45 8546 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
bgneal@45 8547 if (t.initialized && !t.destroyed && !t.isHidden())
bgneal@45 8548 t.save({format : 'raw', no_events : true});
bgneal@45 8549 });
bgneal@45 8550 }
bgneal@45 8551
bgneal@45 8552 tinymce.addUnload(t.destroy, t);
bgneal@45 8553
bgneal@45 8554 if (s.submit_patch) {
bgneal@45 8555 t.onBeforeRenderUI.add(function() {
bgneal@45 8556 var n = t.getElement().form;
bgneal@45 8557
bgneal@45 8558 if (!n)
bgneal@45 8559 return;
bgneal@45 8560
bgneal@45 8561 // Already patched
bgneal@45 8562 if (n._mceOldSubmit)
bgneal@45 8563 return;
bgneal@45 8564
bgneal@45 8565 // Check page uses id="submit" or name="submit" for it's submit button
bgneal@45 8566 if (!n.submit.nodeType && !n.submit.length) {
bgneal@45 8567 t.formElement = n;
bgneal@45 8568 n._mceOldSubmit = n.submit;
bgneal@45 8569 n.submit = function() {
bgneal@45 8570 // Save all instances
bgneal@45 8571 EditorManager.triggerSave();
bgneal@45 8572 t.isNotDirty = 1;
bgneal@45 8573
bgneal@45 8574 return this._mceOldSubmit(this);
bgneal@45 8575 };
bgneal@45 8576 }
bgneal@45 8577
bgneal@45 8578 n = null;
bgneal@45 8579 });
bgneal@45 8580 }
bgneal@45 8581
bgneal@45 8582 // Load scripts
bgneal@45 8583 function loadScripts() {
bgneal@45 8584 if (s.language)
bgneal@45 8585 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
bgneal@45 8586
bgneal@45 8587 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
bgneal@45 8588 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
bgneal@45 8589
bgneal@45 8590 each(explode(s.plugins), function(p) {
bgneal@45 8591 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
bgneal@45 8592 // Skip safari plugin for other browsers
bgneal@45 8593 if (!isWebKit && p == 'safari')
bgneal@45 8594 return;
bgneal@45 8595
bgneal@45 8596 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
bgneal@45 8597 }
bgneal@45 8598 });
bgneal@45 8599
bgneal@45 8600 // Init when que is loaded
bgneal@45 8601 sl.loadQueue(function() {
bgneal@45 8602 if (!t.removed)
bgneal@45 8603 t.init();
bgneal@45 8604 });
bgneal@45 8605 };
bgneal@45 8606
bgneal@45 8607 // Load compat2x first
bgneal@45 8608 if (s.plugins.indexOf('compat2x') != -1) {
bgneal@45 8609 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
bgneal@45 8610 sl.loadQueue(loadScripts);
bgneal@45 8611 } else
bgneal@45 8612 loadScripts();
bgneal@45 8613 },
bgneal@45 8614
bgneal@45 8615 init : function() {
bgneal@45 8616 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
bgneal@45 8617
bgneal@45 8618 EditorManager.add(t);
bgneal@45 8619
bgneal@45 8620 // Create theme
bgneal@45 8621 if (s.theme) {
bgneal@45 8622 s.theme = s.theme.replace(/-/, '');
bgneal@45 8623 o = ThemeManager.get(s.theme);
bgneal@45 8624 t.theme = new o();
bgneal@45 8625
bgneal@45 8626 if (t.theme.init && s.init_theme)
bgneal@45 8627 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
bgneal@45 8628 }
bgneal@45 8629
bgneal@45 8630 // Create all plugins
bgneal@45 8631 each(explode(s.plugins.replace(/\-/g, '')), function(p) {
bgneal@45 8632 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
bgneal@45 8633
bgneal@45 8634 if (c) {
bgneal@45 8635 po = new c(t, u);
bgneal@45 8636
bgneal@45 8637 t.plugins[p] = po;
bgneal@45 8638
bgneal@45 8639 if (po.init)
bgneal@45 8640 po.init(t, u);
bgneal@45 8641 }
bgneal@45 8642 });
bgneal@45 8643
bgneal@45 8644 // Setup popup CSS path(s)
bgneal@45 8645 if (s.popup_css !== false) {
bgneal@45 8646 if (s.popup_css)
bgneal@45 8647 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
bgneal@45 8648 else
bgneal@45 8649 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
bgneal@45 8650 }
bgneal@45 8651
bgneal@45 8652 if (s.popup_css_add)
bgneal@45 8653 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
bgneal@45 8654
bgneal@45 8655 // Setup control factory
bgneal@45 8656 t.controlManager = new tinymce.ControlManager(t);
bgneal@45 8657 t.undoManager = new tinymce.UndoManager(t);
bgneal@45 8658
bgneal@45 8659 // Pass through
bgneal@45 8660 t.undoManager.onAdd.add(function(um, l) {
bgneal@45 8661 if (!l.initial)
bgneal@45 8662 return t.onChange.dispatch(t, l, um);
bgneal@45 8663 });
bgneal@45 8664
bgneal@45 8665 t.undoManager.onUndo.add(function(um, l) {
bgneal@45 8666 return t.onUndo.dispatch(t, l, um);
bgneal@45 8667 });
bgneal@45 8668
bgneal@45 8669 t.undoManager.onRedo.add(function(um, l) {
bgneal@45 8670 return t.onRedo.dispatch(t, l, um);
bgneal@45 8671 });
bgneal@45 8672
bgneal@45 8673 if (s.custom_undo_redo) {
bgneal@45 8674 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
bgneal@45 8675 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
bgneal@45 8676 t.undoManager.add();
bgneal@45 8677 });
bgneal@45 8678 }
bgneal@45 8679
bgneal@45 8680 t.onExecCommand.add(function(ed, c) {
bgneal@45 8681 // Don't refresh the select lists until caret move
bgneal@45 8682 if (!/^(FontName|FontSize)$/.test(c))
bgneal@45 8683 t.nodeChanged();
bgneal@45 8684 });
bgneal@45 8685
bgneal@45 8686 // Remove ghost selections on images and tables in Gecko
bgneal@45 8687 if (isGecko) {
bgneal@45 8688 function repaint(a, o) {
bgneal@45 8689 if (!o || !o.initial)
bgneal@45 8690 t.execCommand('mceRepaint');
bgneal@45 8691 };
bgneal@45 8692
bgneal@45 8693 t.onUndo.add(repaint);
bgneal@45 8694 t.onRedo.add(repaint);
bgneal@45 8695 t.onSetContent.add(repaint);
bgneal@45 8696 }
bgneal@45 8697
bgneal@45 8698 // Enables users to override the control factory
bgneal@45 8699 t.onBeforeRenderUI.dispatch(t, t.controlManager);
bgneal@45 8700
bgneal@45 8701 // Measure box
bgneal@45 8702 if (s.render_ui) {
bgneal@45 8703 w = s.width || e.style.width || e.offsetWidth;
bgneal@45 8704 h = s.height || e.style.height || e.offsetHeight;
bgneal@45 8705 t.orgDisplay = e.style.display;
bgneal@45 8706 re = /^[0-9\.]+(|px)$/i;
bgneal@45 8707
bgneal@45 8708 if (re.test('' + w))
bgneal@45 8709 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
bgneal@45 8710
bgneal@45 8711 if (re.test('' + h))
bgneal@45 8712 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
bgneal@45 8713
bgneal@45 8714 // Render UI
bgneal@45 8715 o = t.theme.renderUI({
bgneal@45 8716 targetNode : e,
bgneal@45 8717 width : w,
bgneal@45 8718 height : h,
bgneal@45 8719 deltaWidth : s.delta_width,
bgneal@45 8720 deltaHeight : s.delta_height
bgneal@45 8721 });
bgneal@45 8722
bgneal@45 8723 t.editorContainer = o.editorContainer;
bgneal@45 8724 }
bgneal@45 8725
bgneal@45 8726
bgneal@45 8727 // Resize editor
bgneal@45 8728 DOM.setStyles(o.sizeContainer || o.editorContainer, {
bgneal@45 8729 width : w,
bgneal@45 8730 height : h
bgneal@45 8731 });
bgneal@45 8732
bgneal@45 8733 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
bgneal@45 8734 if (h < 100)
bgneal@45 8735 h = 100;
bgneal@45 8736
bgneal@45 8737 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '" />';
bgneal@45 8738 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
bgneal@45 8739
bgneal@45 8740 if (tinymce.relaxedDomain)
bgneal@45 8741 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';
bgneal@45 8742
bgneal@45 8743 bi = s.body_id || 'tinymce';
bgneal@45 8744 if (bi.indexOf('=') != -1) {
bgneal@45 8745 bi = t.getParam('body_id', '', 'hash');
bgneal@45 8746 bi = bi[t.id] || bi;
bgneal@45 8747 }
bgneal@45 8748
bgneal@45 8749 bc = s.body_class || '';
bgneal@45 8750 if (bc.indexOf('=') != -1) {
bgneal@45 8751 bc = t.getParam('body_class', '', 'hash');
bgneal@45 8752 bc = bc[t.id] || '';
bgneal@45 8753 }
bgneal@45 8754
bgneal@45 8755 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
bgneal@45 8756
bgneal@45 8757 // Domain relaxing enabled, then set document domain
bgneal@45 8758 if (tinymce.relaxedDomain) {
bgneal@45 8759 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
bgneal@45 8760 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))
bgneal@45 8761 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();})()';
bgneal@45 8762 else if (tinymce.isOpera)
bgneal@45 8763 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';
bgneal@45 8764 }
bgneal@45 8765
bgneal@45 8766 // Create iframe
bgneal@45 8767 n = DOM.add(o.iframeContainer, 'iframe', {
bgneal@45 8768 id : t.id + "_ifr",
bgneal@45 8769 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
bgneal@45 8770 frameBorder : '0',
bgneal@45 8771 style : {
bgneal@45 8772 width : '100%',
bgneal@45 8773 height : h
bgneal@45 8774 }
bgneal@45 8775 });
bgneal@45 8776
bgneal@45 8777 t.contentAreaContainer = o.iframeContainer;
bgneal@45 8778 DOM.get(o.editorContainer).style.display = t.orgDisplay;
bgneal@45 8779 DOM.get(t.id).style.display = 'none';
bgneal@45 8780
bgneal@45 8781 if (!isIE || !tinymce.relaxedDomain)
bgneal@45 8782 t.setupIframe();
bgneal@45 8783
bgneal@45 8784 e = n = o = null; // Cleanup
bgneal@45 8785 },
bgneal@45 8786
bgneal@45 8787 setupIframe : function() {
bgneal@45 8788 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
bgneal@45 8789
bgneal@45 8790 // Setup iframe body
bgneal@45 8791 if (!isIE || !tinymce.relaxedDomain) {
bgneal@45 8792 d.open();
bgneal@45 8793 d.write(t.iframeHTML);
bgneal@45 8794 d.close();
bgneal@45 8795 }
bgneal@45 8796
bgneal@45 8797 // Design mode needs to be added here Ctrl+A will fail otherwise
bgneal@45 8798 if (!isIE) {
bgneal@45 8799 try {
bgneal@45 8800 if (!s.readonly)
bgneal@45 8801 d.designMode = 'On';
bgneal@45 8802 } catch (ex) {
bgneal@45 8803 // Will fail on Gecko if the editor is placed in an hidden container element
bgneal@45 8804 // The design mode will be set ones the editor is focused
bgneal@45 8805 }
bgneal@45 8806 }
bgneal@45 8807
bgneal@45 8808 // IE needs to use contentEditable or it will display non secure items for HTTPS
bgneal@45 8809 if (isIE) {
bgneal@45 8810 // It will not steal focus if we hide it while setting contentEditable
bgneal@45 8811 b = t.getBody();
bgneal@45 8812 DOM.hide(b);
bgneal@45 8813
bgneal@45 8814 if (!s.readonly)
bgneal@45 8815 b.contentEditable = true;
bgneal@45 8816
bgneal@45 8817 DOM.show(b);
bgneal@45 8818 }
bgneal@45 8819
bgneal@45 8820 // Setup objects
bgneal@45 8821 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
bgneal@45 8822 keep_values : true,
bgneal@45 8823 url_converter : t.convertURL,
bgneal@45 8824 url_converter_scope : t,
bgneal@45 8825 hex_colors : s.force_hex_style_colors,
bgneal@45 8826 class_filter : s.class_filter,
bgneal@45 8827 update_styles : 1,
bgneal@45 8828 fix_ie_paragraphs : 1
bgneal@45 8829 });
bgneal@45 8830
bgneal@45 8831 t.serializer = new tinymce.dom.Serializer({
bgneal@45 8832 entity_encoding : s.entity_encoding,
bgneal@45 8833 entities : s.entities,
bgneal@45 8834 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
bgneal@45 8835 extended_valid_elements : s.extended_valid_elements,
bgneal@45 8836 valid_child_elements : s.valid_child_elements,
bgneal@45 8837 invalid_elements : s.invalid_elements,
bgneal@45 8838 fix_table_elements : s.fix_table_elements,
bgneal@45 8839 fix_list_elements : s.fix_list_elements,
bgneal@45 8840 fix_content_duplication : s.fix_content_duplication,
bgneal@45 8841 convert_fonts_to_spans : s.convert_fonts_to_spans,
bgneal@45 8842 font_size_classes : s.font_size_classes,
bgneal@45 8843 font_size_style_values : s.font_size_style_values,
bgneal@45 8844 apply_source_formatting : s.apply_source_formatting,
bgneal@45 8845 remove_linebreaks : s.remove_linebreaks,
bgneal@45 8846 element_format : s.element_format,
bgneal@45 8847 dom : t.dom
bgneal@45 8848 });
bgneal@45 8849
bgneal@45 8850 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
bgneal@45 8851 t.forceBlocks = new tinymce.ForceBlocks(t, {
bgneal@45 8852 forced_root_block : s.forced_root_block
bgneal@45 8853 });
bgneal@45 8854 t.editorCommands = new tinymce.EditorCommands(t);
bgneal@45 8855
bgneal@45 8856 // Pass through
bgneal@45 8857 t.serializer.onPreProcess.add(function(se, o) {
bgneal@45 8858 return t.onPreProcess.dispatch(t, o, se);
bgneal@45 8859 });
bgneal@45 8860
bgneal@45 8861 t.serializer.onPostProcess.add(function(se, o) {
bgneal@45 8862 return t.onPostProcess.dispatch(t, o, se);
bgneal@45 8863 });
bgneal@45 8864
bgneal@45 8865 t.onPreInit.dispatch(t);
bgneal@45 8866
bgneal@45 8867 if (!s.gecko_spellcheck)
bgneal@45 8868 t.getBody().spellcheck = 0;
bgneal@45 8869
bgneal@45 8870 if (!s.readonly)
bgneal@45 8871 t._addEvents();
bgneal@45 8872
bgneal@45 8873 t.controlManager.onPostRender.dispatch(t, t.controlManager);
bgneal@45 8874 t.onPostRender.dispatch(t);
bgneal@45 8875
bgneal@45 8876 if (s.directionality)
bgneal@45 8877 t.getBody().dir = s.directionality;
bgneal@45 8878
bgneal@45 8879 if (s.nowrap)
bgneal@45 8880 t.getBody().style.whiteSpace = "nowrap";
bgneal@45 8881
bgneal@45 8882 if (s.auto_resize)
bgneal@45 8883 t.onNodeChange.add(t.resizeToContent, t);
bgneal@45 8884
bgneal@45 8885 if (s.custom_elements) {
bgneal@45 8886 function handleCustom(ed, o) {
bgneal@45 8887 each(explode(s.custom_elements), function(v) {
bgneal@45 8888 var n;
bgneal@45 8889
bgneal@45 8890 if (v.indexOf('~') === 0) {
bgneal@45 8891 v = v.substring(1);
bgneal@45 8892 n = 'span';
bgneal@45 8893 } else
bgneal@45 8894 n = 'div';
bgneal@45 8895
bgneal@45 8896 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
bgneal@45 8897 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
bgneal@45 8898 });
bgneal@45 8899 };
bgneal@45 8900
bgneal@45 8901 t.onBeforeSetContent.add(handleCustom);
bgneal@45 8902 t.onPostProcess.add(function(ed, o) {
bgneal@45 8903 if (o.set)
bgneal@45 8904 handleCustom(ed, o)
bgneal@45 8905 });
bgneal@45 8906 }
bgneal@45 8907
bgneal@45 8908 if (s.handle_node_change_callback) {
bgneal@45 8909 t.onNodeChange.add(function(ed, cm, n) {
bgneal@45 8910 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
bgneal@45 8911 });
bgneal@45 8912 }
bgneal@45 8913
bgneal@45 8914 if (s.save_callback) {
bgneal@45 8915 t.onSaveContent.add(function(ed, o) {
bgneal@45 8916 var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
bgneal@45 8917
bgneal@45 8918 if (h)
bgneal@45 8919 o.content = h;
bgneal@45 8920 });
bgneal@45 8921 }
bgneal@45 8922
bgneal@45 8923 if (s.onchange_callback) {
bgneal@45 8924 t.onChange.add(function(ed, l) {
bgneal@45 8925 t.execCallback('onchange_callback', t, l);
bgneal@45 8926 });
bgneal@45 8927 }
bgneal@45 8928
bgneal@45 8929 if (s.convert_newlines_to_brs) {
bgneal@45 8930 t.onBeforeSetContent.add(function(ed, o) {
bgneal@45 8931 if (o.initial)
bgneal@45 8932 o.content = o.content.replace(/\r?\n/g, '<br />');
bgneal@45 8933 });
bgneal@45 8934 }
bgneal@45 8935
bgneal@45 8936 if (s.fix_nesting && isIE) {
bgneal@45 8937 t.onBeforeSetContent.add(function(ed, o) {
bgneal@45 8938 o.content = t._fixNesting(o.content);
bgneal@45 8939 });
bgneal@45 8940 }
bgneal@45 8941
bgneal@45 8942 if (s.preformatted) {
bgneal@45 8943 t.onPostProcess.add(function(ed, o) {
bgneal@45 8944 o.content = o.content.replace(/^\s*<pre.*?>/, '');
bgneal@45 8945 o.content = o.content.replace(/<\/pre>\s*$/, '');
bgneal@45 8946
bgneal@45 8947 if (o.set)
bgneal@45 8948 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
bgneal@45 8949 });
bgneal@45 8950 }
bgneal@45 8951
bgneal@45 8952 if (s.verify_css_classes) {
bgneal@45 8953 t.serializer.attribValueFilter = function(n, v) {
bgneal@45 8954 var s, cl;
bgneal@45 8955
bgneal@45 8956 if (n == 'class') {
bgneal@45 8957 // Build regexp for classes
bgneal@45 8958 if (!t.classesRE) {
bgneal@45 8959 cl = t.dom.getClasses();
bgneal@45 8960
bgneal@45 8961 if (cl.length > 0) {
bgneal@45 8962 s = '';
bgneal@45 8963
bgneal@45 8964 each (cl, function(o) {
bgneal@45 8965 s += (s ? '|' : '') + o['class'];
bgneal@45 8966 });
bgneal@45 8967
bgneal@45 8968 t.classesRE = new RegExp('(' + s + ')', 'gi');
bgneal@45 8969 }
bgneal@45 8970 }
bgneal@45 8971
bgneal@45 8972 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
bgneal@45 8973 }
bgneal@45 8974
bgneal@45 8975 return v;
bgneal@45 8976 };
bgneal@45 8977 }
bgneal@45 8978
bgneal@45 8979 if (s.convert_fonts_to_spans)
bgneal@45 8980 t._convertFonts();
bgneal@45 8981
bgneal@45 8982 if (s.inline_styles)
bgneal@45 8983 t._convertInlineElements();
bgneal@45 8984
bgneal@45 8985 if (s.cleanup_callback) {
bgneal@45 8986 t.onBeforeSetContent.add(function(ed, o) {
bgneal@45 8987 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
bgneal@45 8988 });
bgneal@45 8989
bgneal@45 8990 t.onPreProcess.add(function(ed, o) {
bgneal@45 8991 if (o.set)
bgneal@45 8992 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
bgneal@45 8993
bgneal@45 8994 if (o.get)
bgneal@45 8995 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
bgneal@45 8996 });
bgneal@45 8997
bgneal@45 8998 t.onPostProcess.add(function(ed, o) {
bgneal@45 8999 if (o.set)
bgneal@45 9000 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
bgneal@45 9001
bgneal@45 9002 if (o.get)
bgneal@45 9003 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
bgneal@45 9004 });
bgneal@45 9005 }
bgneal@45 9006
bgneal@45 9007 if (s.save_callback) {
bgneal@45 9008 t.onGetContent.add(function(ed, o) {
bgneal@45 9009 if (o.save)
bgneal@45 9010 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
bgneal@45 9011 });
bgneal@45 9012 }
bgneal@45 9013
bgneal@45 9014 if (s.handle_event_callback) {
bgneal@45 9015 t.onEvent.add(function(ed, e, o) {
bgneal@45 9016 if (t.execCallback('handle_event_callback', e, ed, o) === false)
bgneal@45 9017 Event.cancel(e);
bgneal@45 9018 });
bgneal@45 9019 }
bgneal@45 9020
bgneal@45 9021 // Add visual aids when new contents is added
bgneal@45 9022 t.onSetContent.add(function() {
bgneal@45 9023 t.addVisual(t.getBody());
bgneal@45 9024 });
bgneal@45 9025
bgneal@45 9026 // Remove empty contents
bgneal@45 9027 if (s.padd_empty_editor) {
bgneal@45 9028 t.onPostProcess.add(function(ed, o) {
bgneal@45 9029 o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
bgneal@45 9030 });
bgneal@45 9031 }
bgneal@45 9032
bgneal@45 9033 // Fix gecko link bug, when a link is placed at the end of block elements there is
bgneal@45 9034 // no way to move the caret behind the link. This fix adds a bogus br element after the link
bgneal@45 9035 if (isGecko) {
bgneal@45 9036 function fixLinks(ed, o) {
bgneal@45 9037 each(ed.dom.select('a'), function(n) {
bgneal@45 9038 var pn = n.parentNode;
bgneal@45 9039
bgneal@45 9040 if (ed.dom.isBlock(pn) && pn.lastChild === n)
bgneal@45 9041 ed.dom.add(pn, 'br', {'mce_bogus' : 1});
bgneal@45 9042 });
bgneal@45 9043 };
bgneal@45 9044
bgneal@45 9045 t.onExecCommand.add(function(ed, cmd) {
bgneal@45 9046 if (cmd === 'CreateLink')
bgneal@45 9047 fixLinks(ed);
bgneal@45 9048 });
bgneal@45 9049
bgneal@45 9050 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
bgneal@45 9051 }
bgneal@45 9052
bgneal@45 9053 if (isGecko && !s.readonly) {
bgneal@45 9054 try {
bgneal@45 9055 // Design mode must be set here once again to fix a bug where
bgneal@45 9056 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
bgneal@45 9057 d.designMode = 'Off';
bgneal@45 9058 d.designMode = 'On';
bgneal@45 9059 } catch (ex) {
bgneal@45 9060 // Will fail on Gecko if the editor is placed in an hidden container element
bgneal@45 9061 // The design mode will be set ones the editor is focused
bgneal@45 9062 }
bgneal@45 9063 }
bgneal@45 9064
bgneal@45 9065 // A small timeout was needed since firefox will remove. Bug: #1838304
bgneal@45 9066 setTimeout(function () {
bgneal@45 9067 if (t.removed)
bgneal@45 9068 return;
bgneal@45 9069
bgneal@45 9070 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
bgneal@45 9071 t.startContent = t.getContent({format : 'raw'});
bgneal@45 9072 t.undoManager.add({initial : true});
bgneal@45 9073 t.initialized = true;
bgneal@45 9074
bgneal@45 9075 t.onInit.dispatch(t);
bgneal@45 9076 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
bgneal@45 9077 t.execCallback('init_instance_callback', t);
bgneal@45 9078 t.focus(true);
bgneal@45 9079 t.nodeChanged({initial : 1});
bgneal@45 9080
bgneal@45 9081 // Load specified content CSS last
bgneal@45 9082 if (s.content_css) {
bgneal@45 9083 tinymce.each(explode(s.content_css), function(u) {
bgneal@45 9084 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
bgneal@45 9085 });
bgneal@45 9086 }
bgneal@45 9087
bgneal@45 9088 // Handle auto focus
bgneal@45 9089 if (s.auto_focus) {
bgneal@45 9090 setTimeout(function () {
bgneal@45 9091 var ed = EditorManager.get(s.auto_focus);
bgneal@45 9092
bgneal@45 9093 ed.selection.select(ed.getBody(), 1);
bgneal@45 9094 ed.selection.collapse(1);
bgneal@45 9095 ed.getWin().focus();
bgneal@45 9096 }, 100);
bgneal@45 9097 }
bgneal@45 9098 }, 1);
bgneal@45 9099
bgneal@45 9100 e = null;
bgneal@45 9101 },
bgneal@45 9102
bgneal@45 9103
bgneal@45 9104 focus : function(sf) {
bgneal@45 9105 var oed, t = this, ce = t.settings.content_editable;
bgneal@45 9106
bgneal@45 9107 if (!sf) {
bgneal@45 9108 // Is not content editable or the selection is outside the area in IE
bgneal@45 9109 // the IE statement is needed to avoid bluring if element selections inside layers since
bgneal@45 9110 // the layer is like it's own document in IE
bgneal@45 9111 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
bgneal@45 9112 t.getWin().focus();
bgneal@45 9113
bgneal@45 9114 }
bgneal@45 9115
bgneal@45 9116 if (EditorManager.activeEditor != t) {
bgneal@45 9117 if ((oed = EditorManager.activeEditor) != null)
bgneal@45 9118 oed.onDeactivate.dispatch(oed, t);
bgneal@45 9119
bgneal@45 9120 t.onActivate.dispatch(t, oed);
bgneal@45 9121 }
bgneal@45 9122
bgneal@45 9123 EditorManager._setActive(t);
bgneal@45 9124 },
bgneal@45 9125
bgneal@45 9126 execCallback : function(n) {
bgneal@45 9127 var t = this, f = t.settings[n], s;
bgneal@45 9128
bgneal@45 9129 if (!f)
bgneal@45 9130 return;
bgneal@45 9131
bgneal@45 9132 // Look through lookup
bgneal@45 9133 if (t.callbackLookup && (s = t.callbackLookup[n])) {
bgneal@45 9134 f = s.func;
bgneal@45 9135 s = s.scope;
bgneal@45 9136 }
bgneal@45 9137
bgneal@45 9138 if (is(f, 'string')) {
bgneal@45 9139 s = f.replace(/\.\w+$/, '');
bgneal@45 9140 s = s ? tinymce.resolve(s) : 0;
bgneal@45 9141 f = tinymce.resolve(f);
bgneal@45 9142 t.callbackLookup = t.callbackLookup || {};
bgneal@45 9143 t.callbackLookup[n] = {func : f, scope : s};
bgneal@45 9144 }
bgneal@45 9145
bgneal@45 9146 return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
bgneal@45 9147 },
bgneal@45 9148
bgneal@45 9149 translate : function(s) {
bgneal@45 9150 var c = this.settings.language || 'en', i18n = EditorManager.i18n;
bgneal@45 9151
bgneal@45 9152 if (!s)
bgneal@45 9153 return '';
bgneal@45 9154
bgneal@45 9155 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
bgneal@45 9156 return i18n[c + '.' + b] || '{#' + b + '}';
bgneal@45 9157 });
bgneal@45 9158 },
bgneal@45 9159
bgneal@45 9160 getLang : function(n, dv) {
bgneal@45 9161 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
bgneal@45 9162 },
bgneal@45 9163
bgneal@45 9164 getParam : function(n, dv, ty) {
bgneal@45 9165 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
bgneal@45 9166
bgneal@45 9167 if (ty === 'hash') {
bgneal@45 9168 o = {};
bgneal@45 9169
bgneal@45 9170 if (is(v, 'string')) {
bgneal@45 9171 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
bgneal@45 9172 v = v.split('=');
bgneal@45 9173
bgneal@45 9174 if (v.length > 1)
bgneal@45 9175 o[tr(v[0])] = tr(v[1]);
bgneal@45 9176 else
bgneal@45 9177 o[tr(v[0])] = tr(v);
bgneal@45 9178 });
bgneal@45 9179 } else
bgneal@45 9180 o = v;
bgneal@45 9181
bgneal@45 9182 return o;
bgneal@45 9183 }
bgneal@45 9184
bgneal@45 9185 return v;
bgneal@45 9186 },
bgneal@45 9187
bgneal@45 9188 nodeChanged : function(o) {
bgneal@45 9189 var t = this, s = t.selection, n = s.getNode() || t.getBody();
bgneal@45 9190
bgneal@45 9191 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
bgneal@45 9192 if (t.initialized) {
bgneal@45 9193 t.onNodeChange.dispatch(
bgneal@45 9194 t,
bgneal@45 9195 o ? o.controlManager || t.controlManager : t.controlManager,
bgneal@45 9196 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
bgneal@45 9197 s.isCollapsed(),
bgneal@45 9198 o
bgneal@45 9199 );
bgneal@45 9200 }
bgneal@45 9201 },
bgneal@45 9202
bgneal@45 9203 addButton : function(n, s) {
bgneal@45 9204 var t = this;
bgneal@45 9205
bgneal@45 9206 t.buttons = t.buttons || {};
bgneal@45 9207 t.buttons[n] = s;
bgneal@45 9208 },
bgneal@45 9209
bgneal@45 9210 addCommand : function(n, f, s) {
bgneal@45 9211 this.execCommands[n] = {func : f, scope : s || this};
bgneal@45 9212 },
bgneal@45 9213
bgneal@45 9214 addQueryStateHandler : function(n, f, s) {
bgneal@45 9215 this.queryStateCommands[n] = {func : f, scope : s || this};
bgneal@45 9216 },
bgneal@45 9217
bgneal@45 9218 addQueryValueHandler : function(n, f, s) {
bgneal@45 9219 this.queryValueCommands[n] = {func : f, scope : s || this};
bgneal@45 9220 },
bgneal@45 9221
bgneal@45 9222 addShortcut : function(pa, desc, cmd_func, sc) {
bgneal@45 9223 var t = this, c;
bgneal@45 9224
bgneal@45 9225 if (!t.settings.custom_shortcuts)
bgneal@45 9226 return false;
bgneal@45 9227
bgneal@45 9228 t.shortcuts = t.shortcuts || {};
bgneal@45 9229
bgneal@45 9230 if (is(cmd_func, 'string')) {
bgneal@45 9231 c = cmd_func;
bgneal@45 9232
bgneal@45 9233 cmd_func = function() {
bgneal@45 9234 t.execCommand(c, false, null);
bgneal@45 9235 };
bgneal@45 9236 }
bgneal@45 9237
bgneal@45 9238 if (is(cmd_func, 'object')) {
bgneal@45 9239 c = cmd_func;
bgneal@45 9240
bgneal@45 9241 cmd_func = function() {
bgneal@45 9242 t.execCommand(c[0], c[1], c[2]);
bgneal@45 9243 };
bgneal@45 9244 }
bgneal@45 9245
bgneal@45 9246 each(explode(pa), function(pa) {
bgneal@45 9247 var o = {
bgneal@45 9248 func : cmd_func,
bgneal@45 9249 scope : sc || this,
bgneal@45 9250 desc : desc,
bgneal@45 9251 alt : false,
bgneal@45 9252 ctrl : false,
bgneal@45 9253 shift : false
bgneal@45 9254 };
bgneal@45 9255
bgneal@45 9256 each(explode(pa, '+'), function(v) {
bgneal@45 9257 switch (v) {
bgneal@45 9258 case 'alt':
bgneal@45 9259 case 'ctrl':
bgneal@45 9260 case 'shift':
bgneal@45 9261 o[v] = true;
bgneal@45 9262 break;
bgneal@45 9263
bgneal@45 9264 default:
bgneal@45 9265 o.charCode = v.charCodeAt(0);
bgneal@45 9266 o.keyCode = v.toUpperCase().charCodeAt(0);
bgneal@45 9267 }
bgneal@45 9268 });
bgneal@45 9269
bgneal@45 9270 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
bgneal@45 9271 });
bgneal@45 9272
bgneal@45 9273 return true;
bgneal@45 9274 },
bgneal@45 9275
bgneal@45 9276 execCommand : function(cmd, ui, val, a) {
bgneal@45 9277 var t = this, s = 0, o, st;
bgneal@45 9278
bgneal@45 9279 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
bgneal@45 9280 t.focus();
bgneal@45 9281
bgneal@45 9282 o = {};
bgneal@45 9283 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
bgneal@45 9284 if (o.terminate)
bgneal@45 9285 return false;
bgneal@45 9286
bgneal@45 9287 // Command callback
bgneal@45 9288 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
bgneal@45 9289 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9290 return true;
bgneal@45 9291 }
bgneal@45 9292
bgneal@45 9293 // Registred commands
bgneal@45 9294 if (o = t.execCommands[cmd]) {
bgneal@45 9295 st = o.func.call(o.scope, ui, val);
bgneal@45 9296
bgneal@45 9297 // Fall through on true
bgneal@45 9298 if (st !== true) {
bgneal@45 9299 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9300 return st;
bgneal@45 9301 }
bgneal@45 9302 }
bgneal@45 9303
bgneal@45 9304 // Plugin commands
bgneal@45 9305 each(t.plugins, function(p) {
bgneal@45 9306 if (p.execCommand && p.execCommand(cmd, ui, val)) {
bgneal@45 9307 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9308 s = 1;
bgneal@45 9309 return false;
bgneal@45 9310 }
bgneal@45 9311 });
bgneal@45 9312
bgneal@45 9313 if (s)
bgneal@45 9314 return true;
bgneal@45 9315
bgneal@45 9316 // Theme commands
bgneal@45 9317 if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
bgneal@45 9318 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9319 return true;
bgneal@45 9320 }
bgneal@45 9321
bgneal@45 9322 // Execute global commands
bgneal@45 9323 if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {
bgneal@45 9324 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9325 return true;
bgneal@45 9326 }
bgneal@45 9327
bgneal@45 9328 // Editor commands
bgneal@45 9329 if (t.editorCommands.execCommand(cmd, ui, val)) {
bgneal@45 9330 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9331 return true;
bgneal@45 9332 }
bgneal@45 9333
bgneal@45 9334 // Browser commands
bgneal@45 9335 t.getDoc().execCommand(cmd, ui, val);
bgneal@45 9336 t.onExecCommand.dispatch(t, cmd, ui, val, a);
bgneal@45 9337 },
bgneal@45 9338
bgneal@45 9339 queryCommandState : function(c) {
bgneal@45 9340 var t = this, o, s;
bgneal@45 9341
bgneal@45 9342 // Is hidden then return undefined
bgneal@45 9343 if (t._isHidden())
bgneal@45 9344 return;
bgneal@45 9345
bgneal@45 9346 // Registred commands
bgneal@45 9347 if (o = t.queryStateCommands[c]) {
bgneal@45 9348 s = o.func.call(o.scope);
bgneal@45 9349
bgneal@45 9350 // Fall though on true
bgneal@45 9351 if (s !== true)
bgneal@45 9352 return s;
bgneal@45 9353 }
bgneal@45 9354
bgneal@45 9355 // Registred commands
bgneal@45 9356 o = t.editorCommands.queryCommandState(c);
bgneal@45 9357 if (o !== -1)
bgneal@45 9358 return o;
bgneal@45 9359
bgneal@45 9360 // Browser commands
bgneal@45 9361 try {
bgneal@45 9362 return this.getDoc().queryCommandState(c);
bgneal@45 9363 } catch (ex) {
bgneal@45 9364 // Fails sometimes see bug: 1896577
bgneal@45 9365 }
bgneal@45 9366 },
bgneal@45 9367
bgneal@45 9368 queryCommandValue : function(c) {
bgneal@45 9369 var t = this, o, s;
bgneal@45 9370
bgneal@45 9371 // Is hidden then return undefined
bgneal@45 9372 if (t._isHidden())
bgneal@45 9373 return;
bgneal@45 9374
bgneal@45 9375 // Registred commands
bgneal@45 9376 if (o = t.queryValueCommands[c]) {
bgneal@45 9377 s = o.func.call(o.scope);
bgneal@45 9378
bgneal@45 9379 // Fall though on true
bgneal@45 9380 if (s !== true)
bgneal@45 9381 return s;
bgneal@45 9382 }
bgneal@45 9383
bgneal@45 9384 // Registred commands
bgneal@45 9385 o = t.editorCommands.queryCommandValue(c);
bgneal@45 9386 if (is(o))
bgneal@45 9387 return o;
bgneal@45 9388
bgneal@45 9389 // Browser commands
bgneal@45 9390 try {
bgneal@45 9391 return this.getDoc().queryCommandValue(c);
bgneal@45 9392 } catch (ex) {
bgneal@45 9393 // Fails sometimes see bug: 1896577
bgneal@45 9394 }
bgneal@45 9395 },
bgneal@45 9396
bgneal@45 9397 show : function() {
bgneal@45 9398 var t = this;
bgneal@45 9399
bgneal@45 9400 DOM.show(t.getContainer());
bgneal@45 9401 DOM.hide(t.id);
bgneal@45 9402 t.load();
bgneal@45 9403 },
bgneal@45 9404
bgneal@45 9405 hide : function() {
bgneal@45 9406 var t = this, d = t.getDoc();
bgneal@45 9407
bgneal@45 9408 // Fixed bug where IE has a blinking cursor left from the editor
bgneal@45 9409 if (isIE && d)
bgneal@45 9410 d.execCommand('SelectAll');
bgneal@45 9411
bgneal@45 9412 // We must save before we hide so Safari doesn't crash
bgneal@45 9413 t.save();
bgneal@45 9414 DOM.hide(t.getContainer());
bgneal@45 9415 DOM.setStyle(t.id, 'display', t.orgDisplay);
bgneal@45 9416 },
bgneal@45 9417
bgneal@45 9418 isHidden : function() {
bgneal@45 9419 return !DOM.isHidden(this.id);
bgneal@45 9420 },
bgneal@45 9421
bgneal@45 9422 setProgressState : function(b, ti, o) {
bgneal@45 9423 this.onSetProgressState.dispatch(this, b, ti, o);
bgneal@45 9424
bgneal@45 9425 return b;
bgneal@45 9426 },
bgneal@45 9427
bgneal@45 9428 resizeToContent : function() {
bgneal@45 9429 var t = this;
bgneal@45 9430
bgneal@45 9431 DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight);
bgneal@45 9432 },
bgneal@45 9433
bgneal@45 9434 load : function(o) {
bgneal@45 9435 var t = this, e = t.getElement(), h;
bgneal@45 9436
bgneal@45 9437 if (e) {
bgneal@45 9438 o = o || {};
bgneal@45 9439 o.load = true;
bgneal@45 9440
bgneal@45 9441 // Double encode existing entities in the value
bgneal@45 9442 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
bgneal@45 9443 o.element = e;
bgneal@45 9444
bgneal@45 9445 if (!o.no_events)
bgneal@45 9446 t.onLoadContent.dispatch(t, o);
bgneal@45 9447
bgneal@45 9448 o.element = e = null;
bgneal@45 9449
bgneal@45 9450 return h;
bgneal@45 9451 }
bgneal@45 9452 },
bgneal@45 9453
bgneal@45 9454 save : function(o) {
bgneal@45 9455 var t = this, e = t.getElement(), h, f;
bgneal@45 9456
bgneal@45 9457 if (!e || !t.initialized)
bgneal@45 9458 return;
bgneal@45 9459
bgneal@45 9460 o = o || {};
bgneal@45 9461 o.save = true;
bgneal@45 9462
bgneal@45 9463 // Add undo level will trigger onchange event
bgneal@45 9464 if (!o.no_events) {
bgneal@45 9465 t.undoManager.typing = 0;
bgneal@45 9466 t.undoManager.add();
bgneal@45 9467 }
bgneal@45 9468
bgneal@45 9469 o.element = e;
bgneal@45 9470 h = o.content = t.getContent(o);
bgneal@45 9471
bgneal@45 9472 if (!o.no_events)
bgneal@45 9473 t.onSaveContent.dispatch(t, o);
bgneal@45 9474
bgneal@45 9475 h = o.content;
bgneal@45 9476
bgneal@45 9477 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
bgneal@45 9478 e.innerHTML = h;
bgneal@45 9479
bgneal@45 9480 // Update hidden form element
bgneal@45 9481 if (f = DOM.getParent(t.id, 'form')) {
bgneal@45 9482 each(f.elements, function(e) {
bgneal@45 9483 if (e.name == t.id) {
bgneal@45 9484 e.value = h;
bgneal@45 9485 return false;
bgneal@45 9486 }
bgneal@45 9487 });
bgneal@45 9488 }
bgneal@45 9489 } else
bgneal@45 9490 e.value = h;
bgneal@45 9491
bgneal@45 9492 o.element = e = null;
bgneal@45 9493
bgneal@45 9494 return h;
bgneal@45 9495 },
bgneal@45 9496
bgneal@45 9497 setContent : function(h, o) {
bgneal@45 9498 var t = this;
bgneal@45 9499
bgneal@45 9500 o = o || {};
bgneal@45 9501 o.format = o.format || 'html';
bgneal@45 9502 o.set = true;
bgneal@45 9503 o.content = h;
bgneal@45 9504
bgneal@45 9505 if (!o.no_events)
bgneal@45 9506 t.onBeforeSetContent.dispatch(t, o);
bgneal@45 9507
bgneal@45 9508 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
bgneal@45 9509 // It will also be impossible to place the caret in the editor unless there is a BR element present
bgneal@45 9510 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
bgneal@45 9511 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
bgneal@45 9512 o.format = 'raw';
bgneal@45 9513 }
bgneal@45 9514
bgneal@45 9515 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
bgneal@45 9516
bgneal@45 9517 if (o.format != 'raw' && t.settings.cleanup) {
bgneal@45 9518 o.getInner = true;
bgneal@45 9519 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
bgneal@45 9520 }
bgneal@45 9521
bgneal@45 9522 if (!o.no_events)
bgneal@45 9523 t.onSetContent.dispatch(t, o);
bgneal@45 9524
bgneal@45 9525 return o.content;
bgneal@45 9526 },
bgneal@45 9527
bgneal@45 9528 getContent : function(o) {
bgneal@45 9529 var t = this, h;
bgneal@45 9530
bgneal@45 9531 o = o || {};
bgneal@45 9532 o.format = o.format || 'html';
bgneal@45 9533 o.get = true;
bgneal@45 9534
bgneal@45 9535 if (!o.no_events)
bgneal@45 9536 t.onBeforeGetContent.dispatch(t, o);
bgneal@45 9537
bgneal@45 9538 if (o.format != 'raw' && t.settings.cleanup) {
bgneal@45 9539 o.getInner = true;
bgneal@45 9540 h = t.serializer.serialize(t.getBody(), o);
bgneal@45 9541 } else
bgneal@45 9542 h = t.getBody().innerHTML;
bgneal@45 9543
bgneal@45 9544 h = h.replace(/^\s*|\s*$/g, '');
bgneal@45 9545 o.content = h;
bgneal@45 9546
bgneal@45 9547 if (!o.no_events)
bgneal@45 9548 t.onGetContent.dispatch(t, o);
bgneal@45 9549
bgneal@45 9550 return o.content;
bgneal@45 9551 },
bgneal@45 9552
bgneal@45 9553 isDirty : function() {
bgneal@45 9554 var t = this;
bgneal@45 9555
bgneal@45 9556 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
bgneal@45 9557 },
bgneal@45 9558
bgneal@45 9559 getContainer : function() {
bgneal@45 9560 var t = this;
bgneal@45 9561
bgneal@45 9562 if (!t.container)
bgneal@45 9563 t.container = DOM.get(t.editorContainer || t.id + '_parent');
bgneal@45 9564
bgneal@45 9565 return t.container;
bgneal@45 9566 },
bgneal@45 9567
bgneal@45 9568 getContentAreaContainer : function() {
bgneal@45 9569 return this.contentAreaContainer;
bgneal@45 9570 },
bgneal@45 9571
bgneal@45 9572 getElement : function() {
bgneal@45 9573 return DOM.get(this.settings.content_element || this.id);
bgneal@45 9574 },
bgneal@45 9575
bgneal@45 9576 getWin : function() {
bgneal@45 9577 var t = this, e;
bgneal@45 9578
bgneal@45 9579 if (!t.contentWindow) {
bgneal@45 9580 e = DOM.get(t.id + "_ifr");
bgneal@45 9581
bgneal@45 9582 if (e)
bgneal@45 9583 t.contentWindow = e.contentWindow;
bgneal@45 9584 }
bgneal@45 9585
bgneal@45 9586 return t.contentWindow;
bgneal@45 9587 },
bgneal@45 9588
bgneal@45 9589 getDoc : function() {
bgneal@45 9590 var t = this, w;
bgneal@45 9591
bgneal@45 9592 if (!t.contentDocument) {
bgneal@45 9593 w = t.getWin();
bgneal@45 9594
bgneal@45 9595 if (w)
bgneal@45 9596 t.contentDocument = w.document;
bgneal@45 9597 }
bgneal@45 9598
bgneal@45 9599 return t.contentDocument;
bgneal@45 9600 },
bgneal@45 9601
bgneal@45 9602 getBody : function() {
bgneal@45 9603 return this.bodyElement || this.getDoc().body;
bgneal@45 9604 },
bgneal@45 9605
bgneal@45 9606 convertURL : function(u, n, e) {
bgneal@45 9607 var t = this, s = t.settings;
bgneal@45 9608
bgneal@45 9609 // Use callback instead
bgneal@45 9610 if (s.urlconverter_callback)
bgneal@45 9611 return t.execCallback('urlconverter_callback', u, e, true, n);
bgneal@45 9612
bgneal@45 9613 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
bgneal@45 9614 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
bgneal@45 9615 return u;
bgneal@45 9616
bgneal@45 9617 // Convert to relative
bgneal@45 9618 if (s.relative_urls)
bgneal@45 9619 return t.documentBaseURI.toRelative(u);
bgneal@45 9620
bgneal@45 9621 // Convert to absolute
bgneal@45 9622 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
bgneal@45 9623
bgneal@45 9624 return u;
bgneal@45 9625 },
bgneal@45 9626
bgneal@45 9627 addVisual : function(e) {
bgneal@45 9628 var t = this, s = t.settings;
bgneal@45 9629
bgneal@45 9630 e = e || t.getBody();
bgneal@45 9631
bgneal@45 9632 if (!is(t.hasVisual))
bgneal@45 9633 t.hasVisual = s.visual;
bgneal@45 9634
bgneal@45 9635 each(t.dom.select('table,a', e), function(e) {
bgneal@45 9636 var v;
bgneal@45 9637
bgneal@45 9638 switch (e.nodeName) {
bgneal@45 9639 case 'TABLE':
bgneal@45 9640 v = t.dom.getAttrib(e, 'border');
bgneal@45 9641
bgneal@45 9642 if (!v || v == '0') {
bgneal@45 9643 if (t.hasVisual)
bgneal@45 9644 t.dom.addClass(e, s.visual_table_class);
bgneal@45 9645 else
bgneal@45 9646 t.dom.removeClass(e, s.visual_table_class);
bgneal@45 9647 }
bgneal@45 9648
bgneal@45 9649 return;
bgneal@45 9650
bgneal@45 9651 case 'A':
bgneal@45 9652 v = t.dom.getAttrib(e, 'name');
bgneal@45 9653
bgneal@45 9654 if (v) {
bgneal@45 9655 if (t.hasVisual)
bgneal@45 9656 t.dom.addClass(e, 'mceItemAnchor');
bgneal@45 9657 else
bgneal@45 9658 t.dom.removeClass(e, 'mceItemAnchor');
bgneal@45 9659 }
bgneal@45 9660
bgneal@45 9661 return;
bgneal@45 9662 }
bgneal@45 9663 });
bgneal@45 9664
bgneal@45 9665 t.onVisualAid.dispatch(t, e, t.hasVisual);
bgneal@45 9666 },
bgneal@45 9667
bgneal@45 9668 remove : function() {
bgneal@45 9669 var t = this, e = t.getContainer();
bgneal@45 9670
bgneal@45 9671 t.removed = 1; // Cancels post remove event execution
bgneal@45 9672 t.hide();
bgneal@45 9673
bgneal@45 9674 t.execCallback('remove_instance_callback', t);
bgneal@45 9675 t.onRemove.dispatch(t);
bgneal@45 9676
bgneal@45 9677 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
bgneal@45 9678 t.onExecCommand.listeners = [];
bgneal@45 9679
bgneal@45 9680 EditorManager.remove(t);
bgneal@45 9681 DOM.remove(e);
bgneal@45 9682 },
bgneal@45 9683
bgneal@45 9684 destroy : function(s) {
bgneal@45 9685 var t = this;
bgneal@45 9686
bgneal@45 9687 // One time is enough
bgneal@45 9688 if (t.destroyed)
bgneal@45 9689 return;
bgneal@45 9690
bgneal@45 9691 if (!s) {
bgneal@45 9692 tinymce.removeUnload(t.destroy);
bgneal@45 9693 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
bgneal@45 9694
bgneal@45 9695 // Manual destroy
bgneal@45 9696 if (t.theme && t.theme.destroy)
bgneal@45 9697 t.theme.destroy();
bgneal@45 9698
bgneal@45 9699 // Destroy controls, selection and dom
bgneal@45 9700 t.controlManager.destroy();
bgneal@45 9701 t.selection.destroy();
bgneal@45 9702 t.dom.destroy();
bgneal@45 9703
bgneal@45 9704 // Remove all events
bgneal@45 9705
bgneal@45 9706 // Don't clear the window or document if content editable
bgneal@45 9707 // is enabled since other instances might still be present
bgneal@45 9708 if (!t.settings.content_editable) {
bgneal@45 9709 Event.clear(t.getWin());
bgneal@45 9710 Event.clear(t.getDoc());
bgneal@45 9711 }
bgneal@45 9712
bgneal@45 9713 Event.clear(t.getBody());
bgneal@45 9714 Event.clear(t.formElement);
bgneal@45 9715 }
bgneal@45 9716
bgneal@45 9717 if (t.formElement) {
bgneal@45 9718 t.formElement.submit = t.formElement._mceOldSubmit;
bgneal@45 9719 t.formElement._mceOldSubmit = null;
bgneal@45 9720 }
bgneal@45 9721
bgneal@45 9722 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
bgneal@45 9723
bgneal@45 9724 if (t.selection)
bgneal@45 9725 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
bgneal@45 9726
bgneal@45 9727 t.destroyed = 1;
bgneal@45 9728 },
bgneal@45 9729
bgneal@45 9730 // Internal functions
bgneal@45 9731
bgneal@45 9732 _addEvents : function() {
bgneal@45 9733 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
bgneal@45 9734 var t = this, i, s = t.settings, lo = {
bgneal@45 9735 mouseup : 'onMouseUp',
bgneal@45 9736 mousedown : 'onMouseDown',
bgneal@45 9737 click : 'onClick',
bgneal@45 9738 keyup : 'onKeyUp',
bgneal@45 9739 keydown : 'onKeyDown',
bgneal@45 9740 keypress : 'onKeyPress',
bgneal@45 9741 submit : 'onSubmit',
bgneal@45 9742 reset : 'onReset',
bgneal@45 9743 contextmenu : 'onContextMenu',
bgneal@45 9744 dblclick : 'onDblClick',
bgneal@45 9745 paste : 'onPaste' // Doesn't work in all browsers yet
bgneal@45 9746 };
bgneal@45 9747
bgneal@45 9748 function eventHandler(e, o) {
bgneal@45 9749 var ty = e.type;
bgneal@45 9750
bgneal@45 9751 // Don't fire events when it's removed
bgneal@45 9752 if (t.removed)
bgneal@45 9753 return;
bgneal@45 9754
bgneal@45 9755 // Generic event handler
bgneal@45 9756 if (t.onEvent.dispatch(t, e, o) !== false) {
bgneal@45 9757 // Specific event handler
bgneal@45 9758 t[lo[e.fakeType || e.type]].dispatch(t, e, o);
bgneal@45 9759 }
bgneal@45 9760 };
bgneal@45 9761
bgneal@45 9762 // Add DOM events
bgneal@45 9763 each(lo, function(v, k) {
bgneal@45 9764 switch (k) {
bgneal@45 9765 case 'contextmenu':
bgneal@45 9766 if (tinymce.isOpera) {
bgneal@45 9767 // Fake contextmenu on Opera
bgneal@45 9768 Event.add(t.getBody(), 'mousedown', function(e) {
bgneal@45 9769 if (e.ctrlKey) {
bgneal@45 9770 e.fakeType = 'contextmenu';
bgneal@45 9771 eventHandler(e);
bgneal@45 9772 }
bgneal@45 9773 });
bgneal@45 9774 } else
bgneal@45 9775 Event.add(t.getBody(), k, eventHandler);
bgneal@45 9776 break;
bgneal@45 9777
bgneal@45 9778 case 'paste':
bgneal@45 9779 Event.add(t.getBody(), k, function(e) {
bgneal@45 9780 var tx, h, el, r;
bgneal@45 9781
bgneal@45 9782 // Get plain text data
bgneal@45 9783 if (e.clipboardData)
bgneal@45 9784 tx = e.clipboardData.getData('text/plain');
bgneal@45 9785 else if (tinymce.isIE)
bgneal@45 9786 tx = t.getWin().clipboardData.getData('Text');
bgneal@45 9787
bgneal@45 9788 // Get HTML data
bgneal@45 9789 /*if (tinymce.isIE) {
bgneal@45 9790 el = DOM.add(DOM.doc.body, 'div', {style : 'visibility:hidden;overflow:hidden;position:absolute;width:1px;height:1px'});
bgneal@45 9791 r = DOM.doc.body.createTextRange();
bgneal@45 9792 r.moveToElementText(el);
bgneal@45 9793 r.execCommand('Paste');
bgneal@45 9794 h = el.innerHTML;
bgneal@45 9795 DOM.remove(el);
bgneal@45 9796 }*/
bgneal@45 9797
bgneal@45 9798 eventHandler(e, {text : tx, html : h});
bgneal@45 9799 });
bgneal@45 9800 break;
bgneal@45 9801
bgneal@45 9802 case 'submit':
bgneal@45 9803 case 'reset':
bgneal@45 9804 Event.add(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
bgneal@45 9805 break;
bgneal@45 9806
bgneal@45 9807 default:
bgneal@45 9808 Event.add(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
bgneal@45 9809 }
bgneal@45 9810 });
bgneal@45 9811
bgneal@45 9812 Event.add(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
bgneal@45 9813 t.focus(true);
bgneal@45 9814 });
bgneal@45 9815
bgneal@45 9816
bgneal@45 9817 // Fixes bug where a specified document_base_uri could result in broken images
bgneal@45 9818 // This will also fix drag drop of images in Gecko
bgneal@45 9819 if (tinymce.isGecko) {
bgneal@45 9820 // Convert all images to absolute URLs
bgneal@45 9821 /* t.onSetContent.add(function(ed, o) {
bgneal@45 9822 each(ed.dom.select('img'), function(e) {
bgneal@45 9823 var v;
bgneal@45 9824
bgneal@45 9825 if (v = e.getAttribute('mce_src'))
bgneal@45 9826 e.src = t.documentBaseURI.toAbsolute(v);
bgneal@45 9827 })
bgneal@45 9828 });*/
bgneal@45 9829
bgneal@45 9830 Event.add(t.getDoc(), 'DOMNodeInserted', function(e) {
bgneal@45 9831 var v;
bgneal@45 9832
bgneal@45 9833 e = e.target;
bgneal@45 9834
bgneal@45 9835 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))
bgneal@45 9836 e.src = t.documentBaseURI.toAbsolute(v);
bgneal@45 9837 });
bgneal@45 9838 }
bgneal@45 9839
bgneal@45 9840 // Set various midas options in Gecko
bgneal@45 9841 if (isGecko) {
bgneal@45 9842 function setOpts() {
bgneal@45 9843 var t = this, d = t.getDoc(), s = t.settings;
bgneal@45 9844
bgneal@45 9845 if (isGecko && !s.readonly) {
bgneal@45 9846 if (t._isHidden()) {
bgneal@45 9847 try {
bgneal@45 9848 if (!s.content_editable)
bgneal@45 9849 d.designMode = 'On';
bgneal@45 9850 } catch (ex) {
bgneal@45 9851 // Fails if it's hidden
bgneal@45 9852 }
bgneal@45 9853 }
bgneal@45 9854
bgneal@45 9855 try {
bgneal@45 9856 // Try new Gecko method
bgneal@45 9857 d.execCommand("styleWithCSS", 0, false);
bgneal@45 9858 } catch (ex) {
bgneal@45 9859 // Use old method
bgneal@45 9860 if (!t._isHidden())
bgneal@45 9861 try {d.execCommand("useCSS", 0, true);} catch (ex) {}
bgneal@45 9862 }
bgneal@45 9863
bgneal@45 9864 if (!s.table_inline_editing)
bgneal@45 9865 try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
bgneal@45 9866
bgneal@45 9867 if (!s.object_resizing)
bgneal@45 9868 try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
bgneal@45 9869 }
bgneal@45 9870 };
bgneal@45 9871
bgneal@45 9872 t.onBeforeExecCommand.add(setOpts);
bgneal@45 9873 t.onMouseDown.add(setOpts);
bgneal@45 9874 }
bgneal@45 9875
bgneal@45 9876 // Add node change handlers
bgneal@45 9877 t.onMouseUp.add(t.nodeChanged);
bgneal@45 9878 t.onClick.add(t.nodeChanged);
bgneal@45 9879 t.onKeyUp.add(function(ed, e) {
bgneal@45 9880 var c = e.keyCode;
bgneal@45 9881
bgneal@45 9882 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)
bgneal@45 9883 t.nodeChanged();
bgneal@45 9884 });
bgneal@45 9885
bgneal@45 9886 // Add reset handler
bgneal@45 9887 t.onReset.add(function() {
bgneal@45 9888 t.setContent(t.startContent, {format : 'raw'});
bgneal@45 9889 });
bgneal@45 9890
bgneal@45 9891 // Add shortcuts
bgneal@45 9892 if (s.custom_shortcuts) {
bgneal@45 9893 if (s.custom_undo_redo_keyboard_shortcuts) {
bgneal@45 9894 t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
bgneal@45 9895 t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
bgneal@45 9896 }
bgneal@45 9897
bgneal@45 9898 // Add default shortcuts for gecko
bgneal@45 9899 if (isGecko) {
bgneal@45 9900 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
bgneal@45 9901 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
bgneal@45 9902 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
bgneal@45 9903 }
bgneal@45 9904
bgneal@45 9905 // BlockFormat shortcuts keys
bgneal@45 9906 for (i=1; i<=6; i++)
bgneal@45 9907 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);
bgneal@45 9908
bgneal@45 9909 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
bgneal@45 9910 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
bgneal@45 9911 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
bgneal@45 9912
bgneal@45 9913 function find(e) {
bgneal@45 9914 var v = null;
bgneal@45 9915
bgneal@45 9916 if (!e.altKey && !e.ctrlKey && !e.metaKey)
bgneal@45 9917 return v;
bgneal@45 9918
bgneal@45 9919 each(t.shortcuts, function(o) {
bgneal@45 9920 if (tinymce.isMac && o.ctrl != e.metaKey)
bgneal@45 9921 return;
bgneal@45 9922 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
bgneal@45 9923 return;
bgneal@45 9924
bgneal@45 9925 if (o.alt != e.altKey)
bgneal@45 9926 return;
bgneal@45 9927
bgneal@45 9928 if (o.shift != e.shiftKey)
bgneal@45 9929 return;
bgneal@45 9930
bgneal@45 9931 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
bgneal@45 9932 v = o;
bgneal@45 9933 return false;
bgneal@45 9934 }
bgneal@45 9935 });
bgneal@45 9936
bgneal@45 9937 return v;
bgneal@45 9938 };
bgneal@45 9939
bgneal@45 9940 t.onKeyUp.add(function(ed, e) {
bgneal@45 9941 var o = find(e);
bgneal@45 9942
bgneal@45 9943 if (o)
bgneal@45 9944 return Event.cancel(e);
bgneal@45 9945 });
bgneal@45 9946
bgneal@45 9947 t.onKeyPress.add(function(ed, e) {
bgneal@45 9948 var o = find(e);
bgneal@45 9949
bgneal@45 9950 if (o)
bgneal@45 9951 return Event.cancel(e);
bgneal@45 9952 });
bgneal@45 9953
bgneal@45 9954 t.onKeyDown.add(function(ed, e) {
bgneal@45 9955 var o = find(e);
bgneal@45 9956
bgneal@45 9957 if (o) {
bgneal@45 9958 o.func.call(o.scope);
bgneal@45 9959 return Event.cancel(e);
bgneal@45 9960 }
bgneal@45 9961 });
bgneal@45 9962 }
bgneal@45 9963
bgneal@45 9964 if (tinymce.isIE) {
bgneal@45 9965 // Fix so resize will only update the width and height attributes not the styles of an image
bgneal@45 9966 // It will also block mceItemNoResize items
bgneal@45 9967 Event.add(t.getDoc(), 'controlselect', function(e) {
bgneal@45 9968 var re = t.resizeInfo, cb;
bgneal@45 9969
bgneal@45 9970 e = e.target;
bgneal@45 9971
bgneal@45 9972 // Don't do this action for non image elements
bgneal@45 9973 if (e.nodeName !== 'IMG')
bgneal@45 9974 return;
bgneal@45 9975
bgneal@45 9976 if (re)
bgneal@45 9977 Event.remove(re.node, re.ev, re.cb);
bgneal@45 9978
bgneal@45 9979 if (!t.dom.hasClass(e, 'mceItemNoResize')) {
bgneal@45 9980 ev = 'resizeend';
bgneal@45 9981 cb = Event.add(e, ev, function(e) {
bgneal@45 9982 var v;
bgneal@45 9983
bgneal@45 9984 e = e.target;
bgneal@45 9985
bgneal@45 9986 if (v = t.dom.getStyle(e, 'width')) {
bgneal@45 9987 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
bgneal@45 9988 t.dom.setStyle(e, 'width', '');
bgneal@45 9989 }
bgneal@45 9990
bgneal@45 9991 if (v = t.dom.getStyle(e, 'height')) {
bgneal@45 9992 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
bgneal@45 9993 t.dom.setStyle(e, 'height', '');
bgneal@45 9994 }
bgneal@45 9995 });
bgneal@45 9996 } else {
bgneal@45 9997 ev = 'resizestart';
bgneal@45 9998 cb = Event.add(e, 'resizestart', Event.cancel, Event);
bgneal@45 9999 }
bgneal@45 10000
bgneal@45 10001 re = t.resizeInfo = {
bgneal@45 10002 node : e,
bgneal@45 10003 ev : ev,
bgneal@45 10004 cb : cb
bgneal@45 10005 };
bgneal@45 10006 });
bgneal@45 10007
bgneal@45 10008 t.onKeyDown.add(function(ed, e) {
bgneal@45 10009 switch (e.keyCode) {
bgneal@45 10010 case 8:
bgneal@45 10011 // Fix IE control + backspace browser bug
bgneal@45 10012 if (t.selection.getRng().item) {
bgneal@45 10013 t.selection.getRng().item(0).removeNode();
bgneal@45 10014 return Event.cancel(e);
bgneal@45 10015 }
bgneal@45 10016 }
bgneal@45 10017 });
bgneal@45 10018 }
bgneal@45 10019
bgneal@45 10020 if (tinymce.isOpera) {
bgneal@45 10021 t.onClick.add(function(ed, e) {
bgneal@45 10022 Event.prevent(e);
bgneal@45 10023 });
bgneal@45 10024 }
bgneal@45 10025
bgneal@45 10026 // Add custom undo/redo handlers
bgneal@45 10027 if (s.custom_undo_redo) {
bgneal@45 10028 function addUndo() {
bgneal@45 10029 t.undoManager.typing = 0;
bgneal@45 10030 t.undoManager.add();
bgneal@45 10031 };
bgneal@45 10032
bgneal@45 10033 // Add undo level on editor blur
bgneal@45 10034 if (tinymce.isIE) {
bgneal@45 10035 Event.add(t.getWin(), 'blur', function(e) {
bgneal@45 10036 var n;
bgneal@45 10037
bgneal@45 10038 // Check added for fullscreen bug
bgneal@45 10039 if (t.selection) {
bgneal@45 10040 n = t.selection.getNode();
bgneal@45 10041
bgneal@45 10042 // Add undo level is selection was lost to another document
bgneal@45 10043 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())
bgneal@45 10044 addUndo();
bgneal@45 10045 }
bgneal@45 10046 });
bgneal@45 10047 } else {
bgneal@45 10048 Event.add(t.getDoc(), 'blur', function() {
bgneal@45 10049 if (t.selection && !t.removed)
bgneal@45 10050 addUndo();
bgneal@45 10051 });
bgneal@45 10052 }
bgneal@45 10053
bgneal@45 10054 t.onMouseDown.add(addUndo);
bgneal@45 10055
bgneal@45 10056 t.onKeyUp.add(function(ed, e) {
bgneal@45 10057 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {
bgneal@45 10058 t.undoManager.typing = 0;
bgneal@45 10059 t.undoManager.add();
bgneal@45 10060 }
bgneal@45 10061 });
bgneal@45 10062
bgneal@45 10063 t.onKeyDown.add(function(ed, e) {
bgneal@45 10064 // Is caracter positon keys
bgneal@45 10065 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
bgneal@45 10066 if (t.undoManager.typing) {
bgneal@45 10067 t.undoManager.add();
bgneal@45 10068 t.undoManager.typing = 0;
bgneal@45 10069 }
bgneal@45 10070
bgneal@45 10071 return;
bgneal@45 10072 }
bgneal@45 10073
bgneal@45 10074 if (!t.undoManager.typing) {
bgneal@45 10075 t.undoManager.add();
bgneal@45 10076 t.undoManager.typing = 1;
bgneal@45 10077 }
bgneal@45 10078 });
bgneal@45 10079 }
bgneal@45 10080 },
bgneal@45 10081
bgneal@45 10082 _convertInlineElements : function() {
bgneal@45 10083 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;
bgneal@45 10084
bgneal@45 10085 function convert(ed, o) {
bgneal@45 10086 if (!s.inline_styles)
bgneal@45 10087 return;
bgneal@45 10088
bgneal@45 10089 if (o.get) {
bgneal@45 10090 each(t.dom.select('table,u,strike', o.node), function(n) {
bgneal@45 10091 switch (n.nodeName) {
bgneal@45 10092 case 'TABLE':
bgneal@45 10093 if (v = dom.getAttrib(n, 'height')) {
bgneal@45 10094 dom.setStyle(n, 'height', v);
bgneal@45 10095 dom.setAttrib(n, 'height', '');
bgneal@45 10096 }
bgneal@45 10097 break;
bgneal@45 10098
bgneal@45 10099 case 'U':
bgneal@45 10100 case 'STRIKE':
bgneal@45 10101 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
bgneal@45 10102 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';
bgneal@45 10103 dom.setAttrib(n, 'mce_style', '');
bgneal@45 10104 dom.setAttrib(n, 'mce_name', 'span');
bgneal@45 10105 break;
bgneal@45 10106 }
bgneal@45 10107 });
bgneal@45 10108 } else if (o.set) {
bgneal@45 10109 each(t.dom.select('table,span', o.node).reverse(), function(n) {
bgneal@45 10110 if (n.nodeName == 'TABLE') {
bgneal@45 10111 if (v = dom.getStyle(n, 'height'))
bgneal@45 10112 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));
bgneal@45 10113 } else {
bgneal@45 10114 // Convert spans to elements
bgneal@45 10115 if (n.style.textDecoration == 'underline')
bgneal@45 10116 na = 'u';
bgneal@45 10117 else if (n.style.textDecoration == 'line-through')
bgneal@45 10118 na = 'strike';
bgneal@45 10119 else
bgneal@45 10120 na = '';
bgneal@45 10121
bgneal@45 10122 if (na) {
bgneal@45 10123 n.style.textDecoration = '';
bgneal@45 10124 dom.setAttrib(n, 'mce_style', '');
bgneal@45 10125
bgneal@45 10126 e = dom.create(na, {
bgneal@45 10127 style : dom.getAttrib(n, 'style')
bgneal@45 10128 });
bgneal@45 10129
bgneal@45 10130 dom.replace(e, n, 1);
bgneal@45 10131 }
bgneal@45 10132 }
bgneal@45 10133 });
bgneal@45 10134 }
bgneal@45 10135 };
bgneal@45 10136
bgneal@45 10137 t.onPreProcess.add(convert);
bgneal@45 10138
bgneal@45 10139 if (!s.cleanup_on_startup) {
bgneal@45 10140 t.onSetContent.add(function(ed, o) {
bgneal@45 10141 if (o.initial)
bgneal@45 10142 convert(t, {node : t.getBody(), set : 1});
bgneal@45 10143 });
bgneal@45 10144 }
bgneal@45 10145 },
bgneal@45 10146
bgneal@45 10147 _convertFonts : function() {
bgneal@45 10148 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;
bgneal@45 10149
bgneal@45 10150 // No need
bgneal@45 10151 if (!s.inline_styles)
bgneal@45 10152 return;
bgneal@45 10153
bgneal@45 10154 // Font pt values and font size names
bgneal@45 10155 fz = [8, 10, 12, 14, 18, 24, 36];
bgneal@45 10156 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
bgneal@45 10157
bgneal@45 10158 if (sl = s.font_size_style_values)
bgneal@45 10159 sl = explode(sl);
bgneal@45 10160
bgneal@45 10161 if (cl = s.font_size_classes)
bgneal@45 10162 cl = explode(cl);
bgneal@45 10163
bgneal@45 10164 function process(no) {
bgneal@45 10165 var n, sp, nl, x;
bgneal@45 10166
bgneal@45 10167 // Keep unit tests happy
bgneal@45 10168 if (!s.inline_styles)
bgneal@45 10169 return;
bgneal@45 10170
bgneal@45 10171 nl = t.dom.select('font', no);
bgneal@45 10172 for (x = nl.length - 1; x >= 0; x--) {
bgneal@45 10173 n = nl[x];
bgneal@45 10174
bgneal@45 10175 sp = dom.create('span', {
bgneal@45 10176 style : dom.getAttrib(n, 'style'),
bgneal@45 10177 'class' : dom.getAttrib(n, 'class')
bgneal@45 10178 });
bgneal@45 10179
bgneal@45 10180 dom.setStyles(sp, {
bgneal@45 10181 fontFamily : dom.getAttrib(n, 'face'),
bgneal@45 10182 color : dom.getAttrib(n, 'color'),
bgneal@45 10183 backgroundColor : n.style.backgroundColor
bgneal@45 10184 });
bgneal@45 10185
bgneal@45 10186 if (n.size) {
bgneal@45 10187 if (sl)
bgneal@45 10188 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
bgneal@45 10189 else
bgneal@45 10190 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);
bgneal@45 10191 }
bgneal@45 10192
bgneal@45 10193 dom.setAttrib(sp, 'mce_style', '');
bgneal@45 10194 dom.replace(sp, n, 1);
bgneal@45 10195 }
bgneal@45 10196 };
bgneal@45 10197
bgneal@45 10198 // Run on cleanup
bgneal@45 10199 t.onPreProcess.add(function(ed, o) {
bgneal@45 10200 if (o.get)
bgneal@45 10201 process(o.node);
bgneal@45 10202 });
bgneal@45 10203
bgneal@45 10204 t.onSetContent.add(function(ed, o) {
bgneal@45 10205 if (o.initial)
bgneal@45 10206 process(o.node);
bgneal@45 10207 });
bgneal@45 10208 },
bgneal@45 10209
bgneal@45 10210 _isHidden : function() {
bgneal@45 10211 var s;
bgneal@45 10212
bgneal@45 10213 if (!isGecko)
bgneal@45 10214 return 0;
bgneal@45 10215
bgneal@45 10216 // Weird, wheres that cursor selection?
bgneal@45 10217 s = this.selection.getSel();
bgneal@45 10218 return (!s || !s.rangeCount || s.rangeCount == 0);
bgneal@45 10219 },
bgneal@45 10220
bgneal@45 10221 // Fix for bug #1867292
bgneal@45 10222 _fixNesting : function(s) {
bgneal@45 10223 var d = [], i;
bgneal@45 10224
bgneal@45 10225 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
bgneal@45 10226 var e;
bgneal@45 10227
bgneal@45 10228 // Handle end element
bgneal@45 10229 if (b === '/') {
bgneal@45 10230 if (!d.length)
bgneal@45 10231 return '';
bgneal@45 10232
bgneal@45 10233 if (c !== d[d.length - 1].tag) {
bgneal@45 10234 for (i=d.length - 1; i>=0; i--) {
bgneal@45 10235 if (d[i].tag === c) {
bgneal@45 10236 d[i].close = 1;
bgneal@45 10237 break;
bgneal@45 10238 }
bgneal@45 10239 }
bgneal@45 10240
bgneal@45 10241 return '';
bgneal@45 10242 } else {
bgneal@45 10243 d.pop();
bgneal@45 10244
bgneal@45 10245 if (d.length && d[d.length - 1].close) {
bgneal@45 10246 a = a + '</' + d[d.length - 1].tag + '>';
bgneal@45 10247 d.pop();
bgneal@45 10248 }
bgneal@45 10249 }
bgneal@45 10250 } else {
bgneal@45 10251 // Ignore these
bgneal@45 10252 if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
bgneal@45 10253 return a;
bgneal@45 10254
bgneal@45 10255 // Ignore closed ones
bgneal@45 10256 if (/\/>$/.test(a))
bgneal@45 10257 return a;
bgneal@45 10258
bgneal@45 10259 d.push({tag : c}); // Push start element
bgneal@45 10260 }
bgneal@45 10261
bgneal@45 10262 return a;
bgneal@45 10263 });
bgneal@45 10264
bgneal@45 10265 // End all open tags
bgneal@45 10266 for (i=d.length - 1; i>=0; i--)
bgneal@45 10267 s += '</' + d[i].tag + '>';
bgneal@45 10268
bgneal@45 10269 return s;
bgneal@45 10270 }
bgneal@45 10271
bgneal@45 10272 });
bgneal@45 10273 })(tinymce);
bgneal@45 10274 (function(tinymce) {
bgneal@45 10275 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;
bgneal@45 10276
bgneal@45 10277 tinymce.create('tinymce.EditorCommands', {
bgneal@45 10278 EditorCommands : function(ed) {
bgneal@45 10279 this.editor = ed;
bgneal@45 10280 },
bgneal@45 10281
bgneal@45 10282 execCommand : function(cmd, ui, val) {
bgneal@45 10283 var t = this, ed = t.editor, f;
bgneal@45 10284
bgneal@45 10285 switch (cmd) {
bgneal@45 10286 // Ignore these
bgneal@45 10287 case 'mceResetDesignMode':
bgneal@45 10288 case 'mceBeginUndoLevel':
bgneal@45 10289 return true;
bgneal@45 10290
bgneal@45 10291 // Ignore these
bgneal@45 10292 case 'unlink':
bgneal@45 10293 t.UnLink();
bgneal@45 10294 return true;
bgneal@45 10295
bgneal@45 10296 // Bundle these together
bgneal@45 10297 case 'JustifyLeft':
bgneal@45 10298 case 'JustifyCenter':
bgneal@45 10299 case 'JustifyRight':
bgneal@45 10300 case 'JustifyFull':
bgneal@45 10301 t.mceJustify(cmd, cmd.substring(7).toLowerCase());
bgneal@45 10302 return true;
bgneal@45 10303
bgneal@45 10304 default:
bgneal@45 10305 f = this[cmd];
bgneal@45 10306
bgneal@45 10307 if (f) {
bgneal@45 10308 f.call(this, ui, val);
bgneal@45 10309 return true;
bgneal@45 10310 }
bgneal@45 10311 }
bgneal@45 10312
bgneal@45 10313 return false;
bgneal@45 10314 },
bgneal@45 10315
bgneal@45 10316 Indent : function() {
bgneal@45 10317 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;
bgneal@45 10318
bgneal@45 10319 // Setup indent level
bgneal@45 10320 iv = ed.settings.indentation;
bgneal@45 10321 iu = /[a-z%]+$/i.exec(iv);
bgneal@45 10322 iv = parseInt(iv);
bgneal@45 10323
bgneal@45 10324 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
bgneal@45 10325 each(s.getSelectedBlocks(), function(e) {
bgneal@45 10326 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
bgneal@45 10327 });
bgneal@45 10328
bgneal@45 10329 return;
bgneal@45 10330 }
bgneal@45 10331
bgneal@45 10332 ed.getDoc().execCommand('Indent', false, null);
bgneal@45 10333
bgneal@45 10334 if (isIE) {
bgneal@45 10335 d.getParent(s.getNode(), function(n) {
bgneal@45 10336 if (n.nodeName == 'BLOCKQUOTE') {
bgneal@45 10337 n.dir = n.style.cssText = '';
bgneal@45 10338 }
bgneal@45 10339 });
bgneal@45 10340 }
bgneal@45 10341 },
bgneal@45 10342
bgneal@45 10343 Outdent : function() {
bgneal@45 10344 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;
bgneal@45 10345
bgneal@45 10346 // Setup indent level
bgneal@45 10347 iv = ed.settings.indentation;
bgneal@45 10348 iu = /[a-z%]+$/i.exec(iv);
bgneal@45 10349 iv = parseInt(iv);
bgneal@45 10350
bgneal@45 10351 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
bgneal@45 10352 each(s.getSelectedBlocks(), function(e) {
bgneal@45 10353 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
bgneal@45 10354 d.setStyle(e, 'paddingLeft', v ? v + iu : '');
bgneal@45 10355 });
bgneal@45 10356
bgneal@45 10357 return;
bgneal@45 10358 }
bgneal@45 10359
bgneal@45 10360 ed.getDoc().execCommand('Outdent', false, null);
bgneal@45 10361 },
bgneal@45 10362
bgneal@45 10363 /*
bgneal@45 10364 mceSetAttribute : function(u, v) {
bgneal@45 10365 var ed = this.editor, d = ed.dom, e;
bgneal@45 10366
bgneal@45 10367 if (e = d.getParent(ed.selection.getNode(), d.isBlock))
bgneal@45 10368 d.setAttrib(e, v.name, v.value);
bgneal@45 10369 },
bgneal@45 10370 */
bgneal@45 10371 mceSetContent : function(u, v) {
bgneal@45 10372 this.editor.setContent(v);
bgneal@45 10373 },
bgneal@45 10374
bgneal@45 10375 mceToggleVisualAid : function() {
bgneal@45 10376 var ed = this.editor;
bgneal@45 10377
bgneal@45 10378 ed.hasVisual = !ed.hasVisual;
bgneal@45 10379 ed.addVisual();
bgneal@45 10380 },
bgneal@45 10381
bgneal@45 10382 mceReplaceContent : function(u, v) {
bgneal@45 10383 var s = this.editor.selection;
bgneal@45 10384
bgneal@45 10385 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
bgneal@45 10386 },
bgneal@45 10387
bgneal@45 10388 mceInsertLink : function(u, v) {
bgneal@45 10389 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'A');
bgneal@45 10390
bgneal@45 10391 if (tinymce.is(v, 'string'))
bgneal@45 10392 v = {href : v};
bgneal@45 10393
bgneal@45 10394 function set(e) {
bgneal@45 10395 each(v, function(v, k) {
bgneal@45 10396 ed.dom.setAttrib(e, k, v);
bgneal@45 10397 });
bgneal@45 10398 };
bgneal@45 10399
bgneal@45 10400 if (!e) {
bgneal@45 10401 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
bgneal@45 10402 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) {
bgneal@45 10403 set(e);
bgneal@45 10404 });
bgneal@45 10405 } else {
bgneal@45 10406 if (v.href)
bgneal@45 10407 set(e);
bgneal@45 10408 else
bgneal@45 10409 ed.dom.remove(e, 1);
bgneal@45 10410 }
bgneal@45 10411 },
bgneal@45 10412
bgneal@45 10413 UnLink : function() {
bgneal@45 10414 var ed = this.editor, s = ed.selection;
bgneal@45 10415
bgneal@45 10416 if (s.isCollapsed())
bgneal@45 10417 s.select(s.getNode());
bgneal@45 10418
bgneal@45 10419 ed.getDoc().execCommand('unlink', false, null);
bgneal@45 10420 s.collapse(0);
bgneal@45 10421 },
bgneal@45 10422
bgneal@45 10423 FontName : function(u, v) {
bgneal@45 10424 var t = this, ed = t.editor, s = ed.selection, e;
bgneal@45 10425
bgneal@45 10426 if (!v) {
bgneal@45 10427 if (s.isCollapsed())
bgneal@45 10428 s.select(s.getNode());
bgneal@45 10429 } else {
bgneal@45 10430 if (ed.settings.convert_fonts_to_spans)
bgneal@45 10431 t._applyInlineStyle('span', {style : {fontFamily : v}});
bgneal@45 10432 else
bgneal@45 10433 ed.getDoc().execCommand('FontName', false, v);
bgneal@45 10434 }
bgneal@45 10435 },
bgneal@45 10436
bgneal@45 10437 FontSize : function(u, v) {
bgneal@45 10438 var ed = this.editor, s = ed.settings, fc, fs;
bgneal@45 10439
bgneal@45 10440 // Use style options instead
bgneal@45 10441 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) {
bgneal@45 10442 fs = tinymce.explode(s.font_size_style_values);
bgneal@45 10443 fc = tinymce.explode(s.font_size_classes);
bgneal@45 10444
bgneal@45 10445 if (fc)
bgneal@45 10446 v = fc[v - 1] || v;
bgneal@45 10447 else
bgneal@45 10448 v = fs[v - 1] || v;
bgneal@45 10449 }
bgneal@45 10450
bgneal@45 10451 if (v >= 1 && v <= 7)
bgneal@45 10452 ed.getDoc().execCommand('FontSize', false, v);
bgneal@45 10453 else
bgneal@45 10454 this._applyInlineStyle('span', {style : {fontSize : v}});
bgneal@45 10455 },
bgneal@45 10456
bgneal@45 10457 queryCommandValue : function(c) {
bgneal@45 10458 var f = this['queryValue' + c];
bgneal@45 10459
bgneal@45 10460 if (f)
bgneal@45 10461 return f.call(this, c);
bgneal@45 10462
bgneal@45 10463 return false;
bgneal@45 10464 },
bgneal@45 10465
bgneal@45 10466 queryCommandState : function(cmd) {
bgneal@45 10467 var f;
bgneal@45 10468
bgneal@45 10469 switch (cmd) {
bgneal@45 10470 // Bundle these together
bgneal@45 10471 case 'JustifyLeft':
bgneal@45 10472 case 'JustifyCenter':
bgneal@45 10473 case 'JustifyRight':
bgneal@45 10474 case 'JustifyFull':
bgneal@45 10475 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
bgneal@45 10476
bgneal@45 10477 default:
bgneal@45 10478 if (f = this['queryState' + cmd])
bgneal@45 10479 return f.call(this, cmd);
bgneal@45 10480 }
bgneal@45 10481
bgneal@45 10482 return -1;
bgneal@45 10483 },
bgneal@45 10484
bgneal@45 10485 _queryState : function(c) {
bgneal@45 10486 try {
bgneal@45 10487 return this.editor.getDoc().queryCommandState(c);
bgneal@45 10488 } catch (ex) {
bgneal@45 10489 // Ignore exception
bgneal@45 10490 }
bgneal@45 10491 },
bgneal@45 10492
bgneal@45 10493 _queryVal : function(c) {
bgneal@45 10494 try {
bgneal@45 10495 return this.editor.getDoc().queryCommandValue(c);
bgneal@45 10496 } catch (ex) {
bgneal@45 10497 // Ignore exception
bgneal@45 10498 }
bgneal@45 10499 },
bgneal@45 10500
bgneal@45 10501 queryValueFontSize : function() {
bgneal@45 10502 var ed = this.editor, v = 0, p;
bgneal@45 10503
bgneal@45 10504 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN'))
bgneal@45 10505 v = p.style.fontSize;
bgneal@45 10506
bgneal@45 10507 if (!v && (isOpera || isWebKit)) {
bgneal@45 10508 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
bgneal@45 10509 v = p.size;
bgneal@45 10510
bgneal@45 10511 return v;
bgneal@45 10512 }
bgneal@45 10513
bgneal@45 10514 return v || this._queryVal('FontSize');
bgneal@45 10515 },
bgneal@45 10516
bgneal@45 10517 queryValueFontName : function() {
bgneal@45 10518 var ed = this.editor, v = 0, p;
bgneal@45 10519
bgneal@45 10520 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
bgneal@45 10521 v = p.face;
bgneal@45 10522
bgneal@45 10523 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN'))
bgneal@45 10524 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
bgneal@45 10525
bgneal@45 10526 if (!v)
bgneal@45 10527 v = this._queryVal('FontName');
bgneal@45 10528
bgneal@45 10529 return v;
bgneal@45 10530 },
bgneal@45 10531
bgneal@45 10532 mceJustify : function(c, v) {
bgneal@45 10533 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;
bgneal@45 10534
bgneal@45 10535 if (ed.settings.inline_styles && this.queryStateJustify(c, v))
bgneal@45 10536 rm = 1;
bgneal@45 10537
bgneal@45 10538 bl = dom.getParent(n, ed.dom.isBlock);
bgneal@45 10539
bgneal@45 10540 if (nn == 'IMG') {
bgneal@45 10541 if (v == 'full')
bgneal@45 10542 return;
bgneal@45 10543
bgneal@45 10544 if (rm) {
bgneal@45 10545 if (v == 'center')
bgneal@45 10546 dom.setStyle(bl || n.parentNode, 'textAlign', '');
bgneal@45 10547
bgneal@45 10548 dom.setStyle(n, 'float', '');
bgneal@45 10549 this.mceRepaint();
bgneal@45 10550 return;
bgneal@45 10551 }
bgneal@45 10552
bgneal@45 10553 if (v == 'center') {
bgneal@45 10554 // Do not change table elements
bgneal@45 10555 if (bl && /^(TD|TH)$/.test(bl.nodeName))
bgneal@45 10556 bl = 0;
bgneal@45 10557
bgneal@45 10558 if (!bl || bl.childNodes.length > 1) {
bgneal@45 10559 nb = dom.create('p');
bgneal@45 10560 nb.appendChild(n.cloneNode(false));
bgneal@45 10561
bgneal@45 10562 if (bl)
bgneal@45 10563 dom.insertAfter(nb, bl);
bgneal@45 10564 else
bgneal@45 10565 dom.insertAfter(nb, n);
bgneal@45 10566
bgneal@45 10567 dom.remove(n);
bgneal@45 10568 n = nb.firstChild;
bgneal@45 10569 bl = nb;
bgneal@45 10570 }
bgneal@45 10571
bgneal@45 10572 dom.setStyle(bl, 'textAlign', v);
bgneal@45 10573 dom.setStyle(n, 'float', '');
bgneal@45 10574 } else {
bgneal@45 10575 dom.setStyle(n, 'float', v);
bgneal@45 10576 dom.setStyle(bl || n.parentNode, 'textAlign', '');
bgneal@45 10577 }
bgneal@45 10578
bgneal@45 10579 this.mceRepaint();
bgneal@45 10580 return;
bgneal@45 10581 }
bgneal@45 10582
bgneal@45 10583 // Handle the alignment outselfs, less quirks in all browsers
bgneal@45 10584 if (ed.settings.inline_styles && ed.settings.forced_root_block) {
bgneal@45 10585 if (rm)
bgneal@45 10586 v = '';
bgneal@45 10587
bgneal@45 10588 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
bgneal@45 10589 dom.setAttrib(e, 'align', '');
bgneal@45 10590 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
bgneal@45 10591 });
bgneal@45 10592
bgneal@45 10593 return;
bgneal@45 10594 } else if (!rm)
bgneal@45 10595 ed.getDoc().execCommand(c, false, null);
bgneal@45 10596
bgneal@45 10597 if (ed.settings.inline_styles) {
bgneal@45 10598 if (rm) {
bgneal@45 10599 dom.getParent(ed.selection.getNode(), function(n) {
bgneal@45 10600 if (n.style && n.style.textAlign)
bgneal@45 10601 dom.setStyle(n, 'textAlign', '');
bgneal@45 10602 });
bgneal@45 10603
bgneal@45 10604 return;
bgneal@45 10605 }
bgneal@45 10606
bgneal@45 10607 each(dom.select('*'), function(n) {
bgneal@45 10608 var v = n.align;
bgneal@45 10609
bgneal@45 10610 if (v) {
bgneal@45 10611 if (v == 'full')
bgneal@45 10612 v = 'justify';
bgneal@45 10613
bgneal@45 10614 dom.setStyle(n, 'textAlign', v);
bgneal@45 10615 dom.setAttrib(n, 'align', '');
bgneal@45 10616 }
bgneal@45 10617 });
bgneal@45 10618 }
bgneal@45 10619 },
bgneal@45 10620
bgneal@45 10621 mceSetCSSClass : function(u, v) {
bgneal@45 10622 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
bgneal@45 10623 },
bgneal@45 10624
bgneal@45 10625 getSelectedElement : function() {
bgneal@45 10626 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;
bgneal@45 10627
bgneal@45 10628 if (se.isCollapsed() || r.item)
bgneal@45 10629 return se.getNode();
bgneal@45 10630
bgneal@45 10631 // Setup regexp
bgneal@45 10632 re = ed.settings.merge_styles_invalid_parents;
bgneal@45 10633 if (tinymce.is(re, 'string'))
bgneal@45 10634 re = new RegExp(re, 'i');
bgneal@45 10635
bgneal@45 10636 if (isIE) {
bgneal@45 10637 r1 = r.duplicate();
bgneal@45 10638 r1.collapse(true);
bgneal@45 10639 sc = r1.parentElement();
bgneal@45 10640
bgneal@45 10641 r2 = r.duplicate();
bgneal@45 10642 r2.collapse(false);
bgneal@45 10643 ec = r2.parentElement();
bgneal@45 10644
bgneal@45 10645 if (sc != ec) {
bgneal@45 10646 r1.move('character', 1);
bgneal@45 10647 sc = r1.parentElement();
bgneal@45 10648 }
bgneal@45 10649
bgneal@45 10650 if (sc == ec) {
bgneal@45 10651 r1 = r.duplicate();
bgneal@45 10652 r1.moveToElementText(sc);
bgneal@45 10653
bgneal@45 10654 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
bgneal@45 10655 return re && re.test(sc.nodeName) ? null : sc;
bgneal@45 10656 }
bgneal@45 10657 } else {
bgneal@45 10658 function getParent(n) {
bgneal@45 10659 return dom.getParent(n, '*');
bgneal@45 10660 };
bgneal@45 10661
bgneal@45 10662 sc = r.startContainer;
bgneal@45 10663 ec = r.endContainer;
bgneal@45 10664 so = r.startOffset;
bgneal@45 10665 eo = r.endOffset;
bgneal@45 10666
bgneal@45 10667 if (!r.collapsed) {
bgneal@45 10668 if (sc == ec) {
bgneal@45 10669 if (so - eo < 2) {
bgneal@45 10670 if (sc.hasChildNodes()) {
bgneal@45 10671 sp = sc.childNodes[so];
bgneal@45 10672 return re && re.test(sp.nodeName) ? null : sp;
bgneal@45 10673 }
bgneal@45 10674 }
bgneal@45 10675 }
bgneal@45 10676 }
bgneal@45 10677
bgneal@45 10678 if (sc.nodeType != 3 || ec.nodeType != 3)
bgneal@45 10679 return null;
bgneal@45 10680
bgneal@45 10681 if (so == 0) {
bgneal@45 10682 sp = getParent(sc);
bgneal@45 10683
bgneal@45 10684 if (sp && sp.firstChild != sc)
bgneal@45 10685 sp = null;
bgneal@45 10686 }
bgneal@45 10687
bgneal@45 10688 if (so == sc.nodeValue.length) {
bgneal@45 10689 e = sc.nextSibling;
bgneal@45 10690
bgneal@45 10691 if (e && e.nodeType == 1)
bgneal@45 10692 sp = sc.nextSibling;
bgneal@45 10693 }
bgneal@45 10694
bgneal@45 10695 if (eo == 0) {
bgneal@45 10696 e = ec.previousSibling;
bgneal@45 10697
bgneal@45 10698 if (e && e.nodeType == 1)
bgneal@45 10699 ep = e;
bgneal@45 10700 }
bgneal@45 10701
bgneal@45 10702 if (eo == ec.nodeValue.length) {
bgneal@45 10703 ep = getParent(ec);
bgneal@45 10704
bgneal@45 10705 if (ep && ep.lastChild != ec)
bgneal@45 10706 ep = null;
bgneal@45 10707 }
bgneal@45 10708
bgneal@45 10709 // Same element
bgneal@45 10710 if (sp == ep)
bgneal@45 10711 return re && sp && re.test(sp.nodeName) ? null : sp;
bgneal@45 10712 }
bgneal@45 10713
bgneal@45 10714 return null;
bgneal@45 10715 },
bgneal@45 10716
bgneal@45 10717 mceSetStyleInfo : function(u, v) {
bgneal@45 10718 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;
bgneal@45 10719
bgneal@45 10720 function set(n, e) {
bgneal@45 10721 if (n.nodeType == 1) {
bgneal@45 10722 switch (v.command) {
bgneal@45 10723 case 'setattrib':
bgneal@45 10724 return dom.setAttrib(n, v.name, v.value);
bgneal@45 10725
bgneal@45 10726 case 'setstyle':
bgneal@45 10727 return dom.setStyle(n, v.name, v.value);
bgneal@45 10728
bgneal@45 10729 case 'removeformat':
bgneal@45 10730 return dom.setAttrib(n, 'class', '');
bgneal@45 10731 }
bgneal@45 10732 }
bgneal@45 10733 };
bgneal@45 10734
bgneal@45 10735 // Setup regexp
bgneal@45 10736 re = ed.settings.merge_styles_invalid_parents;
bgneal@45 10737 if (tinymce.is(re, 'string'))
bgneal@45 10738 re = new RegExp(re, 'i');
bgneal@45 10739
bgneal@45 10740 // Set style info on selected element
bgneal@45 10741 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)
bgneal@45 10742 set(e, 1);
bgneal@45 10743 else {
bgneal@45 10744 // Generate wrappers and set styles on them
bgneal@45 10745 d.execCommand('FontName', false, '__');
bgneal@45 10746 each(dom.select('span,font'), function(n) {
bgneal@45 10747 var sp, e;
bgneal@45 10748
bgneal@45 10749 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
bgneal@45 10750 sp = dom.create(nn, {mce_new : '1'});
bgneal@45 10751
bgneal@45 10752 set(sp);
bgneal@45 10753
bgneal@45 10754 each (n.childNodes, function(n) {
bgneal@45 10755 sp.appendChild(n.cloneNode(true));
bgneal@45 10756 });
bgneal@45 10757
bgneal@45 10758 dom.replace(sp, n);
bgneal@45 10759 }
bgneal@45 10760 });
bgneal@45 10761 }
bgneal@45 10762
bgneal@45 10763 // Remove wrappers inside new ones
bgneal@45 10764 each(dom.select(nn).reverse(), function(n) {
bgneal@45 10765 var p = n.parentNode;
bgneal@45 10766
bgneal@45 10767 // Check if it's an old span in a new wrapper
bgneal@45 10768 if (!dom.getAttrib(n, 'mce_new')) {
bgneal@45 10769 // Find new wrapper
bgneal@45 10770 p = dom.getParent(n, '*[mce_new]');
bgneal@45 10771
bgneal@45 10772 if (p)
bgneal@45 10773 dom.remove(n, 1);
bgneal@45 10774 }
bgneal@45 10775 });
bgneal@45 10776
bgneal@45 10777 // Merge wrappers with parent wrappers
bgneal@45 10778 each(dom.select(nn).reverse(), function(n) {
bgneal@45 10779 var p = n.parentNode;
bgneal@45 10780
bgneal@45 10781 if (!p || !dom.getAttrib(n, 'mce_new'))
bgneal@45 10782 return;
bgneal@45 10783
bgneal@45 10784 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
bgneal@45 10785 return;
bgneal@45 10786
bgneal@45 10787 // Has parent of the same type and only child
bgneal@45 10788 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
bgneal@45 10789 return dom.remove(p, 1);
bgneal@45 10790
bgneal@45 10791 // Has parent that is more suitable to have the class and only child
bgneal@45 10792 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
bgneal@45 10793 set(p); // Set style info on parent instead
bgneal@45 10794 dom.setAttrib(n, 'class', '');
bgneal@45 10795 }
bgneal@45 10796 });
bgneal@45 10797
bgneal@45 10798 // Remove empty wrappers
bgneal@45 10799 each(dom.select(nn).reverse(), function(n) {
bgneal@45 10800 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
bgneal@45 10801 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
bgneal@45 10802 return dom.remove(n, 1);
bgneal@45 10803
bgneal@45 10804 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
bgneal@45 10805 }
bgneal@45 10806 });
bgneal@45 10807
bgneal@45 10808 s.moveToBookmark(b);
bgneal@45 10809 },
bgneal@45 10810
bgneal@45 10811 queryStateJustify : function(c, v) {
bgneal@45 10812 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;
bgneal@45 10813
bgneal@45 10814 if (n && n.nodeName == 'IMG') {
bgneal@45 10815 if (dom.getStyle(n, 'float') == v)
bgneal@45 10816 return 1;
bgneal@45 10817
bgneal@45 10818 return n.parentNode.style.textAlign == v;
bgneal@45 10819 }
bgneal@45 10820
bgneal@45 10821 n = dom.getParent(ed.selection.getStart(), function(n) {
bgneal@45 10822 return n.nodeType == 1 && n.style.textAlign;
bgneal@45 10823 });
bgneal@45 10824
bgneal@45 10825 if (v == 'full')
bgneal@45 10826 v = 'justify';
bgneal@45 10827
bgneal@45 10828 if (ed.settings.inline_styles)
bgneal@45 10829 return (n && n.style.textAlign == v);
bgneal@45 10830
bgneal@45 10831 return this._queryState(c);
bgneal@45 10832 },
bgneal@45 10833
bgneal@45 10834 ForeColor : function(ui, v) {
bgneal@45 10835 var ed = this.editor;
bgneal@45 10836
bgneal@45 10837 if (ed.settings.convert_fonts_to_spans) {
bgneal@45 10838 this._applyInlineStyle('span', {style : {color : v}});
bgneal@45 10839 return;
bgneal@45 10840 } else
bgneal@45 10841 ed.getDoc().execCommand('ForeColor', false, v);
bgneal@45 10842 },
bgneal@45 10843
bgneal@45 10844 HiliteColor : function(ui, val) {
bgneal@45 10845 var t = this, ed = t.editor, d = ed.getDoc();
bgneal@45 10846
bgneal@45 10847 if (ed.settings.convert_fonts_to_spans) {
bgneal@45 10848 this._applyInlineStyle('span', {style : {backgroundColor : val}});
bgneal@45 10849 return;
bgneal@45 10850 }
bgneal@45 10851
bgneal@45 10852 function set(s) {
bgneal@45 10853 if (!isGecko)
bgneal@45 10854 return;
bgneal@45 10855
bgneal@45 10856 try {
bgneal@45 10857 // Try new Gecko method
bgneal@45 10858 d.execCommand("styleWithCSS", 0, s);
bgneal@45 10859 } catch (ex) {
bgneal@45 10860 // Use old
bgneal@45 10861 d.execCommand("useCSS", 0, !s);
bgneal@45 10862 }
bgneal@45 10863 };
bgneal@45 10864
bgneal@45 10865 if (isGecko || isOpera) {
bgneal@45 10866 set(true);
bgneal@45 10867 d.execCommand('hilitecolor', false, val);
bgneal@45 10868 set(false);
bgneal@45 10869 } else
bgneal@45 10870 d.execCommand('BackColor', false, val);
bgneal@45 10871 },
bgneal@45 10872
bgneal@45 10873 FormatBlock : function(ui, val) {
bgneal@45 10874 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
bgneal@45 10875
bgneal@45 10876 function isBlock(n) {
bgneal@45 10877 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
bgneal@45 10878 };
bgneal@45 10879
bgneal@45 10880 bl = dom.getParent(s.getNode(), function(n) {
bgneal@45 10881 return isBlock(n);
bgneal@45 10882 });
bgneal@45 10883
bgneal@45 10884 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
bgneal@45 10885 // FF and Opera doesn't change parent DIV elements if you switch format
bgneal@45 10886 if (bl) {
bgneal@45 10887 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
bgneal@45 10888 // Rename block element
bgneal@45 10889 nb = ed.dom.create(val);
bgneal@45 10890
bgneal@45 10891 each(dom.getAttribs(bl), function(v) {
bgneal@45 10892 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
bgneal@45 10893 });
bgneal@45 10894
bgneal@45 10895 b = s.getBookmark();
bgneal@45 10896 dom.replace(nb, bl, 1);
bgneal@45 10897 s.moveToBookmark(b);
bgneal@45 10898 ed.nodeChanged();
bgneal@45 10899 return;
bgneal@45 10900 }
bgneal@45 10901 }
bgneal@45 10902
bgneal@45 10903 val = ed.settings.forced_root_block ? (val || '<p>') : val;
bgneal@45 10904
bgneal@45 10905 if (val.indexOf('<') == -1)
bgneal@45 10906 val = '<' + val + '>';
bgneal@45 10907
bgneal@45 10908 if (tinymce.isGecko)
bgneal@45 10909 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');
bgneal@45 10910
bgneal@45 10911 ed.getDoc().execCommand('FormatBlock', false, val);
bgneal@45 10912 },
bgneal@45 10913
bgneal@45 10914 mceCleanup : function() {
bgneal@45 10915 var ed = this.editor, s = ed.selection, b = s.getBookmark();
bgneal@45 10916 ed.setContent(ed.getContent());
bgneal@45 10917 s.moveToBookmark(b);
bgneal@45 10918 },
bgneal@45 10919
bgneal@45 10920 mceRemoveNode : function(ui, val) {
bgneal@45 10921 var ed = this.editor, s = ed.selection, b, n = val || s.getNode();
bgneal@45 10922
bgneal@45 10923 // Make sure that the body node isn't removed
bgneal@45 10924 if (n == ed.getBody())
bgneal@45 10925 return;
bgneal@45 10926
bgneal@45 10927 b = s.getBookmark();
bgneal@45 10928 ed.dom.remove(n, 1);
bgneal@45 10929 s.moveToBookmark(b);
bgneal@45 10930 ed.nodeChanged();
bgneal@45 10931 },
bgneal@45 10932
bgneal@45 10933 mceSelectNodeDepth : function(ui, val) {
bgneal@45 10934 var ed = this.editor, s = ed.selection, c = 0;
bgneal@45 10935
bgneal@45 10936 ed.dom.getParent(s.getNode(), function(n) {
bgneal@45 10937 if (n.nodeType == 1 && c++ == val) {
bgneal@45 10938 s.select(n);
bgneal@45 10939 ed.nodeChanged();
bgneal@45 10940 return false;
bgneal@45 10941 }
bgneal@45 10942 }, ed.getBody());
bgneal@45 10943 },
bgneal@45 10944
bgneal@45 10945 mceSelectNode : function(u, v) {
bgneal@45 10946 this.editor.selection.select(v);
bgneal@45 10947 },
bgneal@45 10948
bgneal@45 10949 mceInsertContent : function(ui, val) {
bgneal@45 10950 this.editor.selection.setContent(val);
bgneal@45 10951 },
bgneal@45 10952
bgneal@45 10953 mceInsertRawHTML : function(ui, val) {
bgneal@45 10954 var ed = this.editor;
bgneal@45 10955
bgneal@45 10956 ed.selection.setContent('tiny_mce_marker');
bgneal@45 10957 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
bgneal@45 10958 },
bgneal@45 10959
bgneal@45 10960 mceRepaint : function() {
bgneal@45 10961 var s, b, e = this.editor;
bgneal@45 10962
bgneal@45 10963 if (tinymce.isGecko) {
bgneal@45 10964 try {
bgneal@45 10965 s = e.selection;
bgneal@45 10966 b = s.getBookmark(true);
bgneal@45 10967
bgneal@45 10968 if (s.getSel())
bgneal@45 10969 s.getSel().selectAllChildren(e.getBody());
bgneal@45 10970
bgneal@45 10971 s.collapse(true);
bgneal@45 10972 s.moveToBookmark(b);
bgneal@45 10973 } catch (ex) {
bgneal@45 10974 // Ignore
bgneal@45 10975 }
bgneal@45 10976 }
bgneal@45 10977 },
bgneal@45 10978
bgneal@45 10979 queryStateUnderline : function() {
bgneal@45 10980 var ed = this.editor, n = ed.selection.getNode();
bgneal@45 10981
bgneal@45 10982 if (n && n.nodeName == 'A')
bgneal@45 10983 return false;
bgneal@45 10984
bgneal@45 10985 return this._queryState('Underline');
bgneal@45 10986 },
bgneal@45 10987
bgneal@45 10988 queryStateOutdent : function() {
bgneal@45 10989 var ed = this.editor, n;
bgneal@45 10990
bgneal@45 10991 if (ed.settings.inline_styles) {
bgneal@45 10992 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
bgneal@45 10993 return true;
bgneal@45 10994
bgneal@45 10995 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
bgneal@45 10996 return true;
bgneal@45 10997 }
bgneal@45 10998
bgneal@45 10999 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE'));
bgneal@45 11000 },
bgneal@45 11001
bgneal@45 11002 queryStateInsertUnorderedList : function() {
bgneal@45 11003 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
bgneal@45 11004 },
bgneal@45 11005
bgneal@45 11006 queryStateInsertOrderedList : function() {
bgneal@45 11007 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
bgneal@45 11008 },
bgneal@45 11009
bgneal@45 11010 queryStatemceBlockQuote : function() {
bgneal@45 11011 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
bgneal@45 11012 },
bgneal@45 11013
bgneal@45 11014 _applyInlineStyle : function(na, at, op) {
bgneal@45 11015 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found;
bgneal@45 11016
bgneal@45 11017 na = na.toUpperCase();
bgneal@45 11018
bgneal@45 11019 if (op && op.check_classes && at['class'])
bgneal@45 11020 op.check_classes.push(at['class']);
bgneal@45 11021
bgneal@45 11022 function removeEmpty() {
bgneal@45 11023 each(dom.select(na).reverse(), function(n) {
bgneal@45 11024 var c = 0;
bgneal@45 11025
bgneal@45 11026 // Check if there is any attributes
bgneal@45 11027 each(dom.getAttribs(n), function(an) {
bgneal@45 11028 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') {
bgneal@45 11029 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName));
bgneal@45 11030 c++;
bgneal@45 11031 }
bgneal@45 11032 });
bgneal@45 11033
bgneal@45 11034 // No attributes then remove the element and keep the children
bgneal@45 11035 if (c == 0)
bgneal@45 11036 dom.remove(n, 1);
bgneal@45 11037 });
bgneal@45 11038 };
bgneal@45 11039
bgneal@45 11040 function replaceFonts() {
bgneal@45 11041 var bm;
bgneal@45 11042
bgneal@45 11043 each(dom.select('span,font'), function(n) {
bgneal@45 11044 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') {
bgneal@45 11045 if (!bm)
bgneal@45 11046 bm = ed.selection.getBookmark();
bgneal@45 11047
bgneal@45 11048 at._mce_new = '1';
bgneal@45 11049 dom.replace(dom.create(na, at), n, 1);
bgneal@45 11050 }
bgneal@45 11051 });
bgneal@45 11052
bgneal@45 11053 // Remove redundant elements
bgneal@45 11054 each(dom.select(na + '[_mce_new]'), function(n) {
bgneal@45 11055 function removeStyle(n) {
bgneal@45 11056 if (n.nodeType == 1) {
bgneal@45 11057 each(at.style, function(v, k) {
bgneal@45 11058 dom.setStyle(n, k, '');
bgneal@45 11059 });
bgneal@45 11060
bgneal@45 11061 // Remove spans with the same class or marked classes
bgneal@45 11062 if (at['class'] && n.className && op) {
bgneal@45 11063 each(op.check_classes, function(c) {
bgneal@45 11064 if (dom.hasClass(n, c))
bgneal@45 11065 dom.removeClass(n, c);
bgneal@45 11066 });
bgneal@45 11067 }
bgneal@45 11068 }
bgneal@45 11069 };
bgneal@45 11070
bgneal@45 11071 // Remove specified style information from child elements
bgneal@45 11072 each(dom.select(na, n), removeStyle);
bgneal@45 11073
bgneal@45 11074 // Remove the specified style information on parent if current node is only child (IE)
bgneal@45 11075 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1)
bgneal@45 11076 removeStyle(n.parentNode);
bgneal@45 11077
bgneal@45 11078 // Remove the child elements style info if a parent already has it
bgneal@45 11079 dom.getParent(n.parentNode, function(pn) {
bgneal@45 11080 if (pn.nodeType == 1) {
bgneal@45 11081 if (at.style) {
bgneal@45 11082 each(at.style, function(v, k) {
bgneal@45 11083 var sv;
bgneal@45 11084
bgneal@45 11085 if (!lo[k] && (sv = dom.getStyle(pn, k))) {
bgneal@45 11086 if (sv === v)
bgneal@45 11087 dom.setStyle(n, k, '');
bgneal@45 11088
bgneal@45 11089 lo[k] = 1;
bgneal@45 11090 }
bgneal@45 11091 });
bgneal@45 11092 }
bgneal@45 11093
bgneal@45 11094 // Remove spans with the same class or marked classes
bgneal@45 11095 if (at['class'] && pn.className && op) {
bgneal@45 11096 each(op.check_classes, function(c) {
bgneal@45 11097 if (dom.hasClass(pn, c))
bgneal@45 11098 dom.removeClass(n, c);
bgneal@45 11099 });
bgneal@45 11100 }
bgneal@45 11101 }
bgneal@45 11102
bgneal@45 11103 return false;
bgneal@45 11104 });
bgneal@45 11105
bgneal@45 11106 n.removeAttribute('_mce_new');
bgneal@45 11107 });
bgneal@45 11108
bgneal@45 11109 removeEmpty();
bgneal@45 11110 ed.selection.moveToBookmark(bm);
bgneal@45 11111
bgneal@45 11112 return !!bm;
bgneal@45 11113 };
bgneal@45 11114
bgneal@45 11115 // Create inline elements
bgneal@45 11116 ed.focus();
bgneal@45 11117 ed.getDoc().execCommand('FontName', false, 'mceinline');
bgneal@45 11118 replaceFonts();
bgneal@45 11119
bgneal@45 11120 if (kh = t._applyInlineStyle.keyhandler) {
bgneal@45 11121 ed.onKeyUp.remove(kh);
bgneal@45 11122 ed.onKeyPress.remove(kh);
bgneal@45 11123 ed.onKeyDown.remove(kh);
bgneal@45 11124 ed.onSetContent.remove(t._applyInlineStyle.chandler);
bgneal@45 11125 }
bgneal@45 11126
bgneal@45 11127 if (ed.selection.isCollapsed()) {
bgneal@45 11128 // IE will format the current word so this code can't be executed on that browser
bgneal@45 11129 if (!isIE) {
bgneal@45 11130 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) {
bgneal@45 11131 each(at.style, function(v, k) {
bgneal@45 11132 var kv;
bgneal@45 11133
bgneal@45 11134 if (kv = dom.getStyle(n, k)) {
bgneal@45 11135 if (kv == v) {
bgneal@45 11136 dom.setStyle(n, k, '');
bgneal@45 11137 found = 2;
bgneal@45 11138 return false;
bgneal@45 11139 }
bgneal@45 11140
bgneal@45 11141 found = 1;
bgneal@45 11142 return false;
bgneal@45 11143 }
bgneal@45 11144 });
bgneal@45 11145
bgneal@45 11146 if (found)
bgneal@45 11147 return false;
bgneal@45 11148 });
bgneal@45 11149
bgneal@45 11150 if (found == 2) {
bgneal@45 11151 bm = ed.selection.getBookmark();
bgneal@45 11152
bgneal@45 11153 removeEmpty();
bgneal@45 11154
bgneal@45 11155 ed.selection.moveToBookmark(bm);
bgneal@45 11156
bgneal@45 11157 // Node change needs to be detached since the onselect event
bgneal@45 11158 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix!
bgneal@45 11159 window.setTimeout(function() {
bgneal@45 11160 ed.nodeChanged();
bgneal@45 11161 }, 1);
bgneal@45 11162
bgneal@45 11163 return;
bgneal@45 11164 }
bgneal@45 11165 }
bgneal@45 11166
bgneal@45 11167 // Start collecting styles
bgneal@45 11168 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style);
bgneal@45 11169
bgneal@45 11170 t._applyInlineStyle.chandler = ed.onSetContent.add(function() {
bgneal@45 11171 delete t._pendingStyles;
bgneal@45 11172 });
bgneal@45 11173
bgneal@45 11174 t._applyInlineStyle.keyhandler = kh = function(e) {
bgneal@45 11175 // Use pending styles
bgneal@45 11176 if (t._pendingStyles) {
bgneal@45 11177 at.style = t._pendingStyles;
bgneal@45 11178 delete t._pendingStyles;
bgneal@45 11179 }
bgneal@45 11180
bgneal@45 11181 if (replaceFonts()) {
bgneal@45 11182 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler);
bgneal@45 11183 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler);
bgneal@45 11184 }
bgneal@45 11185
bgneal@45 11186 if (e.type == 'keyup')
bgneal@45 11187 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler);
bgneal@45 11188 };
bgneal@45 11189
bgneal@45 11190 ed.onKeyDown.add(kh);
bgneal@45 11191 ed.onKeyPress.add(kh);
bgneal@45 11192 ed.onKeyUp.add(kh);
bgneal@45 11193 } else
bgneal@45 11194 t._pendingStyles = 0;
bgneal@45 11195 }
bgneal@45 11196 });
bgneal@45 11197 })(tinymce);(function(tinymce) {
bgneal@45 11198 tinymce.create('tinymce.UndoManager', {
bgneal@45 11199 index : 0,
bgneal@45 11200 data : null,
bgneal@45 11201 typing : 0,
bgneal@45 11202
bgneal@45 11203 UndoManager : function(ed) {
bgneal@45 11204 var t = this, Dispatcher = tinymce.util.Dispatcher;
bgneal@45 11205
bgneal@45 11206 t.editor = ed;
bgneal@45 11207 t.data = [];
bgneal@45 11208 t.onAdd = new Dispatcher(this);
bgneal@45 11209 t.onUndo = new Dispatcher(this);
bgneal@45 11210 t.onRedo = new Dispatcher(this);
bgneal@45 11211 },
bgneal@45 11212
bgneal@45 11213 add : function(l) {
bgneal@45 11214 var t = this, i, ed = t.editor, b, s = ed.settings, la;
bgneal@45 11215
bgneal@45 11216 l = l || {};
bgneal@45 11217 l.content = l.content || ed.getContent({format : 'raw', no_events : 1});
bgneal@45 11218
bgneal@45 11219 // Add undo level if needed
bgneal@45 11220 l.content = l.content.replace(/^\s*|\s*$/g, '');
bgneal@45 11221 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];
bgneal@45 11222 if (!l.initial && la && l.content == la.content)
bgneal@45 11223 return null;
bgneal@45 11224
bgneal@45 11225 // Time to compress
bgneal@45 11226 if (s.custom_undo_redo_levels) {
bgneal@45 11227 if (t.data.length > s.custom_undo_redo_levels) {
bgneal@45 11228 for (i = 0; i < t.data.length - 1; i++)
bgneal@45 11229 t.data[i] = t.data[i + 1];
bgneal@45 11230
bgneal@45 11231 t.data.length--;
bgneal@45 11232 t.index = t.data.length;
bgneal@45 11233 }
bgneal@45 11234 }
bgneal@45 11235
bgneal@45 11236 if (s.custom_undo_redo_restore_selection && !l.initial)
bgneal@45 11237 l.bookmark = b = l.bookmark || ed.selection.getBookmark();
bgneal@45 11238
bgneal@45 11239 if (t.index < t.data.length)
bgneal@45 11240 t.index++;
bgneal@45 11241
bgneal@45 11242 // Only initial marked undo levels should be allowed as first item
bgneal@45 11243 // This to workaround a bug with Firefox and the blur event
bgneal@45 11244 if (t.data.length === 0 && !l.initial)
bgneal@45 11245 return null;
bgneal@45 11246
bgneal@45 11247 // Add level
bgneal@45 11248 t.data.length = t.index + 1;
bgneal@45 11249 t.data[t.index++] = l;
bgneal@45 11250
bgneal@45 11251 if (l.initial)
bgneal@45 11252 t.index = 0;
bgneal@45 11253
bgneal@45 11254 // Set initial bookmark use first real undo level
bgneal@45 11255 if (t.data.length == 2 && t.data[0].initial)
bgneal@45 11256 t.data[0].bookmark = b;
bgneal@45 11257
bgneal@45 11258 t.onAdd.dispatch(t, l);
bgneal@45 11259 ed.isNotDirty = 0;
bgneal@45 11260
bgneal@45 11261 //console.dir(t.data);
bgneal@45 11262
bgneal@45 11263 return l;
bgneal@45 11264 },
bgneal@45 11265
bgneal@45 11266 undo : function() {
bgneal@45 11267 var t = this, ed = t.editor, l = l, i;
bgneal@45 11268
bgneal@45 11269 if (t.typing) {
bgneal@45 11270 t.add();
bgneal@45 11271 t.typing = 0;
bgneal@45 11272 }
bgneal@45 11273
bgneal@45 11274 if (t.index > 0) {
bgneal@45 11275 // If undo on last index then take snapshot
bgneal@45 11276 if (t.index == t.data.length && t.index > 1) {
bgneal@45 11277 i = t.index;
bgneal@45 11278 t.typing = 0;
bgneal@45 11279
bgneal@45 11280 if (!t.add())
bgneal@45 11281 t.index = i;
bgneal@45 11282
bgneal@45 11283 --t.index;
bgneal@45 11284 }
bgneal@45 11285
bgneal@45 11286 l = t.data[--t.index];
bgneal@45 11287 ed.setContent(l.content, {format : 'raw'});
bgneal@45 11288 ed.selection.moveToBookmark(l.bookmark);
bgneal@45 11289
bgneal@45 11290 t.onUndo.dispatch(t, l);
bgneal@45 11291 }
bgneal@45 11292
bgneal@45 11293 return l;
bgneal@45 11294 },
bgneal@45 11295
bgneal@45 11296 redo : function() {
bgneal@45 11297 var t = this, ed = t.editor, l = null;
bgneal@45 11298
bgneal@45 11299 if (t.index < t.data.length - 1) {
bgneal@45 11300 l = t.data[++t.index];
bgneal@45 11301 ed.setContent(l.content, {format : 'raw'});
bgneal@45 11302 ed.selection.moveToBookmark(l.bookmark);
bgneal@45 11303
bgneal@45 11304 t.onRedo.dispatch(t, l);
bgneal@45 11305 }
bgneal@45 11306
bgneal@45 11307 return l;
bgneal@45 11308 },
bgneal@45 11309
bgneal@45 11310 clear : function() {
bgneal@45 11311 var t = this;
bgneal@45 11312
bgneal@45 11313 t.data = [];
bgneal@45 11314 t.index = 0;
bgneal@45 11315 t.typing = 0;
bgneal@45 11316 t.add({initial : true});
bgneal@45 11317 },
bgneal@45 11318
bgneal@45 11319 hasUndo : function() {
bgneal@45 11320 return this.index != 0 || this.typing;
bgneal@45 11321 },
bgneal@45 11322
bgneal@45 11323 hasRedo : function() {
bgneal@45 11324 return this.index < this.data.length - 1;
bgneal@45 11325 }
bgneal@45 11326
bgneal@45 11327 });
bgneal@45 11328 })(tinymce);
bgneal@45 11329 (function(tinymce) {
bgneal@45 11330 // Shorten names
bgneal@45 11331 var Event, isIE, isGecko, isOpera, each, extend;
bgneal@45 11332
bgneal@45 11333 Event = tinymce.dom.Event;
bgneal@45 11334 isIE = tinymce.isIE;
bgneal@45 11335 isGecko = tinymce.isGecko;
bgneal@45 11336 isOpera = tinymce.isOpera;
bgneal@45 11337 each = tinymce.each;
bgneal@45 11338 extend = tinymce.extend;
bgneal@45 11339
bgneal@45 11340 tinymce.create('tinymce.ForceBlocks', {
bgneal@45 11341 ForceBlocks : function(ed) {
bgneal@45 11342 var t = this, s = ed.settings, elm;
bgneal@45 11343
bgneal@45 11344 t.editor = ed;
bgneal@45 11345 t.dom = ed.dom;
bgneal@45 11346 elm = (s.forced_root_block || 'p').toLowerCase();
bgneal@45 11347 s.element = elm.toUpperCase();
bgneal@45 11348
bgneal@45 11349 ed.onPreInit.add(t.setup, t);
bgneal@45 11350
bgneal@45 11351 t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');
bgneal@45 11352 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
bgneal@45 11353 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
bgneal@45 11354 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');
bgneal@45 11355 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
bgneal@45 11356 t.reTrailBr = new RegExp('\\s*<br \\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
bgneal@45 11357
bgneal@45 11358 function padd(ed, o) {
bgneal@45 11359 if (isOpera)
bgneal@45 11360 o.content = o.content.replace(t.reOpera, '</' + elm + '>');
bgneal@45 11361
bgneal@45 11362 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');
bgneal@45 11363
bgneal@45 11364 if (!isIE && !isOpera && o.set) {
bgneal@45 11365 // Use &nbsp; instead of BR in padded paragraphs
bgneal@45 11366 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
bgneal@45 11367 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
bgneal@45 11368 } else {
bgneal@45 11369 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
bgneal@45 11370 o.content = o.content.replace(t.reTrailBr, '</' + elm + '>');
bgneal@45 11371 }
bgneal@45 11372 };
bgneal@45 11373
bgneal@45 11374 ed.onBeforeSetContent.add(padd);
bgneal@45 11375 ed.onPostProcess.add(padd);
bgneal@45 11376
bgneal@45 11377 if (s.forced_root_block) {
bgneal@45 11378 ed.onInit.add(t.forceRoots, t);
bgneal@45 11379 ed.onSetContent.add(t.forceRoots, t);
bgneal@45 11380 ed.onBeforeGetContent.add(t.forceRoots, t);
bgneal@45 11381 }
bgneal@45 11382 },
bgneal@45 11383
bgneal@45 11384 setup : function() {
bgneal@45 11385 var t = this, ed = t.editor, s = ed.settings;
bgneal@45 11386
bgneal@45 11387 // Force root blocks when typing and when getting output
bgneal@45 11388 if (s.forced_root_block) {
bgneal@45 11389 ed.onKeyUp.add(t.forceRoots, t);
bgneal@45 11390 ed.onPreProcess.add(t.forceRoots, t);
bgneal@45 11391 }
bgneal@45 11392
bgneal@45 11393 if (s.force_br_newlines) {
bgneal@45 11394 // Force IE to produce BRs on enter
bgneal@45 11395 if (isIE) {
bgneal@45 11396 ed.onKeyPress.add(function(ed, e) {
bgneal@45 11397 var n, s = ed.selection;
bgneal@45 11398
bgneal@45 11399 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
bgneal@45 11400 s.setContent('<br id="__" /> ', {format : 'raw'});
bgneal@45 11401 n = ed.dom.get('__');
bgneal@45 11402 n.removeAttribute('id');
bgneal@45 11403 s.select(n);
bgneal@45 11404 s.collapse();
bgneal@45 11405 return Event.cancel(e);
bgneal@45 11406 }
bgneal@45 11407 });
bgneal@45 11408 }
bgneal@45 11409
bgneal@45 11410 return;
bgneal@45 11411 }
bgneal@45 11412
bgneal@45 11413 if (!isIE && s.force_p_newlines) {
bgneal@45 11414 /* ed.onPreProcess.add(function(ed, o) {
bgneal@45 11415 each(ed.dom.select('br', o.node), function(n) {
bgneal@45 11416 var p = n.parentNode;
bgneal@45 11417
bgneal@45 11418 // Replace <p><br /></p> with <p>&nbsp;</p>
bgneal@45 11419 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
bgneal@45 11420 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
bgneal@45 11421 }
bgneal@45 11422 });
bgneal@45 11423 });*/
bgneal@45 11424
bgneal@45 11425 ed.onKeyPress.add(function(ed, e) {
bgneal@45 11426 if (e.keyCode == 13 && !e.shiftKey) {
bgneal@45 11427 if (!t.insertPara(e))
bgneal@45 11428 Event.cancel(e);
bgneal@45 11429 }
bgneal@45 11430 });
bgneal@45 11431
bgneal@45 11432 if (isGecko) {
bgneal@45 11433 ed.onKeyDown.add(function(ed, e) {
bgneal@45 11434 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
bgneal@45 11435 t.backspaceDelete(e, e.keyCode == 8);
bgneal@45 11436 });
bgneal@45 11437 }
bgneal@45 11438 }
bgneal@45 11439
bgneal@45 11440 function ren(rn, na) {
bgneal@45 11441 var ne = ed.dom.create(na);
bgneal@45 11442
bgneal@45 11443 each(rn.attributes, function(a) {
bgneal@45 11444 if (a.specified && a.nodeValue)
bgneal@45 11445 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);
bgneal@45 11446 });
bgneal@45 11447
bgneal@45 11448 each(rn.childNodes, function(n) {
bgneal@45 11449 ne.appendChild(n.cloneNode(true));
bgneal@45 11450 });
bgneal@45 11451
bgneal@45 11452 rn.parentNode.replaceChild(ne, rn);
bgneal@45 11453
bgneal@45 11454 return ne;
bgneal@45 11455 };
bgneal@45 11456
bgneal@45 11457 // Replaces IE:s auto generated paragraphs with the specified element name
bgneal@45 11458 if (isIE && s.element != 'P') {
bgneal@45 11459 ed.onKeyPress.add(function(ed, e) {
bgneal@45 11460 t.lastElm = ed.selection.getNode().nodeName;
bgneal@45 11461 });
bgneal@45 11462
bgneal@45 11463 ed.onKeyUp.add(function(ed, e) {
bgneal@45 11464 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();
bgneal@45 11465
bgneal@45 11466 if (b.childNodes.length === 1 && n.nodeName == 'P') {
bgneal@45 11467 n = ren(n, s.element);
bgneal@45 11468 sel.select(n);
bgneal@45 11469 sel.collapse();
bgneal@45 11470 ed.nodeChanged();
bgneal@45 11471 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
bgneal@45 11472 bl = ed.dom.getParent(n, 'P');
bgneal@45 11473
bgneal@45 11474 if (bl) {
bgneal@45 11475 ren(bl, s.element);
bgneal@45 11476 ed.nodeChanged();
bgneal@45 11477 }
bgneal@45 11478 }
bgneal@45 11479 });
bgneal@45 11480 }
bgneal@45 11481 },
bgneal@45 11482
bgneal@45 11483 find : function(n, t, s) {
bgneal@45 11484 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;
bgneal@45 11485
bgneal@45 11486 while (n = w.nextNode()) {
bgneal@45 11487 c++;
bgneal@45 11488
bgneal@45 11489 // Index by node
bgneal@45 11490 if (t == 0 && n == s)
bgneal@45 11491 return c;
bgneal@45 11492
bgneal@45 11493 // Node by index
bgneal@45 11494 if (t == 1 && c == s)
bgneal@45 11495 return n;
bgneal@45 11496 }
bgneal@45 11497
bgneal@45 11498 return -1;
bgneal@45 11499 },
bgneal@45 11500
bgneal@45 11501 forceRoots : function(ed, e) {
bgneal@45 11502 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;
bgneal@45 11503 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;
bgneal@45 11504
bgneal@45 11505 // Fix for bug #1863847
bgneal@45 11506 //if (e && e.keyCode == 13)
bgneal@45 11507 // return true;
bgneal@45 11508
bgneal@45 11509 // Wrap non blocks into blocks
bgneal@45 11510 for (i = nl.length - 1; i >= 0; i--) {
bgneal@45 11511 nx = nl[i];
bgneal@45 11512
bgneal@45 11513 // Is text or non block element
bgneal@45 11514 if (nx.nodeType == 3 || (!t.dom.isBlock(nx) && nx.nodeType != 8)) {
bgneal@45 11515 if (!bl) {
bgneal@45 11516 // Create new block but ignore whitespace
bgneal@45 11517 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
bgneal@45 11518 // Store selection
bgneal@45 11519 if (si == -2 && r) {
bgneal@45 11520 if (!isIE) {
bgneal@45 11521 // If selection is element then mark it
bgneal@45 11522 if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {
bgneal@45 11523 // Save the id of the selected element
bgneal@45 11524 eid = n.getAttribute("id");
bgneal@45 11525 n.setAttribute("id", "__mce");
bgneal@45 11526 } else {
bgneal@45 11527 // If element is inside body, might not be the case in contentEdiable mode
bgneal@45 11528 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {
bgneal@45 11529 so = r.startOffset;
bgneal@45 11530 eo = r.endOffset;
bgneal@45 11531 si = t.find(b, 0, r.startContainer);
bgneal@45 11532 ei = t.find(b, 0, r.endContainer);
bgneal@45 11533 }
bgneal@45 11534 }
bgneal@45 11535 } else {
bgneal@45 11536 tr = d.body.createTextRange();
bgneal@45 11537 tr.moveToElementText(b);
bgneal@45 11538 tr.collapse(1);
bgneal@45 11539 bp = tr.move('character', c) * -1;
bgneal@45 11540
bgneal@45 11541 tr = r.duplicate();
bgneal@45 11542 tr.collapse(1);
bgneal@45 11543 sp = tr.move('character', c) * -1;
bgneal@45 11544
bgneal@45 11545 tr = r.duplicate();
bgneal@45 11546 tr.collapse(0);
bgneal@45 11547 le = (tr.move('character', c) * -1) - sp;
bgneal@45 11548
bgneal@45 11549 si = sp - bp;
bgneal@45 11550 ei = le;
bgneal@45 11551 }
bgneal@45 11552 }
bgneal@45 11553
bgneal@45 11554 bl = ed.dom.create(ed.settings.forced_root_block);
bgneal@45 11555 bl.appendChild(nx.cloneNode(1));
bgneal@45 11556 nx.parentNode.replaceChild(bl, nx);
bgneal@45 11557 }
bgneal@45 11558 } else {
bgneal@45 11559 if (bl.hasChildNodes())
bgneal@45 11560 bl.insertBefore(nx, bl.firstChild);
bgneal@45 11561 else
bgneal@45 11562 bl.appendChild(nx);
bgneal@45 11563 }
bgneal@45 11564 } else
bgneal@45 11565 bl = null; // Time to create new block
bgneal@45 11566 }
bgneal@45 11567
bgneal@45 11568 // Restore selection
bgneal@45 11569 if (si != -2) {
bgneal@45 11570 if (!isIE) {
bgneal@45 11571 bl = b.getElementsByTagName(ed.settings.element)[0];
bgneal@45 11572 r = d.createRange();
bgneal@45 11573
bgneal@45 11574 // Select last location or generated block
bgneal@45 11575 if (si != -1)
bgneal@45 11576 r.setStart(t.find(b, 1, si), so);
bgneal@45 11577 else
bgneal@45 11578 r.setStart(bl, 0);
bgneal@45 11579
bgneal@45 11580 // Select last location or generated block
bgneal@45 11581 if (ei != -1)
bgneal@45 11582 r.setEnd(t.find(b, 1, ei), eo);
bgneal@45 11583 else
bgneal@45 11584 r.setEnd(bl, 0);
bgneal@45 11585
bgneal@45 11586 if (s) {
bgneal@45 11587 s.removeAllRanges();
bgneal@45 11588 s.addRange(r);
bgneal@45 11589 }
bgneal@45 11590 } else {
bgneal@45 11591 try {
bgneal@45 11592 r = s.createRange();
bgneal@45 11593 r.moveToElementText(b);
bgneal@45 11594 r.collapse(1);
bgneal@45 11595 r.moveStart('character', si);
bgneal@45 11596 r.moveEnd('character', ei);
bgneal@45 11597 r.select();
bgneal@45 11598 } catch (ex) {
bgneal@45 11599 // Ignore
bgneal@45 11600 }
bgneal@45 11601 }
bgneal@45 11602 } else if (!isIE && (n = ed.dom.get('__mce'))) {
bgneal@45 11603 // Restore the id of the selected element
bgneal@45 11604 if (eid)
bgneal@45 11605 n.setAttribute('id', eid);
bgneal@45 11606 else
bgneal@45 11607 n.removeAttribute('id');
bgneal@45 11608
bgneal@45 11609 // Move caret before selected element
bgneal@45 11610 r = d.createRange();
bgneal@45 11611 r.setStartBefore(n);
bgneal@45 11612 r.setEndBefore(n);
bgneal@45 11613 se.setRng(r);
bgneal@45 11614 }
bgneal@45 11615 },
bgneal@45 11616
bgneal@45 11617 getParentBlock : function(n) {
bgneal@45 11618 var d = this.dom;
bgneal@45 11619
bgneal@45 11620 return d.getParent(n, d.isBlock);
bgneal@45 11621 },
bgneal@45 11622
bgneal@45 11623 insertPara : function(e) {
bgneal@45 11624 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;
bgneal@45 11625 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
bgneal@45 11626
bgneal@45 11627 function isEmpty(n) {
bgneal@45 11628 n = n.innerHTML;
bgneal@45 11629 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
bgneal@45 11630 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
bgneal@45 11631
bgneal@45 11632 return n.replace(/[ \t\r\n]+/g, '') == '';
bgneal@45 11633 };
bgneal@45 11634
bgneal@45 11635 // If root blocks are forced then use Operas default behavior since it's really good
bgneal@45 11636 // Removed due to bug: #1853816
bgneal@45 11637 // if (se.forced_root_block && isOpera)
bgneal@45 11638 // return true;
bgneal@45 11639
bgneal@45 11640 // Setup before range
bgneal@45 11641 rb = d.createRange();
bgneal@45 11642
bgneal@45 11643 // If is before the first block element and in body, then move it into first block element
bgneal@45 11644 rb.setStart(s.anchorNode, s.anchorOffset);
bgneal@45 11645 rb.collapse(true);
bgneal@45 11646
bgneal@45 11647 // Setup after range
bgneal@45 11648 ra = d.createRange();
bgneal@45 11649
bgneal@45 11650 // If is before the first block element and in body, then move it into first block element
bgneal@45 11651 ra.setStart(s.focusNode, s.focusOffset);
bgneal@45 11652 ra.collapse(true);
bgneal@45 11653
bgneal@45 11654 // Setup start/end points
bgneal@45 11655 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
bgneal@45 11656 sn = dir ? s.anchorNode : s.focusNode;
bgneal@45 11657 so = dir ? s.anchorOffset : s.focusOffset;
bgneal@45 11658 en = dir ? s.focusNode : s.anchorNode;
bgneal@45 11659 eo = dir ? s.focusOffset : s.anchorOffset;
bgneal@45 11660
bgneal@45 11661 // If selection is in empty table cell
bgneal@45 11662 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
bgneal@45 11663 dom.remove(sn.firstChild); // Remove BR
bgneal@45 11664
bgneal@45 11665 // Create two new block elements
bgneal@45 11666 ed.dom.add(sn, se.element, null, '<br />');
bgneal@45 11667 aft = ed.dom.add(sn, se.element, null, '<br />');
bgneal@45 11668
bgneal@45 11669 // Move caret into the last one
bgneal@45 11670 r = d.createRange();
bgneal@45 11671 r.selectNodeContents(aft);
bgneal@45 11672 r.collapse(1);
bgneal@45 11673 ed.selection.setRng(r);
bgneal@45 11674
bgneal@45 11675 return false;
bgneal@45 11676 }
bgneal@45 11677
bgneal@45 11678 // If the caret is in an invalid location in FF we need to move it into the first block
bgneal@45 11679 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
bgneal@45 11680 sn = en = sn.firstChild;
bgneal@45 11681 so = eo = 0;
bgneal@45 11682 rb = d.createRange();
bgneal@45 11683 rb.setStart(sn, 0);
bgneal@45 11684 ra = d.createRange();
bgneal@45 11685 ra.setStart(en, 0);
bgneal@45 11686 }
bgneal@45 11687
bgneal@45 11688 // Never use body as start or end node
bgneal@45 11689 sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
bgneal@45 11690 sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
bgneal@45 11691 en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
bgneal@45 11692 en = en.nodeName == "BODY" ? en.firstChild : en;
bgneal@45 11693
bgneal@45 11694 // Get start and end blocks
bgneal@45 11695 sb = t.getParentBlock(sn);
bgneal@45 11696 eb = t.getParentBlock(en);
bgneal@45 11697 bn = sb ? sb.nodeName : se.element; // Get block name to create
bgneal@45 11698
bgneal@45 11699 // Return inside list use default browser behavior
bgneal@45 11700 if (t.dom.getParent(sb, 'OL,UL,PRE'))
bgneal@45 11701 return true;
bgneal@45 11702
bgneal@45 11703 // If caption or absolute layers then always generate new blocks within
bgneal@45 11704 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
bgneal@45 11705 bn = se.element;
bgneal@45 11706 sb = null;
bgneal@45 11707 }
bgneal@45 11708
bgneal@45 11709 // If caption or absolute layers then always generate new blocks within
bgneal@45 11710 if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
bgneal@45 11711 bn = se.element;
bgneal@45 11712 eb = null;
bgneal@45 11713 }
bgneal@45 11714
bgneal@45 11715 // Use P instead
bgneal@45 11716 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
bgneal@45 11717 bn = se.element;
bgneal@45 11718 sb = eb = null;
bgneal@45 11719 }
bgneal@45 11720
bgneal@45 11721 // Setup new before and after blocks
bgneal@45 11722 bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
bgneal@45 11723 aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
bgneal@45 11724
bgneal@45 11725 // Remove id from after clone
bgneal@45 11726 aft.removeAttribute('id');
bgneal@45 11727
bgneal@45 11728 // Is header and cursor is at the end, then force paragraph under
bgneal@45 11729 if (/^(H[1-6])$/.test(bn) && sn.nodeValue && so == sn.nodeValue.length)
bgneal@45 11730 aft = ed.dom.create(se.element);
bgneal@45 11731
bgneal@45 11732 // Find start chop node
bgneal@45 11733 n = sc = sn;
bgneal@45 11734 do {
bgneal@45 11735 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
bgneal@45 11736 break;
bgneal@45 11737
bgneal@45 11738 sc = n;
bgneal@45 11739 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
bgneal@45 11740
bgneal@45 11741 // Find end chop node
bgneal@45 11742 n = ec = en;
bgneal@45 11743 do {
bgneal@45 11744 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
bgneal@45 11745 break;
bgneal@45 11746
bgneal@45 11747 ec = n;
bgneal@45 11748 } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
bgneal@45 11749
bgneal@45 11750 // Place first chop part into before block element
bgneal@45 11751 if (sc.nodeName == bn)
bgneal@45 11752 rb.setStart(sc, 0);
bgneal@45 11753 else
bgneal@45 11754 rb.setStartBefore(sc);
bgneal@45 11755
bgneal@45 11756 rb.setEnd(sn, so);
bgneal@45 11757 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
bgneal@45 11758
bgneal@45 11759 // Place secnd chop part within new block element
bgneal@45 11760 try {
bgneal@45 11761 ra.setEndAfter(ec);
bgneal@45 11762 } catch(ex) {
bgneal@45 11763 //console.debug(s.focusNode, s.focusOffset);
bgneal@45 11764 }
bgneal@45 11765
bgneal@45 11766 ra.setStart(en, eo);
bgneal@45 11767 aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
bgneal@45 11768
bgneal@45 11769 // Create range around everything
bgneal@45 11770 r = d.createRange();
bgneal@45 11771 if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
bgneal@45 11772 r.setStartBefore(sc.parentNode);
bgneal@45 11773 } else {
bgneal@45 11774 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
bgneal@45 11775 r.setStartBefore(rb.startContainer);
bgneal@45 11776 else
bgneal@45 11777 r.setStart(rb.startContainer, rb.startOffset);
bgneal@45 11778 }
bgneal@45 11779
bgneal@45 11780 if (!ec.nextSibling && ec.parentNode.nodeName == bn)
bgneal@45 11781 r.setEndAfter(ec.parentNode);
bgneal@45 11782 else
bgneal@45 11783 r.setEnd(ra.endContainer, ra.endOffset);
bgneal@45 11784
bgneal@45 11785 // Delete and replace it with new block elements
bgneal@45 11786 r.deleteContents();
bgneal@45 11787
bgneal@45 11788 if (isOpera)
bgneal@45 11789 ed.getWin().scrollTo(0, vp.y);
bgneal@45 11790
bgneal@45 11791 // Never wrap blocks in blocks
bgneal@45 11792 if (bef.firstChild && bef.firstChild.nodeName == bn)
bgneal@45 11793 bef.innerHTML = bef.firstChild.innerHTML;
bgneal@45 11794
bgneal@45 11795 if (aft.firstChild && aft.firstChild.nodeName == bn)
bgneal@45 11796 aft.innerHTML = aft.firstChild.innerHTML;
bgneal@45 11797
bgneal@45 11798 // Padd empty blocks
bgneal@45 11799 if (isEmpty(bef))
bgneal@45 11800 bef.innerHTML = '<br />';
bgneal@45 11801
bgneal@45 11802 function appendStyles(e, en) {
bgneal@45 11803 var nl = [], nn, n, i;
bgneal@45 11804
bgneal@45 11805 e.innerHTML = '';
bgneal@45 11806
bgneal@45 11807 // Make clones of style elements
bgneal@45 11808 if (se.keep_styles) {
bgneal@45 11809 n = en;
bgneal@45 11810 do {
bgneal@45 11811 // We only want style specific elements
bgneal@45 11812 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
bgneal@45 11813 nn = n.cloneNode(false);
bgneal@45 11814 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
bgneal@45 11815 nl.push(nn);
bgneal@45 11816 }
bgneal@45 11817 } while (n = n.parentNode);
bgneal@45 11818 }
bgneal@45 11819
bgneal@45 11820 // Append style elements to aft
bgneal@45 11821 if (nl.length > 0) {
bgneal@45 11822 for (i = nl.length - 1, nn = e; i >= 0; i--)
bgneal@45 11823 nn = nn.appendChild(nl[i]);
bgneal@45 11824
bgneal@45 11825 // Padd most inner style element
bgneal@45 11826 nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
bgneal@45 11827 return nl[0]; // Move caret to most inner element
bgneal@45 11828 } else
bgneal@45 11829 e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
bgneal@45 11830 };
bgneal@45 11831
bgneal@45 11832 // Fill empty afterblook with current style
bgneal@45 11833 if (isEmpty(aft))
bgneal@45 11834 car = appendStyles(aft, en);
bgneal@45 11835
bgneal@45 11836 // Opera needs this one backwards for older versions
bgneal@45 11837 if (isOpera && parseFloat(opera.version()) < 9.5) {
bgneal@45 11838 r.insertNode(bef);
bgneal@45 11839 r.insertNode(aft);
bgneal@45 11840 } else {
bgneal@45 11841 r.insertNode(aft);
bgneal@45 11842 r.insertNode(bef);
bgneal@45 11843 }
bgneal@45 11844
bgneal@45 11845 // Normalize
bgneal@45 11846 aft.normalize();
bgneal@45 11847 bef.normalize();
bgneal@45 11848
bgneal@45 11849 function first(n) {
bgneal@45 11850 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n;
bgneal@45 11851 };
bgneal@45 11852
bgneal@45 11853 // Move cursor and scroll into view
bgneal@45 11854 r = d.createRange();
bgneal@45 11855 r.selectNodeContents(isGecko ? first(car || aft) : car || aft);
bgneal@45 11856 r.collapse(1);
bgneal@45 11857 s.removeAllRanges();
bgneal@45 11858 s.addRange(r);
bgneal@45 11859
bgneal@45 11860 // 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
bgneal@45 11861 y = ed.dom.getPos(aft).y;
bgneal@45 11862 ch = aft.clientHeight;
bgneal@45 11863
bgneal@45 11864 // Is element within viewport
bgneal@45 11865 if (y < vp.y || y + ch > vp.y + vp.h) {
bgneal@45 11866 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
bgneal@45 11867 //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));
bgneal@45 11868 }
bgneal@45 11869
bgneal@45 11870 return false;
bgneal@45 11871 },
bgneal@45 11872
bgneal@45 11873 backspaceDelete : function(e, bs) {
bgneal@45 11874 var t = this, ed = t.editor, b = ed.getBody(), n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;
bgneal@45 11875
bgneal@45 11876 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
bgneal@45 11877 // This workaround removes the element by hand and moves the caret to the previous element
bgneal@45 11878 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
bgneal@45 11879 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
bgneal@45 11880 // Find previous block element
bgneal@45 11881 n = sc;
bgneal@45 11882 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
bgneal@45 11883
bgneal@45 11884 if (n) {
bgneal@45 11885 if (sc != b.firstChild) {
bgneal@45 11886 // Find last text node
bgneal@45 11887 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
bgneal@45 11888 while (tn = w.nextNode())
bgneal@45 11889 n = tn;
bgneal@45 11890
bgneal@45 11891 // Place caret at the end of last text node
bgneal@45 11892 r = ed.getDoc().createRange();
bgneal@45 11893 r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
bgneal@45 11894 r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
bgneal@45 11895 se.setRng(r);
bgneal@45 11896
bgneal@45 11897 // Remove the target container
bgneal@45 11898 ed.dom.remove(sc);
bgneal@45 11899 }
bgneal@45 11900
bgneal@45 11901 return Event.cancel(e);
bgneal@45 11902 }
bgneal@45 11903 }
bgneal@45 11904 }
bgneal@45 11905
bgneal@45 11906 // Gecko generates BR elements here and there, we don't like those so lets remove them
bgneal@45 11907 function handler(e) {
bgneal@45 11908 var pr;
bgneal@45 11909
bgneal@45 11910 e = e.target;
bgneal@45 11911
bgneal@45 11912 // A new BR was created in a block element, remove it
bgneal@45 11913 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
bgneal@45 11914 pr = e.previousSibling;
bgneal@45 11915
bgneal@45 11916 Event.remove(b, 'DOMNodeInserted', handler);
bgneal@45 11917
bgneal@45 11918 // Is there whitespace at the end of the node before then we might need the pesky BR
bgneal@45 11919 // to place the caret at a correct location see bug: #2013943
bgneal@45 11920 if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
bgneal@45 11921 return;
bgneal@45 11922
bgneal@45 11923 // Only remove BR elements that got inserted in the middle of the text
bgneal@45 11924 if (e.previousSibling || e.nextSibling)
bgneal@45 11925 ed.dom.remove(e);
bgneal@45 11926 }
bgneal@45 11927 };
bgneal@45 11928
bgneal@45 11929 // Listen for new nodes
bgneal@45 11930 Event._add(b, 'DOMNodeInserted', handler);
bgneal@45 11931
bgneal@45 11932 // Remove listener
bgneal@45 11933 window.setTimeout(function() {
bgneal@45 11934 Event._remove(b, 'DOMNodeInserted', handler);
bgneal@45 11935 }, 1);
bgneal@45 11936 }
bgneal@45 11937 });
bgneal@45 11938 })(tinymce);
bgneal@45 11939 (function(tinymce) {
bgneal@45 11940 // Shorten names
bgneal@45 11941 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
bgneal@45 11942
bgneal@45 11943 tinymce.create('tinymce.ControlManager', {
bgneal@45 11944 ControlManager : function(ed, s) {
bgneal@45 11945 var t = this, i;
bgneal@45 11946
bgneal@45 11947 s = s || {};
bgneal@45 11948 t.editor = ed;
bgneal@45 11949 t.controls = {};
bgneal@45 11950 t.onAdd = new tinymce.util.Dispatcher(t);
bgneal@45 11951 t.onPostRender = new tinymce.util.Dispatcher(t);
bgneal@45 11952 t.prefix = s.prefix || ed.id + '_';
bgneal@45 11953 t._cls = {};
bgneal@45 11954
bgneal@45 11955 t.onPostRender.add(function() {
bgneal@45 11956 each(t.controls, function(c) {
bgneal@45 11957 c.postRender();
bgneal@45 11958 });
bgneal@45 11959 });
bgneal@45 11960 },
bgneal@45 11961
bgneal@45 11962 get : function(id) {
bgneal@45 11963 return this.controls[this.prefix + id] || this.controls[id];
bgneal@45 11964 },
bgneal@45 11965
bgneal@45 11966 setActive : function(id, s) {
bgneal@45 11967 var c = null;
bgneal@45 11968
bgneal@45 11969 if (c = this.get(id))
bgneal@45 11970 c.setActive(s);
bgneal@45 11971
bgneal@45 11972 return c;
bgneal@45 11973 },
bgneal@45 11974
bgneal@45 11975 setDisabled : function(id, s) {
bgneal@45 11976 var c = null;
bgneal@45 11977
bgneal@45 11978 if (c = this.get(id))
bgneal@45 11979 c.setDisabled(s);
bgneal@45 11980
bgneal@45 11981 return c;
bgneal@45 11982 },
bgneal@45 11983
bgneal@45 11984 add : function(c) {
bgneal@45 11985 var t = this;
bgneal@45 11986
bgneal@45 11987 if (c) {
bgneal@45 11988 t.controls[c.id] = c;
bgneal@45 11989 t.onAdd.dispatch(c, t);
bgneal@45 11990 }
bgneal@45 11991
bgneal@45 11992 return c;
bgneal@45 11993 },
bgneal@45 11994
bgneal@45 11995 createControl : function(n) {
bgneal@45 11996 var c, t = this, ed = t.editor;
bgneal@45 11997
bgneal@45 11998 each(ed.plugins, function(p) {
bgneal@45 11999 if (p.createControl) {
bgneal@45 12000 c = p.createControl(n, t);
bgneal@45 12001
bgneal@45 12002 if (c)
bgneal@45 12003 return false;
bgneal@45 12004 }
bgneal@45 12005 });
bgneal@45 12006
bgneal@45 12007 switch (n) {
bgneal@45 12008 case "|":
bgneal@45 12009 case "separator":
bgneal@45 12010 return t.createSeparator();
bgneal@45 12011 }
bgneal@45 12012
bgneal@45 12013 if (!c && ed.buttons && (c = ed.buttons[n]))
bgneal@45 12014 return t.createButton(n, c);
bgneal@45 12015
bgneal@45 12016 return t.add(c);
bgneal@45 12017 },
bgneal@45 12018
bgneal@45 12019 createDropMenu : function(id, s, cc) {
bgneal@45 12020 var t = this, ed = t.editor, c, bm, v, cls;
bgneal@45 12021
bgneal@45 12022 s = extend({
bgneal@45 12023 'class' : 'mceDropDown',
bgneal@45 12024 constrain : ed.settings.constrain_menus
bgneal@45 12025 }, s);
bgneal@45 12026
bgneal@45 12027 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
bgneal@45 12028 if (v = ed.getParam('skin_variant'))
bgneal@45 12029 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
bgneal@45 12030
bgneal@45 12031 id = t.prefix + id;
bgneal@45 12032 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
bgneal@45 12033 c = t.controls[id] = new cls(id, s);
bgneal@45 12034 c.onAddItem.add(function(c, o) {
bgneal@45 12035 var s = o.settings;
bgneal@45 12036
bgneal@45 12037 s.title = ed.getLang(s.title, s.title);
bgneal@45 12038
bgneal@45 12039 if (!s.onclick) {
bgneal@45 12040 s.onclick = function(v) {
bgneal@45 12041 ed.execCommand(s.cmd, s.ui || false, s.value);
bgneal@45 12042 };
bgneal@45 12043 }
bgneal@45 12044 });
bgneal@45 12045
bgneal@45 12046 ed.onRemove.add(function() {
bgneal@45 12047 c.destroy();
bgneal@45 12048 });
bgneal@45 12049
bgneal@45 12050 // Fix for bug #1897785, #1898007
bgneal@45 12051 if (tinymce.isIE) {
bgneal@45 12052 c.onShowMenu.add(function() {
bgneal@45 12053 bm = ed.selection.getBookmark(1);
bgneal@45 12054 });
bgneal@45 12055
bgneal@45 12056 c.onHideMenu.add(function() {
bgneal@45 12057 if (bm) {
bgneal@45 12058 ed.selection.moveToBookmark(bm);
bgneal@45 12059 bm = 0;
bgneal@45 12060 }
bgneal@45 12061 });
bgneal@45 12062 }
bgneal@45 12063
bgneal@45 12064 return t.add(c);
bgneal@45 12065 },
bgneal@45 12066
bgneal@45 12067 createListBox : function(id, s, cc) {
bgneal@45 12068 var t = this, ed = t.editor, cmd, c, cls;
bgneal@45 12069
bgneal@45 12070 if (t.get(id))
bgneal@45 12071 return null;
bgneal@45 12072
bgneal@45 12073 s.title = ed.translate(s.title);
bgneal@45 12074 s.scope = s.scope || ed;
bgneal@45 12075
bgneal@45 12076 if (!s.onselect) {
bgneal@45 12077 s.onselect = function(v) {
bgneal@45 12078 ed.execCommand(s.cmd, s.ui || false, v || s.value);
bgneal@45 12079 };
bgneal@45 12080 }
bgneal@45 12081
bgneal@45 12082 s = extend({
bgneal@45 12083 title : s.title,
bgneal@45 12084 'class' : 'mce_' + id,
bgneal@45 12085 scope : s.scope,
bgneal@45 12086 control_manager : t
bgneal@45 12087 }, s);
bgneal@45 12088
bgneal@45 12089 id = t.prefix + id;
bgneal@45 12090
bgneal@45 12091 if (ed.settings.use_native_selects)
bgneal@45 12092 c = new tinymce.ui.NativeListBox(id, s);
bgneal@45 12093 else {
bgneal@45 12094 cls = cc || t._cls.listbox || tinymce.ui.ListBox;
bgneal@45 12095 c = new cls(id, s);
bgneal@45 12096 }
bgneal@45 12097
bgneal@45 12098 t.controls[id] = c;
bgneal@45 12099
bgneal@45 12100 // Fix focus problem in Safari
bgneal@45 12101 if (tinymce.isWebKit) {
bgneal@45 12102 c.onPostRender.add(function(c, n) {
bgneal@45 12103 // Store bookmark on mousedown
bgneal@45 12104 Event.add(n, 'mousedown', function() {
bgneal@45 12105 ed.bookmark = ed.selection.getBookmark('simple');
bgneal@45 12106 });
bgneal@45 12107
bgneal@45 12108 // Restore on focus, since it might be lost
bgneal@45 12109 Event.add(n, 'focus', function() {
bgneal@45 12110 ed.selection.moveToBookmark(ed.bookmark);
bgneal@45 12111 ed.bookmark = null;
bgneal@45 12112 });
bgneal@45 12113 });
bgneal@45 12114 }
bgneal@45 12115
bgneal@45 12116 if (c.hideMenu)
bgneal@45 12117 ed.onMouseDown.add(c.hideMenu, c);
bgneal@45 12118
bgneal@45 12119 return t.add(c);
bgneal@45 12120 },
bgneal@45 12121
bgneal@45 12122 createButton : function(id, s, cc) {
bgneal@45 12123 var t = this, ed = t.editor, o, c, cls;
bgneal@45 12124
bgneal@45 12125 if (t.get(id))
bgneal@45 12126 return null;
bgneal@45 12127
bgneal@45 12128 s.title = ed.translate(s.title);
bgneal@45 12129 s.label = ed.translate(s.label);
bgneal@45 12130 s.scope = s.scope || ed;
bgneal@45 12131
bgneal@45 12132 if (!s.onclick && !s.menu_button) {
bgneal@45 12133 s.onclick = function() {
bgneal@45 12134 ed.execCommand(s.cmd, s.ui || false, s.value);
bgneal@45 12135 };
bgneal@45 12136 }
bgneal@45 12137
bgneal@45 12138 s = extend({
bgneal@45 12139 title : s.title,
bgneal@45 12140 'class' : 'mce_' + id,
bgneal@45 12141 unavailable_prefix : ed.getLang('unavailable', ''),
bgneal@45 12142 scope : s.scope,
bgneal@45 12143 control_manager : t
bgneal@45 12144 }, s);
bgneal@45 12145
bgneal@45 12146 id = t.prefix + id;
bgneal@45 12147
bgneal@45 12148 if (s.menu_button) {
bgneal@45 12149 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
bgneal@45 12150 c = new cls(id, s);
bgneal@45 12151 ed.onMouseDown.add(c.hideMenu, c);
bgneal@45 12152 } else {
bgneal@45 12153 cls = t._cls.button || tinymce.ui.Button;
bgneal@45 12154 c = new cls(id, s);
bgneal@45 12155 }
bgneal@45 12156
bgneal@45 12157 return t.add(c);
bgneal@45 12158 },
bgneal@45 12159
bgneal@45 12160 createMenuButton : function(id, s, cc) {
bgneal@45 12161 s = s || {};
bgneal@45 12162 s.menu_button = 1;
bgneal@45 12163
bgneal@45 12164 return this.createButton(id, s, cc);
bgneal@45 12165 },
bgneal@45 12166
bgneal@45 12167 createSplitButton : function(id, s, cc) {
bgneal@45 12168 var t = this, ed = t.editor, cmd, c, cls;
bgneal@45 12169
bgneal@45 12170 if (t.get(id))
bgneal@45 12171 return null;
bgneal@45 12172
bgneal@45 12173 s.title = ed.translate(s.title);
bgneal@45 12174 s.scope = s.scope || ed;
bgneal@45 12175
bgneal@45 12176 if (!s.onclick) {
bgneal@45 12177 s.onclick = function(v) {
bgneal@45 12178 ed.execCommand(s.cmd, s.ui || false, v || s.value);
bgneal@45 12179 };
bgneal@45 12180 }
bgneal@45 12181
bgneal@45 12182 if (!s.onselect) {
bgneal@45 12183 s.onselect = function(v) {
bgneal@45 12184 ed.execCommand(s.cmd, s.ui || false, v || s.value);
bgneal@45 12185 };
bgneal@45 12186 }
bgneal@45 12187
bgneal@45 12188 s = extend({
bgneal@45 12189 title : s.title,
bgneal@45 12190 'class' : 'mce_' + id,
bgneal@45 12191 scope : s.scope,
bgneal@45 12192 control_manager : t
bgneal@45 12193 }, s);
bgneal@45 12194
bgneal@45 12195 id = t.prefix + id;
bgneal@45 12196 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
bgneal@45 12197 c = t.add(new cls(id, s));
bgneal@45 12198 ed.onMouseDown.add(c.hideMenu, c);
bgneal@45 12199
bgneal@45 12200 return c;
bgneal@45 12201 },
bgneal@45 12202
bgneal@45 12203 createColorSplitButton : function(id, s, cc) {
bgneal@45 12204 var t = this, ed = t.editor, cmd, c, cls, bm;
bgneal@45 12205
bgneal@45 12206 if (t.get(id))
bgneal@45 12207 return null;
bgneal@45 12208
bgneal@45 12209 s.title = ed.translate(s.title);
bgneal@45 12210 s.scope = s.scope || ed;
bgneal@45 12211
bgneal@45 12212 if (!s.onclick) {
bgneal@45 12213 s.onclick = function(v) {
bgneal@45 12214 if (tinymce.isIE)
bgneal@45 12215 bm = ed.selection.getBookmark(1);
bgneal@45 12216
bgneal@45 12217 ed.execCommand(s.cmd, s.ui || false, v || s.value);
bgneal@45 12218 };
bgneal@45 12219 }
bgneal@45 12220
bgneal@45 12221 if (!s.onselect) {
bgneal@45 12222 s.onselect = function(v) {
bgneal@45 12223 ed.execCommand(s.cmd, s.ui || false, v || s.value);
bgneal@45 12224 };
bgneal@45 12225 }
bgneal@45 12226
bgneal@45 12227 s = extend({
bgneal@45 12228 title : s.title,
bgneal@45 12229 'class' : 'mce_' + id,
bgneal@45 12230 'menu_class' : ed.getParam('skin') + 'Skin',
bgneal@45 12231 scope : s.scope,
bgneal@45 12232 more_colors_title : ed.getLang('more_colors')
bgneal@45 12233 }, s);
bgneal@45 12234
bgneal@45 12235 id = t.prefix + id;
bgneal@45 12236 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
bgneal@45 12237 c = new cls(id, s);
bgneal@45 12238 ed.onMouseDown.add(c.hideMenu, c);
bgneal@45 12239
bgneal@45 12240 // Remove the menu element when the editor is removed
bgneal@45 12241 ed.onRemove.add(function() {
bgneal@45 12242 c.destroy();
bgneal@45 12243 });
bgneal@45 12244
bgneal@45 12245 // Fix for bug #1897785, #1898007
bgneal@45 12246 if (tinymce.isIE) {
bgneal@45 12247 c.onHideMenu.add(function() {
bgneal@45 12248 if (bm) {
bgneal@45 12249 ed.selection.moveToBookmark(bm);
bgneal@45 12250 bm = 0;
bgneal@45 12251 }
bgneal@45 12252 });
bgneal@45 12253 }
bgneal@45 12254
bgneal@45 12255 return t.add(c);
bgneal@45 12256 },
bgneal@45 12257
bgneal@45 12258 createToolbar : function(id, s, cc) {
bgneal@45 12259 var c, t = this, cls;
bgneal@45 12260
bgneal@45 12261 id = t.prefix + id;
bgneal@45 12262 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
bgneal@45 12263 c = new cls(id, s);
bgneal@45 12264
bgneal@45 12265 if (t.get(id))
bgneal@45 12266 return null;
bgneal@45 12267
bgneal@45 12268 return t.add(c);
bgneal@45 12269 },
bgneal@45 12270
bgneal@45 12271 createSeparator : function(cc) {
bgneal@45 12272 var cls = cc || this._cls.separator || tinymce.ui.Separator;
bgneal@45 12273
bgneal@45 12274 return new cls();
bgneal@45 12275 },
bgneal@45 12276
bgneal@45 12277 setControlType : function(n, c) {
bgneal@45 12278 return this._cls[n.toLowerCase()] = c;
bgneal@45 12279 },
bgneal@45 12280
bgneal@45 12281 destroy : function() {
bgneal@45 12282 each(this.controls, function(c) {
bgneal@45 12283 c.destroy();
bgneal@45 12284 });
bgneal@45 12285
bgneal@45 12286 this.controls = null;
bgneal@45 12287 }
bgneal@45 12288
bgneal@45 12289 });
bgneal@45 12290 })(tinymce);
bgneal@45 12291 (function(tinymce) {
bgneal@45 12292 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
bgneal@45 12293
bgneal@45 12294 tinymce.create('tinymce.WindowManager', {
bgneal@45 12295 WindowManager : function(ed) {
bgneal@45 12296 var t = this;
bgneal@45 12297
bgneal@45 12298 t.editor = ed;
bgneal@45 12299 t.onOpen = new Dispatcher(t);
bgneal@45 12300 t.onClose = new Dispatcher(t);
bgneal@45 12301 t.params = {};
bgneal@45 12302 t.features = {};
bgneal@45 12303 },
bgneal@45 12304
bgneal@45 12305 open : function(s, p) {
bgneal@45 12306 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
bgneal@45 12307
bgneal@45 12308 // Default some options
bgneal@45 12309 s = s || {};
bgneal@45 12310 p = p || {};
bgneal@45 12311 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
bgneal@45 12312 sh = isOpera ? vp.h : screen.height;
bgneal@45 12313 s.name = s.name || 'mc_' + new Date().getTime();
bgneal@45 12314 s.width = parseInt(s.width || 320);
bgneal@45 12315 s.height = parseInt(s.height || 240);
bgneal@45 12316 s.resizable = true;
bgneal@45 12317 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
bgneal@45 12318 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
bgneal@45 12319 p.inline = false;
bgneal@45 12320 p.mce_width = s.width;
bgneal@45 12321 p.mce_height = s.height;
bgneal@45 12322 p.mce_auto_focus = s.auto_focus;
bgneal@45 12323
bgneal@45 12324 if (mo) {
bgneal@45 12325 if (isIE) {
bgneal@45 12326 s.center = true;
bgneal@45 12327 s.help = false;
bgneal@45 12328 s.dialogWidth = s.width + 'px';
bgneal@45 12329 s.dialogHeight = s.height + 'px';
bgneal@45 12330 s.scroll = s.scrollbars || false;
bgneal@45 12331 }
bgneal@45 12332 }
bgneal@45 12333
bgneal@45 12334 // Build features string
bgneal@45 12335 each(s, function(v, k) {
bgneal@45 12336 if (tinymce.is(v, 'boolean'))
bgneal@45 12337 v = v ? 'yes' : 'no';
bgneal@45 12338
bgneal@45 12339 if (!/^(name|url)$/.test(k)) {
bgneal@45 12340 if (isIE && mo)
bgneal@45 12341 f += (f ? ';' : '') + k + ':' + v;
bgneal@45 12342 else
bgneal@45 12343 f += (f ? ',' : '') + k + '=' + v;
bgneal@45 12344 }
bgneal@45 12345 });
bgneal@45 12346
bgneal@45 12347 t.features = s;
bgneal@45 12348 t.params = p;
bgneal@45 12349 t.onOpen.dispatch(t, s, p);
bgneal@45 12350
bgneal@45 12351 u = s.url || s.file;
bgneal@45 12352 u = tinymce._addVer(u);
bgneal@45 12353
bgneal@45 12354 try {
bgneal@45 12355 if (isIE && mo) {
bgneal@45 12356 w = 1;
bgneal@45 12357 window.showModalDialog(u, window, f);
bgneal@45 12358 } else
bgneal@45 12359 w = window.open(u, s.name, f);
bgneal@45 12360 } catch (ex) {
bgneal@45 12361 // Ignore
bgneal@45 12362 }
bgneal@45 12363
bgneal@45 12364 if (!w)
bgneal@45 12365 alert(t.editor.getLang('popup_blocked'));
bgneal@45 12366 },
bgneal@45 12367
bgneal@45 12368 close : function(w) {
bgneal@45 12369 w.close();
bgneal@45 12370 this.onClose.dispatch(this);
bgneal@45 12371 },
bgneal@45 12372
bgneal@45 12373 createInstance : function(cl, a, b, c, d, e) {
bgneal@45 12374 var f = tinymce.resolve(cl);
bgneal@45 12375
bgneal@45 12376 return new f(a, b, c, d, e);
bgneal@45 12377 },
bgneal@45 12378
bgneal@45 12379 confirm : function(t, cb, s, w) {
bgneal@45 12380 w = w || window;
bgneal@45 12381
bgneal@45 12382 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
bgneal@45 12383 },
bgneal@45 12384
bgneal@45 12385 alert : function(tx, cb, s, w) {
bgneal@45 12386 var t = this;
bgneal@45 12387
bgneal@45 12388 w = w || window;
bgneal@45 12389 w.alert(t._decode(t.editor.getLang(tx, tx)));
bgneal@45 12390
bgneal@45 12391 if (cb)
bgneal@45 12392 cb.call(s || t);
bgneal@45 12393 },
bgneal@45 12394
bgneal@45 12395 // Internal functions
bgneal@45 12396
bgneal@45 12397 _decode : function(s) {
bgneal@45 12398 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
bgneal@45 12399 }
bgneal@45 12400
bgneal@45 12401 });
bgneal@45 12402 }(tinymce));(function(tinymce) {
bgneal@45 12403 tinymce.CommandManager = function() {
bgneal@45 12404 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};
bgneal@45 12405
bgneal@45 12406 function add(collection, cmd, func, scope) {
bgneal@45 12407 if (typeof(cmd) == 'string')
bgneal@45 12408 cmd = [cmd];
bgneal@45 12409
bgneal@45 12410 tinymce.each(cmd, function(cmd) {
bgneal@45 12411 collection[cmd.toLowerCase()] = {func : func, scope : scope};
bgneal@45 12412 });
bgneal@45 12413 };
bgneal@45 12414
bgneal@45 12415 tinymce.extend(this, {
bgneal@45 12416 add : function(cmd, func, scope) {
bgneal@45 12417 add(execCommands, cmd, func, scope);
bgneal@45 12418 },
bgneal@45 12419
bgneal@45 12420 addQueryStateHandler : function(cmd, func, scope) {
bgneal@45 12421 add(queryStateCommands, cmd, func, scope);
bgneal@45 12422 },
bgneal@45 12423
bgneal@45 12424 addQueryValueHandler : function(cmd, func, scope) {
bgneal@45 12425 add(queryValueCommands, cmd, func, scope);
bgneal@45 12426 },
bgneal@45 12427
bgneal@45 12428 execCommand : function(scope, cmd, ui, value, args) {
bgneal@45 12429 if (cmd = execCommands[cmd.toLowerCase()]) {
bgneal@45 12430 if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)
bgneal@45 12431 return true;
bgneal@45 12432 }
bgneal@45 12433 },
bgneal@45 12434
bgneal@45 12435 queryCommandValue : function() {
bgneal@45 12436 if (cmd = queryValueCommands[cmd.toLowerCase()])
bgneal@45 12437 return cmd.func.call(scope || cmd.scope, ui, value, args);
bgneal@45 12438 },
bgneal@45 12439
bgneal@45 12440 queryCommandState : function() {
bgneal@45 12441 if (cmd = queryStateCommands[cmd.toLowerCase()])
bgneal@45 12442 return cmd.func.call(scope || cmd.scope, ui, value, args);
bgneal@45 12443 }
bgneal@45 12444 });
bgneal@45 12445 };
bgneal@45 12446
bgneal@45 12447 tinymce.GlobalCommands = new tinymce.CommandManager();
bgneal@45 12448 })(tinymce);(function(tinymce) {
bgneal@45 12449 function processRange(dom, start, end, callback) {
bgneal@45 12450 var ancestor, n, startPoint, endPoint, sib;
bgneal@45 12451
bgneal@45 12452 function findEndPoint(n, c) {
bgneal@45 12453 do {
bgneal@45 12454 if (n.parentNode == c)
bgneal@45 12455 return n;
bgneal@45 12456
bgneal@45 12457 n = n.parentNode;
bgneal@45 12458 } while(n);
bgneal@45 12459 };
bgneal@45 12460
bgneal@45 12461 function process(n) {
bgneal@45 12462 callback(n);
bgneal@45 12463 tinymce.walk(n, callback, 'childNodes');
bgneal@45 12464 };
bgneal@45 12465
bgneal@45 12466 // Find common ancestor and end points
bgneal@45 12467 ancestor = dom.findCommonAncestor(start, end);
bgneal@45 12468 startPoint = findEndPoint(start, ancestor) || start;
bgneal@45 12469 endPoint = findEndPoint(end, ancestor) || end;
bgneal@45 12470
bgneal@45 12471 // Process left leaf
bgneal@45 12472 for (n = start; n && n != startPoint; n = n.parentNode) {
bgneal@45 12473 for (sib = n.nextSibling; sib; sib = sib.nextSibling)
bgneal@45 12474 process(sib);
bgneal@45 12475 }
bgneal@45 12476
bgneal@45 12477 // Process middle from start to end point
bgneal@45 12478 if (startPoint != endPoint) {
bgneal@45 12479 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling)
bgneal@45 12480 process(n);
bgneal@45 12481 } else
bgneal@45 12482 process(startPoint);
bgneal@45 12483
bgneal@45 12484 // Process right leaf
bgneal@45 12485 for (n = end; n && n != endPoint; n = n.parentNode) {
bgneal@45 12486 for (sib = n.previousSibling; sib; sib = sib.previousSibling)
bgneal@45 12487 process(sib);
bgneal@45 12488 }
bgneal@45 12489 };
bgneal@45 12490
bgneal@45 12491 tinymce.GlobalCommands.add('RemoveFormat', function() {
bgneal@45 12492 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n;
bgneal@45 12493
bgneal@45 12494 function findFormatRoot(n) {
bgneal@45 12495 var sp;
bgneal@45 12496
bgneal@45 12497 dom.getParent(n, function(n) {
bgneal@45 12498 if (dom.is(n, ed.getParam('removeformat_selector')))
bgneal@45 12499 sp = n;
bgneal@45 12500
bgneal@45 12501 return dom.isBlock(n);
bgneal@45 12502 }, ed.getBody())
bgneal@45 12503
bgneal@45 12504 return sp;
bgneal@45 12505 };
bgneal@45 12506
bgneal@45 12507 function collect(n) {
bgneal@45 12508 if (dom.is(n, ed.getParam('removeformat_selector')))
bgneal@45 12509 nodes.push(n);
bgneal@45 12510 };
bgneal@45 12511
bgneal@45 12512 function walk(n) {
bgneal@45 12513 collect(n);
bgneal@45 12514 tinymce.walk(n, collect, 'childNodes');
bgneal@45 12515 };
bgneal@45 12516
bgneal@45 12517 bm = s.getBookmark();
bgneal@45 12518 sc = r.startContainer;
bgneal@45 12519 ec = r.endContainer;
bgneal@45 12520 so = r.startOffset;
bgneal@45 12521 eo = r.endOffset;
bgneal@45 12522 sc = sc.nodeType == 1 ? sc.childNodes[so] : sc;
bgneal@45 12523 ec = ec.nodeType == 1 ? ec.childNodes[eo - 1] : ec;
bgneal@45 12524
bgneal@45 12525 // Same container
bgneal@45 12526 if (sc == ec) { // TEXT_NODE
bgneal@45 12527 start = findFormatRoot(sc);
bgneal@45 12528
bgneal@45 12529 // Handle single text node
bgneal@45 12530 if (sc.nodeType == 3) {
bgneal@45 12531 if (start && start.nodeType == 1) { // ELEMENT
bgneal@45 12532 n = sc.splitText(so);
bgneal@45 12533 n.splitText(eo - so);
bgneal@45 12534 dom.split(start, n);
bgneal@45 12535
bgneal@45 12536 s.moveToBookmark(bm);
bgneal@45 12537 }
bgneal@45 12538
bgneal@45 12539 return;
bgneal@45 12540 }
bgneal@45 12541
bgneal@45 12542 // Handle single element
bgneal@45 12543 walk(dom.split(start, sc) || sc);
bgneal@45 12544 } else {
bgneal@45 12545 // Find start/end format root
bgneal@45 12546 start = findFormatRoot(sc);
bgneal@45 12547 end = findFormatRoot(ec);
bgneal@45 12548
bgneal@45 12549 // Split start text node
bgneal@45 12550 if (start) {
bgneal@45 12551 if (sc.nodeType == 3) { // TEXT
bgneal@45 12552 // Since IE doesn't support white space nodes in the DOM we need to
bgneal@45 12553 // add this invisible character so that the splitText function can split the contents
bgneal@45 12554 if (so == sc.nodeValue.length)
bgneal@45 12555 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix
bgneal@45 12556
bgneal@45 12557 sc = sc.splitText(so);
bgneal@45 12558 }
bgneal@45 12559 }
bgneal@45 12560
bgneal@45 12561 // Split end text node
bgneal@45 12562 if (end) {
bgneal@45 12563 if (ec.nodeType == 3) // TEXT
bgneal@45 12564 ec.splitText(eo);
bgneal@45 12565 }
bgneal@45 12566
bgneal@45 12567 // If the start and end format root is the same then we need to wrap
bgneal@45 12568 // the end node in a span since the split calls might change the reference
bgneal@45 12569 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p>
bgneal@45 12570 if (start && start == end)
bgneal@45 12571 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec);
bgneal@45 12572
bgneal@45 12573 // Split all start containers down to the format root
bgneal@45 12574 if (start)
bgneal@45 12575 start = dom.split(start, sc);
bgneal@45 12576 else
bgneal@45 12577 start = sc;
bgneal@45 12578
bgneal@45 12579 // If there is a span wrapper use that one instead
bgneal@45 12580 if (n = dom.get('__end')) {
bgneal@45 12581 ec = n;
bgneal@45 12582 end = findFormatRoot(ec);
bgneal@45 12583 }
bgneal@45 12584
bgneal@45 12585 // Split all end containers down to the format root
bgneal@45 12586 if (end)
bgneal@45 12587 end = dom.split(end, ec);
bgneal@45 12588 else
bgneal@45 12589 end = ec;
bgneal@45 12590
bgneal@45 12591 // Collect nodes in between
bgneal@45 12592 processRange(dom, start, end, collect);
bgneal@45 12593
bgneal@45 12594 // Remove invisible character for IE workaround if we find it
bgneal@45 12595 if (sc.nodeValue == '\uFEFF')
bgneal@45 12596 sc.nodeValue = '';
bgneal@45 12597
bgneal@45 12598 // Process start/end container elements
bgneal@45 12599 walk(ec);
bgneal@45 12600 walk(sc);
bgneal@45 12601 }
bgneal@45 12602
bgneal@45 12603 // Remove all collected nodes
bgneal@45 12604 tinymce.each(nodes, function(n) {
bgneal@45 12605 dom.remove(n, 1);
bgneal@45 12606 });
bgneal@45 12607
bgneal@45 12608 // Remove leftover wrapper
bgneal@45 12609 dom.remove('__end', 1);
bgneal@45 12610
bgneal@45 12611 s.moveToBookmark(bm);
bgneal@45 12612 });
bgneal@45 12613 })(tinymce);
bgneal@45 12614 (function(tinymce) {
bgneal@45 12615 tinymce.GlobalCommands.add('mceBlockQuote', function() {
bgneal@45 12616 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;
bgneal@45 12617
bgneal@45 12618 function getBQ(e) {
bgneal@45 12619 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
bgneal@45 12620 };
bgneal@45 12621
bgneal@45 12622 // Get start/end block
bgneal@45 12623 sb = dom.getParent(s.getStart(), dom.isBlock);
bgneal@45 12624 eb = dom.getParent(s.getEnd(), dom.isBlock);
bgneal@45 12625
bgneal@45 12626 // Remove blockquote(s)
bgneal@45 12627 if (bq = getBQ(sb)) {
bgneal@45 12628 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
bgneal@45 12629 bm = s.getBookmark();
bgneal@45 12630
bgneal@45 12631 // Move all elements after the end block into new bq
bgneal@45 12632 if (getBQ(eb)) {
bgneal@45 12633 bq2 = bq.cloneNode(false);
bgneal@45 12634
bgneal@45 12635 while (n = eb.nextSibling)
bgneal@45 12636 bq2.appendChild(n.parentNode.removeChild(n));
bgneal@45 12637 }
bgneal@45 12638
bgneal@45 12639 // Add new bq after
bgneal@45 12640 if (bq2)
bgneal@45 12641 dom.insertAfter(bq2, bq);
bgneal@45 12642
bgneal@45 12643 // Move all selected blocks after the current bq
bgneal@45 12644 nl = s.getSelectedBlocks(sb, eb);
bgneal@45 12645 for (i = nl.length - 1; i >= 0; i--) {
bgneal@45 12646 dom.insertAfter(nl[i], bq);
bgneal@45 12647 }
bgneal@45 12648
bgneal@45 12649 // Empty bq, then remove it
bgneal@45 12650 if (/^\s*$/.test(bq.innerHTML))
bgneal@45 12651 dom.remove(bq, 1); // Keep children so boomark restoration works correctly
bgneal@45 12652
bgneal@45 12653 // Empty bq, then remote it
bgneal@45 12654 if (bq2 && /^\s*$/.test(bq2.innerHTML))
bgneal@45 12655 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly
bgneal@45 12656
bgneal@45 12657 if (!bm) {
bgneal@45 12658 // Move caret inside empty block element
bgneal@45 12659 if (!tinymce.isIE) {
bgneal@45 12660 r = ed.getDoc().createRange();
bgneal@45 12661 r.setStart(sb, 0);
bgneal@45 12662 r.setEnd(sb, 0);
bgneal@45 12663 s.setRng(r);
bgneal@45 12664 } else {
bgneal@45 12665 s.select(sb);
bgneal@45 12666 s.collapse(0);
bgneal@45 12667
bgneal@45 12668 // IE misses the empty block some times element so we must move back the caret
bgneal@45 12669 if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
bgneal@45 12670 r = s.getRng();
bgneal@45 12671 r.move('character', -1);
bgneal@45 12672 r.select();
bgneal@45 12673 }
bgneal@45 12674 }
bgneal@45 12675 } else
bgneal@45 12676 ed.selection.moveToBookmark(bm);
bgneal@45 12677
bgneal@45 12678 return;
bgneal@45 12679 }
bgneal@45 12680
bgneal@45 12681 // Since IE can start with a totally empty document we need to add the first bq and paragraph
bgneal@45 12682 if (tinymce.isIE && !sb && !eb) {
bgneal@45 12683 ed.getDoc().execCommand('Indent');
bgneal@45 12684 n = getBQ(s.getNode());
bgneal@45 12685 n.style.margin = n.dir = ''; // IE adds margin and dir to bq
bgneal@45 12686 return;
bgneal@45 12687 }
bgneal@45 12688
bgneal@45 12689 if (!sb || !eb)
bgneal@45 12690 return;
bgneal@45 12691
bgneal@45 12692 // If empty paragraph node then do not use bookmark
bgneal@45 12693 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
bgneal@45 12694 bm = s.getBookmark();
bgneal@45 12695
bgneal@45 12696 // Move selected block elements into a bq
bgneal@45 12697 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
bgneal@45 12698 // Found existing BQ add to this one
bgneal@45 12699 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
bgneal@45 12700 bq = e;
bgneal@45 12701 return;
bgneal@45 12702 }
bgneal@45 12703
bgneal@45 12704 // No BQ found, create one
bgneal@45 12705 if (!bq) {
bgneal@45 12706 bq = dom.create('blockquote');
bgneal@45 12707 e.parentNode.insertBefore(bq, e);
bgneal@45 12708 }
bgneal@45 12709
bgneal@45 12710 // Add children from existing BQ
bgneal@45 12711 if (e.nodeName == 'BLOCKQUOTE' && bq) {
bgneal@45 12712 n = e.firstChild;
bgneal@45 12713
bgneal@45 12714 while (n) {
bgneal@45 12715 bq.appendChild(n.cloneNode(true));
bgneal@45 12716 n = n.nextSibling;
bgneal@45 12717 }
bgneal@45 12718
bgneal@45 12719 dom.remove(e);
bgneal@45 12720 return;
bgneal@45 12721 }
bgneal@45 12722
bgneal@45 12723 // Add non BQ element to BQ
bgneal@45 12724 bq.appendChild(dom.remove(e));
bgneal@45 12725 });
bgneal@45 12726
bgneal@45 12727 if (!bm) {
bgneal@45 12728 // Move caret inside empty block element
bgneal@45 12729 if (!tinymce.isIE) {
bgneal@45 12730 r = ed.getDoc().createRange();
bgneal@45 12731 r.setStart(sb, 0);
bgneal@45 12732 r.setEnd(sb, 0);
bgneal@45 12733 s.setRng(r);
bgneal@45 12734 } else {
bgneal@45 12735 s.select(sb);
bgneal@45 12736 s.collapse(1);
bgneal@45 12737 }
bgneal@45 12738 } else
bgneal@45 12739 s.moveToBookmark(bm);
bgneal@45 12740 });
bgneal@45 12741 })(tinymce);
bgneal@45 12742 (function(tinymce) {
bgneal@45 12743 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) {
bgneal@45 12744 tinymce.GlobalCommands.add(cmd, function() {
bgneal@45 12745 var ed = this, doc = ed.getDoc();
bgneal@45 12746
bgneal@45 12747 try {
bgneal@45 12748 doc.execCommand(cmd, false, null);
bgneal@45 12749
bgneal@45 12750 // On WebKit the command will just be ignored if it's not enabled
bgneal@45 12751 if (!doc.queryCommandSupported(cmd))
bgneal@45 12752 throw 'Error';
bgneal@45 12753 } catch (ex) {
bgneal@45 12754 ed.windowManager.alert(ed.getLang('clipboard_no_support'));
bgneal@45 12755 }
bgneal@45 12756 });
bgneal@45 12757 });
bgneal@45 12758 })(tinymce);
bgneal@45 12759 (function(tinymce) {
bgneal@45 12760 tinymce.GlobalCommands.add('InsertHorizontalRule', function() {
bgneal@45 12761 if (tinymce.isOpera)
bgneal@45 12762 return this.getDoc().execCommand('InsertHorizontalRule', false, '');
bgneal@45 12763
bgneal@45 12764 this.selection.setContent('<hr />');
bgneal@45 12765 });
bgneal@45 12766 })(tinymce);
bgneal@45 12767 (function() {
bgneal@45 12768 var cmds = tinymce.GlobalCommands;
bgneal@45 12769
bgneal@45 12770 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() {
bgneal@45 12771 this.undoManager.add();
bgneal@45 12772 });
bgneal@45 12773
bgneal@45 12774 cmds.add('Undo', function() {
bgneal@45 12775 var ed = this;
bgneal@45 12776
bgneal@45 12777 if (ed.settings.custom_undo_redo) {
bgneal@45 12778 ed.undoManager.undo();
bgneal@45 12779 ed.nodeChanged();
bgneal@45 12780 return true;
bgneal@45 12781 }
bgneal@45 12782
bgneal@45 12783 return false; // Run browser command
bgneal@45 12784 });
bgneal@45 12785
bgneal@45 12786 cmds.add('Redo', function() {
bgneal@45 12787 var ed = this;
bgneal@45 12788
bgneal@45 12789 if (ed.settings.custom_undo_redo) {
bgneal@45 12790 ed.undoManager.redo();
bgneal@45 12791 ed.nodeChanged();
bgneal@45 12792 return true;
bgneal@45 12793 }
bgneal@45 12794
bgneal@45 12795 return false; // Run browser command
bgneal@45 12796 });
bgneal@45 12797 })();