[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/plugins/editors/tinymce/jscripts/tiny_mce/ -> tiny_mce_src.js (source)

   1  var tinymce = {
   2      majorVersion : '3',
   3      minorVersion : '2.6',
   4      releaseDate : '2009-08-19',
   5  
   6      _init : function() {
   7          var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
   8  
   9          t.isOpera = w.opera && opera.buildNumber;
  10  
  11          t.isWebKit = /WebKit/.test(ua);
  12  
  13          t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
  14  
  15          t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
  16  
  17          t.isGecko = !t.isWebKit && /Gecko/.test(ua);
  18  
  19          t.isMac = ua.indexOf('Mac') != -1;
  20  
  21          t.isAir = /adobeair/i.test(ua);
  22  
  23          // TinyMCE .NET webcontrol might be setting the values for TinyMCE
  24          if (w.tinyMCEPreInit) {
  25              t.suffix = tinyMCEPreInit.suffix;
  26              t.baseURL = tinyMCEPreInit.base;
  27              t.query = tinyMCEPreInit.query;
  28              return;
  29          }
  30  
  31          // Get suffix and base
  32          t.suffix = '';
  33  
  34          // If base element found, add that infront of baseURL
  35          nl = d.getElementsByTagName('base');
  36          for (i=0; i<nl.length; i++) {
  37              if (v = nl[i].href) {
  38                  // Host only value like http://site.com or http://site.com:8008
  39                  if (/^https?:\/\/[^\/]+$/.test(v))
  40                      v += '/';
  41  
  42                  base = v ? v.match(/.*\//)[0] : ''; // Get only directory
  43              }
  44          }
  45  
  46  		function getBase(n) {
  47              if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {
  48                  if (/_(src|dev)\.js/g.test(n.src))
  49                      t.suffix = '_src';
  50  
  51                  if ((p = n.src.indexOf('?')) != -1)
  52                      t.query = n.src.substring(p + 1);
  53  
  54                  t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
  55  
  56                  // If path to script is relative and a base href was found add that one infront
  57                  // the src property will always be an absolute one on non IE browsers and IE 8
  58                  // so this logic will basically only be executed on older IE versions
  59                  if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
  60                      t.baseURL = base + t.baseURL;
  61  
  62                  return t.baseURL;
  63              }
  64  
  65              return null;
  66          };
  67  
  68          // Check document
  69          nl = d.getElementsByTagName('script');
  70          for (i=0; i<nl.length; i++) {
  71              if (getBase(nl[i]))
  72                  return;
  73          }
  74  
  75          // Check head
  76          n = d.getElementsByTagName('head')[0];
  77          if (n) {
  78              nl = n.getElementsByTagName('script');
  79              for (i=0; i<nl.length; i++) {
  80                  if (getBase(nl[i]))
  81                      return;
  82              }
  83          }
  84  
  85          return;
  86      },
  87  
  88      is : function(o, t) {
  89          var n = typeof(o);
  90  
  91          if (!t)
  92              return n != 'undefined';
  93  
  94          if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
  95              return true;
  96  
  97          return n == t;
  98      },
  99  
 100      each : function(o, cb, s) {
 101          var n, l;
 102  
 103          if (!o)
 104              return 0;
 105  
 106          s = s || o;
 107  
 108          if (typeof(o.length) != 'undefined') {
 109              // Indexed arrays, needed for Safari
 110              for (n=0, l = o.length; n<l; n++) {
 111                  if (cb.call(s, o[n], n, o) === false)
 112                      return 0;
 113              }
 114          } else {
 115              // Hashtables
 116              for (n in o) {
 117                  if (o.hasOwnProperty(n)) {
 118                      if (cb.call(s, o[n], n, o) === false)
 119                          return 0;
 120                  }
 121              }
 122          }
 123  
 124          return 1;
 125      },
 126  
 127  
 128      map : function(a, f) {
 129          var o = [];
 130  
 131          tinymce.each(a, function(v) {
 132              o.push(f(v));
 133          });
 134  
 135          return o;
 136      },
 137  
 138      grep : function(a, f) {
 139          var o = [];
 140  
 141          tinymce.each(a, function(v) {
 142              if (!f || f(v))
 143                  o.push(v);
 144          });
 145  
 146          return o;
 147      },
 148  
 149      inArray : function(a, v) {
 150          var i, l;
 151  
 152          if (a) {
 153              for (i = 0, l = a.length; i < l; i++) {
 154                  if (a[i] === v)
 155                      return i;
 156              }
 157          }
 158  
 159          return -1;
 160      },
 161  
 162      extend : function(o, e) {
 163          var i, a = arguments;
 164  
 165          for (i=1; i<a.length; i++) {
 166              e = a[i];
 167  
 168              tinymce.each(e, function(v, n) {
 169                  if (typeof(v) !== 'undefined')
 170                      o[n] = v;
 171              });
 172          }
 173  
 174          return o;
 175      },
 176  
 177  
 178      trim : function(s) {
 179          return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
 180      },
 181  
 182      create : function(s, p) {
 183          var t = this, sp, ns, cn, scn, c, de = 0;
 184  
 185          // Parse : <prefix> <class>:<super class>
 186          s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
 187          cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
 188  
 189          // Create namespace for new class
 190          ns = t.createNS(s[3].replace(/\.\w+$/, ''));
 191  
 192          // Class already exists
 193          if (ns[cn])
 194              return;
 195  
 196          // Make pure static class
 197          if (s[2] == 'static') {
 198              ns[cn] = p;
 199  
 200              if (this.onCreate)
 201                  this.onCreate(s[2], s[3], ns[cn]);
 202  
 203              return;
 204          }
 205  
 206          // Create default constructor
 207          if (!p[cn]) {
 208              p[cn] = function() {};
 209              de = 1;
 210          }
 211  
 212          // Add constructor and methods
 213          ns[cn] = p[cn];
 214          t.extend(ns[cn].prototype, p);
 215  
 216          // Extend
 217          if (s[5]) {
 218              sp = t.resolve(s[5]).prototype;
 219              scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
 220  
 221              // Extend constructor
 222              c = ns[cn];
 223              if (de) {
 224                  // Add passthrough constructor
 225                  ns[cn] = function() {
 226                      return sp[scn].apply(this, arguments);
 227                  };
 228              } else {
 229                  // Add inherit constructor
 230                  ns[cn] = function() {
 231                      this.parent = sp[scn];
 232                      return c.apply(this, arguments);
 233                  };
 234              }
 235              ns[cn].prototype[cn] = ns[cn];
 236  
 237              // Add super methods
 238              t.each(sp, function(f, n) {
 239                  ns[cn].prototype[n] = sp[n];
 240              });
 241  
 242              // Add overridden methods
 243              t.each(p, function(f, n) {
 244                  // Extend methods if needed
 245                  if (sp[n]) {
 246                      ns[cn].prototype[n] = function() {
 247                          this.parent = sp[n];
 248                          return f.apply(this, arguments);
 249                      };
 250                  } else {
 251                      if (n != cn)
 252                          ns[cn].prototype[n] = f;
 253                  }
 254              });
 255          }
 256  
 257          // Add static methods
 258          t.each(p['static'], function(f, n) {
 259              ns[cn][n] = f;
 260          });
 261  
 262          if (this.onCreate)
 263              this.onCreate(s[2], s[3], ns[cn].prototype);
 264      },
 265  
 266      walk : function(o, f, n, s) {
 267          s = s || this;
 268  
 269          if (o) {
 270              if (n)
 271                  o = o[n];
 272  
 273              tinymce.each(o, function(o, i) {
 274                  if (f.call(s, o, i, n) === false)
 275                      return false;
 276  
 277                  tinymce.walk(o, f, n, s);
 278              });
 279          }
 280      },
 281  
 282      createNS : function(n, o) {
 283          var i, v;
 284  
 285          o = o || window;
 286  
 287          n = n.split('.');
 288          for (i=0; i<n.length; i++) {
 289              v = n[i];
 290  
 291              if (!o[v])
 292                  o[v] = {};
 293  
 294              o = o[v];
 295          }
 296  
 297          return o;
 298      },
 299  
 300      resolve : function(n, o) {
 301          var i, l;
 302  
 303          o = o || window;
 304  
 305          n = n.split('.');
 306          for (i = 0, l = n.length; i < l; i++) {
 307              o = o[n[i]];
 308  
 309              if (!o)
 310                  break;
 311          }
 312  
 313          return o;
 314      },
 315  
 316      addUnload : function(f, s) {
 317          var t = this, w = window;
 318  
 319          f = {func : f, scope : s || this};
 320  
 321          if (!t.unloads) {
 322  			function unload() {
 323                  var li = t.unloads, o, n;
 324  
 325                  if (li) {
 326                      // Call unload handlers
 327                      for (n in li) {
 328                          o = li[n];
 329  
 330                          if (o && o.func)
 331                              o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
 332                      }
 333  
 334                      // Detach unload function
 335                      if (w.detachEvent) {
 336                          w.detachEvent('onbeforeunload', fakeUnload);
 337                          w.detachEvent('onunload', unload);
 338                      } else if (w.removeEventListener)
 339                          w.removeEventListener('unload', unload, false);
 340  
 341                      // Destroy references
 342                      t.unloads = o = li = w = unload = 0;
 343  
 344                      // Run garbarge collector on IE
 345                      if (window.CollectGarbage)
 346                          window.CollectGarbage();
 347                  }
 348              };
 349  
 350  			function fakeUnload() {
 351                  var d = document;
 352  
 353                  // Is there things still loading, then do some magic
 354                  if (d.readyState == 'interactive') {
 355  					function stop() {
 356                          // Prevent memory leak
 357                          d.detachEvent('onstop', stop);
 358  
 359                          // Call unload handler
 360                          if (unload)
 361                              unload();
 362  
 363                          d = 0;
 364                      };
 365  
 366                      // Fire unload when the currently loading page is stopped
 367                      if (d)
 368                          d.attachEvent('onstop', stop);
 369  
 370                      // Remove onstop listener after a while to prevent the unload function
 371                      // to execute if the user presses cancel in an onbeforeunload
 372                      // confirm dialog and then presses the browser stop button
 373                      window.setTimeout(function() {
 374                          if (d)
 375                              d.detachEvent('onstop', stop);
 376                      }, 0);
 377                  }
 378              };
 379  
 380              // Attach unload handler
 381              if (w.attachEvent) {
 382                  w.attachEvent('onunload', unload);
 383                  w.attachEvent('onbeforeunload', fakeUnload);
 384              } else if (w.addEventListener)
 385                  w.addEventListener('unload', unload, false);
 386  
 387              // Setup initial unload handler array
 388              t.unloads = [f];
 389          } else
 390              t.unloads.push(f);
 391  
 392          return f;
 393      },
 394  
 395      removeUnload : function(f) {
 396          var u = this.unloads, r = null;
 397  
 398          tinymce.each(u, function(o, i) {
 399              if (o && o.func == f) {
 400                  u.splice(i, 1);
 401                  r = f;
 402                  return false;
 403              }
 404          });
 405  
 406          return r;
 407      },
 408  
 409      explode : function(s, d) {
 410          return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
 411      },
 412  
 413      _addVer : function(u) {
 414          var v;
 415  
 416          if (!this.query)
 417              return u;
 418  
 419          v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
 420  
 421          if (u.indexOf('#') == -1)
 422              return u + v;
 423  
 424          return u.replace('#', v + '#');
 425      }
 426  
 427      };
 428  
 429  // Required for GZip AJAX loading
 430  window.tinymce = tinymce;
 431  
 432  // Initialize the API
 433  tinymce._init();
 434  tinymce.create('tinymce.util.Dispatcher', {
 435      scope : null,
 436      listeners : null,
 437  
 438      Dispatcher : function(s) {
 439          this.scope = s || this;
 440          this.listeners = [];
 441      },
 442  
 443      add : function(cb, s) {
 444          this.listeners.push({cb : cb, scope : s || this.scope});
 445  
 446          return cb;
 447      },
 448  
 449      addToTop : function(cb, s) {
 450          this.listeners.unshift({cb : cb, scope : s || this.scope});
 451  
 452          return cb;
 453      },
 454  
 455      remove : function(cb) {
 456          var l = this.listeners, o = null;
 457  
 458          tinymce.each(l, function(c, i) {
 459              if (cb == c.cb) {
 460                  o = cb;
 461                  l.splice(i, 1);
 462                  return false;
 463              }
 464          });
 465  
 466          return o;
 467      },
 468  
 469      dispatch : function() {
 470          var s, a = arguments, i, li = this.listeners, c;
 471  
 472          // Needs to be a real loop since the listener count might change while looping
 473          // And this is also more efficient
 474          for (i = 0; i<li.length; i++) {
 475              c = li[i];
 476              s = c.cb.apply(c.scope, a);
 477  
 478              if (s === false)
 479                  break;
 480          }
 481  
 482          return s;
 483      }
 484  
 485      });
 486  (function() {
 487      var each = tinymce.each;
 488  
 489      tinymce.create('tinymce.util.URI', {
 490          URI : function(u, s) {
 491              var t = this, o, a, b;
 492  
 493              // Trim whitespace
 494              u = tinymce.trim(u);
 495  
 496              // Default settings
 497              s = t.settings = s || {};
 498  
 499              // Strange app protocol or local anchor
 500              if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
 501                  t.source = u;
 502                  return;
 503              }
 504  
 505              // Absolute path with no host, fake host and protocol
 506              if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
 507                  u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
 508  
 509              // Relative path http:// or protocol relative //path
 510              if (!/^\w*:?\/\//.test(u))
 511                  u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
 512  
 513              // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
 514              u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
 515              u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
 516              each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
 517                  var s = u[i];
 518  
 519                  // Zope 3 workaround, they use @@something
 520                  if (s)
 521                      s = s.replace(/\(mce_at\)/g, '@@');
 522  
 523                  t[v] = s;
 524              });
 525  
 526              if (b = s.base_uri) {
 527                  if (!t.protocol)
 528                      t.protocol = b.protocol;
 529  
 530                  if (!t.userInfo)
 531                      t.userInfo = b.userInfo;
 532  
 533                  if (!t.port && t.host == 'mce_host')
 534                      t.port = b.port;
 535  
 536                  if (!t.host || t.host == 'mce_host')
 537                      t.host = b.host;
 538  
 539                  t.source = '';
 540              }
 541  
 542              //t.path = t.path || '/';
 543          },
 544  
 545          setPath : function(p) {
 546              var t = this;
 547  
 548              p = /^(.*?)\/?(\w+)?$/.exec(p);
 549  
 550              // Update path parts
 551              t.path = p[0];
 552              t.directory = p[1];
 553              t.file = p[2];
 554  
 555              // Rebuild source
 556              t.source = '';
 557              t.getURI();
 558          },
 559  
 560          toRelative : function(u) {
 561              var t = this, o;
 562  
 563              if (u === "./")
 564                  return u;
 565  
 566              u = new tinymce.util.URI(u, {base_uri : t});
 567  
 568              // Not on same domain/port or protocol
 569              if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
 570                  return u.getURI();
 571  
 572              o = t.toRelPath(t.path, u.path);
 573  
 574              // Add query
 575              if (u.query)
 576                  o += '?' + u.query;
 577  
 578              // Add anchor
 579              if (u.anchor)
 580                  o += '#' + u.anchor;
 581  
 582              return o;
 583          },
 584      
 585          toAbsolute : function(u, nh) {
 586              var u = new tinymce.util.URI(u, {base_uri : this});
 587  
 588              return u.getURI(this.host == u.host ? nh : 0);
 589          },
 590  
 591          toRelPath : function(base, path) {
 592              var items, bp = 0, out = '', i, l;
 593  
 594              // Split the paths
 595              base = base.substring(0, base.lastIndexOf('/'));
 596              base = base.split('/');
 597              items = path.split('/');
 598  
 599              if (base.length >= items.length) {
 600                  for (i = 0, l = base.length; i < l; i++) {
 601                      if (i >= items.length || base[i] != items[i]) {
 602                          bp = i + 1;
 603                          break;
 604                      }
 605                  }
 606              }
 607  
 608              if (base.length < items.length) {
 609                  for (i = 0, l = items.length; i < l; i++) {
 610                      if (i >= base.length || base[i] != items[i]) {
 611                          bp = i + 1;
 612                          break;
 613                      }
 614                  }
 615              }
 616  
 617              if (bp == 1)
 618                  return path;
 619  
 620              for (i = 0, l = base.length - (bp - 1); i < l; i++)
 621                  out += "../";
 622  
 623              for (i = bp - 1, l = items.length; i < l; i++) {
 624                  if (i != bp - 1)
 625                      out += "/" + items[i];
 626                  else
 627                      out += items[i];
 628              }
 629  
 630              return out;
 631          },
 632  
 633          toAbsPath : function(base, path) {
 634              var i, nb = 0, o = [], tr;
 635  
 636              // Split paths
 637              tr = /\/$/.test(path) ? '/' : '';
 638              base = base.split('/');
 639              path = path.split('/');
 640  
 641              // Remove empty chunks
 642              each(base, function(k) {
 643                  if (k)
 644                      o.push(k);
 645              });
 646  
 647              base = o;
 648  
 649              // Merge relURLParts chunks
 650              for (i = path.length - 1, o = []; i >= 0; i--) {
 651                  // Ignore empty or .
 652                  if (path[i].length == 0 || path[i] == ".")
 653                      continue;
 654  
 655                  // Is parent
 656                  if (path[i] == '..') {
 657                      nb++;
 658                      continue;
 659                  }
 660  
 661                  // Move up
 662                  if (nb > 0) {
 663                      nb--;
 664                      continue;
 665                  }
 666  
 667                  o.push(path[i]);
 668              }
 669  
 670              i = base.length - nb;
 671  
 672              // If /a/b/c or /
 673              if (i <= 0)
 674                  return '/' + o.reverse().join('/') + tr;
 675  
 676              return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr;
 677          },
 678  
 679          getURI : function(nh) {
 680              var s, t = this;
 681  
 682              // Rebuild source
 683              if (!t.source || nh) {
 684                  s = '';
 685  
 686                  if (!nh) {
 687                      if (t.protocol)
 688                          s += t.protocol + '://';
 689  
 690                      if (t.userInfo)
 691                          s += t.userInfo + '@';
 692  
 693                      if (t.host)
 694                          s += t.host;
 695  
 696                      if (t.port)
 697                          s += ':' + t.port;
 698                  }
 699  
 700                  if (t.path)
 701                      s += t.path;
 702  
 703                  if (t.query)
 704                      s += '?' + t.query;
 705  
 706                  if (t.anchor)
 707                      s += '#' + t.anchor;
 708  
 709                  t.source = s;
 710              }
 711  
 712              return t.source;
 713          }
 714      });
 715  })();
 716  (function() {
 717      var each = tinymce.each;
 718  
 719      tinymce.create('static tinymce.util.Cookie', {
 720          getHash : function(n) {
 721              var v = this.get(n), h;
 722  
 723              if (v) {
 724                  each(v.split('&'), function(v) {
 725                      v = v.split('=');
 726                      h = h || {};
 727                      h[unescape(v[0])] = unescape(v[1]);
 728                  });
 729              }
 730  
 731              return h;
 732          },
 733  
 734          setHash : function(n, v, e, p, d, s) {
 735              var o = '';
 736  
 737              each(v, function(v, k) {
 738                  o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
 739              });
 740  
 741              this.set(n, o, e, p, d, s);
 742          },
 743  
 744          get : function(n) {
 745              var c = document.cookie, e, p = n + "=", b;
 746  
 747              // Strict mode
 748              if (!c)
 749                  return;
 750  
 751              b = c.indexOf("; " + p);
 752  
 753              if (b == -1) {
 754                  b = c.indexOf(p);
 755  
 756                  if (b != 0)
 757                      return null;
 758              } else
 759                  b += 2;
 760  
 761              e = c.indexOf(";", b);
 762  
 763              if (e == -1)
 764                  e = c.length;
 765  
 766              return unescape(c.substring(b + p.length, e));
 767          },
 768  
 769          set : function(n, v, e, p, d, s) {
 770              document.cookie = n + "=" + escape(v) +
 771                  ((e) ? "; expires=" + e.toGMTString() : "") +
 772                  ((p) ? "; path=" + escape(p) : "") +
 773                  ((d) ? "; domain=" + d : "") +
 774                  ((s) ? "; secure" : "");
 775          },
 776  
 777          remove : function(n, p) {
 778              var d = new Date();
 779  
 780              d.setTime(d.getTime() - 1000);
 781  
 782              this.set(n, '', d, p, d);
 783          }
 784      });
 785  })();
 786  tinymce.create('static tinymce.util.JSON', {
 787      serialize : function(o) {
 788          var i, v, s = tinymce.util.JSON.serialize, t;
 789  
 790          if (o == null)
 791              return 'null';
 792  
 793          t = typeof o;
 794  
 795          if (t == 'string') {
 796              v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
 797  
 798              return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {
 799                  i = v.indexOf(b);
 800  
 801                  if (i + 1)
 802                      return '\\' + v.charAt(i + 1);
 803  
 804                  a = b.charCodeAt().toString(16);
 805  
 806                  return '\\u' + '0000'.substring(a.length) + a;
 807              }) + '"';
 808          }
 809  
 810          if (t == 'object') {
 811              if (o.hasOwnProperty && o instanceof Array) {
 812                      for (i=0, v = '['; i<o.length; i++)
 813                          v += (i > 0 ? ',' : '') + s(o[i]);
 814  
 815                      return v + ']';
 816                  }
 817  
 818                  v = '{';
 819  
 820                  for (i in o)
 821                      v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
 822  
 823                  return v + '}';
 824          }
 825  
 826          return '' + o;
 827      },
 828  
 829      parse : function(s) {
 830          try {
 831              return eval('(' + s + ')');
 832          } catch (ex) {
 833              // Ignore
 834          }
 835      }
 836  
 837      });
 838  tinymce.create('static tinymce.util.XHR', {
 839      send : function(o) {
 840          var x, t, w = window, c = 0;
 841  
 842          // Default settings
 843          o.scope = o.scope || this;
 844          o.success_scope = o.success_scope || o.scope;
 845          o.error_scope = o.error_scope || o.scope;
 846          o.async = o.async === false ? false : true;
 847          o.data = o.data || '';
 848  
 849  		function get(s) {
 850              x = 0;
 851  
 852              try {
 853                  x = new ActiveXObject(s);
 854              } catch (ex) {
 855              }
 856  
 857              return x;
 858          };
 859  
 860          x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
 861  
 862          if (x) {
 863              if (x.overrideMimeType)
 864                  x.overrideMimeType(o.content_type);
 865  
 866              x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
 867  
 868              if (o.content_type)
 869                  x.setRequestHeader('Content-Type', o.content_type);
 870  
 871              x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
 872  
 873              x.send(o.data);
 874  
 875  			function ready() {
 876                  if (!o.async || x.readyState == 4 || c++ > 10000) {
 877                      if (o.success && c < 10000 && x.status == 200)
 878                          o.success.call(o.success_scope, '' + x.responseText, x, o);
 879                      else if (o.error)
 880                          o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
 881  
 882                      x = null;
 883                  } else
 884                      w.setTimeout(ready, 10);
 885              };
 886  
 887              // Syncronous request
 888              if (!o.async)
 889                  return ready();
 890  
 891              // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
 892              t = w.setTimeout(ready, 10);
 893          }
 894      }
 895  });
 896  (function() {
 897      var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
 898  
 899      tinymce.create('tinymce.util.JSONRequest', {
 900          JSONRequest : function(s) {
 901              this.settings = extend({
 902              }, s);
 903              this.count = 0;
 904          },
 905  
 906          send : function(o) {
 907              var ecb = o.error, scb = o.success;
 908  
 909              o = extend(this.settings, o);
 910  
 911              o.success = function(c, x) {
 912                  c = JSON.parse(c);
 913  
 914                  if (typeof(c) == 'undefined') {
 915                      c = {
 916                          error : 'JSON Parse error.'
 917                      };
 918                  }
 919  
 920                  if (c.error)
 921                      ecb.call(o.error_scope || o.scope, c.error, x);
 922                  else
 923                      scb.call(o.success_scope || o.scope, c.result);
 924              };
 925  
 926              o.error = function(ty, x) {
 927                  ecb.call(o.error_scope || o.scope, ty, x);
 928              };
 929  
 930              o.data = JSON.serialize({
 931                  id : o.id || 'c' + (this.count++),
 932                  method : o.method,
 933                  params : o.params
 934              });
 935  
 936              // JSON content type for Ruby on rails. Bug: #1883287
 937              o.content_type = 'application/json';
 938  
 939              XHR.send(o);
 940          },
 941  
 942          'static' : {
 943              sendRPC : function(o) {
 944                  return new tinymce.util.JSONRequest().send(o);
 945              }
 946          }
 947      });
 948  }());(function(tinymce) {
 949      // Shorten names
 950      var each = tinymce.each, is = tinymce.is;
 951      var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;
 952  
 953      tinymce.create('tinymce.dom.DOMUtils', {
 954          doc : null,
 955          root : null,
 956          files : null,
 957          pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
 958          props : {
 959              "for" : "htmlFor",
 960              "class" : "className",
 961              className : "className",
 962              checked : "checked",
 963              disabled : "disabled",
 964              maxlength : "maxLength",
 965              readonly : "readOnly",
 966              selected : "selected",
 967              value : "value",
 968              id : "id",
 969              name : "name",
 970              type : "type"
 971          },
 972  
 973          DOMUtils : function(d, s) {
 974              var t = this;
 975  
 976              t.doc = d;
 977              t.win = window;
 978              t.files = {};
 979              t.cssFlicker = false;
 980              t.counter = 0;
 981              t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; 
 982              t.stdMode = d.documentMode === 8;
 983  
 984              t.settings = s = tinymce.extend({
 985                  keep_values : false,
 986                  hex_colors : 1,
 987                  process_html : 1
 988              }, s);
 989  
 990              // Fix IE6SP2 flicker and check it failed for pre SP2
 991              if (tinymce.isIE6) {
 992                  try {
 993                      d.execCommand('BackgroundImageCache', false, true);
 994                  } catch (e) {
 995                      t.cssFlicker = true;
 996                  }
 997              }
 998  
 999              tinymce.addUnload(t.destroy, t);
1000          },
1001  
1002          getRoot : function() {
1003              var t = this, s = t.settings;
1004  
1005              return (s && t.get(s.root_element)) || t.doc.body;
1006          },
1007  
1008          getViewPort : function(w) {
1009              var d, b;
1010  
1011              w = !w ? this.win : w;
1012              d = w.document;
1013              b = this.boxModel ? d.documentElement : d.body;
1014  
1015              // Returns viewport size excluding scrollbars
1016              return {
1017                  x : w.pageXOffset || b.scrollLeft,
1018                  y : w.pageYOffset || b.scrollTop,
1019                  w : w.innerWidth || b.clientWidth,
1020                  h : w.innerHeight || b.clientHeight
1021              };
1022          },
1023  
1024          getRect : function(e) {
1025              var p, t = this, sr;
1026  
1027              e = t.get(e);
1028              p = t.getPos(e);
1029              sr = t.getSize(e);
1030  
1031              return {
1032                  x : p.x,
1033                  y : p.y,
1034                  w : sr.w,
1035                  h : sr.h
1036              };
1037          },
1038  
1039          getSize : function(e) {
1040              var t = this, w, h;
1041  
1042              e = t.get(e);
1043              w = t.getStyle(e, 'width');
1044              h = t.getStyle(e, 'height');
1045  
1046              // Non pixel value, then force offset/clientWidth
1047              if (w.indexOf('px') === -1)
1048                  w = 0;
1049  
1050              // Non pixel value, then force offset/clientWidth
1051              if (h.indexOf('px') === -1)
1052                  h = 0;
1053  
1054              return {
1055                  w : parseInt(w) || e.offsetWidth || e.clientWidth,
1056                  h : parseInt(h) || e.offsetHeight || e.clientHeight
1057              };
1058          },
1059  
1060          getParent : function(n, f, r) {
1061              return this.getParents(n, f, r, false);
1062          },
1063  
1064          getParents : function(n, f, r, c) {
1065              var t = this, na, se = t.settings, o = [];
1066  
1067              n = t.get(n);
1068              c = c === undefined;
1069  
1070              if (se.strict_root)
1071                  r = r || t.getRoot();
1072  
1073              // Wrap node name as func
1074              if (is(f, 'string')) {
1075                  na = f;
1076  
1077                  if (f === '*') {
1078                      f = function(n) {return n.nodeType == 1;};
1079                  } else {
1080                      f = function(n) {
1081                          return t.is(n, na);
1082                      };
1083                  }
1084              }
1085  
1086              while (n) {
1087                  if (n == r || !n.nodeType || n.nodeType === 9)
1088                      break;
1089  
1090                  if (!f || f(n)) {
1091                      if (c)
1092                          o.push(n);
1093                      else
1094                          return n;
1095                  }
1096  
1097                  n = n.parentNode;
1098              }
1099  
1100              return c ? o : null;
1101          },
1102  
1103          get : function(e) {
1104              var n;
1105  
1106              if (e && this.doc && typeof(e) == 'string') {
1107                  n = e;
1108                  e = this.doc.getElementById(e);
1109  
1110                  // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
1111                  if (e && e.id !== n)
1112                      return this.doc.getElementsByName(n)[1];
1113              }
1114  
1115              return e;
1116          },
1117  
1118          getNext : function(node, selector) {
1119              return this._findSib(node, selector, 'nextSibling');
1120          },
1121  
1122          getPrev : function(node, selector) {
1123              return this._findSib(node, selector, 'previousSibling');
1124          },
1125  
1126  
1127          select : function(pa, s) {
1128              var t = this;
1129  
1130              return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);
1131          },
1132  
1133          is : function(n, patt) {
1134              return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0;
1135          },
1136  
1137  
1138          add : function(p, n, a, h, c) {
1139              var t = this;
1140  
1141              return this.run(p, function(p) {
1142                  var e, k;
1143  
1144                  e = is(n, 'string') ? t.doc.createElement(n) : n;
1145                  t.setAttribs(e, a);
1146  
1147                  if (h) {
1148                      if (h.nodeType)
1149                          e.appendChild(h);
1150                      else
1151                          t.setHTML(e, h);
1152                  }
1153  
1154                  return !c ? p.appendChild(e) : e;
1155              });
1156          },
1157  
1158          create : function(n, a, h) {
1159              return this.add(this.doc.createElement(n), n, a, h, 1);
1160          },
1161  
1162          createHTML : function(n, a, h) {
1163              var o = '', t = this, k;
1164  
1165              o += '<' + n;
1166  
1167              for (k in a) {
1168                  if (a.hasOwnProperty(k))
1169                      o += ' ' + k + '="' + t.encode(a[k]) + '"';
1170              }
1171  
1172              if (tinymce.is(h))
1173                  return o + '>' + h + '</' + n + '>';
1174  
1175              return o + ' />';
1176          },
1177  
1178          remove : function(n, k) {
1179              var t = this;
1180  
1181              return this.run(n, function(n) {
1182                  var p, g, i;
1183  
1184                  p = n.parentNode;
1185  
1186                  if (!p)
1187                      return null;
1188  
1189                  if (k) {
1190                      for (i = n.childNodes.length - 1; i >= 0; i--)
1191                          t.insertAfter(n.childNodes[i], n);
1192  
1193                      //each(n.childNodes, function(c) {
1194                      //    p.insertBefore(c.cloneNode(true), n);
1195                      //});
1196                  }
1197  
1198                  // Fix IE psuedo leak
1199                  if (t.fixPsuedoLeaks) {
1200                      p = n.cloneNode(true);
1201                      k = 'IELeakGarbageBin';
1202                      g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});
1203                      g.appendChild(n);
1204                      g.innerHTML = '';
1205  
1206                      return p;
1207                  }
1208  
1209                  return p.removeChild(n);
1210              });
1211          },
1212  
1213          setStyle : function(n, na, v) {
1214              var t = this;
1215  
1216              return t.run(n, function(e) {
1217                  var s, i;
1218  
1219                  s = e.style;
1220  
1221                  // Camelcase it, if needed
1222                  na = na.replace(/-(\D)/g, function(a, b){
1223                      return b.toUpperCase();
1224                  });
1225  
1226                  // Default px suffix on these
1227                  if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
1228                      v += 'px';
1229  
1230                  switch (na) {
1231                      case 'opacity':
1232                          // IE specific opacity
1233                          if (isIE) {
1234                              s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
1235  
1236                              if (!n.currentStyle || !n.currentStyle.hasLayout)
1237                                  s.display = 'inline-block';
1238                          }
1239  
1240                          // Fix for older browsers
1241                          s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
1242                          break;
1243  
1244                      case 'float':
1245                          isIE ? s.styleFloat = v : s.cssFloat = v;
1246                          break;
1247                      
1248                      default:
1249                          s[na] = v || '';
1250                  }
1251  
1252                  // Force update of the style data
1253                  if (t.settings.update_styles)
1254                      t.setAttrib(e, 'mce_style');
1255              });
1256          },
1257  
1258          getStyle : function(n, na, c) {
1259              n = this.get(n);
1260  
1261              if (!n)
1262                  return false;
1263  
1264              // Gecko
1265              if (this.doc.defaultView && c) {
1266                  // Remove camelcase
1267                  na = na.replace(/[A-Z]/g, function(a){
1268                      return '-' + a;
1269                  });
1270  
1271                  try {
1272                      return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
1273                  } catch (ex) {
1274                      // Old safari might fail
1275                      return null;
1276                  }
1277              }
1278  
1279              // Camelcase it, if needed
1280              na = na.replace(/-(\D)/g, function(a, b){
1281                  return b.toUpperCase();
1282              });
1283  
1284              if (na == 'float')
1285                  na = isIE ? 'styleFloat' : 'cssFloat';
1286  
1287              // IE & Opera
1288              if (n.currentStyle && c)
1289                  return n.currentStyle[na];
1290  
1291              return n.style[na];
1292          },
1293  
1294          setStyles : function(e, o) {
1295              var t = this, s = t.settings, ol;
1296  
1297              ol = s.update_styles;
1298              s.update_styles = 0;
1299  
1300              each(o, function(v, n) {
1301                  t.setStyle(e, n, v);
1302              });
1303  
1304              // Update style info
1305              s.update_styles = ol;
1306              if (s.update_styles)
1307                  t.setAttrib(e, s.cssText);
1308          },
1309  
1310          setAttrib : function(e, n, v) {
1311              var t = this;
1312  
1313              // Whats the point
1314              if (!e || !n)
1315                  return;
1316  
1317              // Strict XML mode
1318              if (t.settings.strict)
1319                  n = n.toLowerCase();
1320  
1321              return this.run(e, function(e) {
1322                  var s = t.settings;
1323  
1324                  switch (n) {
1325                      case "style":
1326                          if (!is(v, 'string')) {
1327                              each(v, function(v, n) {
1328                                  t.setStyle(e, n, v);
1329                              });
1330  
1331                              return;
1332                          }
1333  
1334                          // No mce_style for elements with these since they might get resized by the user
1335                          if (s.keep_values) {
1336                              if (v && !t._isRes(v))
1337                                  e.setAttribute('mce_style', v, 2);
1338                              else
1339                                  e.removeAttribute('mce_style', 2);
1340                          }
1341  
1342                          e.style.cssText = v;
1343                          break;
1344  
1345                      case "class":
1346                          e.className = v || ''; // Fix IE null bug
1347                          break;
1348  
1349                      case "src":
1350                      case "href":
1351                          if (s.keep_values) {
1352                              if (s.url_converter)
1353                                  v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
1354  
1355                              t.setAttrib(e, 'mce_' + n, v, 2);
1356                          }
1357  
1358                          break;
1359                      
1360                      case "shape":
1361                          e.setAttribute('mce_style', v);
1362                          break;
1363                  }
1364  
1365                  if (is(v) && v !== null && v.length !== 0)
1366                      e.setAttribute(n, '' + v, 2);
1367                  else
1368                      e.removeAttribute(n, 2);
1369              });
1370          },
1371  
1372          setAttribs : function(e, o) {
1373              var t = this;
1374  
1375              return this.run(e, function(e) {
1376                  each(o, function(v, n) {
1377                      t.setAttrib(e, n, v);
1378                  });
1379              });
1380          },
1381  
1382          getAttrib : function(e, n, dv) {
1383              var v, t = this;
1384  
1385              e = t.get(e);
1386  
1387              if (!e || e.nodeType !== 1)
1388                  return false;
1389  
1390              if (!is(dv))
1391                  dv = '';
1392  
1393              // Try the mce variant for these
1394              if (/^(src|href|style|coords|shape)$/.test(n)) {
1395                  v = e.getAttribute("mce_" + n);
1396  
1397                  if (v)
1398                      return v;
1399              }
1400  
1401              if (isIE && t.props[n]) {
1402                  v = e[t.props[n]];
1403                  v = v && v.nodeValue ? v.nodeValue : v;
1404              }
1405  
1406              if (!v)
1407                  v = e.getAttribute(n, 2);
1408  
1409              if (n === 'style') {
1410                  v = v || e.style.cssText;
1411  
1412                  if (v) {
1413                      v = t.serializeStyle(t.parseStyle(v));
1414  
1415                      if (t.settings.keep_values && !t._isRes(v))
1416                          e.setAttribute('mce_style', v);
1417                  }
1418              }
1419  
1420              // Remove Apple and WebKit stuff
1421              if (isWebKit && n === "class" && v)
1422                  v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
1423  
1424              // Handle IE issues
1425              if (isIE) {
1426                  switch (n) {
1427                      case 'rowspan':
1428                      case 'colspan':
1429                          // IE returns 1 as default value
1430                          if (v === 1)
1431                              v = '';
1432  
1433                          break;
1434  
1435                      case 'size':
1436                          // IE returns +0 as default value for size
1437                          if (v === '+0' || v === 20 || v === 0)
1438                              v = '';
1439  
1440                          break;
1441  
1442                      case 'width':
1443                      case 'height':
1444                      case 'vspace':
1445                      case 'checked':
1446                      case 'disabled':
1447                      case 'readonly':
1448                          if (v === 0)
1449                              v = '';
1450  
1451                          break;
1452  
1453                      case 'hspace':
1454                          // IE returns -1 as default value
1455                          if (v === -1)
1456                              v = '';
1457  
1458                          break;
1459  
1460                      case 'maxlength':
1461                      case 'tabindex':
1462                          // IE returns default value
1463                          if (v === 32768 || v === 2147483647 || v === '32768')
1464                              v = '';
1465  
1466                          break;
1467  
1468                      case 'multiple':
1469                      case 'compact':
1470                      case 'noshade':
1471                      case 'nowrap':
1472                          if (v === 65535)
1473                              return n;
1474  
1475                          return dv;
1476  
1477                      case 'shape':
1478                          v = v.toLowerCase();
1479                          break;
1480  
1481                      default:
1482                          // IE has odd anonymous function for event attributes
1483                          if (n.indexOf('on') === 0 && v)
1484                              v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
1485                  }
1486              }
1487  
1488              return (v !== undefined && v !== null && v !== '') ? '' + v : dv;
1489          },
1490  
1491          getPos : function(n, ro) {
1492              var t = this, x = 0, y = 0, e, d = t.doc, r;
1493  
1494              n = t.get(n);
1495              ro = ro || d.body;
1496  
1497              if (n) {
1498                  // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1499                  if (isIE && !t.stdMode) {
1500                      n = n.getBoundingClientRect();
1501                      e = t.boxModel ? d.documentElement : d.body;
1502                      x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
1503                      x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
1504                      n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset
1505  
1506                      return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
1507                  }
1508  
1509                  r = n;
1510                  while (r && r != ro && r.nodeType) {
1511                      x += r.offsetLeft || 0;
1512                      y += r.offsetTop || 0;
1513                      r = r.offsetParent;
1514                  }
1515  
1516                  r = n.parentNode;
1517                  while (r && r != ro && r.nodeType) {
1518                      x -= r.scrollLeft || 0;
1519                      y -= r.scrollTop || 0;
1520                      r = r.parentNode;
1521                  }
1522              }
1523  
1524              return {x : x, y : y};
1525          },
1526  
1527          parseStyle : function(st) {
1528              var t = this, s = t.settings, o = {};
1529  
1530              if (!st)
1531                  return o;
1532  
1533  			function compress(p, s, ot) {
1534                  var t, r, b, l;
1535  
1536                  // Get values and check it it needs compressing
1537                  t = o[p + '-top' + s];
1538                  if (!t)
1539                      return;
1540  
1541                  r = o[p + '-right' + s];
1542                  if (t != r)
1543                      return;
1544  
1545                  b = o[p + '-bottom' + s];
1546                  if (r != b)
1547                      return;
1548  
1549                  l = o[p + '-left' + s];
1550                  if (b != l)
1551                      return;
1552  
1553                  // Compress
1554                  o[ot] = l;
1555                  delete o[p + '-top' + s];
1556                  delete o[p + '-right' + s];
1557                  delete o[p + '-bottom' + s];
1558                  delete o[p + '-left' + s];
1559              };
1560  
1561  			function compress2(ta, a, b, c) {
1562                  var t;
1563  
1564                  t = o[a];
1565                  if (!t)
1566                      return;
1567  
1568                  t = o[b];
1569                  if (!t)
1570                      return;
1571  
1572                  t = o[c];
1573                  if (!t)
1574                      return;
1575  
1576                  // Compress
1577                  o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
1578                  delete o[a];
1579                  delete o[b];
1580                  delete o[c];
1581              };
1582  
1583              st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1584  
1585              each(st.split(';'), function(v) {
1586                  var sv, ur = [];
1587  
1588                  if (v) {
1589                      v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
1590                      v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
1591                      v = v.split(':');
1592                      sv = tinymce.trim(v[1]);
1593                      sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});
1594  
1595                      sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
1596                          return t.toHex(v);
1597                      });
1598  
1599                      if (s.url_converter) {
1600                          sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
1601                              return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
1602                          });
1603                      }
1604  
1605                      o[tinymce.trim(v[0]).toLowerCase()] = sv;
1606                  }
1607              });
1608  
1609              compress("border", "", "border");
1610              compress("border", "-width", "border-width");
1611              compress("border", "-color", "border-color");
1612              compress("border", "-style", "border-style");
1613              compress("padding", "", "padding");
1614              compress("margin", "", "margin");
1615              compress2('border', 'border-width', 'border-style', 'border-color');
1616  
1617              if (isIE) {
1618                  // Remove pointless border
1619                  if (o.border == 'medium none')
1620                      o.border = '';
1621              }
1622  
1623              return o;
1624          },
1625  
1626          serializeStyle : function(o) {
1627              var s = '';
1628  
1629              each(o, function(v, k) {
1630                  if (k && v) {
1631                      if (tinymce.isGecko && k.indexOf('-moz-') === 0)
1632                          return;
1633  
1634                      switch (k) {
1635                          case 'color':
1636                          case 'background-color':
1637                              v = v.toLowerCase();
1638                              break;
1639                      }
1640  
1641                      s += (s ? ' ' : '') + k + ': ' + v + ';';
1642                  }
1643              });
1644  
1645              return s;
1646          },
1647  
1648          loadCSS : function(u) {
1649              var t = this, d = t.doc, head;
1650  
1651              if (!u)
1652                  u = '';
1653  
1654              head = t.select('head')[0];
1655  
1656              each(u.split(','), function(u) {
1657                  var link;
1658  
1659                  if (t.files[u])
1660                      return;
1661  
1662                  t.files[u] = true;
1663                  link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});
1664  
1665                  // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
1666                  // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading
1667                  // It's ugly but it seems to work fine.
1668                  if (isIE && d.documentMode) {
1669                      link.onload = function() {
1670                          d.recalc();
1671                          link.onload = null;
1672                      };
1673                  }
1674  
1675                  head.appendChild(link);
1676              });
1677          },
1678  
1679          addClass : function(e, c) {
1680              return this.run(e, function(e) {
1681                  var o;
1682  
1683                  if (!c)
1684                      return 0;
1685  
1686                  if (this.hasClass(e, c))
1687                      return e.className;
1688  
1689                  o = this.removeClass(e, c);
1690  
1691                  return e.className = (o != '' ? (o + ' ') : '') + c;
1692              });
1693          },
1694  
1695          removeClass : function(e, c) {
1696              var t = this, re;
1697  
1698              return t.run(e, function(e) {
1699                  var v;
1700  
1701                  if (t.hasClass(e, c)) {
1702                      if (!re)
1703                          re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
1704  
1705                      v = e.className.replace(re, ' ');
1706  
1707                      return e.className = tinymce.trim(v != ' ' ? v : '');
1708                  }
1709  
1710                  return e.className;
1711              });
1712          },
1713  
1714          hasClass : function(n, c) {
1715              n = this.get(n);
1716  
1717              if (!n || !c)
1718                  return false;
1719  
1720              return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
1721          },
1722  
1723          show : function(e) {
1724              return this.setStyle(e, 'display', 'block');
1725          },
1726  
1727          hide : function(e) {
1728              return this.setStyle(e, 'display', 'none');
1729          },
1730  
1731          isHidden : function(e) {
1732              e = this.get(e);
1733  
1734              return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
1735          },
1736  
1737          uniqueId : function(p) {
1738              return (!p ? 'mce_' : p) + (this.counter++);
1739          },
1740  
1741          setHTML : function(e, h) {
1742              var t = this;
1743  
1744              return this.run(e, function(e) {
1745                  var x, i, nl, n, p, x;
1746  
1747                  h = t.processHTML(h);
1748  
1749                  if (isIE) {
1750  					function set() {
1751                          try {
1752                              // IE will remove comments from the beginning
1753                              // unless you padd the contents with something
1754                              e.innerHTML = '<br />' + h;
1755                              e.removeChild(e.firstChild);
1756                          } catch (ex) {
1757                              // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p
1758                              // This seems to fix this problem
1759  
1760                              // Remove all child nodes
1761                              while (e.firstChild)
1762                                  e.firstChild.removeNode();
1763  
1764                              // Create new div with HTML contents and a BR infront to keep comments
1765                              x = t.create('div');
1766                              x.innerHTML = '<br />' + h;
1767  
1768                              // Add all children from div to target
1769                              each (x.childNodes, function(n, i) {
1770                                  // Skip br element
1771                                  if (i)
1772                                      e.appendChild(n);
1773                              });
1774                          }
1775                      };
1776  
1777                      // IE has a serious bug when it comes to paragraphs it can produce an invalid
1778                      // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
1779                      // It seems to be that IE doesn't like a root block element placed inside another root block element
1780                      if (t.settings.fix_ie_paragraphs)
1781                          h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');
1782  
1783                      set();
1784  
1785                      if (t.settings.fix_ie_paragraphs) {
1786                          // Check for odd paragraphs this is a sign of a broken DOM
1787                          nl = e.getElementsByTagName("p");
1788                          for (i = nl.length - 1, x = 0; i >= 0; i--) {
1789                              n = nl[i];
1790  
1791                              if (!n.hasChildNodes()) {
1792                                  if (!n.mce_keep) {
1793                                      x = 1; // Is broken
1794                                      break;
1795                                  }
1796  
1797                                  n.removeAttribute('mce_keep');
1798                              }
1799                          }
1800                      }
1801  
1802                      // Time to fix the madness IE left us
1803                      if (x) {
1804                          // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1805                          // after we use innerHTML we can fix the DOM tree
1806                          h = h.replace(/<p ([^>]+)>|<p>/g, '<div $1 mce_tmp="1">');
1807                          h = h.replace(/<\/p>/g, '</div>');
1808  
1809                          // Set the new HTML with DIVs
1810                          set();
1811  
1812                          // Replace all DIV elements with he mce_tmp attibute back to paragraphs
1813                          // This is needed since IE has a annoying bug see above for details
1814                          // This is a slow process but it has to be done. :(
1815                          if (t.settings.fix_ie_paragraphs) {
1816                              nl = e.getElementsByTagName("DIV");
1817                              for (i = nl.length - 1; i >= 0; i--) {
1818                                  n = nl[i];
1819  
1820                                  // Is it a temp div
1821                                  if (n.mce_tmp) {
1822                                      // Create new paragraph
1823                                      p = t.doc.createElement('p');
1824  
1825                                      // Copy all attributes
1826                                      n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
1827                                          var v;
1828  
1829                                          if (b !== 'mce_tmp') {
1830                                              v = n.getAttribute(b);
1831  
1832                                              if (!v && b === 'class')
1833                                                  v = n.className;
1834  
1835                                              p.setAttribute(b, v);
1836                                          }
1837                                      });
1838  
1839                                      // Append all children to new paragraph
1840                                      for (x = 0; x<n.childNodes.length; x++)
1841                                          p.appendChild(n.childNodes[x].cloneNode(true));
1842  
1843                                      // Replace div with new paragraph
1844                                      n.swapNode(p);
1845                                  }
1846                              }
1847                          }
1848                      }
1849                  } else
1850                      e.innerHTML = h;
1851  
1852                  return h;
1853              });
1854          },
1855  
1856          processHTML : function(h) {
1857              var t = this, s = t.settings;
1858  
1859              if (!s.process_html)
1860                  return h;
1861  
1862              // Convert strong and em to b and i in FF since it can't handle them
1863              if (tinymce.isGecko) {
1864                  h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
1865                  h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
1866              } else if (isIE) {
1867                  h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
1868                  h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct
1869              }
1870  
1871              // Fix some issues
1872              h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open
1873  
1874              // Store away src and href in mce_src and mce_href since browsers mess them up
1875              if (s.keep_values) {
1876                  // Wrap scripts and styles in comments for serialization purposes
1877                  if (/<script|noscript|style/i.test(h)) {
1878  					function trim(s) {
1879                          // Remove prefix and suffix code for element
1880                          s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');
1881                          s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
1882                          s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');
1883                          s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
1884  
1885                          return s;
1886                      };
1887  
1888                      // Wrap the script contents in CDATA and keep them from executing
1889                      h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {
1890                          // Force type attribute
1891                          if (!attribs)
1892                              attribs = ' type="text/javascript"';
1893  
1894                          // Convert the src attribute of the scripts
1895                          attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {
1896                              if (s.url_converter)
1897                                  url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));
1898  
1899                              return 'mce_src="' + url + '"';
1900                          });
1901  
1902                          // Wrap text contents
1903                          if (tinymce.trim(text))
1904                              text = '<!--\n' + trim(text) + '\n// -->';
1905  
1906                          return '<mce:script' + attribs + '>' + text + '</mce:script>';
1907                      });
1908  
1909                      // Wrap style elements
1910                      h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {
1911                          // Wrap text contents
1912                          if (text)
1913                              text = '<!--\n' + trim(text) + '\n-->';
1914  
1915                          return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' mce_bogus="1">' + text + '</style>';
1916                      });
1917  
1918                      // Wrap noscript elements
1919                      h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {
1920                          return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';
1921                      });
1922                  }
1923  
1924                  h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');
1925  
1926                  // Process all tags with src, href or style
1927                  h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {
1928  					function handle(m, b, c) {
1929                          var u = c;
1930  
1931                          // Tag already got a mce_ version
1932                          if (a.indexOf('mce_' + b) != -1)
1933                              return m;
1934  
1935                          if (b == 'style') {
1936                              // No mce_style for elements with these since they might get resized by the user
1937                              if (t._isRes(c))
1938                                  return m;
1939  
1940                              if (s.hex_colors) {
1941                                  u = u.replace(/rgb\([^\)]+\)/g, function(v) {
1942                                      return t.toHex(v);
1943                                  });
1944                              }
1945  
1946                              if (s.url_converter) {
1947                                  u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) {
1948                                      return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')';
1949                                  });
1950                              }
1951                          } else if (b != 'coords' && b != 'shape') {
1952                              if (s.url_converter)
1953                                  u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
1954                          }
1955  
1956                          return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
1957                      };
1958  
1959                      a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C
1960                      a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C
1961  
1962                      return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE
1963                  });
1964              }
1965  
1966              return h;
1967          },
1968  
1969          getOuterHTML : function(e) {
1970              var d;
1971  
1972              e = this.get(e);
1973  
1974              if (!e)
1975                  return null;
1976  
1977              if (e.outerHTML !== undefined)
1978                  return e.outerHTML;
1979  
1980              d = (e.ownerDocument || this.doc).createElement("body");
1981              d.appendChild(e.cloneNode(true));
1982  
1983              return d.innerHTML;
1984          },
1985  
1986          setOuterHTML : function(e, h, d) {
1987              var t = this;
1988  
1989  			function setHTML(e, h, d) {
1990                  var n, tp;
1991                  
1992                  tp = d.createElement("body");
1993                  tp.innerHTML = h;
1994  
1995                  n = tp.lastChild;
1996                  while (n) {
1997                      t.insertAfter(n.cloneNode(true), e);
1998                      n = n.previousSibling;
1999                  }
2000  
2001                  t.remove(e);
2002              };
2003  
2004              return this.run(e, function(e) {
2005                  e = t.get(e);
2006  
2007                  // Only set HTML on elements
2008                  if (e.nodeType == 1) {
2009                      d = d || e.ownerDocument || t.doc;
2010  
2011                      if (isIE) {
2012                          try {
2013                              // Try outerHTML for IE it sometimes produces an unknown runtime error
2014                              if (isIE && e.nodeType == 1)
2015                                  e.outerHTML = h;
2016                              else
2017                                  setHTML(e, h, d);
2018                          } catch (ex) {
2019                              // Fix for unknown runtime error
2020                              setHTML(e, h, d);
2021                          }
2022                      } else
2023                          setHTML(e, h, d);
2024                  }
2025              });
2026          },
2027  
2028          decode : function(s) {
2029              var e, n, v;
2030  
2031              // Look for entities to decode
2032              if (/&[^;]+;/.test(s)) {
2033                  // Decode the entities using a div element not super efficient but less code
2034                  e = this.doc.createElement("div");
2035                  e.innerHTML = s;
2036                  n = e.firstChild;
2037                  v = '';
2038  
2039                  if (n) {
2040                      do {
2041                          v += n.nodeValue;
2042                      } while (n.nextSibling);
2043                  }
2044  
2045                  return v || s;
2046              }
2047  
2048              return s;
2049          },
2050  
2051          encode : function(s) {
2052              return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
2053                  switch (c) {
2054                      case '&':
2055                          return '&amp;';
2056  
2057                      case '"':
2058                          return '&quot;';
2059  
2060                      case '<':
2061                          return '&lt;';
2062  
2063                      case '>':
2064                          return '&gt;';
2065                  }
2066  
2067                  return c;
2068              }) : s;
2069          },
2070  
2071          insertAfter : function(n, r) {
2072              var t = this;
2073  
2074              r = t.get(r);
2075  
2076              return this.run(n, function(n) {
2077                  var p, ns;
2078  
2079                  p = r.parentNode;
2080                  ns = r.nextSibling;
2081  
2082                  if (ns)
2083                      p.insertBefore(n, ns);
2084                  else
2085                      p.appendChild(n);
2086  
2087                  return n;
2088              });
2089          },
2090  
2091          isBlock : function(n) {
2092              if (n.nodeType && n.nodeType !== 1)
2093                  return false;
2094  
2095              n = n.nodeName || n;
2096  
2097              return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
2098          },
2099  
2100          replace : function(n, o, k) {
2101              var t = this;
2102  
2103              if (is(o, 'array'))
2104                  n = n.cloneNode(true);
2105  
2106              return t.run(o, function(o) {
2107                  if (k) {
2108                      each(o.childNodes, function(c) {
2109                          n.appendChild(c.cloneNode(true));
2110                      });
2111                  }
2112  
2113                  // Fix IE psuedo leak for elements since replacing elements if fairly common
2114                  // Will break parentNode for some unknown reason
2115                  if (t.fixPsuedoLeaks && o.nodeType === 1) {
2116                      o.parentNode.insertBefore(n, o);
2117                      t.remove(o);
2118                      return n;
2119                  }
2120  
2121                  return o.parentNode.replaceChild(n, o);
2122              });
2123          },
2124  
2125          findCommonAncestor : function(a, b) {
2126              var ps = a, pe;
2127  
2128              while (ps) {
2129                  pe = b;
2130  
2131                  while (pe && ps != pe)
2132                      pe = pe.parentNode;
2133  
2134                  if (ps == pe)
2135                      break;
2136  
2137                  ps = ps.parentNode;
2138              }
2139  
2140              if (!ps && a.ownerDocument)
2141                  return a.ownerDocument.documentElement;
2142  
2143              return ps;
2144          },
2145  
2146          toHex : function(s) {
2147              var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
2148  
2149  			function hex(s) {
2150                  s = parseInt(s).toString(16);
2151  
2152                  return s.length > 1 ? s : '0' + s; // 0 -> 00
2153              };
2154  
2155              if (c) {
2156                  s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
2157  
2158                  return s;
2159              }
2160  
2161              return s;
2162          },
2163  
2164          getClasses : function() {
2165              var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
2166  
2167              if (t.classes)
2168                  return t.classes;
2169  
2170  			function addClasses(s) {
2171                  // IE style imports
2172                  each(s.imports, function(r) {
2173                      addClasses(r);
2174                  });
2175  
2176                  each(s.cssRules || s.rules, function(r) {
2177                      // Real type or fake it on IE
2178                      switch (r.type || 1) {
2179                          // Rule
2180                          case 1:
2181                              if (r.selectorText) {
2182                                  each(r.selectorText.split(','), function(v) {
2183                                      v = v.replace(/^\s*|\s*$|^\s\./g, "");
2184  
2185                                      // Is internal or it doesn't contain a class
2186                                      if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
2187                                          return;
2188  
2189                                      // Remove everything but class name
2190                                      ov = v;
2191                                      v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
2192  
2193                                      // Filter classes
2194                                      if (f && !(v = f(v, ov)))
2195                                          return;
2196  
2197                                      if (!lo[v]) {
2198                                          cl.push({'class' : v});
2199                                          lo[v] = 1;
2200                                      }
2201                                  });
2202                              }
2203                              break;
2204  
2205                          // Import
2206                          case 3:
2207                              addClasses(r.styleSheet);
2208                              break;
2209                      }
2210                  });
2211              };
2212  
2213              try {
2214                  each(t.doc.styleSheets, addClasses);
2215              } catch (ex) {
2216                  // Ignore
2217              }
2218  
2219              if (cl.length > 0)
2220                  t.classes = cl;
2221  
2222              return cl;
2223          },
2224  
2225          run : function(e, f, s) {
2226              var t = this, o;
2227  
2228              if (t.doc && typeof(e) === 'string')
2229                  e = t.get(e);
2230  
2231              if (!e)
2232                  return false;
2233  
2234              s = s || this;
2235              if (!e.nodeType && (e.length || e.length === 0)) {
2236                  o = [];
2237  
2238                  each(e, function(e, i) {
2239                      if (e) {
2240                          if (typeof(e) == 'string')
2241                              e = t.doc.getElementById(e);
2242  
2243                          o.push(f.call(s, e, i));
2244                      }
2245                  });
2246  
2247                  return o;
2248              }
2249  
2250              return f.call(s, e);
2251          },
2252  
2253          getAttribs : function(n) {
2254              var o;
2255  
2256              n = this.get(n);
2257  
2258              if (!n)
2259                  return [];
2260  
2261              if (isIE) {
2262                  o = [];
2263  
2264                  // Object will throw exception in IE
2265                  if (n.nodeName == 'OBJECT')
2266                      return n.attributes;
2267  
2268                  // It's crazy that this is faster in IE but it's because it returns all attributes all the time
2269                  n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
2270                      o.push({specified : 1, nodeName : b});
2271                  });
2272  
2273                  return o;
2274              }
2275  
2276              return n.attributes;
2277          },
2278  
2279          destroy : function(s) {
2280              var t = this;
2281  
2282              if (t.events)
2283                  t.events.destroy();
2284  
2285              t.win = t.doc = t.root = t.events = null;
2286  
2287              // Manual destroy then remove unload handler
2288              if (!s)
2289                  tinymce.removeUnload(t.destroy);
2290          },
2291  
2292          createRng : function() {
2293              var d = this.doc;
2294  
2295              return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
2296          },
2297  
2298          split : function(pe, e, re) {
2299              var t = this, r = t.createRng(), bef, aft, pa;
2300  
2301              // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence
2302              // but we don't want that in our code since it serves no purpose
2303              // For example if this is chopped:
2304              //   <p>text 1<span><b>CHOP</b></span>text 2</p>
2305              // would produce:
2306              //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
2307              // this function will then trim of empty edges and produce:
2308              //   <p>text 1</p><b>CHOP</b><p>text 2</p>
2309  			function trimEdge(n, na) {
2310                  n = n[na];
2311  
2312                  if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na]))
2313                      t.remove(n[na]);
2314              };
2315  
2316  			function isEmpty(n) {
2317                  n = t.getOuterHTML(n);
2318                  n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
2319                  n = n.replace(/<[^>]+>/g, ''); // Remove all tags
2320  
2321                  return n.replace(/[ \t\r\n]+|&nbsp;|&#160;/g, '') == '';
2322              };
2323  
2324              if (pe && e) {
2325                  // Get before chunk
2326                  r.setStartBefore(pe);
2327                  r.setEndBefore(e);
2328                  bef = r.extractContents();
2329  
2330                  // Get after chunk
2331                  r = t.createRng();
2332                  r.setStartAfter(e);
2333                  r.setEndAfter(pe);
2334                  aft = r.extractContents();
2335  
2336                  // Insert chunks and remove parent
2337                  pa = pe.parentNode;
2338  
2339                  // Remove right side edge of the before contents
2340                  trimEdge(bef, 'lastChild');
2341  
2342                  if (!isEmpty(bef))
2343                      pa.insertBefore(bef, pe);
2344  
2345                  if (re)
2346                      pa.replaceChild(re, e);
2347                  else
2348                      pa.insertBefore(e, pe);
2349  
2350                  // Remove left site edge of the after contents
2351                  trimEdge(aft, 'firstChild');
2352  
2353                  if (!isEmpty(aft))
2354                      pa.insertBefore(aft, pe);
2355  
2356                  t.remove(pe);
2357  
2358                  return re || e;
2359              }
2360          },
2361  
2362          bind : function(target, name, func, scope) {
2363              var t = this;
2364  
2365              if (!t.events)
2366                  t.events = new tinymce.dom.EventUtils();
2367  
2368              return t.events.add(target, name, func, scope || this);
2369          },
2370  
2371          unbind : function(target, name, func) {
2372              var t = this;
2373  
2374              if (!t.events)
2375                  t.events = new tinymce.dom.EventUtils();
2376  
2377              return t.events.remove(target, name, func);
2378          },
2379  
2380  
2381          _findSib : function(node, selector, name) {
2382              var t = this, f = selector;
2383  
2384              if (node) {
2385                  // If expression make a function of it using is
2386                  if (is(f, 'string')) {
2387                      f = function(node) {
2388                          return t.is(node, selector);
2389                      };
2390                  }
2391  
2392                  // Loop all siblings
2393                  for (node = node[name]; node; node = node[name]) {
2394                      if (f(node))
2395                          return node;
2396                  }
2397              }
2398  
2399              return null;
2400          },
2401  
2402          _isRes : function(c) {
2403              // Is live resizble element
2404              return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
2405          }
2406  
2407          /*
2408          walk : function(n, f, s) {
2409              var d = this.doc, w;
2410  
2411              if (d.createTreeWalker) {
2412                  w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
2413  
2414                  while ((n = w.nextNode()) != null)
2415                      f.call(s || this, n);
2416              } else
2417                  tinymce.walk(n, f, 'childNodes', s);
2418          }
2419          */
2420  
2421          /*
2422          toRGB : function(s) {
2423              var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
2424  
2425              if (c) {
2426                  // #FFF -> #FFFFFF
2427                  if (!is(c[3]))
2428                      c[3] = c[2] = c[1];
2429  
2430                  return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2431              }
2432  
2433              return s;
2434          }
2435          */
2436      });
2437  
2438      tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
2439  })(tinymce);
2440  (function(ns) {
2441      // Traverse constants
2442      var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend;
2443  
2444  	function indexOf(child, parent) {
2445          var i, node;
2446  
2447          if (child.parentNode != parent)
2448              return -1;
2449  
2450          for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling)
2451              i++;
2452  
2453          return i;
2454      };
2455  
2456  	function nodeIndex(n) {
2457          var i = 0;
2458  
2459          while (n.previousSibling) {
2460              i++;
2461              n = n.previousSibling;
2462          }
2463  
2464          return i;
2465      };
2466  
2467  	function getSelectedNode(container, offset) {
2468          var child;
2469  
2470          if (container.nodeType == 3 /* TEXT_NODE */)
2471              return container;
2472  
2473          if (offset < 0)
2474              return container;
2475  
2476          child = container.firstChild;
2477          while (child != null && offset > 0) {
2478              --offset;
2479              child = child.nextSibling;
2480          }
2481  
2482          if (child != null)
2483              return child;
2484  
2485          return container;
2486      };
2487  
2488      // Range constructor
2489  	function Range(dom) {
2490          var d = dom.doc;
2491  
2492          extend(this, {
2493              dom : dom,
2494  
2495              // Inital states
2496              startContainer : d,
2497              startOffset : 0,
2498              endContainer : d,
2499              endOffset : 0,
2500              collapsed : true,
2501              commonAncestorContainer : d,
2502  
2503              // Range constants
2504              START_TO_START : 0,
2505              START_TO_END : 1,
2506              END_TO_END : 2,
2507              END_TO_START : 3
2508          });
2509      };
2510  
2511      // Add range methods
2512      extend(Range.prototype, {
2513          setStart : function(n, o) {
2514              this._setEndPoint(true, n, o);
2515          },
2516  
2517          setEnd : function(n, o) {
2518              this._setEndPoint(false, n, o);
2519          },
2520  
2521          setStartBefore : function(n) {
2522              this.setStart(n.parentNode, nodeIndex(n));
2523          },
2524  
2525          setStartAfter : function(n) {
2526              this.setStart(n.parentNode, nodeIndex(n) + 1);
2527          },
2528  
2529          setEndBefore : function(n) {
2530              this.setEnd(n.parentNode, nodeIndex(n));
2531          },
2532  
2533          setEndAfter : function(n) {
2534              this.setEnd(n.parentNode, nodeIndex(n) + 1);
2535          },
2536  
2537          collapse : function(ts) {
2538              var t = this;
2539  
2540              if (ts) {
2541                  t.endContainer = t.startContainer;
2542                  t.endOffset = t.startOffset;
2543              } else {
2544                  t.startContainer = t.endContainer;
2545                  t.startOffset = t.endOffset;
2546              }
2547  
2548              t.collapsed = true;
2549          },
2550  
2551          selectNode : function(n) {
2552              this.setStartBefore(n);
2553              this.setEndAfter(n);
2554          },
2555  
2556          selectNodeContents : function(n) {
2557              this.setStart(n, 0);
2558              this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
2559          },
2560  
2561          compareBoundaryPoints : function(h, r) {
2562              var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset;
2563  
2564              // Check START_TO_START
2565              if (h === 0)
2566                  return t._compareBoundaryPoints(sc, so, sc, so);
2567  
2568              // Check START_TO_END
2569              if (h === 1)
2570                  return t._compareBoundaryPoints(sc, so, ec, eo);
2571  
2572              // Check END_TO_END
2573              if (h === 2)
2574                  return t._compareBoundaryPoints(ec, eo, ec, eo);
2575  
2576              // Check END_TO_START
2577              if (h === 3)
2578                  return t._compareBoundaryPoints(ec, eo, sc, so);
2579          },
2580  
2581          deleteContents : function() {
2582              this._traverse(DELETE);
2583          },
2584  
2585          extractContents : function() {
2586              return this._traverse(EXTRACT);
2587          },
2588  
2589          cloneContents : function() {
2590              return this._traverse(CLONE);
2591          },
2592  
2593          insertNode : function(n) {
2594              var t = this, nn, o;
2595  
2596              // Node is TEXT_NODE or CDATA
2597              if (n.nodeType === 3 || n.nodeType === 4) {
2598                  nn = t.startContainer.splitText(t.startOffset);
2599                  t.startContainer.parentNode.insertBefore(n, nn);
2600              } else {
2601                  // Insert element node
2602                  if (t.startContainer.childNodes.length > 0)
2603                      o = t.startContainer.childNodes[t.startOffset];
2604  
2605                  t.startContainer.insertBefore(n, o);
2606              }
2607          },
2608  
2609          surroundContents : function(n) {
2610              var t = this, f = t.extractContents();
2611  
2612              t.insertNode(n);
2613              n.appendChild(f);
2614              t.selectNode(n);
2615          },
2616  
2617          cloneRange : function() {
2618              var t = this;
2619  
2620              return extend(new Range(t.dom), {
2621                  startContainer : t.startContainer,
2622                  startOffset : t.startOffset,
2623                  endContainer : t.endContainer,
2624                  endOffset : t.endOffset,
2625                  collapsed : t.collapsed,
2626                  commonAncestorContainer : t.commonAncestorContainer
2627              });
2628          },
2629  
2630  /*
2631          detach : function() {
2632              // Not implemented
2633          },
2634  */
2635          // Internal methods
2636  
2637          _isCollapsed : function() {
2638              return (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
2639          },
2640  
2641          _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) {
2642              var c, offsetC, n, cmnRoot, childA, childB;
2643  
2644              // In the first case the boundary-points have the same container. A is before B 
2645              // if its offset is less than the offset of B, A is equal to B if its offset is 
2646              // equal to the offset of B, and A is after B if its offset is greater than the 
2647              // offset of B.
2648              if (containerA == containerB) {
2649                  if (offsetA == offsetB) {
2650                      return 0; // equal
2651                  } else if (offsetA < offsetB) {
2652                      return -1; // before
2653                  } else {
2654                      return 1; // after
2655                  }
2656              }
2657  
2658              // In the second case a child node C of the container of A is an ancestor 
2659              // container of B. In this case, A is before B if the offset of A is less than or 
2660              // equal to the index of the child node C and A is after B otherwise.
2661              c = containerB;
2662              while (c && c.parentNode != containerA) {
2663                  c = c.parentNode;
2664              }
2665              if (c) {
2666                  offsetC = 0;
2667                  n = containerA.firstChild;
2668  
2669                  while (n != c && offsetC < offsetA) {
2670                      offsetC++;
2671                      n = n.nextSibling;
2672                  }
2673  
2674                  if (offsetA <= offsetC) {
2675                      return -1; // before
2676                  } else {
2677                      return 1; // after
2678                  }
2679              }
2680  
2681              // In the third case a child node C of the container of B is an ancestor container 
2682              // of A. In this case, A is before B if the index of the child node C is less than 
2683              // the offset of B and A is after B otherwise.
2684              c = containerA;
2685              while (c && c.parentNode != containerB) {
2686                  c = c.parentNode;
2687              }
2688  
2689              if (c) {
2690                  offsetC = 0;
2691                  n = containerB.firstChild;
2692  
2693                  while (n != c && offsetC < offsetB) {
2694                      offsetC++;
2695                      n = n.nextSibling;
2696                  }
2697  
2698                  if (offsetC < offsetB) {
2699                      return -1; // before
2700                  } else {
2701                      return 1; // after
2702                  }
2703              }
2704  
2705              // In the fourth case, none of three other cases hold: the containers of A and B 
2706              // are siblings or descendants of sibling nodes. In this case, A is before B if 
2707              // the container of A is before the container of B in a pre-order traversal of the
2708              // Ranges' context tree and A is after B otherwise.
2709              cmnRoot = this.dom.findCommonAncestor(containerA, containerB);
2710              childA = containerA;
2711  
2712              while (childA && childA.parentNode != cmnRoot) {
2713                  childA = childA.parentNode;  
2714              }
2715  
2716              if (!childA) {
2717                  childA = cmnRoot;
2718              }
2719  
2720              childB = containerB;
2721              while (childB && childB.parentNode != cmnRoot) {
2722                  childB = childB.parentNode;
2723              }
2724  
2725              if (!childB) {
2726                  childB = cmnRoot;
2727              }
2728  
2729              if (childA == childB) {
2730                  return 0; // equal
2731              }
2732  
2733              n = cmnRoot.firstChild;
2734              while (n) {
2735                  if (n == childA) {
2736                      return -1; // before
2737                  }
2738  
2739                  if (n == childB) {
2740                      return 1; // after
2741                  }
2742  
2743                  n = n.nextSibling;
2744              }
2745          },
2746  
2747          _setEndPoint : function(st, n, o) {
2748              var t = this, ec, sc;
2749  
2750              if (st) {
2751                  t.startContainer = n;
2752                  t.startOffset = o;
2753              } else {
2754                  t.endContainer = n;
2755                  t.endOffset = o;
2756              }
2757  
2758              // If one boundary-point of a Range is set to have a root container 
2759              // other than the current one for the Range, the Range is collapsed to 
2760              // the new position. This enforces the restriction that both boundary-
2761              // points of a Range must have the same root container.
2762              ec = t.endContainer;
2763              while (ec.parentNode)
2764                  ec = ec.parentNode;
2765  
2766              sc = t.startContainer;
2767              while (sc.parentNode)
2768                  sc = sc.parentNode;
2769  
2770              if (sc != ec) {
2771                  t.collapse(st);
2772              } else {
2773                  // The start position of a Range is guaranteed to never be after the 
2774                  // end position. To enforce this restriction, if the start is set to 
2775                  // be at a position after the end, the Range is collapsed to that 
2776                  // position.
2777                  if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0)
2778                      t.collapse(st);
2779              }
2780  
2781              t.collapsed = t._isCollapsed();
2782              t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer);
2783          },
2784  
2785          // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :)
2786  
2787          _traverse : function(how) {
2788              var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
2789  
2790              if (t.startContainer == t.endContainer)
2791                  return t._traverseSameContainer(how);
2792  
2793              for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
2794                  if (p == t.startContainer)
2795                      return t._traverseCommonStartContainer(c, how);
2796  
2797                  ++endContainerDepth;
2798              }
2799  
2800              for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
2801                  if (p == t.endContainer)
2802                      return t._traverseCommonEndContainer(c, how);
2803  
2804                  ++startContainerDepth;
2805              }
2806  
2807              depthDiff = startContainerDepth - endContainerDepth;
2808  
2809              startNode = t.startContainer;
2810              while (depthDiff > 0) {
2811                  startNode = startNode.parentNode;
2812                  depthDiff--;
2813              }
2814  
2815              endNode = t.endContainer;
2816              while (depthDiff < 0) {
2817                  endNode = endNode.parentNode;
2818                  depthDiff++;
2819              }
2820  
2821              // ascend the ancestor hierarchy until we have a common parent.
2822              for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
2823                  startNode = sp;
2824                  endNode = ep;
2825              }
2826  
2827              return t._traverseCommonAncestors(startNode, endNode, how);
2828          },
2829  
2830          _traverseSameContainer : function(how) {
2831              var t = this, frag, s, sub, n, cnt, sibling, xferNode;
2832  
2833              if (how != DELETE)
2834                  frag = t.dom.doc.createDocumentFragment();
2835  
2836              // If selection is empty, just return the fragment
2837              if (t.startOffset == t.endOffset)
2838                  return frag;
2839  
2840              // Text node needs special case handling
2841              if (t.startContainer.nodeType == 3 /* TEXT_NODE */) {
2842                  // get the substring
2843                  s = t.startContainer.nodeValue;
2844                  sub = s.substring(t.startOffset, t.endOffset);
2845  
2846                  // set the original text node to its new value
2847                  if (how != CLONE) {
2848                      t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset);
2849  
2850                      // Nothing is partially selected, so collapse to start point
2851                      t.collapse(true);
2852                  }
2853  
2854                  if (how == DELETE)
2855                      return null;
2856  
2857                  frag.appendChild(t.dom.doc.createTextNode(sub));
2858                  return frag;
2859              }
2860  
2861              // Copy nodes between the start/end offsets.
2862              n = getSelectedNode(t.startContainer, t.startOffset);
2863              cnt = t.endOffset - t.startOffset;
2864  
2865              while (cnt > 0) {
2866                  sibling = n.nextSibling;
2867                  xferNode = t._traverseFullySelected(n, how);
2868  
2869                  if (frag)
2870                      frag.appendChild( xferNode );
2871  
2872                  --cnt;
2873                  n = sibling;
2874              }
2875  
2876              // Nothing is partially selected, so collapse to start point
2877              if (how != CLONE)
2878                  t.collapse(true);
2879  
2880              return frag;
2881          },
2882  
2883          _traverseCommonStartContainer : function(endAncestor, how) {
2884              var t = this, frag, n, endIdx, cnt, sibling, xferNode;
2885  
2886              if (how != DELETE)
2887                  frag = t.dom.doc.createDocumentFragment();
2888  
2889              n = t._traverseRightBoundary(endAncestor, how);
2890  
2891              if (frag)
2892                  frag.appendChild(n);
2893  
2894              endIdx = indexOf(endAncestor, t.startContainer);
2895              cnt = endIdx - t.startOffset;
2896  
2897              if (cnt <= 0) {
2898                  // Collapse to just before the endAncestor, which 
2899                  // is partially selected.
2900                  if (how != CLONE) {
2901                      t.setEndBefore(endAncestor);
2902                      t.collapse(false);
2903                  }
2904  
2905                  return frag;
2906              }
2907  
2908              n = endAncestor.previousSibling;
2909              while (cnt > 0) {
2910                  sibling = n.previousSibling;
2911                  xferNode = t._traverseFullySelected(n, how);
2912  
2913                  if (frag)
2914                      frag.insertBefore(xferNode, frag.firstChild);
2915  
2916                  --cnt;
2917                  n = sibling;
2918              }
2919  
2920              // Collapse to just before the endAncestor, which 
2921              // is partially selected.
2922              if (how != CLONE) {
2923                  t.setEndBefore(endAncestor);
2924                  t.collapse(false);
2925              }
2926  
2927              return frag;
2928          },
2929  
2930          _traverseCommonEndContainer : function(startAncestor, how) {
2931              var t = this, frag, startIdx, n, cnt, sibling, xferNode;
2932  
2933              if (how != DELETE)
2934                  frag = t.dom.doc.createDocumentFragment();
2935  
2936              n = t._traverseLeftBoundary(startAncestor, how);
2937              if (frag)
2938                  frag.appendChild(n);
2939  
2940              startIdx = indexOf(startAncestor, t.endContainer);
2941              ++startIdx;  // Because we already traversed it....
2942  
2943              cnt = t.endOffset - startIdx;
2944              n = startAncestor.nextSibling;
2945              while (cnt > 0) {
2946                  sibling = n.nextSibling;
2947                  xferNode = t._traverseFullySelected(n, how);
2948  
2949                  if (frag)
2950                      frag.appendChild(xferNode);
2951  
2952                  --cnt;
2953                  n = sibling;
2954              }
2955  
2956              if (how != CLONE) {
2957                  t.setStartAfter(startAncestor);
2958                  t.collapse(true);
2959              }
2960  
2961              return frag;
2962          },
2963  
2964          _traverseCommonAncestors : function(startAncestor, endAncestor, how) {
2965              var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
2966  
2967              if (how != DELETE)
2968                  frag = t.dom.doc.createDocumentFragment();
2969  
2970              n = t._traverseLeftBoundary(startAncestor, how);
2971              if (frag)
2972                  frag.appendChild(n);
2973  
2974              commonParent = startAncestor.parentNode;
2975              startOffset = indexOf(startAncestor, commonParent);
2976              endOffset = indexOf(endAncestor, commonParent);
2977              ++startOffset;
2978  
2979              cnt = endOffset - startOffset;
2980              sibling = startAncestor.nextSibling;
2981  
2982              while (cnt > 0) {
2983                  nextSibling = sibling.nextSibling;
2984                  n = t._traverseFullySelected(sibling, how);
2985  
2986                  if (frag)
2987                      frag.appendChild(n);
2988  
2989                  sibling = nextSibling;
2990                  --cnt;
2991              }
2992  
2993              n = t._traverseRightBoundary(endAncestor, how);
2994  
2995              if (frag)
2996                  frag.appendChild(n);
2997  
2998              if (how != CLONE) {
2999                  t.setStartAfter(startAncestor);
3000                  t.collapse(true);
3001              }
3002  
3003              return frag;
3004          },
3005  
3006          _traverseRightBoundary : function(root, how) {
3007              var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent;
3008              var isFullySelected = next != t.endContainer;
3009  
3010              if (next == root)
3011                  return t._traverseNode(next, isFullySelected, false, how);
3012  
3013              parent = next.parentNode;
3014              clonedParent = t._traverseNode(parent, false, false, how);
3015  
3016              while (parent != null) {
3017                  while (next != null) {
3018                      prevSibling = next.previousSibling;
3019                      clonedChild = t._traverseNode(next, isFullySelected, false, how);
3020  
3021                      if (how != DELETE)
3022                          clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
3023  
3024                      isFullySelected = true;
3025                      next = prevSibling;
3026                  }
3027  
3028                  if (parent == root)
3029                      return clonedParent;
3030  
3031                  next = parent.previousSibling;
3032                  parent = parent.parentNode;
3033  
3034                  clonedGrandParent = t._traverseNode(parent, false, false, how);
3035  
3036                  if (how != DELETE)
3037                      clonedGrandParent.appendChild(clonedParent);
3038  
3039                  clonedParent = clonedGrandParent;
3040              }
3041  
3042              // should never occur
3043              return null;
3044          },
3045  
3046          _traverseLeftBoundary : function(root, how) {
3047              var t = this, next = getSelectedNode(t.startContainer, t.startOffset);
3048              var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
3049  
3050              if (next == root)
3051                  return t._traverseNode(next, isFullySelected, true, how);
3052  
3053              parent = next.parentNode;
3054              clonedParent = t._traverseNode(parent, false, true, how);
3055  
3056              while (parent != null) {
3057                  while (next != null) {
3058                      nextSibling = next.nextSibling;
3059                      clonedChild = t._traverseNode(next, isFullySelected, true, how);
3060  
3061                      if (how != DELETE)
3062                          clonedParent.appendChild(clonedChild);
3063  
3064                      isFullySelected = true;
3065                      next = nextSibling;
3066                  }
3067  
3068                  if (parent == root)
3069                      return clonedParent;
3070  
3071                  next = parent.nextSibling;
3072                  parent = parent.parentNode;
3073  
3074                  clonedGrandParent = t._traverseNode(parent, false, true, how);
3075  
3076                  if (how != DELETE)
3077                      clonedGrandParent.appendChild(clonedParent);
3078  
3079                  clonedParent = clonedGrandParent;
3080              }
3081  
3082              // should never occur
3083              return null;
3084          },
3085  
3086          _traverseNode : function(n, isFullySelected, isLeft, how) {
3087              var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode;
3088  
3089              if (isFullySelected)
3090                  return t._traverseFullySelected(n, how);
3091  
3092              if (n.nodeType == 3 /* TEXT_NODE */) {
3093                  txtValue = n.nodeValue;
3094  
3095                  if (isLeft) {
3096                      offset = t.startOffset;
3097                      newNodeValue = txtValue.substring(offset);
3098                      oldNodeValue = txtValue.substring(0, offset);
3099                  } else {
3100                      offset = t.endOffset;
3101                      newNodeValue = txtValue.substring(0, offset);
3102                      oldNodeValue = txtValue.substring(offset);
3103                  }
3104  
3105                  if (how != CLONE)
3106                      n.nodeValue = oldNodeValue;
3107  
3108                  if (how == DELETE)
3109                      return null;
3110  
3111                  newNode = n.cloneNode(false);
3112                  newNode.nodeValue = newNodeValue;
3113  
3114                  return newNode;
3115              }
3116  
3117              if (how == DELETE)
3118                  return null;
3119  
3120              return n.cloneNode(false);
3121          },
3122  
3123          _traverseFullySelected : function(n, how) {
3124              var t = this;
3125  
3126              if (how != DELETE)
3127                  return how == CLONE ? n.cloneNode(true) : n;
3128  
3129              n.parentNode.removeChild(n);
3130              return null;
3131          }
3132      });
3133  
3134      ns.Range = Range;
3135  })(tinymce.dom);
3136  (function() {
3137  	function Selection(selection) {
3138          var t = this, invisibleChar = '\uFEFF', range, lastIERng;
3139  
3140  		function compareRanges(rng1, rng2) {
3141              if (rng1 && rng2) {
3142                  // Both are control ranges and the selected element matches
3143                  if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
3144                      return 1;
3145  
3146                  // Both are text ranges and the range matches
3147                  if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
3148                      return 1;
3149              }
3150  
3151              return 0;
3152          };
3153  
3154  		function getRange() {
3155              var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos, endPos, element, sc, ec, collapsed;
3156  
3157  			function findIndex(element) {
3158                  var nl = element.parentNode.childNodes, i;
3159  
3160                  for (i = nl.length - 1; i >= 0; i--) {
3161                      if (nl[i] == element)
3162                          return i;
3163                  }
3164  
3165                  return -1;
3166              };
3167  
3168  			function findEndPoint(start) {
3169                  var rng = ieRange.duplicate(), parent, i, nl, n, offset = 0, index = 0, pos, tmpRng;
3170  
3171                  // Insert marker character
3172                  rng.collapse(start);
3173                  parent = rng.parentElement();
3174                  rng.pasteHTML(invisibleChar); // Needs to be a pasteHTML instead of .text = since IE has a bug with nodeValue
3175  
3176                  // Find marker character
3177                  nl = parent.childNodes;
3178                  for (i = 0; i < nl.length; i++) {
3179                      n = nl[i];
3180  
3181                      // Calculate node index excluding text node fragmentation
3182                      if (i > 0 && (n.nodeType !== 3 || nl[i - 1].nodeType !== 3))
3183                          index++;
3184  
3185                      // If text node then calculate offset
3186                      if (n.nodeType === 3) {
3187                          // Look for marker
3188                          pos = n.nodeValue.indexOf(invisibleChar);
3189                          if (pos !== -1) {
3190                              offset += pos;
3191                              break;
3192                          }
3193  
3194                          offset += n.nodeValue.length;
3195                      } else
3196                          offset = 0;
3197                  }
3198  
3199                  // Remove marker character
3200                  rng.moveStart('character', -1);
3201                  rng.text = '';
3202  
3203                  return {index : index, offset : offset, parent : parent};
3204              };
3205  
3206              // If selection is outside the current document just return an empty range
3207              element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
3208              if (element.ownerDocument != dom.doc)
3209                  return domRange;
3210  
3211              // Handle control selection or text selection of a image
3212              if (ieRange.item || !element.hasChildNodes()) {
3213                  domRange.setStart(element.parentNode, findIndex(element));
3214                  domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
3215  
3216                  return domRange;
3217              }
3218  
3219              // Check collapsed state
3220              collapsed = selection.isCollapsed();
3221  
3222              // Find start and end pos index and offset
3223              startPos = findEndPoint(true);
3224              endPos = findEndPoint(false);
3225  
3226              // Normalize the elements to avoid fragmented dom
3227              startPos.parent.normalize();
3228              endPos.parent.normalize();
3229  
3230              // Set start container and offset
3231              sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)];
3232  
3233              if (sc.nodeType != 3)
3234                  domRange.setStart(startPos.parent, startPos.index);
3235              else
3236                  domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset);
3237  
3238              // Set end container and offset
3239              ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)];
3240  
3241              if (ec.nodeType != 3) {
3242                  if (!collapsed)
3243                      endPos.index++;
3244  
3245                  domRange.setEnd(endPos.parent, endPos.index);
3246              } else
3247                  domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset);
3248  
3249              // If not collapsed then make sure offsets are valid
3250              if (!collapsed) {
3251                  sc = domRange.startContainer;
3252                  if (sc.nodeType == 1)
3253                      domRange.setStart(sc, Math.min(domRange.startOffset, sc.childNodes.length));
3254  
3255                  ec = domRange.endContainer;
3256                  if (ec.nodeType == 1)
3257                      domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length));
3258              }
3259  
3260              // Restore selection to new range
3261              t.addRange(domRange);
3262  
3263              return domRange;
3264          };
3265  
3266          this.addRange = function(rng) {
3267              var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo;
3268  
3269              // Setup some shorter versions
3270              sc = rng.startContainer;
3271              so = rng.startOffset;
3272              ec = rng.endContainer;
3273              eo = rng.endOffset;
3274              ieRng = body.createTextRange();
3275  
3276              // Find element
3277              sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;
3278              ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;
3279  
3280              // Single element selection
3281              if (sc == ec && sc.nodeType == 1) {
3282                  // Make control selection for some elements
3283                  if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {
3284                      ieRng = body.createControlRange();
3285                      ieRng.addElement(sc);
3286                  } else {
3287                      ieRng = body.createTextRange();
3288  
3289                      // Padd empty elements with invisible character
3290                      if (!sc.hasChildNodes() && sc.canHaveHTML)
3291                          sc.innerHTML = invisibleChar;
3292  
3293                      // Select element contents
3294                      ieRng.moveToElementText(sc);
3295  
3296                      // If it's only containing a padding remove it so the caret remains
3297                      if (sc.innerHTML == invisibleChar) {
3298                          ieRng.collapse(true);
3299                          sc.removeChild(sc.firstChild);
3300                      }
3301                  }
3302  
3303                  if (so == eo)
3304                      ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);
3305  
3306                  ieRng.select();
3307  
3308                  return;
3309              }
3310  
3311  			function getCharPos(container, offset) {
3312                  var nodeVal, rng, pos;
3313  
3314                  if (container.nodeType != 3)
3315                      return -1;
3316  
3317                  nodeVal = container.nodeValue;
3318                  rng = body.createTextRange();
3319  
3320                  // Insert marker at offset position
3321                  container.nodeValue = nodeVal.substring(0, offset) + invisibleChar + nodeVal.substring(offset);
3322  
3323                  // Find char pos of marker and remove it
3324                  rng.moveToElementText(container.parentNode);
3325                  rng.findText(invisibleChar);
3326                  pos = Math.abs(rng.moveStart('character', -0xFFFFF));
3327                  container.nodeValue = nodeVal;
3328  
3329                  return pos;
3330              };
3331  
3332              // Collapsed range
3333              if (rng.collapsed) {
3334                  pos = getCharPos(sc, so);
3335  
3336                  ieRng = body.createTextRange();
3337                  ieRng.move('character', pos);
3338                  ieRng.select();
3339  
3340                  return;
3341              } else {
3342                  // If same text container
3343                  if (sc == ec && sc.nodeType == 3) {
3344                      startPos = getCharPos(sc, so);
3345  
3346                      ieRng.move('character', startPos);
3347                      ieRng.moveEnd('character', eo - so);
3348                      ieRng.select();
3349  
3350                      return;
3351                  }
3352  
3353                  // Get caret positions
3354                  startPos = getCharPos(sc, so);
3355                  endPos = getCharPos(ec, eo);
3356                  ieRng = body.createTextRange();
3357  
3358                  // Move start of range to start character position or start element
3359                  if (startPos == -1) {
3360                      ieRng.moveToElementText(sc);
3361                      startPos = 0;
3362                  } else
3363                      ieRng.move('character', startPos);
3364  
3365                  // Move end of range to end character position or end element
3366                  tmpRng = body.createTextRange();
3367  
3368                  if (endPos == -1)
3369                      tmpRng.moveToElementText(ec);
3370                  else
3371                      tmpRng.move('character', endPos);
3372  
3373                  ieRng.setEndPoint('EndToEnd', tmpRng);
3374                  ieRng.select();
3375  
3376                  return;
3377              }
3378          };
3379  
3380          this.getRangeAt = function() {
3381              // Setup new range if the cache is empty
3382              if (!range || !compareRanges(lastIERng, selection.getRng())) {
3383                  range = getRange();
3384  
3385                  // Store away text range for next call
3386                  lastIERng = selection.getRng();
3387              }
3388  
3389              // Return cached range
3390              return range;
3391          };
3392  
3393          this.destroy = function() {
3394              // Destroy cached range and last IE range to avoid memory leaks
3395              lastIERng = range = null;
3396          };
3397      };
3398  
3399      // Expose the selection object
3400      tinymce.dom.TridentSelection = Selection;
3401  })();
3402  
3403  /*
3404   * Sizzle CSS Selector Engine - v1.0
3405   *  Copyright 2009, The Dojo Foundation
3406   *  Released under the MIT, BSD, and GPL Licenses.
3407   *  More information: http://sizzlejs.com/
3408   */
3409  (function(){
3410  
3411  var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
3412      done = 0,
3413      toString = Object.prototype.toString,
3414      hasDuplicate = false;
3415  
3416  var Sizzle = function(selector, context, results, seed) {
3417      results = results || [];
3418      var origContext = context = context || document;
3419  
3420      if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3421          return [];
3422      }
3423      
3424      if ( !selector || typeof selector !== "string" ) {
3425          return results;
3426      }
3427  
3428      var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
3429      
3430      // Reset the position of the chunker regexp (start from head)
3431      chunker.lastIndex = 0;
3432      
3433      while ( (m = chunker.exec(selector)) !== null ) {
3434          parts.push( m[1] );
3435          
3436          if ( m[2] ) {
3437              extra = RegExp.rightContext;
3438              break;
3439          }
3440      }
3441  
3442      if ( parts.length > 1 && origPOS.exec( selector ) ) {
3443          if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
3444              set = posProcess( parts[0] + parts[1], context );
3445          } else {
3446              set = Expr.relative[ parts[0] ] ?
3447                  [ context ] :
3448                  Sizzle( parts.shift(), context );
3449  
3450              while ( parts.length ) {
3451                  selector = parts.shift();
3452  
3453                  if ( Expr.relative[ selector ] )
3454                      selector += parts.shift();
3455  
3456                  set = posProcess( selector, set );
3457              }
3458          }
3459      } else {
3460          // Take a shortcut and set the context if the root selector is an ID
3461          // (but not if it'll be faster if the inner selector is an ID)
3462          if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
3463                  Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
3464              var ret = Sizzle.find( parts.shift(), context, contextXML );
3465              context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
3466          }
3467  
3468          if ( context ) {
3469              var ret = seed ?
3470                  { expr: parts.pop(), set: makeArray(seed) } :
3471                  Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
3472              set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
3473  
3474              if ( parts.length > 0 ) {
3475                  checkSet = makeArray(set);
3476              } else {
3477                  prune = false;
3478              }
3479  
3480              while ( parts.length ) {
3481                  var cur = parts.pop(), pop = cur;
3482  
3483                  if ( !Expr.relative[ cur ] ) {
3484                      cur = "";
3485                  } else {
3486                      pop = parts.pop();
3487                  }
3488  
3489                  if ( pop == null ) {
3490                      pop = context;
3491                  }
3492  
3493                  Expr.relative[ cur ]( checkSet, pop, contextXML );
3494              }
3495          } else {
3496              checkSet = parts = [];
3497          }
3498      }
3499  
3500      if ( !checkSet ) {
3501          checkSet = set;
3502      }
3503  
3504      if ( !checkSet ) {
3505          throw "Syntax error, unrecognized expression: " + (cur || selector);
3506      }
3507  
3508      if ( toString.call(checkSet) === "[object Array]" ) {
3509          if ( !prune ) {
3510              results.push.apply( results, checkSet );
3511          } else if ( context && context.nodeType === 1 ) {
3512              for ( var i = 0; checkSet[i] != null; i++ ) {
3513                  if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
3514                      results.push( set[i] );
3515                  }
3516              }
3517          } else {
3518              for ( var i = 0; checkSet[i] != null; i++ ) {
3519                  if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
3520                      results.push( set[i] );
3521                  }
3522              }
3523          }
3524      } else {
3525          makeArray( checkSet, results );
3526      }
3527  
3528      if ( extra ) {
3529          Sizzle( extra, origContext, results, seed );
3530          Sizzle.uniqueSort( results );
3531      }
3532  
3533      return results;
3534  };
3535  
3536  Sizzle.uniqueSort = function(results){
3537      if ( sortOrder ) {
3538          hasDuplicate = false;
3539          results.sort(sortOrder);
3540  
3541          if ( hasDuplicate ) {
3542              for ( var i = 1; i < results.length; i++ ) {
3543                  if ( results[i] === results[i-1] ) {
3544                      results.splice(i--, 1);
3545                  }
3546              }
3547          }
3548      }
3549  };
3550  
3551  Sizzle.matches = function(expr, set){
3552      return Sizzle(expr, null, null, set);
3553  };
3554  
3555  Sizzle.find = function(expr, context, isXML){
3556      var set, match;
3557  
3558      if ( !expr ) {
3559          return [];
3560      }
3561  
3562      for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
3563          var type = Expr.order[i], match;
3564          
3565          if ( (match = Expr.match[ type ].exec( expr )) ) {
3566              var left = RegExp.leftContext;
3567  
3568              if ( left.substr( left.length - 1 ) !== "\\" ) {
3569                  match[1] = (match[1] || "").replace(/\\/g, "");
3570                  set = Expr.find[ type ]( match, context, isXML );
3571                  if ( set != null ) {
3572                      expr = expr.replace( Expr.match[ type ], "" );
3573                      break;
3574                  }
3575              }
3576          }
3577      }
3578  
3579      if ( !set ) {
3580          set = context.getElementsByTagName("*");
3581      }
3582  
3583      return {set: set, expr: expr};
3584  };
3585  
3586  Sizzle.filter = function(expr, set, inplace, not){
3587      var old = expr, result = [], curLoop = set, match, anyFound,
3588          isXMLFilter = set && set[0] && isXML(set[0]);
3589  
3590      while ( expr && set.length ) {
3591          for ( var type in Expr.filter ) {
3592              if ( (match = Expr.match[ type ].exec( expr )) != null ) {
3593                  var filter = Expr.filter[ type ], found, item;
3594                  anyFound = false;
3595  
3596                  if ( curLoop == result ) {
3597                      result = [];
3598                  }
3599  
3600                  if ( Expr.preFilter[ type ] ) {
3601                      match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
3602  
3603                      if ( !match ) {
3604                          anyFound = found = true;
3605                      } else if ( match === true ) {
3606                          continue;
3607                      }
3608                  }
3609  
3610                  if ( match ) {
3611                      for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
3612                          if ( item ) {
3613                              found = filter( item, match, i, curLoop );
3614                              var pass = not ^ !!found;
3615  
3616                              if ( inplace && found != null ) {
3617                                  if ( pass ) {
3618                                      anyFound = true;
3619                                  } else {
3620                                      curLoop[i] = false;
3621                                  }
3622                              } else if ( pass ) {
3623                                  result.push( item );
3624                                  anyFound = true;
3625                              }
3626                          }
3627                      }
3628                  }
3629  
3630                  if ( found !== undefined ) {
3631                      if ( !inplace ) {
3632                          curLoop = result;
3633                      }
3634  
3635                      expr = expr.replace( Expr.match[ type ], "" );
3636  
3637                      if ( !anyFound ) {
3638                          return [];
3639                      }
3640  
3641                      break;
3642                  }
3643              }
3644          }
3645  
3646          // Improper expression
3647          if ( expr == old ) {
3648              if ( anyFound == null ) {
3649                  throw "Syntax error, unrecognized expression: " + expr;
3650              } else {
3651                  break;
3652              }
3653          }
3654  
3655          old = expr;
3656      }
3657  
3658      return curLoop;
3659  };
3660  
3661  var Expr = Sizzle.selectors = {
3662      order: [ "ID", "NAME", "TAG" ],
3663      match: {
3664          ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
3665          CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
3666          NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
3667          ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
3668          TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
3669          CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
3670          POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
3671          PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
3672      },
3673      attrMap: {
3674          "class": "className",
3675          "for": "htmlFor"
3676      },
3677      attrHandle: {
3678          href: function(elem){
3679              return elem.getAttribute("href");
3680          }
3681      },
3682      relative: {
3683          "+": function(checkSet, part, isXML){
3684              var isPartStr = typeof part === "string",
3685                  isTag = isPartStr && !/\W/.test(part),
3686                  isPartStrNotTag = isPartStr && !isTag;
3687  
3688              if ( isTag && !isXML ) {
3689                  part = part.toUpperCase();
3690              }
3691  
3692              for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
3693                  if ( (elem = checkSet[i]) ) {
3694                      while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
3695  
3696                      checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
3697                          elem || false :
3698                          elem === part;
3699                  }
3700              }
3701  
3702              if ( isPartStrNotTag ) {
3703                  Sizzle.filter( part, checkSet, true );
3704              }
3705          },
3706          ">": function(checkSet, part, isXML){
3707              var isPartStr = typeof part === "string";
3708  
3709              if ( isPartStr && !/\W/.test(part) ) {
3710                  part = isXML ? part : part.toUpperCase();
3711  
3712                  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3713                      var elem = checkSet[i];
3714                      if ( elem ) {
3715                          var parent = elem.parentNode;
3716                          checkSet[i] = parent.nodeName === part ? parent : false;
3717                      }
3718                  }
3719              } else {
3720                  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3721                      var elem = checkSet[i];
3722                      if ( elem ) {
3723                          checkSet[i] = isPartStr ?
3724                              elem.parentNode :
3725                              elem.parentNode === part;
3726                      }
3727                  }
3728  
3729                  if ( isPartStr ) {
3730                      Sizzle.filter( part, checkSet, true );
3731                  }
3732              }
3733          },
3734          "": function(checkSet, part, isXML){
3735              var doneName = done++, checkFn = dirCheck;
3736  
3737              if ( !part.match(/\W/) ) {
3738                  var nodeCheck = part = isXML ? part : part.toUpperCase();
3739                  checkFn = dirNodeCheck;
3740              }
3741  
3742              checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
3743          },
3744          "~": function(checkSet, part, isXML){
3745              var doneName = done++, checkFn = dirCheck;
3746  
3747              if ( typeof part === "string" && !part.match(/\W/) ) {
3748                  var nodeCheck = part = isXML ? part : part.toUpperCase();
3749                  checkFn = dirNodeCheck;
3750              }
3751  
3752              checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
3753          }
3754      },
3755      find: {
3756          ID: function(match, context, isXML){
3757              if ( typeof context.getElementById !== "undefined" && !isXML ) {
3758                  var m = context.getElementById(match[1]);
3759                  return m ? [m] : [];
3760              }
3761          },
3762          NAME: function(match, context, isXML){
3763              if ( typeof context.getElementsByName !== "undefined" ) {
3764                  var ret = [], results = context.getElementsByName(match[1]);
3765  
3766                  for ( var i = 0, l = results.length; i < l; i++ ) {
3767                      if ( results[i].getAttribute("name") === match[1] ) {
3768                          ret.push( results[i] );
3769                      }
3770                  }
3771  
3772                  return ret.length === 0 ? null : ret;
3773              }
3774          },
3775          TAG: function(match, context){
3776              return context.getElementsByTagName(match[1]);
3777          }
3778      },
3779      preFilter: {
3780          CLASS: function(match, curLoop, inplace, result, not, isXML){
3781              match = " " + match[1].replace(/\\/g, "") + " ";
3782  
3783              if ( isXML ) {
3784                  return match;
3785              }
3786  
3787              for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
3788                  if ( elem ) {
3789                      if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
3790                          if ( !inplace )
3791                              result.push( elem );
3792                      } else if ( inplace ) {
3793                          curLoop[i] = false;
3794                      }
3795                  }
3796              }
3797  
3798              return false;
3799          },
3800          ID: function(match){
3801              return match[1].replace(/\\/g, "");
3802          },
3803          TAG: function(match, curLoop){
3804              for ( var i = 0; curLoop[i] === false; i++ ){}
3805              return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
3806          },
3807          CHILD: function(match){
3808              if ( match[1] == "nth" ) {
3809                  // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
3810                  var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
3811                      match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
3812                      !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
3813  
3814                  // calculate the numbers (first)n+(last) including if they are negative
3815                  match[2] = (test[1] + (test[2] || 1)) - 0;
3816                  match[3] = test[3] - 0;
3817              }
3818  
3819              // TODO: Move to normal caching system
3820              match[0] = done++;
3821  
3822              return match;
3823          },
3824          ATTR: function(match, curLoop, inplace, result, not, isXML){
3825              var name = match[1].replace(/\\/g, "");
3826              
3827              if ( !isXML && Expr.attrMap[name] ) {
3828                  match[1] = Expr.attrMap[name];
3829              }
3830  
3831              if ( match[2] === "~=" ) {
3832                  match[4] = " " + match[4] + " ";
3833              }
3834  
3835              return match;
3836          },
3837          PSEUDO: function(match, curLoop, inplace, result, not){
3838              if ( match[1] === "not" ) {
3839                  // If we're dealing with a complex expression, or a simple one
3840                  if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
3841                      match[3] = Sizzle(match[3], null, null, curLoop);
3842                  } else {
3843                      var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
3844                      if ( !inplace ) {
3845                          result.push.apply( result, ret );
3846                      }
3847                      return false;
3848                  }
3849              } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
3850                  return true;
3851              }
3852              
3853              return match;
3854          },
3855          POS: function(match){
3856              match.unshift( true );
3857              return match;
3858          }
3859      },
3860      filters: {
3861          enabled: function(elem){
3862              return elem.disabled === false && elem.type !== "hidden";
3863          },
3864          disabled: function(elem){
3865              return elem.disabled === true;
3866          },
3867          checked: function(elem){
3868              return elem.checked === true;
3869          },
3870          selected: function(elem){
3871              // Accessing this property makes selected-by-default
3872              // options in Safari work properly
3873              elem.parentNode.selectedIndex;
3874              return elem.selected === true;
3875          },
3876          parent: function(elem){
3877              return !!elem.firstChild;
3878          },
3879          empty: function(elem){
3880              return !elem.firstChild;
3881          },
3882          has: function(elem, i, match){
3883              return !!Sizzle( match[3], elem ).length;
3884          },
3885          header: function(elem){
3886              return /h\d/i.test( elem.nodeName );
3887          },
3888          text: function(elem){
3889              return "text" === elem.type;
3890          },
3891          radio: function(elem){
3892              return "radio" === elem.type;
3893          },
3894          checkbox: function(elem){
3895              return "checkbox" === elem.type;
3896          },
3897          file: function(elem){
3898              return "file" === elem.type;
3899          },
3900          password: function(elem){
3901              return "password" === elem.type;
3902          },
3903          submit: function(elem){
3904              return "submit" === elem.type;
3905          },
3906          image: function(elem){
3907              return "image" === elem.type;
3908          },
3909          reset: function(elem){
3910              return "reset" === elem.type;
3911          },
3912          button: function(elem){
3913              return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
3914          },
3915          input: function(elem){
3916              return /input|select|textarea|button/i.test(elem.nodeName);
3917          }
3918      },
3919      setFilters: {
3920          first: function(elem, i){
3921              return i === 0;
3922          },
3923          last: function(elem, i, match, array){
3924              return i === array.length - 1;
3925          },
3926          even: function(elem, i){
3927              return i % 2 === 0;
3928          },
3929          odd: function(elem, i){
3930              return i % 2 === 1;
3931          },
3932          lt: function(elem, i, match){
3933              return i < match[3] - 0;
3934          },
3935          gt: function(elem, i, match){
3936              return i > match[3] - 0;
3937          },
3938          nth: function(elem, i, match){
3939              return match[3] - 0 == i;
3940          },
3941          eq: function(elem, i, match){
3942              return match[3] - 0 == i;
3943          }
3944      },
3945      filter: {
3946          PSEUDO: function(elem, match, i, array){
3947              var name = match[1], filter = Expr.filters[ name ];
3948  
3949              if ( filter ) {
3950                  return filter( elem, i, match, array );
3951              } else if ( name === "contains" ) {
3952                  return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
3953              } else if ( name === "not" ) {
3954                  var not = match[3];
3955  
3956                  for ( var i = 0, l = not.length; i < l; i++ ) {
3957                      if ( not[i] === elem ) {
3958                          return false;
3959                      }
3960                  }
3961  
3962                  return true;
3963              }
3964          },
3965          CHILD: function(elem, match){
3966              var type = match[1], node = elem;
3967              switch (type) {
3968                  case 'only':
3969                  case 'first':
3970                      while (node = node.previousSibling)  {
3971                          if ( node.nodeType === 1 ) return false;
3972                      }
3973                      if ( type == 'first') return true;
3974                      node = elem;
3975                  case 'last':
3976                      while (node = node.nextSibling)  {
3977                          if ( node.nodeType === 1 ) return false;
3978                      }
3979                      return true;
3980                  case 'nth':
3981                      var first = match[2], last = match[3];
3982  
3983                      if ( first == 1 && last == 0 ) {
3984                          return true;
3985                      }
3986                      
3987                      var doneName = match[0],
3988                          parent = elem.parentNode;
3989      
3990                      if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
3991                          var count = 0;
3992                          for ( node = parent.firstChild; node; node = node.nextSibling ) {
3993                              if ( node.nodeType === 1 ) {
3994                                  node.nodeIndex = ++count;
3995                              }
3996                          } 
3997                          parent.sizcache = doneName;
3998                      }
3999                      
4000                      var diff = elem.nodeIndex - last;
4001                      if ( first == 0 ) {
4002                          return diff == 0;
4003                      } else {
4004                          return ( diff % first == 0 && diff / first >= 0 );
4005                      }
4006              }
4007          },
4008          ID: function(elem, match){
4009              return elem.nodeType === 1 && elem.getAttribute("id") === match;
4010          },
4011          TAG: function(elem, match){
4012              return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4013          },
4014          CLASS: function(elem, match){
4015              return (" " + (elem.className || elem.getAttribute("class")) + " ")
4016                  .indexOf( match ) > -1;
4017          },
4018          ATTR: function(elem, match){
4019              var name = match[1],
4020                  result = Expr.attrHandle[ name ] ?
4021                      Expr.attrHandle[ name ]( elem ) :
4022                      elem[ name ] != null ?
4023                          elem[ name ] :
4024                          elem.getAttribute( name ),
4025                  value = result + "",
4026                  type = match[2],
4027                  check = match[4];
4028  
4029              return result == null ?
4030                  type === "!=" :
4031                  type === "=" ?
4032                  value === check :
4033                  type === "*=" ?
4034                  value.indexOf(check) >= 0 :
4035                  type === "~=" ?
4036                  (" " + value + " ").indexOf(check) >= 0 :
4037                  !check ?
4038                  value && result !== false :
4039                  type === "!=" ?
4040                  value != check :
4041                  type === "^=" ?
4042                  value.indexOf(check) === 0 :
4043                  type === "$=" ?
4044                  value.substr(value.length - check.length) === check :
4045                  type === "|=" ?
4046                  value === check || value.substr(0, check.length + 1) === check + "-" :
4047                  false;
4048          },
4049          POS: function(elem, match, i, array){
4050              var name = match[2], filter = Expr.setFilters[ name ];
4051  
4052              if ( filter ) {
4053                  return filter( elem, i, match, array );
4054              }
4055          }
4056      }
4057  };
4058  
4059  var origPOS = Expr.match.POS;
4060  
4061  for ( var type in Expr.match ) {
4062      Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4063  }
4064  
4065  var makeArray = function(array, results) {
4066      array = Array.prototype.slice.call( array );
4067  
4068      if ( results ) {
4069          results.push.apply( results, array );
4070          return results;
4071      }
4072      
4073      return array;
4074  };
4075  
4076  // Perform a simple check to determine if the browser is capable of
4077  // converting a NodeList to an array using builtin methods.
4078  try {
4079      Array.prototype.slice.call( document.documentElement.childNodes );
4080  
4081  // Provide a fallback method if it does not work
4082  } catch(e){
4083      makeArray = function(array, results) {
4084          var ret = results || [];
4085  
4086          if ( toString.call(array) === "[object Array]" ) {
4087              Array.prototype.push.apply( ret, array );
4088          } else {
4089              if ( typeof array.length === "number" ) {
4090                  for ( var i = 0, l = array.length; i < l; i++ ) {
4091                      ret.push( array[i] );
4092                  }
4093              } else {
4094                  for ( var i = 0; array[i]; i++ ) {
4095                      ret.push( array[i] );
4096                  }
4097              }
4098          }
4099  
4100          return ret;
4101      };
4102  }
4103  
4104  var sortOrder;
4105  
4106  if ( document.documentElement.compareDocumentPosition ) {
4107      sortOrder = function( a, b ) {
4108          var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4109          if ( ret === 0 ) {
4110              hasDuplicate = true;
4111          }
4112          return ret;
4113      };
4114  } else if ( "sourceIndex" in document.documentElement ) {
4115      sortOrder = function( a, b ) {
4116          var ret = a.sourceIndex - b.sourceIndex;
4117          if ( ret === 0 ) {
4118              hasDuplicate = true;
4119          }
4120          return ret;
4121      };
4122  } else if ( document.createRange ) {
4123      sortOrder = function( a, b ) {
4124          var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4125          aRange.setStart(a, 0);
4126          aRange.setEnd(a, 0);
4127          bRange.setStart(b, 0);
4128          bRange.setEnd(b, 0);
4129          var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4130          if ( ret === 0 ) {
4131              hasDuplicate = true;
4132          }
4133          return ret;
4134      };
4135  }
4136  
4137  // Check to see if the browser returns elements by name when
4138  // querying by getElementById (and provide a workaround)
4139  (function(){
4140      // We're going to inject a fake input element with a specified name
4141      var form = document.createElement("div"),
4142          id = "script" + (new Date).getTime();
4143      form.innerHTML = "<a name='" + id + "'/>";
4144  
4145      // Inject it into the root element, check its status, and remove it quickly
4146      var root = document.documentElement;
4147      root.insertBefore( form, root.firstChild );
4148  
4149      // The workaround has to do additional checks after a getElementById
4150      // Which slows things down for other browsers (hence the branching)
4151      if ( !!document.getElementById( id ) ) {
4152          Expr.find.ID = function(match, context, isXML){
4153              if ( typeof context.getElementById !== "undefined" && !isXML ) {
4154                  var m = context.getElementById(match[1]);
4155                  return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4156              }
4157          };
4158  
4159          Expr.filter.ID = function(elem, match){
4160              var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4161              return elem.nodeType === 1 && node && node.nodeValue === match;
4162          };
4163      }
4164  
4165      root.removeChild( form );
4166  })();
4167  
4168  (function(){
4169      // Check to see if the browser returns only elements
4170      // when doing getElementsByTagName("*")
4171  
4172      // Create a fake element
4173      var div = document.createElement("div");
4174      div.appendChild( document.createComment("") );
4175  
4176      // Make sure no comments are found
4177      if ( div.getElementsByTagName("*").length > 0 ) {
4178          Expr.find.TAG = function(match, context){
4179              var results = context.getElementsByTagName(match[1]);
4180  
4181              // Filter out possible comments
4182              if ( match[1] === "*" ) {
4183                  var tmp = [];
4184  
4185                  for ( var i = 0; results[i]; i++ ) {
4186                      if ( results[i].nodeType === 1 ) {
4187                          tmp.push( results[i] );
4188                      }
4189                  }
4190  
4191                  results = tmp;
4192              }
4193  
4194              return results;
4195          };
4196      }
4197  
4198      // Check to see if an attribute returns normalized href attributes
4199      div.innerHTML = "<a href='#'></a>";
4200      if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4201              div.firstChild.getAttribute("href") !== "#" ) {
4202          Expr.attrHandle.href = function(elem){
4203              return elem.getAttribute("href", 2);
4204          };
4205      }
4206  })();
4207  
4208  if ( document.querySelectorAll ) (function(){
4209      var oldSizzle = Sizzle, div = document.createElement("div");
4210      div.innerHTML = "<p class='TEST'></p>";
4211  
4212      // Safari can't handle uppercase or unicode characters when
4213      // in quirks mode.
4214      if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4215          return;
4216      }
4217      
4218      Sizzle = function(query, context, extra, seed){
4219          context = context || document;
4220  
4221          // Only use querySelectorAll on non-XML documents
4222          // (ID selectors don't work in non-HTML documents)
4223          if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4224              try {
4225                  return makeArray( context.querySelectorAll(query), extra );
4226              } catch(e){}
4227          }
4228          
4229          return oldSizzle(query, context, extra, seed);
4230      };
4231  
4232      for ( var prop in oldSizzle ) {
4233          Sizzle[ prop ] = oldSizzle[ prop ];
4234      }
4235  })();
4236  
4237  if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4238      var div = document.createElement("div");
4239      div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4240  
4241      // Opera can't find a second classname (in 9.6)
4242      if ( div.getElementsByClassName("e").length === 0 )
4243          return;
4244  
4245      // Safari caches class attributes, doesn't catch changes (in 3.2)
4246      div.lastChild.className = "e";
4247  
4248      if ( div.getElementsByClassName("e").length === 1 )
4249          return;
4250  
4251      Expr.order.splice(1, 0, "CLASS");
4252      Expr.find.CLASS = function(match, context, isXML) {
4253          if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4254              return context.getElementsByClassName(match[1]);
4255          }
4256      };
4257  })();
4258  
4259  function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4260      var sibDir = dir == "previousSibling" && !isXML;
4261      for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4262          var elem = checkSet[i];
4263          if ( elem ) {
4264              if ( sibDir && elem.nodeType === 1 ){
4265                  elem.sizcache = doneName;
4266                  elem.sizset = i;
4267              }
4268              elem = elem[dir];
4269              var match = false;
4270  
4271              while ( elem ) {
4272                  if ( elem.sizcache === doneName ) {
4273                      match = checkSet[elem.sizset];
4274                      break;
4275                  }
4276  
4277                  if ( elem.nodeType === 1 && !isXML ){
4278                      elem.sizcache = doneName;
4279                      elem.sizset = i;
4280                  }
4281  
4282                  if ( elem.nodeName === cur ) {
4283                      match = elem;
4284                      break;
4285                  }
4286  
4287                  elem = elem[dir];
4288              }
4289  
4290              checkSet[i] = match;
4291          }
4292      }
4293  }
4294  
4295  function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4296      var sibDir = dir == "previousSibling" && !isXML;
4297      for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4298          var elem = checkSet[i];
4299          if ( elem ) {
4300              if ( sibDir && elem.nodeType === 1 ) {
4301                  elem.sizcache = doneName;
4302                  elem.sizset = i;
4303              }
4304              elem = elem[dir];
4305              var match = false;
4306  
4307              while ( elem ) {
4308                  if ( elem.sizcache === doneName ) {
4309                      match = checkSet[elem.sizset];
4310                      break;
4311                  }
4312  
4313                  if ( elem.nodeType === 1 ) {
4314                      if ( !isXML ) {
4315                          elem.sizcache = doneName;
4316                          elem.sizset = i;
4317                      }
4318                      if ( typeof cur !== "string" ) {
4319                          if ( elem === cur ) {
4320                              match = true;
4321                              break;
4322                          }
4323  
4324                      } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4325                          match = elem;
4326                          break;
4327                      }
4328                  }
4329  
4330                  elem = elem[dir];
4331              }
4332  
4333              checkSet[i] = match;
4334          }
4335      }
4336  }
4337  
4338  var contains = document.compareDocumentPosition ?  function(a, b){
4339      return a.compareDocumentPosition(b) & 16;
4340  } : function(a, b){
4341      return a !== b && (a.contains ? a.contains(b) : true);
4342  };
4343  
4344  var isXML = function(elem){
4345      return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4346          !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4347  };
4348  
4349  var posProcess = function(selector, context){
4350      var tmpSet = [], later = "", match,
4351          root = context.nodeType ? [context] : context;
4352  
4353      // Position selectors must be done after the filter
4354      // And so must :not(positional) so we move all PSEUDOs to the end
4355      while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4356          later += match[0];
4357          selector = selector.replace( Expr.match.PSEUDO, "" );
4358      }
4359  
4360      selector = Expr.relative[selector] ? selector + "*" : selector;
4361  
4362      for ( var i = 0, l = root.length; i < l; i++ ) {
4363          Sizzle( selector, root[i], tmpSet );
4364      }
4365  
4366      return Sizzle.filter( later, tmpSet );
4367  };
4368  
4369  // EXPOSE
4370  
4371  window.tinymce.dom.Sizzle = Sizzle;
4372  
4373  })();
4374  
4375  (function(tinymce) {
4376      // Shorten names
4377      var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
4378  
4379      tinymce.create('tinymce.dom.EventUtils', {
4380          EventUtils : function() {
4381              this.inits = [];
4382              this.events = [];
4383          },
4384  
4385          add : function(o, n, f, s) {
4386              var cb, t = this, el = t.events, r;
4387  
4388              if (n instanceof Array) {
4389                  r = [];
4390  
4391                  each(n, function(n) {
4392                      r.push(t.add(o, n, f, s));
4393                  });
4394  
4395                  return r;
4396              }
4397  
4398              // Handle array
4399              if (o && o.hasOwnProperty && o instanceof Array) {
4400                  r = [];
4401  
4402                  each(o, function(o) {
4403                      o = DOM.get(o);
4404                      r.push(t.add(o, n, f, s));
4405                  });
4406  
4407                  return r;
4408              }
4409  
4410              o = DOM.get(o);
4411  
4412              if (!o)
4413                  return;
4414  
4415              // Setup event callback
4416              cb = function(e) {
4417                  // Is all events disabled
4418                  if (t.disabled)
4419                      return;
4420  
4421                  e = e || window.event;
4422  
4423                  // Patch in target, preventDefault and stopPropagation in IE it's W3C valid
4424                  if (e && isIE) {
4425                      if (!e.target)
4426                          e.target = e.srcElement;
4427  
4428                      // Patch in preventDefault, stopPropagation methods for W3C compatibility
4429                      tinymce.extend(e, t._stoppers);
4430                  }
4431  
4432                  if (!s)
4433                      return f(e);
4434  
4435                  return f.call(s, e);
4436              };
4437  
4438              if (n == 'unload') {
4439                  tinymce.unloads.unshift({func : cb});
4440                  return cb;
4441              }
4442  
4443              if (n == 'init') {
4444                  if (t.domLoaded)
4445                      cb();
4446                  else
4447                      t.inits.push(cb);
4448  
4449                  return cb;
4450              }
4451  
4452              // Store away listener reference
4453              el.push({
4454                  obj : o,
4455                  name : n,
4456                  func : f,
4457                  cfunc : cb,
4458                  scope : s
4459              });
4460  
4461              t._add(o, n, cb);
4462  
4463              return f;
4464          },
4465  
4466          remove : function(o, n, f) {
4467              var t = this, a = t.events, s = false, r;
4468  
4469              // Handle array
4470              if (o && o.hasOwnProperty && o instanceof Array) {
4471                  r = [];
4472  
4473                  each(o, function(o) {
4474                      o = DOM.get(o);
4475                      r.push(t.remove(o, n, f));
4476                  });
4477  
4478                  return r;
4479              }
4480  
4481              o = DOM.get(o);
4482  
4483              each(a, function(e, i) {
4484                  if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
4485                      a.splice(i, 1);
4486                      t._remove(o, n, e.cfunc);
4487                      s = true;
4488                      return false;
4489                  }
4490              });
4491  
4492              return s;
4493          },
4494  
4495          clear : function(o) {
4496              var t = this, a = t.events, i, e;
4497  
4498              if (o) {
4499                  o = DOM.get(o);
4500  
4501                  for (i = a.length - 1; i >= 0; i--) {
4502                      e = a[i];
4503  
4504                      if (e.obj === o) {
4505                          t._remove(e.obj, e.name, e.cfunc);
4506                          e.obj = e.cfunc = null;
4507                          a.splice(i, 1);
4508                      }
4509                  }
4510              }
4511          },
4512  
4513          cancel : function(e) {
4514              if (!e)
4515                  return false;
4516  
4517              this.stop(e);
4518  
4519              return this.prevent(e);
4520          },
4521  
4522          stop : function(e) {
4523              if (e.stopPropagation)
4524                  e.stopPropagation();
4525              else
4526                  e.cancelBubble = true;
4527  
4528              return false;
4529          },
4530  
4531          prevent : function(e) {
4532              if (e.preventDefault)
4533                  e.preventDefault();
4534              else
4535                  e.returnValue = false;
4536  
4537              return false;
4538          },
4539  
4540          destroy : function() {
4541              var t = this;
4542  
4543              each(t.events, function(e, i) {
4544                  t._remove(e.obj, e.name, e.cfunc);
4545                  e.obj = e.cfunc = null;
4546              });
4547  
4548              t.events = [];
4549              t = null;
4550          },
4551  
4552          _add : function(o, n, f) {
4553              if (o.attachEvent)
4554                  o.attachEvent('on' + n, f);
4555              else if (o.addEventListener)
4556                  o.addEventListener(n, f, false);
4557              else
4558                  o['on' + n] = f;
4559          },
4560  
4561          _remove : function(o, n, f) {
4562              if (o) {
4563                  try {
4564                      if (o.detachEvent)
4565                          o.detachEvent('on' + n, f);
4566                      else if (o.removeEventListener)
4567                          o.removeEventListener(n, f, false);
4568                      else
4569                          o['on' + n] = null;
4570                  } catch (ex) {
4571                      // Might fail with permission denined on IE so we just ignore that
4572                  }
4573              }
4574          },
4575  
4576          _pageInit : function(win) {
4577              var t = this;
4578  
4579              // Keep it from running more than once
4580              if (t.domLoaded)
4581                  return;
4582  
4583              t.domLoaded = true;
4584  
4585              each(t.inits, function(c) {
4586                  c();
4587              });
4588  
4589              t.inits = [];
4590          },
4591  
4592          _wait : function(win) {
4593              var t = this, doc = win.document;
4594  
4595              // No need since the document is already loaded
4596              if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {
4597                  t.domLoaded = 1;
4598                  return;
4599              }
4600  
4601              // Use IE method
4602              if (doc.attachEvent) {
4603                  doc.attachEvent("onreadystatechange", function() {
4604                      if (doc.readyState === "complete") {
4605                          doc.detachEvent("onreadystatechange", arguments.callee);
4606                          t._pageInit(win);
4607                      }
4608                  });
4609  
4610                  if (doc.documentElement.doScroll && win == win.top) {
4611                      (function() {
4612                          if (t.domLoaded)
4613                              return;
4614  
4615                          try {
4616                              // If IE is used, use the trick by Diego Perini
4617                              // http://javascript.nwbox.com/IEContentLoaded/
4618                              doc.documentElement.doScroll("left");
4619                          } catch (ex) {
4620                              setTimeout(arguments.callee, 0);
4621                              return;
4622                          }
4623  
4624                          t._pageInit(win);
4625                      })();
4626                  }
4627              } else if (doc.addEventListener) {
4628                  t._add(win, 'DOMContentLoaded', function() {
4629                      t._pageInit(win);
4630                  });
4631              }
4632  
4633              t._add(win, 'load', function() {
4634                  t._pageInit(win);
4635              });
4636          },
4637  
4638          _stoppers : {
4639              preventDefault :  function() {
4640                  this.returnValue = false;
4641              },
4642  
4643              stopPropagation : function() {
4644                  this.cancelBubble = true;
4645              }
4646          }
4647      });
4648  
4649      Event = tinymce.dom.Event = new tinymce.dom.EventUtils();
4650  
4651      // Dispatch DOM content loaded event for IE and Safari
4652      Event._wait(window);
4653  
4654      tinymce.addUnload(function() {
4655          Event.destroy();
4656      });
4657  })(tinymce);
4658  (function(tinymce) {
4659      var each = tinymce.each;
4660  
4661      tinymce.create('tinymce.dom.Element', {
4662          Element : function(id, s) {
4663              var t = this, dom, el;
4664  
4665              s = s || {};
4666              t.id = id;
4667              t.dom = dom = s.dom || tinymce.DOM;
4668              t.settings = s;
4669  
4670              // Only IE leaks DOM references, this is a lot faster
4671              if (!tinymce.isIE)
4672                  el = t.dom.get(t.id);
4673  
4674              each([
4675                  'getPos',
4676                  'getRect',
4677                  'getParent',
4678                  'add',
4679                  'setStyle',
4680                  'getStyle',
4681                  'setStyles',
4682                  'setAttrib',
4683                  'setAttribs',
4684                  'getAttrib',
4685                  'addClass',
4686                  'removeClass',
4687                  'hasClass',
4688                  'getOuterHTML',
4689                  'setOuterHTML',
4690                  'remove',
4691                  'show',
4692                  'hide',
4693                  'isHidden',
4694                  'setHTML',
4695                  'get'
4696              ], function(k) {
4697                  t[k] = function() {
4698                      var a = [id], i;
4699  
4700                      for (i = 0; i < arguments.length; i++)
4701                          a.push(arguments[i]);
4702  
4703                      a = dom[k].apply(dom, a);
4704                      t.update(k);
4705  
4706                      return a;
4707                  };
4708              });
4709          },
4710  
4711          on : function(n, f, s) {
4712              return tinymce.dom.Event.add(this.id, n, f, s);
4713          },
4714  
4715          getXY : function() {
4716              return {
4717                  x : parseInt(this.getStyle('left')),
4718                  y : parseInt(this.getStyle('top'))
4719              };
4720          },
4721  
4722          getSize : function() {
4723              var n = this.dom.get(this.id);
4724  
4725              return {
4726                  w : parseInt(this.getStyle('width') || n.clientWidth),
4727                  h : parseInt(this.getStyle('height') || n.clientHeight)
4728              };
4729          },
4730  
4731          moveTo : function(x, y) {
4732              this.setStyles({left : x, top : y});
4733          },
4734  
4735          moveBy : function(x, y) {
4736              var p = this.getXY();
4737  
4738              this.moveTo(p.x + x, p.y + y);
4739          },
4740  
4741          resizeTo : function(w, h) {
4742              this.setStyles({width : w, height : h});
4743          },
4744  
4745          resizeBy : function(w, h) {
4746              var s = this.getSize();
4747  
4748              this.resizeTo(s.w + w, s.h + h);
4749          },
4750  
4751          update : function(k) {
4752              var t = this, b, dom = t.dom;
4753  
4754              if (tinymce.isIE6 && t.settings.blocker) {
4755                  k = k || '';
4756  
4757                  // Ignore getters
4758                  if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
4759                      return;
4760  
4761                  // Remove blocker on remove
4762                  if (k == 'remove') {
4763                      dom.remove(t.blocker);
4764                      return;
4765                  }
4766  
4767                  if (!t.blocker) {
4768                      t.blocker = dom.uniqueId();
4769                      b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
4770                      dom.setStyle(b, 'opacity', 0);
4771                  } else
4772                      b = dom.get(t.blocker);
4773  
4774                  dom.setStyle(b, 'left', t.getStyle('left', 1));
4775                  dom.setStyle(b, 'top', t.getStyle('top', 1));
4776                  dom.setStyle(b, 'width', t.getStyle('width', 1));
4777                  dom.setStyle(b, 'height', t.getStyle('height', 1));
4778                  dom.setStyle(b, 'display', t.getStyle('display', 1));
4779                  dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);
4780              }
4781          }
4782      });
4783  })(tinymce);
4784  (function(tinymce) {
4785  	function trimNl(s) {
4786          return s.replace(/[\n\r]+/g, '');
4787      };
4788  
4789      // Shorten names
4790      var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
4791  
4792      tinymce.create('tinymce.dom.Selection', {
4793          Selection : function(dom, win, serializer) {
4794              var t = this;
4795  
4796              t.dom = dom;
4797              t.win = win;
4798              t.serializer = serializer;
4799  
4800              // Add events
4801              each([
4802                  'onBeforeSetContent',
4803                  'onBeforeGetContent',
4804                  'onSetContent',
4805                  'onGetContent'
4806              ], function(e) {
4807                  t[e] = new tinymce.util.Dispatcher(t);
4808              });
4809  
4810              // No W3C Range support
4811              if (!t.win.getSelection)
4812                  t.tridentSel = new tinymce.dom.TridentSelection(t);
4813  
4814              // Prevent leaks
4815              tinymce.addUnload(t.destroy, t);
4816          },
4817  
4818          getContent : function(s) {
4819              var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;
4820  
4821              s = s || {};
4822              wb = wa = '';
4823              s.get = true;
4824              s.format = s.format || 'html';
4825              t.onBeforeGetContent.dispatch(t, s);
4826  
4827              if (s.format == 'text')
4828                  return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));
4829  
4830              if (r.cloneContents) {
4831                  n = r.cloneContents();
4832  
4833                  if (n)
4834                      e.appendChild(n);
4835              } else if (is(r.item) || is(r.htmlText))
4836                  e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
4837              else
4838                  e.innerHTML = r.toString();
4839  
4840              // Keep whitespace before and after
4841              if (/^\s/.test(e.innerHTML))
4842                  wb = ' ';
4843  
4844              if (/\s+$/.test(e.innerHTML))
4845                  wa = ' ';
4846  
4847              s.getInner = true;
4848  
4849              s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
4850              t.onGetContent.dispatch(t, s);
4851  
4852              return s.content;
4853          },
4854  
4855          setContent : function(h, s) {
4856              var t = this, r = t.getRng(), c, d = t.win.document;
4857  
4858              s = s || {format : 'html'};
4859              s.set = true;
4860              h = s.content = t.dom.processHTML(h);
4861  
4862              // Dispatch before set content event
4863              t.onBeforeSetContent.dispatch(t, s);
4864              h = s.content;
4865  
4866              if (r.insertNode) {
4867                  // Make caret marker since insertNode places the caret in the beginning of text after insert
4868                  h += '<span id="__caret">_</span>';
4869  
4870                  // Delete and insert new node
4871                  r.deleteContents();
4872                  r.insertNode(t.getRng().createContextualFragment(h));
4873  
4874                  // Move to caret marker
4875                  c = t.dom.get('__caret');
4876  
4877                  // Make sure we wrap it compleatly, Opera fails with a simple select call
4878                  r = d.createRange();
4879                  r.setStartBefore(c);
4880                  r.setEndAfter(c);
4881                  t.setRng(r);
4882  
4883                  // Delete the marker, and hopefully the caret gets placed in the right location
4884                  // Removed this since it seems to remove &nbsp; in FF and simply deleting it
4885                  // doesn't seem to affect the caret position in any browser
4886                  //d.execCommand('Delete', false, null);
4887  
4888                  // Remove the caret position
4889                  t.dom.remove('__caret');
4890              } else {
4891                  if (r.item) {
4892                      // Delete content and get caret text selection
4893                      d.execCommand('Delete', false, null);
4894                      r = t.getRng();
4895                  }
4896  
4897                  r.pasteHTML(h);
4898              }
4899  
4900              // Dispatch set content event
4901              t.onSetContent.dispatch(t, s);
4902          },
4903  
4904          getStart : function() {
4905              var t = this, r = t.getRng(), e;
4906  
4907              if (isIE) {
4908                  if (r.item)
4909                      return r.item(0);
4910  
4911                  r = r.duplicate();
4912                  r.collapse(1);
4913                  e = r.parentElement();
4914  
4915                  if (e && e.nodeName == 'BODY')
4916                      return e.firstChild;
4917  
4918                  return e;
4919              } else {
4920                  e = r.startContainer;
4921  
4922                  if (e.nodeName == 'BODY')
4923                      return e.firstChild;
4924  
4925                  return t.dom.getParent(e, '*');
4926              }
4927          },
4928  
4929          getEnd : function() {
4930              var t = this, r = t.getRng(), e;
4931  
4932              if (isIE) {
4933                  if (r.item)
4934                      return r.item(0);
4935  
4936                  r = r.duplicate();
4937                  r.collapse(0);
4938                  e = r.parentElement();
4939  
4940                  if (e && e.nodeName == 'BODY')
4941                      return e.lastChild;
4942  
4943                  return e;
4944              } else {
4945                  e = r.endContainer;
4946  
4947                  if (e.nodeName == 'BODY')
4948                      return e.lastChild;
4949  
4950                  return t.dom.getParent(e, '*');
4951              }
4952          },
4953  
4954          getBookmark : function(si) {
4955              var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv;
4956              sx = vp.x;
4957              sy = vp.y;
4958  
4959              // Simple bookmark fast but not as persistent
4960              if (si)
4961                  return {rng : r, scrollX : sx, scrollY : sy};
4962  
4963              // Handle IE
4964              if (isIE) {
4965                  // Control selection
4966                  if (r.item) {
4967                      e = r.item(0);
4968  
4969                      each(t.dom.select(e.nodeName), function(n, i) {
4970                          if (e == n) {
4971                              sp = i;
4972                              return false;
4973                          }
4974                      });
4975  
4976                      return {
4977                          tag : e.nodeName,
4978                          index : sp,
4979                          scrollX : sx,
4980                          scrollY : sy
4981                      };
4982                  }
4983  
4984                  // Text selection
4985                  tr = t.dom.doc.body.createTextRange();
4986                  tr.moveToElementText(ro);
4987                  tr.collapse(true);
4988                  bp = Math.abs(tr.move('character', c));
4989  
4990                  tr = r.duplicate();
4991                  tr.collapse(true);
4992                  sp = Math.abs(tr.move('character', c));
4993  
4994                  tr = r.duplicate();
4995                  tr.collapse(false);
4996                  le = Math.abs(tr.move('character', c)) - sp;
4997  
4998                  return {
4999                      start : sp - bp,
5000                      length : le,
5001                      scrollX : sx,
5002                      scrollY : sy
5003                  };
5004              }
5005  
5006              // Handle W3C
5007              e = t.getNode();
5008              s = t.getSel();
5009  
5010              if (!s)
5011                  return null;
5012  
5013              // Image selection
5014              if (e && e.nodeName == 'IMG') {
5015                  return {
5016                      scrollX : sx,
5017                      scrollY : sy
5018                  };
5019              }
5020  
5021              // Text selection
5022  
5023  			function getPos(r, sn, en) {
5024                  var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
5025  
5026                  while ((n = w.nextNode()) != null) {
5027                      if (n == sn)
5028                          d.start = p;
5029  
5030                      if (n == en) {
5031                          d.end = p;
5032                          return d;
5033                      }
5034  
5035                      p += trimNl(n.nodeValue || '').length;
5036                  }
5037  
5038                  return null;
5039              };
5040  
5041              // Caret or selection
5042              if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
5043                  e = getPos(ro, s.anchorNode, s.focusNode);
5044  
5045                  if (!e)
5046                      return {scrollX : sx, scrollY : sy};
5047  
5048                  // Count whitespace before
5049                  trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
5050  
5051                  return {
5052                      start : Math.max(e.start + s.anchorOffset - wb, 0),
5053                      end : Math.max(e.end + s.focusOffset - wb, 0),
5054                      scrollX : sx,
5055                      scrollY : sy,
5056                      beg : s.anchorOffset - wb == 0
5057                  };
5058              } else {
5059                  e = getPos(ro, r.startContainer, r.endContainer);
5060  
5061                  // Count whitespace before start and end container
5062                  //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
5063                  //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});
5064  
5065                  if (!e)
5066                      return {scrollX : sx, scrollY : sy};
5067  
5068                  return {
5069                      start : Math.max(e.start + r.startOffset - wb, 0),
5070                      end : Math.max(e.end + r.endOffset - wa, 0),
5071                      scrollX : sx,
5072                      scrollY : sy,
5073                      beg : r.startOffset - wb == 0
5074                  };
5075              }
5076          },
5077  
5078          moveToBookmark : function(b) {
5079              var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;
5080  
5081  			function getPos(r, sp, ep) {
5082                  var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;
5083  
5084                  while ((n = w.nextNode()) != null) {
5085                      wa = wb = 0;
5086  
5087                      nv = n.nodeValue || '';
5088                      //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
5089                      //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});
5090  
5091                      nvl = trimNl(nv).length;
5092                      p += nvl;
5093  
5094                      if (p >= sp && !d.startNode) {
5095                          o = sp - (p - nvl);
5096  
5097                          // Fix for odd quirk in FF
5098                          if (b.beg && o >= nvl)
5099                              continue;
5100  
5101                          d.startNode = n;
5102                          d.startOffset = o + wb;
5103                      }
5104  
5105                      if (p >= ep) {
5106                          d.endNode = n;
5107                          d.endOffset = ep - (p - nvl) + wb;
5108                          return d;
5109                      }
5110                  }
5111  
5112                  return null;
5113              };
5114  
5115              if (!b)
5116                  return false;
5117  
5118              t.win.scrollTo(b.scrollX, b.scrollY);
5119  
5120              // Handle explorer
5121              if (isIE) {
5122                  // Handle simple
5123                  if (r = b.rng) {
5124                      try {
5125                          r.select();
5126                      } catch (ex) {
5127                          // Ignore
5128                      }
5129  
5130                      return true;
5131                  }
5132  
5133                  t.win.focus();
5134  
5135                  // Handle control bookmark
5136                  if (b.tag) {
5137                      r = ro.createControlRange();
5138  
5139                      each(t.dom.select(b.tag), function(n, i) {
5140                          if (i == b.index)
5141                              r.addElement(n);
5142                      });
5143                  } else {
5144                      // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
5145                      try {
5146                          // Incorrect bookmark
5147                          if (b.start < 0)
5148                              return true;
5149  
5150                          r = s.createRange();
5151                          r.moveToElementText(ro);
5152                          r.collapse(true);
5153                          r.moveStart('character', b.start);
5154                          r.moveEnd('character', b.length);
5155                      } catch (ex2) {
5156                          return true;
5157                      }
5158                  }
5159  
5160                  try {
5161                      r.select();
5162                  } catch (ex) {
5163                      // Needed for some odd IE bug #1843306
5164                  }
5165  
5166                  return true;
5167              }
5168  
5169              // Handle W3C
5170              if (!s)
5171                  return false;
5172  
5173              // Handle simple
5174              if (b.rng) {
5175                  s.removeAllRanges();
5176                  s.addRange(b.rng);
5177              } else {
5178                  if (is(b.start) && is(b.end)) {
5179                      try {
5180                          sd = getPos(ro, b.start, b.end);
5181  
5182                          if (sd) {
5183                              r = t.dom.doc.createRange();
5184                              r.setStart(sd.startNode, sd.startOffset);
5185                              r.setEnd(sd.endNode, sd.endOffset);
5186                              s.removeAllRanges();
5187                              s.addRange(r);
5188                          }
5189  
5190                          if (!tinymce.isOpera)
5191                              t.win.focus();
5192                      } catch (ex) {
5193                          // Ignore
5194                      }
5195                  }
5196              }
5197          },
5198  
5199          select : function(n, c) {
5200              var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;
5201  
5202  			function find(n, start) {
5203                  var walker, o;
5204  
5205                  if (n) {
5206                      walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
5207  
5208                      // Find first/last non empty text node
5209                      while (n = walker.nextNode()) {
5210                          o = n;
5211  
5212                          if (tinymce.trim(n.nodeValue).length != 0) {
5213                              if (start)
5214                                  return n;
5215                              else
5216                                  o = n;
5217                          }
5218                      }
5219                  }
5220  
5221                  return o;
5222              };
5223  
5224              if (isIE) {
5225                  try {
5226                      b = d.body;
5227  
5228                      if (/^(IMG|TABLE)$/.test(n.nodeName)) {
5229                          r = b.createControlRange();
5230                          r.addElement(n);
5231                      } else {
5232                          r = b.createTextRange();
5233                          r.moveToElementText(n);
5234                      }
5235  
5236                      r.select();
5237                  } catch (ex) {
5238                      // Throws illigal agrument in IE some times
5239                  }
5240              } else {
5241                  if (c) {
5242                      fn = find(n, 1) || t.dom.select('br:first', n)[0];
5243                      ln = find(n, 0) || t.dom.select('br:last', n)[0];
5244  
5245                      if (fn && ln) {
5246                          r = d.createRange();
5247  
5248                          if (fn.nodeName == 'BR')
5249                              r.setStartBefore(fn);
5250                          else
5251                              r.setStart(fn, 0);
5252  
5253                          if (ln.nodeName == 'BR')
5254                              r.setEndBefore(ln);
5255                          else
5256                              r.setEnd(ln, ln.nodeValue.length);
5257                      } else
5258                          r.selectNode(n);
5259                  } else
5260                      r.selectNode(n);
5261  
5262                  t.setRng(r);
5263              }
5264  
5265              return n;
5266          },
5267  
5268          isCollapsed : function() {
5269              var t = this, r = t.getRng(), s = t.getSel();
5270  
5271              if (!r || r.item)
5272                  return false;
5273  
5274              return !s || r.boundingWidth == 0 || r.collapsed;
5275          },
5276  
5277          collapse : function(b) {
5278              var t = this, r = t.getRng(), n;
5279  
5280              // Control range on IE
5281              if (r.item) {
5282                  n = r.item(0);
5283                  r = this.win.document.body.createTextRange();
5284                  r.moveToElementText(n);
5285              }
5286  
5287              r.collapse(!!b);
5288              t.setRng(r);
5289          },
5290  
5291          getSel : function() {
5292              var t = this, w = this.win;
5293  
5294              return w.getSelection ? w.getSelection() : w.document.selection;
5295          },
5296  
5297          getRng : function(w3c) {
5298              var t = this, s, r;
5299  
5300              // Found tridentSel object then we need to use that one
5301              if (w3c && t.tridentSel)
5302                  return t.tridentSel.getRangeAt(0);
5303  
5304              try {
5305                  if (s = t.getSel())
5306                      r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
5307              } catch (ex) {
5308                  // IE throws unspecified error here if TinyMCE is placed in a frame/iframe
5309              }
5310  
5311              // No range found then create an empty one
5312              // This can occur when the editor is placed in a hidden container element on Gecko
5313              // Or on IE when there was an exception
5314              if (!r)
5315                  r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();
5316  
5317              return r;
5318          },
5319  
5320          setRng : function(r) {
5321              var s, t = this;
5322  
5323              if (!t.tridentSel) {
5324                  s = t.getSel();
5325  
5326                  if (s) {
5327                      s.removeAllRanges();
5328                      s.addRange(r);
5329                  }
5330              } else {
5331                  // Is W3C Range
5332                  if (r.cloneRange) {
5333                      t.tridentSel.addRange(r);
5334                      return;
5335                  }
5336  
5337                  // Is IE specific range
5338                  try {
5339                      r.select();
5340                  } catch (ex) {
5341                      // Needed for some odd IE bug #1843306
5342                  }
5343              }
5344          },
5345  
5346          setNode : function(n) {
5347              var t = this;
5348  
5349              t.setContent(t.dom.getOuterHTML(n));
5350  
5351              return n;
5352          },
5353  
5354          getNode : function() {
5355              var t = this, r = t.getRng(), s = t.getSel(), e;
5356  
5357              if (!isIE) {
5358                  // Range maybe lost after the editor is made visible again
5359                  if (!r)
5360                      return t.dom.getRoot();
5361  
5362                  e = r.commonAncestorContainer;
5363  
5364                  // Handle selection a image or other control like element such as anchors
5365                  if (!r.collapsed) {
5366                      // If the anchor node is a element instead of a text node then return this element
5367                      if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) 
5368                          return s.anchorNode.childNodes[s.anchorOffset]; 
5369  
5370                      if (r.startContainer == r.endContainer) {
5371                          if (r.startOffset - r.endOffset < 2) {
5372                              if (r.startContainer.hasChildNodes())
5373                                  e = r.startContainer.childNodes[r.startOffset];
5374                          }
5375                      }
5376                  }
5377  
5378                  return t.dom.getParent(e, '*');
5379              }
5380  
5381              return r.item ? r.item(0) : r.parentElement();
5382          },
5383  
5384          getSelectedBlocks : function(st, en) {
5385              var t = this, dom = t.dom, sb, eb, n, bl = [];
5386  
5387              sb = dom.getParent(st || t.getStart(), dom.isBlock);
5388              eb = dom.getParent(en || t.getEnd(), dom.isBlock);
5389  
5390              if (sb)
5391                  bl.push(sb);
5392  
5393              if (sb && eb && sb != eb) {
5394                  n = sb;
5395  
5396                  while ((n = n.nextSibling) && n != eb) {
5397                      if (dom.isBlock(n))
5398                          bl.push(n);
5399                  }
5400              }
5401  
5402              if (eb && sb != eb)
5403                  bl.push(eb);
5404  
5405              return bl;
5406          },
5407  
5408          destroy : function(s) {
5409              var t = this;
5410  
5411              t.win = null;
5412  
5413              if (t.tridentSel)
5414                  t.tridentSel.destroy();
5415  
5416              // Manual destroy then remove unload handler
5417              if (!s)
5418                  tinymce.removeUnload(t.destroy);
5419          }
5420      });
5421  })(tinymce);
5422  (function(tinymce) {
5423      tinymce.create('tinymce.dom.XMLWriter', {
5424          node : null,
5425  
5426          XMLWriter : function(s) {
5427              // Get XML document
5428  			function getXML() {
5429                  var i = document.implementation;
5430  
5431                  if (!i || !i.createDocument) {
5432                      // Try IE objects
5433                      try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
5434                      try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
5435                  } else
5436                      return i.createDocument('', '', null);
5437              };
5438  
5439              this.doc = getXML();
5440              
5441              // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
5442              this.valid = tinymce.isOpera || tinymce.isWebKit;
5443  
5444              this.reset();
5445          },
5446  
5447          reset : function() {
5448              var t = this, d = t.doc;
5449  
5450              if (d.firstChild)
5451                  d.removeChild(d.firstChild);
5452  
5453              t.node = d.appendChild(d.createElement("html"));
5454          },
5455  
5456          writeStartElement : function(n) {
5457              var t = this;
5458  
5459              t.node = t.node.appendChild(t.doc.createElement(n));
5460          },
5461  
5462          writeAttribute : function(n, v) {
5463              if (this.valid)
5464                  v = v.replace(/>/g, '%MCGT%');
5465  
5466              this.node.setAttribute(n, v);
5467          },
5468  
5469          writeEndElement : function() {
5470              this.node = this.node.parentNode;
5471          },
5472  
5473          writeFullEndElement : function() {
5474              var t = this, n = t.node;
5475  
5476              n.appendChild(t.doc.createTextNode(""));
5477              t.node = n.parentNode;
5478          },
5479  
5480          writeText : function(v) {
5481              if (this.valid)
5482                  v = v.replace(/>/g, '%MCGT%');
5483  
5484              this.node.appendChild(this.doc.createTextNode(v));
5485          },
5486  
5487          writeCDATA : function(v) {
5488              this.node.appendChild(this.doc.createCDATASection(v));
5489          },
5490  
5491          writeComment : function(v) {
5492              // Fix for bug #2035694
5493              if (tinymce.isIE)
5494                  v = v.replace(/^\-|\-$/g, ' ');
5495  
5496              this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));
5497          },
5498  
5499          getContent : function() {
5500              var h;
5501  
5502              h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
5503              h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
5504              h = h.replace(/ ?\/>/g, ' />');
5505  
5506              if (this.valid)
5507                  h = h.replace(/\%MCGT%/g, '&gt;');
5508  
5509              return h;
5510          }
5511      });
5512  })(tinymce);
5513  (function(tinymce) {
5514      tinymce.create('tinymce.dom.StringWriter', {
5515          str : null,
5516          tags : null,
5517          count : 0,
5518          settings : null,
5519          indent : null,
5520  
5521          StringWriter : function(s) {
5522              this.settings = tinymce.extend({
5523                  indent_char : ' ',
5524                  indentation : 0
5525              }, s);
5526  
5527              this.reset();
5528          },
5529  
5530          reset : function() {
5531              this.indent = '';
5532              this.str = "";
5533              this.tags = [];
5534              this.count = 0;
5535          },
5536  
5537          writeStartElement : function(n) {
5538              this._writeAttributesEnd();
5539              this.writeRaw('<' + n);
5540              this.tags.push(n);
5541              this.inAttr = true;
5542              this.count++;
5543              this.elementCount = this.count;
5544          },
5545  
5546          writeAttribute : function(n, v) {
5547              var t = this;
5548  
5549              t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
5550          },
5551  
5552          writeEndElement : function() {
5553              var n;
5554  
5555              if (this.tags.length > 0) {
5556                  n = this.tags.pop();
5557  
5558                  if (this._writeAttributesEnd(1))
5559                      this.writeRaw('</' + n + '>');
5560  
5561                  if (this.settings.indentation > 0)
5562                      this.writeRaw('\n');
5563              }
5564          },
5565  
5566          writeFullEndElement : function() {
5567              if (this.tags.length > 0) {
5568                  this._writeAttributesEnd();
5569                  this.writeRaw('</' + this.tags.pop() + '>');
5570  
5571                  if (this.settings.indentation > 0)
5572                      this.writeRaw('\n');
5573              }
5574          },
5575  
5576          writeText : function(v) {
5577              this._writeAttributesEnd();
5578              this.writeRaw(this.encode(v));
5579              this.count++;
5580          },
5581  
5582          writeCDATA : function(v) {
5583              this._writeAttributesEnd();
5584              this.writeRaw('<![CDATA[' + v + ']]>');
5585              this.count++;
5586          },
5587  
5588          writeComment : function(v) {
5589              this._writeAttributesEnd();
5590              this.writeRaw('<!-- ' + v + '-->');
5591              this.count++;
5592          },
5593  
5594          writeRaw : function(v) {
5595              this.str += v;
5596          },
5597  
5598          encode : function(s) {
5599              return s.replace(/[<>&"]/g, function(v) {
5600                  switch (v) {
5601                      case '<':
5602                          return '&lt;';
5603  
5604                      case '>':
5605                          return '&gt;';
5606  
5607                      case '&':
5608                          return '&amp;';
5609  
5610                      case '"':
5611                          return '&quot;';
5612                  }
5613  
5614                  return v;
5615              });
5616          },
5617  
5618          getContent : function() {
5619              return this.str;
5620          },
5621  
5622          _writeAttributesEnd : function(s) {
5623              if (!this.inAttr)
5624                  return;
5625  
5626              this.inAttr = false;
5627  
5628              if (s && this.elementCount == this.count) {
5629                  this.writeRaw(' />');
5630                  return false;
5631              }
5632  
5633              this.writeRaw('>');
5634  
5635              return true;
5636          }
5637      });
5638  })(tinymce);
5639  (function(tinymce) {
5640      // Shorten names
5641      var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;
5642  
5643  	function wildcardToRE(s) {
5644          return s.replace(/([?+*])/g, '.$1');
5645      };
5646  
5647      tinymce.create('tinymce.dom.Serializer', {
5648          Serializer : function(s) {
5649              var t = this;
5650  
5651              t.key = 0;
5652              t.onPreProcess = new Dispatcher(t);
5653              t.onPostProcess = new Dispatcher(t);
5654  
5655              try {
5656                  t.writer = new tinymce.dom.XMLWriter();
5657              } catch (ex) {
5658                  // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
5659                  t.writer = new tinymce.dom.StringWriter();
5660              }
5661  
5662              // Default settings
5663              t.settings = s = extend({
5664                  dom : tinymce.DOM,
5665                  valid_nodes : 0,
5666                  node_filter : 0,
5667                  attr_filter : 0,
5668                  invalid_attrs : /^(mce_|_moz_|sizset|sizcache)/,
5669                  closed : /^(br|hr|input|meta|img|link|param|area)$/,
5670                  entity_encoding : 'named',
5671                  entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro',
5672                  bool_attrs : /(checked|disabled|readonly|selected|nowrap)/,
5673                  valid_elements : '*[*]',
5674                  extended_valid_elements : 0,
5675                  valid_child_elements : 0,
5676                  invalid_elements : 0,
5677                  fix_table_elements : 1,
5678                  fix_list_elements : true,
5679                  fix_content_duplication : true,
5680                  convert_fonts_to_spans : false,
5681                  font_size_classes : 0,
5682                  font_size_style_values : 0,
5683                  apply_source_formatting : 0,
5684                  indent_mode : 'simple',
5685                  indent_char : '\t',
5686                  indent_levels : 1,
5687                  remove_linebreaks : 1,
5688                  remove_redundant_brs : 1,
5689                  element_format : 'xhtml'
5690              }, s);
5691  
5692              t.dom = s.dom;
5693  
5694              if (s.remove_redundant_brs) {
5695                  t.onPostProcess.add(function(se, o) {
5696                      // Remove single BR at end of block elements since they get rendered
5697                      o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {
5698                          // Check if it's a single element
5699                          if (/^<br \/>\s*<\//.test(a))
5700                              return '</' + c + '>';
5701  
5702                          return a;
5703                      });
5704                  });
5705              }
5706  
5707              // Remove XHTML element endings i.e. produce crap :) XHTML is better
5708              if (s.element_format == 'html') {
5709                  t.onPostProcess.add(function(se, o) {
5710                      o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');
5711                  });
5712              }
5713  
5714              if (s.fix_list_elements) {
5715                  t.onPreProcess.add(function(se, o) {
5716                      var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;
5717  
5718  					function prevNode(e, n) {
5719                          var a = n.split(','), i;
5720  
5721                          while ((e = e.previousSibling) != null) {
5722                              for (i=0; i<a.length; i++) {
5723                                  if (e.nodeName == a[i])
5724                                      return e;
5725                              }
5726                          }
5727  
5728                          return null;
5729                      };
5730  
5731                      for (x=0; x<a.length; x++) {
5732                          nl = t.dom.select(a[x], o.node);
5733  
5734                          for (i=0; i<nl.length; i++) {
5735                              n = nl[i];
5736                              p = n.parentNode;
5737  
5738                              if (r.test(p.nodeName)) {
5739                                  np = prevNode(n, 'LI');
5740  
5741                                  if (!np) {
5742                                      np = t.dom.create('li');
5743                                      np.innerHTML = '&nbsp;';
5744                                      np.appendChild(n);
5745                                      p.insertBefore(np, p.firstChild);
5746                                  } else
5747                                      np.appendChild(n);
5748                              }
5749                          }
5750                      }
5751                  });
5752              }
5753  
5754              if (s.fix_table_elements) {
5755                  t.onPreProcess.add(function(se, o) {
5756                      each(t.dom.select('p table', o.node).reverse(), function(n) {
5757                          var parent = t.dom.getParent(n.parentNode, 'table,p');
5758  
5759                          if (parent.nodeName != 'TABLE') {
5760                              // IE has a odd bug where tables inside paragraphs sometimes gets wrapped in a BODY and documentFragement element
5761                              // This hack seems to resolve that issue. This will normally not happed since your contents should be valid in the first place
5762                              if (isIE)
5763                                  t.dom.setOuterHTML(n, n.outerHTML);
5764  
5765                              t.dom.split(parent, n);
5766                          }
5767                      });
5768                  });
5769              }
5770          },
5771  
5772          setEntities : function(s) {
5773              var t = this, a, i, l = {}, re = '', v;
5774  
5775              // No need to setup more than once
5776              if (t.entityLookup)
5777                  return;
5778  
5779              // Build regex and lookup array
5780              a = s.split(',');
5781              for (i = 0; i < a.length; i += 2) {
5782                  v = a[i];
5783  
5784                  // Don't add default &amp; &quot; etc.
5785                  if (v == 34 || v == 38 || v == 60 || v == 62)
5786                      continue;
5787  
5788                  l[String.fromCharCode(a[i])] = a[i + 1];
5789  
5790                  v = parseInt(a[i]).toString(16);
5791                  re += '\\u' + '0000'.substring(v.length) + v;
5792              }
5793  
5794              if (!re) {
5795                  t.settings.entity_encoding = 'raw';
5796                  return;
5797              }
5798  
5799              t.entitiesRE = new RegExp('[' + re + ']', 'g');
5800              t.entityLookup = l;
5801          },
5802  
5803          setValidChildRules : function(s) {
5804              this.childRules = null;
5805              this.addValidChildRules(s);
5806          },
5807  
5808          addValidChildRules : function(s) {
5809              var t = this, inst, intr, bloc;
5810  
5811              if (!s)
5812                  return;
5813  
5814              inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment';
5815              intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment';
5816              bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP';
5817  
5818              each(s.split(','), function(s) {
5819                  var p = s.split(/\[|\]/), re;
5820  
5821                  s = '';
5822                  each(p[1].split('|'), function(v) {
5823                      if (s)
5824                          s += '|';
5825  
5826                      switch (v) {
5827                          case '%itrans':
5828                              v = intr;
5829                              break;
5830  
5831                          case '%itrans_na':
5832                              v = intr.substring(2);
5833                              break;
5834  
5835                          case '%istrict':
5836                              v = inst;
5837                              break;
5838  
5839                          case '%istrict_na':
5840                              v = inst.substring(2);
5841                              break;
5842  
5843                          case '%btrans':
5844                              v = bloc;
5845                              break;
5846  
5847                          case '%bstrict':
5848                              v = bloc;
5849                              break;
5850                      }
5851  
5852                      s += v;
5853                  });
5854                  re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5855  
5856                  each(p[0].split('/'), function(s) {
5857                      t.childRules = t.childRules || {};
5858                      t.childRules[s] = re;
5859                  });
5860              });
5861  
5862              // Build regex
5863              s = '';
5864              each(t.childRules, function(v, k) {
5865                  if (s)
5866                      s += '|';
5867  
5868                  s += k;
5869              });
5870  
5871              t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5872  
5873              /*console.debug(t.parentElementsRE.toString());
5874              each(t.childRules, function(v) {
5875                  console.debug(v.toString());
5876              });*/
5877          },
5878  
5879          setRules : function(s) {
5880              var t = this;
5881  
5882              t._setup();
5883              t.rules = {};
5884              t.wildRules = [];
5885              t.validElements = {};
5886  
5887              return t.addRules(s);
5888          },
5889  
5890          addRules : function(s) {
5891              var t = this, dr;
5892  
5893              if (!s)
5894                  return;
5895  
5896              t._setup();
5897  
5898              each(s.split(','), function(s) {
5899                  var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
5900  
5901                  // Extend with default rules
5902                  if (dr)
5903                      at = tinymce.extend([], dr.attribs);
5904  
5905                  // Parse attributes
5906                  if (p.length > 1) {
5907                      each(p[1].split('|'), function(s) {
5908                          var ar = {}, i;
5909  
5910                          at = at || [];
5911  
5912                          // Parse attribute rule
5913                          s = s.replace(/::/g, '~');
5914                          s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
5915                          s[2] = s[2].replace(/~/g, ':');
5916  
5917                          // Add required attributes
5918                          if (s[1] == '!') {
5919                              ra = ra || [];
5920                              ra.push(s[2]);
5921                          }
5922  
5923                          // Remove inherited attributes
5924                          if (s[1] == '-') {
5925                              for (i = 0; i <at.length; i++) {
5926                                  if (at[i].name == s[2]) {
5927                                      at.splice(i, 1);
5928                                      return;
5929                                  }
5930                              }
5931                          }
5932  
5933                          switch (s[3]) {
5934                              // Add default attrib values
5935                              case '=':
5936                                  ar.defaultVal = s[4] || '';
5937                                  break;
5938  
5939                              // Add forced attrib values
5940                              case ':':
5941                                  ar.forcedVal = s[4];
5942                                  break;
5943  
5944                              // Add validation values
5945                              case '<':
5946                                  ar.validVals = s[4].split('?');
5947                                  break;
5948                          }
5949  
5950                          if (/[*.?]/.test(s[2])) {
5951                              wat = wat || [];
5952                              ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
5953                              wat.push(ar);
5954                          } else {
5955                              ar.name = s[2];
5956                              at.push(ar);
5957                          }
5958  
5959                          va.push(s[2]);
5960                      });
5961                  }
5962  
5963                  // Handle element names
5964                  each(tn, function(s, i) {
5965                      var pr = s.charAt(0), x = 1, ru = {};
5966  
5967                      // Extend with default rule data
5968                      if (dr) {
5969                          if (dr.noEmpty)
5970                              ru.noEmpty = dr.noEmpty;
5971  
5972                          if (dr.fullEnd)
5973                              ru.fullEnd = dr.fullEnd;
5974  
5975                          if (dr.padd)
5976                              ru.padd = dr.padd;
5977                      }
5978  
5979                      // Handle prefixes
5980                      switch (pr) {
5981                          case '-':
5982                              ru.noEmpty = true;
5983                              break;
5984  
5985                          case '+':
5986                              ru.fullEnd = true;
5987                              break;
5988  
5989                          case '#':
5990                              ru.padd = true;
5991                              break;
5992  
5993                          default:
5994                              x = 0;
5995                      }
5996  
5997                      tn[i] = s = s.substring(x);
5998                      t.validElements[s] = 1;
5999  
6000                      // Add element name or element regex
6001                      if (/[*.?]/.test(tn[0])) {
6002                          ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
6003                          t.wildRules = t.wildRules || {};
6004                          t.wildRules.push(ru);
6005                      } else {
6006                          ru.name = tn[0];
6007  
6008                          // Store away default rule
6009                          if (tn[0] == '@')
6010                              dr = ru;
6011  
6012                          t.rules[s] = ru;
6013                      }
6014  
6015                      ru.attribs = at;
6016  
6017                      if (ra)
6018                          ru.requiredAttribs = ra;
6019  
6020                      if (wat) {
6021                          // Build valid attributes regexp
6022                          s = '';
6023                          each(va, function(v) {
6024                              if (s)
6025                                  s += '|';
6026  
6027                              s += '(' + wildcardToRE(v) + ')';
6028                          });
6029                          ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
6030                          ru.wildAttribs = wat;
6031                      }
6032                  });
6033              });
6034  
6035              // Build valid elements regexp
6036              s = '';
6037              each(t.validElements, function(v, k) {
6038                  if (s)
6039                      s += '|';
6040  
6041                  if (k != '@')
6042                      s += k;
6043              });
6044              t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');
6045  
6046              //console.debug(t.validElementsRE.toString());
6047              //console.dir(t.rules);
6048              //console.dir(t.wildRules);
6049          },
6050  
6051          findRule : function(n) {
6052              var t = this, rl = t.rules, i, r;
6053  
6054              t._setup();
6055  
6056              // Exact match
6057              r = rl[n];
6058              if (r)
6059                  return r;
6060  
6061              // Try wildcards
6062              rl = t.wildRules;
6063              for (i = 0; i < rl.length; i++) {
6064                  if (rl[i].nameRE.test(n))
6065                      return rl[i];
6066              }
6067  
6068              return null;
6069          },
6070  
6071          findAttribRule : function(ru, n) {
6072              var i, wa = ru.wildAttribs;
6073  
6074              for (i = 0; i < wa.length; i++) {
6075                  if (wa[i].nameRE.test(n))
6076                      return wa[i];
6077              }
6078  
6079              return null;
6080          },
6081  
6082          serialize : function(n, o) {
6083              var h, t = this, doc;
6084  
6085              t._setup();
6086              o = o || {};
6087              o.format = o.format || 'html';
6088              n = n.cloneNode(true);
6089              t.processObj = o;
6090  
6091              // Nodes needs to be attached to something in WebKit due to a bug https://bugs.webkit.org/show_bug.cgi?id=25571
6092              if (tinymce.isWebKit) {
6093                  doc = n.ownerDocument.implementation.createHTMLDocument("");
6094                  doc.body.appendChild(doc.importNode(n));
6095              }
6096  
6097              t.key = '' + (parseInt(t.key) + 1);
6098  
6099              // Pre process
6100              if (!o.no_events) {
6101                  o.node = n;
6102                  t.onPreProcess.dispatch(t, o);
6103              }
6104  
6105              // Serialize HTML DOM into a string
6106              t.writer.reset();
6107              t._serializeNode(n, o.getInner);
6108  
6109              // Post process
6110              o.content = t.writer.getContent();
6111  
6112              if (!o.no_events)
6113                  t.onPostProcess.dispatch(t, o);
6114  
6115              t._postProcess(o);
6116              o.node = null;
6117  
6118              return tinymce.trim(o.content);
6119          },
6120  
6121          // Internal functions
6122  
6123          _postProcess : function(o) {
6124              var t = this, s = t.settings, h = o.content, sc = [], p;
6125  
6126              if (o.format == 'html') {
6127                  // Protect some elements
6128                  p = t._protect({
6129                      content : h,
6130                      patterns : [
6131                          {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
6132                          {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},
6133                          {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
6134                          {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
6135                          {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
6136                      ]
6137                  });
6138  
6139                  h = p.content;
6140  
6141                  // Entity encode
6142                  if (s.entity_encoding !== 'raw')
6143                      h = t._encode(h);
6144  
6145                  // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
6146  /*                if (o.set)
6147                      h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
6148                  else
6149                      h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/
6150  
6151                  // Since Gecko and Safari keeps whitespace in the DOM we need to
6152                  // remove it inorder to match other browsers. But I think Gecko and Safari is right.
6153                  // This process is only done when getting contents out from the editor.
6154                  if (!o.set) {
6155                      // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
6156                      h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');
6157  
6158                      if (s.remove_linebreaks) {
6159                          h = h.replace(/\r?\n|\r/g, ' ');
6160                          h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
6161                          h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
6162                          h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start
6163                          h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start
6164                          h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>'); // Trim block end
6165                      }
6166  
6167                      // Simple indentation
6168                      if (s.apply_source_formatting && s.indent_mode == 'simple') {
6169                          // Add line breaks before and after block elements
6170                          h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
6171                          h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
6172                          h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
6173                          h = h.replace(/\n\n/g, '\n');
6174                      }
6175                  }
6176  
6177                  h = t._unprotect(h, p);
6178  
6179                  // Restore CDATA sections
6180                  h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');
6181  
6182                  // Restore the \u00a0 character if raw mode is enabled
6183                  if (s.entity_encoding == 'raw')
6184                      h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
6185  
6186                  // Restore noscript elements
6187                  h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {
6188                      return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';
6189                  });
6190              }
6191  
6192              o.content = h;
6193          },
6194  
6195          _serializeNode : function(n, inn) {
6196              var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed;
6197  
6198              if (!s.node_filter || s.node_filter(n)) {
6199                  switch (n.nodeType) {
6200                      case 1: // Element
6201                          if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
6202                              return;
6203  
6204                          iv = false;
6205                          hc = n.hasChildNodes();
6206                          nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
6207  
6208                          // Add correct prefix on IE
6209                          if (isIE) {
6210                              if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
6211                                  nn = n.scopeName + ':' + nn;
6212                          }
6213  
6214                          // Remove mce prefix on IE needed for the abbr element
6215                          if (nn.indexOf('mce:') === 0)
6216                              nn = nn.substring(4);
6217  
6218                          // Check if valid
6219                          if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
6220                              iv = true;
6221                              break;
6222                          }
6223  
6224                          if (isIE) {
6225                              // Fix IE content duplication (DOM can have multiple copies of the same node)
6226                              if (s.fix_content_duplication) {
6227                                  if (n.mce_serialized == t.key)
6228                                      return;
6229  
6230                                  n.mce_serialized = t.key;
6231                              }
6232  
6233                              // IE sometimes adds a / infront of the node name
6234                              if (nn.charAt(0) == '/')
6235                                  nn = nn.substring(1);
6236                          } else if (isGecko) {
6237                              // Ignore br elements
6238                              if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
6239                                  return;
6240                          }
6241  
6242                          // Check if valid child
6243                          if (t.childRules) {
6244                              if (t.parentElementsRE.test(t.elementName)) {
6245                                  if (!t.childRules[t.elementName].test(nn)) {
6246                                      iv = true;
6247                                      break;
6248                                  }
6249                              }
6250  
6251                              t.elementName = nn;
6252                          }
6253  
6254                          ru = t.findRule(nn);
6255                          nn = ru.name || nn;
6256                          closed = s.closed.test(nn);
6257  
6258                          // Skip empty nodes or empty node name in IE
6259                          if ((!hc && ru.noEmpty) || (isIE && !nn)) {
6260                              iv = true;
6261                              break;
6262                          }
6263  
6264                          // Check required
6265                          if (ru.requiredAttribs) {
6266                              a = ru.requiredAttribs;
6267  
6268                              for (i = a.length - 1; i >= 0; i--) {
6269                                  if (this.dom.getAttrib(n, a[i]) !== '')
6270                                      break;
6271                              }
6272  
6273                              // None of the required was there
6274                              if (i == -1) {
6275                                  iv = true;
6276                                  break;
6277                              }
6278                          }
6279  
6280                          w.writeStartElement(nn);
6281  
6282                          // Add ordered attributes
6283                          if (ru.attribs) {
6284                              for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
6285                                  a = at[i];
6286                                  v = t._getAttrib(n, a);
6287  
6288                                  if (v !== null)
6289                                      w.writeAttribute(a.name, v);
6290                              }
6291                          }
6292  
6293                          // Add wild attributes
6294                          if (ru.validAttribsRE) {
6295                              at = t.dom.getAttribs(n);
6296                              for (i=at.length-1; i>-1; i--) {
6297                                  no = at[i];
6298  
6299                                  if (no.specified) {
6300                                      a = no.nodeName.toLowerCase();
6301  
6302                                      if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
6303                                          continue;
6304  
6305                                      ar = t.findAttribRule(ru, a);
6306                                      v = t._getAttrib(n, ar, a);
6307  
6308                                      if (v !== null)
6309                                          w.writeAttribute(a, v);
6310                                  }
6311                              }
6312                          }
6313  
6314                          // Write text from script
6315                          if (nn === 'script' && tinymce.trim(n.innerHTML)) {
6316                              w.writeText('// '); // Padd it with a comment so it will parse on older browsers
6317                              w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures
6318                              hc = false;
6319                              break;
6320                          }
6321  
6322                          // Padd empty nodes with a &nbsp;
6323                          if (ru.padd) {
6324                              // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug
6325                              if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {
6326                                  if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus'))
6327                                      w.writeText('\u00a0');
6328                              } else if (!hc)
6329                                  w.writeText('\u00a0'); // No children then padd it
6330                          }
6331  
6332                          break;
6333  
6334                      case 3: // Text
6335                          // Check if valid child
6336                          if (t.childRules && t.parentElementsRE.test(t.elementName)) {
6337                              if (!t.childRules[t.elementName].test(n.nodeName))
6338                                  return;
6339                          }
6340  
6341                          return w.writeText(n.nodeValue);
6342  
6343                      case 4: // CDATA
6344                          return w.writeCDATA(n.nodeValue);
6345  
6346                      case 8: // Comment
6347                          return w.writeComment(n.nodeValue);
6348                  }
6349              } else if (n.nodeType == 1)
6350                  hc = n.hasChildNodes();
6351  
6352              if (hc && !closed) {
6353                  cn = n.firstChild;
6354  
6355                  while (cn) {
6356                      t._serializeNode(cn);
6357                      t.elementName = nn;
6358                      cn = cn.nextSibling;
6359                  }
6360              }
6361  
6362              // Write element end
6363              if (!iv) {
6364                  if (!closed)
6365                      w.writeFullEndElement();
6366                  else
6367                      w.writeEndElement();
6368              }
6369          },
6370  
6371          _protect : function(o) {
6372              var t = this;
6373  
6374              o.items = o.items || [];
6375  
6376  			function enc(s) {
6377                  return s.replace(/[\r\n\\]/g, function(c) {
6378                      if (c === '\n')
6379                          return '\\n';
6380                      else if (c === '\\')
6381                          return '\\\\';
6382  
6383                      return '\\r';
6384                  });
6385              };
6386  
6387  			function dec(s) {
6388                  return s.replace(/\\[\\rn]/g, function(c) {
6389                      if (c === '\\n')
6390                          return '\n';
6391                      else if (c === '\\\\')
6392                          return '\\';
6393  
6394                      return '\r';
6395                  });
6396              };
6397  
6398              each(o.patterns, function(p) {
6399                  o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
6400                      b = dec(b);
6401  
6402                      if (p.encode)
6403                          b = t._encode(b);
6404  
6405                      o.items.push(b);
6406                      return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
6407                  }));
6408              });
6409  
6410              return o;
6411          },
6412  
6413          _unprotect : function(h, o) {
6414              h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
6415                  return o.items[parseInt(b)];
6416              });
6417  
6418              o.items = [];
6419  
6420              return h;
6421          },
6422  
6423          _encode : function(h) {
6424              var t = this, s = t.settings, l;
6425  
6426              // Entity encode
6427              if (s.entity_encoding !== 'raw') {
6428                  if (s.entity_encoding.indexOf('named') != -1) {
6429                      t.setEntities(s.entities);
6430                      l = t.entityLookup;
6431  
6432                      h = h.replace(t.entitiesRE, function(a) {
6433                          var v;
6434  
6435                          if (v = l[a])
6436                              a = '&' + v + ';';
6437  
6438                          return a;
6439                      });
6440                  }
6441  
6442                  if (s.entity_encoding.indexOf('numeric') != -1) {
6443                      h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
6444                          return '&#' + a.charCodeAt(0) + ';';
6445                      });
6446                  }
6447              }
6448  
6449              return h;
6450          },
6451  
6452          _setup : function() {
6453              var t = this, s = this.settings;
6454  
6455              if (t.done)
6456                  return;
6457  
6458              t.done = 1;
6459  
6460              t.setRules(s.valid_elements);
6461              t.addRules(s.extended_valid_elements);
6462              t.addValidChildRules(s.valid_child_elements);
6463  
6464              if (s.invalid_elements)
6465                  t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
6466  
6467              if (s.attrib_value_filter)
6468                  t.attribValueFilter = s.attribValueFilter;
6469          },
6470  
6471          _getAttrib : function(n, a, na) {
6472              var i, v;
6473  
6474              na = na || a.name;
6475  
6476              if (a.forcedVal && (v = a.forcedVal)) {
6477                  if (v === '{$uid}')
6478                      return this.dom.uniqueId();
6479  
6480                  return v;
6481              }
6482  
6483              v = this.dom.getAttrib(n, na);
6484  
6485              // Bool attr
6486              if (this.settings.bool_attrs.test(na) && v) {
6487                  v = ('' + v).toLowerCase();
6488  
6489                  if (v === 'false' || v === '0')
6490                      return null;
6491  
6492                  v = na;
6493              }
6494  
6495              switch (na) {
6496                  case 'rowspan':
6497                  case 'colspan':
6498                      // Whats the point? Remove usless attribute value
6499                      if (v == '1')
6500                          v = '';
6501  
6502                      break;
6503              }
6504  
6505              if (this.attribValueFilter)
6506                  v = this.attribValueFilter(na, v, n);
6507  
6508              if (a.validVals) {
6509                  for (i = a.validVals.length - 1; i >= 0; i--) {
6510                      if (v == a.validVals[i])
6511                          break;
6512                  }
6513  
6514                  if (i == -1)
6515                      return null;
6516              }
6517  
6518              if (v === '' && typeof(a.defaultVal) != 'undefined') {
6519                  v = a.defaultVal;
6520  
6521                  if (v === '{$uid}')
6522                      return this.dom.uniqueId();
6523  
6524                  return v;
6525              } else {
6526                  // Remove internal mceItemXX classes when content is extracted from editor
6527                  if (na == 'class' && this.processObj.get)
6528                      v = v.replace(/\s?mceItem\w+\s?/g, '');
6529              }
6530  
6531              if (v === '')
6532                  return null;
6533  
6534  
6535              return v;
6536          }
6537      });
6538  })(tinymce);
6539  (function(tinymce) {
6540      var each = tinymce.each, Event = tinymce.dom.Event;
6541  
6542      tinymce.create('tinymce.dom.ScriptLoader', {
6543          ScriptLoader : function(s) {
6544              this.settings = s || {};
6545              this.queue = [];
6546              this.lookup = {};
6547          },
6548  
6549          isDone : function(u) {
6550              return this.lookup[u] ? this.lookup[u].state == 2 : 0;
6551          },
6552  
6553          markDone : function(u) {
6554              this.lookup[u] = {state : 2, url : u};
6555          },
6556  
6557          add : function(u, cb, s, pr) {
6558              var t = this, lo = t.lookup, o;
6559  
6560              if (o = lo[u]) {
6561                  // Is loaded fire callback
6562                  if (cb && o.state == 2)
6563                      cb.call(s || this);
6564  
6565                  return o;
6566              }
6567  
6568              o = {state : 0, url : u, func : cb, scope : s || this};
6569  
6570              if (pr)
6571                  t.queue.unshift(o);
6572              else
6573                  t.queue.push(o);
6574  
6575              lo[u] = o;
6576  
6577              return o;
6578          },
6579  
6580          load : function(u, cb, s) {
6581              var t = this, o;
6582  
6583              if (o = t.lookup[u]) {
6584                  // Is loaded fire callback
6585                  if (cb && o.state == 2)
6586                      cb.call(s || t);
6587  
6588                  return o;
6589              }
6590  
6591  			function loadScript(u) {
6592                  if (Event.domLoaded || t.settings.strict_mode) {
6593                      tinymce.util.XHR.send({
6594                          url : tinymce._addVer(u),
6595                          error : t.settings.error,
6596                          async : false,
6597                          success : function(co) {
6598                              t.eval(co);
6599                          }
6600                      });
6601                  } else
6602                      document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
6603              };
6604  
6605              if (!tinymce.is(u, 'string')) {
6606                  each(u, function(u) {
6607                      loadScript(u);
6608                  });
6609  
6610                  if (cb)
6611                      cb.call(s || t);
6612              } else {
6613                  loadScript(u);
6614  
6615                  if (cb)
6616                      cb.call(s || t);
6617              }
6618          },
6619  
6620          loadQueue : function(cb, s) {
6621              var t = this;
6622  
6623              if (!t.queueLoading) {
6624                  t.queueLoading = 1;
6625                  t.queueCallbacks = [];
6626  
6627                  t.loadScripts(t.queue, function() {
6628                      t.queueLoading = 0;
6629  
6630                      if (cb)
6631                          cb.call(s || t);
6632  
6633                      each(t.queueCallbacks, function(o) {
6634                          o.func.call(o.scope);
6635                      });
6636                  });
6637              } else if (cb)
6638                  t.queueCallbacks.push({func : cb, scope : s || t});
6639          },
6640  
6641          eval : function(co) {
6642              var w = window;
6643  
6644              // Evaluate script
6645              if (!w.execScript) {
6646                  try {
6647                      eval.call(w, co);
6648                  } catch (ex) {
6649                      eval(co, w); // Firefox 3.0a8
6650                  }
6651              } else
6652                  w.execScript(co); // IE
6653          },
6654  
6655          loadScripts : function(sc, cb, s) {
6656              var t = this, lo = t.lookup;
6657  
6658  			function done(o) {
6659                  o.state = 2; // Has been loaded
6660  
6661                  // Run callback
6662                  if (o.func)
6663                      o.func.call(o.scope || t);
6664              };
6665  
6666  			function allDone() {
6667                  var l;
6668  
6669                  // Check if all files are loaded
6670                  l = sc.length;
6671                  each(sc, function(o) {
6672                      o = lo[o.url];
6673  
6674                      if (o.state === 2) {// It has finished loading
6675                          done(o);
6676                          l--;
6677                      } else
6678                          load(o);
6679                  });
6680  
6681                  // They are all loaded
6682                  if (l === 0 && cb) {
6683                      cb.call(s || t);
6684                      cb = 0;
6685                  }
6686              };
6687  
6688  			function load(o) {
6689                  if (o.state > 0)
6690                      return;
6691  
6692                  o.state = 1; // Is loading
6693  
6694                  tinymce.dom.ScriptLoader.loadScript(o.url, function() {
6695                      done(o);
6696                      allDone();
6697                  });
6698  
6699                  /*
6700                  tinymce.util.XHR.send({
6701                      url : o.url,
6702                      error : t.settings.error,
6703                      success : function(co) {
6704                          t.eval(co);
6705                          done(o);
6706                          allDone();
6707                      }
6708                  });
6709                  */
6710              };
6711  
6712              each(sc, function(o) {
6713                  var u = o.url;
6714  
6715                  // Add to queue if needed
6716                  if (!lo[u]) {
6717                      lo[u] = o;
6718                      t.queue.push(o);
6719                  } else
6720                      o = lo[u];
6721  
6722                  // Is already loading or has been loaded
6723                  if (o.state > 0)
6724                      return;
6725  
6726                  if (!Event.domLoaded && !t.settings.strict_mode) {
6727                      var ix, ol = '';
6728  
6729                      // Add onload events
6730                      if (cb || o.func) {
6731                          o.state = 1; // Is loading
6732  
6733                          ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
6734                              done(o);
6735                              allDone();
6736                          });
6737  
6738                          if (tinymce.isIE)
6739                              ol = ' onreadystatechange="';
6740                          else
6741                              ol = ' onload="';
6742  
6743                          ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
6744                      }
6745  
6746                      document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
6747  
6748                      if (!o.func)
6749                          done(o);
6750                  } else
6751                      load(o);
6752              });
6753  
6754              allDone();
6755          },
6756  
6757          // Static methods
6758          'static' : {
6759              _addOnLoad : function(f) {
6760                  var t = this;
6761  
6762                  t._funcs = t._funcs || [];
6763                  t._funcs.push(f);
6764  
6765                  return t._funcs.length - 1;
6766              },
6767  
6768              _onLoad : function(e, u, ix) {
6769                  if (!tinymce.isIE || e.readyState == 'complete')
6770                      this._funcs[ix].call(this);
6771              },
6772  
6773              loadScript : function(u, cb) {
6774                  var id = tinymce.DOM.uniqueId(), e;
6775  
6776  				function done() {
6777                      Event.clear(id);
6778                      tinymce.DOM.remove(id);
6779  
6780                      if (cb) {
6781                          cb.call(document, u);
6782                          cb = 0;
6783                      }
6784                  };
6785  
6786                  if (tinymce.isIE) {
6787  /*                    Event.add(e, 'readystatechange', function(e) {
6788                          if (e.target && e.target.readyState == 'complete')
6789                              done();
6790                      });*/
6791  
6792                      tinymce.util.XHR.send({
6793                          url : tinymce._addVer(u),
6794                          async : false,
6795                          success : function(co) {
6796                              window.execScript(co);
6797                              done();
6798                          }
6799                      });
6800                  } else {
6801                      e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)});
6802                      Event.add(e, 'load', done);
6803  
6804                      // Check for head or body
6805                      (document.getElementsByTagName('head')[0] || document.body).appendChild(e);
6806                  }
6807              }
6808          }
6809      });
6810  
6811      // Global script loader
6812      tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
6813  })(tinymce);
6814  (function(tinymce) {
6815      // Shorten class names
6816      var DOM = tinymce.DOM, is = tinymce.is;
6817  
6818      tinymce.create('tinymce.ui.Control', {
6819          Control : function(id, s) {
6820              this.id = id;
6821              this.settings = s = s || {};
6822              this.rendered = false;
6823              this.onRender = new tinymce.util.Dispatcher(this);
6824              this.classPrefix = '';
6825              this.scope = s.scope || this;
6826              this.disabled = 0;
6827              this.active = 0;
6828          },
6829  
6830          setDisabled : function(s) {
6831              var e;
6832  
6833              if (s != this.disabled) {
6834                  e = DOM.get(this.id);
6835  
6836                  // Add accessibility title for unavailable actions
6837                  if (e && this.settings.unavailable_prefix) {
6838                      if (s) {
6839                          this.prevTitle = e.title;
6840                          e.title = this.settings.unavailable_prefix + ": " + e.title;
6841                      } else
6842                          e.title = this.prevTitle;
6843                  }
6844  
6845                  this.setState('Disabled', s);
6846                  this.setState('Enabled', !s);
6847                  this.disabled = s;
6848              }
6849          },
6850  
6851          isDisabled : function() {
6852              return this.disabled;
6853          },
6854  
6855          setActive : function(s) {
6856              if (s != this.active) {
6857                  this.setState('Active', s);
6858                  this.active = s;
6859              }
6860          },
6861  
6862          isActive : function() {
6863              return this.active;
6864          },
6865  
6866          setState : function(c, s) {
6867              var n = DOM.get(this.id);
6868  
6869              c = this.classPrefix + c;
6870  
6871              if (s)
6872                  DOM.addClass(n, c);
6873              else
6874                  DOM.removeClass(n, c);
6875          },
6876  
6877          isRendered : function() {
6878              return this.rendered;
6879          },
6880  
6881          renderHTML : function() {
6882          },
6883  
6884          renderTo : function(n) {
6885              DOM.setHTML(n, this.renderHTML());
6886          },
6887  
6888          postRender : function() {
6889              var t = this, b;
6890  
6891              // Set pending states
6892              if (is(t.disabled)) {
6893                  b = t.disabled;
6894                  t.disabled = -1;
6895                  t.setDisabled(b);
6896              }
6897  
6898              if (is(t.active)) {
6899                  b = t.active;
6900                  t.active = -1;
6901                  t.setActive(b);
6902              }
6903          },
6904  
6905          remove : function() {
6906              DOM.remove(this.id);
6907              this.destroy();
6908          },
6909  
6910          destroy : function() {
6911              tinymce.dom.Event.clear(this.id);
6912          }
6913      });
6914  })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
6915      Container : function(id, s) {
6916          this.parent(id, s);
6917  
6918          this.controls = [];
6919  
6920          this.lookup = {};
6921      },
6922  
6923      add : function(c) {
6924          this.lookup[c.id] = c;
6925          this.controls.push(c);
6926  
6927          return c;
6928      },
6929  
6930      get : function(n) {
6931          return this.lookup[n];
6932      }
6933  });
6934  
6935  tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
6936      Separator : function(id, s) {
6937          this.parent(id, s);
6938          this.classPrefix = 'mceSeparator';
6939      },
6940  
6941      renderHTML : function() {
6942          return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});
6943      }
6944  });
6945  (function(tinymce) {
6946      var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6947  
6948      tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
6949          MenuItem : function(id, s) {
6950              this.parent(id, s);
6951              this.classPrefix = 'mceMenuItem';
6952          },
6953  
6954          setSelected : function(s) {
6955              this.setState('Selected', s);
6956              this.selected = s;
6957          },
6958  
6959          isSelected : function() {
6960              return this.selected;
6961          },
6962  
6963          postRender : function() {
6964              var t = this;
6965              
6966              t.parent();
6967  
6968              // Set pending state
6969              if (is(t.selected))
6970                  t.setSelected(t.selected);
6971          }
6972      });
6973  })(tinymce);
6974  (function(tinymce) {
6975      var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6976  
6977      tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
6978          Menu : function(id, s) {
6979              var t = this;
6980  
6981              t.parent(id, s);
6982              t.items = {};
6983              t.collapsed = false;
6984              t.menuCount = 0;
6985              t.onAddItem = new tinymce.util.Dispatcher(this);
6986          },
6987  
6988          expand : function(d) {
6989              var t = this;
6990  
6991              if (d) {
6992                  walk(t, function(o) {
6993                      if (o.expand)
6994                          o.expand();
6995                  }, 'items', t);
6996              }
6997  
6998              t.collapsed = false;
6999          },
7000  
7001          collapse : function(d) {
7002              var t = this;
7003  
7004              if (d) {
7005                  walk(t, function(o) {
7006                      if (o.collapse)
7007                          o.collapse();
7008                  }, 'items', t);
7009              }
7010  
7011              t.collapsed = true;
7012          },
7013  
7014          isCollapsed : function() {
7015              return this.collapsed;
7016          },
7017  
7018          add : function(o) {
7019              if (!o.settings)
7020                  o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
7021  
7022              this.onAddItem.dispatch(this, o);
7023  
7024              return this.items[o.id] = o;
7025          },
7026  
7027          addSeparator : function() {
7028              return this.add({separator : true});
7029          },
7030  
7031          addMenu : function(o) {
7032              if (!o.collapse)
7033                  o = this.createMenu(o);
7034  
7035              this.menuCount++;
7036  
7037              return this.add(o);
7038          },
7039  
7040          hasMenus : function() {
7041              return this.menuCount !== 0;
7042          },
7043  
7044          remove : function(o) {
7045              delete this.items[o.id];
7046          },
7047  
7048          removeAll : function() {
7049              var t = this;
7050  
7051              walk(t, function(o) {
7052                  if (o.removeAll)
7053                      o.removeAll();
7054                  else
7055                      o.remove();
7056  
7057                  o.destroy();
7058              }, 'items', t);
7059  
7060              t.items = {};
7061          },
7062  
7063          createMenu : function(o) {
7064              var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
7065  
7066              m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
7067  
7068              return m;
7069          }
7070      });
7071  })(tinymce);(function(tinymce) {
7072      var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
7073  
7074      tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
7075          DropMenu : function(id, s) {
7076              s = s || {};
7077              s.container = s.container || DOM.doc.body;
7078              s.offset_x = s.offset_x || 0;
7079              s.offset_y = s.offset_y || 0;
7080              s.vp_offset_x = s.vp_offset_x || 0;
7081              s.vp_offset_y = s.vp_offset_y || 0;
7082  
7083              if (is(s.icons) && !s.icons)
7084                  s['class'] += ' mceNoIcons';
7085  
7086              this.parent(id, s);
7087              this.onShowMenu = new tinymce.util.Dispatcher(this);
7088              this.onHideMenu = new tinymce.util.Dispatcher(this);
7089              this.classPrefix = 'mceMenu';
7090          },
7091  
7092          createMenu : function(s) {
7093              var t = this, cs = t.settings, m;
7094  
7095              s.container = s.container || cs.container;
7096              s.parent = t;
7097              s.constrain = s.constrain || cs.constrain;
7098              s['class'] = s['class'] || cs['class'];
7099              s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
7100              s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
7101              m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
7102  
7103              m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
7104  
7105              return m;
7106          },
7107  
7108          update : function() {
7109              var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
7110  
7111              tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
7112              th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
7113  
7114              if (!DOM.boxModel)
7115                  t.element.setStyles({width : tw + 2, height : th + 2});
7116              else
7117                  t.element.setStyles({width : tw, height : th});
7118  
7119              if (s.max_width)
7120                  DOM.setStyle(co, 'width', tw);
7121  
7122              if (s.max_height) {
7123                  DOM.setStyle(co, 'height', th);
7124  
7125                  if (tb.clientHeight < s.max_height)
7126                      DOM.setStyle(co, 'overflow', 'hidden');
7127              }
7128          },
7129  
7130          showMenu : function(x, y, px) {
7131              var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
7132  
7133              t.collapse(1);
7134  
7135              if (t.isMenuVisible)
7136                  return;
7137  
7138              if (!t.rendered) {
7139                  co = DOM.add(t.settings.container, t.renderNode());
7140  
7141                  each(t.items, function(o) {
7142                      o.postRender();
7143                  });
7144  
7145                  t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
7146              } else
7147                  co = DOM.get('menu_' + t.id);
7148  
7149              // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
7150              if (!tinymce.isOpera)
7151                  DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});
7152  
7153              DOM.show(co);
7154              t.update();
7155  
7156              x += s.offset_x || 0;
7157              y += s.offset_y || 0;
7158              vp.w -= 4;
7159              vp.h -= 4;
7160  
7161              // Move inside viewport if not submenu
7162              if (s.constrain) {
7163                  w = co.clientWidth - ot;
7164                  h = co.clientHeight - ot;
7165                  mx = vp.x + vp.w;
7166                  my = vp.y + vp.h;
7167  
7168                  if ((x + s.vp_offset_x + w) > mx)
7169                      x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);
7170  
7171                  if ((y + s.vp_offset_y + h) > my)
7172                      y = Math.max(0, (my - s.vp_offset_y) - h);
7173              }
7174  
7175              DOM.setStyles(co, {left : x , top : y});
7176              t.element.update();
7177  
7178              t.isMenuVisible = 1;
7179              t.mouseClickFunc = Event.add(co, 'click', function(e) {
7180                  var m;
7181  
7182                  e = e.target;
7183  
7184                  if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {
7185                      m = t.items[e.id];
7186  
7187                      if (m.isDisabled())
7188                          return;
7189  
7190                      dm = t;
7191  
7192                      while (dm) {
7193                          if (dm.hideMenu)
7194                              dm.hideMenu();
7195  
7196                          dm = dm.settings.parent;
7197                      }
7198  
7199                      if (m.settings.onclick)
7200                          m.settings.onclick(e);
7201  
7202                      return Event.cancel(e); // Cancel to fix onbeforeunload problem
7203                  }
7204              });
7205  
7206              if (t.hasMenus()) {
7207                  t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
7208                      var m, r, mi;
7209  
7210                      e = e.target;
7211                      if (e && (e = DOM.getParent(e, 'tr'))) {
7212                          m = t.items[e.id];
7213  
7214                          if (t.lastMenu)
7215                              t.lastMenu.collapse(1);
7216  
7217                          if (m.isDisabled())
7218                              return;
7219  
7220                          if (e && DOM.hasClass(e, cp + 'ItemSub')) {
7221                              //p = DOM.getPos(s.container);
7222                              r = DOM.getRect(e);
7223                              m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
7224                              t.lastMenu = m;
7225                              DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
7226                          }
7227                      }
7228                  });
7229              }
7230  
7231              t.onShowMenu.dispatch(t);
7232  
7233              if (s.keyboard_focus) {
7234                  Event.add(co, 'keydown', t._keyHandler, t);
7235                  DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
7236                  t._focusIdx = 0;
7237              }
7238          },
7239  
7240          hideMenu : function(c) {
7241              var t = this, co = DOM.get('menu_' + t.id), e;
7242  
7243              if (!t.isMenuVisible)
7244                  return;
7245  
7246              Event.remove(co, 'mouseover', t.mouseOverFunc);
7247              Event.remove(co, 'click', t.mouseClickFunc);
7248              Event.remove(co, 'keydown', t._keyHandler);
7249              DOM.hide(co);
7250              t.isMenuVisible = 0;
7251  
7252              if (!c)
7253                  t.collapse(1);
7254  
7255              if (t.element)
7256                  t.element.hide();
7257  
7258              if (e = DOM.get(t.id))
7259                  DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
7260  
7261              t.onHideMenu.dispatch(t);
7262          },
7263  
7264          add : function(o) {
7265              var t = this, co;
7266  
7267              o = t.parent(o);
7268  
7269              if (t.isRendered && (co = DOM.get('menu_' + t.id)))
7270                  t._add(DOM.select('tbody', co)[0], o);
7271  
7272              return o;
7273          },
7274  
7275          collapse : function(d) {
7276              this.parent(d);
7277              this.hideMenu(1);
7278          },
7279  
7280          remove : function(o) {
7281              DOM.remove(o.id);
7282              this.destroy();
7283  
7284              return this.parent(o);
7285          },
7286  
7287          destroy : function() {
7288              var t = this, co = DOM.get('menu_' + t.id);
7289  
7290              Event.remove(co, 'mouseover', t.mouseOverFunc);
7291              Event.remove(co, 'click', t.mouseClickFunc);
7292  
7293              if (t.element)
7294                  t.element.remove();
7295  
7296              DOM.remove(co);
7297          },
7298  
7299          renderNode : function() {
7300              var t = this, s = t.settings, n, tb, co, w;
7301  
7302              w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
7303              co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
7304              t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
7305  
7306              if (s.menu_line)
7307                  DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});
7308  
7309  //            n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
7310              n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
7311              tb = DOM.add(n, 'tbody');
7312  
7313              each(t.items, function(o) {
7314                  t._add(tb, o);
7315              });
7316  
7317              t.rendered = true;
7318  
7319              return w;
7320          },
7321  
7322          // Internal functions
7323  
7324          _keyHandler : function(e) {
7325              var t = this, kc = e.keyCode;
7326  
7327  			function focus(d) {
7328                  var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
7329  
7330                  if (e) {
7331                      t._focusIdx = i;
7332                      e.focus();
7333                  }
7334              };
7335  
7336              switch (kc) {
7337                  case 38:
7338                      focus(-1); // Select first link
7339                      return;
7340  
7341                  case 40:
7342                      focus(1);
7343                      return;
7344  
7345                  case 13:
7346                      return;
7347  
7348                  case 27:
7349                      return this.hideMenu();
7350              }
7351          },
7352  
7353          _add : function(tb, o) {
7354              var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;
7355  
7356              if (s.separator) {
7357                  ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
7358                  DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});
7359  
7360                  if (n = ro.previousSibling)
7361                      DOM.addClass(n, 'mceLast');
7362  
7363                  return;
7364              }
7365  
7366              n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
7367              n = it = DOM.add(n, 'td');
7368              n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});
7369  
7370              DOM.addClass(it, s['class']);
7371  //            n = DOM.add(n, 'span', {'class' : 'item'});
7372  
7373              ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
7374  
7375              if (s.icon_src)
7376                  DOM.add(ic, 'img', {src : s.icon_src});
7377  
7378              n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
7379  
7380              if (o.settings.style)
7381                  DOM.setAttrib(n, 'style', o.settings.style);
7382  
7383              if (tb.childNodes.length == 1)
7384                  DOM.addClass(ro, 'mceFirst');
7385  
7386              if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
7387                  DOM.addClass(ro, 'mceFirst');
7388  
7389              if (o.collapse)
7390                  DOM.addClass(ro, cp + 'ItemSub');
7391  
7392              if (n = ro.previousSibling)
7393                  DOM.removeClass(n, 'mceLast');
7394  
7395              DOM.addClass(ro, 'mceLast');
7396          }
7397      });
7398  })(tinymce);(function(tinymce) {
7399      var DOM = tinymce.DOM;
7400  
7401      tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
7402          Button : function(id, s) {
7403              this.parent(id, s);
7404              this.classPrefix = 'mceButton';
7405          },
7406  
7407          renderHTML : function() {
7408              var cp = this.classPrefix, s = this.settings, h, l;
7409  
7410              l = DOM.encode(s.label || '');
7411              h = '<a id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM.encode(s.title) + '">';
7412  
7413              if (s.image)
7414                  h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
7415              else
7416                  h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';
7417  
7418              return h;
7419          },
7420  
7421          postRender : function() {
7422              var t = this, s = t.settings;
7423  
7424              tinymce.dom.Event.add(t.id, 'click', function(e) {
7425                  if (!t.isDisabled())
7426                      return s.onclick.call(s.scope, e);
7427              });
7428          }
7429      });
7430  })(tinymce);
7431  (function(tinymce) {
7432      var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
7433  
7434      tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
7435          ListBox : function(id, s) {
7436              var t = this;
7437  
7438              t.parent(id, s);
7439  
7440              t.items = [];
7441  
7442              t.onChange = new Dispatcher(t);
7443  
7444              t.onPostRender = new Dispatcher(t);
7445  
7446              t.onAdd = new Dispatcher(t);
7447  
7448              t.onRenderMenu = new tinymce.util.Dispatcher(this);
7449  
7450              t.classPrefix = 'mceListBox';
7451          },
7452  
7453          select : function(va) {
7454              var t = this, fv, f;
7455  
7456              if (va == undefined)
7457                  return t.selectByIndex(-1);
7458  
7459              // Is string or number make function selector
7460              if (va && va.call)
7461                  f = va;
7462              else {
7463                  f = function(v) {
7464                      return v == va;
7465                  };
7466              }
7467  
7468              // Do we need to do something?
7469              if (va != t.selectedValue) {
7470                  // Find item
7471                  each(t.items, function(o, i) {
7472                      if (f(o.value)) {
7473                          fv = 1;
7474                          t.selectByIndex(i);
7475                          return false;
7476                      }
7477                  });
7478  
7479                  if (!fv)
7480                      t.selectByIndex(-1);
7481              }
7482          },
7483  
7484          selectByIndex : function(idx) {
7485              var t = this, e, o;
7486  
7487              if (idx != t.selectedIndex) {
7488                  e = DOM.get(t.id + '_text');
7489                  o = t.items[idx];
7490  
7491                  if (o) {
7492                      t.selectedValue = o.value;
7493                      t.selectedIndex = idx;
7494                      DOM.setHTML(e, DOM.encode(o.title));
7495                      DOM.removeClass(e, 'mceTitle');
7496                  } else {
7497                      DOM.setHTML(e, DOM.encode(t.settings.title));
7498                      DOM.addClass(e, 'mceTitle');
7499                      t.selectedValue = t.selectedIndex = null;
7500                  }
7501  
7502                  e = 0;
7503              }
7504          },
7505  
7506          add : function(n, v, o) {
7507              var t = this;
7508  
7509              o = o || {};
7510              o = tinymce.extend(o, {
7511                  title : n,
7512                  value : v
7513              });
7514  
7515              t.items.push(o);
7516              t.onAdd.dispatch(t, o);
7517          },
7518  
7519          getLength : function() {
7520              return this.items.length;
7521          },
7522  
7523          renderHTML : function() {
7524              var h = '', t = this, s = t.settings, cp = t.classPrefix;
7525  
7526              h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
7527              h += '<td>' + DOM.createHTML('a', {id : t.id + '_text', href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';
7528              h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';
7529              h += '</tr></tbody></table>';
7530  
7531              return h;
7532          },
7533  
7534          showMenu : function() {
7535              var t = this, p1, p2, e = DOM.get(this.id), m;
7536  
7537              if (t.isDisabled() || t.items.length == 0)
7538                  return;
7539  
7540              if (t.menu && t.menu.isMenuVisible)
7541                  return t.hideMenu();
7542  
7543              if (!t.isMenuRendered) {
7544                  t.renderMenu();
7545                  t.isMenuRendered = true;
7546              }
7547  
7548              p1 = DOM.getPos(this.settings.menu_container);
7549              p2 = DOM.getPos(e);
7550  
7551              m = t.menu;
7552              m.settings.offset_x = p2.x;
7553              m.settings.offset_y = p2.y;
7554              m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
7555  
7556              // Select in menu
7557              if (t.oldID)
7558                  m.items[t.oldID].setSelected(0);
7559  
7560              each(t.items, function(o) {
7561                  if (o.value === t.selectedValue) {
7562                      m.items[o.id].setSelected(1);
7563                      t.oldID = o.id;
7564                  }
7565              });
7566  
7567              m.showMenu(0, e.clientHeight);
7568  
7569              Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7570              DOM.addClass(t.id, t.classPrefix + 'Selected');
7571  
7572              //DOM.get(t.id + '_text').focus();
7573          },
7574  
7575          hideMenu : function(e) {
7576              var t = this;
7577  
7578              // Prevent double toogles by canceling the mouse click event to the button
7579              if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
7580                  return;
7581  
7582              if (!e || !DOM.getParent(e.target, '.mceMenu')) {
7583                  DOM.removeClass(t.id, t.classPrefix + 'Selected');
7584                  Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7585  
7586                  if (t.menu)
7587                      t.menu.hideMenu();
7588              }
7589          },
7590  
7591          renderMenu : function() {
7592              var t = this, m;
7593  
7594              m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
7595                  menu_line : 1,
7596                  'class' : t.classPrefix + 'Menu mceNoIcons',
7597                  max_width : 150,
7598                  max_height : 150
7599              });
7600  
7601              m.onHideMenu.add(t.hideMenu, t);
7602  
7603              m.add({
7604                  title : t.settings.title,
7605                  'class' : 'mceMenuItemTitle',
7606                  onclick : function() {
7607                      if (t.settings.onselect('') !== false)
7608                          t.select(''); // Must be runned after
7609                  }
7610              });
7611  
7612              each(t.items, function(o) {
7613                  o.id = DOM.uniqueId();
7614                  o.onclick = function() {
7615                      if (t.settings.onselect(o.value) !== false)
7616                          t.select(o.value); // Must be runned after
7617                  };
7618  
7619                  m.add(o);
7620              });
7621  
7622              t.onRenderMenu.dispatch(t, m);
7623              t.menu = m;
7624          },
7625  
7626          postRender : function() {
7627              var t = this, cp = t.classPrefix;
7628  
7629              Event.add(t.id, 'click', t.showMenu, t);
7630              Event.add(t.id + '_text', 'focus', function(e) {
7631                  if (!t._focused) {
7632                      t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
7633                          var idx = -1, v, kc = e.keyCode;
7634  
7635                          // Find current index
7636                          each(t.items, function(v, i) {
7637                              if (t.selectedValue == v.value)
7638                                  idx = i;
7639                          });
7640  
7641                          // Move up/down
7642                          if (kc == 38)
7643                              v = t.items[idx - 1];
7644                          else if (kc == 40)
7645                              v = t.items[idx + 1];
7646                          else if (kc == 13) {
7647                              // Fake select on enter
7648                              v = t.selectedValue;
7649                              t.selectedValue = null; // Needs to be null to fake change
7650                              t.settings.onselect(v);
7651                              return Event.cancel(e);
7652                          }
7653  
7654                          if (v) {
7655                              t.hideMenu();
7656                              t.select(v.value);
7657                          }
7658                      });
7659                  }
7660  
7661                  t._focused = 1;
7662              });
7663              Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});
7664  
7665              // Old IE doesn't have hover on all elements
7666              if (tinymce.isIE6 || !DOM.boxModel) {
7667                  Event.add(t.id, 'mouseover', function() {
7668                      if (!DOM.hasClass(t.id, cp + 'Disabled'))
7669                          DOM.addClass(t.id, cp + 'Hover');
7670                  });
7671  
7672                  Event.add(t.id, 'mouseout', function() {
7673                      if (!DOM.hasClass(t.id, cp + 'Disabled'))
7674                          DOM.removeClass(t.id, cp + 'Hover');
7675                  });
7676              }
7677  
7678              t.onPostRender.dispatch(t, DOM.get(t.id));
7679          },
7680  
7681          destroy : function() {
7682              this.parent();
7683  
7684              Event.clear(this.id + '_text');
7685              Event.clear(this.id + '_open');
7686          }
7687      });
7688  })(tinymce);(function(tinymce) {
7689      var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
7690  
7691      tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
7692          NativeListBox : function(id, s) {
7693              this.parent(id, s);
7694              this.classPrefix = 'mceNativeListBox';
7695          },
7696  
7697          setDisabled : function(s) {
7698              DOM.get(this.id).disabled = s;
7699          },
7700  
7701          isDisabled : function() {
7702              return DOM.get(this.id).disabled;
7703          },
7704  
7705          select : function(va) {
7706              var t = this, fv, f;
7707  
7708              if (va == undefined)
7709                  return t.selectByIndex(-1);
7710  
7711              // Is string or number make function selector
7712              if (va && va.call)
7713                  f = va;
7714              else {
7715                  f = function(v) {
7716                      return v == va;
7717                  };
7718              }
7719  
7720              // Do we need to do something?
7721              if (va != t.selectedValue) {
7722                  // Find item
7723                  each(t.items, function(o, i) {
7724                      if (f(o.value)) {
7725                          fv = 1;
7726                          t.selectByIndex(i);
7727                          return false;
7728                      }
7729                  });
7730  
7731                  if (!fv)
7732                      t.selectByIndex(-1);
7733              }
7734          },
7735  
7736          selectByIndex : function(idx) {
7737              DOM.get(this.id).selectedIndex = idx + 1;
7738              this.selectedValue = this.items[idx] ? this.items[idx].value : null;
7739          },
7740  
7741          add : function(n, v, a) {
7742              var o, t = this;
7743  
7744              a = a || {};
7745              a.value = v;
7746  
7747              if (t.isRendered())
7748                  DOM.add(DOM.get(this.id), 'option', a, n);
7749  
7750              o = {
7751                  title : n,
7752                  value : v,
7753                  attribs : a
7754              };
7755  
7756              t.items.push(o);
7757              t.onAdd.dispatch(t, o);
7758          },
7759  
7760          getLength : function() {
7761              return DOM.get(this.id).options.length - 1;
7762          },
7763  
7764          renderHTML : function() {
7765              var h, t = this;
7766  
7767              h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');
7768  
7769              each(t.items, function(it) {
7770                  h += DOM.createHTML('option', {value : it.value}, it.title);
7771              });
7772  
7773              h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);
7774  
7775              return h;
7776          },
7777  
7778          postRender : function() {
7779              var t = this, ch;
7780  
7781              t.rendered = true;
7782  
7783  			function onChange(e) {
7784                  var v = t.items[e.target.selectedIndex - 1];
7785  
7786                  if (v && (v = v.value)) {
7787                      t.onChange.dispatch(t, v);
7788  
7789                      if (t.settings.onselect)
7790                          t.settings.onselect(v);
7791                  }
7792              };
7793  
7794              Event.add(t.id, 'change', onChange);
7795  
7796              // Accessibility keyhandler
7797              Event.add(t.id, 'keydown', function(e) {
7798                  var bf;
7799  
7800                  Event.remove(t.id, 'change', ch);
7801  
7802                  bf = Event.add(t.id, 'blur', function() {
7803                      Event.add(t.id, 'change', onChange);
7804                      Event.remove(t.id, 'blur', bf);
7805                  });
7806  
7807                  if (e.keyCode == 13 || e.keyCode == 32) {
7808                      onChange(e);
7809                      return Event.cancel(e);
7810                  }
7811              });
7812  
7813              t.onPostRender.dispatch(t, DOM.get(t.id));
7814          }
7815      });
7816  })(tinymce);(function(tinymce) {
7817      var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7818  
7819      tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
7820          MenuButton : function(id, s) {
7821              this.parent(id, s);
7822  
7823              this.onRenderMenu = new tinymce.util.Dispatcher(this);
7824  
7825              s.menu_container = s.menu_container || DOM.doc.body;
7826          },
7827  
7828          showMenu : function() {
7829              var t = this, p1, p2, e = DOM.get(t.id), m;
7830  
7831              if (t.isDisabled())
7832                  return;
7833  
7834              if (!t.isMenuRendered) {
7835                  t.renderMenu();
7836                  t.isMenuRendered = true;
7837              }
7838  
7839              if (t.isMenuVisible)
7840                  return t.hideMenu();
7841  
7842              p1 = DOM.getPos(t.settings.menu_container);
7843              p2 = DOM.getPos(e);
7844  
7845              m = t.menu;
7846              m.settings.offset_x = p2.x;
7847              m.settings.offset_y = p2.y;
7848              m.settings.vp_offset_x = p2.x;
7849              m.settings.vp_offset_y = p2.y;
7850              m.settings.keyboard_focus = t._focused;
7851              m.showMenu(0, e.clientHeight);
7852  
7853              Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7854              t.setState('Selected', 1);
7855  
7856              t.isMenuVisible = 1;
7857          },
7858  
7859          renderMenu : function() {
7860              var t = this, m;
7861  
7862              m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
7863                  menu_line : 1,
7864                  'class' : this.classPrefix + 'Menu',
7865                  icons : t.settings.icons
7866              });
7867  
7868              m.onHideMenu.add(t.hideMenu, t);
7869  
7870              t.onRenderMenu.dispatch(t, m);
7871              t.menu = m;
7872          },
7873  
7874          hideMenu : function(e) {
7875              var t = this;
7876  
7877              // Prevent double toogles by canceling the mouse click event to the button
7878              if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
7879                  return;
7880  
7881              if (!e || !DOM.getParent(e.target, '.mceMenu')) {
7882                  t.setState('Selected', 0);
7883                  Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7884                  if (t.menu)
7885                      t.menu.hideMenu();
7886              }
7887  
7888              t.isMenuVisible = 0;
7889          },
7890  
7891          postRender : function() {
7892              var t = this, s = t.settings;
7893  
7894              Event.add(t.id, 'click', function() {
7895                  if (!t.isDisabled()) {
7896                      if (s.onclick)
7897                          s.onclick(t.value);
7898  
7899                      t.showMenu();
7900                  }
7901              });
7902          }
7903      });
7904  })(tinymce);
7905  (function(tinymce) {
7906      var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7907  
7908      tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
7909          SplitButton : function(id, s) {
7910              this.parent(id, s);
7911              this.classPrefix = 'mceSplitButton';
7912          },
7913  
7914          renderHTML : function() {
7915              var h, t = this, s = t.settings, h1;
7916  
7917              h = '<tbody><tr>';
7918  
7919              if (s.image)
7920                  h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
7921              else
7922                  h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');
7923  
7924              h += '<td>' + DOM.createHTML('a', {id : t.id + '_action', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';
7925      
7926              h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});
7927              h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';
7928  
7929              h += '</tr></tbody>';
7930  
7931              return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);
7932          },
7933  
7934          postRender : function() {
7935              var t = this, s = t.settings;
7936  
7937              if (s.onclick) {
7938                  Event.add(t.id + '_action', 'click', function() {
7939                      if (!t.isDisabled())
7940                          s.onclick(t.value);
7941                  });
7942              }
7943  
7944              Event.add(t.id + '_open', 'click', t.showMenu, t);
7945              Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});
7946              Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});
7947  
7948              // Old IE doesn't have hover on all elements
7949              if (tinymce.isIE6 || !DOM.boxModel) {
7950                  Event.add(t.id, 'mouseover', function() {
7951                      if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7952                          DOM.addClass(t.id, 'mceSplitButtonHover');
7953                  });
7954  
7955                  Event.add(t.id, 'mouseout', function() {
7956                      if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7957                          DOM.removeClass(t.id, 'mceSplitButtonHover');
7958                  });
7959              }
7960          },
7961  
7962          destroy : function() {
7963              this.parent();
7964  
7965              Event.clear(this.id + '_action');
7966              Event.clear(this.id + '_open');
7967          }
7968      });
7969  })(tinymce);
7970  (function(tinymce) {
7971      var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;
7972  
7973      tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
7974          ColorSplitButton : function(id, s) {
7975              var t = this;
7976  
7977              t.parent(id, s);
7978  
7979              t.settings = s = tinymce.extend({
7980                  colors : '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF',
7981                  grid_width : 8,
7982                  default_color : '#888888'
7983              }, t.settings);
7984  
7985              t.onShowMenu = new tinymce.util.Dispatcher(t);
7986  
7987              t.onHideMenu = new tinymce.util.Dispatcher(t);
7988  
7989              t.value = s.default_color;
7990          },
7991  
7992          showMenu : function() {
7993              var t = this, r, p, e, p2;
7994  
7995              if (t.isDisabled())
7996                  return;
7997  
7998              if (!t.isMenuRendered) {
7999                  t.renderMenu();
8000                  t.isMenuRendered = true;
8001              }
8002  
8003              if (t.isMenuVisible)
8004                  return t.hideMenu();
8005  
8006              e = DOM.get(t.id);
8007              DOM.show(t.id + '_menu');
8008              DOM.addClass(e, 'mceSplitButtonSelected');
8009              p2 = DOM.getPos(e);
8010              DOM.setStyles(t.id + '_menu', {
8011                  left : p2.x,
8012                  top : p2.y + e.clientHeight,
8013                  zIndex : 200000
8014              });
8015              e = 0;
8016  
8017              Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
8018              t.onShowMenu.dispatch(t);
8019  
8020              if (t._focused) {
8021                  t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
8022                      if (e.keyCode == 27)
8023                          t.hideMenu();
8024                  });
8025  
8026                  DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
8027              }
8028  
8029              t.isMenuVisible = 1;
8030          },
8031  
8032          hideMenu : function(e) {
8033              var t = this;
8034  
8035              // Prevent double toogles by canceling the mouse click event to the button
8036              if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
8037                  return;
8038  
8039              if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
8040                  DOM.removeClass(t.id, 'mceSplitButtonSelected');
8041                  Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
8042                  Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
8043                  DOM.hide(t.id + '_menu');
8044              }
8045  
8046              t.onHideMenu.dispatch(t);
8047  
8048              t.isMenuVisible = 0;
8049          },
8050  
8051          renderMenu : function() {
8052              var t = this, m, i = 0, s = t.settings, n, tb, tr, w;
8053  
8054              w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
8055              m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
8056              DOM.add(m, 'span', {'class' : 'mceMenuLine'});
8057  
8058              n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});
8059              tb = DOM.add(n, 'tbody');
8060  
8061              // Generate color grid
8062              i = 0;
8063              each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
8064                  c = c.replace(/^#/, '');
8065  
8066                  if (!i--) {
8067                      tr = DOM.add(tb, 'tr');
8068                      i = s.grid_width - 1;
8069                  }
8070  
8071                  n = DOM.add(tr, 'td');
8072  
8073                  n = DOM.add(n, 'a', {
8074                      href : 'javascript:;',
8075                      style : {
8076                          backgroundColor : '#' + c
8077                      },
8078                      mce_color : '#' + c
8079                  });
8080              });
8081  
8082              if (s.more_colors_func) {
8083                  n = DOM.add(tb, 'tr');
8084                  n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
8085                  n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);
8086  
8087                  Event.add(n, 'click', function(e) {
8088                      s.more_colors_func.call(s.more_colors_scope || this);
8089                      return Event.cancel(e); // Cancel to fix onbeforeunload problem
8090                  });
8091              }
8092  
8093              DOM.addClass(m, 'mceColorSplitMenu');
8094  
8095              Event.add(t.id + '_menu', 'click', function(e) {
8096                  var c;
8097  
8098                  e = e.target;
8099  
8100                  if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))
8101                      t.setColor(c);
8102  
8103                  return Event.cancel(e); // Prevent IE auto save warning
8104              });
8105  
8106              return w;
8107          },
8108  
8109          setColor : function(c) {
8110              var t = this;
8111  
8112              DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
8113  
8114              t.value = c;
8115              t.hideMenu();
8116              t.settings.onselect(c);
8117          },
8118  
8119          postRender : function() {
8120              var t = this, id = t.id;
8121  
8122              t.parent();
8123              DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
8124              DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);
8125          },
8126  
8127          destroy : function() {
8128              this.parent();
8129  
8130              Event.clear(this.id + '_menu');
8131              Event.clear(this.id + '_more');
8132              DOM.remove(this.id + '_menu');
8133          }
8134      });
8135  })(tinymce);
8136  tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
8137      renderHTML : function() {
8138          var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;
8139  
8140          cl = t.controls;
8141          for (i=0; i<cl.length; i++) {
8142              // Get current control, prev control, next control and if the control is a list box or not
8143              co = cl[i];
8144              pr = cl[i - 1];
8145              nx = cl[i + 1];
8146  
8147              // Add toolbar start
8148              if (i === 0) {
8149                  c = 'mceToolbarStart';
8150  
8151                  if (co.Button)
8152                      c += ' mceToolbarStartButton';
8153                  else if (co.SplitButton)
8154                      c += ' mceToolbarStartSplitButton';
8155                  else if (co.ListBox)
8156                      c += ' mceToolbarStartListBox';
8157  
8158                  h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
8159              }
8160  
8161              // Add toolbar end before list box and after the previous button
8162              // This is to fix the o2k7 editor skins
8163              if (pr && co.ListBox) {
8164                  if (pr.Button || pr.SplitButton)
8165                      h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
8166              }
8167  
8168              // Render control HTML
8169  
8170              // IE 8 quick fix, needed to propertly generate a hit area for anchors
8171              if (dom.stdMode)
8172                  h += '<td style="position: relative">' + co.renderHTML() + '</td>';
8173              else
8174                  h += '<td>' + co.renderHTML() + '</td>';
8175  
8176              // Add toolbar start after list box and before the next button
8177              // This is to fix the o2k7 editor skins
8178              if (nx && co.ListBox) {
8179                  if (nx.Button || nx.SplitButton)
8180                      h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
8181              }
8182          }
8183  
8184          c = 'mceToolbarEnd';
8185  
8186          if (co.Button)
8187              c += ' mceToolbarEndButton';
8188          else if (co.SplitButton)
8189              c += ' mceToolbarEndSplitButton';
8190          else if (co.ListBox)
8191              c += ' mceToolbarEndListBox';
8192  
8193          h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
8194  
8195          return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>');
8196      }
8197  });
8198  (function(tinymce) {
8199      var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
8200  
8201      tinymce.create('tinymce.AddOnManager', {
8202          items : [],
8203          urls : {},
8204          lookup : {},
8205  
8206          onAdd : new Dispatcher(this),
8207  
8208          get : function(n) {
8209              return this.lookup[n];
8210          },
8211  
8212          requireLangPack : function(n) {
8213              var u, s = tinymce.EditorManager.settings;
8214  
8215              if (s && s.language) {
8216                  u = this.urls[n] + '/langs/' + s.language + '.js';
8217  
8218                  if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
8219                      tinymce.ScriptLoader.load(u);
8220                  else
8221                      tinymce.ScriptLoader.add(u);
8222              }
8223          },
8224  
8225          add : function(id, o) {
8226              this.items.push(o);
8227              this.lookup[id] = o;
8228              this.onAdd.dispatch(this, id, o);
8229  
8230              return o;
8231          },
8232  
8233          load : function(n, u, cb, s) {
8234              var t = this;
8235  
8236              if (t.urls[n])
8237                  return;
8238  
8239              if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
8240                  u = tinymce.baseURL + '/' +  u;
8241  
8242              t.urls[n] = u.substring(0, u.lastIndexOf('/'));
8243              tinymce.ScriptLoader.add(u, cb, s);
8244          }
8245      });
8246  
8247      // Create plugin and theme managers
8248      tinymce.PluginManager = new tinymce.AddOnManager();
8249      tinymce.ThemeManager = new tinymce.AddOnManager();
8250  }(tinymce));
8251  
8252  (function(tinymce) {
8253      // Shorten names
8254      var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;
8255  
8256      tinymce.create('static tinymce.EditorManager', {
8257          editors : {},
8258  
8259          i18n : {},
8260      
8261          activeEditor : null,
8262  
8263          preInit : function() {
8264              var t = this, lo = window.location;
8265  
8266              // Setup some URLs where the editor API is located and where the document is
8267              tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
8268              if (!/[\/\\]$/.test(tinymce.documentBaseURL))
8269                  tinymce.documentBaseURL += '/';
8270  
8271              tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
8272              tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);
8273  
8274              // Add before unload listener
8275              // This was required since IE was leaking memory if you added and removed beforeunload listeners
8276              // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
8277              t.onBeforeUnload = new tinymce.util.Dispatcher(t);
8278  
8279              // Must be on window or IE will leak if the editor is placed in frame or iframe
8280              Event.add(window, 'beforeunload', function(e) {
8281                  t.onBeforeUnload.dispatch(t, e);
8282              });
8283          },
8284  
8285          init : function(s) {
8286              var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;
8287  
8288  			function execCallback(se, n, s) {
8289                  var f = se[n];
8290  
8291                  if (!f)
8292                      return;
8293  
8294                  if (tinymce.is(f, 'string')) {
8295                      s = f.replace(/\.\w+$/, '');
8296                      s = s ? tinymce.resolve(s) : 0;
8297                      f = tinymce.resolve(f);
8298                  }
8299  
8300                  return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
8301              };
8302  
8303              s = extend({
8304                  theme : "simple",
8305                  language : "en",
8306                  strict_loading_mode : document.contentType == 'application/xhtml+xml'
8307              }, s);
8308  
8309              t.settings = s;
8310  
8311              // If page not loaded and strict mode isn't enabled then load them
8312              if (!Event.domLoaded && !s.strict_loading_mode) {
8313                  // Load language
8314                  if (s.language)
8315                      sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
8316  
8317                  // Load theme
8318                  if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
8319                      ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
8320  
8321                  // Load plugins
8322                  if (s.plugins) {
8323                      pl = explode(s.plugins);
8324  
8325                      // Load rest if plugins
8326                      each(pl, function(v) {
8327                          if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
8328                              // Skip safari plugin for other browsers
8329                              if (!tinymce.isWebKit && v == 'safari')
8330                                  return;
8331  
8332                              PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
8333                          }
8334                      });
8335                  }
8336  
8337                  sl.loadQueue();
8338              }
8339  
8340              // Legacy call
8341              Event.add(document, 'init', function() {
8342                  var l, co;
8343  
8344                  execCallback(s, 'onpageload');
8345  
8346                  // Verify that it's a valid browser
8347                  if (s.browsers) {
8348                      l = false;
8349  
8350                      each(explode(s.browsers), function(v) {
8351                          switch (v) {
8352                              case 'ie':
8353                              case 'msie':
8354                                  if (tinymce.isIE)
8355                                      l = true;
8356                                  break;
8357  
8358                              case 'gecko':
8359                                  if (tinymce.isGecko)
8360                                      l = true;
8361                                  break;
8362  
8363                              case 'safari':
8364                              case 'webkit':
8365                                  if (tinymce.isWebKit)
8366                                      l = true;
8367                                  break;
8368  
8369                              case 'opera':
8370                                  if (tinymce.isOpera)
8371                                      l = true;
8372  
8373                                  break;
8374                          }
8375                      });
8376  
8377                      // Not a valid one
8378                      if (!l)
8379                          return;
8380                  }
8381  
8382                  switch (s.mode) {
8383                      case "exact":
8384                          l = s.elements || '';
8385  
8386                          if(l.length > 0) {
8387                              each(explode(l), function(v) {
8388                                  if (DOM.get(v)) {
8389                                      ed = new tinymce.Editor(v, s);
8390                                      el.push(ed);
8391                                      ed.render(1);
8392                                  } else {
8393                                      c = 0;
8394  
8395                                      each(document.forms, function(f) {
8396                                          each(f.elements, function(e) {
8397                                              if (e.name === v) {
8398                                                  v = 'mce_editor_' + c;
8399                                                  DOM.setAttrib(e, 'id', v);
8400  
8401                                                  ed = new tinymce.Editor(v, s);
8402                                                  el.push(ed);
8403                                                  ed.render(1);
8404                                              }
8405                                          });
8406                                      });
8407                                  }
8408                              });
8409                          }
8410                          break;
8411  
8412                      case "textareas":
8413                      case "specific_textareas":
8414  						function hasClass(n, c) {
8415                              return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
8416                          };
8417  
8418                          each(DOM.select('textarea'), function(v) {
8419                              if (s.editor_deselector && hasClass(v, s.editor_deselector))
8420                                  return;
8421  
8422                              if (!s.editor_selector || hasClass(v, s.editor_selector)) {
8423                                  // Can we use the name
8424                                  e = DOM.get(v.name);
8425                                  if (!v.id && !e)
8426                                      v.id = v.name;
8427  
8428                                  // Generate unique name if missing or already exists
8429                                  if (!v.id || t.get(v.id))
8430                                      v.id = DOM.uniqueId();
8431  
8432                                  ed = new tinymce.Editor(v.id, s);
8433                                  el.push(ed);
8434                                  ed.render(1);
8435                              }
8436                          });
8437                          break;
8438                  }
8439  
8440                  // Call onInit when all editors are initialized
8441                  if (s.oninit) {
8442                      l = co = 0;
8443  
8444                      each (el, function(ed) {
8445                          co++;
8446  
8447                          if (!ed.initialized) {
8448                              // Wait for it
8449                              ed.onInit.add(function() {
8450                                  l++;
8451  
8452                                  // All done
8453                                  if (l == co)
8454                                      execCallback(s, 'oninit');
8455                              });
8456                          } else
8457                              l++;
8458  
8459                          // All done
8460                          if (l == co)
8461                              execCallback(s, 'oninit');                    
8462                      });
8463                  }
8464              });
8465          },
8466  
8467          get : function(id) {
8468              return this.editors[id];
8469          },
8470  
8471          getInstanceById : function(id) {
8472              return this.get(id);
8473          },
8474  
8475          add : function(e) {
8476              this.editors[e.id] = e;
8477              this._setActive(e);
8478  
8479              return e;
8480          },
8481  
8482          remove : function(e) {
8483              var t = this;
8484  
8485              // Not in the collection
8486              if (!t.editors[e.id])
8487                  return null;
8488  
8489              delete t.editors[e.id];
8490  
8491              // Select another editor since the active one was removed
8492              if (t.activeEditor == e) {
8493                  t._setActive(null);
8494  
8495                  each(t.editors, function(e) {
8496                      t._setActive(e);
8497                      return false; // Break
8498                  });
8499              }
8500  
8501              e.destroy();
8502  
8503              return e;
8504          },
8505  
8506          execCommand : function(c, u, v) {
8507              var t = this, ed = t.get(v), w;
8508  
8509              // Manager commands
8510              switch (c) {
8511                  case "mceFocus":
8512                      ed.focus();
8513                      return true;
8514  
8515                  case "mceAddEditor":
8516                  case "mceAddControl":
8517                      if (!t.get(v))
8518                          new tinymce.Editor(v, t.settings).render();
8519  
8520                      return true;
8521  
8522                  case "mceAddFrameControl":
8523                      w = v.window;
8524  
8525                      // Add tinyMCE global instance and tinymce namespace to specified window
8526                      w.tinyMCE = tinyMCE;
8527                      w.tinymce = tinymce;
8528  
8529                      tinymce.DOM.doc = w.document;
8530                      tinymce.DOM.win = w;
8531  
8532                      ed = new tinymce.Editor(v.element_id, v);
8533                      ed.render();
8534  
8535                      // Fix IE memory leaks
8536                      if (tinymce.isIE) {
8537  						function clr() {
8538                              ed.destroy();
8539                              w.detachEvent('onunload', clr);
8540                              w = w.tinyMCE = w.tinymce = null; // IE leak
8541                          };
8542  
8543                          w.attachEvent('onunload', clr);
8544                      }
8545  
8546                      v.page_window = null;
8547  
8548                      return true;
8549  
8550                  case "mceRemoveEditor":
8551                  case "mceRemoveControl":
8552                      if (ed)
8553                          ed.remove();
8554  
8555                      return true;
8556  
8557                  case 'mceToggleEditor':
8558                      if (!ed) {
8559                          t.execCommand('mceAddControl', 0, v);
8560                          return true;
8561                      }
8562  
8563                      if (ed.isHidden())
8564                          ed.show();
8565                      else
8566                          ed.hide();
8567  
8568                      return true;
8569              }
8570  
8571              // Run command on active editor
8572              if (t.activeEditor)
8573                  return t.activeEditor.execCommand(c, u, v);
8574  
8575              return false;
8576          },
8577  
8578          execInstanceCommand : function(id, c, u, v) {
8579              var ed = this.get(id);
8580  
8581              if (ed)
8582                  return ed.execCommand(c, u, v);
8583  
8584              return false;
8585          },
8586  
8587          triggerSave : function() {
8588              each(this.editors, function(e) {
8589                  e.save();
8590              });
8591          },
8592  
8593          addI18n : function(p, o) {
8594              var lo, i18n = this.i18n;
8595  
8596              if (!tinymce.is(p, 'string')) {
8597                  each(p, function(o, lc) {
8598                      each(o, function(o, g) {
8599                          each(o, function(o, k) {
8600                              if (g === 'common')
8601                                  i18n[lc + '.' + k] = o;
8602                              else
8603                                  i18n[lc + '.' + g + '.' + k] = o;
8604                          });
8605                      });
8606                  });
8607              } else {
8608                  each(o, function(o, k) {
8609                      i18n[p + '.' + k] = o;
8610                  });
8611              }
8612          },
8613  
8614          // Private methods
8615  
8616          _setActive : function(e) {
8617              this.selectedInstance = this.activeEditor = e;
8618          }
8619      });
8620  
8621      tinymce.EditorManager.preInit();
8622  })(tinymce);
8623  
8624  var tinyMCE = window.tinyMCE = tinymce.EditorManager;
8625  (function(tinymce) {
8626      var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
8627      var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
8628      var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
8629      var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
8630  
8631      tinymce.create('tinymce.Editor', {
8632          Editor : function(id, s) {
8633              var t = this;
8634  
8635              t.id = t.editorId = id;
8636  
8637              t.execCommands = {};
8638              t.queryStateCommands = {};
8639              t.queryValueCommands = {};
8640  
8641              t.isNotDirty = false;
8642  
8643              t.plugins = {};
8644  
8645              // Add events to the editor
8646              each([
8647                  'onPreInit',
8648  
8649                  'onBeforeRenderUI',
8650  
8651                  'onPostRender',
8652  
8653                  'onInit',
8654  
8655                  'onRemove',
8656  
8657                  'onActivate',
8658  
8659                  'onDeactivate',
8660  
8661                  'onClick',
8662  
8663                  'onEvent',
8664  
8665                  'onMouseUp',
8666  
8667                  'onMouseDown',
8668  
8669                  'onDblClick',
8670  
8671                  'onKeyDown',
8672  
8673                  'onKeyUp',
8674  
8675                  'onKeyPress',
8676  
8677                  'onContextMenu',
8678  
8679                  'onSubmit',
8680  
8681                  'onReset',
8682  
8683                  'onPaste',
8684  
8685                  'onPreProcess',
8686  
8687                  'onPostProcess',
8688  
8689                  'onBeforeSetContent',
8690  
8691                  'onBeforeGetContent',
8692  
8693                  'onSetContent',
8694  
8695                  'onGetContent',
8696  
8697                  'onLoadContent',
8698  
8699                  'onSaveContent',
8700  
8701                  'onNodeChange',
8702  
8703                  'onChange',
8704  
8705                  'onBeforeExecCommand',
8706  
8707                  'onExecCommand',
8708  
8709                  'onUndo',
8710  
8711                  'onRedo',
8712  
8713                  'onVisualAid',
8714  
8715                  'onSetProgressState'
8716              ], function(e) {
8717                  t[e] = new Dispatcher(t);
8718              });
8719  
8720              t.settings = s = extend({
8721                  id : id,
8722                  language : 'en',
8723                  docs_language : 'en',
8724                  theme : 'simple',
8725                  skin : 'default',
8726                  delta_width : 0,
8727                  delta_height : 0,
8728                  popup_css : '',
8729                  plugins : '',
8730                  document_base_url : tinymce.documentBaseURL,
8731                  add_form_submit_trigger : 1,
8732                  submit_patch : 1,
8733                  add_unload_trigger : 1,
8734                  convert_urls : 1,
8735                  relative_urls : 1,
8736                  remove_script_host : 1,
8737                  table_inline_editing : 0,
8738                  object_resizing : 1,
8739                  cleanup : 1,
8740                  accessibility_focus : 1,
8741                  custom_shortcuts : 1,
8742                  custom_undo_redo_keyboard_shortcuts : 1,
8743                  custom_undo_redo_restore_selection : 1,
8744                  custom_undo_redo : 1,
8745                  doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
8746                  visual_table_class : 'mceItemTable',
8747                  visual : 1,
8748                  inline_styles : true,
8749                  convert_fonts_to_spans : true,
8750                  font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
8751                  apply_source_formatting : 1,
8752                  directionality : 'ltr',
8753                  forced_root_block : 'p',
8754                  valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',
8755                  hidden_input : 1,
8756                  padd_empty_editor : 1,
8757                  render_ui : 1,
8758                  init_theme : 1,
8759                  force_p_newlines : 1,
8760                  indentation : '30px',
8761                  keep_styles : 1,
8762                  fix_table_elements : 1,
8763                  removeformat_selector : 'span,b,strong,em,i,font,u,strike'
8764              }, s);
8765  
8766              t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
8767                  base_uri : tinyMCE.baseURI
8768              });
8769  
8770              t.baseURI = EditorManager.baseURI;
8771  
8772              // Call setup
8773              t.execCallback('setup', t);
8774          },
8775  
8776          render : function(nst) {
8777              var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
8778  
8779              // Page is not loaded yet, wait for it
8780              if (!Event.domLoaded) {
8781                  Event.add(document, 'init', function() {
8782                      t.render();
8783                  });
8784                  return;
8785              }
8786  
8787              // Force strict loading mode if render us called by user and not internally
8788              if (!nst) {
8789                  s.strict_loading_mode = 1;
8790                  tinyMCE.settings = s;
8791              }
8792  
8793              // Element not found, then skip initialization
8794              if (!t.getElement())
8795                  return;
8796  
8797              if (s.strict_loading_mode) {
8798                  sl.settings.strict_mode = s.strict_loading_mode;
8799                  tinymce.DOM.settings.strict = 1;
8800              }
8801  
8802              // Add hidden input for non input elements inside form elements
8803              if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
8804                  DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
8805  
8806              if (tinymce.WindowManager)
8807                  t.windowManager = new tinymce.WindowManager(t);
8808  
8809              if (s.encoding == 'xml') {
8810                  t.onGetContent.add(function(ed, o) {
8811                      if (o.save)
8812                          o.content = DOM.encode(o.content);
8813                  });
8814              }
8815  
8816              if (s.add_form_submit_trigger) {
8817                  t.onSubmit.addToTop(function() {
8818                      if (t.initialized) {
8819                          t.save();
8820                          t.isNotDirty = 1;
8821                      }
8822                  });
8823              }
8824  
8825              if (s.add_unload_trigger) {
8826                  t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
8827                      if (t.initialized && !t.destroyed && !t.isHidden())
8828                          t.save({format : 'raw', no_events : true});
8829                  });
8830              }
8831  
8832              tinymce.addUnload(t.destroy, t);
8833  
8834              if (s.submit_patch) {
8835                  t.onBeforeRenderUI.add(function() {
8836                      var n = t.getElement().form;
8837  
8838                      if (!n)
8839                          return;
8840  
8841                      // Already patched
8842                      if (n._mceOldSubmit)
8843                          return;
8844  
8845                      // Check page uses id="submit" or name="submit" for it's submit button
8846                      if (!n.submit.nodeType && !n.submit.length) {
8847                          t.formElement = n;
8848                          n._mceOldSubmit = n.submit;
8849                          n.submit = function() {
8850                              // Save all instances
8851                              EditorManager.triggerSave();
8852                              t.isNotDirty = 1;
8853  
8854                              return t.formElement._mceOldSubmit(t.formElement);
8855                          };
8856                      }
8857  
8858                      n = null;
8859                  });
8860              }
8861  
8862              // Load scripts
8863  			function loadScripts() {
8864                  if (s.language)
8865                      sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
8866  
8867                  if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
8868                      ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
8869  
8870                  each(explode(s.plugins), function(p) {
8871                      if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
8872                          // Skip safari plugin for other browsers
8873                          if (!isWebKit && p == 'safari')
8874                              return;
8875  
8876                          PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
8877                      }
8878                  });
8879  
8880                  // Init when que is loaded
8881                  sl.loadQueue(function() {
8882                      if (!t.removed)
8883                          t.init();
8884                  });
8885              };
8886  
8887              loadScripts();
8888          },
8889  
8890          init : function() {
8891              var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
8892  
8893              EditorManager.add(t);
8894  
8895              if (s.theme) {
8896                  s.theme = s.theme.replace(/-/, '');
8897                  o = ThemeManager.get(s.theme);
8898                  t.theme = new o();
8899  
8900                  if (t.theme.init && s.init_theme)
8901                      t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
8902              }
8903  
8904              // Create all plugins
8905              each(explode(s.plugins.replace(/\-/g, '')), function(p) {
8906                  var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
8907  
8908                  if (c) {
8909                      po = new c(t, u);
8910  
8911                      t.plugins[p] = po;
8912  
8913                      if (po.init)
8914                          po.init(t, u);
8915                  }
8916              });
8917  
8918              // Setup popup CSS path(s)
8919              if (s.popup_css !== false) {
8920                  if (s.popup_css)
8921                      s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
8922                  else
8923                      s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
8924              }
8925  
8926              if (s.popup_css_add)
8927                  s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
8928  
8929              t.controlManager = new tinymce.ControlManager(t);
8930  
8931              t.undoManager = new tinymce.UndoManager(t);
8932  
8933              // Pass through
8934              t.undoManager.onAdd.add(function(um, l) {
8935                  if (!l.initial)
8936                      return t.onChange.dispatch(t, l, um);
8937              });
8938  
8939              t.undoManager.onUndo.add(function(um, l) {
8940                  return t.onUndo.dispatch(t, l, um);
8941              });
8942  
8943              t.undoManager.onRedo.add(function(um, l) {
8944                  return t.onRedo.dispatch(t, l, um);
8945              });
8946  
8947              if (s.custom_undo_redo) {
8948                  t.onExecCommand.add(function(ed, cmd, ui, val, a) {
8949                      if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
8950                          t.undoManager.add();
8951                  });
8952              }
8953  
8954              t.onExecCommand.add(function(ed, c) {
8955                  // Don't refresh the select lists until caret move
8956                  if (!/^(FontName|FontSize)$/.test(c))
8957                      t.nodeChanged();
8958              });
8959  
8960              // Remove ghost selections on images and tables in Gecko
8961              if (isGecko) {
8962  				function repaint(a, o) {
8963                      if (!o || !o.initial)
8964                          t.execCommand('mceRepaint');
8965                  };
8966  
8967                  t.onUndo.add(repaint);
8968                  t.onRedo.add(repaint);
8969                  t.onSetContent.add(repaint);
8970              }
8971  
8972              // Enables users to override the control factory
8973              t.onBeforeRenderUI.dispatch(t, t.controlManager);
8974  
8975              // Measure box
8976              if (s.render_ui) {
8977                  w = s.width || e.style.width || e.offsetWidth;
8978                  h = s.height || e.style.height || e.offsetHeight;
8979                  t.orgDisplay = e.style.display;
8980                  re = /^[0-9\.]+(|px)$/i;
8981  
8982                  if (re.test('' + w))
8983                      w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
8984  
8985                  if (re.test('' + h))
8986                      h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
8987  
8988                  // Render UI
8989                  o = t.theme.renderUI({
8990                      targetNode : e,
8991                      width : w,
8992                      height : h,
8993                      deltaWidth : s.delta_width,
8994                      deltaHeight : s.delta_height
8995                  });
8996  
8997                  t.editorContainer = o.editorContainer;
8998              }
8999  
9000  
9001              // User specified a document.domain value
9002              if (document.domain && location.hostname != document.domain)
9003                  tinymce.relaxedDomain = document.domain;
9004  
9005              // Resize editor
9006              DOM.setStyles(o.sizeContainer || o.editorContainer, {
9007                  width : w,
9008                  height : h
9009              });
9010  
9011              h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
9012              if (h < 100)
9013                  h = 100;
9014  
9015              t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
9016  
9017              // We only need to override paths if we have to
9018              // IE has a bug where it remove site absolute urls to relative ones if this is specified
9019              if (s.document_base_url != tinymce.documentBaseURL)
9020                  t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
9021  
9022              t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
9023  
9024              if (tinymce.relaxedDomain)
9025                  t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';
9026  
9027              bi = s.body_id || 'tinymce';
9028              if (bi.indexOf('=') != -1) {
9029                  bi = t.getParam('body_id', '', 'hash');
9030                  bi = bi[t.id] || bi;
9031              }
9032  
9033              bc = s.body_class || '';
9034              if (bc.indexOf('=') != -1) {
9035                  bc = t.getParam('body_class', '', 'hash');
9036                  bc = bc[t.id] || '';
9037              }
9038  
9039              t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
9040  
9041              // Domain relaxing enabled, then set document domain
9042              if (tinymce.relaxedDomain) {
9043                  // We need to write the contents here in IE since multiple writes messes up refresh button and back button
9044                  if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))
9045                      u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';
9046                  else if (tinymce.isOpera)
9047                      u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                    
9048              }
9049  
9050              // Create iframe
9051              n = DOM.add(o.iframeContainer, 'iframe', {
9052                  id : t.id + "_ifr",
9053                  src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
9054                  frameBorder : '0',
9055                  style : {
9056                      width : '100%',
9057                      height : h
9058                  }
9059              });
9060  
9061              t.contentAreaContainer = o.iframeContainer;
9062              DOM.get(o.editorContainer).style.display = t.orgDisplay;
9063              DOM.get(t.id).style.display = 'none';
9064  
9065              if (!isIE || !tinymce.relaxedDomain)
9066                  t.setupIframe();
9067  
9068              e = n = o = null; // Cleanup
9069          },
9070  
9071          setupIframe : function() {
9072              var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
9073  
9074              // Setup iframe body
9075              if (!isIE || !tinymce.relaxedDomain) {
9076                  d.open();
9077                  d.write(t.iframeHTML);
9078                  d.close();
9079              }
9080  
9081              // Design mode needs to be added here Ctrl+A will fail otherwise
9082              if (!isIE) {
9083                  try {
9084                      if (!s.readonly)
9085                          d.designMode = 'On';
9086                  } catch (ex) {
9087                      // Will fail on Gecko if the editor is placed in an hidden container element
9088                      // The design mode will be set ones the editor is focused
9089                  }
9090              }
9091  
9092              // IE needs to use contentEditable or it will display non secure items for HTTPS
9093              if (isIE) {
9094                  // It will not steal focus if we hide it while setting contentEditable
9095                  b = t.getBody();
9096                  DOM.hide(b);
9097  
9098                  if (!s.readonly)
9099                      b.contentEditable = true;
9100  
9101                  DOM.show(b);
9102              }
9103  
9104              t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
9105                  keep_values : true,
9106                  url_converter : t.convertURL,
9107                  url_converter_scope : t,
9108                  hex_colors : s.force_hex_style_colors,
9109                  class_filter : s.class_filter,
9110                  update_styles : 1,
9111                  fix_ie_paragraphs : 1
9112              });
9113  
9114              t.serializer = new tinymce.dom.Serializer(extend(s, {
9115                  valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
9116                  dom : t.dom
9117              }));
9118  
9119              t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
9120  
9121              t.forceBlocks = new tinymce.ForceBlocks(t, {
9122                  forced_root_block : s.forced_root_block
9123              });
9124              t.editorCommands = new tinymce.EditorCommands(t);
9125  
9126              // Pass through
9127              t.serializer.onPreProcess.add(function(se, o) {
9128                  return t.onPreProcess.dispatch(t, o, se);
9129              });
9130  
9131              t.serializer.onPostProcess.add(function(se, o) {
9132                  return t.onPostProcess.dispatch(t, o, se);
9133              });
9134  
9135              t.onPreInit.dispatch(t);
9136  
9137              if (!s.gecko_spellcheck)
9138                  t.getBody().spellcheck = 0;
9139  
9140              if (!s.readonly)
9141                  t._addEvents();
9142  
9143              t.controlManager.onPostRender.dispatch(t, t.controlManager);
9144              t.onPostRender.dispatch(t);
9145  
9146              if (s.directionality)
9147                  t.getBody().dir = s.directionality;
9148  
9149              if (s.nowrap)
9150                  t.getBody().style.whiteSpace = "nowrap";
9151  
9152              if (s.custom_elements) {
9153  				function handleCustom(ed, o) {
9154                      each(explode(s.custom_elements), function(v) {
9155                          var n;
9156  
9157                          if (v.indexOf('~') === 0) {
9158                              v = v.substring(1);
9159                              n = 'span';
9160                          } else
9161                              n = 'div';
9162  
9163                          o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
9164                          o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
9165                      });
9166                  };
9167  
9168                  t.onBeforeSetContent.add(handleCustom);
9169                  t.onPostProcess.add(function(ed, o) {
9170                      if (o.set)
9171                          handleCustom(ed, o);
9172                  });
9173              }
9174  
9175              if (s.handle_node_change_callback) {
9176                  t.onNodeChange.add(function(ed, cm, n) {
9177                      t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
9178                  });
9179              }
9180  
9181              if (s.save_callback) {
9182                  t.onSaveContent.add(function(ed, o) {
9183                      var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
9184  
9185                      if (h)
9186                          o.content = h;
9187                  });
9188              }
9189  
9190              if (s.onchange_callback) {
9191                  t.onChange.add(function(ed, l) {
9192                      t.execCallback('onchange_callback', t, l);
9193                  });
9194              }
9195  
9196              if (s.convert_newlines_to_brs) {
9197                  t.onBeforeSetContent.add(function(ed, o) {
9198                      if (o.initial)
9199                          o.content = o.content.replace(/\r?\n/g, '<br />');
9200                  });
9201              }
9202  
9203              if (s.fix_nesting && isIE) {
9204                  t.onBeforeSetContent.add(function(ed, o) {
9205                      o.content = t._fixNesting(o.content);
9206                  });
9207              }
9208  
9209              if (s.preformatted) {
9210                  t.onPostProcess.add(function(ed, o) {
9211                      o.content = o.content.replace(/^\s*<pre.*?>/, '');
9212                      o.content = o.content.replace(/<\/pre>\s*$/, '');
9213  
9214                      if (o.set)
9215                          o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
9216                  });
9217              }
9218  
9219              if (s.verify_css_classes) {
9220                  t.serializer.attribValueFilter = function(n, v) {
9221                      var s, cl;
9222  
9223                      if (n == 'class') {
9224                          // Build regexp for classes
9225                          if (!t.classesRE) {
9226                              cl = t.dom.getClasses();
9227  
9228                              if (cl.length > 0) {
9229                                  s = '';
9230  
9231                                  each (cl, function(o) {
9232                                      s += (s ? '|' : '') + o['class'];
9233                                  });
9234  
9235                                  t.classesRE = new RegExp('(' + s + ')', 'gi');
9236                              }
9237                          }
9238  
9239                          return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
9240                      }
9241  
9242                      return v;
9243                  };
9244              }
9245  
9246              if (s.convert_fonts_to_spans)
9247                  t._convertFonts();
9248  
9249              if (s.inline_styles)
9250                  t._convertInlineElements();
9251  
9252              if (s.cleanup_callback) {
9253                  t.onBeforeSetContent.add(function(ed, o) {
9254                      o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
9255                  });
9256  
9257                  t.onPreProcess.add(function(ed, o) {
9258                      if (o.set)
9259                          t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
9260  
9261                      if (o.get)
9262                          t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
9263                  });
9264  
9265                  t.onPostProcess.add(function(ed, o) {
9266                      if (o.set)
9267                          o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
9268  
9269                      if (o.get)                        
9270                          o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
9271                  });
9272              }
9273  
9274              if (s.save_callback) {
9275                  t.onGetContent.add(function(ed, o) {
9276                      if (o.save)
9277                          o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
9278                  });
9279              }
9280  
9281              if (s.handle_event_callback) {
9282                  t.onEvent.add(function(ed, e, o) {
9283                      if (t.execCallback('handle_event_callback', e, ed, o) === false)
9284                          Event.cancel(e);
9285                  });
9286              }
9287  
9288              // Add visual aids when new contents is added
9289              t.onSetContent.add(function() {
9290                  t.addVisual(t.getBody());
9291              });
9292  
9293              // Remove empty contents
9294              if (s.padd_empty_editor) {
9295                  t.onPostProcess.add(function(ed, o) {
9296                      o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
9297                  });
9298              }
9299  
9300              if (isGecko) {
9301                  // Fix gecko link bug, when a link is placed at the end of block elements there is
9302                  // no way to move the caret behind the link. This fix adds a bogus br element after the link
9303  				function fixLinks(ed, o) {
9304                      each(ed.dom.select('a'), function(n) {
9305                          var pn = n.parentNode;
9306  
9307                          if (ed.dom.isBlock(pn) && pn.lastChild === n)
9308                              ed.dom.add(pn, 'br', {'mce_bogus' : 1});
9309                      });
9310                  };
9311  
9312                  t.onExecCommand.add(function(ed, cmd) {
9313                      if (cmd === 'CreateLink')
9314                          fixLinks(ed);
9315                  });
9316  
9317                  t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
9318  
9319                  if (!s.readonly) {
9320                      try {
9321                          // Design mode must be set here once again to fix a bug where
9322                          // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
9323                          d.designMode = 'Off';
9324                          d.designMode = 'On';
9325                      } catch (ex) {
9326                          // Will fail on Gecko if the editor is placed in an hidden container element
9327                          // The design mode will be set ones the editor is focused
9328                      }
9329                  }
9330              }
9331  
9332              // A small timeout was needed since firefox will remove. Bug: #1838304
9333              setTimeout(function () {
9334                  if (t.removed)
9335                      return;
9336  
9337                  t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
9338                  t.startContent = t.getContent({format : 'raw'});
9339                  t.undoManager.add({initial : true});
9340                  t.initialized = true;
9341  
9342                  t.onInit.dispatch(t);
9343                  t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
9344                  t.execCallback('init_instance_callback', t);
9345                  t.focus(true);
9346                  t.nodeChanged({initial : 1});
9347  
9348                  // Load specified content CSS last
9349                  if (s.content_css) {
9350                      tinymce.each(explode(s.content_css), function(u) {
9351                          t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
9352                      });
9353                  }
9354  
9355                  // Handle auto focus
9356                  if (s.auto_focus) {
9357                      setTimeout(function () {
9358                          var ed = EditorManager.get(s.auto_focus);
9359  
9360                          ed.selection.select(ed.getBody(), 1);
9361                          ed.selection.collapse(1);
9362                          ed.getWin().focus();
9363                      }, 100);
9364                  }
9365              }, 1);
9366      
9367              e = null;
9368          },
9369  
9370  
9371          focus : function(sf) {
9372              var oed, t = this, ce = t.settings.content_editable;
9373  
9374              if (!sf) {
9375                  // Is not content editable or the selection is outside the area in IE
9376                  // the IE statement is needed to avoid bluring if element selections inside layers since
9377                  // the layer is like it's own document in IE
9378                  if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
9379                      t.getWin().focus();
9380  
9381              }
9382  
9383              if (EditorManager.activeEditor != t) {
9384                  if ((oed = EditorManager.activeEditor) != null)
9385                      oed.onDeactivate.dispatch(oed, t);
9386  
9387                  t.onActivate.dispatch(t, oed);
9388              }
9389  
9390              EditorManager._setActive(t);
9391          },
9392  
9393          execCallback : function(n) {
9394              var t = this, f = t.settings[n], s;
9395  
9396              if (!f)
9397                  return;
9398  
9399              // Look through lookup
9400              if (t.callbackLookup && (s = t.callbackLookup[n])) {
9401                  f = s.func;
9402                  s = s.scope;
9403              }
9404  
9405              if (is(f, 'string')) {
9406                  s = f.replace(/\.\w+$/, '');
9407                  s = s ? tinymce.resolve(s) : 0;
9408                  f = tinymce.resolve(f);
9409                  t.callbackLookup = t.callbackLookup || {};
9410                  t.callbackLookup[n] = {func : f, scope : s};
9411              }
9412  
9413              return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
9414          },
9415  
9416          translate : function(s) {
9417              var c = this.settings.language || 'en', i18n = EditorManager.i18n;
9418  
9419              if (!s)
9420                  return '';
9421  
9422              return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
9423                  return i18n[c + '.' + b] || '{#' + b + '}';
9424              });
9425          },
9426  
9427          getLang : function(n, dv) {
9428              return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
9429          },
9430  
9431          getParam : function(n, dv, ty) {
9432              var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
9433  
9434              if (ty === 'hash') {
9435                  o = {};
9436  
9437                  if (is(v, 'string')) {
9438                      each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
9439                          v = v.split('=');
9440  
9441                          if (v.length > 1)
9442                              o[tr(v[0])] = tr(v[1]);
9443                          else
9444                              o[tr(v[0])] = tr(v);
9445                      });
9446                  } else
9447                      o = v;
9448  
9449                  return o;
9450              }
9451  
9452              return v;
9453          },
9454  
9455          nodeChanged : function(o) {
9456              var t = this, s = t.selection, n = s.getNode() || t.getBody();
9457  
9458              // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
9459              if (t.initialized) {
9460                  t.onNodeChange.dispatch(
9461                      t,
9462                      o ? o.controlManager || t.controlManager : t.controlManager,
9463                      isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
9464                      s.isCollapsed(),
9465                      o
9466                  );
9467              }
9468          },
9469  
9470          addButton : function(n, s) {
9471              var t = this;
9472  
9473              t.buttons = t.buttons || {};
9474              t.buttons[n] = s;
9475          },
9476  
9477          addCommand : function(n, f, s) {
9478              this.execCommands[n] = {func : f, scope : s || this};
9479          },
9480  
9481          addQueryStateHandler : function(n, f, s) {
9482              this.queryStateCommands[n] = {func : f, scope : s || this};
9483          },
9484  
9485          addQueryValueHandler : function(n, f, s) {
9486              this.queryValueCommands[n] = {func : f, scope : s || this};
9487          },
9488  
9489          addShortcut : function(pa, desc, cmd_func, sc) {
9490              var t = this, c;
9491  
9492              if (!t.settings.custom_shortcuts)
9493                  return false;
9494  
9495              t.shortcuts = t.shortcuts || {};
9496  
9497              if (is(cmd_func, 'string')) {
9498                  c = cmd_func;
9499  
9500                  cmd_func = function() {
9501                      t.execCommand(c, false, null);
9502                  };
9503              }
9504  
9505              if (is(cmd_func, 'object')) {
9506                  c = cmd_func;
9507  
9508                  cmd_func = function() {
9509                      t.execCommand(c[0], c[1], c[2]);
9510                  };
9511              }
9512  
9513              each(explode(pa), function(pa) {
9514                  var o = {
9515                      func : cmd_func,
9516                      scope : sc || this,
9517                      desc : desc,
9518                      alt : false,
9519                      ctrl : false,
9520                      shift : false
9521                  };
9522  
9523                  each(explode(pa, '+'), function(v) {
9524                      switch (v) {
9525                          case 'alt':
9526                          case 'ctrl':
9527                          case 'shift':
9528                              o[v] = true;
9529                              break;
9530  
9531                          default:
9532                              o.charCode = v.charCodeAt(0);
9533                              o.keyCode = v.toUpperCase().charCodeAt(0);
9534                      }
9535                  });
9536  
9537                  t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
9538              });
9539  
9540              return true;
9541          },
9542  
9543          execCommand : function(cmd, ui, val, a) {
9544              var t = this, s = 0, o, st;
9545  
9546              if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
9547                  t.focus();
9548  
9549              o = {};
9550              t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
9551              if (o.terminate)
9552                  return false;
9553  
9554              // Command callback
9555              if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
9556                  t.onExecCommand.dispatch(t, cmd, ui, val, a);
9557                  return true;
9558              }
9559  
9560              // Registred commands
9561              if (o = t.execCommands[cmd]) {
9562                  st = o.func.call(o.scope, ui, val);
9563  
9564                  // Fall through on true
9565                  if (st !== true) {
9566                      t.onExecCommand.dispatch(t, cmd, ui, val, a);
9567                      return st;
9568                  }
9569              }
9570  
9571              // Plugin commands
9572              each(t.plugins, function(p) {
9573                  if (p.execCommand && p.execCommand(cmd, ui, val)) {
9574                      t.onExecCommand.dispatch(t, cmd, ui, val, a);
9575                      s = 1;
9576                      return false;
9577                  }
9578              });
9579  
9580              if (s)
9581                  return true;
9582  
9583              // Theme commands
9584              if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
9585                  t.onExecCommand.dispatch(t, cmd, ui, val, a);
9586                  return true;
9587              }
9588  
9589              // Execute global commands
9590              if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {
9591                  t.onExecCommand.dispatch(t, cmd, ui, val, a);
9592                  return true;
9593              }
9594  
9595              // Editor commands
9596              if (t.editorCommands.execCommand(cmd, ui, val)) {
9597                  t.onExecCommand.dispatch(t, cmd, ui, val, a);
9598                  return true;
9599              }
9600  
9601              // Browser commands
9602              t.getDoc().execCommand(cmd, ui, val);
9603              t.onExecCommand.dispatch(t, cmd, ui, val, a);
9604          },
9605  
9606          queryCommandState : function(c) {
9607              var t = this, o, s;
9608  
9609              // Is hidden then return undefined
9610              if (t._isHidden())
9611                  return;
9612  
9613              // Registred commands
9614              if (o = t.queryStateCommands[c]) {
9615                  s = o.func.call(o.scope);
9616  
9617                  // Fall though on true
9618                  if (s !== true)
9619                      return s;
9620              }
9621  
9622              // Registred commands
9623              o = t.editorCommands.queryCommandState(c);
9624              if (o !== -1)
9625                  return o;
9626  
9627              // Browser commands
9628              try {
9629                  return this.getDoc().queryCommandState(c);
9630              } catch (ex) {
9631                  // Fails sometimes see bug: 1896577
9632              }
9633          },
9634  
9635          queryCommandValue : function(c) {
9636              var t = this, o, s;
9637  
9638              // Is hidden then return undefined
9639              if (t._isHidden())
9640                  return;
9641  
9642              // Registred commands
9643              if (o = t.queryValueCommands[c]) {
9644                  s = o.func.call(o.scope);
9645  
9646                  // Fall though on true
9647                  if (s !== true)
9648                      return s;
9649              }
9650  
9651              // Registred commands
9652              o = t.editorCommands.queryCommandValue(c);
9653              if (is(o))
9654                  return o;
9655  
9656              // Browser commands
9657              try {
9658                  return this.getDoc().queryCommandValue(c);
9659              } catch (ex) {
9660                  // Fails sometimes see bug: 1896577
9661              }
9662          },
9663  
9664          show : function() {
9665              var t = this;
9666  
9667              DOM.show(t.getContainer());
9668              DOM.hide(t.id);
9669              t.load();
9670          },
9671  
9672          hide : function() {
9673              var t = this, d = t.getDoc();
9674  
9675              // Fixed bug where IE has a blinking cursor left from the editor
9676              if (isIE && d)
9677                  d.execCommand('SelectAll');
9678  
9679              // We must save before we hide so Safari doesn't crash
9680              t.save();
9681              DOM.hide(t.getContainer());
9682              DOM.setStyle(t.id, 'display', t.orgDisplay);
9683          },
9684  
9685          isHidden : function() {
9686              return !DOM.isHidden(this.id);
9687          },
9688  
9689          setProgressState : function(b, ti, o) {
9690              this.onSetProgressState.dispatch(this, b, ti, o);
9691  
9692              return b;
9693          },
9694  
9695          load : function(o) {
9696              var t = this, e = t.getElement(), h;
9697  
9698              if (e) {
9699                  o = o || {};
9700                  o.load = true;
9701  
9702                  // Double encode existing entities in the value
9703                  h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
9704                  o.element = e;
9705  
9706                  if (!o.no_events)
9707                      t.onLoadContent.dispatch(t, o);
9708  
9709                  o.element = e = null;
9710  
9711                  return h;
9712              }
9713          },
9714  
9715          save : function(o) {
9716              var t = this, e = t.getElement(), h, f;
9717  
9718              if (!e || !t.initialized)
9719                  return;
9720  
9721              o = o || {};
9722              o.save = true;
9723  
9724              // Add undo level will trigger onchange event
9725              if (!o.no_events) {
9726                  t.undoManager.typing = 0;
9727                  t.undoManager.add();
9728              }
9729  
9730              o.element = e;
9731              h = o.content = t.getContent(o);
9732  
9733              if (!o.no_events)
9734                  t.onSaveContent.dispatch(t, o);
9735  
9736              h = o.content;
9737  
9738              if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
9739                  e.innerHTML = h;
9740  
9741                  // Update hidden form element
9742                  if (f = DOM.getParent(t.id, 'form')) {
9743                      each(f.elements, function(e) {
9744                          if (e.name == t.id) {
9745                              e.value = h;
9746                              return false;
9747                          }
9748                      });
9749                  }
9750              } else
9751                  e.value = h;
9752  
9753              o.element = e = null;
9754  
9755              return h;
9756          },
9757  
9758          setContent : function(h, o) {
9759              var t = this;
9760  
9761              o = o || {};
9762              o.format = o.format || 'html';
9763              o.set = true;
9764              o.content = h;
9765  
9766              if (!o.no_events)
9767                  t.onBeforeSetContent.dispatch(t, o);
9768  
9769              // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
9770              // It will also be impossible to place the caret in the editor unless there is a BR element present
9771              if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
9772                  o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
9773                  o.format = 'raw';
9774              }
9775  
9776              o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
9777  
9778              if (o.format != 'raw' && t.settings.cleanup) {
9779                  o.getInner = true;
9780                  o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
9781              }
9782  
9783              if (!o.no_events)
9784                  t.onSetContent.dispatch(t, o);
9785  
9786              return o.content;
9787          },
9788  
9789          getContent : function(o) {
9790              var t = this, h;
9791  
9792              o = o || {};
9793              o.format = o.format || 'html';
9794              o.get = true;
9795  
9796              if (!o.no_events)
9797                  t.onBeforeGetContent.dispatch(t, o);
9798  
9799              if (o.format != 'raw' && t.settings.cleanup) {
9800                  o.getInner = true;
9801                  h = t.serializer.serialize(t.getBody(), o);
9802              } else
9803                  h = t.getBody().innerHTML;
9804  
9805              h = h.replace(/^\s*|\s*$/g, '');
9806              o.content = h;
9807  
9808              if (!o.no_events)
9809                  t.onGetContent.dispatch(t, o);
9810  
9811              return o.content;
9812          },
9813  
9814          isDirty : function() {
9815              var t = this;
9816  
9817              return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
9818          },
9819  
9820          getContainer : function() {
9821              var t = this;
9822  
9823              if (!t.container)
9824                  t.container = DOM.get(t.editorContainer || t.id + '_parent');
9825  
9826              return t.container;
9827          },
9828  
9829          getContentAreaContainer : function() {
9830              return this.contentAreaContainer;
9831          },
9832  
9833          getElement : function() {
9834              return DOM.get(this.settings.content_element || this.id);
9835          },
9836  
9837          getWin : function() {
9838              var t = this, e;
9839  
9840              if (!t.contentWindow) {
9841                  e = DOM.get(t.id + "_ifr");
9842  
9843                  if (e)
9844                      t.contentWindow = e.contentWindow;
9845              }
9846  
9847              return t.contentWindow;
9848          },
9849  
9850          getDoc : function() {
9851              var t = this, w;
9852  
9853              if (!t.contentDocument) {
9854                  w = t.getWin();
9855  
9856                  if (w)
9857                      t.contentDocument = w.document;
9858              }
9859  
9860              return t.contentDocument;
9861          },
9862  
9863          getBody : function() {
9864              return this.bodyElement || this.getDoc().body;
9865          },
9866  
9867          convertURL : function(u, n, e) {
9868              var t = this, s = t.settings;
9869  
9870              // Use callback instead
9871              if (s.urlconverter_callback)
9872                  return t.execCallback('urlconverter_callback', u, e, true, n);
9873  
9874              // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
9875              if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
9876                  return u;
9877  
9878              // Convert to relative
9879              if (s.relative_urls)
9880                  return t.documentBaseURI.toRelative(u);
9881  
9882              // Convert to absolute
9883              u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
9884  
9885              return u;
9886          },
9887  
9888          addVisual : function(e) {
9889              var t = this, s = t.settings;
9890  
9891              e = e || t.getBody();
9892  
9893              if (!is(t.hasVisual))
9894                  t.hasVisual = s.visual;
9895  
9896              each(t.dom.select('table,a', e), function(e) {
9897                  var v;
9898  
9899                  switch (e.nodeName) {
9900                      case 'TABLE':
9901                          v = t.dom.getAttrib(e, 'border');
9902  
9903                          if (!v || v == '0') {
9904                              if (t.hasVisual)
9905                                  t.dom.addClass(e, s.visual_table_class);
9906                              else
9907                                  t.dom.removeClass(e, s.visual_table_class);
9908                          }
9909  
9910                          return;
9911  
9912                      case 'A':
9913                          v = t.dom.getAttrib(e, 'name');
9914  
9915                          if (v) {
9916                              if (t.hasVisual)
9917                                  t.dom.addClass(e, 'mceItemAnchor');
9918                              else
9919                                  t.dom.removeClass(e, 'mceItemAnchor');
9920                          }
9921  
9922                          return;
9923                  }
9924              });
9925  
9926              t.onVisualAid.dispatch(t, e, t.hasVisual);
9927          },
9928  
9929          remove : function() {
9930              var t = this, e = t.getContainer();
9931  
9932              t.removed = 1; // Cancels post remove event execution
9933              t.hide();
9934  
9935              t.execCallback('remove_instance_callback', t);
9936              t.onRemove.dispatch(t);
9937  
9938              // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
9939              t.onExecCommand.listeners = [];
9940  
9941              EditorManager.remove(t);
9942              DOM.remove(e);
9943          },
9944  
9945          destroy : function(s) {
9946              var t = this;
9947  
9948              // One time is enough
9949              if (t.destroyed)
9950                  return;
9951  
9952              if (!s) {
9953                  tinymce.removeUnload(t.destroy);
9954                  tinyMCE.onBeforeUnload.remove(t._beforeUnload);
9955  
9956                  // Manual destroy
9957                  if (t.theme && t.theme.destroy)
9958                      t.theme.destroy();
9959  
9960                  // Destroy controls, selection and dom
9961                  t.controlManager.destroy();
9962                  t.selection.destroy();
9963                  t.dom.destroy();
9964  
9965                  // Remove all events
9966  
9967                  // Don't clear the window or document if content editable
9968                  // is enabled since other instances might still be present
9969                  if (!t.settings.content_editable) {
9970                      Event.clear(t.getWin());
9971                      Event.clear(t.getDoc());
9972                  }
9973  
9974                  Event.clear(t.getBody());
9975                  Event.clear(t.formElement);
9976              }
9977  
9978              if (t.formElement) {
9979                  t.formElement.submit = t.formElement._mceOldSubmit;
9980                  t.formElement._mceOldSubmit = null;
9981              }
9982  
9983              t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
9984  
9985              if (t.selection)
9986                  t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
9987  
9988              t.destroyed = 1;
9989          },
9990  
9991          // Internal functions
9992  
9993          _addEvents : function() {
9994              // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
9995              var t = this, i, s = t.settings, lo = {
9996                  mouseup : 'onMouseUp',
9997                  mousedown : 'onMouseDown',
9998                  click : 'onClick',
9999                  keyup : 'onKeyUp',
10000                  keydown : 'onKeyDown',
10001                  keypress : 'onKeyPress',
10002                  submit : 'onSubmit',
10003                  reset : 'onReset',
10004                  contextmenu : 'onContextMenu',
10005                  dblclick : 'onDblClick',
10006                  paste : 'onPaste' // Doesn't work in all browsers yet
10007              };
10008  
10009  			function eventHandler(e, o) {
10010                  var ty = e.type;
10011  
10012                  // Don't fire events when it's removed
10013                  if (t.removed)
10014                      return;
10015  
10016                  // Generic event handler
10017                  if (t.onEvent.dispatch(t, e, o) !== false) {
10018                      // Specific event handler
10019                      t[lo[e.fakeType || e.type]].dispatch(t, e, o);
10020                  }
10021              };
10022  
10023              // Add DOM events
10024              each(lo, function(v, k) {
10025                  switch (k) {
10026                      case 'contextmenu':
10027                          if (tinymce.isOpera) {
10028                              // Fake contextmenu on Opera
10029                              t.dom.bind(t.getBody(), 'mousedown', function(e) {
10030                                  if (e.ctrlKey) {
10031                                      e.fakeType = 'contextmenu';
10032                                      eventHandler(e);
10033                                  }
10034                              });
10035                          } else
10036                              t.dom.bind(t.getBody(), k, eventHandler);
10037                          break;
10038  
10039                      case 'paste':
10040                          t.dom.bind(t.getBody(), k, function(e) {
10041                              eventHandler(e);
10042                          });
10043                          break;
10044  
10045                      case 'submit':
10046                      case 'reset':
10047                          t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
10048                          break;
10049  
10050                      default:
10051                          t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
10052                  }
10053              });
10054  
10055              t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
10056                  t.focus(true);
10057              });
10058  
10059  
10060              // Fixes bug where a specified document_base_uri could result in broken images
10061              // This will also fix drag drop of images in Gecko
10062              if (tinymce.isGecko) {
10063                  // Convert all images to absolute URLs
10064  /*                t.onSetContent.add(function(ed, o) {
10065                      each(ed.dom.select('img'), function(e) {
10066                          var v;
10067  
10068                          if (v = e.getAttribute('mce_src'))
10069                              e.src = t.documentBaseURI.toAbsolute(v);
10070                      })
10071                  });*/
10072  
10073                  t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
10074                      var v;
10075  
10076                      e = e.target;
10077  
10078                      if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))
10079                          e.src = t.documentBaseURI.toAbsolute(v);
10080                  });
10081              }
10082  
10083              // Set various midas options in Gecko
10084              if (isGecko) {
10085  				function setOpts() {
10086                      var t = this, d = t.getDoc(), s = t.settings;
10087  
10088                      if (isGecko && !s.readonly) {
10089                          if (t._isHidden()) {
10090                              try {
10091                                  if (!s.content_editable)
10092                                      d.designMode = 'On';
10093                              } catch (ex) {
10094                                  // Fails if it's hidden
10095                              }
10096                          }
10097  
10098                          try {
10099                              // Try new Gecko method
10100                              d.execCommand("styleWithCSS", 0, false);
10101                          } catch (ex) {
10102                              // Use old method
10103                              if (!t._isHidden())
10104                                  try {d.execCommand("useCSS", 0, true);} catch (ex) {}
10105                          }
10106  
10107                          if (!s.table_inline_editing)
10108                              try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
10109  
10110                          if (!s.object_resizing)
10111                              try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
10112                      }
10113                  };
10114  
10115                  t.onBeforeExecCommand.add(setOpts);
10116                  t.onMouseDown.add(setOpts);
10117              }
10118  
10119              // Add node change handlers
10120              t.onMouseUp.add(t.nodeChanged);
10121              t.onClick.add(t.nodeChanged);
10122              t.onKeyUp.add(function(ed, e) {
10123                  var c = e.keyCode;
10124  
10125                  if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)
10126                      t.nodeChanged();
10127              });
10128  
10129              // Add reset handler
10130              t.onReset.add(function() {
10131                  t.setContent(t.startContent, {format : 'raw'});
10132              });
10133  
10134              // Add shortcuts
10135              if (s.custom_shortcuts) {
10136                  if (s.custom_undo_redo_keyboard_shortcuts) {
10137                      t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
10138                      t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
10139                  }
10140  
10141                  // Add default shortcuts for gecko
10142                  if (isGecko) {
10143                      t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
10144                      t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
10145                      t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
10146                  }
10147  
10148                  // BlockFormat shortcuts keys
10149                  for (i=1; i<=6; i++)
10150                      t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);
10151  
10152                  t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
10153                  t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
10154                  t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
10155  
10156  				function find(e) {
10157                      var v = null;
10158  
10159                      if (!e.altKey && !e.ctrlKey && !e.metaKey)
10160                          return v;
10161  
10162                      each(t.shortcuts, function(o) {
10163                          if (tinymce.isMac && o.ctrl != e.metaKey)
10164                              return;
10165                          else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
10166                              return;
10167  
10168                          if (o.alt != e.altKey)
10169                              return;
10170  
10171                          if (o.shift != e.shiftKey)
10172                              return;
10173  
10174                          if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
10175                              v = o;
10176                              return false;
10177                          }
10178                      });
10179  
10180                      return v;
10181                  };
10182  
10183                  t.onKeyUp.add(function(ed, e) {
10184                      var o = find(e);
10185  
10186                      if (o)
10187                          return Event.cancel(e);
10188                  });
10189  
10190                  t.onKeyPress.add(function(ed, e) {
10191                      var o = find(e);
10192  
10193                      if (o)
10194                          return Event.cancel(e);
10195                  });
10196  
10197                  t.onKeyDown.add(function(ed, e) {
10198                      var o = find(e);
10199  
10200                      if (o) {
10201                          o.func.call(o.scope);
10202                          return Event.cancel(e);
10203                      }
10204                  });
10205              }
10206  
10207              if (tinymce.isIE) {
10208                  // Fix so resize will only update the width and height attributes not the styles of an image
10209                  // It will also block mceItemNoResize items
10210                  t.dom.bind(t.getDoc(), 'controlselect', function(e) {
10211                      var re = t.resizeInfo, cb;
10212  
10213                      e = e.target;
10214  
10215                      // Don't do this action for non image elements
10216                      if (e.nodeName !== 'IMG')
10217                          return;
10218  
10219                      if (re)
10220                          t.dom.unbind(re.node, re.ev, re.cb);
10221  
10222                      if (!t.dom.hasClass(e, 'mceItemNoResize')) {
10223                          ev = 'resizeend';
10224                          cb = t.dom.bind(e, ev, function(e) {
10225                              var v;
10226  
10227                              e = e.target;
10228  
10229                              if (v = t.dom.getStyle(e, 'width')) {
10230                                  t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
10231                                  t.dom.setStyle(e, 'width', '');
10232                              }
10233  
10234                              if (v = t.dom.getStyle(e, 'height')) {
10235                                  t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
10236                                  t.dom.setStyle(e, 'height', '');
10237                              }
10238                          });
10239                      } else {
10240                          ev = 'resizestart';
10241                          cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);
10242                      }
10243  
10244                      re = t.resizeInfo = {
10245                          node : e,
10246                          ev : ev,
10247                          cb : cb
10248                      };
10249                  });
10250  
10251                  t.onKeyDown.add(function(ed, e) {
10252                      switch (e.keyCode) {
10253                          case 8:
10254                              // Fix IE control + backspace browser bug
10255                              if (t.selection.getRng().item) {
10256                                  t.selection.getRng().item(0).removeNode();
10257                                  return Event.cancel(e);
10258                              }
10259                      }
10260                  });
10261  
10262                  /*if (t.dom.boxModel) {
10263                      t.getBody().style.height = '100%';
10264  
10265                      Event.add(t.getWin(), 'resize', function(e) {
10266                          var docElm = t.getDoc().documentElement;
10267  
10268                          docElm.style.height = (docElm.offsetHeight - 10) + 'px';
10269                      });
10270                  }*/
10271              }
10272  
10273              if (tinymce.isOpera) {
10274                  t.onClick.add(function(ed, e) {
10275                      Event.prevent(e);
10276                  });
10277              }
10278  
10279              // Add custom undo/redo handlers
10280              if (s.custom_undo_redo) {
10281  				function addUndo() {
10282                      t.undoManager.typing = 0;
10283                      t.undoManager.add();
10284                  };
10285  
10286                  // Add undo level on editor blur
10287                  if (tinymce.isIE) {
10288                      t.dom.bind(t.getWin(), 'blur', function(e) {
10289                          var n;
10290  
10291                          // Check added for fullscreen bug
10292                          if (t.selection) {
10293                              n = t.selection.getNode();
10294  
10295                              // Add undo level is selection was lost to another document
10296                              if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())
10297                                  addUndo();
10298                          }
10299                      });
10300                  } else {
10301                      t.dom.bind(t.getDoc(), 'blur', function() {
10302                          if (t.selection && !t.removed)
10303                              addUndo();
10304                      });
10305                  }
10306  
10307                  t.onMouseDown.add(addUndo);
10308  
10309                  t.onKeyUp.add(function(ed, e) {
10310                      if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {
10311                          t.undoManager.typing = 0;
10312                          t.undoManager.add();
10313                      }
10314                  });
10315  
10316                  t.onKeyDown.add(function(ed, e) {
10317                      // Is caracter positon keys
10318                      if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
10319                          if (t.undoManager.typing) {
10320                              t.undoManager.add();
10321                              t.undoManager.typing = 0;
10322                          }
10323  
10324                          return;
10325                      }
10326  
10327                      if (!t.undoManager.typing) {
10328                          t.undoManager.add();
10329                          t.undoManager.typing = 1;
10330                      }
10331                  });
10332              }
10333          },
10334  
10335          _convertInlineElements : function() {
10336              var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;
10337  
10338  			function convert(ed, o) {
10339                  if (!s.inline_styles)
10340                      return;
10341  
10342                  if (o.get) {
10343                      each(t.dom.select('table,u,strike', o.node), function(n) {
10344                          switch (n.nodeName) {
10345                              case 'TABLE':
10346                                  if (v = dom.getAttrib(n, 'height')) {
10347                                      dom.setStyle(n, 'height', v);
10348                                      dom.setAttrib(n, 'height', '');
10349                                  }
10350                                  break;
10351  
10352                              case 'U':
10353                              case 'STRIKE':
10354                                  //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
10355                                  n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';
10356                                  dom.setAttrib(n, 'mce_style', '');
10357                                  dom.setAttrib(n, 'mce_name', 'span');
10358                                  break;
10359                          }
10360                      });
10361                  } else if (o.set) {
10362                      each(t.dom.select('table,span', o.node).reverse(), function(n) {
10363                          if (n.nodeName == 'TABLE') {
10364                              if (v = dom.getStyle(n, 'height'))
10365                                  dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));
10366                          } else {
10367                              // Convert spans to elements
10368                              if (n.style.textDecoration == 'underline')
10369                                  na = 'u';
10370                              else if (n.style.textDecoration == 'line-through')
10371                                  na = 'strike';
10372                              else
10373                                  na = '';
10374  
10375                              if (na) {
10376                                  n.style.textDecoration = '';
10377                                  dom.setAttrib(n, 'mce_style', '');
10378  
10379                                  e = dom.create(na, {
10380                                      style : dom.getAttrib(n, 'style')
10381                                  });
10382  
10383                                  dom.replace(e, n, 1);
10384                              }
10385                          }
10386                      });
10387                  }
10388              };
10389  
10390              t.onPreProcess.add(convert);
10391  
10392              if (!s.cleanup_on_startup) {
10393                  t.onSetContent.add(function(ed, o) {
10394                      if (o.initial)
10395                          convert(t, {node : t.getBody(), set : 1});
10396                  });
10397              }
10398          },
10399  
10400          _convertFonts : function() {
10401              var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;
10402  
10403              // No need
10404              if (!s.inline_styles)
10405                  return;
10406  
10407              // Font pt values and font size names
10408              fz = [8, 10, 12, 14, 18, 24, 36];
10409              fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
10410  
10411              if (sl = s.font_size_style_values)
10412                  sl = explode(sl);
10413  
10414              if (cl = s.font_size_classes)
10415                  cl = explode(cl);
10416  
10417  			function process(no) {
10418                  var n, sp, nl, x;
10419  
10420                  // Keep unit tests happy
10421                  if (!s.inline_styles)
10422                      return;
10423  
10424                  nl = t.dom.select('font', no);
10425                  for (x = nl.length - 1; x >= 0; x--) {
10426                      n = nl[x];
10427  
10428                      sp = dom.create('span', {
10429                          style : dom.getAttrib(n, 'style'),
10430                          'class' : dom.getAttrib(n, 'class')
10431                      });
10432  
10433                      dom.setStyles(sp, {
10434                          fontFamily : dom.getAttrib(n, 'face'),
10435                          color : dom.getAttrib(n, 'color'),
10436                          backgroundColor : n.style.backgroundColor
10437                      });
10438  
10439                      if (n.size) {
10440                          if (sl)
10441                              dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
10442                          else
10443                              dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);
10444                      }
10445  
10446                      dom.setAttrib(sp, 'mce_style', '');
10447                      dom.replace(sp, n, 1);
10448                  }
10449              };
10450  
10451              // Run on cleanup
10452              t.onPreProcess.add(function(ed, o) {
10453                  if (o.get)
10454                      process(o.node);
10455              });
10456  
10457              t.onSetContent.add(function(ed, o) {
10458                  if (o.initial)
10459                      process(o.node);
10460              });
10461          },
10462  
10463          _isHidden : function() {
10464              var s;
10465  
10466              if (!isGecko)
10467                  return 0;
10468  
10469              // Weird, wheres that cursor selection?
10470              s = this.selection.getSel();
10471              return (!s || !s.rangeCount || s.rangeCount == 0);
10472          },
10473  
10474          // Fix for bug #1867292
10475          _fixNesting : function(s) {
10476              var d = [], i;
10477  
10478              s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
10479                  var e;
10480  
10481                  // Handle end element
10482                  if (b === '/') {
10483                      if (!d.length)
10484                          return '';
10485  
10486                      if (c !== d[d.length - 1].tag) {
10487                          for (i=d.length - 1; i>=0; i--) {
10488                              if (d[i].tag === c) {
10489                                  d[i].close = 1;
10490                                  break;
10491                              }
10492                          }
10493  
10494                          return '';
10495                      } else {
10496                          d.pop();
10497  
10498                          if (d.length && d[d.length - 1].close) {
10499                              a = a + '</' + d[d.length - 1].tag + '>';
10500                              d.pop();
10501                          }
10502                      }
10503                  } else {
10504                      // Ignore these
10505                      if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
10506                          return a;
10507  
10508                      // Ignore closed ones
10509                      if (/\/>$/.test(a))
10510                          return a;
10511  
10512                      d.push({tag : c}); // Push start element
10513                  }
10514  
10515                  return a;
10516              });
10517  
10518              // End all open tags
10519              for (i=d.length - 1; i>=0; i--)
10520                  s += '</' + d[i].tag + '>';
10521  
10522              return s;
10523          }
10524      });
10525  })(tinymce);
10526  (function(tinymce) {
10527      var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;
10528  
10529      tinymce.create('tinymce.EditorCommands', {
10530          EditorCommands : function(ed) {
10531              this.editor = ed;
10532          },
10533  
10534          execCommand : function(cmd, ui, val) {
10535              var t = this, ed = t.editor, f;
10536  
10537              switch (cmd) {
10538                  // Ignore these
10539                  case 'mceResetDesignMode':
10540                  case 'mceBeginUndoLevel':
10541                      return true;
10542  
10543                  // Ignore these
10544                  case 'unlink':
10545                      t.UnLink();
10546                      return true;
10547  
10548                  // Bundle these together
10549                  case 'JustifyLeft':
10550                  case 'JustifyCenter':
10551                  case 'JustifyRight':
10552                  case 'JustifyFull':
10553                      t.mceJustify(cmd, cmd.substring(7).toLowerCase());
10554                      return true;
10555  
10556                  default:
10557                      f = this[cmd];
10558  
10559                      if (f) {
10560                          f.call(this, ui, val);
10561                          return true;
10562                      }
10563              }
10564  
10565              return false;
10566          },
10567  
10568          Indent : function() {
10569              var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;
10570  
10571              // Setup indent level
10572              iv = ed.settings.indentation;
10573              iu = /[a-z%]+$/i.exec(iv);
10574              iv = parseInt(iv);
10575  
10576              if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
10577                  each(s.getSelectedBlocks(), function(e) {
10578                      d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
10579                  });
10580  
10581                  return;
10582              }
10583  
10584              ed.getDoc().execCommand('Indent', false, null);
10585  
10586              if (isIE) {
10587                  d.getParent(s.getNode(), function(n) {
10588                      if (n.nodeName == 'BLOCKQUOTE') {
10589                          n.dir = n.style.cssText = '';
10590                      }
10591                  });
10592              }
10593          },
10594  
10595          Outdent : function() {
10596              var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;
10597  
10598              // Setup indent level
10599              iv = ed.settings.indentation;
10600              iu = /[a-z%]+$/i.exec(iv);
10601              iv = parseInt(iv);
10602  
10603              if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
10604                  each(s.getSelectedBlocks(), function(e) {
10605                      v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
10606                      d.setStyle(e, 'paddingLeft', v ? v + iu : '');
10607                  });
10608  
10609                  return;
10610              }
10611  
10612              ed.getDoc().execCommand('Outdent', false, null);
10613          },
10614  
10615  /*
10616          mceSetAttribute : function(u, v) {
10617              var ed = this.editor, d = ed.dom, e;
10618  
10619              if (e = d.getParent(ed.selection.getNode(), d.isBlock))
10620                  d.setAttrib(e, v.name, v.value);
10621          },
10622  */
10623          mceSetContent : function(u, v) {
10624              this.editor.setContent(v);
10625          },
10626  
10627          mceToggleVisualAid : function() {
10628              var ed = this.editor;
10629  
10630              ed.hasVisual = !ed.hasVisual;
10631              ed.addVisual();
10632          },
10633  
10634          mceReplaceContent : function(u, v) {
10635              var s = this.editor.selection;
10636  
10637              s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
10638          },
10639  
10640          mceInsertLink : function(u, v) {
10641              var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'a');
10642  
10643              if (tinymce.is(v, 'string'))
10644                  v = {href : v};
10645  
10646  			function set(e) {
10647                  each(v, function(v, k) {
10648                      ed.dom.setAttrib(e, k, v);
10649                  });
10650              };
10651  
10652              if (!e) {
10653                  ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
10654                  each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) {
10655                      set(e);
10656                  });
10657              } else {
10658                  if (v.href)
10659                      set(e);
10660                  else
10661                      ed.dom.remove(e, 1);
10662              }
10663          },
10664  
10665          UnLink : function() {
10666              var ed = this.editor, s = ed.selection;
10667  
10668              if (s.isCollapsed())
10669                  s.select(s.getNode());
10670  
10671              ed.getDoc().execCommand('unlink', false, null);
10672              s.collapse(0);
10673          },
10674  
10675          FontName : function(u, v) {
10676              var t = this, ed = t.editor, s = ed.selection, e;
10677  
10678              if (!v) {
10679                  if (s.isCollapsed())
10680                      s.select(s.getNode());
10681              } else {
10682                  if (ed.settings.convert_fonts_to_spans)
10683                      t._applyInlineStyle('span', {style : {fontFamily : v}});
10684                  else
10685                      ed.getDoc().execCommand('FontName', false, v);
10686              }
10687          },
10688  
10689          FontSize : function(u, v) {
10690              var ed = this.editor, s = ed.settings, fc, fs;
10691  
10692              // Use style options instead
10693              if (s.convert_fonts_to_spans && v >= 1 && v <= 7) {
10694                  fs = tinymce.explode(s.font_size_style_values);
10695                  fc = tinymce.explode(s.font_size_classes);
10696  
10697                  if (fc)
10698                      v = fc[v - 1] || v;
10699                  else
10700                      v = fs[v - 1] || v;
10701              }
10702  
10703              if (v >= 1 && v <= 7)
10704                  ed.getDoc().execCommand('FontSize', false, v);
10705              else
10706                  this._applyInlineStyle('span', {style : {fontSize : v}});
10707          },
10708  
10709          queryCommandValue : function(c) {
10710              var f = this['queryValue' + c];
10711  
10712              if (f)
10713                  return f.call(this, c);
10714  
10715              return false;
10716          },
10717  
10718          queryCommandState : function(cmd) {
10719              var f;
10720  
10721              switch (cmd) {
10722                  // Bundle these together
10723                  case 'JustifyLeft':
10724                  case 'JustifyCenter':
10725                  case 'JustifyRight':
10726                  case 'JustifyFull':
10727                      return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
10728  
10729                  default:
10730                      if (f = this['queryState' + cmd])
10731                          return f.call(this, cmd);
10732              }
10733  
10734              return -1;
10735          },
10736  
10737          _queryState : function(c) {
10738              try {
10739                  return this.editor.getDoc().queryCommandState(c);
10740              } catch (ex) {
10741                  // Ignore exception
10742              }
10743          },
10744  
10745          _queryVal : function(c) {
10746              try {
10747                  return this.editor.getDoc().queryCommandValue(c);
10748              } catch (ex) {
10749                  // Ignore exception
10750              }
10751          },
10752  
10753          queryValueFontSize : function() {
10754              var ed = this.editor, v = 0, p;
10755  
10756              if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))
10757                  v = p.style.fontSize;
10758  
10759              if (!v && (isOpera || isWebKit)) {
10760                  if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))
10761                      v = p.size;
10762  
10763                  return v;
10764              }
10765  
10766              return v || this._queryVal('FontSize');
10767          },
10768  
10769          queryValueFontName : function() {
10770              var ed = this.editor, v = 0, p;
10771  
10772              if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))
10773                  v = p.face;
10774  
10775              if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))
10776                  v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
10777  
10778              if (!v)
10779                  v = this._queryVal('FontName');
10780  
10781              return v;
10782          },
10783  
10784          mceJustify : function(c, v) {
10785              var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;
10786  
10787              if (ed.settings.inline_styles && this.queryStateJustify(c, v))
10788                  rm = 1;
10789  
10790              bl = dom.getParent(n, ed.dom.isBlock);
10791  
10792              if (nn == 'IMG') {
10793                  if (v == 'full')
10794                      return;
10795  
10796                  if (rm) {
10797                      if (v == 'center')
10798                          dom.setStyle(bl || n.parentNode, 'textAlign', '');
10799  
10800                      dom.setStyle(n, 'float', '');
10801                      this.mceRepaint();
10802                      return;
10803                  }
10804  
10805                  if (v == 'center') {
10806                      // Do not change table elements
10807                      if (bl && /^(TD|TH)$/.test(bl.nodeName))
10808                          bl = 0;
10809  
10810                      if (!bl || bl.childNodes.length > 1) {
10811                          nb = dom.create('p');
10812                          nb.appendChild(n.cloneNode(false));
10813  
10814                          if (bl)
10815                              dom.insertAfter(nb, bl);
10816                          else
10817                              dom.insertAfter(nb, n);
10818  
10819                          dom.remove(n);
10820                          n = nb.firstChild;
10821                          bl = nb;
10822                      }
10823  
10824                      dom.setStyle(bl, 'textAlign', v);
10825                      dom.setStyle(n, 'float', '');
10826                  } else {
10827                      dom.setStyle(n, 'float', v);
10828                      dom.setStyle(bl || n.parentNode, 'textAlign', '');
10829                  }
10830  
10831                  this.mceRepaint();
10832                  return;
10833              }
10834  
10835              // Handle the alignment outselfs, less quirks in all browsers
10836              if (ed.settings.inline_styles && ed.settings.forced_root_block) {
10837                  if (rm)
10838                      v = '';
10839  
10840                  each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
10841                      dom.setAttrib(e, 'align', '');
10842                      dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
10843                  });
10844  
10845                  return;
10846              } else if (!rm)
10847                  ed.getDoc().execCommand(c, false, null);
10848  
10849              if (ed.settings.inline_styles) {
10850                  if (rm) {
10851                      dom.getParent(ed.selection.getNode(), function(n) {
10852                          if (n.style && n.style.textAlign)
10853                              dom.setStyle(n, 'textAlign', '');
10854                      });
10855  
10856                      return;
10857                  }
10858  
10859                  each(dom.select('*'), function(n) {
10860                      var v = n.align;
10861  
10862                      if (v) {
10863                          if (v == 'full')
10864                              v = 'justify';
10865  
10866                          dom.setStyle(n, 'textAlign', v);
10867                          dom.setAttrib(n, 'align', '');
10868                      }
10869                  });
10870              }
10871          },
10872  
10873          mceSetCSSClass : function(u, v) {
10874              this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
10875          },
10876  
10877          getSelectedElement : function() {
10878              var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re;
10879  
10880              if (se.isCollapsed() || r.item)
10881                  return se.getNode();
10882  
10883              // Setup regexp
10884              re = ed.settings.merge_styles_invalid_parents;
10885              if (tinymce.is(re, 'string'))
10886                  re = new RegExp(re, 'i');
10887  
10888              if (isIE) {
10889                  r1 = r.duplicate();
10890                  r1.collapse(true);
10891                  sc = r1.parentElement();
10892  
10893                  r2 = r.duplicate();
10894                  r2.collapse(false);
10895                  ec = r2.parentElement();
10896  
10897                  if (sc != ec) {
10898                      r1.move('character', 1);
10899                      sc = r1.parentElement();
10900                  }
10901  
10902                  if (sc == ec) {
10903                      r1 = r.duplicate();
10904                      r1.moveToElementText(sc);
10905  
10906                      if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
10907                          return re && re.test(sc.nodeName) ? null : sc;
10908                  }
10909              } else {
10910  				function getParent(n) {
10911                      return dom.getParent(n, '*');
10912                  };
10913  
10914                  sc = r.startContainer;
10915                  ec = r.endContainer;
10916                  so = r.startOffset;
10917                  eo = r.endOffset;
10918  
10919                  if (!r.collapsed) {
10920                      if (sc == ec) {
10921                          if (so - eo < 2) {
10922                              if (sc.hasChildNodes()) {
10923                                  sp = sc.childNodes[so];
10924                                  return re && re.test(sp.nodeName) ? null : sp;
10925                              }
10926                          }
10927                      }
10928                  }
10929  
10930                  if (sc.nodeType != 3 || ec.nodeType != 3)
10931                      return null;
10932  
10933                  if (so == 0) {
10934                      sp = getParent(sc);
10935  
10936                      if (sp && sp.firstChild != sc)
10937                          sp = null;
10938                  }
10939  
10940                  if (so == sc.nodeValue.length) {
10941                      e = sc.nextSibling;
10942  
10943                      if (e && e.nodeType == 1)
10944                          sp = sc.nextSibling;
10945                  }
10946  
10947                  if (eo == 0) {
10948                      e = ec.previousSibling;
10949  
10950                      if (e && e.nodeType == 1)
10951                          ep = e;
10952                  }
10953  
10954                  if (eo == ec.nodeValue.length) {
10955                      ep = getParent(ec);
10956  
10957                      if (ep && ep.lastChild != ec)
10958                          ep = null;
10959                  }
10960  
10961                  // Same element
10962                  if (sp == ep)
10963                      return re && sp && re.test(sp.nodeName) ? null : sp;
10964              }
10965  
10966              return null;
10967          },
10968  
10969          mceSetStyleInfo : function(u, v) {
10970              var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re;
10971  
10972  			function set(n, e) {
10973                  if (n.nodeType == 1) {
10974                      switch (v.command) {
10975                          case 'setattrib':
10976                              return dom.setAttrib(n, v.name, v.value);
10977  
10978                          case 'setstyle':
10979                              return dom.setStyle(n, v.name, v.value);
10980  
10981                          case 'removeformat':
10982                              return dom.setAttrib(n, 'class', '');
10983                      }
10984                  }
10985              };
10986  
10987              // Setup regexp
10988              re = ed.settings.merge_styles_invalid_parents;
10989              if (tinymce.is(re, 'string'))
10990                  re = new RegExp(re, 'i');
10991  
10992              // Set style info on selected element
10993              if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)
10994                  set(e, 1);
10995              else {
10996                  // Generate wrappers and set styles on them
10997                  d.execCommand('FontName', false, '__');
10998                  each(dom.select('span,font'), function(n) {
10999                      var sp, e;
11000  
11001                      if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
11002                          sp = dom.create(nn, {mce_new : '1'});
11003  
11004                          set(sp);
11005  
11006                          each (n.childNodes, function(n) {
11007                              sp.appendChild(n.cloneNode(true));
11008                          });
11009  
11010                          dom.replace(sp, n);
11011                      }
11012                  });
11013              }
11014  
11015              // Remove wrappers inside new ones
11016              each(dom.select(nn).reverse(), function(n) {
11017                  var p = n.parentNode;
11018  
11019                  // Check if it's an old span in a new wrapper
11020                  if (!dom.getAttrib(n, 'mce_new')) {
11021                      // Find new wrapper
11022                      p = dom.getParent(n, '*[mce_new]');
11023  
11024                      if (p)
11025                          dom.remove(n, 1);
11026                  }
11027              });
11028  
11029              // Merge wrappers with parent wrappers
11030              each(dom.select(nn).reverse(), function(n) {
11031                  var p = n.parentNode;
11032  
11033                  if (!p || !dom.getAttrib(n, 'mce_new'))
11034                      return;
11035  
11036                  if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
11037                      return;
11038  
11039                  // Has parent of the same type and only child
11040                  if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
11041                      return dom.remove(p, 1);
11042  
11043                  // Has parent that is more suitable to have the class and only child
11044                  if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
11045                      set(p); // Set style info on parent instead
11046                      dom.setAttrib(n, 'class', '');
11047                  }
11048              });
11049  
11050              // Remove empty wrappers
11051              each(dom.select(nn).reverse(), function(n) {
11052                  if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
11053                      if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
11054                          return dom.remove(n, 1);
11055  
11056                      dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
11057                  }
11058              });
11059  
11060              s.moveToBookmark(b);
11061          },
11062  
11063          queryStateJustify : function(c, v) {
11064              var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;
11065  
11066              if (n && n.nodeName == 'IMG') {
11067                  if (dom.getStyle(n, 'float') == v)
11068                      return 1;
11069  
11070                  return n.parentNode.style.textAlign == v;
11071              }
11072  
11073              n = dom.getParent(ed.selection.getStart(), function(n) {
11074                  return n.nodeType == 1 && n.style.textAlign;
11075              });
11076  
11077              if (v == 'full')
11078                  v = 'justify';
11079  
11080              if (ed.settings.inline_styles)
11081                  return (n && n.style.textAlign == v);
11082  
11083              return this._queryState(c);
11084          },
11085  
11086          ForeColor : function(ui, v) {
11087              var ed = this.editor;
11088  
11089              if (ed.settings.convert_fonts_to_spans) {
11090                  this._applyInlineStyle('span', {style : {color : v}});
11091                  return;
11092              } else
11093                  ed.getDoc().execCommand('ForeColor', false, v);
11094          },
11095  
11096          HiliteColor : function(ui, val) {
11097              var t = this, ed = t.editor, d = ed.getDoc();
11098  
11099              if (ed.settings.convert_fonts_to_spans) {
11100                  this._applyInlineStyle('span', {style : {backgroundColor : val}});
11101                  return;
11102              }
11103  
11104  			function set(s) {
11105                  if (!isGecko)
11106                      return;
11107  
11108                  try {
11109                      // Try new Gecko method
11110                      d.execCommand("styleWithCSS", 0, s);
11111                  } catch (ex) {
11112                      // Use old
11113                      d.execCommand("useCSS", 0, !s);
11114                  }
11115              };
11116  
11117              if (isGecko || isOpera) {
11118                  set(true);
11119                  d.execCommand('hilitecolor', false, val);
11120                  set(false);
11121              } else
11122                  d.execCommand('BackColor', false, val);
11123          },
11124  
11125          FormatBlock : function(ui, val) {
11126              var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
11127  
11128  			function isBlock(n) {
11129                  return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
11130              };
11131  
11132              bl = dom.getParent(s.getNode(), function(n) {
11133                  return isBlock(n);
11134              });
11135  
11136              // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
11137              // FF and Opera doesn't change parent DIV elements if you switch format
11138              if (bl) {
11139                  if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
11140                      // Rename block element
11141                      nb = ed.dom.create(val);
11142  
11143                      each(dom.getAttribs(bl), function(v) {
11144                          dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
11145                      });
11146  
11147                      b = s.getBookmark();
11148                      dom.replace(nb, bl, 1);
11149                      s.moveToBookmark(b);
11150                      ed.nodeChanged();
11151                      return;
11152                  }
11153              }
11154  
11155              val = ed.settings.forced_root_block ? (val || '<p>') : val;
11156  
11157              if (val.indexOf('<') == -1)
11158                  val = '<' + val + '>';
11159  
11160              if (tinymce.isGecko)
11161                  val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');
11162  
11163              ed.getDoc().execCommand('FormatBlock', false, val);
11164          },
11165  
11166          mceCleanup : function() {
11167              var ed = this.editor, s = ed.selection, b = s.getBookmark();
11168              ed.setContent(ed.getContent());
11169              s.moveToBookmark(b);
11170          },
11171  
11172          mceRemoveNode : function(ui, val) {
11173              var ed = this.editor, s = ed.selection, b, n = val || s.getNode();
11174  
11175              // Make sure that the body node isn't removed
11176              if (n == ed.getBody())
11177                  return;
11178  
11179              b = s.getBookmark();
11180              ed.dom.remove(n, 1);
11181              s.moveToBookmark(b);
11182              ed.nodeChanged();
11183          },
11184  
11185          mceSelectNodeDepth : function(ui, val) {
11186              var ed = this.editor, s = ed.selection, c = 0;
11187  
11188              ed.dom.getParent(s.getNode(), function(n) {
11189                  if (n.nodeType == 1 && c++ == val) {
11190                      s.select(n);
11191                      ed.nodeChanged();
11192                      return false;
11193                  }
11194              }, ed.getBody());
11195          },
11196  
11197          mceSelectNode : function(u, v) {
11198              this.editor.selection.select(v);
11199          },
11200  
11201          mceInsertContent : function(ui, val) {
11202              this.editor.selection.setContent(val);
11203          },
11204  
11205          mceInsertRawHTML : function(ui, val) {
11206              var ed = this.editor;
11207  
11208              ed.selection.setContent('tiny_mce_marker');
11209              ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
11210          },
11211  
11212          mceRepaint : function() {
11213              var s, b, e = this.editor;
11214  
11215              if (tinymce.isGecko) {
11216                  try {
11217                      s = e.selection;
11218                      b = s.getBookmark(true);
11219  
11220                      if (s.getSel())
11221                          s.getSel().selectAllChildren(e.getBody());
11222  
11223                      s.collapse(true);
11224                      s.moveToBookmark(b);
11225                  } catch (ex) {
11226                      // Ignore
11227                  }
11228              }
11229          },
11230  
11231          queryStateUnderline : function() {
11232              var ed = this.editor, n = ed.selection.getNode();
11233  
11234              if (n && n.nodeName == 'A')
11235                  return false;
11236  
11237              return this._queryState('Underline');
11238          },
11239  
11240          queryStateOutdent : function() {
11241              var ed = this.editor, n;
11242  
11243              if (ed.settings.inline_styles) {
11244                  if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
11245                      return true;
11246  
11247                  if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
11248                      return true;
11249              }
11250  
11251              return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE'));
11252          },
11253  
11254          queryStateInsertUnorderedList : function() {
11255              return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
11256          },
11257  
11258          queryStateInsertOrderedList : function() {
11259              return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
11260          },
11261  
11262          queryStatemceBlockQuote : function() {
11263              return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
11264          },
11265  
11266          _applyInlineStyle : function(na, at, op) {
11267              var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found;
11268  
11269              na = na.toUpperCase();
11270  
11271              if (op && op.check_classes && at['class'])
11272                  op.check_classes.push(at['class']);
11273  
11274  			function removeEmpty() {
11275                  each(dom.select(na).reverse(), function(n) {
11276                      var c = 0;
11277  
11278                      // Check if there is any attributes
11279                      each(dom.getAttribs(n), function(an) {
11280                          if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') {
11281                              //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName));
11282                              c++;
11283                          }
11284                      });
11285  
11286                      // No attributes then remove the element and keep the children
11287                      if (c == 0)
11288                          dom.remove(n, 1);
11289                  });
11290              };
11291  
11292  			function replaceFonts() {
11293                  var bm;
11294  
11295                  each(dom.select('span,font'), function(n) {
11296                      if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') {
11297                          if (!bm)
11298                              bm = ed.selection.getBookmark();
11299  
11300                          at._mce_new = '1';
11301                          dom.replace(dom.create(na, at), n, 1);
11302                      }
11303                  });
11304  
11305                  // Remove redundant elements
11306                  each(dom.select(na + '[_mce_new]'), function(n) {
11307  					function removeStyle(n) {
11308                          if (n.nodeType == 1) {
11309                              each(at.style, function(v, k) {
11310                                  dom.setStyle(n, k, '');
11311                              });
11312  
11313                              // Remove spans with the same class or marked classes
11314                              if (at['class'] && n.className && op) {
11315                                  each(op.check_classes, function(c) {
11316                                      if (dom.hasClass(n, c))
11317                                          dom.removeClass(n, c);
11318                                  });
11319                              }
11320                          }
11321                      };
11322  
11323                      // Remove specified style information from child elements
11324                      each(dom.select(na, n), removeStyle);
11325  
11326                      // Remove the specified style information on parent if current node is only child (IE)
11327                      if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1)
11328                          removeStyle(n.parentNode);
11329  
11330                      // Remove the child elements style info if a parent already has it
11331                      dom.getParent(n.parentNode, function(pn) {
11332                          if (pn.nodeType == 1) {
11333                              if (at.style) {
11334                                  each(at.style, function(v, k) {
11335                                      var sv;
11336  
11337                                      if (!lo[k] && (sv = dom.getStyle(pn, k))) {
11338                                          if (sv === v)
11339                                              dom.setStyle(n, k, '');
11340  
11341                                          lo[k] = 1;
11342                                      }
11343                                  });
11344                              }
11345  
11346                              // Remove spans with the same class or marked classes
11347                              if (at['class'] && pn.className && op) {
11348                                  each(op.check_classes, function(c) {
11349                                      if (dom.hasClass(pn, c))
11350                                          dom.removeClass(n, c);
11351                                  });
11352                              }
11353                          }
11354  
11355                          return false;
11356                      });
11357  
11358                      n.removeAttribute('_mce_new');
11359                  });
11360  
11361                  removeEmpty();
11362                  ed.selection.moveToBookmark(bm);
11363  
11364                  return !!bm;
11365              };
11366  
11367              // Create inline elements
11368              ed.focus();
11369              ed.getDoc().execCommand('FontName', false, 'mceinline');
11370              replaceFonts();
11371  
11372              if (kh = t._applyInlineStyle.keyhandler) {
11373                  ed.onKeyUp.remove(kh);
11374                  ed.onKeyPress.remove(kh);
11375                  ed.onKeyDown.remove(kh);
11376                  ed.onSetContent.remove(t._applyInlineStyle.chandler);
11377              }
11378  
11379              if (ed.selection.isCollapsed()) {
11380                  // IE will format the current word so this code can't be executed on that browser
11381                  if (!isIE) {
11382                      each(dom.getParents(ed.selection.getNode(), 'span'), function(n) {
11383                          each(at.style, function(v, k) {
11384                              var kv;
11385  
11386                              if (kv = dom.getStyle(n, k)) {
11387                                  if (kv == v) {
11388                                      dom.setStyle(n, k, '');
11389                                      found = 2;
11390                                      return false;
11391                                  }
11392  
11393                                  found = 1;
11394                                  return false;
11395                              }
11396                          });
11397  
11398                          if (found)
11399                              return false;
11400                      });
11401  
11402                      if (found == 2) {
11403                          bm = ed.selection.getBookmark();
11404  
11405                          removeEmpty();
11406  
11407                          ed.selection.moveToBookmark(bm);
11408  
11409                          // Node change needs to be detached since the onselect event
11410                          // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix!
11411                          window.setTimeout(function() {
11412                              ed.nodeChanged();
11413                          }, 1);
11414  
11415                          return;
11416                      }
11417                  }
11418  
11419                  // Start collecting styles
11420                  t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style);
11421  
11422                  t._applyInlineStyle.chandler = ed.onSetContent.add(function() {
11423                      delete t._pendingStyles;
11424                  });
11425  
11426                  t._applyInlineStyle.keyhandler = kh = function(e) {
11427                      // Use pending styles
11428                      if (t._pendingStyles) {
11429                          at.style = t._pendingStyles;
11430                          delete t._pendingStyles;
11431                      }
11432  
11433                      if (replaceFonts()) {
11434                          ed.onKeyDown.remove(t._applyInlineStyle.keyhandler);
11435                          ed.onKeyPress.remove(t._applyInlineStyle.keyhandler);
11436                      }
11437  
11438                      if (e.type == 'keyup')
11439                          ed.onKeyUp.remove(t._applyInlineStyle.keyhandler);
11440                  };
11441  
11442                  ed.onKeyDown.add(kh);
11443                  ed.onKeyPress.add(kh);
11444                  ed.onKeyUp.add(kh);
11445              } else
11446                  t._pendingStyles = 0;
11447          }
11448      });
11449  })(tinymce);(function(tinymce) {
11450      tinymce.create('tinymce.UndoManager', {
11451          index : 0,
11452          data : null,
11453          typing : 0,
11454  
11455          UndoManager : function(ed) {
11456              var t = this, Dispatcher = tinymce.util.Dispatcher;
11457  
11458              t.editor = ed;
11459              t.data = [];
11460              t.onAdd = new Dispatcher(this);
11461              t.onUndo = new Dispatcher(this);
11462              t.onRedo = new Dispatcher(this);
11463          },
11464  
11465          add : function(l) {
11466              var t = this, i, ed = t.editor, b, s = ed.settings, la;
11467  
11468              l = l || {};
11469              l.content = l.content || ed.getContent({format : 'raw', no_events : 1});
11470  
11471              // Add undo level if needed
11472              l.content = l.content.replace(/^\s*|\s*$/g, '');
11473              la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];
11474              if (!l.initial && la && l.content == la.content)
11475                  return null;
11476  
11477              // Time to compress
11478              if (s.custom_undo_redo_levels) {
11479                  if (t.data.length > s.custom_undo_redo_levels) {
11480                      for (i = 0; i < t.data.length - 1; i++)
11481                          t.data[i] = t.data[i + 1];
11482  
11483                      t.data.length--;
11484                      t.index = t.data.length;
11485                  }
11486              }
11487  
11488              if (s.custom_undo_redo_restore_selection && !l.initial)
11489                  l.bookmark = b = l.bookmark || ed.selection.getBookmark();
11490  
11491              if (t.index < t.data.length)
11492                  t.index++;
11493  
11494              // Only initial marked undo levels should be allowed as first item
11495              // This to workaround a bug with Firefox and the blur event
11496              if (t.data.length === 0 && !l.initial)
11497                  return null;
11498  
11499              // Add level
11500              t.data.length = t.index + 1;
11501              t.data[t.index++] = l;
11502  
11503              if (l.initial)
11504                  t.index = 0;
11505  
11506              // Set initial bookmark use first real undo level
11507              if (t.data.length == 2 && t.data[0].initial)
11508                  t.data[0].bookmark = b;
11509  
11510              t.onAdd.dispatch(t, l);
11511              ed.isNotDirty = 0;
11512  
11513              //console.dir(t.data);
11514  
11515              return l;
11516          },
11517  
11518          undo : function() {
11519              var t = this, ed = t.editor, l = l, i;
11520  
11521              if (t.typing) {
11522                  t.add();
11523                  t.typing = 0;
11524              }
11525  
11526              if (t.index > 0) {
11527                  // If undo on last index then take snapshot
11528                  if (t.index == t.data.length && t.index > 1) {
11529                      i = t.index;
11530                      t.typing = 0;
11531  
11532                      if (!t.add())
11533                          t.index = i;
11534  
11535                      --t.index;
11536                  }
11537  
11538                  l = t.data[--t.index];
11539                  ed.setContent(l.content, {format : 'raw'});
11540                  ed.selection.moveToBookmark(l.bookmark);
11541  
11542                  t.onUndo.dispatch(t, l);
11543              }
11544  
11545              return l;
11546          },
11547  
11548          redo : function() {
11549              var t = this, ed = t.editor, l = null;
11550  
11551              if (t.index < t.data.length - 1) {
11552                  l = t.data[++t.index];
11553                  ed.setContent(l.content, {format : 'raw'});
11554                  ed.selection.moveToBookmark(l.bookmark);
11555  
11556                  t.onRedo.dispatch(t, l);
11557              }
11558  
11559              return l;
11560          },
11561  
11562          clear : function() {
11563              var t = this;
11564  
11565              t.data = [];
11566              t.index = 0;
11567              t.typing = 0;
11568              t.add({initial : true});
11569          },
11570  
11571          hasUndo : function() {
11572              return this.index != 0 || this.typing;
11573          },
11574  
11575          hasRedo : function() {
11576              return this.index < this.data.length - 1;
11577          }
11578      });
11579  })(tinymce);
11580  (function(tinymce) {
11581      // Shorten names
11582      var Event, isIE, isGecko, isOpera, each, extend;
11583  
11584      Event = tinymce.dom.Event;
11585      isIE = tinymce.isIE;
11586      isGecko = tinymce.isGecko;
11587      isOpera = tinymce.isOpera;
11588      each = tinymce.each;
11589      extend = tinymce.extend;
11590  
11591      // Checks if the selection/caret is at the end of the specified block element
11592  	function isAtEnd(rng, par) {
11593          var rng2 = par.ownerDocument.createRange();
11594  
11595          rng2.setStart(rng.endContainer, rng.endOffset);
11596          rng2.setEndAfter(par);
11597  
11598          // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
11599          return rng2.cloneContents().textContent.length == 0;
11600      };
11601  
11602  	function isEmpty(n) {
11603          n = n.innerHTML;
11604  
11605          n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars
11606          n = n.replace(/<[^>]+>/g, ''); // Remove all tags
11607  
11608          return n.replace(/[ \t\r\n]+/g, '') == '';
11609      };
11610  
11611      tinymce.create('tinymce.ForceBlocks', {
11612          ForceBlocks : function(ed) {
11613              var t = this, s = ed.settings, elm;
11614  
11615              t.editor = ed;
11616              t.dom = ed.dom;
11617              elm = (s.forced_root_block || 'p').toLowerCase();
11618              s.element = elm.toUpperCase();
11619  
11620              ed.onPreInit.add(t.setup, t);
11621  
11622              t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');
11623              t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
11624              t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
11625              t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');
11626              t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
11627  
11628  			function padd(ed, o) {
11629                  if (isOpera)
11630                      o.content = o.content.replace(t.reOpera, '</' + elm + '>');
11631  
11632                  o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');
11633  
11634                  if (!isIE && !isOpera && o.set) {
11635                      // Use &nbsp; instead of BR in padded paragraphs
11636                      o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
11637                      o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
11638                  } else
11639                      o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
11640              };
11641  
11642              ed.onBeforeSetContent.add(padd);
11643              ed.onPostProcess.add(padd);
11644  
11645              if (s.forced_root_block) {
11646                  ed.onInit.add(t.forceRoots, t);
11647                  ed.onSetContent.add(t.forceRoots, t);
11648                  ed.onBeforeGetContent.add(t.forceRoots, t);
11649              }
11650          },
11651  
11652          setup : function() {
11653              var t = this, ed = t.editor, s = ed.settings;
11654  
11655              // Force root blocks when typing and when getting output
11656              if (s.forced_root_block) {
11657                  ed.onKeyUp.add(t.forceRoots, t);
11658                  ed.onPreProcess.add(t.forceRoots, t);
11659              }
11660  
11661              if (s.force_br_newlines) {
11662                  // Force IE to produce BRs on enter
11663                  if (isIE) {
11664                      ed.onKeyPress.add(function(ed, e) {
11665                          var n, s = ed.selection;
11666  
11667                          if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
11668                              s.setContent('<br id="__" /> ', {format : 'raw'});
11669                              n = ed.dom.get('__');
11670                              n.removeAttribute('id');
11671                              s.select(n);
11672                              s.collapse();
11673                              return Event.cancel(e);
11674                          }
11675                      });
11676                  }
11677  
11678                  return;
11679              }
11680  
11681              if (!isIE && s.force_p_newlines) {
11682  /*                ed.onPreProcess.add(function(ed, o) {
11683                      each(ed.dom.select('br', o.node), function(n) {
11684                          var p = n.parentNode;
11685  
11686                          // Replace <p><br /></p> with <p>&nbsp;</p>
11687                          if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
11688                              p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
11689                          }
11690                      });
11691                  });*/
11692  
11693                  ed.onKeyPress.add(function(ed, e) {
11694                      if (e.keyCode == 13 && !e.shiftKey) {
11695                          if (!t.insertPara(e))
11696                              Event.cancel(e);
11697                      }
11698                  });
11699  
11700                  if (isGecko) {
11701                      ed.onKeyDown.add(function(ed, e) {
11702                          if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
11703                              t.backspaceDelete(e, e.keyCode == 8);
11704                      });
11705                  }
11706              }
11707  
11708  			function ren(rn, na) {
11709                  var ne = ed.dom.create(na);
11710  
11711                  each(rn.attributes, function(a) {
11712                      if (a.specified && a.nodeValue)
11713                          ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);
11714                  });
11715  
11716                  each(rn.childNodes, function(n) {
11717                      ne.appendChild(n.cloneNode(true));
11718                  });
11719  
11720                  rn.parentNode.replaceChild(ne, rn);
11721  
11722                  return ne;
11723              };
11724  
11725              // Padd empty inline elements within block elements
11726              // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>
11727              ed.onPreProcess.add(function(ed, o) {
11728                  each(ed.dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {
11729                      if (isEmpty(p)) {
11730                          each(ed.dom.select('span,em,strong,b,i', o.node), function(n) {
11731                              if (!n.hasChildNodes()) {
11732                                  n.appendChild(ed.getDoc().createTextNode('\u00a0'));
11733                                  return false; // Break the loop one padding is enough
11734                              }
11735                          });
11736                      }
11737                  });
11738              });
11739  
11740              // IE specific fixes
11741              if (isIE) {
11742                  // Replaces IE:s auto generated paragraphs with the specified element name
11743                  if (s.element != 'P') {
11744                      ed.onKeyPress.add(function(ed, e) {
11745                          t.lastElm = ed.selection.getNode().nodeName;
11746                      });
11747  
11748                      ed.onKeyUp.add(function(ed, e) {
11749                          var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();
11750  
11751                          if (b.childNodes.length === 1 && n.nodeName == 'P') {
11752                              n = ren(n, s.element);
11753                              sel.select(n);
11754                              sel.collapse();
11755                              ed.nodeChanged();
11756                          } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
11757                              bl = ed.dom.getParent(n, 'p');
11758  
11759                              if (bl) {
11760                                  ren(bl, s.element);
11761                                  ed.nodeChanged();
11762                              }
11763                          }
11764                      });
11765                  }
11766              }
11767          },
11768  
11769          find : function(n, t, s) {
11770              var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;
11771  
11772              while (n = w.nextNode()) {
11773                  c++;
11774  
11775                  // Index by node
11776                  if (t == 0 && n == s)
11777                      return c;
11778  
11779                  // Node by index
11780                  if (t == 1 && c == s)
11781                      return n;
11782              }
11783  
11784              return -1;
11785          },
11786  
11787          forceRoots : function(ed, e) {
11788              var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF;
11789              var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;
11790  
11791              // Fix for bug #1863847
11792              //if (e && e.keyCode == 13)
11793              //    return true;
11794  
11795              // Wrap non blocks into blocks
11796              for (i = nl.length - 1; i >= 0; i--) {
11797                  nx = nl[i];
11798  
11799                  // Is text or non block element
11800                  if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {
11801                      if (!bl) {
11802                          // Create new block but ignore whitespace
11803                          if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
11804                              // Store selection
11805                              if (si == -2 && r) {
11806                                  if (!isIE) {
11807                                      // If selection is element then mark it
11808                                      if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {
11809                                          // Save the id of the selected element
11810                                          eid = n.getAttribute("id");
11811                                          n.setAttribute("id", "__mce");
11812                                      } else {
11813                                          // If element is inside body, might not be the case in contentEdiable mode
11814                                          if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {
11815                                              so = r.startOffset;
11816                                              eo = r.endOffset;
11817                                              si = t.find(b, 0, r.startContainer);
11818                                              ei = t.find(b, 0, r.endContainer);
11819                                          }
11820                                      }
11821                                  } else {
11822                                      tr = d.body.createTextRange();
11823                                      tr.moveToElementText(b);
11824                                      tr.collapse(1);
11825                                      bp = tr.move('character', c) * -1;
11826  
11827                                      tr = r.duplicate();
11828                                      tr.collapse(1);
11829                                      sp = tr.move('character', c) * -1;
11830  
11831                                      tr = r.duplicate();
11832                                      tr.collapse(0);
11833                                      le = (tr.move('character', c) * -1) - sp;
11834  
11835                                      si = sp - bp;
11836                                      ei = le;
11837                                  }
11838                              }
11839  
11840                              bl = ed.dom.create(ed.settings.forced_root_block);
11841                              bl.appendChild(nx.cloneNode(1));
11842                              nx.parentNode.replaceChild(bl, nx);
11843                          }
11844                      } else {
11845                          if (bl.hasChildNodes())
11846                              bl.insertBefore(nx, bl.firstChild);
11847                          else
11848                              bl.appendChild(nx);
11849                      }
11850                  } else
11851                      bl = null; // Time to create new block
11852              }
11853  
11854              // Restore selection
11855              if (si != -2) {
11856                  if (!isIE) {
11857                      bl = b.getElementsByTagName(ed.settings.element)[0];
11858                      r = d.createRange();
11859  
11860                      // Select last location or generated block
11861                      if (si != -1)
11862                          r.setStart(t.find(b, 1, si), so);
11863                      else
11864                          r.setStart(bl, 0);
11865  
11866                      // Select last location or generated block
11867                      if (ei != -1)
11868                          r.setEnd(t.find(b, 1, ei), eo);
11869                      else
11870                          r.setEnd(bl, 0);
11871  
11872                      if (s) {
11873                          s.removeAllRanges();
11874                          s.addRange(r);
11875                      }
11876                  } else {
11877                      try {
11878                          r = s.createRange();
11879                          r.moveToElementText(b);
11880                          r.collapse(1);
11881                          r.moveStart('character', si);
11882                          r.moveEnd('character', ei);
11883                          r.select();
11884                      } catch (ex) {
11885                          // Ignore
11886                      }
11887                  }
11888              } else if (!isIE && (n = ed.dom.get('__mce'))) {
11889                  // Restore the id of the selected element
11890                  if (eid)
11891                      n.setAttribute('id', eid);
11892                  else
11893                      n.removeAttribute('id');
11894  
11895                  // Move caret before selected element
11896                  r = d.createRange();
11897                  r.setStartBefore(n);
11898                  r.setEndBefore(n);
11899                  se.setRng(r);
11900              }
11901          },
11902  
11903          getParentBlock : function(n) {
11904              var d = this.dom;
11905  
11906              return d.getParent(n, d.isBlock);
11907          },
11908  
11909          insertPara : function(e) {
11910              var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;
11911              var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
11912  
11913              // If root blocks are forced then use Operas default behavior since it's really good
11914  // Removed due to bug: #1853816
11915  //            if (se.forced_root_block && isOpera)
11916  //                return true;
11917  
11918              // Setup before range
11919              rb = d.createRange();
11920  
11921              // If is before the first block element and in body, then move it into first block element
11922              rb.setStart(s.anchorNode, s.anchorOffset);
11923              rb.collapse(true);
11924  
11925              // Setup after range
11926              ra = d.createRange();
11927  
11928              // If is before the first block element and in body, then move it into first block element
11929              ra.setStart(s.focusNode, s.focusOffset);
11930              ra.collapse(true);
11931  
11932              // Setup start/end points
11933              dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
11934              sn = dir ? s.anchorNode : s.focusNode;
11935              so = dir ? s.anchorOffset : s.focusOffset;
11936              en = dir ? s.focusNode : s.anchorNode;
11937              eo = dir ? s.focusOffset : s.anchorOffset;
11938  
11939              // If selection is in empty table cell
11940              if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
11941                  if (sn.firstChild.nodeName == 'BR')
11942                      dom.remove(sn.firstChild); // Remove BR
11943  
11944                  // Create two new block elements
11945                  if (sn.childNodes.length == 0) {
11946                      ed.dom.add(sn, se.element, null, '<br />');
11947                      aft = ed.dom.add(sn, se.element, null, '<br />');
11948                  } else {
11949                      n = sn.innerHTML;
11950                      sn.innerHTML = '';
11951                      ed.dom.add(sn, se.element, null, n);
11952                      aft = ed.dom.add(sn, se.element, null, '<br />');
11953                  }
11954  
11955                  // Move caret into the last one
11956                  r = d.createRange();
11957                  r.selectNodeContents(aft);
11958                  r.collapse(1);
11959                  ed.selection.setRng(r);
11960  
11961                  return false;
11962              }
11963  
11964              // If the caret is in an invalid location in FF we need to move it into the first block
11965              if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
11966                  sn = en = sn.firstChild;
11967                  so = eo = 0;
11968                  rb = d.createRange();
11969                  rb.setStart(sn, 0);
11970                  ra = d.createRange();
11971                  ra.setStart(en, 0);
11972              }
11973  
11974              // Never use body as start or end node
11975              sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11976              sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
11977              en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11978              en = en.nodeName == "BODY" ? en.firstChild : en;
11979  
11980              // Get start and end blocks
11981              sb = t.getParentBlock(sn);
11982              eb = t.getParentBlock(en);
11983              bn = sb ? sb.nodeName : se.element; // Get block name to create
11984  
11985              // Return inside list use default browser behavior
11986              if (t.dom.getParent(sb, 'ol,ul,pre'))
11987                  return true;
11988  
11989              // If caption or absolute layers then always generate new blocks within
11990              if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11991                  bn = se.element;
11992                  sb = null;
11993              }
11994  
11995              // If caption or absolute layers then always generate new blocks within
11996              if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11997                  bn = se.element;
11998                  eb = null;
11999              }
12000  
12001              // Use P instead
12002              if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
12003                  bn = se.element;
12004                  sb = eb = null;
12005              }
12006  
12007              // Setup new before and after blocks
12008              bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
12009              aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
12010  
12011              // Remove id from after clone
12012              aft.removeAttribute('id');
12013  
12014              // Is header and cursor is at the end, then force paragraph under
12015              if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))
12016                  aft = ed.dom.create(se.element);
12017  
12018              // Find start chop node
12019              n = sc = sn;
12020              do {
12021                  if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
12022                      break;
12023  
12024                  sc = n;
12025              } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
12026  
12027              // Find end chop node
12028              n = ec = en;
12029              do {
12030                  if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
12031                      break;
12032  
12033                  ec = n;
12034              } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
12035  
12036              // Place first chop part into before block element
12037              if (sc.nodeName == bn)
12038                  rb.setStart(sc, 0);
12039              else
12040                  rb.setStartBefore(sc);
12041  
12042              rb.setEnd(sn, so);
12043              bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
12044  
12045              // Place secnd chop part within new block element
12046              try {
12047                  ra.setEndAfter(ec);
12048              } catch(ex) {
12049                  //console.debug(s.focusNode, s.focusOffset);
12050              }
12051  
12052              ra.setStart(en, eo);
12053              aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
12054  
12055              // Create range around everything
12056              r = d.createRange();
12057              if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
12058                  r.setStartBefore(sc.parentNode);
12059              } else {
12060                  if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
12061                      r.setStartBefore(rb.startContainer);
12062                  else
12063                      r.setStart(rb.startContainer, rb.startOffset);
12064              }
12065  
12066              if (!ec.nextSibling && ec.parentNode.nodeName == bn)
12067                  r.setEndAfter(ec.parentNode);
12068              else
12069                  r.setEnd(ra.endContainer, ra.endOffset);
12070  
12071              // Delete and replace it with new block elements
12072              r.deleteContents();
12073  
12074              if (isOpera)
12075                  ed.getWin().scrollTo(0, vp.y);
12076  
12077              // Never wrap blocks in blocks
12078              if (bef.firstChild && bef.firstChild.nodeName == bn)
12079                  bef.innerHTML = bef.firstChild.innerHTML;
12080  
12081              if (aft.firstChild && aft.firstChild.nodeName == bn)
12082                  aft.innerHTML = aft.firstChild.innerHTML;
12083  
12084              // Padd empty blocks
12085              if (isEmpty(bef))
12086                  bef.innerHTML = '<br />';
12087  
12088  			function appendStyles(e, en) {
12089                  var nl = [], nn, n, i;
12090  
12091                  e.innerHTML = '';
12092  
12093                  // Make clones of style elements
12094                  if (se.keep_styles) {
12095                      n = en;
12096                      do {
12097                          // We only want style specific elements
12098                          if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
12099                              nn = n.cloneNode(false);
12100                              dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
12101                              nl.push(nn);
12102                          }
12103                      } while (n = n.parentNode);
12104                  }
12105  
12106                  // Append style elements to aft
12107                  if (nl.length > 0) {
12108                      for (i = nl.length - 1, nn = e; i >= 0; i--)
12109                          nn = nn.appendChild(nl[i]);
12110  
12111                      // Padd most inner style element
12112                      nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
12113                      return nl[0]; // Move caret to most inner element
12114                  } else
12115                      e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
12116              };
12117  
12118              // Fill empty afterblook with current style
12119              if (isEmpty(aft))
12120                  car = appendStyles(aft, en);
12121  
12122              // Opera needs this one backwards for older versions
12123              if (isOpera && parseFloat(opera.version()) < 9.5) {
12124                  r.insertNode(bef);
12125                  r.insertNode(aft);
12126              } else {
12127                  r.insertNode(aft);
12128                  r.insertNode(bef);
12129              }
12130  
12131              // Normalize
12132              aft.normalize();
12133              bef.normalize();
12134  
12135  			function first(n) {
12136                  return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n;
12137              };
12138  
12139              // Move cursor and scroll into view
12140              r = d.createRange();
12141              r.selectNodeContents(isGecko ? first(car || aft) : car || aft);
12142              r.collapse(1);
12143              s.removeAllRanges();
12144              s.addRange(r);
12145  
12146              // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
12147              y = ed.dom.getPos(aft).y;
12148              ch = aft.clientHeight;
12149  
12150              // Is element within viewport
12151              if (y < vp.y || y + ch > vp.y + vp.h) {
12152                  ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
12153                  //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight));
12154              }
12155  
12156              return false;
12157          },
12158  
12159          backspaceDelete : function(e, bs) {
12160              var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;
12161  
12162              /*
12163              var par, rng, nextBlock;
12164  
12165              // Delete key will not merge paragraphs on Gecko so we need to do this manually
12166              // Hitting the delete key at the following caret position doesn't merge the elements <p>A|</p><p>B</p>
12167              // This logic will merge them into this: <p>A|B</p>
12168              if (e.keyCode == 46) {
12169                  if (r.collapsed) {
12170                      par = dom.getParent(sc, 'p,h1,h2,h3,h4,h5,h6,div');
12171  
12172                      if (par) {
12173                          rng = dom.createRng();
12174  
12175                          rng.setStart(sc, r.startOffset);
12176                          rng.setEndAfter(par);
12177  
12178                          // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
12179                          if (dom.getOuterHTML(rng.cloneContents()).replace(/<[^>]+>/g, '').length == 0) {
12180                              nextBlock = dom.getNext(par, 'p,h1,h2,h3,h4,h5,h6,div');
12181  
12182                              // Copy all children from next sibling block and remove it
12183                              if (nextBlock) {
12184                                  each(nextBlock.childNodes, function(node) {
12185                                      par.appendChild(node.cloneNode(true));
12186                                  });
12187  
12188                                  dom.remove(nextBlock);
12189                              }
12190  
12191                              // Block the default even since the Gecko team might eventually fix this
12192                              // We will remove this logic once they do we can't feature detect this one
12193                              e.preventDefault();
12194                              return;
12195                          }
12196                      }
12197                  }
12198              }
12199              */
12200  
12201              // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
12202              // This workaround removes the element by hand and moves the caret to the previous element
12203              if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
12204                  if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
12205                      // Find previous block element
12206                      n = sc;
12207                      while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
12208  
12209                      if (n) {
12210                          if (sc != b.firstChild) {
12211                              // Find last text node
12212                              w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
12213                              while (tn = w.nextNode())
12214                                  n = tn;
12215  
12216                              // Place caret at the end of last text node
12217                              r = ed.getDoc().createRange();
12218                              r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
12219                              r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
12220                              se.setRng(r);
12221  
12222                              // Remove the target container
12223                              ed.dom.remove(sc);
12224                          }
12225  
12226                          return Event.cancel(e);
12227                      }
12228                  }
12229              }
12230  
12231              // Gecko generates BR elements here and there, we don't like those so lets remove them
12232  			function handler(e) {
12233                  var pr;
12234  
12235                  e = e.target;
12236  
12237                  // A new BR was created in a block element, remove it
12238                  if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
12239                      pr = e.previousSibling;
12240  
12241                      Event.remove(b, 'DOMNodeInserted', handler);
12242  
12243                      // Is there whitespace at the end of the node before then we might need the pesky BR
12244                      // to place the caret at a correct location see bug: #2013943
12245                      if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
12246                          return;
12247  
12248                      // Only remove BR elements that got inserted in the middle of the text
12249                      if (e.previousSibling || e.nextSibling)
12250                          ed.dom.remove(e);
12251                  }
12252              };
12253  
12254              // Listen for new nodes
12255              Event._add(b, 'DOMNodeInserted', handler);
12256  
12257              // Remove listener
12258              window.setTimeout(function() {
12259                  Event._remove(b, 'DOMNodeInserted', handler);
12260              }, 1);
12261          }
12262      });
12263  })(tinymce);
12264  (function(tinymce) {
12265      // Shorten names
12266      var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
12267  
12268      tinymce.create('tinymce.ControlManager', {
12269          ControlManager : function(ed, s) {
12270              var t = this, i;
12271  
12272              s = s || {};
12273              t.editor = ed;
12274              t.controls = {};
12275              t.onAdd = new tinymce.util.Dispatcher(t);
12276              t.onPostRender = new tinymce.util.Dispatcher(t);
12277              t.prefix = s.prefix || ed.id + '_';
12278              t._cls = {};
12279  
12280              t.onPostRender.add(function() {
12281                  each(t.controls, function(c) {
12282                      c.postRender();
12283                  });
12284              });
12285          },
12286  
12287          get : function(id) {
12288              return this.controls[this.prefix + id] || this.controls[id];
12289          },
12290  
12291          setActive : function(id, s) {
12292              var c = null;
12293  
12294              if (c = this.get(id))
12295                  c.setActive(s);
12296  
12297              return c;
12298          },
12299  
12300          setDisabled : function(id, s) {
12301              var c = null;
12302  
12303              if (c = this.get(id))
12304                  c.setDisabled(s);
12305  
12306              return c;
12307          },
12308  
12309          add : function(c) {
12310              var t = this;
12311  
12312              if (c) {
12313                  t.controls[c.id] = c;
12314                  t.onAdd.dispatch(c, t);
12315              }
12316  
12317              return c;
12318          },
12319  
12320          createControl : function(n) {
12321              var c, t = this, ed = t.editor;
12322  
12323              each(ed.plugins, function(p) {
12324                  if (p.createControl) {
12325                      c = p.createControl(n, t);
12326  
12327                      if (c)
12328                          return false;
12329                  }
12330              });
12331  
12332              switch (n) {
12333                  case "|":
12334                  case "separator":
12335                      return t.createSeparator();
12336              }
12337  
12338              if (!c && ed.buttons && (c = ed.buttons[n]))
12339                  return t.createButton(n, c);
12340  
12341              return t.add(c);
12342          },
12343  
12344          createDropMenu : function(id, s, cc) {
12345              var t = this, ed = t.editor, c, bm, v, cls;
12346  
12347              s = extend({
12348                  'class' : 'mceDropDown',
12349                  constrain : ed.settings.constrain_menus
12350              }, s);
12351  
12352              s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
12353              if (v = ed.getParam('skin_variant'))
12354                  s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
12355  
12356              id = t.prefix + id;
12357              cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
12358              c = t.controls[id] = new cls(id, s);
12359              c.onAddItem.add(function(c, o) {
12360                  var s = o.settings;
12361  
12362                  s.title = ed.getLang(s.title, s.title);
12363  
12364                  if (!s.onclick) {
12365                      s.onclick = function(v) {
12366                          ed.execCommand(s.cmd, s.ui || false, s.value);
12367                      };
12368                  }
12369              });
12370  
12371              ed.onRemove.add(function() {
12372                  c.destroy();
12373              });
12374  
12375              // Fix for bug #1897785, #1898007
12376              if (tinymce.isIE) {
12377                  c.onShowMenu.add(function() {
12378                      // IE 8 needs focus in order to store away a range with the current collapsed caret location
12379                      ed.focus();
12380  
12381                      bm = ed.selection.getBookmark(1);
12382                  });
12383  
12384                  c.onHideMenu.add(function() {
12385                      if (bm) {
12386                          ed.selection.moveToBookmark(bm);
12387                          bm = 0;
12388                      }
12389                  });
12390              }
12391  
12392              return t.add(c);
12393          },
12394  
12395          createListBox : function(id, s, cc) {
12396              var t = this, ed = t.editor, cmd, c, cls;
12397  
12398              if (t.get(id))
12399                  return null;
12400  
12401              s.title = ed.translate(s.title);
12402              s.scope = s.scope || ed;
12403  
12404              if (!s.onselect) {
12405                  s.onselect = function(v) {
12406                      ed.execCommand(s.cmd, s.ui || false, v || s.value);
12407                  };
12408              }
12409  
12410              s = extend({
12411                  title : s.title,
12412                  'class' : 'mce_' + id,
12413                  scope : s.scope,
12414                  control_manager : t
12415              }, s);
12416  
12417              id = t.prefix + id;
12418  
12419              if (ed.settings.use_native_selects)
12420                  c = new tinymce.ui.NativeListBox(id, s);
12421              else {
12422                  cls = cc || t._cls.listbox || tinymce.ui.ListBox;
12423                  c = new cls(id, s);
12424              }
12425  
12426              t.controls[id] = c;
12427  
12428              // Fix focus problem in Safari
12429              if (tinymce.isWebKit) {
12430                  c.onPostRender.add(function(c, n) {
12431                      // Store bookmark on mousedown
12432                      Event.add(n, 'mousedown', function() {
12433                          ed.bookmark = ed.selection.getBookmark(1);
12434                      });
12435  
12436                      // Restore on focus, since it might be lost
12437                      Event.add(n, 'focus', function() {
12438                          ed.selection.moveToBookmark(ed.bookmark);
12439                          ed.bookmark = null;
12440                      });
12441                  });
12442              }
12443  
12444              if (c.hideMenu)
12445                  ed.onMouseDown.add(c.hideMenu, c);
12446  
12447              return t.add(c);
12448          },
12449  
12450          createButton : function(id, s, cc) {
12451              var t = this, ed = t.editor, o, c, cls;
12452  
12453              if (t.get(id))
12454                  return null;
12455  
12456              s.title = ed.translate(s.title);
12457              s.label = ed.translate(s.label);
12458              s.scope = s.scope || ed;
12459  
12460              if (!s.onclick && !s.menu_button) {
12461                  s.onclick = function() {
12462                      ed.execCommand(s.cmd, s.ui || false, s.value);
12463                  };
12464              }
12465  
12466              s = extend({
12467                  title : s.title,
12468                  'class' : 'mce_' + id,
12469                  unavailable_prefix : ed.getLang('unavailable', ''),
12470                  scope : s.scope,
12471                  control_manager : t
12472              }, s);
12473  
12474              id = t.prefix + id;
12475  
12476              if (s.menu_button) {
12477                  cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
12478                  c = new cls(id, s);
12479                  ed.onMouseDown.add(c.hideMenu, c);
12480              } else {
12481                  cls = t._cls.button || tinymce.ui.Button;
12482                  c = new cls(id, s);
12483              }
12484  
12485              return t.add(c);
12486          },
12487  
12488          createMenuButton : function(id, s, cc) {
12489              s = s || {};
12490              s.menu_button = 1;
12491  
12492              return this.createButton(id, s, cc);
12493          },
12494  
12495          createSplitButton : function(id, s, cc) {
12496              var t = this, ed = t.editor, cmd, c, cls;
12497  
12498              if (t.get(id))
12499                  return null;
12500  
12501              s.title = ed.translate(s.title);
12502              s.scope = s.scope || ed;
12503  
12504              if (!s.onclick) {
12505                  s.onclick = function(v) {
12506                      ed.execCommand(s.cmd, s.ui || false, v || s.value);
12507                  };
12508              }
12509  
12510              if (!s.onselect) {
12511                  s.onselect = function(v) {
12512                      ed.execCommand(s.cmd, s.ui || false, v || s.value);
12513                  };
12514              }
12515  
12516              s = extend({
12517                  title : s.title,
12518                  'class' : 'mce_' + id,
12519                  scope : s.scope,
12520                  control_manager : t
12521              }, s);
12522  
12523              id = t.prefix + id;
12524              cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
12525              c = t.add(new cls(id, s));
12526              ed.onMouseDown.add(c.hideMenu, c);
12527  
12528              return c;
12529          },
12530  
12531          createColorSplitButton : function(id, s, cc) {
12532              var t = this, ed = t.editor, cmd, c, cls, bm;
12533  
12534              if (t.get(id))
12535                  return null;
12536  
12537              s.title = ed.translate(s.title);
12538              s.scope = s.scope || ed;
12539  
12540              if (!s.onclick) {
12541                  s.onclick = function(v) {
12542                      if (tinymce.isIE)
12543                          bm = ed.selection.getBookmark(1);
12544  
12545                      ed.execCommand(s.cmd, s.ui || false, v || s.value);
12546                  };
12547              }
12548  
12549              if (!s.onselect) {
12550                  s.onselect = function(v) {
12551                      ed.execCommand(s.cmd, s.ui || false, v || s.value);
12552                  };
12553              }
12554  
12555              s = extend({
12556                  title : s.title,
12557                  'class' : 'mce_' + id,
12558                  'menu_class' : ed.getParam('skin') + 'Skin',
12559                  scope : s.scope,
12560                  more_colors_title : ed.getLang('more_colors')
12561              }, s);
12562  
12563              id = t.prefix + id;
12564              cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
12565              c = new cls(id, s);
12566              ed.onMouseDown.add(c.hideMenu, c);
12567  
12568              // Remove the menu element when the editor is removed
12569              ed.onRemove.add(function() {
12570                  c.destroy();
12571              });
12572  
12573              // Fix for bug #1897785, #1898007
12574              if (tinymce.isIE) {
12575                  c.onShowMenu.add(function() {
12576                      // IE 8 needs focus in order to store away a range with the current collapsed caret location
12577                      ed.focus();
12578                      bm = ed.selection.getBookmark(1);
12579                  });
12580  
12581                  c.onHideMenu.add(function() {
12582                      if (bm) {
12583                          ed.selection.moveToBookmark(bm);
12584                          bm = 0;
12585                      }
12586                  });
12587              }
12588  
12589              return t.add(c);
12590          },
12591  
12592          createToolbar : function(id, s, cc) {
12593              var c, t = this, cls;
12594  
12595              id = t.prefix + id;
12596              cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
12597              c = new cls(id, s);
12598  
12599              if (t.get(id))
12600                  return null;
12601  
12602              return t.add(c);
12603          },
12604  
12605          createSeparator : function(cc) {
12606              var cls = cc || this._cls.separator || tinymce.ui.Separator;
12607  
12608              return new cls();
12609          },
12610  
12611          setControlType : function(n, c) {
12612              return this._cls[n.toLowerCase()] = c;
12613          },
12614      
12615          destroy : function() {
12616              each(this.controls, function(c) {
12617                  c.destroy();
12618              });
12619  
12620              this.controls = null;
12621          }
12622      });
12623  })(tinymce);
12624  (function(tinymce) {
12625      var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
12626  
12627      tinymce.create('tinymce.WindowManager', {
12628          WindowManager : function(ed) {
12629              var t = this;
12630  
12631              t.editor = ed;
12632              t.onOpen = new Dispatcher(t);
12633              t.onClose = new Dispatcher(t);
12634              t.params = {};
12635              t.features = {};
12636          },
12637  
12638          open : function(s, p) {
12639              var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
12640  
12641              // Default some options
12642              s = s || {};
12643              p = p || {};
12644              sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
12645              sh = isOpera ? vp.h : screen.height;
12646              s.name = s.name || 'mc_' + new Date().getTime();
12647              s.width = parseInt(s.width || 320);
12648              s.height = parseInt(s.height || 240);
12649              s.resizable = true;
12650              s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
12651              s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
12652              p.inline = false;
12653              p.mce_width = s.width;
12654              p.mce_height = s.height;
12655              p.mce_auto_focus = s.auto_focus;
12656  
12657              if (mo) {
12658                  if (isIE) {
12659                      s.center = true;
12660                      s.help = false;
12661                      s.dialogWidth = s.width + 'px';
12662                      s.dialogHeight = s.height + 'px';
12663                      s.scroll = s.scrollbars || false;
12664                  }
12665              }
12666  
12667              // Build features string
12668              each(s, function(v, k) {
12669                  if (tinymce.is(v, 'boolean'))
12670                      v = v ? 'yes' : 'no';
12671  
12672                  if (!/^(name|url)$/.test(k)) {
12673                      if (isIE && mo)
12674                          f += (f ? ';' : '') + k + ':' + v;
12675                      else
12676                          f += (f ? ',' : '') + k + '=' + v;
12677                  }
12678              });
12679  
12680              t.features = s;
12681              t.params = p;
12682              t.onOpen.dispatch(t, s, p);
12683  
12684              u = s.url || s.file;
12685              u = tinymce._addVer(u);
12686  
12687              try {
12688                  if (isIE && mo) {
12689                      w = 1;
12690                      window.showModalDialog(u, window, f);
12691                  } else
12692                      w = window.open(u, s.name, f);
12693              } catch (ex) {
12694                  // Ignore
12695              }
12696  
12697              if (!w)
12698                  alert(t.editor.getLang('popup_blocked'));
12699          },
12700  
12701          close : function(w) {
12702              w.close();
12703              this.onClose.dispatch(this);
12704          },
12705  
12706          createInstance : function(cl, a, b, c, d, e) {
12707              var f = tinymce.resolve(cl);
12708  
12709              return new f(a, b, c, d, e);
12710          },
12711  
12712          confirm : function(t, cb, s, w) {
12713              w = w || window;
12714  
12715              cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
12716          },
12717  
12718          alert : function(tx, cb, s, w) {
12719              var t = this;
12720  
12721              w = w || window;
12722              w.alert(t._decode(t.editor.getLang(tx, tx)));
12723  
12724              if (cb)
12725                  cb.call(s || t);
12726          },
12727  
12728          // Internal functions
12729  
12730          _decode : function(s) {
12731              return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
12732          }
12733      });
12734  }(tinymce));(function(tinymce) {
12735      tinymce.CommandManager = function() {
12736          var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};
12737  
12738  		function add(collection, cmd, func, scope) {
12739              if (typeof(cmd) == 'string')
12740                  cmd = [cmd];
12741  
12742              tinymce.each(cmd, function(cmd) {
12743                  collection[cmd.toLowerCase()] = {func : func, scope : scope};
12744              });
12745          };
12746  
12747          tinymce.extend(this, {
12748              add : function(cmd, func, scope) {
12749                  add(execCommands, cmd, func, scope);
12750              },
12751  
12752              addQueryStateHandler : function(cmd, func, scope) {
12753                  add(queryStateCommands, cmd, func, scope);
12754              },
12755  
12756              addQueryValueHandler : function(cmd, func, scope) {
12757                  add(queryValueCommands, cmd, func, scope);
12758              },
12759  
12760              execCommand : function(scope, cmd, ui, value, args) {
12761                  if (cmd = execCommands[cmd.toLowerCase()]) {
12762                      if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)
12763                          return true;
12764                  }
12765              },
12766  
12767              queryCommandValue : function() {
12768                  if (cmd = queryValueCommands[cmd.toLowerCase()])
12769                      return cmd.func.call(scope || cmd.scope, ui, value, args);
12770              },
12771  
12772              queryCommandState : function() {
12773                  if (cmd = queryStateCommands[cmd.toLowerCase()])
12774                      return cmd.func.call(scope || cmd.scope, ui, value, args);
12775              }
12776          });
12777      };
12778  
12779      tinymce.GlobalCommands = new tinymce.CommandManager();
12780  })(tinymce);(function(tinymce) {
12781  	function processRange(dom, start, end, callback) {
12782          var ancestor, n, startPoint, endPoint, sib;
12783  
12784  		function findEndPoint(n, c) {
12785              do {
12786                  if (n.parentNode == c)
12787                      return n;
12788  
12789                  n = n.parentNode;
12790              } while(n);
12791          };
12792  
12793  		function process(n) {
12794              callback(n);
12795              tinymce.walk(n, callback, 'childNodes');
12796          };
12797  
12798          // Find common ancestor and end points
12799          ancestor = dom.findCommonAncestor(start, end);
12800          startPoint = findEndPoint(start, ancestor) || start;
12801          endPoint = findEndPoint(end, ancestor) || end;
12802  
12803          // Process left leaf
12804          for (n = start; n && n != startPoint; n = n.parentNode) {
12805              for (sib = n.nextSibling; sib; sib = sib.nextSibling)
12806                  process(sib);
12807          }
12808  
12809          // Process middle from start to end point
12810          if (startPoint != endPoint) {
12811              for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling)
12812                  process(n);
12813          } else
12814              process(startPoint);
12815  
12816          // Process right leaf
12817          for (n = end; n && n != endPoint; n = n.parentNode) {
12818              for (sib = n.previousSibling; sib; sib = sib.previousSibling)
12819                  process(sib);
12820          }
12821      };
12822  
12823      tinymce.GlobalCommands.add('RemoveFormat', function() {
12824          var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n;
12825  
12826  		function findFormatRoot(n) {
12827              var sp;
12828  
12829              dom.getParent(n, function(n) {
12830                  if (dom.is(n, ed.getParam('removeformat_selector')))
12831                      sp = n;
12832  
12833                  return dom.isBlock(n);
12834              }, ed.getBody());
12835  
12836              return sp;
12837          };
12838  
12839  		function collect(n) {
12840              if (dom.is(n, ed.getParam('removeformat_selector')))
12841                  nodes.push(n);
12842          };
12843  
12844  		function walk(n) {
12845              collect(n);
12846              tinymce.walk(n, collect, 'childNodes');
12847          };
12848  
12849          bm = s.getBookmark();
12850          sc = r.startContainer;
12851          ec = r.endContainer;
12852          so = r.startOffset;
12853          eo = r.endOffset;
12854          sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;
12855          ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;
12856  
12857          // Same container
12858          if (sc == ec) { // TEXT_NODE
12859              start = findFormatRoot(sc);
12860  
12861              // Handle single text node
12862              if (sc.nodeType == 3) {
12863                  if (start && start.nodeType == 1) { // ELEMENT
12864                      n = sc.splitText(so);
12865                      n.splitText(eo - so);
12866                      dom.split(start, n);
12867  
12868                      s.moveToBookmark(bm);
12869                  }
12870  
12871                  return;
12872              }
12873  
12874              // Handle single element
12875              walk(dom.split(start, sc) || sc);
12876          } else {
12877              // Find start/end format root
12878              start = findFormatRoot(sc);
12879              end = findFormatRoot(ec);
12880  
12881              // Split start text node
12882              if (start) {
12883                  if (sc.nodeType == 3) { // TEXT
12884                      // Since IE doesn't support white space nodes in the DOM we need to
12885                      // add this invisible character so that the splitText function can split the contents
12886                      if (so == sc.nodeValue.length)
12887                          sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix
12888  
12889                      sc = sc.splitText(so);
12890                  }
12891              }
12892  
12893              // Split end text node
12894              if (end) {
12895                  if (ec.nodeType == 3) // TEXT
12896                      ec.splitText(eo);
12897              }
12898  
12899              // If the start and end format root is the same then we need to wrap
12900              // the end node in a span since the split calls might change the reference
12901              // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p>
12902              if (start && start == end)
12903                  dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec);
12904  
12905              // Split all start containers down to the format root
12906              if (start)
12907                  start = dom.split(start, sc);
12908              else
12909                  start = sc;
12910  
12911              // If there is a span wrapper use that one instead
12912              if (n = dom.get('__end')) {
12913                  ec = n;
12914                  end = findFormatRoot(ec);
12915              }
12916  
12917              // Split all end containers down to the format root
12918              if (end)
12919                  end = dom.split(end, ec);
12920              else
12921                  end = ec;
12922  
12923              // Collect nodes in between
12924              processRange(dom, start, end, collect);
12925  
12926              // Remove invisible character for IE workaround if we find it
12927              if (sc.nodeValue == '\uFEFF')
12928                  sc.nodeValue = '';
12929  
12930              // Process start/end container elements
12931              walk(ec);
12932              walk(sc);
12933          }
12934  
12935          // Remove all collected nodes
12936          tinymce.each(nodes, function(n) {
12937              dom.remove(n, 1);
12938          });
12939  
12940          // Remove leftover wrapper
12941          dom.remove('__end', 1);
12942  
12943          s.moveToBookmark(bm);
12944      });
12945  })(tinymce);
12946  (function(tinymce) {
12947      tinymce.GlobalCommands.add('mceBlockQuote', function() {
12948          var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;
12949  
12950  		function getBQ(e) {
12951              return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
12952          };
12953  
12954          // Get start/end block
12955          sb = dom.getParent(s.getStart(), dom.isBlock);
12956          eb = dom.getParent(s.getEnd(), dom.isBlock);
12957  
12958          // Remove blockquote(s)
12959          if (bq = getBQ(sb)) {
12960              if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
12961                  bm = s.getBookmark();
12962  
12963              // Move all elements after the end block into new bq
12964              if (getBQ(eb)) {
12965                  bq2 = bq.cloneNode(false);
12966  
12967                  while (n = eb.nextSibling)
12968                      bq2.appendChild(n.parentNode.removeChild(n));
12969              }
12970  
12971              // Add new bq after
12972              if (bq2)
12973                  dom.insertAfter(bq2, bq);
12974  
12975              // Move all selected blocks after the current bq
12976              nl = s.getSelectedBlocks(sb, eb);
12977              for (i = nl.length - 1; i >= 0; i--) {
12978                  dom.insertAfter(nl[i], bq);
12979              }
12980  
12981              // Empty bq, then remove it
12982              if (/^\s*$/.test(bq.innerHTML))
12983                  dom.remove(bq, 1); // Keep children so boomark restoration works correctly
12984  
12985              // Empty bq, then remote it
12986              if (bq2 && /^\s*$/.test(bq2.innerHTML))
12987                  dom.remove(bq2, 1); // Keep children so boomark restoration works correctly
12988  
12989              if (!bm) {
12990                  // Move caret inside empty block element
12991                  if (!tinymce.isIE) {
12992                      r = ed.getDoc().createRange();
12993                      r.setStart(sb, 0);
12994                      r.setEnd(sb, 0);
12995                      s.setRng(r);
12996                  } else {
12997                      s.select(sb);
12998                      s.collapse(0);
12999  
13000                      // IE misses the empty block some times element so we must move back the caret
13001                      if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
13002                          r = s.getRng();
13003                          r.move('character', -1);
13004                          r.select();
13005                      }
13006                  }
13007              } else
13008                  ed.selection.moveToBookmark(bm);
13009  
13010              return;
13011          }
13012  
13013          // Since IE can start with a totally empty document we need to add the first bq and paragraph
13014          if (tinymce.isIE && !sb && !eb) {
13015              ed.getDoc().execCommand('Indent');
13016              n = getBQ(s.getNode());
13017              n.style.margin = n.dir = ''; // IE adds margin and dir to bq
13018              return;
13019          }
13020  
13021          if (!sb || !eb)
13022              return;
13023  
13024          // If empty paragraph node then do not use bookmark
13025          if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
13026              bm = s.getBookmark();
13027  
13028          // Move selected block elements into a bq
13029          tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
13030              // Found existing BQ add to this one
13031              if (e.nodeName == 'BLOCKQUOTE' && !bq) {
13032                  bq = e;
13033                  return;
13034              }
13035  
13036              // No BQ found, create one
13037              if (!bq) {
13038                  bq = dom.create('blockquote');
13039                  e.parentNode.insertBefore(bq, e);
13040              }
13041  
13042              // Add children from existing BQ
13043              if (e.nodeName == 'BLOCKQUOTE' && bq) {
13044                  n = e.firstChild;
13045  
13046                  while (n) {
13047                      bq.appendChild(n.cloneNode(true));
13048                      n = n.nextSibling;
13049                  }
13050  
13051                  dom.remove(e);
13052                  return;
13053              }
13054  
13055              // Add non BQ element to BQ
13056              bq.appendChild(dom.remove(e));
13057          });
13058  
13059          if (!bm) {
13060              // Move caret inside empty block element
13061              if (!tinymce.isIE) {
13062                  r = ed.getDoc().createRange();
13063                  r.setStart(sb, 0);
13064                  r.setEnd(sb, 0);
13065                  s.setRng(r);
13066              } else {
13067                  s.select(sb);
13068                  s.collapse(1);
13069              }
13070          } else
13071              s.moveToBookmark(bm);
13072      });
13073  })(tinymce);
13074  (function(tinymce) {
13075      tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) {
13076          tinymce.GlobalCommands.add(cmd, function() {
13077              var ed = this, doc = ed.getDoc();
13078  
13079              try {
13080                  doc.execCommand(cmd, false, null);
13081  
13082                  // On WebKit the command will just be ignored if it's not enabled
13083                  if (!doc.queryCommandSupported(cmd))
13084                      throw 'Error';
13085              } catch (ex) {
13086                  if (tinymce.isGecko) {
13087                      ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {
13088                          if (s)
13089                              open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');
13090                      });
13091                  } else
13092                      ed.windowManager.alert(ed.getLang('clipboard_no_support'));
13093              }
13094          });
13095      });
13096  })(tinymce);
13097  (function(tinymce) {
13098      tinymce.GlobalCommands.add('InsertHorizontalRule', function() {
13099          if (tinymce.isOpera)
13100              return this.getDoc().execCommand('InsertHorizontalRule', false, '');
13101  
13102          this.selection.setContent('<hr />');
13103      });
13104  })(tinymce);
13105  (function() {
13106      var cmds = tinymce.GlobalCommands;
13107  
13108      cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() {
13109          this.undoManager.add();
13110      });
13111  
13112      cmds.add('Undo', function() {
13113          var ed = this;
13114  
13115          if (ed.settings.custom_undo_redo) {
13116              ed.undoManager.undo();
13117              ed.nodeChanged();
13118              return true;
13119          }
13120  
13121          return false; // Run browser command
13122      });
13123  
13124      cmds.add('Redo', function() {
13125          var ed = this;
13126  
13127          if (ed.settings.custom_undo_redo) {
13128              ed.undoManager.redo();
13129              ed.nodeChanged();
13130              return true;
13131          }
13132  
13133          return false; // Run browser command
13134      });
13135  })();
element ', [['libraries/joomla/html','html.php',159]], 29], 'replacechild': ['replacechild', 'Replaces a node with another ', [['libraries/domit','xml_domit_lite_parser.php',104],['libraries/domit','xml_domit_lite_parser.php',440],['libraries/domit','xml_domit_lite_parser.php',826],['libraries/domit','xml_domit_parser.php',116],['libraries/domit','xml_domit_parser.php',560],['libraries/domit','xml_domit_parser.php',1144]], 30], 'stop': ['stop', '', [['plugins/editors/tinymce/jscripts/tiny_mce','tiny_mce_src.js',355],['libraries/domit','timer.php',11]], 26], 'createdocument': ['createdocument', 'Creates a new DOMIT_Document node and appends a documentElement with the specified info ', [['libraries/domit','xml_domit_shared.php',233],['libraries/domit','xml_domit_rss_shared.php',568]], 4], 'createdocumentfragment': ['createdocumentfragment', 'Creates a new DOMIT_DocumentFragment node ', [['libraries/domit','xml_domit_parser.php',1294]], 9], 'getbase': ['getbase', '', [['plugins/editors/tinymce/jscripts/tiny_mce','tiny_mce_src.js',46],['libraries/joomla/document','document.php',569]], 5], 'menuitem': ['menuitem', 'Legacy function, use {@link MenusHelper::menuItem()} instead ', [['plugins/system/legacy','adminmenus.php',407]], 2], '_setup': ['_setup', 'Make sure that the directories in which we store our data exist. ', [['libraries/openid/Auth/OpenID','FileStore.php',86]], 11], 'date': ['date', '', [], 170], 'explode': ['explode', '', [], 291], 'each': ['each', '', [], 455], 'trim': ['trim', '', [], 453], 'sort': ['sort', '', [], 18], 'substr': ['substr', '', [], 890], 'unlink': ['unlink', '', [], 59], 'split': ['split', '', [], 234], 'abs': ['abs', '', [], 40], 'range': ['range', '', [], 10], 'max': ['max', '', [], 92], 'exec': ['exec', '', [], 50], 'serialize': ['serialize', '', [], 74], 'reset': ['reset', '', [], 54], 'min': ['min', '', [], 63]}; CLASS_DATA={ }; CONST_DATA={ };


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1