tinymce-rails 4.0.2 → 4.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/app/assets/source/tinymce/tinymce.jquery.js +975 -599
  2. data/app/assets/source/tinymce/tinymce.js +975 -599
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/jquery.tinymce.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/example/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/hr/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -0
  12. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/template/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/textcolor/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/css/visualblocks.css +14 -0
  23. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon-small.eot +0 -0
  25. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon-small.svg +150 -141
  26. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon-small.ttf +0 -0
  27. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon-small.woff +0 -0
  28. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon.eot +0 -0
  29. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon.svg +132 -129
  30. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon.ttf +0 -0
  31. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/icomoon.woff +0 -0
  32. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.ie7.min.css +1 -1
  33. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.min.css +1 -1
  34. data/vendor/assets/javascripts/tinymce/themes/modern/theme.js +1 -1
  35. data/vendor/assets/javascripts/tinymce/tinymce.jquery.js +9 -9
  36. data/vendor/assets/javascripts/tinymce/tinymce.js +10 -10
  37. metadata +3 -7
  38. data/vendor/assets/javascripts/tinymce/plugins/compat3x/editable_selects.js +0 -70
  39. data/vendor/assets/javascripts/tinymce/plugins/compat3x/form_utils.js +0 -210
  40. data/vendor/assets/javascripts/tinymce/plugins/compat3x/mctabs.js +0 -162
  41. data/vendor/assets/javascripts/tinymce/plugins/compat3x/tiny_mce_popup.js +0 -435
  42. data/vendor/assets/javascripts/tinymce/plugins/compat3x/validate.js +0 -252
@@ -1,4 +1,4 @@
1
- // 4.0.2 (2013-07-18)
1
+ // 4.0.6 (2013-09-12)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -437,6 +437,7 @@ define("tinymce/dom/EventUtils", [], function() {
437
437
 
438
438
  var eventExpandoPrefix = "mce-data-";
439
439
  var mouseEventRe = /^(?:mouse|contextmenu)|click/;
440
+ var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
440
441
 
441
442
  /**
442
443
  * Binds a native event to a callback on the speified target.
@@ -479,7 +480,7 @@ define("tinymce/dom/EventUtils", [], function() {
479
480
  // Copy all properties from the original event
480
481
  for (name in originalEvent) {
481
482
  // layerX/layerY is deprecated in Chrome and produces a warning
482
- if (name !== "layerX" && name !== "layerY") {
483
+ if (!deprecated[name]) {
483
484
  event[name] = originalEvent[name];
484
485
  }
485
486
  }
@@ -814,6 +815,13 @@ define("tinymce/dom/EventUtils", [], function() {
814
815
  ci = callbackList.length;
815
816
  while (ci--) {
816
817
  if (callbackList[ci].func === callback) {
818
+ var nativeHandler = callbackList.nativeHandler;
819
+
820
+ // Clone callbackList since unbind inside a callback would otherwise break the handlers loop
821
+ callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
822
+ callbackList.nativeHandler = nativeHandler;
823
+
824
+ eventMap[name] = callbackList;
817
825
  callbackList.splice(ci, 1);
818
826
  }
819
827
  }
@@ -1692,7 +1700,11 @@ define("tinymce/dom/Range", [
1692
1700
  if (o) {
1693
1701
  startContainer.insertBefore(n, o);
1694
1702
  } else {
1695
- startContainer.appendChild(n);
1703
+ if (startContainer.nodeType == 3) {
1704
+ dom.insertAfter(n, startContainer);
1705
+ } else {
1706
+ startContainer.appendChild(n);
1707
+ }
1696
1708
  }
1697
1709
  }
1698
1710
  }
@@ -2612,12 +2624,14 @@ define("tinymce/html/Entities", [
2612
2624
  */
2613
2625
  define("tinymce/Env", [], function() {
2614
2626
  var nav = navigator, userAgent = nav.userAgent;
2615
- var opera, webkit, ie, gecko, mac, iDevice;
2627
+ var opera, webkit, ie, ie11, gecko, mac, iDevice;
2616
2628
 
2617
2629
  opera = window.opera && window.opera.buildNumber;
2618
2630
  webkit = /WebKit/.test(userAgent);
2619
2631
  ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
2620
2632
  ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
2633
+ ie11 = userAgent.indexOf('Trident') != -1 ? 11 : false;
2634
+ ie = ie || ie11;
2621
2635
  gecko = !webkit && /Gecko/.test(userAgent);
2622
2636
  mac = userAgent.indexOf('Mac') != -1;
2623
2637
  iDevice = /(iPad|iPhone)/.test(userAgent);
@@ -2784,6 +2798,7 @@ define("tinymce/dom/DOMUtils", [
2784
2798
  self.stdMode = !isIE || doc.documentMode >= 8;
2785
2799
  self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
2786
2800
  self.hasOuterHTML = "outerHTML" in doc.createElement("a");
2801
+ this.boundEvents = [];
2787
2802
 
2788
2803
  self.settings = settings = extend({
2789
2804
  keep_values: false,
@@ -3415,7 +3430,7 @@ define("tinymce/dom/DOMUtils", [
3415
3430
  }
3416
3431
 
3417
3432
  // IE & Opera
3418
- if (name.currentStyle && computed) {
3433
+ if (elm.currentStyle && computed) {
3419
3434
  return elm.currentStyle[name];
3420
3435
  }
3421
3436
 
@@ -3712,8 +3727,8 @@ define("tinymce/dom/DOMUtils", [
3712
3727
  * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
3713
3728
  *
3714
3729
  * @method getPos
3715
- * @param {Element/String} n HTML element or element id to get x, y position from.
3716
- * @param {Element} ro Optional root element to stop calculations at.
3730
+ * @param {Element/String} elm HTML element or element id to get x, y position from.
3731
+ * @param {Element} rootElm Optional root element to stop calculations at.
3717
3732
  * @return {object} Absolute position of the specified element object with x, y fields.
3718
3733
  */
3719
3734
  getPos: function(elm, rootElm) {
@@ -4328,78 +4343,6 @@ define("tinymce/dom/DOMUtils", [
4328
4343
  return this.styles.toHex(Tools.trim(rgbVal));
4329
4344
  },
4330
4345
 
4331
- /**
4332
- * Returns an array of all single CSS classes in the document. A single CSS class is a simple
4333
- * rule like ".class" - complex ones like "div td.class" will not be added to output.
4334
- *
4335
- * @method getClasses
4336
- * @return {Array} Array with class objects - each object has a class field - might be other fields in the future.
4337
- */
4338
- getClasses: function() {
4339
- var self = this, classList = [], lookup = {}, filter = self.settings.class_filter, oldVal;
4340
-
4341
- if (self.classes) {
4342
- return self.classes;
4343
- }
4344
-
4345
- function addClasses(stylesheet) {
4346
- // IE style imports
4347
- each(stylesheet.imports, function(r) {
4348
- addClasses(r);
4349
- });
4350
-
4351
- each(stylesheet.cssRules || stylesheet.rules, function(rule) {
4352
- // Real type or fake it on IE
4353
- switch (rule.type || 1) {
4354
- // Rule
4355
- case 1:
4356
- if (rule.selectorText) {
4357
- each(rule.selectorText.split(','), function(value) {
4358
- value = value.replace(/^\s*|\s*$|^\s\./g, "");
4359
-
4360
- // Is internal or it doesn't contain a class
4361
- if (/\.mce/.test(value) || !/\.[\w\-]+$/.test(value)) {
4362
- return;
4363
- }
4364
-
4365
- // Remove everything but class name
4366
- oldVal = value;
4367
- value = value.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
4368
-
4369
- // Filter classes
4370
- if (filter && !(value = filter(value, oldVal))) {
4371
- return;
4372
- }
4373
-
4374
- if (!lookup[value]) {
4375
- classList.push({'class': value});
4376
- lookup[value] = 1;
4377
- }
4378
- });
4379
- }
4380
- break;
4381
-
4382
- // Import
4383
- case 3:
4384
- addClasses(rule.styleSheet);
4385
- break;
4386
- }
4387
- });
4388
- }
4389
-
4390
- try {
4391
- each(self.doc.styleSheets, addClasses);
4392
- } catch (ex) {
4393
- // Ignore
4394
- }
4395
-
4396
- if (classList.length > 0) {
4397
- self.classes = classList;
4398
- }
4399
-
4400
- return classList;
4401
- },
4402
-
4403
4346
  /**
4404
4347
  * Executes the specified function on the element by id or dom element node or array of elements/id.
4405
4348
  *
@@ -4696,28 +4639,70 @@ define("tinymce/dom/DOMUtils", [
4696
4639
  * Adds an event handler to the specified object.
4697
4640
  *
4698
4641
  * @method bind
4699
- * @param {Element/Document/Window/Array/String} o Object or element id string to add event
4642
+ * @param {Element/Document/Window/Array} target Target element to bind events to.
4700
4643
  * handler to or an array of elements/ids/documents.
4701
- * @param {String} n Name of event handler to add, for example: click.
4702
- * @param {function} f Function to execute when the event occurs.
4703
- * @param {Object} s Optional scope to execute the function in.
4644
+ * @param {String} name Name of event handler to add, for example: click.
4645
+ * @param {function} func Function to execute when the event occurs.
4646
+ * @param {Object} scope Optional scope to execute the function in.
4704
4647
  * @return {function} Function callback handler the same as the one passed in.
4705
4648
  */
4706
4649
  bind: function(target, name, func, scope) {
4707
- return this.events.bind(target, name, func, scope || this);
4650
+ var self = this;
4651
+
4652
+ if (Tools.isArray(target)) {
4653
+ var i = target.length;
4654
+
4655
+ while (i--) {
4656
+ target[i] = self.bind(target[i], name, func, scope);
4657
+ }
4658
+
4659
+ return target;
4660
+ }
4661
+
4662
+ // Collect all window/document events bound by editor instance
4663
+ if (self.settings.collect && (target === self.doc || target === self.win)) {
4664
+ self.boundEvents.push([target, name, func, scope]);
4665
+ }
4666
+
4667
+ return self.events.bind(target, name, func, scope || self);
4708
4668
  },
4709
4669
 
4710
4670
  /**
4711
4671
  * Removes the specified event handler by name and function from an element or collection of elements.
4712
4672
  *
4713
4673
  * @method unbind
4714
- * @param {String/Element/Array} o Element ID string or HTML element or an array of elements or ids to remove handler from.
4715
- * @param {String} n Event handler name, for example: "click"
4716
- * @param {function} f Function to remove.
4674
+ * @param {Element/Document/Window/Array} target Target element to unbind events on.
4675
+ * @param {String} name Event handler name, for example: "click"
4676
+ * @param {function} func Function to remove.
4717
4677
  * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
4718
4678
  * were passed in.
4719
4679
  */
4720
4680
  unbind: function(target, name, func) {
4681
+ var self = this, i;
4682
+
4683
+ if (Tools.isArray(target)) {
4684
+ i = target.length;
4685
+
4686
+ while (i--) {
4687
+ target[i] = self.unbind(target[i], name, func);
4688
+ }
4689
+
4690
+ return target;
4691
+ }
4692
+
4693
+ // Remove any bound events matching the input
4694
+ if (self.boundEvents && (target === self.doc || target === self.win)) {
4695
+ i = self.boundEvents.length;
4696
+
4697
+ while (i--) {
4698
+ var item = self.boundEvents[i];
4699
+
4700
+ if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
4701
+ this.events.unbind(item[0], item[1], item[2]);
4702
+ }
4703
+ }
4704
+ }
4705
+
4721
4706
  return this.events.unbind(target, name, func);
4722
4707
  },
4723
4708
 
@@ -4761,6 +4746,18 @@ define("tinymce/dom/DOMUtils", [
4761
4746
  destroy: function() {
4762
4747
  var self = this;
4763
4748
 
4749
+ // Unbind all events bound to window/document by editor instance
4750
+ if (self.boundEvents) {
4751
+ var i = self.boundEvents.length;
4752
+
4753
+ while (i--) {
4754
+ var item = self.boundEvents[i];
4755
+ this.events.unbind(item[0], item[1], item[2]);
4756
+ }
4757
+
4758
+ self.boundEvents = null;
4759
+ }
4760
+
4764
4761
  self.win = self.doc = self.root = self.events = self.frag = null;
4765
4762
  },
4766
4763
 
@@ -5089,182 +5086,180 @@ define("tinymce/dom/ScriptLoader", [
5089
5086
  * @class tinymce.AddOnManager
5090
5087
  */
5091
5088
  define("tinymce/AddOnManager", [
5092
- "tinymce/dom/ScriptLoader",
5093
- "tinymce/util/Tools"
5089
+ "tinymce/dom/ScriptLoader",
5090
+ "tinymce/util/Tools"
5094
5091
  ], function(ScriptLoader, Tools) {
5095
- var each = Tools.each;
5096
-
5097
- function AddOnManager() {
5098
- var self = this;
5099
-
5100
- self.items = [];
5101
- self.urls = {};
5102
- self.lookup = {};
5103
- }
5104
-
5105
- AddOnManager.prototype = {
5106
- /**
5107
- * Returns the specified add on by the short name.
5108
- *
5109
- * @method get
5110
- * @param {String} name Add-on to look for.
5111
- * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
5112
- */
5113
- get: function(name) {
5114
- if (this.lookup[name]) {
5115
- return this.lookup[name].instance;
5116
- } else {
5117
- return undefined;
5118
- }
5119
- },
5120
-
5121
- dependencies: function(name) {
5122
- var result;
5123
-
5124
- if (this.lookup[name]) {
5125
- result = this.lookup[name].dependencies;
5126
- }
5127
-
5128
- return result || [];
5129
- },
5130
-
5131
- /**
5132
- * Loads a language pack for the specified add-on.
5133
- *
5134
- * @method requireLangPack
5135
- * @param {String} name Short name of the add-on.
5136
- */
5137
- requireLangPack: function(name) {
5138
- var settings = AddOnManager.settings;
5139
-
5140
- if (settings && settings.language && settings.language_load !== false) {
5141
- ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + settings.language + '.js');
5142
- }
5143
- },
5144
-
5145
- /**
5146
- * Adds a instance of the add-on by it's short name.
5147
- *
5148
- * @method add
5149
- * @param {String} id Short name/id for the add-on.
5150
- * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
5151
- * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
5152
- * @example
5153
- * // Create a simple plugin
5154
- * tinymce.create('tinymce.plugins.TestPlugin', {
5155
- * TestPlugin: function(ed, url) {
5156
- * ed.on('click', function(e) {
5157
- * ed.windowManager.alert('Hello World!');
5158
- * });
5159
- * }
5160
- * });
5161
- *
5162
- * // Register plugin using the add method
5163
- * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
5164
- *
5165
- * // Initialize TinyMCE
5166
- * tinymce.init({
5167
- * ...
5168
- * plugins: '-test' // Init the plugin but don't try to load it
5169
- * });
5170
- */
5171
- add: function(id, addOn, dependencies) {
5172
- this.items.push(addOn);
5173
- this.lookup[id] = {instance: addOn, dependencies: dependencies};
5174
-
5175
- return addOn;
5176
- },
5177
-
5178
- createUrl: function(baseUrl, dep) {
5179
- if (typeof dep === "object") {
5180
- return dep;
5181
- } else {
5182
- return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
5183
- }
5184
- },
5185
-
5186
- /**
5187
- * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
5188
- * This should be used in development mode. A new compressor/javascript munger process will ensure that the
5189
- * components are put together into the plugin.js file and compressed correctly.
5190
- *
5191
- * @method addComponents
5192
- * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
5193
- * @param {Array} scripts Array containing the names of the scripts to load.
5194
- */
5195
- addComponents: function(pluginName, scripts) {
5196
- var pluginUrl = this.urls[pluginName];
5197
-
5198
- each(scripts, function(script) {
5199
- ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
5200
- });
5201
- },
5202
-
5203
- /**
5204
- * Loads an add-on from a specific url.
5205
- *
5206
- * @method load
5207
- * @param {String} n Short name of the add-on that gets loaded.
5208
- * @param {String} u URL to the add-on that will get loaded.
5209
- * @param {function} cb Optional callback to execute ones the add-on is loaded.
5210
- * @param {Object} s Optional scope to execute the callback in.
5211
- * @example
5212
- * // Loads a plugin from an external URL
5213
- * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
5214
- *
5215
- * // Initialize TinyMCE
5216
- * tinymce.init({
5217
- * ...
5218
- * plugins: '-myplugin' // Don't try to load it again
5219
- * });
5220
- */
5221
- load: function(n, u, cb, s) {
5222
- var t = this, url = u;
5223
-
5224
- function loadDependencies() {
5225
- var dependencies = t.dependencies(n);
5226
-
5227
- each(dependencies, function(dep) {
5228
- var newUrl = t.createUrl(u, dep);
5229
-
5230
- t.load(newUrl.resource, newUrl, undefined, undefined);
5231
- });
5232
-
5233
- if (cb) {
5234
- if (s) {
5235
- cb.call(s);
5236
- } else {
5237
- cb.call(ScriptLoader);
5238
- }
5239
- }
5240
- }
5241
-
5242
- if (t.urls[n]) {
5243
- return;
5244
- }
5245
-
5246
- if (typeof u === "object") {
5247
- url = u.prefix + u.resource + u.suffix;
5248
- }
5249
-
5250
- if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
5251
- url = AddOnManager.baseURL + '/' + url;
5252
- }
5253
-
5254
- t.urls[n] = url.substring(0, url.lastIndexOf('/'));
5255
-
5256
- if (t.lookup[n]) {
5257
- loadDependencies();
5258
- } else {
5259
- ScriptLoader.ScriptLoader.add(url, loadDependencies, s);
5260
- }
5261
- }
5262
- };
5263
-
5264
- AddOnManager.PluginManager = new AddOnManager();
5265
- AddOnManager.ThemeManager = new AddOnManager();
5266
-
5267
- return AddOnManager;
5092
+ var each = Tools.each;
5093
+
5094
+ function AddOnManager() {
5095
+ var self = this;
5096
+
5097
+ self.items = [];
5098
+ self.urls = {};
5099
+ self.lookup = {};
5100
+ }
5101
+
5102
+ AddOnManager.prototype = {
5103
+ /**
5104
+ * Returns the specified add on by the short name.
5105
+ *
5106
+ * @method get
5107
+ * @param {String} name Add-on to look for.
5108
+ * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
5109
+ */
5110
+ get: function(name) {
5111
+ if (this.lookup[name]) {
5112
+ return this.lookup[name].instance;
5113
+ } else {
5114
+ return undefined;
5115
+ }
5116
+ },
5117
+
5118
+ dependencies: function(name) {
5119
+ var result;
5120
+
5121
+ if (this.lookup[name]) {
5122
+ result = this.lookup[name].dependencies;
5123
+ }
5124
+
5125
+ return result || [];
5126
+ },
5127
+
5128
+ /**
5129
+ * Loads a language pack for the specified add-on.
5130
+ *
5131
+ * @method requireLangPack
5132
+ * @param {String} name Short name of the add-on.
5133
+ */
5134
+ requireLangPack: function(name) {
5135
+ if (AddOnManager.language && AddOnManager.languageLoad !== false) {
5136
+ ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + AddOnManager.language + '.js');
5137
+ }
5138
+ },
5139
+
5140
+ /**
5141
+ * Adds a instance of the add-on by it's short name.
5142
+ *
5143
+ * @method add
5144
+ * @param {String} id Short name/id for the add-on.
5145
+ * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
5146
+ * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
5147
+ * @example
5148
+ * // Create a simple plugin
5149
+ * tinymce.create('tinymce.plugins.TestPlugin', {
5150
+ * TestPlugin: function(ed, url) {
5151
+ * ed.on('click', function(e) {
5152
+ * ed.windowManager.alert('Hello World!');
5153
+ * });
5154
+ * }
5155
+ * });
5156
+ *
5157
+ * // Register plugin using the add method
5158
+ * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
5159
+ *
5160
+ * // Initialize TinyMCE
5161
+ * tinymce.init({
5162
+ * ...
5163
+ * plugins: '-test' // Init the plugin but don't try to load it
5164
+ * });
5165
+ */
5166
+ add: function(id, addOn, dependencies) {
5167
+ this.items.push(addOn);
5168
+ this.lookup[id] = {instance: addOn, dependencies: dependencies};
5169
+
5170
+ return addOn;
5171
+ },
5172
+
5173
+ createUrl: function(baseUrl, dep) {
5174
+ if (typeof dep === "object") {
5175
+ return dep;
5176
+ } else {
5177
+ return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
5178
+ }
5179
+ },
5180
+
5181
+ /**
5182
+ * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
5183
+ * This should be used in development mode. A new compressor/javascript munger process will ensure that the
5184
+ * components are put together into the plugin.js file and compressed correctly.
5185
+ *
5186
+ * @method addComponents
5187
+ * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
5188
+ * @param {Array} scripts Array containing the names of the scripts to load.
5189
+ */
5190
+ addComponents: function(pluginName, scripts) {
5191
+ var pluginUrl = this.urls[pluginName];
5192
+
5193
+ each(scripts, function(script) {
5194
+ ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
5195
+ });
5196
+ },
5197
+
5198
+ /**
5199
+ * Loads an add-on from a specific url.
5200
+ *
5201
+ * @method load
5202
+ * @param {String} n Short name of the add-on that gets loaded.
5203
+ * @param {String} u URL to the add-on that will get loaded.
5204
+ * @param {function} cb Optional callback to execute ones the add-on is loaded.
5205
+ * @param {Object} s Optional scope to execute the callback in.
5206
+ * @example
5207
+ * // Loads a plugin from an external URL
5208
+ * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
5209
+ *
5210
+ * // Initialize TinyMCE
5211
+ * tinymce.init({
5212
+ * ...
5213
+ * plugins: '-myplugin' // Don't try to load it again
5214
+ * });
5215
+ */
5216
+ load: function(n, u, cb, s) {
5217
+ var t = this, url = u;
5218
+
5219
+ function loadDependencies() {
5220
+ var dependencies = t.dependencies(n);
5221
+
5222
+ each(dependencies, function(dep) {
5223
+ var newUrl = t.createUrl(u, dep);
5224
+
5225
+ t.load(newUrl.resource, newUrl, undefined, undefined);
5226
+ });
5227
+
5228
+ if (cb) {
5229
+ if (s) {
5230
+ cb.call(s);
5231
+ } else {
5232
+ cb.call(ScriptLoader);
5233
+ }
5234
+ }
5235
+ }
5236
+
5237
+ if (t.urls[n]) {
5238
+ return;
5239
+ }
5240
+
5241
+ if (typeof u === "object") {
5242
+ url = u.prefix + u.resource + u.suffix;
5243
+ }
5244
+
5245
+ if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
5246
+ url = AddOnManager.baseURL + '/' + url;
5247
+ }
5248
+
5249
+ t.urls[n] = url.substring(0, url.lastIndexOf('/'));
5250
+
5251
+ if (t.lookup[n]) {
5252
+ loadDependencies();
5253
+ } else {
5254
+ ScriptLoader.ScriptLoader.add(url, loadDependencies, s);
5255
+ }
5256
+ }
5257
+ };
5258
+
5259
+ AddOnManager.PluginManager = new AddOnManager();
5260
+ AddOnManager.ThemeManager = new AddOnManager();
5261
+
5262
+ return AddOnManager;
5268
5263
  });
5269
5264
 
5270
5265
  /**
@@ -6402,7 +6397,13 @@ define("tinymce/html/Schema", [
6402
6397
 
6403
6398
  // Add elements clone if needed
6404
6399
  if (!elements[name]) {
6405
- elements[name] = elements[cloneName];
6400
+ var customRule = elements[cloneName];
6401
+
6402
+ customRule = extend({}, customRule);
6403
+ delete customRule.removeEmptyAttrs;
6404
+ delete customRule.removeEmpty;
6405
+
6406
+ elements[name] = customRule;
6406
6407
  }
6407
6408
 
6408
6409
  // Add custom elements at span/div positions
@@ -8289,7 +8290,7 @@ define("tinymce/dom/Serializer", [
8289
8290
  var DOM = DOMUtils.DOM;
8290
8291
 
8291
8292
  /**
8292
- * Constucts a new DOM serializer class.
8293
+ * Constructs a new DOM serializer class.
8293
8294
  *
8294
8295
  * @constructor
8295
8296
  * @method Serializer
@@ -8402,8 +8403,9 @@ define("tinymce/dom/Serializer", [
8402
8403
  value = node.firstChild ? node.firstChild.value : '';
8403
8404
 
8404
8405
  if (name === "script") {
8405
- // Remove mce- prefix from script elements
8406
- node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));
8406
+ // Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
8407
+ var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
8408
+ node.attr('type', type === 'text/javascript' ? null : type);
8407
8409
 
8408
8410
  if (value.length > 0) {
8409
8411
  node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
@@ -9216,10 +9218,9 @@ define("tinymce/dom/ControlSelection", [
9216
9218
  ], function(VK, Tools, Env) {
9217
9219
  return function(selection, editor) {
9218
9220
  var dom = editor.dom, each = Tools.each;
9219
- var selectedElm, selectedElmGhost, resizeHandles, selectedHandle;
9221
+ var selectedElm, selectedElmGhost, resizeHandles, selectedHandle, lastMouseDownEvent;
9220
9222
  var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
9221
- var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie;
9222
- var lastMouseDownEvent;
9223
+ var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
9223
9224
 
9224
9225
  // Details about each resize handle how to scale etc
9225
9226
  resizeHandles = {
@@ -9360,8 +9361,11 @@ define("tinymce/dom/ControlSelection", [
9360
9361
  function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
9361
9362
  var position, targetWidth, targetHeight, e, rect;
9362
9363
 
9364
+ // Fix when inline element is within a relaive container
9365
+ var offsetParent = editor.getBody().offsetParent || editor.getBody();
9366
+
9363
9367
  // Get position and size of target
9364
- position = dom.getPos(targetElm, editor.getBody());
9368
+ position = dom.getPos(targetElm, offsetParent);
9365
9369
  selectedElmX = position.x;
9366
9370
  selectedElmY = position.y;
9367
9371
  rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
@@ -9623,6 +9627,16 @@ define("tinymce/dom/ControlSelection", [
9623
9627
  });
9624
9628
  } else {
9625
9629
  disableGeckoResize();
9630
+
9631
+ if (Env.ie >= 11) {
9632
+ // TODO: Drag/drop doesn't work
9633
+ editor.on('mouseup mousedown', function(e) {
9634
+ if (e.target.nodeName == 'IMG' || editor.selection.getNode().nodeName == 'IMG') {
9635
+ e.preventDefault();
9636
+ editor.selection.select(e.target);
9637
+ }
9638
+ });
9639
+ }
9626
9640
  }
9627
9641
 
9628
9642
  editor.on('nodechange mousedown ResizeEditor', updateResizeRect);
@@ -10333,10 +10347,15 @@ define("tinymce/dom/Selection", [
10333
10347
  * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
10334
10348
  */
10335
10349
  select: function(node, content) {
10336
- var t = this, dom = t.dom, rng = dom.createRng(), idx;
10350
+ var self = this, dom = self.dom, rng = dom.createRng(), idx, nonEmptyElementsMap;
10351
+
10352
+ // Clear stored range set by FocusManager
10353
+ self.lastFocusBookmark = null;
10354
+
10355
+ nonEmptyElementsMap = dom.schema.getNonEmptyElements();
10337
10356
 
10338
10357
  function setPoint(node, start) {
10339
- var walker = new TreeWalker(node, node);
10358
+ var root = node, walker = new TreeWalker(node, root);
10340
10359
 
10341
10360
  do {
10342
10361
  // Text node
@@ -10350,21 +10369,34 @@ define("tinymce/dom/Selection", [
10350
10369
  return;
10351
10370
  }
10352
10371
 
10353
- // BR element
10354
- if (node.nodeName == 'BR') {
10372
+ // BR/IMG/INPUT elements
10373
+ if (nonEmptyElementsMap[node.nodeName]) {
10355
10374
  if (start) {
10356
10375
  rng.setStartBefore(node);
10357
10376
  } else {
10358
- rng.setEndBefore(node);
10377
+ if (node.nodeName == 'BR') {
10378
+ rng.setEndBefore(node);
10379
+ } else {
10380
+ rng.setEndAfter(node);
10381
+ }
10359
10382
  }
10360
10383
 
10361
10384
  return;
10362
10385
  }
10363
10386
  } while ((node = (start ? walker.next() : walker.prev())));
10387
+
10388
+ // Failed to find any text node or other suitable location then move to the root of body
10389
+ if (root.nodeName == 'BODY') {
10390
+ if (start) {
10391
+ rng.setStart(root, 0);
10392
+ } else {
10393
+ rng.setEnd(root, root.childNodes.length);
10394
+ }
10395
+ }
10364
10396
  }
10365
10397
 
10366
10398
  if (node) {
10367
- if (!content && t.controlSelection.controlSelect(node)) {
10399
+ if (!content && self.controlSelection.controlSelect(node)) {
10368
10400
  return;
10369
10401
  }
10370
10402
 
@@ -10378,7 +10410,7 @@ define("tinymce/dom/Selection", [
10378
10410
  setPoint(node);
10379
10411
  }
10380
10412
 
10381
- t.setRng(rng);
10413
+ self.setRng(rng);
10382
10414
  }
10383
10415
 
10384
10416
  return node;
@@ -10451,8 +10483,19 @@ define("tinymce/dom/Selection", [
10451
10483
 
10452
10484
  // Use last rng passed from FocusManager if it's available this enables
10453
10485
  // calls to editor.selection.getStart() to work when caret focus is lost on IE
10454
- if (!w3c && self.restoreRng) {
10455
- return self.restoreRng;
10486
+ if (!w3c && self.lastFocusBookmark) {
10487
+ var bookmark = self.lastFocusBookmark;
10488
+
10489
+ // Convert bookmark to range IE 11 fix
10490
+ if (bookmark.startContainer) {
10491
+ rng = doc.createRange();
10492
+ rng.setStart(bookmark.startContainer, bookmark.startOffset);
10493
+ rng.setEnd(bookmark.endContainer, bookmark.endOffset);
10494
+ } else {
10495
+ rng = bookmark;
10496
+ }
10497
+
10498
+ return rng;
10456
10499
  }
10457
10500
 
10458
10501
  // Found tridentSel object then we need to use that one
@@ -10546,12 +10589,11 @@ define("tinymce/dom/Selection", [
10546
10589
 
10547
10590
  try {
10548
10591
  sel.removeAllRanges();
10592
+ sel.addRange(rng);
10549
10593
  } catch (ex) {
10550
- // IE9 might throw errors here don't know why
10594
+ // IE might throw errors here if the editor is within a hidden container and selection is changed
10551
10595
  }
10552
10596
 
10553
- sel.addRange(rng);
10554
-
10555
10597
  // Forward is set to false and we have an extend function
10556
10598
  if (forward === false && sel.extend) {
10557
10599
  sel.collapse(rng.endContainer, rng.endOffset);
@@ -10955,13 +10997,57 @@ define("tinymce/dom/Selection", [
10955
10997
  return self;
10956
10998
  },
10957
10999
 
11000
+ getScrollContainer: function() {
11001
+ var scrollContainer, node = this.dom.getRoot();
11002
+
11003
+ while (node && node.nodeName != 'BODY') {
11004
+ if (node.scrollHeight > node.clientHeight) {
11005
+ scrollContainer = node;
11006
+ break;
11007
+ }
11008
+
11009
+ node = node.parentNode;
11010
+ }
11011
+
11012
+ return scrollContainer;
11013
+ },
11014
+
10958
11015
  scrollIntoView: function(elm) {
10959
- var y, viewPort, self = this, dom = self.dom;
11016
+ var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
11017
+
11018
+ function getPos(elm) {
11019
+ var x = 0, y = 0;
11020
+
11021
+ var offsetParent = elm;
11022
+ while (offsetParent && offsetParent.nodeType) {
11023
+ x += offsetParent.offsetLeft || 0;
11024
+ y += offsetParent.offsetTop || 0;
11025
+ offsetParent = offsetParent.offsetParent;
11026
+ }
11027
+
11028
+ return {x: x, y: y};
11029
+ }
11030
+
11031
+ if (root.nodeName != 'BODY') {
11032
+ var scrollContainer = self.getScrollContainer();
11033
+ if (scrollContainer) {
11034
+ y = getPos(elm).y - getPos(scrollContainer).y;
11035
+ viewPortH = scrollContainer.clientHeight;
11036
+ viewPortY = scrollContainer.scrollTop;
11037
+ if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
11038
+ scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
11039
+ }
11040
+
11041
+ return;
11042
+ }
11043
+ }
10960
11044
 
10961
11045
  viewPort = dom.getViewPort(self.editor.getWin());
10962
11046
  y = dom.getPos(elm).y;
10963
- if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
10964
- self.editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25);
11047
+ viewPortY = viewPort.y;
11048
+ viewPortH = viewPort.h;
11049
+ if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
11050
+ self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
10965
11051
  }
10966
11052
  },
10967
11053
 
@@ -11268,7 +11354,7 @@ define("tinymce/dom/RangeUtils", [
11268
11354
  /**
11269
11355
  * Text formatter engine class. This class is used to apply formats like bold, italic, font size
11270
11356
  * etc to the current selection or specific nodes. This engine was build to replace the browsers
11271
- * default formatting logic for execCommand due to it's inconsistant and buggy behavior.
11357
+ * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
11272
11358
  *
11273
11359
  * @class tinymce.Formatter
11274
11360
  * @example
@@ -11397,7 +11483,14 @@ define("tinymce/Formatter", [
11397
11483
  },
11398
11484
 
11399
11485
  removeformat: [
11400
- {selector: 'b,strong,em,i,font,u,strike', remove: 'all', split: true, expand: false, block_expand: true, deep: true},
11486
+ {
11487
+ selector: 'b,strong,em,i,font,u,strike,sub,sup',
11488
+ remove: 'all',
11489
+ split: true,
11490
+ expand: false,
11491
+ block_expand: true,
11492
+ deep: true
11493
+ },
11401
11494
  {selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
11402
11495
  {selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
11403
11496
  ]
@@ -12264,9 +12357,11 @@ define("tinymce/Formatter", [
12264
12357
  var startNode;
12265
12358
 
12266
12359
  function matchParents(node) {
12360
+ var root = dom.getRoot();
12361
+
12267
12362
  // Find first node with similar format settings
12268
12363
  node = dom.getParent(node, function(node) {
12269
- return !!matchNode(node, name, vars, true);
12364
+ return node.parentNode === root || !!matchNode(node, name, vars, true);
12270
12365
  });
12271
12366
 
12272
12367
  // Do an exact check on the similar format element
@@ -12342,8 +12437,9 @@ define("tinymce/Formatter", [
12342
12437
  for (x = formatList.length - 1; x >= 0; x--) {
12343
12438
  selector = formatList[x].selector;
12344
12439
 
12345
- // Format is not selector based, then always return TRUE
12346
- if (!selector) {
12440
+ // Format is not selector based then always return TRUE
12441
+ // Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
12442
+ if (!selector || formatList[x].defaultBlock) {
12347
12443
  return TRUE;
12348
12444
  }
12349
12445
 
@@ -12773,7 +12869,7 @@ define("tinymce/Formatter", [
12773
12869
  }
12774
12870
 
12775
12871
  function findBlockEndPoint(container, sibling_name) {
12776
- var node;
12872
+ var node, root = dom.getRoot();
12777
12873
 
12778
12874
  // Expand to block of similar type
12779
12875
  if (!format[0].wrapper) {
@@ -12782,7 +12878,10 @@ define("tinymce/Formatter", [
12782
12878
 
12783
12879
  // Expand to first wrappable block element or any block element
12784
12880
  if (!node) {
12785
- node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isTextBlock);
12881
+ node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
12882
+ // Fixes #6183 where it would expand to editable parent element in inline mode
12883
+ return node != root && isTextBlock(node);
12884
+ });
12786
12885
  }
12787
12886
 
12788
12887
  // Exclude inner lists from wrapping
@@ -12833,14 +12932,14 @@ define("tinymce/Formatter", [
12833
12932
 
12834
12933
  if (format[0].inline) {
12835
12934
  if (rng.collapsed) {
12836
- // Expand left to closest word boundery
12935
+ // Expand left to closest word boundary
12837
12936
  endPoint = findWordEndPoint(startContainer, startOffset, true);
12838
12937
  if (endPoint) {
12839
12938
  startContainer = endPoint.container;
12840
12939
  startOffset = endPoint.offset;
12841
12940
  }
12842
12941
 
12843
- // Expand right to closest word boundery
12942
+ // Expand right to closest word boundary
12844
12943
  endPoint = findWordEndPoint(endContainer, endOffset);
12845
12944
  if (endPoint) {
12846
12945
  endContainer = endPoint.container;
@@ -13536,6 +13635,10 @@ define("tinymce/Formatter", [
13536
13635
 
13537
13636
  // Move selection to text node
13538
13637
  selection.setCursorLocation(node, 1);
13638
+ // If the formatNode is empty, we can remove it safely.
13639
+ if(dom.isEmpty(formatNode)) {
13640
+ dom.remove(formatNode);
13641
+ }
13539
13642
  }
13540
13643
  }
13541
13644
 
@@ -13676,7 +13779,7 @@ define("tinymce/UndoManager", [
13676
13779
  ].join('|'), 'gi');
13677
13780
 
13678
13781
  return function(editor) {
13679
- var self, index = 0, data = [], beforeBookmark, isFirstTypedCharacter;
13782
+ var self, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, lock;
13680
13783
 
13681
13784
  // Returns a trimmed version of the current editor contents
13682
13785
  function getContent() {
@@ -13740,6 +13843,11 @@ define("tinymce/UndoManager", [
13740
13843
  // Make the it dirty if the content was changed after typing the first character
13741
13844
  if (!editor.isDirty()) {
13742
13845
  editor.isNotDirty = !data[0] || getContent() == data[0].content;
13846
+
13847
+ // Fire initial change event
13848
+ if (!editor.isNotDirty) {
13849
+ editor.fire('change', {level: data[0], lastLevel: null});
13850
+ }
13743
13851
  }
13744
13852
 
13745
13853
  editor.fire('TypingUndo');
@@ -13804,7 +13912,9 @@ define("tinymce/UndoManager", [
13804
13912
  * @method beforeChange
13805
13913
  */
13806
13914
  beforeChange: function() {
13807
- beforeBookmark = editor.selection.getBookmark(2, true);
13915
+ if (!lock) {
13916
+ beforeBookmark = editor.selection.getBookmark(2, true);
13917
+ }
13808
13918
  },
13809
13919
 
13810
13920
  /**
@@ -13820,7 +13930,7 @@ define("tinymce/UndoManager", [
13820
13930
  level = level || {};
13821
13931
  level.content = getContent();
13822
13932
 
13823
- if (editor.fire('BeforeAddUndo', {level: level}).isDefaultPrevented()) {
13933
+ if (lock || editor.fire('BeforeAddUndo', {level: level}).isDefaultPrevented()) {
13824
13934
  return null;
13825
13935
  }
13826
13936
 
@@ -13887,6 +13997,11 @@ define("tinymce/UndoManager", [
13887
13997
  if (index > 0) {
13888
13998
  level = data[--index];
13889
13999
 
14000
+ // Undo to first index then set dirty state to false
14001
+ if (index === 0) {
14002
+ editor.isNotDirty = true;
14003
+ }
14004
+
13890
14005
  editor.setContent(level.content, {format: 'raw'});
13891
14006
  editor.selection.moveToBookmark(level.beforeBookmark);
13892
14007
 
@@ -13953,14 +14068,19 @@ define("tinymce/UndoManager", [
13953
14068
  /**
13954
14069
  * Executes the specified function in an undo transation. The selection
13955
14070
  * before the modification will be stored to the undo stack and if the DOM changes
13956
- * it will add a new undo level.
14071
+ * it will add a new undo level. Any methods within the transation that adds undo levels will
14072
+ * be ignored. So a transation can include calls to execCommand or editor.insertContent.
13957
14073
  *
13958
14074
  * @method transact
13959
14075
  * @param {function} callback Function to execute dom manipulation logic in.
13960
14076
  */
13961
14077
  transact: function(callback) {
13962
14078
  self.beforeChange();
14079
+
14080
+ lock = true;
13963
14081
  callback();
14082
+ lock = false;
14083
+
13964
14084
  self.add();
13965
14085
  }
13966
14086
  };
@@ -13988,7 +14108,7 @@ define("tinymce/EnterKey", [
13988
14108
  "tinymce/dom/TreeWalker",
13989
14109
  "tinymce/Env"
13990
14110
  ], function(TreeWalker, Env) {
13991
- var isIE = Env.ie;
14111
+ var isIE = Env.ie && Env.ie < 11;
13992
14112
 
13993
14113
  return function(editor) {
13994
14114
  var dom = editor.dom, selection = editor.selection, settings = editor.settings;
@@ -14556,6 +14676,7 @@ define("tinymce/EnterKey", [
14556
14676
  // Insert new block before
14557
14677
  newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
14558
14678
  renderBlockOnIE(newBlock);
14679
+ moveToCaretPosition(parentBlock);
14559
14680
  } else {
14560
14681
  // Extract after fragment and insert it after the current block
14561
14682
  tmpRng = rng.cloneRange();
@@ -14713,7 +14834,7 @@ define("tinymce/ForceBlocks", [], function() {
14713
14834
 
14714
14835
  // Force root blocks
14715
14836
  if (settings.forced_root_block) {
14716
- editor.on('KeyUp NodeChange', addRootBlocks);
14837
+ editor.on('NodeChange', addRootBlocks);
14717
14838
  }
14718
14839
  };
14719
14840
  });
@@ -15086,8 +15207,8 @@ define("tinymce/EditorCommands", [
15086
15207
  parentNode = selection.getNode();
15087
15208
 
15088
15209
  // Parse the fragment within the context of the parent node
15089
- args = {context: parentNode.nodeName.toLowerCase()};
15090
- fragment = parser.parse(value, args);
15210
+ var parserArgs = {context: parentNode.nodeName.toLowerCase()};
15211
+ fragment = parser.parse(value, parserArgs);
15091
15212
 
15092
15213
  // Move the caret to a more suitable location
15093
15214
  node = fragment.lastChild;
@@ -15103,7 +15224,7 @@ define("tinymce/EditorCommands", [
15103
15224
  }
15104
15225
 
15105
15226
  // If parser says valid we can insert the contents into that parent
15106
- if (!args.invalid) {
15227
+ if (!parserArgs.invalid) {
15107
15228
  value = serializer.serialize(fragment);
15108
15229
 
15109
15230
  // Check if parent is empty or only has one BR element then set the innerHTML of that parent
@@ -15426,7 +15547,7 @@ define("tinymce/util/URI", [
15426
15547
  var each = Tools.each, trim = Tools.trim;
15427
15548
 
15428
15549
  /**
15429
- * Constucts a new URI instance.
15550
+ * Constructs a new URI instance.
15430
15551
  *
15431
15552
  * @constructor
15432
15553
  * @method URI
@@ -15956,7 +16077,7 @@ define("tinymce/util/Class", [
15956
16077
  * element[attr^=value]
15957
16078
  * element[attr$=value]
15958
16079
  * element:<state>
15959
- * element:not(<expession>)
16080
+ * element:not(<expression>)
15960
16081
  * element:first
15961
16082
  * element:last
15962
16083
  * element:odd
@@ -16249,11 +16370,11 @@ define("tinymce/ui/Selector", [
16249
16370
 
16250
16371
  // All filters matched the item
16251
16372
  if (fi === fl) {
16252
- // Matched item is on the last expession like: panel toolbar [button]
16373
+ // Matched item is on the last expression like: panel toolbar [button]
16253
16374
  if (index == selector.length - 1) {
16254
16375
  matches.push(item);
16255
16376
  } else {
16256
- // Collect next expession type
16377
+ // Collect next expression type
16257
16378
  if (item.items) {
16258
16379
  collect(item.items(), selector, index + 1);
16259
16380
  }
@@ -16557,6 +16678,22 @@ define("tinymce/ui/Collection", [
16557
16678
  });
16558
16679
 
16559
16680
  return self;
16681
+ },
16682
+
16683
+ /**
16684
+ * Remove all items from collection and DOM.
16685
+ *
16686
+ * @method remove
16687
+ * @return {tinymce.ui.Collection} Current collection.
16688
+ */
16689
+ remove: function() {
16690
+ var i = this.length;
16691
+
16692
+ while (i--) {
16693
+ this[i].remove();
16694
+ }
16695
+
16696
+ return this;
16560
16697
  }
16561
16698
 
16562
16699
  /**
@@ -16787,6 +16924,11 @@ define("tinymce/ui/DomUtils", [
16787
16924
 
16788
16925
  fire: function(target, name, args) {
16789
16926
  return DOMUtils.DOM.fire(target, name, args);
16927
+ },
16928
+
16929
+ innerHtml: function(elm, html) {
16930
+ // Workaround for <div> in <p> bug on IE 8 #6178
16931
+ DOMUtils.DOM.setHTML(elm, html);
16790
16932
  }
16791
16933
  };
16792
16934
  });
@@ -17681,6 +17823,18 @@ define("tinymce/ui/Control", [
17681
17823
  return classes ? classes.join(' ') : '';
17682
17824
  },
17683
17825
 
17826
+ /**
17827
+ * Sets the inner HTML of the control element.
17828
+ *
17829
+ * @method innerHtml
17830
+ * @param {String} html Html string to set as inner html.
17831
+ * @return {tinymce.ui.Control} Current control object.
17832
+ */
17833
+ innerHtml: function(html) {
17834
+ DomUtils.innerHtml(this.getEl(), html);
17835
+ return this;
17836
+ },
17837
+
17684
17838
  /**
17685
17839
  * Returns the control DOM element or sub element.
17686
17840
  *
@@ -17866,11 +18020,11 @@ define("tinymce/ui/Control", [
17866
18020
  * @return {tinymce.ui.Control} Current control instance.
17867
18021
  */
17868
18022
  remove: function() {
17869
- var self = this, elm = self.getEl(), parent = self.parent(), newItems;
18023
+ var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
17870
18024
 
17871
18025
  if (self.items) {
17872
18026
  var controls = self.items().toArray();
17873
- var i = controls.length;
18027
+ i = controls.length;
17874
18028
  while (i--) {
17875
18029
  controls[i].remove();
17876
18030
  }
@@ -17894,8 +18048,16 @@ define("tinymce/ui/Control", [
17894
18048
  }
17895
18049
 
17896
18050
  delete Control.controlIdLookup[self._id];
18051
+ delete elementIdCache[self._id];
18052
+
18053
+ if (elm && elm.parentNode) {
18054
+ var nodes = elm.getElementsByTagName('*');
18055
+
18056
+ i = nodes.length;
18057
+ while (i--) {
18058
+ delete elementIdCache[nodes[i].id];
18059
+ }
17897
18060
 
17898
- if (elm.parentNode) {
17899
18061
  elm.parentNode.removeChild(elm);
17900
18062
  }
17901
18063
 
@@ -18273,7 +18435,7 @@ define("tinymce/ui/Control", [
18273
18435
  */
18274
18436
  // title: function(value) {} -- Generated
18275
18437
  });
18276
-
18438
+ window.elementIdCache = elementIdCache;
18277
18439
  return Control;
18278
18440
  });
18279
18441
 
@@ -19865,7 +20027,7 @@ define("tinymce/ui/KeyboardNavigation", [
19865
20027
  * @setting {tinymce.ui.Control} root the root control navigation focus movement is scoped to this root.
19866
20028
  * @setting {Array} items an array containing the items to move focus between. Every object in this array must have an
19867
20029
  * id attribute which maps to the actual DOM element and it must be able to have focus i.e. tabIndex=-1.
19868
- * @setting {Function} onCancel the callback for when the user presses escape or otherwise indicates cancelling.
20030
+ * @setting {Function} onCancel the callback for when the user presses escape or otherwise indicates canceling.
19869
20031
  * @setting {Function} onAction (optional) the action handler to call when the user activates an item.
19870
20032
  * @setting {Boolean} enableLeftRight (optional, default) when true, the up/down arrows move through items.
19871
20033
  * @setting {Boolean} enableUpDown (optional) when true, the up/down arrows move through items.
@@ -20564,8 +20726,8 @@ define("tinymce/ui/Window", [
20564
20726
  remove: function() {
20565
20727
  var self = this;
20566
20728
 
20567
- self._super();
20568
20729
  self.dragHelper.destroy();
20730
+ self._super();
20569
20731
 
20570
20732
  if (self.statusbar) {
20571
20733
  this.statusbar.remove();
@@ -20821,6 +20983,12 @@ define("tinymce/WindowManager", [
20821
20983
  return function(editor) {
20822
20984
  var self = this, windows = [];
20823
20985
 
20986
+ function getTopMostWindow() {
20987
+ if (windows.length) {
20988
+ return windows[windows.length - 1];
20989
+ }
20990
+ }
20991
+
20824
20992
  self.windows = windows;
20825
20993
 
20826
20994
  /**
@@ -20840,6 +21008,10 @@ define("tinymce/WindowManager", [
20840
21008
  self.open = function(args, params) {
20841
21009
  var win;
20842
21010
 
21011
+ editor.editorManager.activeEditor = editor;
21012
+
21013
+ args.title = args.title || ' ';
21014
+
20843
21015
  // Handle URL
20844
21016
  args.url = args.url || args.file; // Legacy
20845
21017
  if (args.url) {
@@ -20897,7 +21069,8 @@ define("tinymce/WindowManager", [
20897
21069
  });
20898
21070
  }
20899
21071
 
20900
- // store parameters
21072
+ // store args and parameters
21073
+ win.features = args || {};
20901
21074
  win.params = params || {};
20902
21075
 
20903
21076
  // Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
@@ -20953,8 +21126,8 @@ define("tinymce/WindowManager", [
20953
21126
  * @method close
20954
21127
  */
20955
21128
  self.close = function() {
20956
- if (windows.length) {
20957
- windows[windows.length - 1].close();
21129
+ if (getTopMostWindow()) {
21130
+ getTopMostWindow().close();
20958
21131
  }
20959
21132
  };
20960
21133
 
@@ -20969,12 +21142,8 @@ define("tinymce/WindowManager", [
20969
21142
  * @return {Object} Name/value object with parameters passed from windowManager.open call.
20970
21143
  */
20971
21144
  self.getParams = function() {
20972
- if (windows.length) {
20973
- return windows[windows.length - 1].params;
20974
- }
20975
-
20976
- return null;
20977
- };
21145
+ return getTopMostWindow() ? getTopMostWindow().params : null;
21146
+ };
20978
21147
 
20979
21148
  /**
20980
21149
  * Sets the params of the last opened window.
@@ -20983,8 +21152,8 @@ define("tinymce/WindowManager", [
20983
21152
  * @param {Object} params Params object to set for the last opened window.
20984
21153
  */
20985
21154
  self.setParams = function(params) {
20986
- if (windows.length) {
20987
- windows[windows.length - 1].params = params;
21155
+ if (getTopMostWindow()) {
21156
+ getTopMostWindow().params = params;
20988
21157
  }
20989
21158
  };
20990
21159
  };
@@ -21257,7 +21426,9 @@ define("tinymce/util/Quirks", [
21257
21426
  * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
21258
21427
  * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
21259
21428
  * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
21260
- * browsers
21429
+ * browsers.
21430
+ *
21431
+ * It also fixes a bug on Firefox where it's impossible to delete HR elements.
21261
21432
  */
21262
21433
  function removeHrOnBackspace() {
21263
21434
  editor.on('keydown', function(e) {
@@ -21266,6 +21437,12 @@ define("tinymce/util/Quirks", [
21266
21437
  var node = selection.getNode();
21267
21438
  var previousSibling = node.previousSibling;
21268
21439
 
21440
+ if (node.nodeName == 'HR') {
21441
+ dom.remove(node);
21442
+ e.preventDefault();
21443
+ return;
21444
+ }
21445
+
21269
21446
  if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
21270
21447
  dom.remove(previousSibling);
21271
21448
  e.preventDefault();
@@ -21334,7 +21511,7 @@ define("tinymce/util/Quirks", [
21334
21511
  * Instead of:
21335
21512
  * <p style="color:red">bla|ed</p>
21336
21513
  */
21337
- function removeStylesWhenDeletingAccrossBlockElements() {
21514
+ function removeStylesWhenDeletingAcrossBlockElements() {
21338
21515
  function getAttributeApplyFunction() {
21339
21516
  var template = dom.getAttribs(selection.getStart().cloneNode(false));
21340
21517
 
@@ -21894,6 +22071,44 @@ define("tinymce/util/Quirks", [
21894
22071
  );
21895
22072
  }
21896
22073
 
22074
+ /**
22075
+ * iOS has a bug where it's impossible to type if the document has a touchstart event
22076
+ * bound and the user touches the document while having the on screen keyboard visible.
22077
+ *
22078
+ * The touch event moves the focus to the parent document while having the caret inside the iframe
22079
+ * this fix moves the focus back into the iframe document.
22080
+ */
22081
+ function restoreFocusOnKeyDown() {
22082
+ if (!editor.inline) {
22083
+ editor.on('keydown', function() {
22084
+ if (document.activeElement == document.body) {
22085
+ editor.getWin().focus();
22086
+ }
22087
+ });
22088
+ }
22089
+ }
22090
+
22091
+ /**
22092
+ * IE 11 has an annoying issue where you can't move focus into the editor
22093
+ * by clicking on the white area HTML element. We used to be able to to fix this with
22094
+ * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
22095
+ * object it's not possible anymore. So we need to hack in a ungly CSS to force the
22096
+ * body to be at least 150px. If the user clicks the HTML element out side this 150px region
22097
+ * we simply move the focus into the first paragraph. Not ideal since you loose the
22098
+ * positioning of the caret but goot enough for most cases.
22099
+ */
22100
+ function bodyHeight() {
22101
+ if (!editor.inline) {
22102
+ editor.contentStyles.push('body {min-height: 150px}');
22103
+ editor.on('click', function(e) {
22104
+ if (e.target.nodeName == 'HTML') {
22105
+ editor.execCommand('SelectAll');
22106
+ editor.selection.collapse(true);
22107
+ }
22108
+ });
22109
+ }
22110
+ }
22111
+
21897
22112
  // All browsers
21898
22113
  disableBackspaceIntoATable();
21899
22114
  removeBlockQuoteOnBackSpace();
@@ -21911,13 +22126,14 @@ define("tinymce/util/Quirks", [
21911
22126
  // iOS
21912
22127
  if (Env.iOS) {
21913
22128
  selectionChangeNodeChanged();
22129
+ restoreFocusOnKeyDown();
21914
22130
  } else {
21915
22131
  selectAll();
21916
22132
  }
21917
22133
  }
21918
22134
 
21919
22135
  // IE
21920
- if (isIE) {
22136
+ if (isIE && Env.ie < 11) {
21921
22137
  removeHrOnBackspace();
21922
22138
  ensureBodyHasRoleApplication();
21923
22139
  addNewLinesBeforeBrInPre();
@@ -21928,11 +22144,15 @@ define("tinymce/util/Quirks", [
21928
22144
  fixCaretSelectionOfDocumentElementOnIe();
21929
22145
  }
21930
22146
 
22147
+ if (Env.ie >= 11) {
22148
+ bodyHeight();
22149
+ }
22150
+
21931
22151
  // Gecko
21932
22152
  if (isGecko) {
21933
22153
  removeHrOnBackspace();
21934
22154
  focusBody();
21935
- removeStylesWhenDeletingAccrossBlockElements();
22155
+ removeStylesWhenDeletingAcrossBlockElements();
21936
22156
  setGeckoEditingOptions();
21937
22157
  addBrAfterLastLinks();
21938
22158
  removeGhostSelection();
@@ -22369,7 +22589,7 @@ define("tinymce/Editor", [
22369
22589
  var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
22370
22590
  var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
22371
22591
  var Event = EventUtils.Event;
22372
- var isGecko = Env.gecko, isIE = Env.ie, isOpera = Env.opera;
22592
+ var isGecko = Env.gecko, ie = Env.ie, isOpera = Env.opera;
22373
22593
 
22374
22594
  function getEventTarget(editor, eventName) {
22375
22595
  if (eventName == 'selectionchange' || eventName == 'drop') {
@@ -22456,8 +22676,9 @@ define("tinymce/Editor", [
22456
22676
  ie7_compat: true
22457
22677
  }, settings);
22458
22678
 
22459
- // TODO: Fix this
22460
- AddOnManager.settings = settings;
22679
+ AddOnManager.language = settings.language || 'en';
22680
+ AddOnManager.languageLoad = settings.language_load;
22681
+
22461
22682
  AddOnManager.baseURL = editorManager.baseURL;
22462
22683
 
22463
22684
  /**
@@ -22557,6 +22778,7 @@ define("tinymce/Editor", [
22557
22778
 
22558
22779
  // Call setup
22559
22780
  self.execCallback('setup', self);
22781
+ editorManager.fire('SetupEditor', self);
22560
22782
  }
22561
22783
 
22562
22784
  Editor.prototype = {
@@ -22568,16 +22790,17 @@ define("tinymce/Editor", [
22568
22790
  render: function() {
22569
22791
  var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
22570
22792
 
22793
+ function readyHandler() {
22794
+ DOM.unbind(window, 'ready', readyHandler);
22795
+ self.render();
22796
+ }
22797
+
22571
22798
  // Page is not loaded yet, wait for it
22572
22799
  if (!Event.domLoaded) {
22573
- DOM.bind(window, 'ready', function() {
22574
- self.render();
22575
- });
22800
+ DOM.bind(window, 'ready', readyHandler);
22576
22801
  return;
22577
22802
  }
22578
22803
 
22579
- self.editorManager.settings = settings;
22580
-
22581
22804
  // Element not found, then skip initialization
22582
22805
  if (!self.getElement()) {
22583
22806
  return;
@@ -22662,7 +22885,6 @@ define("tinymce/Editor", [
22662
22885
  self.on('submit', function() {
22663
22886
  if (self.initialized) {
22664
22887
  self.save();
22665
- self.isNotDirty = true;
22666
22888
  }
22667
22889
  });
22668
22890
  }
@@ -22670,7 +22892,7 @@ define("tinymce/Editor", [
22670
22892
  if (settings.add_unload_trigger) {
22671
22893
  self._beforeUnload = function() {
22672
22894
  if (self.initialized && !self.destroyed && !self.isHidden()) {
22673
- self.save({format: 'raw', no_events: true});
22895
+ self.save({format: 'raw', no_events: true, set_dirty: false});
22674
22896
  }
22675
22897
  };
22676
22898
 
@@ -22691,7 +22913,15 @@ define("tinymce/Editor", [
22691
22913
 
22692
22914
  if (settings.theme && typeof settings.theme != "function" &&
22693
22915
  settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
22694
- ThemeManager.load(settings.theme, 'themes/' + settings.theme + '/theme' + suffix + '.js');
22916
+ var themeUrl = settings.theme_url;
22917
+
22918
+ if (themeUrl) {
22919
+ themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
22920
+ } else {
22921
+ themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
22922
+ }
22923
+
22924
+ ThemeManager.load(settings.theme, themeUrl);
22695
22925
  }
22696
22926
 
22697
22927
  if (Tools.isArray(settings.plugins)) {
@@ -22929,7 +23159,7 @@ define("tinymce/Editor", [
22929
23159
 
22930
23160
  // Domain relaxing enabled, then set document domain
22931
23161
  // TODO: Fix this old stuff
22932
- if (self.editorManager.relaxedDomain && (isIE || (isOpera && parseFloat(window.opera.version()) < 11))) {
23162
+ if (self.editorManager.relaxedDomain && (ie || (isOpera && parseFloat(window.opera.version()) < 11))) {
22933
23163
  // We need to write the contents here in IE since multiple writes messes up refresh button and back button
22934
23164
  url = 'javascript:(function(){document.open();document.domain="' + document.domain + '";' +
22935
23165
  'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
@@ -22986,7 +23216,7 @@ define("tinymce/Editor", [
22986
23216
  }
22987
23217
 
22988
23218
  // Setup iframe body
22989
- if ((!isIE || !self.editorManager.relaxedDomain) && !settings.content_editable) {
23219
+ if ((!ie || !self.editorManager.relaxedDomain) && !settings.content_editable) {
22990
23220
  doc.open();
22991
23221
  doc.write(self.iframeHTML);
22992
23222
  doc.close();
@@ -23054,6 +23284,7 @@ define("tinymce/Editor", [
23054
23284
  class_filter: settings.class_filter,
23055
23285
  update_styles: true,
23056
23286
  root_element: settings.content_editable ? self.id : null,
23287
+ collect: settings.content_editable,
23057
23288
  schema: self.schema,
23058
23289
  onSetAttrib: function(e) {
23059
23290
  self.fire('SetAttrib', e);
@@ -23388,8 +23619,8 @@ define("tinymce/Editor", [
23388
23619
  return '';
23389
23620
  }
23390
23621
 
23391
- return i18n[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
23392
- return i18n[lang + '.' + b] || '{#' + b + '}';
23622
+ return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
23623
+ return i18n.data[lang + '.' + b] || '{#' + b + '}';
23393
23624
  });
23394
23625
  },
23395
23626
 
@@ -23402,7 +23633,7 @@ define("tinymce/Editor", [
23402
23633
  */
23403
23634
  getLang: function(name, defaultVal) {
23404
23635
  return (
23405
- this.editorManager.i18n[(this.settings.language || 'en') + '.' + name] ||
23636
+ this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
23406
23637
  (defaultVal !== undefined ? defaultVal : '{#' + name + '}')
23407
23638
  );
23408
23639
  },
@@ -23462,7 +23693,7 @@ define("tinymce/Editor", [
23462
23693
  // Get start node
23463
23694
  root = self.getBody();
23464
23695
  node = selection.getStart() || root;
23465
- node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
23696
+ node = ie && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
23466
23697
 
23467
23698
  // Edge case for <p>|<img></p>
23468
23699
  if (node.nodeName == 'IMG' && selection.isCollapsed()) {
@@ -23813,7 +24044,7 @@ define("tinymce/Editor", [
23813
24044
  var self = this, doc = self.getDoc();
23814
24045
 
23815
24046
  // Fixed bug where IE has a blinking cursor left from the editor
23816
- if (isIE && doc) {
24047
+ if (ie && doc) {
23817
24048
  doc.execCommand('SelectAll');
23818
24049
  }
23819
24050
 
@@ -23932,7 +24163,10 @@ define("tinymce/Editor", [
23932
24163
  }
23933
24164
 
23934
24165
  args.element = elm = null;
23935
- self.isNotDirty = true;
24166
+
24167
+ if (args.set_dirty !== false) {
24168
+ self.isNotDirty = true;
24169
+ }
23936
24170
 
23937
24171
  return html;
23938
24172
  },
@@ -23976,42 +24210,54 @@ define("tinymce/Editor", [
23976
24210
 
23977
24211
  // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
23978
24212
  // It will also be impossible to place the caret in the editor unless there is a BR element present
23979
- if (!isIE && (content.length === 0 || /^\s+$/.test(content))) {
24213
+ if (content.length === 0 || /^\s+$/.test(content)) {
23980
24214
  forcedRootBlockName = self.settings.forced_root_block;
23981
24215
 
23982
24216
  // Check if forcedRootBlock is configured and that the block is a valid child of the body
23983
24217
  if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
23984
- content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';
23985
- } else {
24218
+ if (ie && ie < 11) {
24219
+ // IE renders BR elements in blocks so lets just add an empty block
24220
+ content = '<' + forcedRootBlockName + '></' + forcedRootBlockName + '>';
24221
+ } else {
24222
+ content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';
24223
+ }
24224
+ } else if (!ie) {
24225
+ // We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
23986
24226
  content = '<br data-mce-bogus="1">';
23987
24227
  }
23988
24228
 
23989
24229
  body.innerHTML = content;
23990
- self.selection.select(body, true);
23991
- self.selection.collapse(true);
24230
+
23992
24231
  self.fire('SetContent', args);
23993
- return;
23994
- }
24232
+ } else {
24233
+ // Parse and serialize the html
24234
+ if (args.format !== 'raw') {
24235
+ content = new Serializer({}, self.schema).serialize(
24236
+ self.parser.parse(content, {isRootContent: true})
24237
+ );
24238
+ }
23995
24239
 
23996
- // Parse and serialize the html
23997
- if (args.format !== 'raw') {
23998
- content = new Serializer({}, self.schema).serialize(
23999
- self.parser.parse(content, {isRootContent: true})
24000
- );
24001
- }
24240
+ // Set the new cleaned contents to the editor
24241
+ args.content = trim(content);
24242
+ self.dom.setHTML(body, args.content);
24002
24243
 
24003
- // Set the new cleaned contents to the editor
24004
- args.content = trim(content);
24005
- self.dom.setHTML(body, args.content);
24244
+ // Do post processing
24245
+ if (!args.no_events) {
24246
+ self.fire('SetContent', args);
24247
+ }
24006
24248
 
24007
- // Do post processing
24008
- if (!args.no_events) {
24009
- self.fire('SetContent', args);
24249
+ // Don't normalize selection if the focused element isn't the body in
24250
+ // content editable mode since it will steal focus otherwise
24251
+ /*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
24252
+ self.selection.normalize();
24253
+ }*/
24010
24254
  }
24011
24255
 
24012
- // Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise
24013
- if (!self.settings.content_editable || document.activeElement === self.getBody()) {
24014
- self.selection.normalize();
24256
+ // Move selection to start of body if it's a after init setContent call
24257
+ // This prevents IE 7/8 from moving focus to empty editors
24258
+ if (!args.initial) {
24259
+ self.selection.select(body, true);
24260
+ self.selection.collapse(true);
24015
24261
  }
24016
24262
 
24017
24263
  return args.content;
@@ -24284,7 +24530,7 @@ define("tinymce/Editor", [
24284
24530
  self.removed = 1; // Cancels post remove event execution
24285
24531
 
24286
24532
  // Fixed bug where IE has a blinking cursor left from the editor
24287
- if (isIE && doc) {
24533
+ if (ie && doc) {
24288
24534
  doc.execCommand('SelectAll');
24289
24535
  }
24290
24536
 
@@ -24307,6 +24553,7 @@ define("tinymce/Editor", [
24307
24553
 
24308
24554
  self.editorManager.remove(self);
24309
24555
  DOM.remove(elm);
24556
+ self.destroy();
24310
24557
  }
24311
24558
  },
24312
24559
 
@@ -24352,7 +24599,7 @@ define("tinymce/Editor", [
24352
24599
 
24353
24600
  // We must unbind on Gecko since it would otherwise produce the pesky "attempt
24354
24601
  // to run compile-and-go script on a cleared scope" message
24355
- if (isGecko) {
24602
+ if (automatic && isGecko) {
24356
24603
  Event.unbind(self.getDoc());
24357
24604
  Event.unbind(self.getWin());
24358
24605
  Event.unbind(self.getBody());
@@ -24373,8 +24620,11 @@ define("tinymce/Editor", [
24373
24620
 
24374
24621
  form = self.formElement;
24375
24622
  if (form) {
24376
- form.submit = form._mceOldSubmit;
24377
- form._mceOldSubmit = null;
24623
+ if (form._mceOldSubmit) {
24624
+ form.submit = form._mceOldSubmit;
24625
+ form._mceOldSubmit = null;
24626
+ }
24627
+
24378
24628
  DOM.unbind(form, 'submit reset', self.formEventDelegate);
24379
24629
  }
24380
24630
 
@@ -24538,6 +24788,35 @@ define("tinymce/FocusManager", [
24538
24788
  }
24539
24789
  }
24540
24790
 
24791
+ // We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
24792
+ // TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
24793
+ function createBookmark(rng) {
24794
+ if (rng && rng.startContainer) {
24795
+ return {
24796
+ startContainer: rng.startContainer,
24797
+ startOffset: rng.startOffset,
24798
+ endContainer: rng.endContainer,
24799
+ endOffset: rng.endOffset
24800
+ };
24801
+ }
24802
+
24803
+ return rng;
24804
+ }
24805
+
24806
+ function bookmarkToRng(editor, bookmark) {
24807
+ var rng;
24808
+
24809
+ if (bookmark.startContainer) {
24810
+ rng = editor.getDoc().createRange();
24811
+ rng.setStart(bookmark.startContainer, bookmark.startOffset);
24812
+ rng.setEnd(bookmark.endContainer, bookmark.endOffset);
24813
+ } else {
24814
+ rng = bookmark;
24815
+ }
24816
+
24817
+ return rng;
24818
+ }
24819
+
24541
24820
  function registerEvents(e) {
24542
24821
  var editor = e.editor, lastRng, selectionChangeHandler;
24543
24822
 
@@ -24547,16 +24826,26 @@ define("tinymce/FocusManager", [
24547
24826
 
24548
24827
  editor.on('init', function() {
24549
24828
  // On IE take selection snapshot onbeforedeactivate
24550
- if ("onbeforedeactivate" in document) {
24829
+ if ("onbeforedeactivate" in document && Env.ie < 11) {
24551
24830
  editor.dom.bind(editor.getBody(), 'beforedeactivate', function() {
24552
24831
  var ieSelection = editor.getDoc().selection;
24553
- lastRng = ieSelection && ieSelection.createRange ? ieSelection.createRange() : editor.selection.getRng();
24832
+
24833
+ try {
24834
+ lastRng = ieSelection && ieSelection.createRange ? ieSelection.createRange() : editor.selection.getRng();
24835
+ } catch (ex) {
24836
+ // IE throws "Unexcpected call to method or property access" some times so lets ignore it
24837
+ }
24554
24838
  });
24555
- } else if (editor.inline) {
24839
+ } else if (editor.inline || Env.ie > 10) {
24556
24840
  // On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
24557
- editor.on('nodechange', function() {
24841
+ editor.on('nodechange keyup', function() {
24558
24842
  var isInBody, node = document.activeElement;
24559
24843
 
24844
+ // IE 11 reports active element as iframe not body of iframe
24845
+ if (node && node.id == editor.id + '_ifr') {
24846
+ node = editor.getBody();
24847
+ }
24848
+
24560
24849
  // Check if selection is within editor body
24561
24850
  while (node) {
24562
24851
  if (node == editor.getBody()) {
@@ -24596,12 +24885,17 @@ define("tinymce/FocusManager", [
24596
24885
  }
24597
24886
  });
24598
24887
 
24888
+ // Remove last selection bookmark on mousedown see #6305
24889
+ editor.on('mousedown', function() {
24890
+ editor.selection.lastFocusBookmark = null;
24891
+ });
24892
+
24599
24893
  editor.on('focusin', function() {
24600
24894
  var focusedEditor = editorManager.focusedEditor;
24601
24895
 
24602
- if (editor.selection.restoreRng) {
24603
- editor.selection.setRng(editor.selection.restoreRng);
24604
- editor.selection.restoreRng = null;
24896
+ if (editor.selection.lastFocusBookmark) {
24897
+ editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
24898
+ editor.selection.lastFocusBookmark = null;
24605
24899
  }
24606
24900
 
24607
24901
  if (focusedEditor != editor) {
@@ -24609,6 +24903,7 @@ define("tinymce/FocusManager", [
24609
24903
  focusedEditor.fire('blur', {focusedEditor: editor});
24610
24904
  }
24611
24905
 
24906
+ editorManager.activeEditor = editor;
24612
24907
  editor.fire('focus', {blurredEditor: focusedEditor});
24613
24908
  editor.focus(false);
24614
24909
  editorManager.focusedEditor = editor;
@@ -24616,21 +24911,21 @@ define("tinymce/FocusManager", [
24616
24911
  });
24617
24912
 
24618
24913
  editor.on('focusout', function() {
24619
- editor.selection.restoreRng = lastRng;
24914
+ editor.selection.lastFocusBookmark = createBookmark(lastRng);
24620
24915
 
24621
24916
  window.setTimeout(function() {
24622
24917
  var focusedEditor = editorManager.focusedEditor;
24623
24918
 
24624
24919
  // Focus from editorA into editorB then don't restore selection
24625
24920
  if (focusedEditor != editor) {
24626
- editor.selection.restoreRng = null;
24921
+ editor.selection.lastFocusBookmark = null;
24627
24922
  }
24628
24923
 
24629
- // Still the same editor the the blur was outside any editor
24924
+ // Still the same editor the the blur was outside any editor UI
24630
24925
  if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
24631
24926
  editor.fire('blur', {focusedEditor: null});
24632
24927
  editorManager.focusedEditor = null;
24633
- editor.selection.restoreRng = null;
24928
+ editor.selection.lastFocusBookmark = null;
24634
24929
  }
24635
24930
  }, 0);
24636
24931
  });
@@ -24666,7 +24961,7 @@ define("tinymce/FocusManager", [
24666
24961
  */
24667
24962
 
24668
24963
  /**
24669
- * This class used as a factory for manager for tinymce.Editor instaces.
24964
+ * This class used as a factory for manager for tinymce.Editor instances.
24670
24965
  *
24671
24966
  * @example
24672
24967
  * tinymce.EditorManager.init({});
@@ -24704,7 +24999,7 @@ define("tinymce/EditorManager", [
24704
24999
  * @property minorVersion
24705
25000
  * @type String
24706
25001
  */
24707
- minorVersion : '0',
25002
+ minorVersion : '0.6',
24708
25003
 
24709
25004
  /**
24710
25005
  * Release date of TinyMCE build.
@@ -24712,7 +25007,7 @@ define("tinymce/EditorManager", [
24712
25007
  * @property releaseDate
24713
25008
  * @type String
24714
25009
  */
24715
- releaseDate: '2013-07-18',
25010
+ releaseDate: '2013-09-12',
24716
25011
 
24717
25012
  /**
24718
25013
  * Collection of editor instances.
@@ -24863,11 +25158,11 @@ define("tinymce/EditorManager", [
24863
25158
  return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
24864
25159
  }
24865
25160
 
24866
- self.settings = settings;
24867
-
24868
- DOM.bind(window, 'ready', function() {
25161
+ function readyHandler() {
24869
25162
  var l, co;
24870
25163
 
25164
+ DOM.unbind(window, 'ready', readyHandler);
25165
+
24871
25166
  execCallback(settings, 'onpageload');
24872
25167
 
24873
25168
  if (settings.types) {
@@ -24964,7 +25259,11 @@ define("tinymce/EditorManager", [
24964
25259
  }
24965
25260
  });
24966
25261
  }
24967
- });
25262
+ }
25263
+
25264
+ self.settings = settings;
25265
+
25266
+ DOM.bind(window, 'ready', readyHandler);
24968
25267
  },
24969
25268
 
24970
25269
  /**
@@ -25060,7 +25359,7 @@ define("tinymce/EditorManager", [
25060
25359
  * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
25061
25360
  */
25062
25361
  remove: function(selector) {
25063
- var self = this, i, editors = self.editors, editor;
25362
+ var self = this, i, editors = self.editors, editor, removedFromList;
25064
25363
 
25065
25364
  // Remove all editors
25066
25365
  if (!selector) {
@@ -25095,6 +25394,7 @@ define("tinymce/EditorManager", [
25095
25394
  for (i = 0; i < editors.length; i++) {
25096
25395
  if (editors[i] == editor) {
25097
25396
  editors.splice(i, 1);
25397
+ removedFromList = true;
25098
25398
  break;
25099
25399
  }
25100
25400
  }
@@ -25104,26 +25404,22 @@ define("tinymce/EditorManager", [
25104
25404
  self.activeEditor = editors[0];
25105
25405
  }
25106
25406
 
25107
- // Don't remove missing editor or removed instances
25108
- if (!editor || editor.removed) {
25109
- return;
25110
- }
25111
-
25112
- editor.remove();
25113
- editor.destroy();
25114
-
25115
25407
  /**
25116
25408
  * Fires when an editor is removed from EditorManager collection.
25117
25409
  *
25118
25410
  * @event RemoveEditor
25119
25411
  * @param {Object} e Event arguments.
25120
25412
  */
25121
- self.fire('RemoveEditor', {editor: editor});
25413
+ if (removedFromList) {
25414
+ self.fire('RemoveEditor', {editor: editor});
25415
+ }
25122
25416
 
25123
25417
  if (!editors.length) {
25124
25418
  DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
25125
25419
  }
25126
25420
 
25421
+ editor.remove();
25422
+
25127
25423
  return editor;
25128
25424
  },
25129
25425
 
@@ -25244,10 +25540,10 @@ define("tinymce/LegacyInput", [
25244
25540
  var each = Tools.each, explode = Tools.explode;
25245
25541
 
25246
25542
  EditorManager.on('AddEditor', function(e) {
25247
- var ed = e.editor;
25543
+ var editor = e.editor;
25248
25544
 
25249
- ed.on('preInit', function() {
25250
- var filters, fontSizes, dom, settings = ed.settings;
25545
+ editor.on('preInit', function() {
25546
+ var filters, fontSizes, dom, settings = editor.settings;
25251
25547
 
25252
25548
  function replaceWithSpan(node, styles) {
25253
25549
  each(styles, function(value, name) {
@@ -25260,7 +25556,7 @@ define("tinymce/LegacyInput", [
25260
25556
  }
25261
25557
 
25262
25558
  function convert(e) {
25263
- dom = ed.dom;
25559
+ dom = editor.dom;
25264
25560
 
25265
25561
  if (settings.convert_fonts_to_spans) {
25266
25562
  each(dom.select('font,u,strike', e.node), function(node) {
@@ -25295,7 +25591,7 @@ define("tinymce/LegacyInput", [
25295
25591
  }
25296
25592
  };
25297
25593
 
25298
- ed.on('PreProcess SetContent', convert);
25594
+ editor.on('PreProcess SetContent', convert);
25299
25595
  }
25300
25596
  });
25301
25597
  });
@@ -25686,16 +25982,24 @@ define("tinymce/util/JSONP", [
25686
25982
  * var value = tinymce.util.LocalStorage.getItem('key');
25687
25983
  */
25688
25984
  define("tinymce/util/LocalStorage", [], function() {
25689
- var LocalStorage, storageElm, items, keys, userDataKey;
25985
+ var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
25690
25986
 
25691
25987
  // Check for native support
25692
- if (window.localStorage) {
25693
- return localStorage;
25988
+ try {
25989
+ if (window.localStorage) {
25990
+ return localStorage;
25991
+ }
25992
+ } catch (ex) {
25993
+ // Ignore
25694
25994
  }
25695
25995
 
25696
25996
  userDataKey = "tinymce";
25697
25997
  storageElm = document.documentElement;
25698
- storageElm.addBehavior('#default#userData');
25998
+ hasOldIEDataSupport = !!storageElm.addBehavior;
25999
+
26000
+ if (hasOldIEDataSupport) {
26001
+ storageElm.addBehavior('#default#userData');
26002
+ }
25699
26003
 
25700
26004
  /**
25701
26005
  * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
@@ -25718,6 +26022,11 @@ define("tinymce/util/LocalStorage", [], function() {
25718
26022
 
25719
26023
  items = {};
25720
26024
 
26025
+ // localStorage can be disabled on WebKit/Gecko so make a dummy storage
26026
+ if (!hasOldIEDataSupport) {
26027
+ return;
26028
+ }
26029
+
25721
26030
  function next(end) {
25722
26031
  var value, nextPos;
25723
26032
 
@@ -25752,6 +26061,11 @@ define("tinymce/util/LocalStorage", [], function() {
25752
26061
  function save() {
25753
26062
  var value, data = '';
25754
26063
 
26064
+ // localStorage can be disabled on WebKit/Gecko so make a dummy storage
26065
+ if (!hasOldIEDataSupport) {
26066
+ return;
26067
+ }
26068
+
25755
26069
  for (var key in items) {
25756
26070
  value = items[key];
25757
26071
  data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
@@ -26368,7 +26682,7 @@ define("tinymce/ui/Widget", [
26368
26682
  *
26369
26683
  * @example
26370
26684
  * // Create and render a button to the body element
26371
- * tinymce.ui.Factory({
26685
+ * tinymce.ui.Factory.create({
26372
26686
  * type: 'button',
26373
26687
  * text: 'My button'
26374
26688
  * }).renderTo(document.body);
@@ -26476,7 +26790,7 @@ define("tinymce/ui/Button", [
26476
26790
  *
26477
26791
  * @example
26478
26792
  * // Create and render a buttongroup with two buttons to the body element
26479
- * tinymce.ui.Factory({
26793
+ * tinymce.ui.Factory.create({
26480
26794
  * type: 'buttongroup',
26481
26795
  * items: [
26482
26796
  * {text: 'Button A'},
@@ -26540,7 +26854,7 @@ define("tinymce/ui/ButtonGroup", [
26540
26854
  *
26541
26855
  * @example
26542
26856
  * // Create and render a checkbox to the body element
26543
- * tinymce.ui.Factory({
26857
+ * tinymce.ui.Factory.create({
26544
26858
  * type: 'checkbox',
26545
26859
  * checked: true,
26546
26860
  * text: 'My checkbox'
@@ -27038,6 +27352,11 @@ define("tinymce/ui/ComboBox", [
27038
27352
  return self._super();
27039
27353
  },
27040
27354
 
27355
+ remove: function() {
27356
+ DomUtils.off(this.getEl('inp'));
27357
+ this._super();
27358
+ },
27359
+
27041
27360
  /**
27042
27361
  * Renders the control as a HTML string.
27043
27362
  *
@@ -27173,7 +27492,7 @@ define("tinymce/ui/Path", [
27173
27492
  * @private
27174
27493
  */
27175
27494
  update: function() {
27176
- this.getEl().innerHTML = this._getPathHtml();
27495
+ this.innerHtml(this._getPathHtml());
27177
27496
  },
27178
27497
 
27179
27498
  /**
@@ -28003,191 +28322,195 @@ define("tinymce/ui/FlowLayout", [
28003
28322
  * @class tinymce.ui.FormatControls
28004
28323
  */
28005
28324
  define("tinymce/ui/FormatControls", [
28006
- "tinymce/ui/Factory",
28007
28325
  "tinymce/ui/Control",
28008
28326
  "tinymce/ui/Widget",
28009
28327
  "tinymce/ui/FloatPanel",
28010
28328
  "tinymce/util/Tools",
28011
28329
  "tinymce/EditorManager",
28012
28330
  "tinymce/Env"
28013
- ], function(Factory, Control, Widget, FloatPanel, Tools, EditorManager, Env) {
28331
+ ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
28014
28332
  var each = Tools.each;
28015
28333
 
28334
+ EditorManager.on('AddEditor', function(e) {
28335
+ registerControls(e.editor);
28336
+ });
28337
+
28016
28338
  Control.translate = function(text) {
28017
28339
  return EditorManager.translate(text);
28018
28340
  };
28019
28341
 
28020
28342
  Widget.tooltips = !Env.iOS;
28021
28343
 
28022
- // Generates a preview for a format
28023
- function getPreviewCss(format) {
28024
- var editor = EditorManager.activeEditor, name, previewElm, dom = editor.dom;
28025
- var previewCss = '', parentFontSize, previewStyles;
28344
+ function registerControls(editor) {
28345
+ var formatMenu;
28026
28346
 
28027
- previewStyles = editor.settings.preview_styles;
28347
+ // Generates a preview for a format
28348
+ function getPreviewCss(format) {
28349
+ var name, previewElm, dom = editor.dom;
28350
+ var previewCss = '', parentFontSize, previewStyles;
28028
28351
 
28029
- // No preview forced
28030
- if (previewStyles === false) {
28031
- return '';
28032
- }
28352
+ previewStyles = editor.settings.preview_styles;
28033
28353
 
28034
- // Default preview
28035
- if (!previewStyles) {
28036
- previewStyles = 'font-family font-size font-weight text-decoration text-transform color background-color border border-radius';
28037
- }
28354
+ // No preview forced
28355
+ if (previewStyles === false) {
28356
+ return '';
28357
+ }
28038
28358
 
28039
- // Removes any variables since these can't be previewed
28040
- function removeVars(val) {
28041
- return val.replace(/%(\w+)/g, '');
28042
- }
28359
+ // Default preview
28360
+ if (!previewStyles) {
28361
+ previewStyles = 'font-family font-size font-weight text-decoration ' +
28362
+ 'text-transform color background-color border border-radius';
28363
+ }
28043
28364
 
28044
- // Create block/inline element to use for preview
28045
- format = editor.formatter.get(format);
28046
- if (!format) {
28047
- return;
28048
- }
28365
+ // Removes any variables since these can't be previewed
28366
+ function removeVars(val) {
28367
+ return val.replace(/%(\w+)/g, '');
28368
+ }
28049
28369
 
28050
- format = format[0];
28051
- name = format.block || format.inline || 'span';
28052
- previewElm = dom.create(name);
28370
+ // Create block/inline element to use for preview
28371
+ format = editor.formatter.get(format);
28372
+ if (!format) {
28373
+ return;
28374
+ }
28053
28375
 
28054
- // Add format styles to preview element
28055
- each(format.styles, function(value, name) {
28056
- value = removeVars(value);
28376
+ format = format[0];
28377
+ name = format.block || format.inline || 'span';
28378
+ previewElm = dom.create(name);
28057
28379
 
28058
- if (value) {
28059
- dom.setStyle(previewElm, name, value);
28060
- }
28061
- });
28380
+ // Add format styles to preview element
28381
+ each(format.styles, function(value, name) {
28382
+ value = removeVars(value);
28062
28383
 
28063
- // Add attributes to preview element
28064
- each(format.attributes, function(value, name) {
28065
- value = removeVars(value);
28384
+ if (value) {
28385
+ dom.setStyle(previewElm, name, value);
28386
+ }
28387
+ });
28066
28388
 
28067
- if (value) {
28068
- dom.setAttrib(previewElm, name, value);
28069
- }
28070
- });
28389
+ // Add attributes to preview element
28390
+ each(format.attributes, function(value, name) {
28391
+ value = removeVars(value);
28071
28392
 
28072
- // Add classes to preview element
28073
- each(format.classes, function(value) {
28074
- value = removeVars(value);
28393
+ if (value) {
28394
+ dom.setAttrib(previewElm, name, value);
28395
+ }
28396
+ });
28075
28397
 
28076
- if (!dom.hasClass(previewElm, value)) {
28077
- dom.addClass(previewElm, value);
28078
- }
28079
- });
28398
+ // Add classes to preview element
28399
+ each(format.classes, function(value) {
28400
+ value = removeVars(value);
28080
28401
 
28081
- editor.fire('PreviewFormats');
28402
+ if (!dom.hasClass(previewElm, value)) {
28403
+ dom.addClass(previewElm, value);
28404
+ }
28405
+ });
28082
28406
 
28083
- // Add the previewElm outside the visual area
28084
- dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
28085
- editor.getBody().appendChild(previewElm);
28407
+ editor.fire('PreviewFormats');
28086
28408
 
28087
- // Get parent container font size so we can compute px values out of em/% for older IE:s
28088
- parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
28089
- parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
28409
+ // Add the previewElm outside the visual area
28410
+ dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
28411
+ editor.getBody().appendChild(previewElm);
28090
28412
 
28091
- each(previewStyles.split(' '), function(name) {
28092
- var value = dom.getStyle(previewElm, name, true);
28413
+ // Get parent container font size so we can compute px values out of em/% for older IE:s
28414
+ parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
28415
+ parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
28093
28416
 
28094
- // If background is transparent then check if the body has a background color we can use
28095
- if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
28096
- value = dom.getStyle(editor.getBody(), name, true);
28417
+ each(previewStyles.split(' '), function(name) {
28418
+ var value = dom.getStyle(previewElm, name, true);
28097
28419
 
28098
- // Ignore white since it's the default color, not the nicest fix
28099
- // TODO: Fix this by detecting runtime style
28100
- if (dom.toHex(value).toLowerCase() == '#ffffff') {
28101
- return;
28102
- }
28103
- }
28420
+ // If background is transparent then check if the body has a background color we can use
28421
+ if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
28422
+ value = dom.getStyle(editor.getBody(), name, true);
28104
28423
 
28105
- if (name == 'color') {
28106
- // Ignore black since it's the default color, not the nicest fix
28107
- // TODO: Fix this by detecting runtime style
28108
- if (dom.toHex(value).toLowerCase() == '#000000') {
28109
- return;
28424
+ // Ignore white since it's the default color, not the nicest fix
28425
+ // TODO: Fix this by detecting runtime style
28426
+ if (dom.toHex(value).toLowerCase() == '#ffffff') {
28427
+ return;
28428
+ }
28110
28429
  }
28111
- }
28112
28430
 
28113
- // Old IE won't calculate the font size so we need to do that manually
28114
- if (name == 'font-size') {
28115
- if (/em|%$/.test(value)) {
28116
- if (parentFontSize === 0) {
28431
+ if (name == 'color') {
28432
+ // Ignore black since it's the default color, not the nicest fix
28433
+ // TODO: Fix this by detecting runtime style
28434
+ if (dom.toHex(value).toLowerCase() == '#000000') {
28117
28435
  return;
28118
28436
  }
28437
+ }
28119
28438
 
28120
- // Convert font size from em/% to px
28121
- value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
28122
- value = (value * parentFontSize) + 'px';
28439
+ // Old IE won't calculate the font size so we need to do that manually
28440
+ if (name == 'font-size') {
28441
+ if (/em|%$/.test(value)) {
28442
+ if (parentFontSize === 0) {
28443
+ return;
28444
+ }
28445
+
28446
+ // Convert font size from em/% to px
28447
+ value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
28448
+ value = (value * parentFontSize) + 'px';
28449
+ }
28123
28450
  }
28124
- }
28125
28451
 
28126
- if (name == "border" && value) {
28127
- previewCss += 'padding:0 2px;';
28128
- }
28452
+ if (name == "border" && value) {
28453
+ previewCss += 'padding:0 2px;';
28454
+ }
28129
28455
 
28130
- previewCss += name + ':' + value + ';';
28131
- });
28456
+ previewCss += name + ':' + value + ';';
28457
+ });
28132
28458
 
28133
- editor.fire('AfterPreviewFormats');
28459
+ editor.fire('AfterPreviewFormats');
28134
28460
 
28135
- //previewCss += 'line-height:normal';
28461
+ //previewCss += 'line-height:normal';
28136
28462
 
28137
- dom.remove(previewElm);
28463
+ dom.remove(previewElm);
28138
28464
 
28139
- return previewCss;
28140
- }
28465
+ return previewCss;
28466
+ }
28141
28467
 
28142
- function createListBoxChangeHandler(items, formatName) {
28143
- return function() {
28144
- var self = this;
28468
+ function createListBoxChangeHandler(items, formatName) {
28469
+ return function() {
28470
+ var self = this;
28145
28471
 
28146
- EditorManager.activeEditor.on('nodeChange', function(e) {
28147
- var formatter = EditorManager.activeEditor.formatter;
28148
- var value = null;
28472
+ editor.on('nodeChange', function(e) {
28473
+ var formatter = editor.formatter;
28474
+ var value = null;
28149
28475
 
28150
- each(e.parents, function(node) {
28151
- each(items, function(item) {
28152
- if (formatName) {
28153
- if (formatter.matchNode(node, formatName, {value: item.value})) {
28154
- value = item.value;
28476
+ each(e.parents, function(node) {
28477
+ each(items, function(item) {
28478
+ if (formatName) {
28479
+ if (formatter.matchNode(node, formatName, {value: item.value})) {
28480
+ value = item.value;
28481
+ }
28482
+ } else {
28483
+ if (formatter.matchNode(node, item.value)) {
28484
+ value = item.value;
28485
+ }
28155
28486
  }
28156
- } else {
28157
- if (formatter.matchNode(node, item.value)) {
28158
- value = item.value;
28487
+
28488
+ if (value) {
28489
+ return false;
28159
28490
  }
28160
- }
28491
+ });
28161
28492
 
28162
28493
  if (value) {
28163
28494
  return false;
28164
28495
  }
28165
28496
  });
28166
28497
 
28167
- if (value) {
28168
- return false;
28169
- }
28498
+ self.value(value);
28170
28499
  });
28500
+ };
28501
+ }
28171
28502
 
28172
- self.value(value);
28173
- });
28174
- };
28175
- }
28503
+ function createFormats(formats) {
28504
+ formats = formats.split(';');
28176
28505
 
28177
- function createFormats(formats) {
28178
- formats = formats.split(';');
28506
+ var i = formats.length;
28507
+ while (i--) {
28508
+ formats[i] = formats[i].split('=');
28509
+ }
28179
28510
 
28180
- var i = formats.length;
28181
- while (i--) {
28182
- formats[i] = formats[i].split('=');
28511
+ return formats;
28183
28512
  }
28184
28513
 
28185
- return formats;
28186
- }
28187
-
28188
- EditorManager.on('AddEditor', function(e) {
28189
- var editor = e.editor, formatMenu;
28190
-
28191
28514
  function createFormatMenu() {
28192
28515
  var count = 0, newFormats = [];
28193
28516
 
@@ -28236,8 +28559,7 @@ define("tinymce/ui/FormatControls", [
28236
28559
  each(formats, function(format) {
28237
28560
  var menuItem = {
28238
28561
  text: format.title,
28239
- icon: format.icon,
28240
- preview: true
28562
+ icon: format.icon
28241
28563
  };
28242
28564
 
28243
28565
  if (format.items) {
@@ -28250,22 +28572,7 @@ define("tinymce/ui/FormatControls", [
28250
28572
  newFormats.push(format);
28251
28573
  }
28252
28574
 
28253
- menuItem.textStyle = function() {
28254
- return getPreviewCss(formatName);
28255
- };
28256
-
28257
- menuItem.onclick = function() {
28258
- toggleFormat(formatName);
28259
- };
28260
-
28261
- menuItem.onPostRender = function() {
28262
- var self = this;
28263
-
28264
- self.parent().on('show', function() {
28265
- self.disabled(!editor.formatter.canApply(formatName));
28266
- self.active(editor.formatter.match(formatName));
28267
- });
28268
- };
28575
+ menuItem.format = formatName;
28269
28576
  }
28270
28577
 
28271
28578
  menu.push(menuItem);
@@ -28282,6 +28589,40 @@ define("tinymce/ui/FormatControls", [
28282
28589
 
28283
28590
  var menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
28284
28591
 
28592
+ menu = {
28593
+ type: 'menu',
28594
+ items: menu,
28595
+ onPostRender: function(e) {
28596
+ editor.fire('renderFormatsMenu', {control: e.control});
28597
+ },
28598
+ itemDefaults: {
28599
+ preview: true,
28600
+
28601
+ textStyle: function() {
28602
+ if (this.settings.format) {
28603
+ return getPreviewCss(this.settings.format);
28604
+ }
28605
+ },
28606
+
28607
+ onPostRender: function() {
28608
+ var self = this, formatName = this.settings.format;
28609
+
28610
+ if (formatName) {
28611
+ self.parent().on('show', function() {
28612
+ self.disabled(!editor.formatter.canApply(formatName));
28613
+ self.active(editor.formatter.match(formatName));
28614
+ });
28615
+ }
28616
+ },
28617
+
28618
+ onclick: function() {
28619
+ if (this.settings.format) {
28620
+ toggleFormat(this.settings.format);
28621
+ }
28622
+ }
28623
+ }
28624
+ };
28625
+
28285
28626
  return menu;
28286
28627
  }
28287
28628
 
@@ -28344,7 +28685,7 @@ define("tinymce/ui/FormatControls", [
28344
28685
  each({
28345
28686
  blockquote: ['Toggle blockquote', 'mceBlockQuote'],
28346
28687
  numlist: ['Numbered list', 'InsertOrderedList'],
28347
- bullist: ['Bulleted list', 'InsertUnorderedList'],
28688
+ bullist: ['Bullet list', 'InsertUnorderedList'],
28348
28689
  subscript: ['Subscript', 'Subscript'],
28349
28690
  superscript: ['Superscript', 'Superscript'],
28350
28691
  alignleft: ['Align left', 'JustifyLeft'],
@@ -28483,31 +28824,17 @@ define("tinymce/ui/FormatControls", [
28483
28824
  }
28484
28825
 
28485
28826
  if (fmt) {
28486
- EditorManager.activeEditor.execCommand('mceToggleFormat', false, fmt);
28827
+ editor.execCommand('mceToggleFormat', false, fmt);
28487
28828
  }
28488
28829
  }
28489
28830
 
28490
- Factory.add('styleselect', function(settings) {
28491
- var menu = [].concat(formatMenu);
28492
-
28493
- /*
28494
- menu.push({text: '-'});
28495
- menu.push({
28496
- text: 'Remove formatting',
28497
- icon: 'removeformat',
28498
- onclick: function() {
28499
- editor.execCommand('RemoveFormat');
28500
- }
28501
- });
28502
- */
28503
-
28504
- return Factory.create('menubutton', Tools.extend({
28505
- text: 'Formats',
28506
- menu: menu
28507
- }, settings));
28831
+ editor.addButton('styleselect', {
28832
+ type: 'menubutton',
28833
+ text: 'Formats',
28834
+ menu: formatMenu
28508
28835
  });
28509
28836
 
28510
- Factory.add('formatselect', function(settings) {
28837
+ editor.addButton('formatselect', function() {
28511
28838
  var items = [], blocks = createFormats(editor.settings.block_formats ||
28512
28839
  'Paragraph=p;' +
28513
28840
  'Address=address;' +
@@ -28522,7 +28849,7 @@ define("tinymce/ui/FormatControls", [
28522
28849
 
28523
28850
  each(blocks, function(block) {
28524
28851
  items.push({
28525
- text: {raw: block[0]},
28852
+ text: block[0],
28526
28853
  value: block[1],
28527
28854
  textStyle: function() {
28528
28855
  return getPreviewCss(block[1]);
@@ -28530,16 +28857,17 @@ define("tinymce/ui/FormatControls", [
28530
28857
  });
28531
28858
  });
28532
28859
 
28533
- return Factory.create('listbox', Tools.extend({
28860
+ return {
28861
+ type: 'listbox',
28534
28862
  text: {raw: blocks[0][0]},
28535
28863
  values: items,
28536
28864
  fixedWidth: true,
28537
28865
  onselect: toggleFormat,
28538
28866
  onPostRender: createListBoxChangeHandler(items)
28539
- }, settings));
28867
+ };
28540
28868
  });
28541
28869
 
28542
- Factory.add('fontselect', function(settings) {
28870
+ editor.addButton('fontselect', function() {
28543
28871
  var defaultFontsFormats =
28544
28872
  'Andale Mono=andale mono,times;' +
28545
28873
  'Arial=arial,helvetica,sans-serif;' +
@@ -28569,7 +28897,8 @@ define("tinymce/ui/FormatControls", [
28569
28897
  });
28570
28898
  });
28571
28899
 
28572
- return Factory.create('listbox', Tools.extend({
28900
+ return {
28901
+ type: 'listbox',
28573
28902
  text: 'Font Family',
28574
28903
  tooltip: 'Font Family',
28575
28904
  values: items,
@@ -28577,13 +28906,13 @@ define("tinymce/ui/FormatControls", [
28577
28906
  onPostRender: createListBoxChangeHandler(items, 'fontname'),
28578
28907
  onselect: function(e) {
28579
28908
  if (e.control.settings.value) {
28580
- EditorManager.activeEditor.execCommand('FontName', false, e.control.settings.value);
28909
+ editor.execCommand('FontName', false, e.control.settings.value);
28581
28910
  }
28582
28911
  }
28583
- }, settings));
28912
+ };
28584
28913
  });
28585
28914
 
28586
- Factory.add('fontsizeselect', function(settings) {
28915
+ editor.addButton('fontsizeselect', function() {
28587
28916
  var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
28588
28917
  var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
28589
28918
 
@@ -28591,7 +28920,8 @@ define("tinymce/ui/FormatControls", [
28591
28920
  items.push({text: item, value: item});
28592
28921
  });
28593
28922
 
28594
- return Factory.create('listbox', Tools.extend({
28923
+ return {
28924
+ type: 'listbox',
28595
28925
  text: 'Font Sizes',
28596
28926
  tooltip: 'Font Sizes',
28597
28927
  values: items,
@@ -28599,17 +28929,17 @@ define("tinymce/ui/FormatControls", [
28599
28929
  onPostRender: createListBoxChangeHandler(items, 'fontsize'),
28600
28930
  onclick: function(e) {
28601
28931
  if (e.control.settings.value) {
28602
- EditorManager.activeEditor.execCommand('FontSize', false, e.control.settings.value);
28932
+ editor.execCommand('FontSize', false, e.control.settings.value);
28603
28933
  }
28604
28934
  }
28605
- }, settings));
28935
+ };
28606
28936
  });
28607
28937
 
28608
28938
  editor.addMenuItem('formats', {
28609
28939
  text: 'Formats',
28610
28940
  menu: formatMenu
28611
28941
  });
28612
- });
28942
+ }
28613
28943
  });
28614
28944
 
28615
28945
  // Included from: js/tinymce/classes/ui/GridLayout.js
@@ -28904,10 +29234,25 @@ define("tinymce/ui/Iframe", [
28904
29234
  *
28905
29235
  * @method html
28906
29236
  * @param {String} html HTML string to set as HTML inside the iframe.
29237
+ * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
28907
29238
  * @return {tinymce.ui.Iframe} Current iframe control.
28908
29239
  */
28909
- html: function(html) {
28910
- this.getEl().contentWindow.document.body.innerHTML = html;
29240
+ html: function(html, callback) {
29241
+ var self = this, body = this.getEl().contentWindow.document.body;
29242
+
29243
+ // Wait for iframe to initialize IE 10 takes time
29244
+ if (!body) {
29245
+ setTimeout(function() {
29246
+ self.html(html);
29247
+ }, 0);
29248
+ } else {
29249
+ body.innerHTML = html;
29250
+
29251
+ if (callback) {
29252
+ callback();
29253
+ }
29254
+ }
29255
+
28911
29256
  return this;
28912
29257
  }
28913
29258
  });
@@ -29035,7 +29380,7 @@ define("tinymce/ui/Label", [
29035
29380
  var self = this;
29036
29381
 
29037
29382
  if (self._rendered && text) {
29038
- self.getEl().innerHTML = self.encode(text);
29383
+ this.innerHtml(self.encode(text));
29039
29384
  }
29040
29385
 
29041
29386
  return self._super(text);
@@ -29635,9 +29980,9 @@ define("tinymce/ui/MenuItem", [
29635
29980
  * @method showMenu
29636
29981
  */
29637
29982
  showMenu: function() {
29638
- var self = this, settings = self.settings, menu;
29983
+ var self = this, settings = self.settings, menu, parent = self.parent();
29639
29984
 
29640
- self.parent().items().each(function(ctrl) {
29985
+ parent.items().each(function(ctrl) {
29641
29986
  if (ctrl !== self) {
29642
29987
  ctrl.hideMenu();
29643
29988
  }
@@ -29659,6 +30004,10 @@ define("tinymce/ui/MenuItem", [
29659
30004
  menu.type = menu.type || 'menu';
29660
30005
  }
29661
30006
 
30007
+ if (parent.settings.itemDefaults) {
30008
+ menu.itemDefaults = parent.settings.itemDefaults;
30009
+ }
30010
+
29662
30011
  menu = self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
29663
30012
  menu.reflow();
29664
30013
  menu.fire('show');
@@ -29675,7 +30024,7 @@ define("tinymce/ui/MenuItem", [
29675
30024
  menu.show();
29676
30025
  }
29677
30026
 
29678
- menu._parentMenu = self.parent();
30027
+ menu._parentMenu = parent;
29679
30028
 
29680
30029
  menu.addClass('menu-sub');
29681
30030
 
@@ -29722,17 +30071,22 @@ define("tinymce/ui/MenuItem", [
29722
30071
  */
29723
30072
  renderHtml: function() {
29724
30073
  var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
29725
- var icon = self.settings.icon;
30074
+ var icon = self.settings.icon, image = '';
29726
30075
 
29727
30076
  if (icon) {
29728
30077
  self.parent().addClass('menu-has-icons');
29729
30078
  }
29730
30079
 
30080
+ if (settings.image) {
30081
+ icon = 'none';
30082
+ image = ' style="background-image: url(\'' + settings.image + '\')"';
30083
+ }
30084
+
29731
30085
  icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
29732
30086
 
29733
30087
  return (
29734
30088
  '<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
29735
- (text !== '-' ? '<i class="' + icon + '"></i>&nbsp;' : '') +
30089
+ (text !== '-' ? '<i class="' + icon + '"' + image + '></i>&nbsp;' : '') +
29736
30090
  (text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
29737
30091
  (settings.shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' +
29738
30092
  settings.shortcut + '</div>' : '') +
@@ -29751,7 +30105,7 @@ define("tinymce/ui/MenuItem", [
29751
30105
 
29752
30106
  var textStyle = settings.textStyle;
29753
30107
  if (typeof(textStyle) == "function") {
29754
- textStyle = textStyle();
30108
+ textStyle = textStyle.call(this);
29755
30109
  }
29756
30110
 
29757
30111
  if (textStyle) {
@@ -29801,8 +30155,9 @@ define("tinymce/ui/MenuItem", [
29801
30155
  define("tinymce/ui/Menu", [
29802
30156
  "tinymce/ui/FloatPanel",
29803
30157
  "tinymce/ui/KeyboardNavigation",
29804
- "tinymce/ui/MenuItem"
29805
- ], function(FloatPanel, KeyboardNavigation, MenuItem) {
30158
+ "tinymce/ui/MenuItem",
30159
+ "tinymce/util/Tools"
30160
+ ], function(FloatPanel, KeyboardNavigation, MenuItem, Tools) {
29806
30161
  "use strict";
29807
30162
 
29808
30163
  var Menu = FloatPanel.extend({
@@ -29825,6 +30180,14 @@ define("tinymce/ui/Menu", [
29825
30180
  settings.autohide = true;
29826
30181
  settings.constrainToViewport = true;
29827
30182
 
30183
+ if (settings.itemDefaults) {
30184
+ var items = settings.items, i = items.length;
30185
+
30186
+ while (i--) {
30187
+ items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
30188
+ }
30189
+ }
30190
+
29828
30191
  self._super(settings);
29829
30192
  self.addClass('menu');
29830
30193
 
@@ -30021,6 +30384,14 @@ define("tinymce/ui/ResizeHandle", [
30021
30384
  self.fire('ResizeEnd');
30022
30385
  }
30023
30386
  });
30387
+ },
30388
+
30389
+ remove: function() {
30390
+ if (this.resizeDragHelper) {
30391
+ this.resizeDragHelper.destroy();
30392
+ }
30393
+
30394
+ return this._super();
30024
30395
  }
30025
30396
  });
30026
30397
  });
@@ -30576,6 +30947,11 @@ define("tinymce/ui/TextBox", [
30576
30947
  });
30577
30948
 
30578
30949
  return self._super();
30950
+ },
30951
+
30952
+ remove: function() {
30953
+ DomUtils.off(this.getEl());
30954
+ this._super();
30579
30955
  }
30580
30956
  });
30581
30957
  });