tinymce-rails 3.4.7.0.2 → 3.4.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,9 +5,9 @@
5
5
  var tinymce = {
6
6
  majorVersion : '3',
7
7
 
8
- minorVersion : '4.7',
8
+ minorVersion : '4.8',
9
9
 
10
- releaseDate : '2011-11-03',
10
+ releaseDate : '2012-02-02',
11
11
 
12
12
  _init : function() {
13
13
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -535,7 +535,7 @@ tinymce.create('tinymce.util.Dispatcher', {
535
535
  // And this is also more efficient
536
536
  for (i = 0; i<li.length; i++) {
537
537
  c = li[i];
538
- s = c.cb.apply(c.scope, a);
538
+ s = c.cb.apply(c.scope, a.length > 0 ? a : [c.scope]);
539
539
 
540
540
  if (s === false)
541
541
  break;
@@ -578,7 +578,7 @@ tinymce.create('tinymce.util.Dispatcher', {
578
578
 
579
579
  // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
580
580
  u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
581
- u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
581
+ u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
582
582
  each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
583
583
  var s = u[i];
584
584
 
@@ -1048,7 +1048,10 @@ tinymce.create('static tinymce.util.XHR', {
1048
1048
  TAB: 9,
1049
1049
  SPACEBAR: 32,
1050
1050
  UP: 38,
1051
- DOWN: 40
1051
+ DOWN: 40,
1052
+ modifierPressed: function (e) {
1053
+ return e.shiftKey || e.ctrlKey || e.altKey;
1054
+ }
1052
1055
  }
1053
1056
  })(tinymce);
1054
1057
 
@@ -1062,7 +1065,7 @@ tinymce.create('static tinymce.util.XHR', {
1062
1065
  var rng, blockElm, node, clonedSpan, isDelete;
1063
1066
 
1064
1067
  isDelete = e.keyCode == DELETE;
1065
- if (isDelete || e.keyCode == BACKSPACE) {
1068
+ if ((isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
1066
1069
  e.preventDefault();
1067
1070
  rng = selection.getRng();
1068
1071
 
@@ -1179,6 +1182,26 @@ tinymce.create('static tinymce.util.XHR', {
1179
1182
  });
1180
1183
  };
1181
1184
 
1185
+ function removeStylesOnPTagsInheritedFromHeadingTag(ed) {
1186
+ ed.onKeyDown.add(function(ed, event) {
1187
+ function checkInHeadingTag(ed) {
1188
+ var currentNode = ed.selection.getNode();
1189
+ var headingTags = 'h1,h2,h3,h4,h5,h6';
1190
+ return ed.dom.is(currentNode, headingTags) || ed.dom.getParent(currentNode, headingTags) !== null;
1191
+ }
1192
+
1193
+ if (event.keyCode === VK.ENTER && !VK.modifierPressed(event) && checkInHeadingTag(ed)) {
1194
+ setTimeout(function() {
1195
+ var currentNode = ed.selection.getNode();
1196
+ if (ed.dom.is(currentNode, 'p')) {
1197
+ ed.dom.setAttrib(currentNode, 'style', null);
1198
+ // While tiny's content is correct after this method call, the content shown is not representative of it and needs to be 'repainted'
1199
+ ed.execCommand('mceCleanup');
1200
+ }
1201
+ }, 0);
1202
+ }
1203
+ });
1204
+ }
1182
1205
  function selectionChangeNodeChanged(ed) {
1183
1206
  var lastRng, selectionTimer;
1184
1207
 
@@ -1203,7 +1226,7 @@ tinymce.create('static tinymce.util.XHR', {
1203
1226
  function ensureBodyHasRoleApplication(ed) {
1204
1227
  document.body.setAttribute("role", "application");
1205
1228
  }
1206
-
1229
+
1207
1230
  tinymce.create('tinymce.util.Quirks', {
1208
1231
  Quirks: function(ed) {
1209
1232
  // WebKit
@@ -1224,6 +1247,7 @@ tinymce.create('static tinymce.util.XHR', {
1224
1247
  removeHrOnBackspace(ed);
1225
1248
  emptyEditorWhenDeleting(ed);
1226
1249
  ensureBodyHasRoleApplication(ed);
1250
+ removeStylesOnPTagsInheritedFromHeadingTag(ed)
1227
1251
  }
1228
1252
 
1229
1253
  // Gecko
@@ -3929,6 +3953,7 @@ tinymce.html.Writer = function(settings) {
3929
3953
 
3930
3954
  return this.run(e, function(e) {
3931
3955
  var s = t.settings;
3956
+ var originalValue = e.getAttribute(n);
3932
3957
  if (v !== null) {
3933
3958
  switch (n) {
3934
3959
  case "style":
@@ -3975,6 +4000,12 @@ tinymce.html.Writer = function(settings) {
3975
4000
  e.setAttribute(n, '' + v, 2);
3976
4001
  else
3977
4002
  e.removeAttribute(n, 2);
4003
+
4004
+ // fire onChangeAttrib event for attributes that have changed
4005
+ if (tinyMCE.activeEditor && originalValue != v) {
4006
+ var ed = tinyMCE.activeEditor;
4007
+ ed.onSetAttrib.dispatch(ed, e, n, v);
4008
+ }
3978
4009
  });
3979
4010
  },
3980
4011
 
@@ -4680,6 +4711,12 @@ tinymce.html.Writer = function(settings) {
4680
4711
  function trim(node) {
4681
4712
  var i, children = node.childNodes, type = node.nodeType;
4682
4713
 
4714
+ function surroundedBySpans(node) {
4715
+ var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
4716
+ var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
4717
+ return previousIsSpan && nextIsSpan;
4718
+ }
4719
+
4683
4720
  if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')
4684
4721
  return;
4685
4722
 
@@ -4690,7 +4727,10 @@ tinymce.html.Writer = function(settings) {
4690
4727
  // Keep non whitespace text nodes
4691
4728
  if (type == 3 && node.nodeValue.length > 0) {
4692
4729
  // If parent element isn't a block or there isn't any useful contents for example "<p> </p>"
4693
- if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)
4730
+ // Also keep text nodes with only spaces if surrounded by spans.
4731
+ // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
4732
+ var trimmedLength = tinymce.trim(node.nodeValue).length;
4733
+ if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength == 0 && surroundedBySpans(node))
4694
4734
  return;
4695
4735
  } else if (type == 1) {
4696
4736
  // If the only child is a bookmark then move it up
@@ -4727,9 +4767,9 @@ tinymce.html.Writer = function(settings) {
4727
4767
 
4728
4768
  // Insert middle chunk
4729
4769
  if (re)
4730
- pa.replaceChild(re, e);
4731
- else
4732
- pa.insertBefore(e, pe);
4770
+ pa.replaceChild(re, e);
4771
+ else
4772
+ pa.insertBefore(e, pe);
4733
4773
 
4734
4774
  // Insert after chunk
4735
4775
  pa.insertBefore(trim(aft), pe);
@@ -8029,7 +8069,8 @@ window.tinymce.dom.Sizzle = Sizzle;
8029
8069
  }
8030
8070
 
8031
8071
  s.addRange(r);
8032
- t.selectedRange = s.getRangeAt(0);
8072
+ // adding range isn't always successful so we need to check range count otherwise an exception can occur
8073
+ t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;
8033
8074
  }
8034
8075
  } else {
8035
8076
  // Is W3C Range
@@ -8141,6 +8182,11 @@ window.tinymce.dom.Sizzle = Sizzle;
8141
8182
  normalize : function() {
8142
8183
  var self = this, rng, normalized;
8143
8184
 
8185
+ // TODO:
8186
+ // Retain selection direction.
8187
+ // Lean left/right on Gecko for inline elements.
8188
+ // Run this on mouse up/key up when the user manually moves the selection
8189
+
8144
8190
  // Normalize only on non IE browsers for now
8145
8191
  if (tinymce.isIE)
8146
8192
  return;
@@ -8175,18 +8221,24 @@ window.tinymce.dom.Sizzle = Sizzle;
8175
8221
  if (node.nodeType === 3) {
8176
8222
  offset = start ? 0 : node.nodeValue.length - 1;
8177
8223
  container = node;
8224
+ normalized = true;
8178
8225
  break;
8179
8226
  }
8180
8227
 
8181
- // Found a BR element that we can place the caret before
8182
- if (node.nodeName === 'BR') {
8228
+ // Found a BR/IMG element that we can place the caret before
8229
+ if (/^(BR|IMG)$/.test(node.nodeName)) {
8183
8230
  offset = dom.nodeIndex(node);
8184
8231
  container = node.parentNode;
8232
+
8233
+ // Put caret after image when moving the end point
8234
+ if (node.nodeName == "IMG" && !start) {
8235
+ offset++;
8236
+ }
8237
+
8238
+ normalized = true;
8185
8239
  break;
8186
8240
  }
8187
8241
  } while (node = (start ? walker.next() : walker.prev()));
8188
-
8189
- normalized = true;
8190
8242
  }
8191
8243
  }
8192
8244
  }
@@ -8201,7 +8253,7 @@ window.tinymce.dom.Sizzle = Sizzle;
8201
8253
  // Normalize the end points
8202
8254
  normalizeEndPoint(true);
8203
8255
 
8204
- if (rng.collapsed)
8256
+ if (!rng.collapsed)
8205
8257
  normalizeEndPoint();
8206
8258
 
8207
8259
  // Set the selection if it was normalized
@@ -8313,12 +8365,11 @@ window.tinymce.dom.Sizzle = Sizzle;
8313
8365
  if (!settings.apply_source_formatting)
8314
8366
  settings.indent = false;
8315
8367
 
8316
- settings.remove_trailing_brs = true;
8317
-
8318
8368
  // Default DOM and Schema if they are undefined
8319
8369
  dom = dom || tinymce.DOM;
8320
8370
  schema = schema || new tinymce.html.Schema(settings);
8321
8371
  settings.entity_encoding = settings.entity_encoding || 'named';
8372
+ settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
8322
8373
 
8323
8374
  onPreProcess = new tinymce.util.Dispatcher(self);
8324
8375
 
@@ -8382,8 +8433,8 @@ window.tinymce.dom.Sizzle = Sizzle;
8382
8433
  function trim(value) {
8383
8434
  return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
8384
8435
  .replace(/^[\r\n]*|[\r\n]*$/g, '')
8385
- .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')
8386
- .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
8436
+ .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
8437
+ .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
8387
8438
  };
8388
8439
 
8389
8440
  while (i--) {
@@ -9684,7 +9735,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9684
9735
  // Internal functions
9685
9736
  _setupKeyboardNav : function(){
9686
9737
  var contextMenu, menuItems, t=this;
9687
- contextMenu = DOM.select('#menu_' + t.id)[0];
9738
+ contextMenu = DOM.get('menu_' + t.id);
9688
9739
  menuItems = DOM.select('a[role=option]', 'menu_' + t.id);
9689
9740
  menuItems.splice(0,0,contextMenu);
9690
9741
  t.keyboardNav = new tinymce.ui.KeyboardNavigation({
@@ -9791,10 +9842,26 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9791
9842
  },
9792
9843
 
9793
9844
  postRender : function() {
9794
- var t = this, s = t.settings;
9845
+ var t = this, s = t.settings, bookmark;
9795
9846
 
9847
+ // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so
9848
+ // need to keep the selection in case the selection is lost
9849
+ if (tinymce.isIE && t.editor) {
9850
+ tinymce.dom.Event.add(t.id, 'mousedown', function(e) {
9851
+ bookmark = t.editor.selection.getBookmark();
9852
+ });
9853
+ }
9796
9854
  tinymce.dom.Event.add(t.id, 'click', function(e) {
9797
- if (!t.isDisabled())
9855
+ if (!t.isDisabled()) {
9856
+ // restore the selection in case the selection is lost in IE
9857
+ if (tinymce.isIE && t.editor && bookmark) {
9858
+ tinymce.activeEditor.selection.moveToBookmark(bookmark);
9859
+ }
9860
+ return s.onclick.call(s.scope, e);
9861
+ }
9862
+ });
9863
+ tinymce.dom.Event.add(t.id, 'keyup', function(e) {
9864
+ if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR)
9798
9865
  return s.onclick.call(s.scope, e);
9799
9866
  });
9800
9867
  }
@@ -10501,15 +10568,21 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
10501
10568
  }
10502
10569
 
10503
10570
  n = DOM.add(tr, 'td');
10504
- n = DOM.add(n, 'a', {
10505
- role : 'option',
10571
+ var settings = {
10506
10572
  href : 'javascript:;',
10507
10573
  style : {
10508
10574
  backgroundColor : '#' + c
10509
10575
  },
10510
10576
  'title': t.editor.getLang('colors.' + c, c),
10511
10577
  'data-mce-color' : '#' + c
10512
- });
10578
+ };
10579
+
10580
+ // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.
10581
+ if (!tinymce.isIE ) {
10582
+ settings['role']= 'option';
10583
+ }
10584
+
10585
+ n = DOM.add(n, 'a', settings);
10513
10586
 
10514
10587
  if (t.editor.forcedHighContrastMode) {
10515
10588
  n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });
@@ -11145,7 +11218,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11145
11218
  Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,
11146
11219
  isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
11147
11220
  ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
11148
- inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
11221
+ inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode, VK = tinymce.VK;
11149
11222
 
11150
11223
  tinymce.create('tinymce.Editor', {
11151
11224
  Editor : function(id, s) {
@@ -11169,6 +11242,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11169
11242
 
11170
11243
  'onPostRender',
11171
11244
 
11245
+ 'onLoad',
11246
+
11172
11247
  'onInit',
11173
11248
 
11174
11249
  'onRemove',
@@ -11231,7 +11306,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11231
11306
 
11232
11307
  'onVisualAid',
11233
11308
 
11234
- 'onSetProgressState'
11309
+ 'onSetProgressState',
11310
+
11311
+ 'onSetAttrib'
11235
11312
  ], function(e) {
11236
11313
  t[e] = new Dispatcher(t);
11237
11314
  });
@@ -11583,7 +11660,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11583
11660
  bc = bc[t.id] || '';
11584
11661
  }
11585
11662
 
11586
- t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';
11663
+ t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';
11587
11664
 
11588
11665
  // Domain relaxing enabled, then set document domain
11589
11666
  if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
@@ -12051,7 +12128,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12051
12128
  }
12052
12129
 
12053
12130
  t._refreshContentEditable();
12054
- selection.normalize();
12055
12131
 
12056
12132
  // Is not content editable
12057
12133
  if (!ce)
@@ -12823,30 +12899,32 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12823
12899
 
12824
12900
  // Add block quote deletion handler
12825
12901
  t.onKeyDown.add(function(ed, e) {
12826
- // Was the BACKSPACE key pressed?
12827
- if (e.keyCode != 8)
12902
+ if (e.keyCode != VK.BACKSPACE)
12903
+ return;
12904
+
12905
+ var rng = ed.selection.getRng();
12906
+ if (!rng.collapsed)
12828
12907
  return;
12829
12908
 
12830
- var n = ed.selection.getRng().startContainer;
12831
- var offset = ed.selection.getRng().startOffset;
12909
+ var n = rng.startContainer;
12910
+ var offset = rng.startOffset;
12832
12911
 
12833
12912
  while (n && n.nodeType && n.nodeType != 1 && n.parentNode)
12834
12913
  n = n.parentNode;
12835
-
12914
+
12836
12915
  // Is the cursor at the beginning of a blockquote?
12837
12916
  if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {
12838
12917
  // Remove the blockquote
12839
12918
  ed.formatter.toggle('blockquote', null, n.parentNode);
12840
12919
 
12841
12920
  // Move the caret to the beginning of n
12842
- var rng = ed.selection.getRng();
12843
12921
  rng.setStart(n, 0);
12844
12922
  rng.setEnd(n, 0);
12845
12923
  ed.selection.setRng(rng);
12846
12924
  ed.selection.collapse(false);
12847
12925
  }
12848
12926
  });
12849
-
12927
+
12850
12928
 
12851
12929
 
12852
12930
  // Add reset handler
@@ -13054,9 +13132,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13054
13132
  if (target !== t.getBody()) {
13055
13133
  t.dom.setAttrib(target, "style", null);
13056
13134
 
13057
- each(template, function(attr) {
13058
- target.setAttributeNode(attr.cloneNode(true));
13059
- });
13135
+ each(template, function(attr) {
13136
+ target.setAttributeNode(attr.cloneNode(true));
13137
+ });
13060
13138
  }
13061
13139
  };
13062
13140
  }
@@ -13368,6 +13446,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13368
13446
  var parser, serializer, parentNode, rootNode, fragment, args,
13369
13447
  marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;
13370
13448
 
13449
+ //selection.normalize();
13450
+
13371
13451
  // Setup parser and serializer
13372
13452
  parser = editor.parser;
13373
13453
  serializer = new tinymce.html.Serializer({}, editor.schema);
@@ -13594,7 +13674,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13594
13674
  addCommands({
13595
13675
  // Override justify commands
13596
13676
  'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
13597
- return isFormatMatch('align' + command.substring(7));
13677
+ var name = 'align' + command.substring(7);
13678
+ // Use Formatter.matchNode instead of Formatter.match so that we don't match on parent node. This fixes bug where for both left
13679
+ // and right align buttons can be active. This could occur when selected nodes have align right and the parent has align left.
13680
+ var nodes = selection.isCollapsed() ? [selection.getNode()] : selection.getSelectedBlocks();
13681
+ var matches = tinymce.map(nodes, function(node) {
13682
+ return !!formatter.matchNode(node, name);
13683
+ });
13684
+ return tinymce.inArray(matches, TRUE) !== -1;
13598
13685
  },
13599
13686
 
13600
13687
  'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {
@@ -14991,30 +15078,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14991
15078
  function apply(name, vars, node) {
14992
15079
  var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();
14993
15080
 
14994
- function moveStart(rng) {
14995
- var container = rng.startContainer,
14996
- offset = rng.startOffset,
14997
- walker, node;
14998
-
14999
- // Move startContainer/startOffset in to a suitable node
15000
- if (container.nodeType == 1 || container.nodeValue === "") {
15001
- container = container.nodeType == 1 ? container.childNodes[offset] : container;
15002
-
15003
- // Might fail if the offset is behind the last element in it's container
15004
- if (container) {
15005
- walker = new TreeWalker(container, container.parentNode);
15006
- for (node = walker.current(); node; node = walker.next()) {
15007
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
15008
- rng.setStart(node, 0);
15009
- break;
15010
- }
15011
- }
15012
- }
15013
- }
15014
-
15015
- return rng;
15016
- };
15017
-
15018
15081
  function setElementFormat(elm, fmt) {
15019
15082
  fmt = fmt || format;
15020
15083
 
@@ -15365,7 +15428,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15365
15428
  }
15366
15429
 
15367
15430
  selection.moveToBookmark(bookmark);
15368
- selection.setRng(moveStart(selection.getRng(TRUE)));
15431
+ moveStart(selection.getRng(TRUE));
15369
15432
  ed.nodeChanged();
15370
15433
  } else
15371
15434
  performCaretAction('apply', name, vars);
@@ -15375,44 +15438,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15375
15438
 
15376
15439
  function remove(name, vars, node) {
15377
15440
  var formatList = get(name), format = formatList[0], bookmark, i, rng;
15378
- function moveStart(rng) {
15379
- var container = rng.startContainer,
15380
- offset = rng.startOffset,
15381
- walker, node, nodes, tmpNode;
15382
-
15383
- // Convert text node into index if possible
15384
- if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {
15385
- container = container.parentNode;
15386
- offset = nodeIndex(container) + 1;
15387
- }
15388
-
15389
- // Move startContainer/startOffset in to a suitable node
15390
- if (container.nodeType == 1) {
15391
- nodes = container.childNodes;
15392
- container = nodes[Math.min(offset, nodes.length - 1)];
15393
- walker = new TreeWalker(container);
15394
-
15395
- // If offset is at end of the parent node walk to the next one
15396
- if (offset > nodes.length - 1)
15397
- walker.next();
15398
-
15399
- for (node = walker.current(); node; node = walker.next()) {
15400
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
15401
- // IE has a "neat" feature where it moves the start node into the closest element
15402
- // we can avoid this by inserting an element before it and then remove it after we set the selection
15403
- tmpNode = dom.create('a', null, INVISIBLE_CHAR);
15404
- node.parentNode.insertBefore(tmpNode, node);
15405
-
15406
- // Set selection and remove tmpNode
15407
- rng.setStart(node, 0);
15408
- selection.setRng(rng);
15409
- dom.remove(tmpNode);
15410
-
15411
- return;
15412
- }
15413
- }
15414
- }
15415
- };
15416
15441
 
15417
15442
  // Merges the styles for each node
15418
15443
  function process(node) {
@@ -16500,7 +16525,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
16500
16525
  dom.remove(node);
16501
16526
  } else {
16502
16527
  child = findFirstTextNode(node);
16503
- child = child.deleteData(0, 1);
16528
+
16529
+ if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
16530
+ child = child.deleteData(0, 1);
16531
+ }
16532
+
16504
16533
  dom.remove(node, 1);
16505
16534
  }
16506
16535
 
@@ -16630,34 +16659,39 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
16630
16659
  }
16631
16660
  };
16632
16661
 
16633
- // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
16634
- ed.onBeforeGetContent.addToTop(function() {
16635
- var nodes = [], i;
16662
+ // Only bind the caret events once
16663
+ if (!self._hasCaretEvents) {
16664
+ // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
16665
+ ed.onBeforeGetContent.addToTop(function() {
16666
+ var nodes = [], i;
16636
16667
 
16637
- if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
16638
- // Mark children
16639
- i = nodes.length;
16640
- while (i--) {
16641
- dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
16668
+ if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
16669
+ // Mark children
16670
+ i = nodes.length;
16671
+ while (i--) {
16672
+ dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
16673
+ }
16642
16674
  }
16643
- }
16644
- });
16675
+ });
16645
16676
 
16646
- // Remove caret container on mouse up and on key up
16647
- tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
16648
- ed[name].addToTop(function() {
16649
- removeCaretContainer();
16677
+ // Remove caret container on mouse up and on key up
16678
+ tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
16679
+ ed[name].addToTop(function() {
16680
+ removeCaretContainer();
16681
+ });
16650
16682
  });
16651
- });
16652
16683
 
16653
- // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
16654
- ed.onKeyDown.addToTop(function(ed, e) {
16655
- var keyCode = e.keyCode;
16684
+ // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
16685
+ ed.onKeyDown.addToTop(function(ed, e) {
16686
+ var keyCode = e.keyCode;
16656
16687
 
16657
- if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
16658
- removeCaretContainer(getParentCaretContainer(selection.getStart()));
16659
- }
16660
- });
16688
+ if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
16689
+ removeCaretContainer(getParentCaretContainer(selection.getStart()));
16690
+ }
16691
+ });
16692
+
16693
+ self._hasCaretEvents = true;
16694
+ }
16661
16695
 
16662
16696
  // Do apply or remove caret format
16663
16697
  if (type == "apply") {
@@ -16666,6 +16700,46 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
16666
16700
  removeCaretFormat();
16667
16701
  }
16668
16702
  };
16703
+
16704
+ function moveStart(rng) {
16705
+ var container = rng.startContainer,
16706
+ offset = rng.startOffset,
16707
+ walker, node, nodes, tmpNode;
16708
+
16709
+ // Convert text node into index if possible
16710
+ if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {
16711
+ container = container.parentNode;
16712
+ offset = nodeIndex(container) + 1;
16713
+ }
16714
+
16715
+ // Move startContainer/startOffset in to a suitable node
16716
+ if (container.nodeType == 1) {
16717
+ nodes = container.childNodes;
16718
+ container = nodes[Math.min(offset, nodes.length - 1)];
16719
+ walker = new TreeWalker(container);
16720
+
16721
+ // If offset is at end of the parent node walk to the next one
16722
+ if (offset > nodes.length - 1)
16723
+ walker.next();
16724
+
16725
+ for (node = walker.current(); node; node = walker.next()) {
16726
+ if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
16727
+ // IE has a "neat" feature where it moves the start node into the closest element
16728
+ // we can avoid this by inserting an element before it and then remove it after we set the selection
16729
+ tmpNode = dom.create('a', null, INVISIBLE_CHAR);
16730
+ node.parentNode.insertBefore(tmpNode, node);
16731
+
16732
+ // Set selection and remove tmpNode
16733
+ rng.setStart(node, 0);
16734
+ selection.setRng(rng);
16735
+ dom.remove(tmpNode);
16736
+
16737
+ return;
16738
+ }
16739
+ }
16740
+ }
16741
+ };
16742
+
16669
16743
  };
16670
16744
  })(tinymce);
16671
16745