tinymce-rails 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- // 4.1.2 (2014-07-15)
1
+ // 4.1.3 (2014-07-29)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -428,6 +428,7 @@ define("tinymce/dom/EventUtils", [], function() {
428
428
  events[id][name] = callbackList = [{func: callback, scope: scope}];
429
429
  callbackList.fakeName = fakeName;
430
430
  callbackList.capture = capture;
431
+ //callbackList.callback = callback;
431
432
 
432
433
  // Add the nativeHandler to the callback list so that we can later unbind it
433
434
  callbackList.nativeHandler = nativeHandler;
@@ -3465,7 +3466,7 @@ define("tinymce/dom/DomQuery", [
3465
3466
  'readonly': 'readOnly'
3466
3467
  };
3467
3468
  var cssFix = {
3468
- float: 'cssFloat'
3469
+ 'float': 'cssFloat'
3469
3470
  };
3470
3471
 
3471
3472
  var attrHooks = {}, cssHooks = {};
@@ -4793,7 +4794,7 @@ define("tinymce/dom/DomQuery", [
4793
4794
  DomQuery.overrideDefaults = function(callback) {
4794
4795
  var defaults;
4795
4796
 
4796
- function jQuerySub(selector, context) {
4797
+ function sub(selector, context) {
4797
4798
  defaults = defaults || callback();
4798
4799
 
4799
4800
  if (arguments.length === 0) {
@@ -4804,12 +4805,12 @@ define("tinymce/dom/DomQuery", [
4804
4805
  context = defaults.context;
4805
4806
  }
4806
4807
 
4807
- return new jQuerySub.fn.init(selector, context);
4808
+ return new sub.fn.init(selector, context);
4808
4809
  }
4809
4810
 
4810
- DomQuery.extend(jQuerySub, this);
4811
+ DomQuery.extend(sub, this);
4811
4812
 
4812
- return jQuerySub;
4813
+ return sub;
4813
4814
  };
4814
4815
 
4815
4816
  function appendHooks(targetHooks, prop, hooks) {
@@ -4868,7 +4869,9 @@ define("tinymce/dom/DomQuery", [
4868
4869
  }
4869
4870
 
4870
4871
  if (Env.ie && Env.ie < 9) {
4871
- cssFix.float = 'styleFloat';
4872
+ /*jshint sub:true */
4873
+ /*eslint dot-notation: 0*/
4874
+ cssFix['float'] = 'styleFloat';
4872
4875
 
4873
4876
  appendHooks(cssHooks, 'set', {
4874
4877
  opacity: function(elm, value) {
@@ -7319,6 +7322,10 @@ define("tinymce/dom/DOMUtils", [
7319
7322
  elm = self.$$(elm);
7320
7323
  originalValue = elm.attr(name);
7321
7324
 
7325
+ if (!elm.length) {
7326
+ return;
7327
+ }
7328
+
7322
7329
  hook = self.attrHooks[name];
7323
7330
  if (hook && hook.set) {
7324
7331
  hook.set(elm, value, name);
@@ -7372,11 +7379,14 @@ define("tinymce/dom/DOMUtils", [
7372
7379
 
7373
7380
  elm = self.$$(elm);
7374
7381
 
7375
- hook = self.attrHooks[name];
7376
- if (hook && hook.get) {
7377
- value = hook.get(elm, name);
7378
- } else {
7379
- value = elm.attr(name);
7382
+ if (elm.length) {
7383
+ hook = self.attrHooks[name];
7384
+
7385
+ if (hook && hook.get) {
7386
+ value = hook.get(elm, name);
7387
+ } else {
7388
+ value = elm.attr(name);
7389
+ }
7380
7390
  }
7381
7391
 
7382
7392
  if (typeof value == 'undefined') {
@@ -7395,21 +7405,22 @@ define("tinymce/dom/DOMUtils", [
7395
7405
  * @return {object} Absolute position of the specified element object with x, y fields.
7396
7406
  */
7397
7407
  getPos: function(elm, rootElm) {
7398
- var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
7408
+ var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
7399
7409
 
7400
7410
  elm = self.get(elm);
7401
- rootElm = rootElm || doc.body;
7411
+ rootElm = rootElm || body;
7402
7412
 
7403
7413
  if (elm) {
7404
7414
  // Use getBoundingClientRect if it exists since it's faster than looping offset nodes
7405
- if (rootElm === doc.body && elm.getBoundingClientRect) {
7415
+ // Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
7416
+ if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
7406
7417
  pos = elm.getBoundingClientRect();
7407
- rootElm = self.boxModel ? doc.documentElement : doc.body;
7418
+ rootElm = self.boxModel ? doc.documentElement : body;
7408
7419
 
7409
7420
  // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
7410
7421
  // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
7411
- x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientLeft;
7412
- y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientTop;
7422
+ x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
7423
+ y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
7413
7424
 
7414
7425
  return {x: x, y: y};
7415
7426
  }
@@ -9480,7 +9491,7 @@ define("tinymce/NodeChange", [
9480
9491
 
9481
9492
  // Gecko doesn't support the "selectionchange" event
9482
9493
  if (!('onselectionchange' in editor.getDoc())) {
9483
- editor.on('NodeChange Click MouseUp KeyUp', function(e) {
9494
+ editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
9484
9495
  var nativeRng, fakeRng;
9485
9496
 
9486
9497
  // Since DOM Ranges mutate on modification
@@ -9510,7 +9521,7 @@ define("tinymce/NodeChange", [
9510
9521
  });
9511
9522
 
9512
9523
  editor.on('SelectionChange', function() {
9513
- var startElm = editor.selection.getStart();
9524
+ var startElm = editor.selection.getStart(true);
9514
9525
 
9515
9526
  // Selection change might fire when focus is lost so check if the start is still within the body
9516
9527
  if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
@@ -9518,6 +9529,13 @@ define("tinymce/NodeChange", [
9518
9529
  }
9519
9530
  });
9520
9531
 
9532
+ // Fire an extra nodeChange on mouseup for compatibility reasons
9533
+ editor.on('MouseUp', function(e) {
9534
+ if (!e.isDefaultPrevented()) {
9535
+ editor.nodeChanged();
9536
+ }
9537
+ });
9538
+
9521
9539
  /**
9522
9540
  * Distpaches out a onNodeChange event to all observers. This method should be called when you
9523
9541
  * need to update the UI states or element path etc.
@@ -10088,7 +10106,7 @@ define("tinymce/html/Node", [], function() {
10088
10106
  define("tinymce/html/Schema", [
10089
10107
  "tinymce/util/Tools"
10090
10108
  ], function(Tools) {
10091
- var mapCache = {};
10109
+ var mapCache = {}, dummyObj = {};
10092
10110
  var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
10093
10111
 
10094
10112
  function split(items, delim) {
@@ -10109,11 +10127,11 @@ define("tinymce/html/Schema", [
10109
10127
  function add(name, attributes, children) {
10110
10128
  var ni, i, attributesOrder, args = arguments;
10111
10129
 
10112
- function arrayToMap(array) {
10130
+ function arrayToMap(array, obj) {
10113
10131
  var map = {}, i, l;
10114
10132
 
10115
10133
  for (i = 0, l = array.length; i < l; i++) {
10116
- map[array[i]] = {};
10134
+ map[array[i]] = obj || {};
10117
10135
  }
10118
10136
 
10119
10137
  return map;
@@ -10142,7 +10160,7 @@ define("tinymce/html/Schema", [
10142
10160
  schema[name[ni]] = {
10143
10161
  attributes: arrayToMap(attributesOrder),
10144
10162
  attributesOrder: attributesOrder,
10145
- children: arrayToMap(children)
10163
+ children: arrayToMap(children, dummyObj)
10146
10164
  };
10147
10165
  }
10148
10166
  }
@@ -13865,6 +13883,7 @@ define("tinymce/dom/ControlSelection", [
13865
13883
  }
13866
13884
 
13867
13885
  editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
13886
+ dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
13868
13887
  editor.nodeChanged();
13869
13888
  }
13870
13889
 
@@ -14012,7 +14031,7 @@ define("tinymce/dom/ControlSelection", [
14012
14031
  }
14013
14032
 
14014
14033
  function updateResizeRect(e) {
14015
- var controlElm;
14034
+ var startElm, controlElm;
14016
14035
 
14017
14036
  function isChildOrEqual(node, parent) {
14018
14037
  if (node) {
@@ -14034,9 +14053,10 @@ define("tinymce/dom/ControlSelection", [
14034
14053
 
14035
14054
  if (isChildOrEqual(controlElm, rootElement)) {
14036
14055
  disableGeckoResize();
14056
+ startElm = selection.getStart(true);
14037
14057
 
14038
- if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
14039
- if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
14058
+ if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
14059
+ if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) {
14040
14060
  showResizeRect(controlElm);
14041
14061
  return;
14042
14062
  }
@@ -14196,7 +14216,7 @@ define("tinymce/dom/ControlSelection", [
14196
14216
  }
14197
14217
  }
14198
14218
 
14199
- editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
14219
+ editor.on('nodechange ResizeEditor', updateResizeRect);
14200
14220
 
14201
14221
  // Update resize rect while typing in a table
14202
14222
  editor.on('keydown keyup', function(e) {
@@ -14868,9 +14888,10 @@ define("tinymce/dom/Selection", [
14868
14888
  * node the parent element will be returned.
14869
14889
  *
14870
14890
  * @method getStart
14891
+ * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
14871
14892
  * @return {Element} Start element of selection range.
14872
14893
  */
14873
- getStart: function() {
14894
+ getStart: function(real) {
14874
14895
  var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
14875
14896
 
14876
14897
  if (rng.duplicate || rng.item) {
@@ -14902,7 +14923,9 @@ define("tinymce/dom/Selection", [
14902
14923
  startElement = rng.startContainer;
14903
14924
 
14904
14925
  if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
14905
- startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
14926
+ if (!real || !rng.collapsed) {
14927
+ startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
14928
+ }
14906
14929
  }
14907
14930
 
14908
14931
  if (startElement && startElement.nodeType == 3) {
@@ -14918,9 +14941,10 @@ define("tinymce/dom/Selection", [
14918
14941
  * node the parent element will be returned.
14919
14942
  *
14920
14943
  * @method getEnd
14944
+ * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
14921
14945
  * @return {Element} End element of selection range.
14922
14946
  */
14923
- getEnd: function() {
14947
+ getEnd: function(real) {
14924
14948
  var self = this, rng = self.getRng(), endElement, endOffset;
14925
14949
 
14926
14950
  if (rng.duplicate || rng.item) {
@@ -14945,7 +14969,9 @@ define("tinymce/dom/Selection", [
14945
14969
  endOffset = rng.endOffset;
14946
14970
 
14947
14971
  if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
14948
- endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
14972
+ if (!real || !rng.collapsed) {
14973
+ endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
14974
+ }
14949
14975
  }
14950
14976
 
14951
14977
  if (endElement && endElement.nodeType == 3) {
@@ -15537,8 +15563,8 @@ define("tinymce/dom/Selection", [
15537
15563
  return;
15538
15564
  }
15539
15565
 
15540
- // BR/IMG/INPUT elements
15541
- if (nonEmptyElementsMap[node.nodeName]) {
15566
+ // BR/IMG/INPUT elements but not table cells
15567
+ if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) {
15542
15568
  if (start) {
15543
15569
  rng.setStartBefore(node);
15544
15570
  } else {
@@ -15994,8 +16020,8 @@ define("tinymce/Formatter", [
15994
16020
  {inline: 'strike', remove: 'all'}
15995
16021
  ],
15996
16022
 
15997
- forecolor: {inline: 'span', styles: {color: '%value'}, wrap_links: false, remove_similar: true},
15998
- hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, wrap_links: false, remove_similar: true},
16023
+ forecolor: {inline: 'span', styles: {color: '%value'}, links: true, remove_similar: true},
16024
+ hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, links: true, remove_similar: true},
15999
16025
  fontname: {inline: 'span', styles: {fontFamily: '%value'}},
16000
16026
  fontsize: {inline: 'span', styles: {fontSize: '%value'}},
16001
16027
  fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
@@ -16331,22 +16357,12 @@ define("tinymce/Formatter", [
16331
16357
  each(nodes, process);
16332
16358
  });
16333
16359
 
16334
- // Wrap links inside as well, for example color inside a link when the wrapper is around the link
16335
- if (format.wrap_links === false) {
16360
+ // Apply formats to links as well to get the color of the underline to change as well
16361
+ if (format.links === true) {
16336
16362
  each(newWrappers, function(node) {
16337
16363
  function process(node) {
16338
- var i, currentWrapElm, children;
16339
-
16340
16364
  if (node.nodeName === 'A') {
16341
- currentWrapElm = dom.clone(wrapElm, FALSE);
16342
- newWrappers.push(currentWrapElm);
16343
-
16344
- children = grep(node.childNodes);
16345
- for (i = 0; i < children.length; i++) {
16346
- currentWrapElm.appendChild(children[i]);
16347
- }
16348
-
16349
- node.appendChild(currentWrapElm);
16365
+ setElementFormat(node, format);
16350
16366
  }
16351
16367
 
16352
16368
  each(grep(node.childNodes), process);
@@ -16416,24 +16432,10 @@ define("tinymce/Formatter", [
16416
16432
  // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
16417
16433
  // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
16418
16434
  each(dom.select(format.inline, node), function(child) {
16419
- var parent;
16420
-
16421
16435
  if (isBookmarkNode(child)) {
16422
16436
  return;
16423
16437
  }
16424
16438
 
16425
- // When wrap_links is set to false we don't want
16426
- // to remove the format on children within links
16427
- if (format.wrap_links === false) {
16428
- parent = child.parentNode;
16429
-
16430
- do {
16431
- if (parent.nodeName === 'A') {
16432
- return;
16433
- }
16434
- } while ((parent = parent.parentNode));
16435
- }
16436
-
16437
16439
  removeFormat(format, vars, child, format.exact ? child : null);
16438
16440
  });
16439
16441
  });
@@ -16975,7 +16977,7 @@ define("tinymce/Formatter", [
16975
16977
 
16976
16978
  // Ignore bogus nodes like the <a> tag created by moveStart()
16977
16979
  parents = Tools.grep(parents, function(node) {
16978
- return !node.getAttribute('data-mce-bogus');
16980
+ return node.nodeType == 1 && !node.getAttribute('data-mce-bogus');
16979
16981
  });
16980
16982
 
16981
16983
  // Check for new formats
@@ -17055,8 +17057,8 @@ define("tinymce/Formatter", [
17055
17057
  // Initialize
17056
17058
  defaultFormats();
17057
17059
  addKeyboardShortcuts();
17058
- ed.on('BeforeGetContent', function() {
17059
- if (markCaretContainersBogus) {
17060
+ ed.on('BeforeGetContent', function(e) {
17061
+ if (markCaretContainersBogus && e.format != 'raw') {
17060
17062
  markCaretContainersBogus();
17061
17063
  }
17062
17064
  });
@@ -17546,6 +17548,10 @@ define("tinymce/Formatter", [
17546
17548
  };
17547
17549
  }
17548
17550
 
17551
+ function isColorFormatAndAnchor(node, format) {
17552
+ return format.links && node.tagName == 'A';
17553
+ }
17554
+
17549
17555
  /**
17550
17556
  * Removes the specified format for the specified node. It will also remove the node if it doesn't have
17551
17557
  * any attributes if the format specifies it to do so.
@@ -17561,7 +17567,7 @@ define("tinymce/Formatter", [
17561
17567
  var i, attrs, stylesModified;
17562
17568
 
17563
17569
  // Check if node matches format
17564
- if (!matchName(node, format)) {
17570
+ if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) {
17565
17571
  return FALSE;
17566
17572
  }
17567
17573
 
@@ -20774,7 +20780,7 @@ define("tinymce/util/EventDispatcher", [
20774
20780
  "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
20775
20781
  "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
20776
20782
  "draggesture dragdrop drop drag submit " +
20777
- "compositionstart compositionend compositionupdate",
20783
+ "compositionstart compositionend compositionupdate touchstart touchend",
20778
20784
  ' '
20779
20785
  );
20780
20786
 
@@ -21968,7 +21974,6 @@ define("tinymce/ui/Control", [
21968
21974
  ], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
21969
21975
  "use strict";
21970
21976
 
21971
- var elementIdCache = {};
21972
21977
  var hasMouseWheelEventSupport = "onmousewheel" in document;
21973
21978
  var hasWheelEventSupport = false;
21974
21979
  var classPrefix = "mce-";
@@ -21998,7 +22003,6 @@ define("tinymce/ui/Control", [
21998
22003
 
21999
22004
  var Control = Class.extend({
22000
22005
  Statics: {
22001
- elementIdCache: elementIdCache,
22002
22006
  classPrefix: classPrefix
22003
22007
  },
22004
22008
 
@@ -22041,6 +22045,7 @@ define("tinymce/ui/Control", [
22041
22045
  self._text = self._name = '';
22042
22046
  self._width = self._height = 0;
22043
22047
  self._aria = {role: settings.role};
22048
+ this._elmCache = {};
22044
22049
 
22045
22050
  // Setup classes
22046
22051
  classes = settings.classes;
@@ -22782,15 +22787,16 @@ define("tinymce/ui/Control", [
22782
22787
  *
22783
22788
  * @method getEl
22784
22789
  * @param {String} [suffix] Suffix to get element by.
22785
- * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
22786
22790
  * @return {Element} HTML DOM element for the current control or it's children.
22787
22791
  */
22788
- getEl: function(suffix, dropCache) {
22789
- var elm, id = suffix ? this._id + '-' + suffix : this._id;
22792
+ getEl: function(suffix) {
22793
+ var id = suffix ? this._id + '-' + suffix : this._id;
22790
22794
 
22791
- elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
22795
+ if (!this._elmCache[id]) {
22796
+ this._elmCache[id] = DomUtils.get(id);
22797
+ }
22792
22798
 
22793
- return elm;
22799
+ return this._elmCache[id];
22794
22800
  },
22795
22801
 
22796
22802
  /**
@@ -23001,16 +23007,7 @@ define("tinymce/ui/Control", [
23001
23007
  delete lookup[self._id];
23002
23008
  }
23003
23009
 
23004
- delete elementIdCache[self._id];
23005
-
23006
23010
  if (elm && elm.parentNode) {
23007
- var nodes = elm.getElementsByTagName('*');
23008
-
23009
- i = nodes.length;
23010
- while (i--) {
23011
- delete elementIdCache[nodes[i].id];
23012
- }
23013
-
23014
23011
  elm.parentNode.removeChild(elm);
23015
23012
  }
23016
23013
 
@@ -25163,8 +25160,15 @@ define("tinymce/ui/FloatPanel", [
25163
25160
 
25164
25161
  function bindWindowResizeHandler() {
25165
25162
  if (!windowResizeHandler) {
25163
+ var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight;
25164
+
25166
25165
  windowResizeHandler = function() {
25167
- FloatPanel.hideAll();
25166
+ // Workaround for #7065 IE 7 fires resize events event though the window wasn't resized
25167
+ if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) {
25168
+ clientWidth = docElm.clientWidth;
25169
+ clientHeight = docElm.clientHeight;
25170
+ FloatPanel.hideAll();
25171
+ }
25168
25172
  };
25169
25173
 
25170
25174
  DomUtils.on(window, 'resize', windowResizeHandler);
@@ -26110,6 +26114,14 @@ define("tinymce/WindowManager", [
26110
26114
 
26111
26115
  self.windows = windows;
26112
26116
 
26117
+ editor.on('remove', function() {
26118
+ var i = windows.length;
26119
+
26120
+ while (i--) {
26121
+ windows[i].close();
26122
+ }
26123
+ });
26124
+
26113
26125
  /**
26114
26126
  * Opens a new window.
26115
26127
  *
@@ -26436,7 +26448,7 @@ define("tinymce/util/Quirks", [
26436
26448
 
26437
26449
  // Make sure all elements has a data-mce-style attribute
26438
26450
  if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
26439
- editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
26451
+ editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style'));
26440
26452
  }
26441
26453
  });
26442
26454
 
@@ -26858,35 +26870,6 @@ define("tinymce/util/Quirks", [
26858
26870
  });
26859
26871
  }
26860
26872
 
26861
- /**
26862
- * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
26863
- * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
26864
- */
26865
- function selectionChangeNodeChanged() {
26866
- var lastRng, selectionTimer;
26867
-
26868
- editor.on('selectionchange', function() {
26869
- if (selectionTimer) {
26870
- clearTimeout(selectionTimer);
26871
- selectionTimer = 0;
26872
- }
26873
-
26874
- selectionTimer = window.setTimeout(function() {
26875
- if (editor.removed) {
26876
- return;
26877
- }
26878
-
26879
- var rng = selection.getRng();
26880
-
26881
- // Compare the ranges to see if it was a real change or not
26882
- if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
26883
- editor.nodeChanged();
26884
- lastRng = rng;
26885
- }
26886
- }, 50);
26887
- });
26888
- }
26889
-
26890
26873
  /**
26891
26874
  * Screen readers on IE needs to have the role application set on the body.
26892
26875
  */
@@ -27413,6 +27396,53 @@ define("tinymce/util/Quirks", [
27413
27396
  editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
27414
27397
  }
27415
27398
 
27399
+ /**
27400
+ * iOS Safari and possible other browsers have a bug where it won't fire
27401
+ * a click event when a contentEditable is focused. This function fakes click events
27402
+ * by using touchstart/touchend and measuring the time and distance travelled.
27403
+ */
27404
+ function touchClickEvent() {
27405
+ editor.on('touchstart', function(e) {
27406
+ var elm, time, startTouch, changedTouches;
27407
+
27408
+ elm = e.target;
27409
+ time = new Date().getTime();
27410
+ changedTouches = e.changedTouches;
27411
+
27412
+ if (!changedTouches || changedTouches.length > 1) {
27413
+ return;
27414
+ }
27415
+
27416
+ startTouch = changedTouches[0];
27417
+
27418
+ editor.once('touchend', function(e) {
27419
+ var endTouch = e.changedTouches[0], args;
27420
+
27421
+ if (new Date().getTime() - time > 500) {
27422
+ return;
27423
+ }
27424
+
27425
+ if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) {
27426
+ return;
27427
+ }
27428
+
27429
+ if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) {
27430
+ return;
27431
+ }
27432
+
27433
+ args = {
27434
+ target: elm
27435
+ };
27436
+
27437
+ each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) {
27438
+ args[key] = endTouch[key];
27439
+ });
27440
+
27441
+ args = editor.fire('click', args);
27442
+ });
27443
+ });
27444
+ }
27445
+
27416
27446
  /**
27417
27447
  * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
27418
27448
  * For example this: <form><button></form>
@@ -27459,10 +27489,10 @@ define("tinymce/util/Quirks", [
27459
27489
  blockFormSubmitInsideEditor();
27460
27490
  disableBackspaceIntoATable();
27461
27491
  removeAppleInterchangeBrs();
27492
+ touchClickEvent();
27462
27493
 
27463
27494
  // iOS
27464
27495
  if (Env.iOS) {
27465
- selectionChangeNodeChanged();
27466
27496
  restoreFocusOnKeyDown();
27467
27497
  bodyHeight();
27468
27498
  tapLinksAndImages();
@@ -27664,8 +27694,18 @@ define("tinymce/EditorObservable", [
27664
27694
  "tinymce/dom/DOMUtils",
27665
27695
  "tinymce/util/Tools"
27666
27696
  ], function(Observable, DOMUtils, Tools) {
27667
- var DOM = DOMUtils.DOM;
27697
+ var DOM = DOMUtils.DOM, customEventRootDelegates;
27668
27698
 
27699
+ /**
27700
+ * Returns the event target so for the specified event. Some events fire
27701
+ * only on document, some fire on documentElement etc. This also handles the
27702
+ * custom event root setting where it returns that element instead of the body.
27703
+ *
27704
+ * @private
27705
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
27706
+ * @param {String} eventName Name of the event for example "click".
27707
+ * @return {Element/Document} HTML Element or document target to bind on.
27708
+ */
27669
27709
  function getEventTarget(editor, eventName) {
27670
27710
  if (eventName == 'selectionchange') {
27671
27711
  return editor.getDoc();
@@ -27674,62 +27714,96 @@ define("tinymce/EditorObservable", [
27674
27714
  // Need to bind mousedown/mouseup etc to document not body in iframe mode
27675
27715
  // Since the user might click on the HTML element not the BODY
27676
27716
  if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
27677
- return editor.getDoc();
27717
+ return editor.getDoc().documentElement;
27718
+ }
27719
+
27720
+ // Bind to event root instead of body if it's defined
27721
+ if (editor.settings.event_root) {
27722
+ if (!editor.eventRoot) {
27723
+ editor.eventRoot = DOM.select(editor.settings.event_root)[0];
27724
+ }
27725
+
27726
+ return editor.eventRoot;
27678
27727
  }
27679
27728
 
27680
27729
  return editor.getBody();
27681
27730
  }
27682
27731
 
27683
- function bindEventDelegate(editor, name) {
27684
- var eventRootSelector = editor.settings.event_root, editorManager = editor.editorManager;
27685
- var eventRootElm = editorManager.eventRootElm || getEventTarget(editor, name);
27732
+ /**
27733
+ * Binds a event delegate for the specified name this delegate will fire
27734
+ * the event to the editor dispatcher.
27735
+ *
27736
+ * @private
27737
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
27738
+ * @param {String} eventName Name of the event for example "click".
27739
+ */
27740
+ function bindEventDelegate(editor, eventName) {
27741
+ var eventRootElm = getEventTarget(editor, eventName), delegate;
27742
+
27743
+ if (!editor.delegates) {
27744
+ editor.delegates = {};
27745
+ }
27686
27746
 
27687
- if (eventRootSelector) {
27688
- if (!editorManager.rootEvents) {
27689
- editorManager.rootEvents = {};
27747
+ if (editor.delegates[eventName]) {
27748
+ return;
27749
+ }
27690
27750
 
27691
- editorManager.on('RemoveEditor', function() {
27692
- if (!editorManager.activeEditor) {
27693
- DOM.unbind(eventRootElm);
27694
- delete editorManager.rootEvents;
27751
+ if (editor.settings.event_root) {
27752
+ if (!customEventRootDelegates) {
27753
+ customEventRootDelegates = {};
27754
+ editor.editorManager.on('removeEditor', function() {
27755
+ var name;
27756
+
27757
+ if (!editor.editorManager.activeEditor) {
27758
+ if (customEventRootDelegates) {
27759
+ for (name in customEventRootDelegates) {
27760
+ editor.dom.unbind(getEventTarget(editor, name));
27761
+ }
27762
+
27763
+ customEventRootDelegates = null;
27764
+ }
27695
27765
  }
27696
27766
  });
27697
27767
  }
27698
27768
 
27699
- if (editorManager.rootEvents[name]) {
27769
+ if (customEventRootDelegates[eventName]) {
27700
27770
  return;
27701
27771
  }
27702
27772
 
27703
- if (eventRootElm == editor.getBody()) {
27704
- eventRootElm = DOM.select(eventRootSelector)[0];
27705
- editorManager.eventRootElm = eventRootElm;
27706
- }
27707
-
27708
- editorManager.rootEvents[name] = true;
27709
-
27710
- DOM.bind(eventRootElm, name, function(e) {
27711
- var target = e.target, editors = editorManager.editors, i = editors.length;
27773
+ delegate = function(e) {
27774
+ var target = e.target, editors = editor.editorManager.editors, i = editors.length;
27712
27775
 
27713
27776
  while (i--) {
27714
27777
  var body = editors[i].getBody();
27715
27778
 
27716
27779
  if (body === target || DOM.isChildOf(target, body)) {
27717
27780
  if (!editors[i].hidden) {
27718
- editors[i].fire(name, e);
27781
+ editors[i].fire(eventName, e);
27719
27782
  }
27720
27783
  }
27721
27784
  }
27722
- });
27785
+ };
27786
+
27787
+ customEventRootDelegates[eventName] = delegate;
27788
+ DOM.bind(eventRootElm, eventName, delegate);
27723
27789
  } else {
27724
- editor.dom.bind(eventRootElm, name, function(e) {
27790
+ delegate = function(e) {
27725
27791
  if (!editor.hidden) {
27726
- editor.fire(name, e);
27792
+ editor.fire(eventName, e);
27727
27793
  }
27728
- });
27794
+ };
27795
+
27796
+ DOM.bind(eventRootElm, eventName, delegate);
27797
+ editor.delegates[eventName] = delegate;
27729
27798
  }
27730
27799
  }
27731
27800
 
27732
27801
  var EditorObservable = {
27802
+ /**
27803
+ * Bind any pending event delegates. This gets executed after the target body/document is created.
27804
+ *
27805
+ * @private
27806
+ */
27733
27807
  bindPendingEventDelegates: function() {
27734
27808
  var self = this;
27735
27809
 
@@ -27738,6 +27812,12 @@ define("tinymce/EditorObservable", [
27738
27812
  });
27739
27813
  },
27740
27814
 
27815
+ /**
27816
+ * Toggles a native event on/off this is called by the EventDispatcher when
27817
+ * the first native event handler is added and when the last native event handler is removed.
27818
+ *
27819
+ * @private
27820
+ */
27741
27821
  toggleNativeEvent: function(name, state) {
27742
27822
  var self = this;
27743
27823
 
@@ -27761,8 +27841,35 @@ define("tinymce/EditorObservable", [
27761
27841
  }
27762
27842
  }
27763
27843
  } else if (self.initialized) {
27764
- self.dom.unbind(getEventTarget(self, name), name);
27844
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
27845
+ delete self.delegates[name];
27846
+ }
27847
+ },
27848
+
27849
+ /**
27850
+ * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
27851
+ *
27852
+ * @private
27853
+ */
27854
+ unbindAllNativeEvents: function() {
27855
+ var self = this, name;
27856
+
27857
+ if (self.delegates) {
27858
+ for (name in self.delegates) {
27859
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
27860
+ }
27861
+
27862
+ delete self.delegates;
27863
+ }
27864
+
27865
+ if (!self.inline) {
27866
+ self.getBody().onload = null;
27867
+ self.dom.unbind(self.getWin());
27868
+ self.dom.unbind(self.getDoc());
27765
27869
  }
27870
+
27871
+ self.dom.unbind(self.getBody());
27872
+ self.dom.unbind(self.getContainer());
27766
27873
  }
27767
27874
  };
27768
27875
 
@@ -28569,6 +28676,9 @@ define("tinymce/Editor", [
28569
28676
 
28570
28677
  DOM.setAttrib("src", url || 'javascript:""');
28571
28678
 
28679
+ self.contentAreaContainer = o.iframeContainer;
28680
+ self.iframeElement = ifr;
28681
+
28572
28682
  n = DOM.add(o.iframeContainer, ifr);
28573
28683
 
28574
28684
  // Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
@@ -28581,9 +28691,6 @@ define("tinymce/Editor", [
28581
28691
  }
28582
28692
  }
28583
28693
 
28584
- self.contentAreaContainer = o.iframeContainer;
28585
- self.iframeElement = ifr;
28586
-
28587
28694
  if (o.editorContainer) {
28588
28695
  DOM.get(o.editorContainer).style.display = self.orgDisplay;
28589
28696
  self.hidden = DOM.isHidden(o.editorContainer);
@@ -28894,12 +29001,15 @@ define("tinymce/Editor", [
28894
29001
  // Handle auto focus
28895
29002
  if (settings.auto_focus) {
28896
29003
  setTimeout(function() {
28897
- var ed = self.editorManager.get(settings.auto_focus);
29004
+ var editor;
29005
+
29006
+ if (settings.auto_focus === true) {
29007
+ editor = self;
29008
+ } else {
29009
+ editor = self.editorManager.get(settings.auto_focus);
29010
+ }
28898
29011
 
28899
- ed.selection.select(ed.getBody(), 1);
28900
- ed.selection.collapse(1);
28901
- ed.getBody().focus();
28902
- ed.getWin().focus();
29012
+ editor.focus();
28903
29013
  }, 100);
28904
29014
  }
28905
29015
 
@@ -29942,6 +30052,7 @@ define("tinymce/Editor", [
29942
30052
  if (!self.removed) {
29943
30053
  self.save();
29944
30054
  self.removed = 1;
30055
+ self.unbindAllNativeEvents();
29945
30056
 
29946
30057
  // Remove any hidden input
29947
30058
  if (self.hasHiddenInput) {
@@ -29957,21 +30068,12 @@ define("tinymce/Editor", [
29957
30068
 
29958
30069
  DOM.setStyle(self.id, 'display', self.orgDisplay);
29959
30070
  self.getBody().onload = null; // Prevent #6816
29960
-
29961
- // Don't clear the window or document if content editable
29962
- // is enabled since other instances might still be present
29963
- Event.unbind(self.getWin());
29964
- Event.unbind(self.getDoc());
29965
30071
  }
29966
30072
 
29967
- var elm = self.getContainer();
29968
- Event.unbind(self.getBody());
29969
- Event.unbind(elm);
29970
-
29971
30073
  self.fire('remove');
29972
30074
 
29973
30075
  self.editorManager.remove(self);
29974
- DOM.remove(elm);
30076
+ DOM.remove(self.getContainer());
29975
30077
  self.destroy();
29976
30078
  }
29977
30079
  },
@@ -29999,14 +30101,6 @@ define("tinymce/Editor", [
29999
30101
  return;
30000
30102
  }
30001
30103
 
30002
- // We must unbind on Gecko since it would otherwise produce the pesky "attempt
30003
- // to run compile-and-go script on a cleared scope" message
30004
- if (automatic && isGecko) {
30005
- Event.unbind(self.getDoc());
30006
- Event.unbind(self.getWin());
30007
- Event.unbind(self.getBody());
30008
- }
30009
-
30010
30104
  if (!automatic) {
30011
30105
  self.editorManager.off('beforeunload', self._beforeUnload);
30012
30106
 
@@ -30489,6 +30583,7 @@ define("tinymce/EditorManager", [
30489
30583
  // User has manually destroyed the editor lets clean up the mess
30490
30584
  if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
30491
30585
  removeEditorFromList(editor);
30586
+ editor.unbindAllNativeEvents();
30492
30587
  editor.destroy(true);
30493
30588
  editor = null;
30494
30589
  }
@@ -30519,7 +30614,7 @@ define("tinymce/EditorManager", [
30519
30614
  * @property minorVersion
30520
30615
  * @type String
30521
30616
  */
30522
- minorVersion: '1.2',
30617
+ minorVersion: '1.3',
30523
30618
 
30524
30619
  /**
30525
30620
  * Release date of TinyMCE build.
@@ -30527,7 +30622,7 @@ define("tinymce/EditorManager", [
30527
30622
  * @property releaseDate
30528
30623
  * @type String
30529
30624
  */
30530
- releaseDate: '2014-07-15',
30625
+ releaseDate: '2014-07-29',
30531
30626
 
30532
30627
  /**
30533
30628
  * Collection of editor instances.
@@ -31146,6 +31241,7 @@ define("tinymce/LegacyInput", [
31146
31241
  /**
31147
31242
  * This class enables you to send XMLHTTPRequests cross browser.
31148
31243
  * @class tinymce.util.XHR
31244
+ * @mixes tinymce.util.Observable
31149
31245
  * @static
31150
31246
  * @example
31151
31247
  * // Sends a low level Ajax request
@@ -31155,9 +31251,17 @@ define("tinymce/LegacyInput", [
31155
31251
  * console.debug(text);
31156
31252
  * }
31157
31253
  * });
31254
+ *
31255
+ * // Add custom header to XHR request
31256
+ * tinymce.util.XHR.on('beforeSend', function(e) {
31257
+ * e.xhr.setRequestHeader('X-Requested-With', 'Something');
31258
+ * });
31158
31259
  */
31159
- define("tinymce/util/XHR", [], function() {
31160
- return {
31260
+ define("tinymce/util/XHR", [
31261
+ "tinymce/util/Observable",
31262
+ "tinymce/util/Tools"
31263
+ ], function(Observable, Tools) {
31264
+ var XHR = {
31161
31265
  /**
31162
31266
  * Sends a XMLHTTPRequest.
31163
31267
  * Consult the Wiki for details on what settings this method takes.
@@ -31201,12 +31305,14 @@ define("tinymce/util/XHR", [], function() {
31201
31305
  if (settings.crossDomain) {
31202
31306
  xhr.withCredentials = true;
31203
31307
  }
31308
+
31204
31309
  if (settings.content_type) {
31205
31310
  xhr.setRequestHeader('Content-Type', settings.content_type);
31206
31311
  }
31207
31312
 
31208
31313
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
31209
31314
 
31315
+ xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;
31210
31316
  xhr.send(settings.data);
31211
31317
 
31212
31318
  // Syncronous request
@@ -31219,6 +31325,10 @@ define("tinymce/util/XHR", [], function() {
31219
31325
  }
31220
31326
  }
31221
31327
  };
31328
+
31329
+ Tools.extend(XHR, Observable);
31330
+
31331
+ return XHR;
31222
31332
  });
31223
31333
 
31224
31334
  // Included from: js/tinymce/classes/util/JSON.js
@@ -33085,6 +33195,15 @@ define("tinymce/ui/PanelButton", [
33085
33195
  });
33086
33196
 
33087
33197
  return self._super();
33198
+ },
33199
+
33200
+ remove: function() {
33201
+ if (this.panel) {
33202
+ this.panel.remove();
33203
+ this.panel = null;
33204
+ }
33205
+
33206
+ return this._super();
33088
33207
  }
33089
33208
  });
33090
33209
  });
@@ -34871,7 +34990,6 @@ define("tinymce/ui/FormatControls", [
34871
34990
  paste: ['Paste', 'Paste'],
34872
34991
  help: ['Help', 'mceHelp'],
34873
34992
  selectall: ['Select all', 'SelectAll'],
34874
- hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
34875
34993
  removeformat: ['Clear formatting', 'RemoveFormat'],
34876
34994
  visualaid: ['Visual aids', 'mceToggleVisualAid'],
34877
34995
  newdocument: ['New document', 'mceNewDocument']