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;
@@ -810,7 +810,7 @@ tinymce.create('tinymce.util.Dispatcher', {
810
810
  // And this is also more efficient
811
811
  for (i = 0; i<li.length; i++) {
812
812
  c = li[i];
813
- s = c.cb.apply(c.scope, a);
813
+ s = c.cb.apply(c.scope, a.length > 0 ? a : [c.scope]);
814
814
 
815
815
  if (s === false)
816
816
  break;
@@ -853,7 +853,7 @@ tinymce.create('tinymce.util.Dispatcher', {
853
853
 
854
854
  // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
855
855
  u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
856
- u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
856
+ u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
857
857
  each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
858
858
  var s = u[i];
859
859
 
@@ -1323,7 +1323,10 @@ tinymce.create('static tinymce.util.XHR', {
1323
1323
  TAB: 9,
1324
1324
  SPACEBAR: 32,
1325
1325
  UP: 38,
1326
- DOWN: 40
1326
+ DOWN: 40,
1327
+ modifierPressed: function (e) {
1328
+ return e.shiftKey || e.ctrlKey || e.altKey;
1329
+ }
1327
1330
  }
1328
1331
  })(tinymce);
1329
1332
 
@@ -1337,7 +1340,7 @@ tinymce.create('static tinymce.util.XHR', {
1337
1340
  var rng, blockElm, node, clonedSpan, isDelete;
1338
1341
 
1339
1342
  isDelete = e.keyCode == DELETE;
1340
- if (isDelete || e.keyCode == BACKSPACE) {
1343
+ if ((isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
1341
1344
  e.preventDefault();
1342
1345
  rng = selection.getRng();
1343
1346
 
@@ -1454,6 +1457,26 @@ tinymce.create('static tinymce.util.XHR', {
1454
1457
  });
1455
1458
  };
1456
1459
 
1460
+ function removeStylesOnPTagsInheritedFromHeadingTag(ed) {
1461
+ ed.onKeyDown.add(function(ed, event) {
1462
+ function checkInHeadingTag(ed) {
1463
+ var currentNode = ed.selection.getNode();
1464
+ var headingTags = 'h1,h2,h3,h4,h5,h6';
1465
+ return ed.dom.is(currentNode, headingTags) || ed.dom.getParent(currentNode, headingTags) !== null;
1466
+ }
1467
+
1468
+ if (event.keyCode === VK.ENTER && !VK.modifierPressed(event) && checkInHeadingTag(ed)) {
1469
+ setTimeout(function() {
1470
+ var currentNode = ed.selection.getNode();
1471
+ if (ed.dom.is(currentNode, 'p')) {
1472
+ ed.dom.setAttrib(currentNode, 'style', null);
1473
+ // While tiny's content is correct after this method call, the content shown is not representative of it and needs to be 'repainted'
1474
+ ed.execCommand('mceCleanup');
1475
+ }
1476
+ }, 0);
1477
+ }
1478
+ });
1479
+ }
1457
1480
  function selectionChangeNodeChanged(ed) {
1458
1481
  var lastRng, selectionTimer;
1459
1482
 
@@ -1478,7 +1501,7 @@ tinymce.create('static tinymce.util.XHR', {
1478
1501
  function ensureBodyHasRoleApplication(ed) {
1479
1502
  document.body.setAttribute("role", "application");
1480
1503
  }
1481
-
1504
+
1482
1505
  tinymce.create('tinymce.util.Quirks', {
1483
1506
  Quirks: function(ed) {
1484
1507
  // WebKit
@@ -1499,6 +1522,7 @@ tinymce.create('static tinymce.util.XHR', {
1499
1522
  removeHrOnBackspace(ed);
1500
1523
  emptyEditorWhenDeleting(ed);
1501
1524
  ensureBodyHasRoleApplication(ed);
1525
+ removeStylesOnPTagsInheritedFromHeadingTag(ed)
1502
1526
  }
1503
1527
 
1504
1528
  // Gecko
@@ -4171,6 +4195,7 @@ tinymce.html.Writer = function(settings) {
4171
4195
 
4172
4196
  return this.run(e, function(e) {
4173
4197
  var s = t.settings;
4198
+ var originalValue = e.getAttribute(n);
4174
4199
  if (v !== null) {
4175
4200
  switch (n) {
4176
4201
  case "style":
@@ -4217,6 +4242,12 @@ tinymce.html.Writer = function(settings) {
4217
4242
  e.setAttribute(n, '' + v, 2);
4218
4243
  else
4219
4244
  e.removeAttribute(n, 2);
4245
+
4246
+ // fire onChangeAttrib event for attributes that have changed
4247
+ if (tinyMCE.activeEditor && originalValue != v) {
4248
+ var ed = tinyMCE.activeEditor;
4249
+ ed.onSetAttrib.dispatch(ed, e, n, v);
4250
+ }
4220
4251
  });
4221
4252
  },
4222
4253
 
@@ -4922,6 +4953,12 @@ tinymce.html.Writer = function(settings) {
4922
4953
  function trim(node) {
4923
4954
  var i, children = node.childNodes, type = node.nodeType;
4924
4955
 
4956
+ function surroundedBySpans(node) {
4957
+ var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
4958
+ var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
4959
+ return previousIsSpan && nextIsSpan;
4960
+ }
4961
+
4925
4962
  if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')
4926
4963
  return;
4927
4964
 
@@ -4932,7 +4969,10 @@ tinymce.html.Writer = function(settings) {
4932
4969
  // Keep non whitespace text nodes
4933
4970
  if (type == 3 && node.nodeValue.length > 0) {
4934
4971
  // If parent element isn't a block or there isn't any useful contents for example "<p> </p>"
4935
- if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)
4972
+ // Also keep text nodes with only spaces if surrounded by spans.
4973
+ // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
4974
+ var trimmedLength = tinymce.trim(node.nodeValue).length;
4975
+ if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength == 0 && surroundedBySpans(node))
4936
4976
  return;
4937
4977
  } else if (type == 1) {
4938
4978
  // If the only child is a bookmark then move it up
@@ -4969,9 +5009,9 @@ tinymce.html.Writer = function(settings) {
4969
5009
 
4970
5010
  // Insert middle chunk
4971
5011
  if (re)
4972
- pa.replaceChild(re, e);
4973
- else
4974
- pa.insertBefore(e, pe);
5012
+ pa.replaceChild(re, e);
5013
+ else
5014
+ pa.insertBefore(e, pe);
4975
5015
 
4976
5016
  // Insert after chunk
4977
5017
  pa.insertBefore(trim(aft), pe);
@@ -7201,7 +7241,8 @@ tinymce.html.Writer = function(settings) {
7201
7241
  }
7202
7242
 
7203
7243
  s.addRange(r);
7204
- t.selectedRange = s.getRangeAt(0);
7244
+ // adding range isn't always successful so we need to check range count otherwise an exception can occur
7245
+ t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;
7205
7246
  }
7206
7247
  } else {
7207
7248
  // Is W3C Range
@@ -7313,6 +7354,11 @@ tinymce.html.Writer = function(settings) {
7313
7354
  normalize : function() {
7314
7355
  var self = this, rng, normalized;
7315
7356
 
7357
+ // TODO:
7358
+ // Retain selection direction.
7359
+ // Lean left/right on Gecko for inline elements.
7360
+ // Run this on mouse up/key up when the user manually moves the selection
7361
+
7316
7362
  // Normalize only on non IE browsers for now
7317
7363
  if (tinymce.isIE)
7318
7364
  return;
@@ -7347,18 +7393,24 @@ tinymce.html.Writer = function(settings) {
7347
7393
  if (node.nodeType === 3) {
7348
7394
  offset = start ? 0 : node.nodeValue.length - 1;
7349
7395
  container = node;
7396
+ normalized = true;
7350
7397
  break;
7351
7398
  }
7352
7399
 
7353
- // Found a BR element that we can place the caret before
7354
- if (node.nodeName === 'BR') {
7400
+ // Found a BR/IMG element that we can place the caret before
7401
+ if (/^(BR|IMG)$/.test(node.nodeName)) {
7355
7402
  offset = dom.nodeIndex(node);
7356
7403
  container = node.parentNode;
7404
+
7405
+ // Put caret after image when moving the end point
7406
+ if (node.nodeName == "IMG" && !start) {
7407
+ offset++;
7408
+ }
7409
+
7410
+ normalized = true;
7357
7411
  break;
7358
7412
  }
7359
7413
  } while (node = (start ? walker.next() : walker.prev()));
7360
-
7361
- normalized = true;
7362
7414
  }
7363
7415
  }
7364
7416
  }
@@ -7373,7 +7425,7 @@ tinymce.html.Writer = function(settings) {
7373
7425
  // Normalize the end points
7374
7426
  normalizeEndPoint(true);
7375
7427
 
7376
- if (rng.collapsed)
7428
+ if (!rng.collapsed)
7377
7429
  normalizeEndPoint();
7378
7430
 
7379
7431
  // Set the selection if it was normalized
@@ -7485,12 +7537,11 @@ tinymce.html.Writer = function(settings) {
7485
7537
  if (!settings.apply_source_formatting)
7486
7538
  settings.indent = false;
7487
7539
 
7488
- settings.remove_trailing_brs = true;
7489
-
7490
7540
  // Default DOM and Schema if they are undefined
7491
7541
  dom = dom || tinymce.DOM;
7492
7542
  schema = schema || new tinymce.html.Schema(settings);
7493
7543
  settings.entity_encoding = settings.entity_encoding || 'named';
7544
+ settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
7494
7545
 
7495
7546
  onPreProcess = new tinymce.util.Dispatcher(self);
7496
7547
 
@@ -7554,8 +7605,8 @@ tinymce.html.Writer = function(settings) {
7554
7605
  function trim(value) {
7555
7606
  return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
7556
7607
  .replace(/^[\r\n]*|[\r\n]*$/g, '')
7557
- .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')
7558
- .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
7608
+ .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
7609
+ .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
7559
7610
  };
7560
7611
 
7561
7612
  while (i--) {
@@ -8856,7 +8907,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
8856
8907
  // Internal functions
8857
8908
  _setupKeyboardNav : function(){
8858
8909
  var contextMenu, menuItems, t=this;
8859
- contextMenu = DOM.select('#menu_' + t.id)[0];
8910
+ contextMenu = DOM.get('menu_' + t.id);
8860
8911
  menuItems = DOM.select('a[role=option]', 'menu_' + t.id);
8861
8912
  menuItems.splice(0,0,contextMenu);
8862
8913
  t.keyboardNav = new tinymce.ui.KeyboardNavigation({
@@ -8963,10 +9014,26 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
8963
9014
  },
8964
9015
 
8965
9016
  postRender : function() {
8966
- var t = this, s = t.settings;
9017
+ var t = this, s = t.settings, bookmark;
8967
9018
 
9019
+ // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so
9020
+ // need to keep the selection in case the selection is lost
9021
+ if (tinymce.isIE && t.editor) {
9022
+ tinymce.dom.Event.add(t.id, 'mousedown', function(e) {
9023
+ bookmark = t.editor.selection.getBookmark();
9024
+ });
9025
+ }
8968
9026
  tinymce.dom.Event.add(t.id, 'click', function(e) {
8969
- if (!t.isDisabled())
9027
+ if (!t.isDisabled()) {
9028
+ // restore the selection in case the selection is lost in IE
9029
+ if (tinymce.isIE && t.editor && bookmark) {
9030
+ tinymce.activeEditor.selection.moveToBookmark(bookmark);
9031
+ }
9032
+ return s.onclick.call(s.scope, e);
9033
+ }
9034
+ });
9035
+ tinymce.dom.Event.add(t.id, 'keyup', function(e) {
9036
+ if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR)
8970
9037
  return s.onclick.call(s.scope, e);
8971
9038
  });
8972
9039
  }
@@ -9673,15 +9740,21 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9673
9740
  }
9674
9741
 
9675
9742
  n = DOM.add(tr, 'td');
9676
- n = DOM.add(n, 'a', {
9677
- role : 'option',
9743
+ var settings = {
9678
9744
  href : 'javascript:;',
9679
9745
  style : {
9680
9746
  backgroundColor : '#' + c
9681
9747
  },
9682
9748
  'title': t.editor.getLang('colors.' + c, c),
9683
9749
  'data-mce-color' : '#' + c
9684
- });
9750
+ };
9751
+
9752
+ // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.
9753
+ if (!tinymce.isIE ) {
9754
+ settings['role']= 'option';
9755
+ }
9756
+
9757
+ n = DOM.add(n, 'a', settings);
9685
9758
 
9686
9759
  if (t.editor.forcedHighContrastMode) {
9687
9760
  n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });
@@ -10322,7 +10395,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10322
10395
  Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,
10323
10396
  isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
10324
10397
  ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
10325
- inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
10398
+ inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode, VK = tinymce.VK;
10326
10399
 
10327
10400
  tinymce.create('tinymce.Editor', {
10328
10401
  Editor : function(id, s) {
@@ -10346,6 +10419,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10346
10419
 
10347
10420
  'onPostRender',
10348
10421
 
10422
+ 'onLoad',
10423
+
10349
10424
  'onInit',
10350
10425
 
10351
10426
  'onRemove',
@@ -10408,7 +10483,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10408
10483
 
10409
10484
  'onVisualAid',
10410
10485
 
10411
- 'onSetProgressState'
10486
+ 'onSetProgressState',
10487
+
10488
+ 'onSetAttrib'
10412
10489
  ], function(e) {
10413
10490
  t[e] = new Dispatcher(t);
10414
10491
  });
@@ -10760,7 +10837,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10760
10837
  bc = bc[t.id] || '';
10761
10838
  }
10762
10839
 
10763
- t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';
10840
+ t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';
10764
10841
 
10765
10842
  // Domain relaxing enabled, then set document domain
10766
10843
  if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
@@ -11228,7 +11305,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11228
11305
  }
11229
11306
 
11230
11307
  t._refreshContentEditable();
11231
- selection.normalize();
11232
11308
 
11233
11309
  // Is not content editable
11234
11310
  if (!ce)
@@ -12000,30 +12076,32 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12000
12076
 
12001
12077
  // Add block quote deletion handler
12002
12078
  t.onKeyDown.add(function(ed, e) {
12003
- // Was the BACKSPACE key pressed?
12004
- if (e.keyCode != 8)
12079
+ if (e.keyCode != VK.BACKSPACE)
12080
+ return;
12081
+
12082
+ var rng = ed.selection.getRng();
12083
+ if (!rng.collapsed)
12005
12084
  return;
12006
12085
 
12007
- var n = ed.selection.getRng().startContainer;
12008
- var offset = ed.selection.getRng().startOffset;
12086
+ var n = rng.startContainer;
12087
+ var offset = rng.startOffset;
12009
12088
 
12010
12089
  while (n && n.nodeType && n.nodeType != 1 && n.parentNode)
12011
12090
  n = n.parentNode;
12012
-
12091
+
12013
12092
  // Is the cursor at the beginning of a blockquote?
12014
12093
  if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {
12015
12094
  // Remove the blockquote
12016
12095
  ed.formatter.toggle('blockquote', null, n.parentNode);
12017
12096
 
12018
12097
  // Move the caret to the beginning of n
12019
- var rng = ed.selection.getRng();
12020
12098
  rng.setStart(n, 0);
12021
12099
  rng.setEnd(n, 0);
12022
12100
  ed.selection.setRng(rng);
12023
12101
  ed.selection.collapse(false);
12024
12102
  }
12025
12103
  });
12026
-
12104
+
12027
12105
 
12028
12106
 
12029
12107
  // Add reset handler
@@ -12231,9 +12309,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12231
12309
  if (target !== t.getBody()) {
12232
12310
  t.dom.setAttrib(target, "style", null);
12233
12311
 
12234
- each(template, function(attr) {
12235
- target.setAttributeNode(attr.cloneNode(true));
12236
- });
12312
+ each(template, function(attr) {
12313
+ target.setAttributeNode(attr.cloneNode(true));
12314
+ });
12237
12315
  }
12238
12316
  };
12239
12317
  }
@@ -12545,6 +12623,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12545
12623
  var parser, serializer, parentNode, rootNode, fragment, args,
12546
12624
  marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;
12547
12625
 
12626
+ //selection.normalize();
12627
+
12548
12628
  // Setup parser and serializer
12549
12629
  parser = editor.parser;
12550
12630
  serializer = new tinymce.html.Serializer({}, editor.schema);
@@ -12771,7 +12851,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12771
12851
  addCommands({
12772
12852
  // Override justify commands
12773
12853
  'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
12774
- return isFormatMatch('align' + command.substring(7));
12854
+ var name = 'align' + command.substring(7);
12855
+ // Use Formatter.matchNode instead of Formatter.match so that we don't match on parent node. This fixes bug where for both left
12856
+ // and right align buttons can be active. This could occur when selected nodes have align right and the parent has align left.
12857
+ var nodes = selection.isCollapsed() ? [selection.getNode()] : selection.getSelectedBlocks();
12858
+ var matches = tinymce.map(nodes, function(node) {
12859
+ return !!formatter.matchNode(node, name);
12860
+ });
12861
+ return tinymce.inArray(matches, TRUE) !== -1;
12775
12862
  },
12776
12863
 
12777
12864
  'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {
@@ -14168,30 +14255,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14168
14255
  function apply(name, vars, node) {
14169
14256
  var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();
14170
14257
 
14171
- function moveStart(rng) {
14172
- var container = rng.startContainer,
14173
- offset = rng.startOffset,
14174
- walker, node;
14175
-
14176
- // Move startContainer/startOffset in to a suitable node
14177
- if (container.nodeType == 1 || container.nodeValue === "") {
14178
- container = container.nodeType == 1 ? container.childNodes[offset] : container;
14179
-
14180
- // Might fail if the offset is behind the last element in it's container
14181
- if (container) {
14182
- walker = new TreeWalker(container, container.parentNode);
14183
- for (node = walker.current(); node; node = walker.next()) {
14184
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
14185
- rng.setStart(node, 0);
14186
- break;
14187
- }
14188
- }
14189
- }
14190
- }
14191
-
14192
- return rng;
14193
- };
14194
-
14195
14258
  function setElementFormat(elm, fmt) {
14196
14259
  fmt = fmt || format;
14197
14260
 
@@ -14542,7 +14605,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14542
14605
  }
14543
14606
 
14544
14607
  selection.moveToBookmark(bookmark);
14545
- selection.setRng(moveStart(selection.getRng(TRUE)));
14608
+ moveStart(selection.getRng(TRUE));
14546
14609
  ed.nodeChanged();
14547
14610
  } else
14548
14611
  performCaretAction('apply', name, vars);
@@ -14552,44 +14615,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14552
14615
 
14553
14616
  function remove(name, vars, node) {
14554
14617
  var formatList = get(name), format = formatList[0], bookmark, i, rng;
14555
- function moveStart(rng) {
14556
- var container = rng.startContainer,
14557
- offset = rng.startOffset,
14558
- walker, node, nodes, tmpNode;
14559
-
14560
- // Convert text node into index if possible
14561
- if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {
14562
- container = container.parentNode;
14563
- offset = nodeIndex(container) + 1;
14564
- }
14565
-
14566
- // Move startContainer/startOffset in to a suitable node
14567
- if (container.nodeType == 1) {
14568
- nodes = container.childNodes;
14569
- container = nodes[Math.min(offset, nodes.length - 1)];
14570
- walker = new TreeWalker(container);
14571
-
14572
- // If offset is at end of the parent node walk to the next one
14573
- if (offset > nodes.length - 1)
14574
- walker.next();
14575
-
14576
- for (node = walker.current(); node; node = walker.next()) {
14577
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
14578
- // IE has a "neat" feature where it moves the start node into the closest element
14579
- // we can avoid this by inserting an element before it and then remove it after we set the selection
14580
- tmpNode = dom.create('a', null, INVISIBLE_CHAR);
14581
- node.parentNode.insertBefore(tmpNode, node);
14582
-
14583
- // Set selection and remove tmpNode
14584
- rng.setStart(node, 0);
14585
- selection.setRng(rng);
14586
- dom.remove(tmpNode);
14587
-
14588
- return;
14589
- }
14590
- }
14591
- }
14592
- };
14593
14618
 
14594
14619
  // Merges the styles for each node
14595
14620
  function process(node) {
@@ -15677,7 +15702,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15677
15702
  dom.remove(node);
15678
15703
  } else {
15679
15704
  child = findFirstTextNode(node);
15680
- child = child.deleteData(0, 1);
15705
+
15706
+ if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
15707
+ child = child.deleteData(0, 1);
15708
+ }
15709
+
15681
15710
  dom.remove(node, 1);
15682
15711
  }
15683
15712
 
@@ -15807,34 +15836,39 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15807
15836
  }
15808
15837
  };
15809
15838
 
15810
- // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
15811
- ed.onBeforeGetContent.addToTop(function() {
15812
- var nodes = [], i;
15839
+ // Only bind the caret events once
15840
+ if (!self._hasCaretEvents) {
15841
+ // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
15842
+ ed.onBeforeGetContent.addToTop(function() {
15843
+ var nodes = [], i;
15813
15844
 
15814
- if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
15815
- // Mark children
15816
- i = nodes.length;
15817
- while (i--) {
15818
- dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
15845
+ if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
15846
+ // Mark children
15847
+ i = nodes.length;
15848
+ while (i--) {
15849
+ dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
15850
+ }
15819
15851
  }
15820
- }
15821
- });
15852
+ });
15822
15853
 
15823
- // Remove caret container on mouse up and on key up
15824
- tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
15825
- ed[name].addToTop(function() {
15826
- removeCaretContainer();
15854
+ // Remove caret container on mouse up and on key up
15855
+ tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
15856
+ ed[name].addToTop(function() {
15857
+ removeCaretContainer();
15858
+ });
15827
15859
  });
15828
- });
15829
15860
 
15830
- // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
15831
- ed.onKeyDown.addToTop(function(ed, e) {
15832
- var keyCode = e.keyCode;
15861
+ // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
15862
+ ed.onKeyDown.addToTop(function(ed, e) {
15863
+ var keyCode = e.keyCode;
15833
15864
 
15834
- if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
15835
- removeCaretContainer(getParentCaretContainer(selection.getStart()));
15836
- }
15837
- });
15865
+ if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
15866
+ removeCaretContainer(getParentCaretContainer(selection.getStart()));
15867
+ }
15868
+ });
15869
+
15870
+ self._hasCaretEvents = true;
15871
+ }
15838
15872
 
15839
15873
  // Do apply or remove caret format
15840
15874
  if (type == "apply") {
@@ -15843,6 +15877,46 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15843
15877
  removeCaretFormat();
15844
15878
  }
15845
15879
  };
15880
+
15881
+ function moveStart(rng) {
15882
+ var container = rng.startContainer,
15883
+ offset = rng.startOffset,
15884
+ walker, node, nodes, tmpNode;
15885
+
15886
+ // Convert text node into index if possible
15887
+ if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {
15888
+ container = container.parentNode;
15889
+ offset = nodeIndex(container) + 1;
15890
+ }
15891
+
15892
+ // Move startContainer/startOffset in to a suitable node
15893
+ if (container.nodeType == 1) {
15894
+ nodes = container.childNodes;
15895
+ container = nodes[Math.min(offset, nodes.length - 1)];
15896
+ walker = new TreeWalker(container);
15897
+
15898
+ // If offset is at end of the parent node walk to the next one
15899
+ if (offset > nodes.length - 1)
15900
+ walker.next();
15901
+
15902
+ for (node = walker.current(); node; node = walker.next()) {
15903
+ if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
15904
+ // IE has a "neat" feature where it moves the start node into the closest element
15905
+ // we can avoid this by inserting an element before it and then remove it after we set the selection
15906
+ tmpNode = dom.create('a', null, INVISIBLE_CHAR);
15907
+ node.parentNode.insertBefore(tmpNode, node);
15908
+
15909
+ // Set selection and remove tmpNode
15910
+ rng.setStart(node, 0);
15911
+ selection.setRng(rng);
15912
+ dom.remove(tmpNode);
15913
+
15914
+ return;
15915
+ }
15916
+ }
15917
+ }
15918
+ };
15919
+
15846
15920
  };
15847
15921
  })(tinymce);
15848
15922