tinymce-rails 4.1.2 → 4.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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']