tinymce-rails 4.0.11 → 4.0.12

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.0.11 (2013-11-20)
1
+ // 4.0.12 (2013-12-18)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -6227,7 +6227,7 @@ define("tinymce/html/Schema", [
6227
6227
  'meta param embed source wbr track');
6228
6228
  boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
6229
6229
  'noshade nowrap readonly selected autoplay loop controls');
6230
- nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);
6230
+ nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
6231
6231
  textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
6232
6232
  'blockquote center dir fieldset header footer article section hgroup aside nav figure');
6233
6233
  blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
@@ -10808,6 +10808,7 @@ define("tinymce/dom/Selection", [
10808
10808
 
10809
10809
  function normalizeEndPoint(start) {
10810
10810
  var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, nodeName;
10811
+ var directionLeft;
10811
10812
 
10812
10813
  function hasBrBeforeAfter(node, left) {
10813
10814
  var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
@@ -10860,6 +10861,11 @@ define("tinymce/dom/Selection", [
10860
10861
  container = rng[(start ? 'start' : 'end') + 'Container'];
10861
10862
  offset = rng[(start ? 'start' : 'end') + 'Offset'];
10862
10863
  nonEmptyElementsMap = dom.schema.getNonEmptyElements();
10864
+ directionLeft = start;
10865
+
10866
+ if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
10867
+ directionLeft = false;
10868
+ }
10863
10869
 
10864
10870
  // If the container is a document move it to the body element
10865
10871
  if (container.nodeType === 9) {
@@ -10870,7 +10876,7 @@ define("tinymce/dom/Selection", [
10870
10876
  // If the container is body try move it into the closest text node or position
10871
10877
  if (container === body) {
10872
10878
  // If start is before/after a image, table etc
10873
- if (start) {
10879
+ if (directionLeft) {
10874
10880
  node = container.childNodes[offset > 0 ? offset - 1 : 0];
10875
10881
  if (node) {
10876
10882
  nodeName = node.nodeName.toLowerCase();
@@ -10882,7 +10888,7 @@ define("tinymce/dom/Selection", [
10882
10888
 
10883
10889
  // Resolve the index
10884
10890
  if (container.hasChildNodes()) {
10885
- offset = Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
10891
+ offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
10886
10892
  container = container.childNodes[offset];
10887
10893
  offset = 0;
10888
10894
 
@@ -10895,7 +10901,7 @@ define("tinymce/dom/Selection", [
10895
10901
  do {
10896
10902
  // Found a text node use that position
10897
10903
  if (node.nodeType === 3 && node.nodeValue.length > 0) {
10898
- offset = start ? 0 : node.nodeValue.length;
10904
+ offset = directionLeft ? 0 : node.nodeValue.length;
10899
10905
  container = node;
10900
10906
  normalized = true;
10901
10907
  break;
@@ -10907,14 +10913,14 @@ define("tinymce/dom/Selection", [
10907
10913
  container = node.parentNode;
10908
10914
 
10909
10915
  // Put caret after image when moving the end point
10910
- if (node.nodeName == "IMG" && !start) {
10916
+ if (node.nodeName == "IMG" && !directionLeft) {
10911
10917
  offset++;
10912
10918
  }
10913
10919
 
10914
10920
  normalized = true;
10915
10921
  break;
10916
10922
  }
10917
- } while ((node = (start ? walker.next() : walker.prev())));
10923
+ } while ((node = (directionLeft ? walker.next() : walker.prev())));
10918
10924
  }
10919
10925
  }
10920
10926
  }
@@ -10945,7 +10951,7 @@ define("tinymce/dom/Selection", [
10945
10951
  // Lean the start of the selection right if possible
10946
10952
  // So this: x[<b>x]</b>
10947
10953
  // Becomes: x<b>[x]</b>
10948
- if (start && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
10954
+ if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
10949
10955
  findTextNodeRelative(false);
10950
10956
  }
10951
10957
 
@@ -12464,6 +12470,10 @@ define("tinymce/Formatter", [
12464
12470
  function matchParents(node) {
12465
12471
  var root = dom.getRoot();
12466
12472
 
12473
+ if (node === root) {
12474
+ return false;
12475
+ }
12476
+
12467
12477
  // Find first node with similar format settings
12468
12478
  node = dom.getParent(node, function(node) {
12469
12479
  return node.parentNode === root || !!matchNode(node, name, vars, true);
@@ -12978,7 +12988,7 @@ define("tinymce/Formatter", [
12978
12988
 
12979
12989
  // Expand to block of similar type
12980
12990
  if (!format[0].wrapper) {
12981
- node = dom.getParent(container, format[0].block);
12991
+ node = dom.getParent(container, format[0].block, root);
12982
12992
  }
12983
12993
 
12984
12994
  // Expand to first wrappable block element or any block element
@@ -15186,7 +15196,7 @@ define("tinymce/EditorCommands", [
15186
15196
  },
15187
15197
 
15188
15198
  // Override unlink command
15189
- unlink: function(command) {
15199
+ unlink: function() {
15190
15200
  if (selection.isCollapsed()) {
15191
15201
  var elm = selection.getNode();
15192
15202
  if (elm.tagName == 'A') {
@@ -15196,8 +15206,7 @@ define("tinymce/EditorCommands", [
15196
15206
  return;
15197
15207
  }
15198
15208
 
15199
- execNativeCommand(command);
15200
- selection.collapse(FALSE);
15209
+ formatter.remove("link");
15201
15210
  },
15202
15211
 
15203
15212
  // Override justify commands to use the text formatter engine
@@ -21505,92 +21514,166 @@ define("tinymce/util/Quirks", [
21505
21514
  * <h1>a</h1><p>|b</p>
21506
21515
  *
21507
21516
  * Will produce this on backspace:
21508
- * <h1>a<span class="Apple-style-span" style="<all runtime styles>">b</span></p>
21517
+ * <h1>a<span style="<all runtime styles>">b</span></p>
21509
21518
  *
21510
21519
  * This fixes the backspace to produce:
21511
21520
  * <h1>a|b</p>
21512
21521
  *
21513
21522
  * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
21514
21523
  *
21515
- * This code is a bit of a hack and hopefully it will be fixed soon in WebKit.
21524
+ * This fixes the following delete scenarios:
21525
+ * 1. Delete by pressing backspace key.
21526
+ * 2. Delete by pressing delete key.
21527
+ * 3. Delete by pressing backspace key with ctrl/cmd (Word delete).
21528
+ * 4. Delete by pressing delete key with ctrl/cmd (Word delete).
21529
+ * 5. Delete by drag/dropping contents inside the editor.
21530
+ * 6. Delete by using Cut Ctrl+X/Cmd+X.
21531
+ * 7. Delete by selecting contents and writing a character.'
21532
+ *
21533
+ * This code is a ugly hack since writing full custom delete logic for just this bug
21534
+ * fix seemed like a huge task. I hope we can remove this before the year 2030.
21516
21535
  */
21517
21536
  function cleanupStylesWhenDeleting() {
21518
- function removeMergedFormatSpans(isDelete) {
21519
- var rng, blockElm, wrapperElm, bookmark, container, offset, elm;
21537
+ var doc = editor.getDoc();
21520
21538
 
21521
- function isAtStartOrEndOfElm() {
21522
- if (container.nodeType == 3) {
21523
- if (isDelete && offset == container.length) {
21524
- return true;
21525
- }
21539
+ if (!window.MutationObserver) {
21540
+ return;
21541
+ }
21526
21542
 
21527
- if (!isDelete && offset === 0) {
21528
- return true;
21529
- }
21543
+ function customDelete(isForward) {
21544
+ var mutationObserver = new MutationObserver(function() {});
21545
+
21546
+ Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
21547
+ // Mark existing spans
21548
+ if (elm.tagName == 'SPAN') {
21549
+ elm.setAttribute('mce-data-marked', 1);
21530
21550
  }
21531
- }
21532
21551
 
21533
- rng = selection.getRng();
21534
- var tmpRng = [rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset];
21552
+ // Make sure all elements has a data-mce-style attribute
21553
+ if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
21554
+ editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
21555
+ }
21556
+ });
21535
21557
 
21536
- if (!rng.collapsed) {
21537
- isDelete = true;
21538
- }
21558
+ // Observe added nodes and style attribute changes
21559
+ mutationObserver.observe(editor.getDoc(), {
21560
+ childList: true,
21561
+ attributes: true,
21562
+ subtree: true,
21563
+ attributeFilter: ['style']
21564
+ });
21539
21565
 
21540
- container = rng[(isDelete ? 'start' : 'end') + 'Container'];
21541
- offset = rng[(isDelete ? 'start' : 'end') + 'Offset'];
21566
+ editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
21542
21567
 
21543
- if (container.nodeType == 3) {
21544
- blockElm = dom.getParent(rng.startContainer, dom.isBlock);
21568
+ var rng = editor.selection.getRng();
21569
+ var caretElement = rng.startContainer.parentNode;
21545
21570
 
21546
- // On delete clone the root span of the next block element
21547
- if (isDelete) {
21548
- blockElm = dom.getNext(blockElm, dom.isBlock);
21549
- }
21571
+ Tools.each(mutationObserver.takeRecords(), function(record) {
21572
+ // Restore style attribute to previous value
21573
+ if (record.attributeName == "style") {
21574
+ var oldValue = record.target.getAttribute('data-mce-style');
21550
21575
 
21551
- if (blockElm && (isAtStartOrEndOfElm() || !rng.collapsed)) {
21552
- // Wrap children of block in a EM and let WebKit stick is
21553
- // runtime styles junk into that EM
21554
- wrapperElm = dom.create('em', {'id': '__mceDel'});
21576
+ if (oldValue) {
21577
+ record.target.setAttribute("style", oldValue);
21578
+ } else {
21579
+ record.target.removeAttribute("style");
21580
+ }
21581
+ }
21555
21582
 
21556
- each(Tools.grep(blockElm.childNodes), function(node) {
21557
- wrapperElm.appendChild(node);
21558
- });
21583
+ // Remove all spans that isn't maked and retain selection
21584
+ Tools.each(record.addedNodes, function(node) {
21585
+ if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
21586
+ var offset, container;
21559
21587
 
21560
- blockElm.appendChild(wrapperElm);
21561
- }
21562
- }
21588
+ if (node == caretElement) {
21589
+ offset = rng.startOffset;
21590
+ container = node.firstChild;
21591
+ }
21563
21592
 
21564
- // Do the backspace/delete action
21565
- rng = dom.createRng();
21566
- rng.setStart(tmpRng[0], tmpRng[1]);
21567
- rng.setEnd(tmpRng[2], tmpRng[3]);
21568
- selection.setRng(rng);
21569
- editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
21593
+ dom.remove(node, true);
21570
21594
 
21571
- // Remove temp wrapper element
21572
- if (wrapperElm) {
21573
- bookmark = selection.getBookmark();
21595
+ if (container) {
21596
+ rng.setStart(container, offset);
21597
+ rng.setEnd(container, offset);
21598
+ editor.selection.setRng(rng);
21599
+ }
21600
+ }
21601
+ });
21602
+ });
21574
21603
 
21575
- while ((elm = dom.get('__mceDel'))) {
21576
- dom.remove(elm, true);
21577
- }
21604
+ mutationObserver.disconnect();
21578
21605
 
21579
- selection.moveToBookmark(bookmark);
21580
- }
21606
+ // Remove any left over marks
21607
+ Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
21608
+ span.removeAttribute('mce-data-marked');
21609
+ });
21581
21610
  }
21582
21611
 
21583
21612
  editor.on('keydown', function(e) {
21584
- var isDelete;
21613
+ var isForward = e.keyCode == DELETE, isMeta = VK.metaKeyPressed(e);
21614
+
21615
+ if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
21616
+ var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
21617
+
21618
+ // Ignore non meta delete in the where there is text before/after the caret
21619
+ if (!isMeta && rng.collapsed && container.nodeType == 3) {
21620
+ if (isForward ? offset < container.data.length : offset > 0) {
21621
+ return;
21622
+ }
21623
+ }
21585
21624
 
21586
- isDelete = e.keyCode == DELETE;
21587
- if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
21588
21625
  e.preventDefault();
21589
- removeMergedFormatSpans(isDelete);
21626
+
21627
+ if (isMeta) {
21628
+ editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", "word");
21629
+ }
21630
+
21631
+ customDelete(isForward);
21632
+ }
21633
+ });
21634
+
21635
+ editor.on('keypress', function(e) {
21636
+ if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode) {
21637
+ e.preventDefault();
21638
+ customDelete(true);
21639
+ editor.selection.setContent(String.fromCharCode(e.charCode));
21640
+ }
21641
+ });
21642
+
21643
+ editor.addCommand('Delete', function() {
21644
+ customDelete();
21645
+ });
21646
+
21647
+ editor.addCommand('ForwardDelete', function() {
21648
+ customDelete(true);
21649
+ });
21650
+
21651
+ editor.on('dragstart', function(e) {
21652
+ e.dataTransfer.setData('mce-internal', editor.selection.getContent());
21653
+ });
21654
+
21655
+ editor.on('drop', function(e) {
21656
+ if (!isDefaultPrevented(e)) {
21657
+ var internalContent = e.dataTransfer.getData('mce-internal');
21658
+
21659
+ if (internalContent && doc.caretRangeFromPoint) {
21660
+ e.preventDefault();
21661
+ customDelete();
21662
+ editor.selection.setRng(doc.caretRangeFromPoint(e.x, e.y));
21663
+ editor.insertContent(internalContent);
21664
+ }
21590
21665
  }
21591
21666
  });
21592
21667
 
21593
- editor.addCommand('Delete', function() {removeMergedFormatSpans();});
21668
+ editor.on('cut', function(e) {
21669
+ if (!isDefaultPrevented(e) && e.clipboardData) {
21670
+ e.preventDefault();
21671
+ e.clipboardData.clearData();
21672
+ e.clipboardData.setData('text/html', editor.selection.getContent());
21673
+ e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
21674
+ customDelete(true);
21675
+ }
21676
+ });
21594
21677
  }
21595
21678
 
21596
21679
  /**
@@ -21974,68 +22057,6 @@ define("tinymce/util/Quirks", [
21974
22057
  });
21975
22058
  }
21976
22059
 
21977
- /**
21978
- * Backspace or delete on WebKit will combine all visual styles in a span if the last character is deleted.
21979
- *
21980
- * For example backspace on:
21981
- * <p><b>x|</b></p>
21982
- *
21983
- * Will produce:
21984
- * <p><span style="font-weight: bold">|<br></span></p>
21985
- *
21986
- * When it should produce:
21987
- * <p><b>|<br></b></p>
21988
- *
21989
- * See: https://bugs.webkit.org/show_bug.cgi?id=81656
21990
- */
21991
- function keepInlineElementOnDeleteBackspace() {
21992
- editor.on('keydown', function(e) {
21993
- var isDelete, rng, container, offset, brElm, sibling, collapsed, nonEmptyElements;
21994
-
21995
- isDelete = e.keyCode == DELETE;
21996
- if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
21997
- rng = selection.getRng();
21998
- container = rng.startContainer;
21999
- offset = rng.startOffset;
22000
- collapsed = rng.collapsed;
22001
-
22002
- // Override delete if the start container is a text node and is at the beginning of text or
22003
- // just before/after the last character to be deleted in collapsed mode
22004
- if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) ||
22005
- (collapsed && offset === (isDelete ? 0 : 1)))) {
22006
- // Edge case when deleting <p><b><img> |x</b></p>
22007
- sibling = container.previousSibling;
22008
- if (sibling && sibling.nodeName == "IMG") {
22009
- return;
22010
- }
22011
-
22012
- nonEmptyElements = editor.schema.getNonEmptyElements();
22013
-
22014
- // Prevent default logic since it's broken
22015
- e.preventDefault();
22016
-
22017
- // Insert a BR before the text node this will prevent the containing element from being deleted/converted
22018
- brElm = dom.create('br', {id: '__tmp'});
22019
- container.parentNode.insertBefore(brElm, container);
22020
-
22021
- // Do the browser delete
22022
- editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
22023
-
22024
- // Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>
22025
- container = selection.getRng().startContainer;
22026
- sibling = container.previousSibling;
22027
- if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) &&
22028
- !nonEmptyElements[sibling.nodeName.toLowerCase()]) {
22029
- dom.remove(sibling);
22030
- }
22031
-
22032
- // Remove the temp element we inserted
22033
- dom.remove('__tmp');
22034
- }
22035
- }
22036
- });
22037
- }
22038
-
22039
22060
  /**
22040
22061
  * Removes a blockquote when backspace is pressed at the beginning of it.
22041
22062
  *
@@ -22420,7 +22441,6 @@ define("tinymce/util/Quirks", [
22420
22441
 
22421
22442
  // WebKit
22422
22443
  if (isWebKit) {
22423
- keepInlineElementOnDeleteBackspace();
22424
22444
  cleanupStylesWhenDeleting();
22425
22445
  inputMethodFocus();
22426
22446
  selectControlElements();
@@ -22493,7 +22513,7 @@ define("tinymce/util/Observable", [
22493
22513
  var bindingsName = "__bindings";
22494
22514
  var nativeEvents = Tools.makeMap(
22495
22515
  "focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange" +
22496
- " mouseout mouseenter mouseleave keydown keypress keyup contextmenu dragend dragover draggesture dragdrop drop drag", ' '
22516
+ " mouseout mouseenter mouseleave keydown keypress keyup contextmenu dragstart dragend dragover draggesture dragdrop drop drag", ' '
22497
22517
  );
22498
22518
 
22499
22519
  function returnFalse() {
@@ -25157,36 +25177,36 @@ define("tinymce/FocusManager", [
25157
25177
  return rng;
25158
25178
  }
25159
25179
 
25160
- function registerEvents(e) {
25161
- var editor = e.editor, lastRng, selectionChangeHandler;
25162
-
25163
- function isUIElement(elm) {
25164
- return !!DOMUtils.DOM.getParent(elm, FocusManager.isEditorUIElement);
25165
- }
25166
-
25167
- function isNodeInBody(node) {
25168
- var body = editor.getBody();
25180
+ function isUIElement(elm) {
25181
+ return !!DOMUtils.DOM.getParent(elm, FocusManager.isEditorUIElement);
25182
+ }
25169
25183
 
25170
- while (node) {
25171
- if (node == body) {
25172
- return true;
25173
- }
25184
+ function isNodeInBodyOfEditor(node, editor) {
25185
+ var body = editor.getBody();
25174
25186
 
25175
- node = node.parentNode;
25187
+ while (node) {
25188
+ if (node == body) {
25189
+ return true;
25176
25190
  }
25191
+
25192
+ node = node.parentNode;
25177
25193
  }
25194
+ }
25195
+
25196
+ function registerEvents(e) {
25197
+ var editor = e.editor, selectionChangeHandler;
25178
25198
 
25179
25199
  editor.on('init', function() {
25180
25200
  // On IE take selection snapshot onbeforedeactivate
25181
25201
  if ("onbeforedeactivate" in document && Env.ie < 11) {
25182
25202
  editor.dom.bind(editor.getBody(), 'beforedeactivate', function() {
25183
- var ieSelection = editor.getDoc().selection;
25184
-
25185
25203
  try {
25186
- lastRng = ieSelection && ieSelection.createRange ? ieSelection.createRange() : editor.selection.getRng();
25204
+ editor.lastRng = editor.selection.getRng();
25187
25205
  } catch (ex) {
25188
25206
  // IE throws "Unexcpected call to method or property access" some times so lets ignore it
25189
25207
  }
25208
+
25209
+ editor.selection.lastFocusBookmark = createBookmark(editor.lastRng);
25190
25210
  });
25191
25211
  } else if (editor.inline || Env.ie > 10) {
25192
25212
  // On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
@@ -25198,8 +25218,8 @@ define("tinymce/FocusManager", [
25198
25218
  node = editor.getBody();
25199
25219
  }
25200
25220
 
25201
- if (isNodeInBody(node)) {
25202
- lastRng = editor.selection.getRng();
25221
+ if (isNodeInBodyOfEditor(node, editor)) {
25222
+ editor.lastRng = editor.selection.getRng();
25203
25223
  }
25204
25224
  });
25205
25225
 
@@ -25213,7 +25233,7 @@ define("tinymce/FocusManager", [
25213
25233
 
25214
25234
  // Store when it's non collapsed
25215
25235
  if (!rng.collapsed) {
25216
- lastRng = rng;
25236
+ editor.lastRng = rng;
25217
25237
  }
25218
25238
  };
25219
25239
 
@@ -25228,7 +25248,7 @@ define("tinymce/FocusManager", [
25228
25248
  });
25229
25249
 
25230
25250
  editor.on('setcontent', function() {
25231
- lastRng = null;
25251
+ editor.lastRng = null;
25232
25252
  });
25233
25253
 
25234
25254
  // Remove last selection bookmark on mousedown see #6305
@@ -25250,32 +25270,18 @@ define("tinymce/FocusManager", [
25250
25270
  }
25251
25271
 
25252
25272
  editorManager.activeEditor = editor;
25273
+ editorManager.focusedEditor = editor;
25253
25274
  editor.fire('focus', {blurredEditor: focusedEditor});
25254
25275
  editor.focus(false);
25255
- editorManager.focusedEditor = editor;
25256
25276
  }
25257
25277
 
25258
- lastRng = null;
25278
+ editor.lastRng = null;
25259
25279
  });
25260
25280
 
25261
- editor.on('focusout', function(e) {
25262
- // Moving focus to elements within the body that have a control seleciton on IE
25263
- // will fire an focusout event so we need to check if the event is fired on the body
25264
- // or on a sub element see #6456
25265
- if (e.target !== editor.getBody() && isNodeInBody(e.target)) {
25266
- return;
25267
- }
25268
-
25269
- editor.selection.lastFocusBookmark = createBookmark(lastRng);
25270
-
25281
+ editor.on('focusout', function() {
25271
25282
  window.setTimeout(function() {
25272
25283
  var focusedEditor = editorManager.focusedEditor;
25273
25284
 
25274
- // Focus from editorA into editorB then don't restore selection
25275
- if (focusedEditor != editor) {
25276
- editor.selection.lastFocusBookmark = null;
25277
- }
25278
-
25279
25285
  // Still the same editor the the blur was outside any editor UI
25280
25286
  if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
25281
25287
  editor.fire('blur', {focusedEditor: null});
@@ -25286,6 +25292,22 @@ define("tinymce/FocusManager", [
25286
25292
  });
25287
25293
  }
25288
25294
 
25295
+ // Check if focus is moved to an element outside the active editor by checking if the target node
25296
+ // isn't within the body of the activeEditor nor a UI element such as a dialog child control
25297
+ DOMUtils.DOM.bind(document, 'focusin', function(e) {
25298
+ var activeEditor = editorManager.activeEditor;
25299
+
25300
+ if (activeEditor && e.target.ownerDocument == document) {
25301
+ activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.lastRng);
25302
+
25303
+ // Fire a blur event if the element isn't a UI element
25304
+ if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
25305
+ activeEditor.fire('blur', {focusedEditor: null});
25306
+ editorManager.focusedEditor = null;
25307
+ }
25308
+ }
25309
+ });
25310
+
25289
25311
  editorManager.on('AddEditor', registerEvents);
25290
25312
  }
25291
25313
 
@@ -25354,7 +25376,7 @@ define("tinymce/EditorManager", [
25354
25376
  * @property minorVersion
25355
25377
  * @type String
25356
25378
  */
25357
- minorVersion : '0.11',
25379
+ minorVersion : '0.12',
25358
25380
 
25359
25381
  /**
25360
25382
  * Release date of TinyMCE build.
@@ -25362,7 +25384,7 @@ define("tinymce/EditorManager", [
25362
25384
  * @property releaseDate
25363
25385
  * @type String
25364
25386
  */
25365
- releaseDate: '2013-11-20',
25387
+ releaseDate: '2013-12-18',
25366
25388
 
25367
25389
  /**
25368
25390
  * Collection of editor instances.
@@ -26927,10 +26949,10 @@ define("tinymce/ui/Widget", [
26927
26949
  self.canFocus = true;
26928
26950
 
26929
26951
  if (settings.tooltip && Widget.tooltips !== false) {
26930
- self.on('mouseenter mouseleave', function(e) {
26952
+ self.on('mouseenter', function(e) {
26931
26953
  var tooltip = self.tooltip().moveTo(-0xFFFF);
26932
26954
 
26933
- if (e.control == self && e.type == 'mouseenter') {
26955
+ if (e.control == self) {
26934
26956
  var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
26935
26957
 
26936
26958
  tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
@@ -26942,6 +26964,11 @@ define("tinymce/ui/Widget", [
26942
26964
  tooltip.hide();
26943
26965
  }
26944
26966
  });
26967
+
26968
+ self.on('mouseleave mousedown click', function() {
26969
+ self.tooltip().hide();
26970
+ });
26971
+
26945
26972
  }
26946
26973
 
26947
26974
  self.aria('label', settings.tooltip);
@@ -29299,7 +29326,7 @@ define("tinymce/ui/FormatControls", [
29299
29326
 
29300
29327
  return {
29301
29328
  type: 'listbox',
29302
- text: {raw: blocks[0][0]},
29329
+ text: blocks[0][0],
29303
29330
  values: items,
29304
29331
  fixedWidth: true,
29305
29332
  onselect: toggleFormat,