annotate media/js/tiny_mce/tiny_mce_src.js @ 175:776028f4bced

Ticket #60. The TEMPLATE_CONTEXT_PROCESSOR django.core.context_processors.auth moved to django.contrib.auth.context_processors.auth.
author Brian Neal <bgneal@gmail.com>
date Wed, 03 Mar 2010 04:09:42 +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 })();