tinymce-rails 4.4.3 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +2044 -391
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/jquery.tinymce.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.dev.js +141 -0
  12. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/contextmenu/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/fullpage/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/imagetools/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/legacyoutput/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/plugins/noneditable/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.dev.js +143 -0
  28. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.js +1 -1
  29. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/spellchecker/plugin.dev.js +139 -0
  33. data/vendor/assets/javascripts/tinymce/plugins/spellchecker/plugin.js +1 -1
  34. data/vendor/assets/javascripts/tinymce/plugins/tabfocus/plugin.js +1 -1
  35. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.dev.js +143 -0
  36. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +2 -2
  37. data/vendor/assets/javascripts/tinymce/plugins/template/plugin.js +1 -1
  38. data/vendor/assets/javascripts/tinymce/plugins/textpattern/plugin.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/plugins/toc/plugin.js +1 -0
  40. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +1 -1
  41. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/lightgray/AbsoluteLayout.less +17 -0
  43. data/vendor/assets/javascripts/tinymce/skins/lightgray/Animations.less +10 -0
  44. data/vendor/assets/javascripts/tinymce/skins/lightgray/Arrows.less +115 -0
  45. data/vendor/assets/javascripts/tinymce/skins/lightgray/Button.less +175 -0
  46. data/vendor/assets/javascripts/tinymce/skins/lightgray/ButtonGroup.less +71 -0
  47. data/vendor/assets/javascripts/tinymce/skins/lightgray/Checkbox.less +49 -0
  48. data/vendor/assets/javascripts/tinymce/skins/lightgray/ColorBox.less +6 -0
  49. data/vendor/assets/javascripts/tinymce/skins/lightgray/ColorButton.less +72 -0
  50. data/vendor/assets/javascripts/tinymce/skins/lightgray/ColorPicker.less +80 -0
  51. data/vendor/assets/javascripts/tinymce/skins/lightgray/ComboBox.less +97 -0
  52. data/vendor/assets/javascripts/tinymce/skins/lightgray/Container.less +9 -0
  53. data/vendor/assets/javascripts/tinymce/skins/lightgray/Content.Inline.less +4 -0
  54. data/vendor/assets/javascripts/tinymce/skins/lightgray/Content.Objects.less +178 -0
  55. data/vendor/assets/javascripts/tinymce/skins/lightgray/Content.less +27 -0
  56. data/vendor/assets/javascripts/tinymce/skins/lightgray/CropRect.less +62 -0
  57. data/vendor/assets/javascripts/tinymce/skins/lightgray/FieldSet.less +15 -0
  58. data/vendor/assets/javascripts/tinymce/skins/lightgray/FitLayout.less +9 -0
  59. data/vendor/assets/javascripts/tinymce/skins/lightgray/FloatPanel.less +69 -0
  60. data/vendor/assets/javascripts/tinymce/skins/lightgray/FlowLayout.less +36 -0
  61. data/vendor/assets/javascripts/tinymce/skins/lightgray/Icons.Ie7.less +136 -0
  62. data/vendor/assets/javascripts/tinymce/skins/lightgray/Icons.less +182 -0
  63. data/vendor/assets/javascripts/tinymce/skins/lightgray/Iframe.less +6 -0
  64. data/vendor/assets/javascripts/tinymce/skins/lightgray/ImagePanel.less +25 -0
  65. data/vendor/assets/javascripts/tinymce/skins/lightgray/InfoBox.less +71 -0
  66. data/vendor/assets/javascripts/tinymce/skins/lightgray/Label.less +38 -0
  67. data/vendor/assets/javascripts/tinymce/skins/lightgray/ListBox.less +26 -0
  68. data/vendor/assets/javascripts/tinymce/skins/lightgray/Menu.less +34 -0
  69. data/vendor/assets/javascripts/tinymce/skins/lightgray/MenuBar.less +32 -0
  70. data/vendor/assets/javascripts/tinymce/skins/lightgray/MenuButton.less +34 -0
  71. data/vendor/assets/javascripts/tinymce/skins/lightgray/MenuItem.less +176 -0
  72. data/vendor/assets/javascripts/tinymce/skins/lightgray/Mixins.less +54 -0
  73. data/vendor/assets/javascripts/tinymce/skins/lightgray/Notification.less +142 -0
  74. data/vendor/assets/javascripts/tinymce/skins/lightgray/Panel.less +7 -0
  75. data/vendor/assets/javascripts/tinymce/skins/lightgray/Path.less +45 -0
  76. data/vendor/assets/javascripts/tinymce/skins/lightgray/Progress.less +34 -0
  77. data/vendor/assets/javascripts/tinymce/skins/lightgray/Radio.less +1 -0
  78. data/vendor/assets/javascripts/tinymce/skins/lightgray/Reset.less +32 -0
  79. data/vendor/assets/javascripts/tinymce/skins/lightgray/ResizeHandle.less +18 -0
  80. data/vendor/assets/javascripts/tinymce/skins/lightgray/Scrollable.less +44 -0
  81. data/vendor/assets/javascripts/tinymce/skins/lightgray/SelectBox.less +6 -0
  82. data/vendor/assets/javascripts/tinymce/skins/lightgray/Sidebar.less +49 -0
  83. data/vendor/assets/javascripts/tinymce/skins/lightgray/Slider.less +33 -0
  84. data/vendor/assets/javascripts/tinymce/skins/lightgray/Spacer.less +5 -0
  85. data/vendor/assets/javascripts/tinymce/skins/lightgray/SplitButton.less +49 -0
  86. data/vendor/assets/javascripts/tinymce/skins/lightgray/StackLayout.less +5 -0
  87. data/vendor/assets/javascripts/tinymce/skins/lightgray/TabPanel.less +44 -0
  88. data/vendor/assets/javascripts/tinymce/skins/lightgray/TextBox.less +41 -0
  89. data/vendor/assets/javascripts/tinymce/skins/lightgray/Throbber.less +19 -0
  90. data/vendor/assets/javascripts/tinymce/skins/lightgray/TinyMCE.less +159 -0
  91. data/vendor/assets/javascripts/tinymce/skins/lightgray/ToolTip.less +133 -0
  92. data/vendor/assets/javascripts/tinymce/skins/lightgray/Variables.less +218 -0
  93. data/vendor/assets/javascripts/tinymce/skins/lightgray/Window.less +127 -0
  94. data/vendor/assets/javascripts/tinymce/skins/lightgray/content.inline.min.css +1 -1
  95. data/vendor/assets/javascripts/tinymce/skins/lightgray/content.min.css +1 -1
  96. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/tinymce.eot +0 -0
  97. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/tinymce.svg +2 -0
  98. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/tinymce.ttf +0 -0
  99. data/vendor/assets/javascripts/tinymce/skins/lightgray/fonts/tinymce.woff +0 -0
  100. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.dev.less +48 -0
  101. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.ie7.dev.less +47 -0
  102. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.ie7.less +2777 -0
  103. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.ie7.min.css +1 -1
  104. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.less +2874 -0
  105. data/vendor/assets/javascripts/tinymce/skins/lightgray/skin.min.css +1 -1
  106. data/vendor/assets/javascripts/tinymce/themes/inlite/theme.js +1 -1
  107. data/vendor/assets/javascripts/tinymce/themes/modern/theme.js +1 -1
  108. data/vendor/assets/javascripts/tinymce/tinymce.js +14 -14
  109. metadata +65 -4
  110. data/vendor/assets/javascripts/tinymce/plugins/media/moxieplayer.swf +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9656b8fc6acfb837cf040107a3904b4341814f9
4
- data.tar.gz: 36e7680a0fb749e516b243b1f52f9a09c0fbaeb6
3
+ metadata.gz: d2e35b083e2056e0438cb7f2e5ea2dc974b7c6f2
4
+ data.tar.gz: b815c34b3b67a72259a6e1269ed9043205b5b069
5
5
  SHA512:
6
- metadata.gz: 1f9c9d43dc30ecfed40fa2d6410c73c50eaba7a018d1c78cd3a1d425427e773053ac4a60295ac8c8a8720e9a8196aad9a6448d188152d02ae49b6d8cec3f1568
7
- data.tar.gz: 296c4d13eab4d4c92e9fd0e1b783a393ef8a34cfc703e4bbdc5b59cc79f53cb2d96ccaaa36466172577b8250b0a56a838991f9101bd5bc3289e81f11f69f514b
6
+ metadata.gz: dc0f81674481d4508be6cf224293e96d4fb2e4bbdf2c344032d020f796ca9f644d4fed19ba4951ecfc1c4a0277cfd280803ee709495bdaa5c496ea2001781cc6
7
+ data.tar.gz: c776c3dce0209da1d38af43a2d3abbddc1608fedf68b91a76ffe3e197ec1459f0c81f02b11845ce95436060f6acf2b6d2c9e208d30e7235a4b631bcec42e3e0e
@@ -1,4 +1,4 @@
1
- // 4.4.3 (2016-09-01)
1
+ // 4.5.0 (2016-11-23)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -583,6 +583,26 @@ define("tinymce/util/Delay", [
583
583
  return clearInterval(id);
584
584
  }
585
585
 
586
+ function debounce(callback, time) {
587
+ var timer, func;
588
+
589
+ func = function() {
590
+ var args = arguments;
591
+
592
+ clearTimeout(timer);
593
+
594
+ timer = wrappedSetTimeout(function() {
595
+ callback.apply(this, args);
596
+ }, time);
597
+ };
598
+
599
+ func.stop = function() {
600
+ clearTimeout(timer);
601
+ };
602
+
603
+ return func;
604
+ }
605
+
586
606
  return {
587
607
  /**
588
608
  * Requests an animation frame and fallbacks to a timeout on older browsers.
@@ -668,32 +688,17 @@ define("tinymce/util/Delay", [
668
688
  },
669
689
 
670
690
  /**
671
- * Creates throttled callback function that only gets executed once within the specified time.
691
+ * Creates debounced callback function that only gets executed once within the specified time.
672
692
  *
673
- * @method throttle
693
+ * @method debounce
674
694
  * @param {function} callback Callback to execute when timer finishes.
675
695
  * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
676
- * @return {Function} Throttled function callback.
696
+ * @return {Function} debounced function callback.
677
697
  */
678
- throttle: function(callback, time) {
679
- var timer, func;
680
-
681
- func = function() {
682
- var args = arguments;
683
-
684
- clearTimeout(timer);
685
-
686
- timer = wrappedSetTimeout(function() {
687
- callback.apply(this, args);
688
- }, time);
689
- };
690
-
691
- func.stop = function() {
692
- clearTimeout(timer);
693
- };
698
+ debounce: debounce,
694
699
 
695
- return func;
696
- },
700
+ // Throttle needs to be debounce due to backwards compatibility.
701
+ throttle: debounce,
697
702
 
698
703
  /**
699
704
  * Clears an interval timer so it won't execute.
@@ -3799,6 +3804,18 @@ define("tinymce/util/Tools", [
3799
3804
  return map;
3800
3805
  }
3801
3806
 
3807
+ /**
3808
+ * JavaScript does not protect hasOwnProperty method, so it is possible to overwrite it. This is
3809
+ * object independent version.
3810
+ *
3811
+ * @param {Object} obj
3812
+ * @param {String} prop
3813
+ * @returns {Boolean}
3814
+ */
3815
+ function hasOwnProperty(obj, prop) {
3816
+ return Object.prototype.hasOwnProperty.call(obj, prop);
3817
+ }
3818
+
3802
3819
  /**
3803
3820
  * Creates a class, subclass or static singleton.
3804
3821
  * More details on this method can be found in the Wiki.
@@ -4142,14 +4159,17 @@ define("tinymce/util/Tools", [
4142
4159
  grep: Arr.filter,
4143
4160
 
4144
4161
  /**
4145
- * Returns true/false if the object is an array or not.
4162
+ * Returns an index of the item or -1 if item is not present in the array.
4146
4163
  *
4147
- * @method isArray
4148
- * @param {Object} obj Object to check.
4149
- * @return {boolean} true/false state if the object is an array or not.
4164
+ * @method inArray
4165
+ * @param {any} item Item to search for.
4166
+ * @param {Array} arr Array to search in.
4167
+ * @return {Number} index of the item or -1 if item was not found.
4150
4168
  */
4151
4169
  inArray: Arr.indexOf,
4152
4170
 
4171
+ hasOwn: hasOwnProperty,
4172
+
4153
4173
  extend: extend,
4154
4174
  create: create,
4155
4175
  walk: walk,
@@ -5779,7 +5799,7 @@ define("tinymce/html/Styles", [], function() {
5779
5799
  urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
5780
5800
  styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
5781
5801
  trimRightRegExp = /\s+$/,
5782
- undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
5802
+ i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
5783
5803
 
5784
5804
  settings = settings || {};
5785
5805
 
@@ -5940,6 +5960,14 @@ define("tinymce/html/Styles", [], function() {
5940
5960
  return str;
5941
5961
  }
5942
5962
 
5963
+ function decodeSingleHexSequence(escSeq) {
5964
+ return String.fromCharCode(parseInt(escSeq.slice(1), 16));
5965
+ }
5966
+
5967
+ function decodeHexSequences(value) {
5968
+ return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence);
5969
+ }
5970
+
5943
5971
  function processUrl(match, url, url2, url3, str, str2) {
5944
5972
  str = str || str2;
5945
5973
 
@@ -5953,7 +5981,7 @@ define("tinymce/html/Styles", [], function() {
5953
5981
  url = decode(url || url2 || url3);
5954
5982
 
5955
5983
  if (!settings.allow_script_urls) {
5956
- var scriptUrl = url.replace(/[\s\r\n]+/, '');
5984
+ var scriptUrl = url.replace(/[\s\r\n]+/g, '');
5957
5985
 
5958
5986
  if (/(java|vb)script:/i.test(scriptUrl)) {
5959
5987
  return "";
@@ -5983,17 +6011,22 @@ define("tinymce/html/Styles", [], function() {
5983
6011
 
5984
6012
  // Parse styles
5985
6013
  while ((matches = styleRegExp.exec(css))) {
6014
+ styleRegExp.lastIndex = matches.index + matches[0].length;
5986
6015
  name = matches[1].replace(trimRightRegExp, '').toLowerCase();
5987
6016
  value = matches[2].replace(trimRightRegExp, '');
5988
6017
 
5989
- // Decode escaped sequences like \65 -> e
5990
- /*jshint loopfunc:true*/
5991
- /*eslint no-loop-func:0 */
5992
- value = value.replace(/\\[0-9a-f]+/g, function(e) {
5993
- return String.fromCharCode(parseInt(e.substr(1), 16));
5994
- });
6018
+ if (name && value) {
6019
+ // Decode escaped sequences like \65 -> e
6020
+ name = decodeHexSequences(name);
6021
+ value = decodeHexSequences(value);
6022
+
6023
+ // Skip properties with double quotes and sequences like \" \' in their names
6024
+ // See 'mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations'
6025
+ // https://cure53.de/fp170.pdf
6026
+ if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) {
6027
+ continue;
6028
+ }
5995
6029
 
5996
- if (name && value.length > 0) {
5997
6030
  // Don't allow behavior name or expression/comments within the values
5998
6031
  if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
5999
6032
  continue;
@@ -6013,8 +6046,6 @@ define("tinymce/html/Styles", [], function() {
6013
6046
  value = value.replace(urlOrStrRegExp, processUrl);
6014
6047
  styles[name] = isEncoded ? decode(value, true) : value;
6015
6048
  }
6016
-
6017
- styleRegExp.lastIndex = matches.index + matches[0].length;
6018
6049
  }
6019
6050
  // Compress the styles to reduce it's size for example IE will expand styles
6020
6051
  compress("border", "", true);
@@ -6060,7 +6091,7 @@ define("tinymce/html/Styles", [], function() {
6060
6091
  name = styleList[i];
6061
6092
  value = styles[name];
6062
6093
 
6063
- if (value !== undef && value.length > 0) {
6094
+ if (value) {
6064
6095
  css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
6065
6096
  }
6066
6097
  }
@@ -6093,10 +6124,8 @@ define("tinymce/html/Styles", [], function() {
6093
6124
  for (name in styles) {
6094
6125
  value = styles[name];
6095
6126
 
6096
- if (value !== undef && value.length > 0) {
6097
- if (!invalidStyles || isValid(name, elementName)) {
6098
- css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
6099
- }
6127
+ if (value && (!invalidStyles || isValid(name, elementName))) {
6128
+ css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
6100
6129
  }
6101
6130
  }
6102
6131
  }
@@ -8659,7 +8688,7 @@ define("tinymce/dom/DOMUtils", [
8659
8688
  target.removeChild(target.firstChild);
8660
8689
  } catch (ex) {
8661
8690
  // IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p
8662
- $('<div>').html('<br>' + html).contents().slice(1).appendTo(target);
8691
+ $('<div></div>').html('<br>' + html).contents().slice(1).appendTo(target);
8663
8692
  }
8664
8693
 
8665
8694
  return html;
@@ -8683,7 +8712,7 @@ define("tinymce/dom/DOMUtils", [
8683
8712
  elm = this.get(elm);
8684
8713
 
8685
8714
  // Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
8686
- return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('<div>').append($(elm).clone()).html();
8715
+ return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('<div></div>').append($(elm).clone()).html();
8687
8716
  },
8688
8717
 
8689
8718
  /**
@@ -8964,7 +8993,9 @@ define("tinymce/dom/DOMUtils", [
8964
8993
 
8965
8994
  if (type === 1) {
8966
8995
  // Ignore bogus elements
8967
- if (node.getAttribute('data-mce-bogus')) {
8996
+ var bogusVal = node.getAttribute('data-mce-bogus');
8997
+ if (bogusVal) {
8998
+ node = walker.next(bogusVal === 'all');
8968
8999
  continue;
8969
9000
  }
8970
9001
 
@@ -8974,6 +9005,7 @@ define("tinymce/dom/DOMUtils", [
8974
9005
  // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
8975
9006
  if (name === 'br') {
8976
9007
  brCount++;
9008
+ node = walker.next();
8977
9009
  continue;
8978
9010
  }
8979
9011
 
@@ -9000,7 +9032,9 @@ define("tinymce/dom/DOMUtils", [
9000
9032
  if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
9001
9033
  return false;
9002
9034
  }
9003
- } while ((node = walker.next()));
9035
+
9036
+ node = walker.next();
9037
+ } while (node);
9004
9038
  }
9005
9039
 
9006
9040
  return brCount <= 1;
@@ -10142,6 +10176,12 @@ define("tinymce/caret/CaretContainer", [
10142
10176
  return textNode;
10143
10177
  }
10144
10178
 
10179
+ function createBogusBr() {
10180
+ var br = document.createElement('br');
10181
+ br.setAttribute('data-mce-bogus', '1');
10182
+ return br;
10183
+ }
10184
+
10145
10185
  function insertBlock(blockName, node, before) {
10146
10186
  var doc, blockNode, parentNode;
10147
10187
 
@@ -10149,7 +10189,7 @@ define("tinymce/caret/CaretContainer", [
10149
10189
  blockNode = doc.createElement(blockName);
10150
10190
  blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after');
10151
10191
  blockNode.setAttribute('data-mce-bogus', 'all');
10152
- blockNode.appendChild(doc.createTextNode('\u00a0'));
10192
+ blockNode.appendChild(createBogusBr());
10153
10193
  parentNode = node.parentNode;
10154
10194
 
10155
10195
  if (!before) {
@@ -10165,9 +10205,13 @@ define("tinymce/caret/CaretContainer", [
10165
10205
  return blockNode;
10166
10206
  }
10167
10207
 
10208
+ function hasContent(node) {
10209
+ return node.firstChild !== node.lastChild || !NodeType.isBr(node.firstChild);
10210
+ }
10211
+
10168
10212
  function remove(caretContainerNode) {
10169
10213
  if (isElement(caretContainerNode) && isCaretContainer(caretContainerNode)) {
10170
- if (caretContainerNode.innerHTML != '&nbsp;') {
10214
+ if (hasContent(caretContainerNode)) {
10171
10215
  caretContainerNode.removeAttribute('data-mce-caret');
10172
10216
  } else {
10173
10217
  removeNode(caretContainerNode);
@@ -10188,12 +10232,35 @@ define("tinymce/caret/CaretContainer", [
10188
10232
  return isText(node) && node.data[node.data.length - 1] == Zwsp.ZWSP;
10189
10233
  }
10190
10234
 
10235
+ function trimBogusBr(elm) {
10236
+ var brs = elm.getElementsByTagName('br');
10237
+ var lastBr = brs[brs.length - 1];
10238
+ if (NodeType.isBogus(lastBr)) {
10239
+ lastBr.parentNode.removeChild(lastBr);
10240
+ }
10241
+ }
10242
+
10243
+ function showCaretContainerBlock(caretContainer) {
10244
+ if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) {
10245
+ trimBogusBr(caretContainer);
10246
+ caretContainer.removeAttribute('data-mce-caret');
10247
+ caretContainer.removeAttribute('data-mce-bogus');
10248
+ caretContainer.removeAttribute('style');
10249
+ caretContainer.removeAttribute('_moz_abspos');
10250
+ return caretContainer;
10251
+ }
10252
+
10253
+ return null;
10254
+ }
10255
+
10191
10256
  return {
10192
10257
  isCaretContainer: isCaretContainer,
10193
10258
  isCaretContainerBlock: isCaretContainerBlock,
10194
10259
  isCaretContainerInline: isCaretContainerInline,
10260
+ showCaretContainerBlock: showCaretContainerBlock,
10195
10261
  insertInline: insertInline,
10196
10262
  insertBlock: insertBlock,
10263
+ hasContent: hasContent,
10197
10264
  remove: remove,
10198
10265
  startsWithCaretContainer: startsWithCaretContainer,
10199
10266
  endsWithCaretContainer: endsWithCaretContainer
@@ -10225,9 +10292,14 @@ define("tinymce/dom/RangeUtils", [
10225
10292
  "tinymce/caret/CaretContainer"
10226
10293
  ], function(Tools, TreeWalker, NodeType, Range, CaretContainer) {
10227
10294
  var each = Tools.each,
10295
+ isContentEditableTrue = NodeType.isContentEditableTrue,
10228
10296
  isContentEditableFalse = NodeType.isContentEditableFalse,
10229
10297
  isCaretContainer = CaretContainer.isCaretContainer;
10230
10298
 
10299
+ function hasCeProperty(node) {
10300
+ return isContentEditableTrue(node) || isContentEditableFalse(node);
10301
+ }
10302
+
10231
10303
  function getEndChild(container, index) {
10232
10304
  var childNodes = container.childNodes;
10233
10305
 
@@ -10242,6 +10314,30 @@ define("tinymce/dom/RangeUtils", [
10242
10314
  return childNodes[index] || container;
10243
10315
  }
10244
10316
 
10317
+ function findParent(node, rootNode, predicate) {
10318
+ while (node && node !== rootNode) {
10319
+ if (predicate(node)) {
10320
+ return node;
10321
+ }
10322
+
10323
+ node = node.parentNode;
10324
+ }
10325
+
10326
+ return null;
10327
+ }
10328
+
10329
+ function hasParent(node, rootNode, predicate) {
10330
+ return findParent(node, rootNode, predicate) !== null;
10331
+ }
10332
+
10333
+ function isFormatterCaret(node) {
10334
+ return node.id === '_mce_caret';
10335
+ }
10336
+
10337
+ function isCeFalseCaretContainer(node, rootNode) {
10338
+ return isCaretContainer(node) && hasParent(node, rootNode, isFormatterCaret) === false;
10339
+ }
10340
+
10245
10341
  function RangeUtils(dom) {
10246
10342
  /**
10247
10343
  * Walks the specified range like object and executes the callback for each sibling collection it finds.
@@ -10530,7 +10626,7 @@ define("tinymce/dom/RangeUtils", [
10530
10626
  walker = new TreeWalker(startNode, parentBlockContainer);
10531
10627
  while ((node = walker[left ? 'prev' : 'next']())) {
10532
10628
  // Break if we hit a non content editable node
10533
- if (dom.getContentEditableParent(node) === "false" || isCaretContainer(node)) {
10629
+ if (dom.getContentEditableParent(node) === "false" || isCeFalseCaretContainer(node, dom.getRoot())) {
10534
10630
  return;
10535
10631
  }
10536
10632
 
@@ -10777,6 +10873,11 @@ define("tinymce/dom/RangeUtils", [
10777
10873
  return null;
10778
10874
  }
10779
10875
 
10876
+ function moveOutOfContentEditableFalse(rng, rootNode) {
10877
+ var parentElement = rng && rng.parentElement ? rng.parentElement() : null;
10878
+ return isContentEditableFalse(findParent(parentElement, rootNode, hasCeProperty)) ? null : rng;
10879
+ }
10880
+
10780
10881
  /**
10781
10882
  * Gets the caret range for the given x/y location.
10782
10883
  *
@@ -10806,6 +10907,8 @@ define("tinymce/dom/RangeUtils", [
10806
10907
  } catch (ex) {
10807
10908
  rng = findClosestIeRange(clientX, clientY, doc);
10808
10909
  }
10910
+
10911
+ return moveOutOfContentEditableFalse(rng, doc.body);
10809
10912
  }
10810
10913
 
10811
10914
  return rng;
@@ -11530,6 +11633,7 @@ define("tinymce/html/Schema", [
11530
11633
  var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
11531
11634
 
11532
11635
  function split(items, delim) {
11636
+ items = Tools.trim(items);
11533
11637
  return items ? items.split(delim || ' ') : [];
11534
11638
  }
11535
11639
 
@@ -11545,7 +11649,7 @@ define("tinymce/html/Schema", [
11545
11649
  var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
11546
11650
 
11547
11651
  function add(name, attributes, children) {
11548
- var ni, i, attributesOrder, args = arguments;
11652
+ var ni, attributesOrder, element;
11549
11653
 
11550
11654
  function arrayToMap(array, obj) {
11551
11655
  var map = {}, i, l;
@@ -11564,24 +11668,18 @@ define("tinymce/html/Schema", [
11564
11668
  children = split(children);
11565
11669
  }
11566
11670
 
11567
- // Split string children
11568
- for (i = 3; i < args.length; i++) {
11569
- if (typeof args[i] === "string") {
11570
- args[i] = split(args[i]);
11571
- }
11572
-
11573
- children.push.apply(children, args[i]);
11574
- }
11575
-
11576
11671
  name = split(name);
11577
11672
  ni = name.length;
11578
11673
  while (ni--) {
11579
- attributesOrder = [].concat(globalAttributes, split(attributes));
11580
- schema[name[ni]] = {
11674
+ attributesOrder = split([globalAttributes, attributes].join(' '));
11675
+
11676
+ element = {
11581
11677
  attributes: arrayToMap(attributesOrder),
11582
11678
  attributesOrder: attributesOrder,
11583
11679
  children: arrayToMap(children, dummyObj)
11584
11680
  };
11681
+
11682
+ schema[name[ni]] = element;
11585
11683
  }
11586
11684
  }
11587
11685
 
@@ -11606,62 +11704,61 @@ define("tinymce/html/Schema", [
11606
11704
  }
11607
11705
 
11608
11706
  // Attributes present on all elements
11609
- globalAttributes = split("id accesskey class dir lang style tabindex title");
11707
+ globalAttributes = "id accesskey class dir lang style tabindex title";
11610
11708
 
11611
11709
  // Event attributes can be opt-in/opt-out
11612
11710
  /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
11613
- "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
11614
- "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
11615
- "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
11616
- "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
11617
- "onwaiting"
11618
- );*/
11711
+ "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
11712
+ "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
11713
+ "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
11714
+ "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
11715
+ "onwaiting"
11716
+ );*/
11619
11717
 
11620
11718
  // Block content elements
11621
- blockContent = split(
11622
- "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
11623
- );
11719
+ blockContent =
11720
+ "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul";
11624
11721
 
11625
11722
  // Phrasing content elements from the HTML5 spec (inline)
11626
- phrasingContent = split(
11723
+ phrasingContent =
11627
11724
  "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
11628
11725
  "label map noscript object q s samp script select small span strong sub sup " +
11629
11726
  "textarea u var #text #comment"
11630
- );
11727
+ ;
11631
11728
 
11632
11729
  // Add HTML5 items to globalAttributes, blockContent, phrasingContent
11633
11730
  if (type != "html4") {
11634
- globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
11635
- "hidden spellcheck translate"));
11636
- blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
11637
- phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output picture " +
11638
- "progress time wbr video ruby bdi keygen"));
11731
+ globalAttributes += " contenteditable contextmenu draggable dropzone " +
11732
+ "hidden spellcheck translate";
11733
+ blockContent += " article aside details dialog figure header footer hgroup section nav";
11734
+ phrasingContent += " audio canvas command datalist mark meter output picture " +
11735
+ "progress time wbr video ruby bdi keygen";
11639
11736
  }
11640
11737
 
11641
11738
  // Add HTML4 elements unless it's html5-strict
11642
11739
  if (type != "html5-strict") {
11643
- globalAttributes.push("xml:lang");
11740
+ globalAttributes += " xml:lang";
11644
11741
 
11645
- html4PhrasingContent = split("acronym applet basefont big font strike tt");
11646
- phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
11742
+ html4PhrasingContent = "acronym applet basefont big font strike tt";
11743
+ phrasingContent = [phrasingContent, html4PhrasingContent].join(' ');
11647
11744
 
11648
- each(html4PhrasingContent, function(name) {
11745
+ each(split(html4PhrasingContent), function(name) {
11649
11746
  add(name, "", phrasingContent);
11650
11747
  });
11651
11748
 
11652
- html4BlockContent = split("center dir isindex noframes");
11653
- blockContent.push.apply(blockContent, html4BlockContent);
11749
+ html4BlockContent = "center dir isindex noframes";
11750
+ blockContent = [blockContent, html4BlockContent].join(' ');
11654
11751
 
11655
11752
  // Flow content elements from the HTML5 spec (block+inline)
11656
- flowContent = [].concat(blockContent, phrasingContent);
11753
+ flowContent = [blockContent, phrasingContent].join(' ');
11657
11754
 
11658
- each(html4BlockContent, function(name) {
11755
+ each(split(html4BlockContent), function(name) {
11659
11756
  add(name, "", flowContent);
11660
11757
  });
11661
11758
  }
11662
11759
 
11663
11760
  // Flow content elements from the HTML5 spec (block+inline)
11664
- flowContent = flowContent || [].concat(blockContent, phrasingContent);
11761
+ flowContent = flowContent || [blockContent, phrasingContent].join(" ");
11665
11762
 
11666
11763
  // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
11667
11764
  // Schema items <element name>, <specific attributes>, <children ..>
@@ -11674,8 +11771,8 @@ define("tinymce/html/Schema", [
11674
11771
  add("style", "media type scoped");
11675
11772
  add("script", "src async defer type charset");
11676
11773
  add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
11677
- "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
11678
- "onpopstate onresize onscroll onstorage onunload", flowContent);
11774
+ "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
11775
+ "onpopstate onresize onscroll onstorage onunload", flowContent);
11679
11776
  add("address dt dd div caption", "", flowContent);
11680
11777
  add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
11681
11778
  add("blockquote", "cite", flowContent);
@@ -11689,9 +11786,9 @@ define("tinymce/html/Schema", [
11689
11786
  add("img", "src sizes srcset alt usemap ismap width height");
11690
11787
  add("iframe", "src name width height", flowContent);
11691
11788
  add("embed", "src type width height");
11692
- add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
11789
+ add("object", "data type typemustmatch name usemap form width height", [flowContent, "param"].join(' '));
11693
11790
  add("param", "name value");
11694
- add("map", "name", flowContent, "area");
11791
+ add("map", "name", [flowContent, "area"].join(' '));
11695
11792
  add("area", "alt coords shape href target rel media hreflang type");
11696
11793
  add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
11697
11794
  add("colgroup", "span", "col");
@@ -11701,7 +11798,7 @@ define("tinymce/html/Schema", [
11701
11798
  add("td", "colspan rowspan headers", flowContent);
11702
11799
  add("th", "colspan rowspan headers scope abbr", flowContent);
11703
11800
  add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
11704
- add("fieldset", "disabled form name", flowContent, "legend");
11801
+ add("fieldset", "disabled form name", [flowContent, "legend"].join(' '));
11705
11802
  add("label", "form for", phrasingContent);
11706
11803
  add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
11707
11804
  "formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
@@ -11712,33 +11809,34 @@ define("tinymce/html/Schema", [
11712
11809
  add("optgroup", "disabled label", "option");
11713
11810
  add("option", "disabled label selected value");
11714
11811
  add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
11715
- add("menu", "type label", flowContent, "li");
11812
+ add("menu", "type label", [flowContent, "li"].join(' '));
11716
11813
  add("noscript", "", flowContent);
11717
11814
 
11718
11815
  // Extend with HTML5 elements
11719
11816
  if (type != "html4") {
11720
11817
  add("wbr");
11721
- add("ruby", "", phrasingContent, "rt rp");
11818
+ add("ruby", "", [phrasingContent, "rt rp"].join(' '));
11722
11819
  add("figcaption", "", flowContent);
11723
11820
  add("mark rt rp summary bdi", "", phrasingContent);
11724
11821
  add("canvas", "width height", flowContent);
11725
11822
  add("video", "src crossorigin poster preload autoplay mediagroup loop " +
11726
- "muted controls width height buffered", flowContent, "track source");
11727
- add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
11823
+ "muted controls width height buffered", [flowContent, "track source"].join(' '));
11824
+ add("audio", "src crossorigin preload autoplay mediagroup loop muted controls " +
11825
+ "buffered volume", [flowContent, "track source"].join(' '));
11728
11826
  add("picture", "", "img source");
11729
11827
  add("source", "src srcset type media sizes");
11730
11828
  add("track", "kind src srclang label default");
11731
- add("datalist", "", phrasingContent, "option");
11829
+ add("datalist", "", [phrasingContent, "option"].join(' '));
11732
11830
  add("article section nav aside header footer", "", flowContent);
11733
11831
  add("hgroup", "", "h1 h2 h3 h4 h5 h6");
11734
- add("figure", "", flowContent, "figcaption");
11832
+ add("figure", "", [flowContent, "figcaption"].join(' '));
11735
11833
  add("time", "datetime", phrasingContent);
11736
11834
  add("dialog", "open", flowContent);
11737
11835
  add("command", "type label icon disabled checked radiogroup command");
11738
11836
  add("output", "for form name", phrasingContent);
11739
11837
  add("progress", "value max", phrasingContent);
11740
11838
  add("meter", "value min max low high optimum", phrasingContent);
11741
- add("details", "open", flowContent, "summary");
11839
+ add("details", "open", [flowContent, "summary"].join(' '));
11742
11840
  add("keygen", "autofocus challenge disabled form keytype name");
11743
11841
  }
11744
11842
 
@@ -11800,8 +11898,8 @@ define("tinymce/html/Schema", [
11800
11898
 
11801
11899
  // Delete header, footer, sectioning and heading content descendants
11802
11900
  /*each('dt th address', function(name) {
11803
- delete schema[name].children[name];
11804
- });*/
11901
+ delete schema[name].children[name];
11902
+ });*/
11805
11903
 
11806
11904
  // Caption can't have tables
11807
11905
  delete schema.caption.children.table;
@@ -11891,18 +11989,18 @@ define("tinymce/html/Schema", [
11891
11989
  whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
11892
11990
  selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
11893
11991
  shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
11894
- 'meta param embed source wbr track');
11992
+ 'meta param embed source wbr track');
11895
11993
  boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
11896
- 'noshade nowrap readonly selected autoplay loop controls');
11994
+ 'noshade nowrap readonly selected autoplay loop controls');
11897
11995
  nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
11898
11996
  moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap);
11899
11997
  textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
11900
- 'blockquote center dir fieldset header footer article section hgroup aside nav figure');
11998
+ 'blockquote center dir fieldset header footer article section hgroup aside nav figure');
11901
11999
  blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
11902
- 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
11903
- 'datalist select optgroup figcaption', textBlockElementsMap);
12000
+ 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
12001
+ 'datalist select optgroup figcaption', textBlockElementsMap);
11904
12002
  textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
11905
- 'dfn code mark q sup sub samp');
12003
+ 'dfn code mark q sup sub samp');
11906
12004
 
11907
12005
  each((settings.special || 'script noscript style textarea').split(' '), function(name) {
11908
12006
  specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
@@ -12216,8 +12314,8 @@ define("tinymce/html/Schema", [
12216
12314
  // Remove these by default
12217
12315
  // TODO: Reenable in 4.1
12218
12316
  /*each(split('script style'), function(name) {
12219
- delete elements[name];
12220
- });*/
12317
+ delete elements[name];
12318
+ });*/
12221
12319
  } else {
12222
12320
  setValidElements(settings.valid_elements);
12223
12321
  }
@@ -12229,6 +12327,28 @@ define("tinymce/html/Schema", [
12229
12327
  // Todo: Remove this when we fix list handling to be valid
12230
12328
  addValidChildren('+ol[ul|ol],+ul[ul|ol]');
12231
12329
 
12330
+
12331
+ // Some elements are not valid by themselves - require parents
12332
+ each({
12333
+ dd: 'dl',
12334
+ dt: 'dl',
12335
+ li: 'ul ol',
12336
+ td: 'tr',
12337
+ th: 'tr',
12338
+ tr: 'tbody thead tfoot',
12339
+ tbody: 'table',
12340
+ thead: 'table',
12341
+ tfoot: 'table',
12342
+ legend: 'fieldset',
12343
+ area: 'map',
12344
+ param: 'video audio object'
12345
+ }, function(parents, item) {
12346
+ if (elements[item]) {
12347
+ elements[item].parentsRequired = split(parents);
12348
+ }
12349
+ });
12350
+
12351
+
12232
12352
  // Delete invalid elements
12233
12353
  if (settings.invalid_elements) {
12234
12354
  each(explode(settings.invalid_elements), function(item) {
@@ -12944,7 +13064,7 @@ define("tinymce/html/SaxParser", [
12944
13064
  value = ' ' + value;
12945
13065
  }
12946
13066
 
12947
- if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
13067
+ if (!settings.allow_conditional_comments && value.substr(0, 3).toLowerCase() === '[if') {
12948
13068
  value = ' ' + value;
12949
13069
  }
12950
13070
 
@@ -13740,6 +13860,38 @@ define("tinymce/html/DomParser", [
13740
13860
  });
13741
13861
  }
13742
13862
 
13863
+ if (!settings.allow_unsafe_link_target) {
13864
+ self.addAttributeFilter('href', function(nodes) {
13865
+ var i = nodes.length, node, rel;
13866
+ var rules = 'noopener noreferrer';
13867
+
13868
+ function addTargetRules(rel) {
13869
+ rel = removeTargetRules(rel);
13870
+ return rel ? [rel, rules].join(' ') : rules;
13871
+ }
13872
+
13873
+ function removeTargetRules(rel) {
13874
+ var regExp = new RegExp('(' + rules.replace(' ', '|') + ')', 'g');
13875
+ if (rel) {
13876
+ rel = Tools.trim(rel.replace(regExp, ''));
13877
+ }
13878
+ return rel ? rel : null;
13879
+ }
13880
+
13881
+ function toggleTargetRules(rel, isUnsafe) {
13882
+ return isUnsafe ? addTargetRules(rel) : removeTargetRules(rel);
13883
+ }
13884
+
13885
+ while (i--) {
13886
+ node = nodes[i];
13887
+ rel = node.attr('rel');
13888
+ if (node.name === 'a') {
13889
+ node.attr('rel', toggleTargetRules(rel, node.attr('target') == '_blank'));
13890
+ }
13891
+ }
13892
+ });
13893
+ }
13894
+
13743
13895
  // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
13744
13896
  if (!settings.allow_html_in_named_anchor) {
13745
13897
  self.addAttributeFilter('id,name', function(nodes) {
@@ -14200,7 +14352,7 @@ define("tinymce/dom/Serializer", [
14200
14352
  "tinymce/text/Zwsp"
14201
14353
  ], function(DOMUtils, DomParser, SaxParser, Entities, Serializer, Node, Schema, Env, Tools, Zwsp) {
14202
14354
  var each = Tools.each, trim = Tools.trim;
14203
- var DOM = DOMUtils.DOM, tempAttrs = ["data-mce-selected"];
14355
+ var DOM = DOMUtils.DOM;
14204
14356
 
14205
14357
  /**
14206
14358
  * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
@@ -14238,7 +14390,7 @@ define("tinymce/dom/Serializer", [
14238
14390
  * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
14239
14391
  */
14240
14392
  return function(settings, editor) {
14241
- var dom, schema, htmlParser;
14393
+ var dom, schema, htmlParser, tempAttrs = ["data-mce-selected"];
14242
14394
 
14243
14395
  if (editor) {
14244
14396
  dom = editor.dom;
@@ -14256,18 +14408,8 @@ define("tinymce/dom/Serializer", [
14256
14408
  return html;
14257
14409
  }
14258
14410
 
14259
- /**
14260
- * Returns a trimmed version of the editor contents to be used for the undo level. This
14261
- * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
14262
- * remove the data-mce-selected attributes used for selection of objects and caret containers.
14263
- * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
14264
- * be removed by the serialization logic when you save.
14265
- *
14266
- * @private
14267
- * @return {String} HTML contents of the editor excluding some internal bogus elements.
14268
- */
14269
- function getTrimmedContent() {
14270
- var content = editor.getBody().innerHTML;
14411
+ function trimContent(html) {
14412
+ var content = html;
14271
14413
  var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
14272
14414
  var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema;
14273
14415
 
@@ -14292,6 +14434,20 @@ define("tinymce/dom/Serializer", [
14292
14434
  return trim(content);
14293
14435
  }
14294
14436
 
14437
+ /**
14438
+ * Returns a trimmed version of the editor contents to be used for the undo level. This
14439
+ * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
14440
+ * remove the data-mce-selected attributes used for selection of objects and caret containers.
14441
+ * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
14442
+ * be removed by the serialization logic when you save.
14443
+ *
14444
+ * @private
14445
+ * @return {String} HTML contents of the editor excluding some internal bogus elements.
14446
+ */
14447
+ function getTrimmedContent() {
14448
+ return trimContent(editor.getBody().innerHTML);
14449
+ }
14450
+
14295
14451
  function addTempAttr(name) {
14296
14452
  if (Tools.inArray(tempAttrs, name) === -1) {
14297
14453
  htmlParser.addAttributeFilter(name, function(nodes, name) {
@@ -14673,7 +14829,8 @@ define("tinymce/dom/Serializer", [
14673
14829
 
14674
14830
  // Internal
14675
14831
  trimHtml: trimHtml,
14676
- getTrimmedContent: getTrimmedContent
14832
+ getTrimmedContent: getTrimmedContent,
14833
+ trimContent: trimContent
14677
14834
  };
14678
14835
  };
14679
14836
  });
@@ -15964,13 +16121,17 @@ define("tinymce/util/Fun", [], function() {
15964
16121
  };
15965
16122
  }
15966
16123
 
16124
+ function noop() {
16125
+ }
16126
+
15967
16127
  return {
15968
16128
  constant: constant,
15969
16129
  negate: negate,
15970
16130
  and: and,
15971
16131
  or: or,
15972
16132
  curry: curry,
15973
- compose: compose
16133
+ compose: compose,
16134
+ noop: noop
15974
16135
  };
15975
16136
  });
15976
16137
 
@@ -16937,8 +17098,9 @@ define("tinymce/dom/BookmarkManager", [
16937
17098
  "tinymce/caret/CaretContainer",
16938
17099
  "tinymce/caret/CaretBookmark",
16939
17100
  "tinymce/caret/CaretPosition",
16940
- "tinymce/dom/NodeType"
16941
- ], function(Env, Tools, CaretContainer, CaretBookmark, CaretPosition, NodeType) {
17101
+ "tinymce/dom/NodeType",
17102
+ "tinymce/dom/RangeUtils"
17103
+ ], function(Env, Tools, CaretContainer, CaretBookmark, CaretPosition, NodeType, RangeUtils) {
16942
17104
  var isContentEditableFalse = NodeType.isContentEditableFalse;
16943
17105
 
16944
17106
  /**
@@ -17055,9 +17217,16 @@ define("tinymce/dom/BookmarkManager", [
17055
17217
  }
17056
17218
 
17057
17219
  function findAdjacentContentEditableFalseElm(rng) {
17058
- function findSibling(node) {
17220
+ function findSibling(node, offset) {
17059
17221
  var sibling;
17060
17222
 
17223
+ if (NodeType.isElement(node)) {
17224
+ node = RangeUtils.getNode(node, offset);
17225
+ if (isContentEditableFalse(node)) {
17226
+ return node;
17227
+ }
17228
+ }
17229
+
17061
17230
  if (CaretContainer.isCaretContainer(node)) {
17062
17231
  if (NodeType.isText(node) && CaretContainer.isCaretContainerBlock(node)) {
17063
17232
  node = node.parentNode;
@@ -17075,7 +17244,7 @@ define("tinymce/dom/BookmarkManager", [
17075
17244
  }
17076
17245
  }
17077
17246
 
17078
- return findSibling(rng.startContainer) || findSibling(rng.endContainer);
17247
+ return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset);
17079
17248
  }
17080
17249
 
17081
17250
  if (type == 2) {
@@ -17875,6 +18044,10 @@ define("tinymce/dom/Selection", [
17875
18044
 
17876
18045
  doc = self.win.document;
17877
18046
 
18047
+ if (typeof doc === 'undefined' || doc === null) {
18048
+ return null;
18049
+ }
18050
+
17878
18051
  // Use last rng passed from FocusManager if it's available this enables
17879
18052
  // calls to editor.selection.getStart() to work when caret focus is lost on IE
17880
18053
  if (!w3c && self.lastFocusBookmark) {
@@ -18025,6 +18198,8 @@ define("tinymce/dom/Selection", [
18025
18198
  }
18026
18199
  }
18027
18200
  }
18201
+
18202
+ self.editor.fire('AfterSetSelectionRange', {range: rng});
18028
18203
  } else {
18029
18204
  // Is W3C Range fake range on IE
18030
18205
  if (rng.cloneRange) {
@@ -18537,12 +18712,179 @@ define("tinymce/dom/ElementUtils", [
18537
18712
  * @class tinymce.fmt.Preview
18538
18713
  */
18539
18714
  define("tinymce/fmt/Preview", [
18540
- "tinymce/util/Tools"
18541
- ], function(Tools) {
18715
+ "tinymce/dom/DOMUtils",
18716
+ "tinymce/util/Tools",
18717
+ "tinymce/html/Schema"
18718
+ ], function(DOMUtils, Tools, Schema) {
18542
18719
  var each = Tools.each;
18720
+ var dom = DOMUtils.DOM;
18721
+
18722
+ function parsedSelectorToHtml(ancestry, editor) {
18723
+ var elm, item, fragment;
18724
+ var schema = editor && editor.schema || new Schema({});
18725
+
18726
+ function decorate(elm, item) {
18727
+ if (item.classes.length) {
18728
+ dom.addClass(elm, item.classes.join(' '));
18729
+ }
18730
+ dom.setAttribs(elm, item.attrs);
18731
+ }
18732
+
18733
+ function createElement(sItem) {
18734
+ var elm;
18735
+
18736
+ item = typeof sItem === 'string' ? {
18737
+ name: sItem,
18738
+ classes: [],
18739
+ attrs: {}
18740
+ } : sItem;
18741
+
18742
+ elm = dom.create(item.name);
18743
+ decorate(elm, item);
18744
+ return elm;
18745
+ }
18746
+
18747
+ function getRequiredParent(elm, candidate) {
18748
+ var name = typeof elm !== 'string' ? elm.nodeName.toLowerCase() : elm;
18749
+ var elmRule = schema.getElementRule(name);
18750
+ var parentsRequired = elmRule.parentsRequired;
18751
+
18752
+ if (parentsRequired && parentsRequired.length) {
18753
+ return candidate && Tools.inArray(parentsRequired, candidate) !== -1 ? candidate : parentsRequired[0];
18754
+ } else {
18755
+ return false;
18756
+ }
18757
+ }
18758
+
18759
+ function wrapInHtml(elm, ancestry, siblings) {
18760
+ var parent, parentCandidate, parentRequired;
18761
+ var ancestor = ancestry.length && ancestry[0];
18762
+ var ancestorName = ancestor && ancestor.name;
18763
+
18764
+ parentRequired = getRequiredParent(elm, ancestorName);
18765
+
18766
+ if (parentRequired) {
18767
+ if (ancestorName == parentRequired) {
18768
+ parentCandidate = ancestry[0];
18769
+ ancestry = ancestry.slice(1);
18770
+ } else {
18771
+ parentCandidate = parentRequired;
18772
+ }
18773
+ } else if (ancestor) {
18774
+ parentCandidate = ancestry[0];
18775
+ ancestry = ancestry.slice(1);
18776
+ } else if (!siblings) {
18777
+ return elm;
18778
+ }
18779
+
18780
+ if (parentCandidate) {
18781
+ parent = createElement(parentCandidate);
18782
+ parent.appendChild(elm);
18783
+ }
18784
+
18785
+ if (siblings) {
18786
+ if (!parent) {
18787
+ // if no more ancestry, wrap in generic div
18788
+ parent = dom.create('div');
18789
+ parent.appendChild(elm);
18790
+ }
18791
+
18792
+ Tools.each(siblings, function(sibling) {
18793
+ var siblingElm = createElement(sibling);
18794
+ parent.insertBefore(siblingElm, elm);
18795
+ });
18796
+ }
18797
+
18798
+ return wrapInHtml(parent, ancestry, parentCandidate && parentCandidate.siblings);
18799
+ }
18800
+
18801
+ if (ancestry && ancestry.length) {
18802
+ item = ancestry[0];
18803
+ elm = createElement(item);
18804
+ fragment = dom.create('div');
18805
+ fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), item.siblings));
18806
+ return fragment;
18807
+ } else {
18808
+ return '';
18809
+ }
18810
+ }
18811
+
18812
+
18813
+ function selectorToHtml(selector, editor) {
18814
+ return parsedSelectorToHtml(parseSelector(selector, editor));
18815
+ }
18816
+
18817
+
18818
+ function parseSelectorItem(item) {
18819
+ var tagName;
18820
+ var obj = {
18821
+ classes: [],
18822
+ attrs: {}
18823
+ };
18824
+
18825
+ item = obj.selector = Tools.trim(item);
18826
+
18827
+ // matching IDs, CLASSes, ATTRIBUTES and PSEUDOs
18828
+ tagName = item.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g, function($0, $1, $2, $3, $4) {
18829
+ switch ($1) {
18830
+ case '#':
18831
+ obj.attrs.id = $2;
18832
+ break;
18833
+
18834
+ case '.':
18835
+ obj.classes.push($2);
18836
+ break;
18837
+
18838
+ case ':':
18839
+ if (Tools.inArray('checked disabled enabled read-only required'.split(' '), $2) !== -1) {
18840
+ obj.attrs[$2] = $2;
18841
+ }
18842
+ break;
18843
+ }
18844
+
18845
+ // atribute matched
18846
+ if ($3 == '[') {
18847
+ var m = $4.match(/([\w\-]+)(?:\=\"([^\"]+))?/);
18848
+ if (m) {
18849
+ obj.attrs[m[1]] = m[2];
18850
+ }
18851
+ }
18852
+
18853
+ return '';
18854
+ });
18855
+
18856
+ obj.name = tagName || 'div';
18857
+ return obj;
18858
+ }
18859
+
18860
+
18861
+ function parseSelector(selector) {
18862
+ if (!selector || typeof selector !== 'string') {
18863
+ return [];
18864
+ }
18865
+
18866
+ // take into account only first one
18867
+ selector = selector.split(/\s*,\s*/)[0];
18868
+
18869
+ // tighten
18870
+ selector = selector.replace(/\s*(~\+|~|\+|>)\s*/g, '$1');
18871
+
18872
+ // split either on > or on space, but not the one inside brackets
18873
+ return Tools.map(selector.split(/(?:>|\s+(?![^\[\]]+\]))/), function(item) {
18874
+ // process each sibling selector separately
18875
+ var siblings = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem);
18876
+ var obj = siblings.pop(); // the last one is our real target
18877
+
18878
+ if (siblings.length) {
18879
+ obj.siblings = siblings;
18880
+ }
18881
+ return obj;
18882
+ }).reverse();
18883
+ }
18884
+
18543
18885
 
18544
18886
  function getCssText(editor, format) {
18545
- var name, previewElm, dom = editor.dom;
18887
+ var name, previewFrag, previewElm, items;
18546
18888
  var previewCss = '', parentFontSize, previewStyles;
18547
18889
 
18548
18890
  previewStyles = editor.settings.preview_styles;
@@ -18553,7 +18895,7 @@ define("tinymce/fmt/Preview", [
18553
18895
  }
18554
18896
 
18555
18897
  // Default preview
18556
- if (!previewStyles) {
18898
+ if (typeof previewStyles !== 'string') {
18557
18899
  previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
18558
18900
  'text-transform color background-color border border-radius outline text-shadow';
18559
18901
  }
@@ -18573,8 +18915,29 @@ define("tinymce/fmt/Preview", [
18573
18915
  format = format[0];
18574
18916
  }
18575
18917
 
18918
+ // Format specific preview override
18919
+ // TODO: This should probably be further reduced by the previewStyles option
18920
+ if ('preview' in format) {
18921
+ previewStyles = format.preview;
18922
+ if (previewStyles === false) {
18923
+ return '';
18924
+ }
18925
+ }
18926
+
18576
18927
  name = format.block || format.inline || 'span';
18577
- previewElm = dom.create(name);
18928
+
18929
+ items = parseSelector(format.selector);
18930
+ if (items.length) {
18931
+ if (!items[0].name) { // e.g. something like ul > .someClass was provided
18932
+ items[0].name = name;
18933
+ }
18934
+ name = format.selector;
18935
+ previewFrag = parsedSelectorToHtml(items);
18936
+ } else {
18937
+ previewFrag = parsedSelectorToHtml([name]);
18938
+ }
18939
+
18940
+ previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild;
18578
18941
 
18579
18942
  // Add format styles to preview element
18580
18943
  each(format.styles, function(value, name) {
@@ -18606,8 +18969,8 @@ define("tinymce/fmt/Preview", [
18606
18969
  editor.fire('PreviewFormats');
18607
18970
 
18608
18971
  // Add the previewElm outside the visual area
18609
- dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
18610
- editor.getBody().appendChild(previewElm);
18972
+ dom.setStyles(previewFrag, {position: 'absolute', left: -0xFFFF});
18973
+ editor.getBody().appendChild(previewFrag);
18611
18974
 
18612
18975
  // Get parent container font size so we can compute px values out of em/% for older IE:s
18613
18976
  parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
@@ -18659,13 +19022,15 @@ define("tinymce/fmt/Preview", [
18659
19022
 
18660
19023
  //previewCss += 'line-height:normal';
18661
19024
 
18662
- dom.remove(previewElm);
19025
+ dom.remove(previewFrag);
18663
19026
 
18664
19027
  return previewCss;
18665
19028
  }
18666
19029
 
18667
19030
  return {
18668
- getCssText: getCssText
19031
+ getCssText: getCssText,
19032
+ parseSelector: parseSelector,
19033
+ selectorToHtml: selectorToHtml
18669
19034
  };
18670
19035
  });
18671
19036
 
@@ -18692,7 +19057,7 @@ define("tinymce/fmt/Hooks", [
18692
19057
  "tinymce/dom/NodeType",
18693
19058
  "tinymce/dom/DomQuery"
18694
19059
  ], function(Arr, NodeType, $) {
18695
- var postProcessHooks = [], filter = Arr.filter, each = Arr.each;
19060
+ var postProcessHooks = {}, filter = Arr.filter, each = Arr.each;
18696
19061
 
18697
19062
  function addPostProcessHook(name, hook) {
18698
19063
  var hooks = postProcessHooks[name];
@@ -18843,16 +19208,23 @@ define("tinymce/Formatter", [
18843
19208
  ],
18844
19209
 
18845
19210
  alignleft: [
18846
- {selector: 'figure.image', collapsed: false, classes: 'align-left', ceFalseOverride: true},
19211
+ {
19212
+ selector: 'figure.image',
19213
+ collapsed: false,
19214
+ classes: 'align-left',
19215
+ ceFalseOverride: true,
19216
+ preview: 'font-family font-size'
19217
+ },
18847
19218
  {
18848
19219
  selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li',
18849
19220
  styles: {
18850
19221
  textAlign: 'left'
18851
19222
  },
18852
19223
  inherit: false,
19224
+ preview: false,
18853
19225
  defaultBlock: 'div'
18854
19226
  },
18855
- {selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
19227
+ {selector: 'img,table', collapsed: false, styles: {'float': 'left'}, preview: 'font-family font-size'}
18856
19228
  ],
18857
19229
 
18858
19230
  aligncenter: [
@@ -18862,24 +19234,62 @@ define("tinymce/Formatter", [
18862
19234
  textAlign: 'center'
18863
19235
  },
18864
19236
  inherit: false,
19237
+ preview: false,
18865
19238
  defaultBlock: 'div'
18866
19239
  },
18867
- {selector: 'figure.image', collapsed: false, classes: 'align-center', ceFalseOverride: true},
18868
- {selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
18869
- {selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
19240
+ {
19241
+ selector: 'figure.image',
19242
+ collapsed: false,
19243
+ classes: 'align-center',
19244
+ ceFalseOverride: true,
19245
+ preview: 'font-family font-size'
19246
+ },
19247
+ {
19248
+ selector: 'img',
19249
+ collapsed: false,
19250
+ styles: {
19251
+ display: 'block',
19252
+ marginLeft: 'auto',
19253
+ marginRight: 'auto'
19254
+ },
19255
+ preview: false
19256
+ },
19257
+ {
19258
+ selector: 'table',
19259
+ collapsed: false,
19260
+ styles: {
19261
+ marginLeft: 'auto',
19262
+ marginRight: 'auto'
19263
+ },
19264
+ preview: 'font-family font-size'
19265
+ }
18870
19266
  ],
18871
19267
 
18872
19268
  alignright: [
18873
- {selector: 'figure.image', collapsed: false, classes: 'align-right', ceFalseOverride: true},
19269
+ {
19270
+ selector: 'figure.image',
19271
+ collapsed: false,
19272
+ classes: 'align-right',
19273
+ ceFalseOverride: true,
19274
+ preview: 'font-family font-size'
19275
+ },
18874
19276
  {
18875
19277
  selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li',
18876
19278
  styles: {
18877
19279
  textAlign: 'right'
18878
19280
  },
18879
19281
  inherit: false,
19282
+ preview: 'font-family font-size',
18880
19283
  defaultBlock: 'div'
18881
19284
  },
18882
- {selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
19285
+ {
19286
+ selector: 'img,table',
19287
+ collapsed: false,
19288
+ styles: {
19289
+ 'float': 'right'
19290
+ },
19291
+ preview: 'font-family font-size'
19292
+ }
18883
19293
  ],
18884
19294
 
18885
19295
  alignjustify: [
@@ -18889,7 +19299,8 @@ define("tinymce/Formatter", [
18889
19299
  textAlign: 'justify'
18890
19300
  },
18891
19301
  inherit: false,
18892
- defaultBlock: 'div'
19302
+ defaultBlock: 'div',
19303
+ preview: 'font-family font-size'
18893
19304
  }
18894
19305
  ],
18895
19306
 
@@ -19139,6 +19550,30 @@ define("tinymce/Formatter", [
19139
19550
  }
19140
19551
  }
19141
19552
 
19553
+ function applyNodeStyle(formatList, node) {
19554
+ var found = false;
19555
+
19556
+ if (!format.selector) {
19557
+ return false;
19558
+ }
19559
+
19560
+ // Look for matching formats
19561
+ each(formatList, function(format) {
19562
+ // Check collapsed state if it exists
19563
+ if ('collapsed' in format && format.collapsed !== isCollapsed) {
19564
+ return;
19565
+ }
19566
+
19567
+ if (dom.is(node, format.selector) && !isCaretNode(node)) {
19568
+ setElementFormat(node, format);
19569
+ found = true;
19570
+ return false;
19571
+ }
19572
+ });
19573
+
19574
+ return found;
19575
+ }
19576
+
19142
19577
  // This converts: <p>[a</p><p>]b</p> -> <p>[a]</p><p>b</p>
19143
19578
  function adjustSelectionToVisibleSelection() {
19144
19579
  function findSelectionEnd(start, end) {
@@ -19185,7 +19620,7 @@ define("tinymce/Formatter", [
19185
19620
  * Process a list of nodes wrap them.
19186
19621
  */
19187
19622
  function process(node) {
19188
- var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
19623
+ var nodeName, parentName, hasContentEditableState, lastContentEditable;
19189
19624
 
19190
19625
  lastContentEditable = contentEditable;
19191
19626
  nodeName = node.nodeName.toLowerCase();
@@ -19229,19 +19664,7 @@ define("tinymce/Formatter", [
19229
19664
 
19230
19665
  // Handle selector patterns
19231
19666
  if (format.selector) {
19232
- // Look for matching formats
19233
- each(formatList, function(format) {
19234
- // Check collapsed state if it exists
19235
- if ('collapsed' in format && format.collapsed !== isCollapsed) {
19236
- return;
19237
- }
19238
-
19239
- if (dom.is(node, format.selector) && !isCaretNode(node)) {
19240
- setElementFormat(node, format);
19241
- found = true;
19242
- return false;
19243
- }
19244
- });
19667
+ var found = applyNodeStyle(formatList, node);
19245
19668
 
19246
19669
  // Continue processing if a selector match wasn't found and a inline element is defined
19247
19670
  if (!format.inline || found) {
@@ -19411,10 +19834,12 @@ define("tinymce/Formatter", [
19411
19834
  if (format) {
19412
19835
  if (node) {
19413
19836
  if (node.nodeType) {
19414
- rng = dom.createRng();
19415
- rng.setStartBefore(node);
19416
- rng.setEndAfter(node);
19417
- applyRngStyle(expandRng(rng, formatList), null, true);
19837
+ if (!applyNodeStyle(formatList, node)) {
19838
+ rng = dom.createRng();
19839
+ rng.setStartBefore(node);
19840
+ rng.setEndAfter(node);
19841
+ applyRngStyle(expandRng(rng, formatList), null, true);
19842
+ }
19418
19843
  } else {
19419
19844
  applyRngStyle(node, null, true);
19420
19845
  }
@@ -21175,6 +21600,344 @@ define("tinymce/Formatter", [
21175
21600
  };
21176
21601
  });
21177
21602
 
21603
+ // Included from: js/tinymce/classes/undo/Diff.js
21604
+
21605
+ /**
21606
+ * Diff.js
21607
+ *
21608
+ * Released under LGPL License.
21609
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
21610
+ *
21611
+ * License: http://www.tinymce.com/license
21612
+ * Contributing: http://www.tinymce.com/contributing
21613
+ */
21614
+
21615
+ /**
21616
+ * JS Implementation of the O(ND) Difference Algorithm by Eugene W. Myers.
21617
+ *
21618
+ * @class tinymce.undo.Diff
21619
+ * @private
21620
+ */
21621
+ define("tinymce/undo/Diff", [
21622
+ ], function () {
21623
+ var KEEP = 0, INSERT = 1, DELETE = 2;
21624
+
21625
+ var diff = function (left, right) {
21626
+ var size = left.length + right.length + 2;
21627
+ var vDown = new Array(size);
21628
+ var vUp = new Array(size);
21629
+
21630
+ var snake = function (start, end, diag) {
21631
+ return {
21632
+ start: start,
21633
+ end: end,
21634
+ diag: diag
21635
+ };
21636
+ };
21637
+
21638
+ var buildScript = function (start1, end1, start2, end2, script) {
21639
+ var middle = getMiddleSnake(start1, end1, start2, end2);
21640
+
21641
+ if (middle === null || middle.start === end1 && middle.diag === end1 - end2 ||
21642
+ middle.end === start1 && middle.diag === start1 - start2) {
21643
+ var i = start1;
21644
+ var j = start2;
21645
+ while (i < end1 || j < end2) {
21646
+ if (i < end1 && j < end2 && left[i] === right[j]) {
21647
+ script.push([KEEP, left[i]]);
21648
+ ++i;
21649
+ ++j;
21650
+ } else {
21651
+ if (end1 - start1 > end2 - start2) {
21652
+ script.push([DELETE, left[i]]);
21653
+ ++i;
21654
+ } else {
21655
+ script.push([INSERT, right[j]]);
21656
+ ++j;
21657
+ }
21658
+ }
21659
+ }
21660
+ } else {
21661
+ buildScript(start1, middle.start, start2, middle.start - middle.diag, script);
21662
+ for (var i2 = middle.start; i2 < middle.end; ++i2) {
21663
+ script.push([KEEP, left[i2]]);
21664
+ }
21665
+ buildScript(middle.end, end1, middle.end - middle.diag, end2, script);
21666
+ }
21667
+ };
21668
+
21669
+ var buildSnake = function (start, diag, end1, end2) {
21670
+ var end = start;
21671
+ while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) {
21672
+ ++end;
21673
+ }
21674
+ return snake(start, end, diag);
21675
+ };
21676
+
21677
+ var getMiddleSnake = function (start1, end1, start2, end2) {
21678
+ // Myers Algorithm
21679
+ // Initialisations
21680
+ var m = end1 - start1;
21681
+ var n = end2 - start2;
21682
+ if (m === 0 || n === 0) {
21683
+ return null;
21684
+ }
21685
+
21686
+ var delta = m - n;
21687
+ var sum = n + m;
21688
+ var offset = (sum % 2 === 0 ? sum : sum + 1) / 2;
21689
+ vDown[1 + offset] = start1;
21690
+ vUp[1 + offset] = end1 + 1;
21691
+
21692
+ for (var d = 0; d <= offset; ++d) {
21693
+ // Down
21694
+ for (var k = -d; k <= d; k += 2) {
21695
+ // First step
21696
+
21697
+ var i = k + offset;
21698
+ if (k === -d || k != d && vDown[i - 1] < vDown[i + 1]) {
21699
+ vDown[i] = vDown[i + 1];
21700
+ } else {
21701
+ vDown[i] = vDown[i - 1] + 1;
21702
+ }
21703
+
21704
+ var x = vDown[i];
21705
+ var y = x - start1 + start2 - k;
21706
+
21707
+ while (x < end1 && y < end2 && left[x] === right[y]) {
21708
+ vDown[i] = ++x;
21709
+ ++y;
21710
+ }
21711
+ // Second step
21712
+ if (delta % 2 != 0 && delta - d <= k && k <= delta + d) {
21713
+ if (vUp[i - delta] <= vDown[i]) {
21714
+ return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2);
21715
+ }
21716
+ }
21717
+ }
21718
+
21719
+ // Up
21720
+ for (k = delta - d; k <= delta + d; k += 2) {
21721
+ // First step
21722
+ i = k + offset - delta;
21723
+ if (k === delta - d || k != delta + d && vUp[i + 1] <= vUp[i - 1]) {
21724
+ vUp[i] = vUp[i + 1] - 1;
21725
+ } else {
21726
+ vUp[i] = vUp[i - 1];
21727
+ }
21728
+
21729
+ x = vUp[i] - 1;
21730
+ y = x - start1 + start2 - k;
21731
+ while (x >= start1 && y >= start2 && left[x] === right[y]) {
21732
+ vUp[i] = x--;
21733
+ y--;
21734
+ }
21735
+ // Second step
21736
+ if (delta % 2 === 0 && -d <= k && k <= d) {
21737
+ if (vUp[i] <= vDown[i + delta]) {
21738
+ return buildSnake(vUp[i], k + start1 - start2, end1, end2);
21739
+ }
21740
+ }
21741
+ }
21742
+ }
21743
+ };
21744
+
21745
+ var script = [];
21746
+ buildScript(0, left.length, 0, right.length, script);
21747
+ return script;
21748
+ };
21749
+
21750
+ return {
21751
+ KEEP: KEEP,
21752
+ DELETE: DELETE,
21753
+ INSERT: INSERT,
21754
+ diff: diff
21755
+ };
21756
+ });
21757
+
21758
+ // Included from: js/tinymce/classes/undo/Fragments.js
21759
+
21760
+ /**
21761
+ * Fragments.js
21762
+ *
21763
+ * Released under LGPL License.
21764
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
21765
+ *
21766
+ * License: http://www.tinymce.com/license
21767
+ * Contributing: http://www.tinymce.com/contributing
21768
+ */
21769
+
21770
+ /**
21771
+ * This module reads and applies html fragments from/to dom nodes.
21772
+ *
21773
+ * @class tinymce.undo.Fragments
21774
+ * @private
21775
+ */
21776
+ define("tinymce/undo/Fragments", [
21777
+ "tinymce/util/Arr",
21778
+ "tinymce/html/Entities",
21779
+ "tinymce/undo/Diff"
21780
+ ], function (Arr, Entities, Diff) {
21781
+ var getOuterHtml = function (elm) {
21782
+ if (elm.nodeType === 1) {
21783
+ return elm.outerHTML;
21784
+ } else if (elm.nodeType === 3) {
21785
+ return Entities.encodeRaw(elm.data, false);
21786
+ } else if (elm.nodeType === 8) {
21787
+ return '<!--' + elm.data + '-->';
21788
+ }
21789
+
21790
+ return '';
21791
+ };
21792
+
21793
+ var createFragment = function(html) {
21794
+ var frag, node, container;
21795
+
21796
+ container = document.createElement("div");
21797
+ frag = document.createDocumentFragment();
21798
+
21799
+ if (html) {
21800
+ container.innerHTML = html;
21801
+ }
21802
+
21803
+ while ((node = container.firstChild)) {
21804
+ frag.appendChild(node);
21805
+ }
21806
+
21807
+ return frag;
21808
+ };
21809
+
21810
+ var insertAt = function (elm, html, index) {
21811
+ var fragment = createFragment(html);
21812
+ if (elm.hasChildNodes() && index < elm.childNodes.length) {
21813
+ var target = elm.childNodes[index];
21814
+ target.parentNode.insertBefore(fragment, target);
21815
+ } else {
21816
+ elm.appendChild(fragment);
21817
+ }
21818
+ };
21819
+
21820
+ var removeAt = function (elm, index) {
21821
+ if (elm.hasChildNodes() && index < elm.childNodes.length) {
21822
+ var target = elm.childNodes[index];
21823
+ target.parentNode.removeChild(target);
21824
+ }
21825
+ };
21826
+
21827
+ var applyDiff = function (diff, elm) {
21828
+ var index = 0;
21829
+ Arr.each(diff, function (action) {
21830
+ if (action[0] === Diff.KEEP) {
21831
+ index++;
21832
+ } else if (action[0] === Diff.INSERT) {
21833
+ insertAt(elm, action[1], index);
21834
+ index++;
21835
+ } else if (action[0] === Diff.DELETE) {
21836
+ removeAt(elm, index);
21837
+ }
21838
+ });
21839
+ };
21840
+
21841
+ var read = function (elm) {
21842
+ return Arr.map(elm.childNodes, getOuterHtml);
21843
+ };
21844
+
21845
+ var write = function (fragments, elm) {
21846
+ var currentFragments = Arr.map(elm.childNodes, getOuterHtml);
21847
+ applyDiff(Diff.diff(currentFragments, fragments), elm);
21848
+ return elm;
21849
+ };
21850
+
21851
+ return {
21852
+ read: read,
21853
+ write: write
21854
+ };
21855
+ });
21856
+
21857
+ // Included from: js/tinymce/classes/undo/Levels.js
21858
+
21859
+ /**
21860
+ * Levels.js
21861
+ *
21862
+ * Released under LGPL License.
21863
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
21864
+ *
21865
+ * License: http://www.tinymce.com/license
21866
+ * Contributing: http://www.tinymce.com/contributing
21867
+ */
21868
+
21869
+ /**
21870
+ * This module handles getting/setting undo levels to/from editor instances.
21871
+ *
21872
+ * @class tinymce.undo.Levels
21873
+ * @private
21874
+ */
21875
+ define("tinymce/undo/Levels", [
21876
+ "tinymce/util/Arr",
21877
+ "tinymce/undo/Fragments"
21878
+ ], function (Arr, Fragments) {
21879
+ var hasIframes = function (html) {
21880
+ return html.indexOf('</iframe>') !== -1;
21881
+ };
21882
+
21883
+ var createFragmentedLevel = function (fragments) {
21884
+ return {
21885
+ type: 'fragmented',
21886
+ fragments: fragments,
21887
+ content: '',
21888
+ bookmark: null,
21889
+ beforeBookmark: null
21890
+ };
21891
+ };
21892
+
21893
+ var createCompleteLevel = function (content) {
21894
+ return {
21895
+ type: 'complete',
21896
+ fragments: null,
21897
+ content: content,
21898
+ bookmark: null,
21899
+ beforeBookmark: null
21900
+ };
21901
+ };
21902
+
21903
+ var createFromEditor = function (editor) {
21904
+ var fragments, content;
21905
+
21906
+ fragments = Fragments.read(editor.getBody());
21907
+ content = Arr.map(fragments, function (html) {
21908
+ return editor.serializer.trimContent(html);
21909
+ }).join('');
21910
+
21911
+ return hasIframes(content) ? createFragmentedLevel(fragments) : createCompleteLevel(content);
21912
+ };
21913
+
21914
+ var applyToEditor = function (editor, level, before) {
21915
+ if (level.type === 'fragmented') {
21916
+ Fragments.write(level.fragments, editor.getBody());
21917
+ } else {
21918
+ editor.setContent(level.content, {format: 'raw'});
21919
+ }
21920
+
21921
+ editor.selection.moveToBookmark(before ? level.beforeBookmark : level.bookmark);
21922
+ };
21923
+
21924
+ var getLevelContent = function (level) {
21925
+ return level.type === 'fragmented' ? level.fragments.join('') : level.content;
21926
+ };
21927
+
21928
+ var isEq = function (level1, level2) {
21929
+ return getLevelContent(level1) === getLevelContent(level2);
21930
+ };
21931
+
21932
+ return {
21933
+ createFragmentedLevel: createFragmentedLevel,
21934
+ createCompleteLevel: createCompleteLevel,
21935
+ createFromEditor: createFromEditor,
21936
+ applyToEditor: applyToEditor,
21937
+ isEq: isEq
21938
+ };
21939
+ });
21940
+
21178
21941
  // Included from: js/tinymce/classes/UndoManager.js
21179
21942
 
21180
21943
  /**
@@ -21194,15 +21957,13 @@ define("tinymce/Formatter", [
21194
21957
  */
21195
21958
  define("tinymce/UndoManager", [
21196
21959
  "tinymce/util/VK",
21960
+ "tinymce/util/Tools",
21961
+ "tinymce/undo/Levels",
21197
21962
  "tinymce/Env"
21198
- ], function(VK, Env) {
21963
+ ], function(VK, Tools, Levels, Env) {
21199
21964
  return function(editor) {
21200
21965
  var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
21201
21966
 
21202
- function getContent() {
21203
- return editor.serializer.getTrimmedContent();
21204
- }
21205
-
21206
21967
  function setDirty(state) {
21207
21968
  editor.setDirty(state);
21208
21969
  }
@@ -21212,6 +21973,13 @@ define("tinymce/UndoManager", [
21212
21973
  self.add({}, e);
21213
21974
  }
21214
21975
 
21976
+ function endTyping() {
21977
+ if (self.typing) {
21978
+ self.typing = false;
21979
+ self.add();
21980
+ }
21981
+ }
21982
+
21215
21983
  // Add initial undo level when the editor is initialized
21216
21984
  editor.on('init', function() {
21217
21985
  self.add();
@@ -21221,7 +21989,8 @@ define("tinymce/UndoManager", [
21221
21989
  editor.on('BeforeExecCommand', function(e) {
21222
21990
  var cmd = e.command;
21223
21991
 
21224
- if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
21992
+ if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') {
21993
+ endTyping();
21225
21994
  self.beforeChange();
21226
21995
  }
21227
21996
  });
@@ -21230,7 +21999,7 @@ define("tinymce/UndoManager", [
21230
21999
  editor.on('ExecCommand', function(e) {
21231
22000
  var cmd = e.command;
21232
22001
 
21233
- if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
22002
+ if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') {
21234
22003
  addNonTypingUndoLevel(e);
21235
22004
  }
21236
22005
  });
@@ -21251,12 +22020,12 @@ define("tinymce/UndoManager", [
21251
22020
  return;
21252
22021
  }
21253
22022
 
21254
- if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
22023
+ if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45 || e.ctrlKey) {
21255
22024
  addNonTypingUndoLevel();
21256
22025
  editor.nodeChanged();
21257
22026
  }
21258
22027
 
21259
- if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
22028
+ if (keyCode === 46 || keyCode === 8 || (Env.mac && (keyCode === 91 || keyCode === 93))) {
21260
22029
  editor.nodeChanged();
21261
22030
  }
21262
22031
 
@@ -21264,7 +22033,7 @@ define("tinymce/UndoManager", [
21264
22033
  if (isFirstTypedCharacter && self.typing) {
21265
22034
  // Make it dirty if the content was changed after typing the first character
21266
22035
  if (!editor.isDirty()) {
21267
- setDirty(data[0] && getContent() != data[0].content);
22036
+ setDirty(data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0]));
21268
22037
 
21269
22038
  // Fire initial change event
21270
22039
  if (editor.isDirty()) {
@@ -21288,7 +22057,7 @@ define("tinymce/UndoManager", [
21288
22057
  }
21289
22058
 
21290
22059
  // Is character position keys left,right,up,down,home,end,pgdown,pgup,enter
21291
- if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
22060
+ if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45) {
21292
22061
  if (self.typing) {
21293
22062
  addNonTypingUndoLevel(e);
21294
22063
  }
@@ -21298,7 +22067,7 @@ define("tinymce/UndoManager", [
21298
22067
 
21299
22068
  // If key isn't Ctrl+Alt/AltGr
21300
22069
  var modKey = (e.ctrlKey && !e.altKey) || e.metaKey;
21301
- if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing && !modKey) {
22070
+ if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !self.typing && !modKey) {
21302
22071
  self.beforeChange();
21303
22072
  self.typing = true;
21304
22073
  self.add({}, e);
@@ -21356,10 +22125,11 @@ define("tinymce/UndoManager", [
21356
22125
  * @return {Object} Undo level that got added or null it a level wasn't needed.
21357
22126
  */
21358
22127
  add: function(level, event) {
21359
- var i, settings = editor.settings, lastLevel;
22128
+ var i, settings = editor.settings, lastLevel, currentLevel;
21360
22129
 
22130
+ currentLevel = Levels.createFromEditor(editor);
21361
22131
  level = level || {};
21362
- level.content = getContent();
22132
+ level = Tools.extend(level, currentLevel);
21363
22133
 
21364
22134
  if (locks || editor.removed) {
21365
22135
  return null;
@@ -21371,7 +22141,7 @@ define("tinymce/UndoManager", [
21371
22141
  }
21372
22142
 
21373
22143
  // Add undo level if needed
21374
- if (lastLevel && lastLevel.content == level.content) {
22144
+ if (lastLevel && Levels.isEq(lastLevel, level)) {
21375
22145
  return null;
21376
22146
  }
21377
22147
 
@@ -21431,11 +22201,8 @@ define("tinymce/UndoManager", [
21431
22201
 
21432
22202
  if (index > 0) {
21433
22203
  level = data[--index];
21434
-
21435
- editor.setContent(level.content, {format: 'raw'});
21436
- editor.selection.moveToBookmark(level.beforeBookmark);
22204
+ Levels.applyToEditor(editor, level, true);
21437
22205
  setDirty(true);
21438
-
21439
22206
  editor.fire('undo', {level: level});
21440
22207
  }
21441
22208
 
@@ -21453,11 +22220,8 @@ define("tinymce/UndoManager", [
21453
22220
 
21454
22221
  if (index < data.length - 1) {
21455
22222
  level = data[++index];
21456
-
21457
- editor.setContent(level.content, {format: 'raw'});
21458
- editor.selection.moveToBookmark(level.bookmark);
22223
+ Levels.applyToEditor(editor, level, false);
21459
22224
  setDirty(true);
21460
-
21461
22225
  editor.fire('redo', {level: level});
21462
22226
  }
21463
22227
 
@@ -21485,7 +22249,7 @@ define("tinymce/UndoManager", [
21485
22249
  */
21486
22250
  hasUndo: function() {
21487
22251
  // Has undo levels or typing and content isn't the same as the initial level
21488
- return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
22252
+ return index > 0 || (self.typing && data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0]));
21489
22253
  },
21490
22254
 
21491
22255
  /**
@@ -21495,7 +22259,7 @@ define("tinymce/UndoManager", [
21495
22259
  * @return {Boolean} true/false if the undo manager has any redo levels.
21496
22260
  */
21497
22261
  hasRedo: function() {
21498
- return index < data.length - 1 && !this.typing;
22262
+ return index < data.length - 1 && !self.typing;
21499
22263
  },
21500
22264
 
21501
22265
  /**
@@ -21509,6 +22273,7 @@ define("tinymce/UndoManager", [
21509
22273
  * @return {Object} Undo level that got added or null it a level wasn't needed.
21510
22274
  */
21511
22275
  transact: function(callback) {
22276
+ endTyping();
21512
22277
  self.beforeChange();
21513
22278
 
21514
22279
  try {
@@ -21536,8 +22301,7 @@ define("tinymce/UndoManager", [
21536
22301
  if (self.transact(callback1)) {
21537
22302
  bookmark = data[index].bookmark;
21538
22303
  lastLevel = data[index - 1];
21539
- editor.setContent(lastLevel.content, {format: 'raw'});
21540
- editor.selection.moveToBookmark(lastLevel.beforeBookmark);
22304
+ Levels.applyToEditor(editor, lastLevel, true);
21541
22305
 
21542
22306
  if (self.transact(callback2)) {
21543
22307
  data[index - 1].beforeBookmark = bookmark;
@@ -21571,8 +22335,9 @@ define("tinymce/UndoManager", [
21571
22335
  define("tinymce/EnterKey", [
21572
22336
  "tinymce/dom/TreeWalker",
21573
22337
  "tinymce/dom/RangeUtils",
22338
+ "tinymce/caret/CaretContainer",
21574
22339
  "tinymce/Env"
21575
- ], function(TreeWalker, RangeUtils, Env) {
22340
+ ], function(TreeWalker, RangeUtils, CaretContainer, Env) {
21576
22341
  var isIE = Env.ie && Env.ie < 11;
21577
22342
 
21578
22343
  return function(editor) {
@@ -22141,6 +22906,11 @@ define("tinymce/EnterKey", [
22141
22906
  parentBlockName = containerBlockName;
22142
22907
  }
22143
22908
 
22909
+ if (editor.undoManager.typing) {
22910
+ editor.undoManager.typing = false;
22911
+ editor.undoManager.add();
22912
+ }
22913
+
22144
22914
  // Handle enter in list item
22145
22915
  if (/^(LI|DT|DD)$/.test(parentBlockName)) {
22146
22916
  if (!newBlockName && shiftKey) {
@@ -22178,7 +22948,9 @@ define("tinymce/EnterKey", [
22178
22948
  newBlockName = newBlockName || 'P';
22179
22949
 
22180
22950
  // Insert new block before/after the parent block depending on caret location
22181
- if (isCaretAtStartOrEndOfBlock()) {
22951
+ if (CaretContainer.isCaretContainerBlock(parentBlock)) {
22952
+ newBlock = CaretContainer.showCaretContainerBlock(parentBlock);
22953
+ } else if (isCaretAtStartOrEndOfBlock()) {
22182
22954
  insertNewBlockAfter();
22183
22955
  } else if (isCaretAtStartOrEndOfBlock(true)) {
22184
22956
  // Insert new block before
@@ -22216,6 +22988,7 @@ define("tinymce/EnterKey", [
22216
22988
  // Allow custom handling of new blocks
22217
22989
  editor.fire('NewBlock', {newBlock: newBlock});
22218
22990
 
22991
+ undoManager.typing = false;
22219
22992
  undoManager.add();
22220
22993
  }
22221
22994
 
@@ -22400,6 +23173,7 @@ define("tinymce/caret/CaretUtils", [
22400
23173
  isContentEditableFalse = NodeType.isContentEditableFalse,
22401
23174
  isBlockLike = NodeType.matchStyleValues('display', 'block table table-cell table-caption'),
22402
23175
  isCaretContainer = CaretContainer.isCaretContainer,
23176
+ isCaretContainerBlock = CaretContainer.isCaretContainerBlock,
22403
23177
  curry = Fun.curry,
22404
23178
  isElement = NodeType.isElement,
22405
23179
  isCaretCandidate = CaretCandidate.isCaretCandidate;
@@ -22412,18 +23186,30 @@ define("tinymce/caret/CaretUtils", [
22412
23186
  return direction < 0;
22413
23187
  }
22414
23188
 
23189
+ function skipCaretContainers(walk, shallow) {
23190
+ var node;
23191
+
23192
+ while ((node = walk(shallow))) {
23193
+ if (!isCaretContainerBlock(node)) {
23194
+ return node;
23195
+ }
23196
+ }
23197
+
23198
+ return null;
23199
+ }
23200
+
22415
23201
  function findNode(node, direction, predicateFn, rootNode, shallow) {
22416
23202
  var walker = new TreeWalker(node, rootNode);
22417
23203
 
22418
23204
  if (isBackwards(direction)) {
22419
- if (isContentEditableFalse(node)) {
22420
- node = walker.prev(true);
23205
+ if (isContentEditableFalse(node) || isCaretContainerBlock(node)) {
23206
+ node = skipCaretContainers(walker.prev, true);
22421
23207
  if (predicateFn(node)) {
22422
23208
  return node;
22423
23209
  }
22424
23210
  }
22425
23211
 
22426
- while ((node = walker.prev(shallow))) {
23212
+ while ((node = skipCaretContainers(walker.prev, shallow))) {
22427
23213
  if (predicateFn(node)) {
22428
23214
  return node;
22429
23215
  }
@@ -22431,14 +23217,14 @@ define("tinymce/caret/CaretUtils", [
22431
23217
  }
22432
23218
 
22433
23219
  if (isForwards(direction)) {
22434
- if (isContentEditableFalse(node)) {
22435
- node = walker.next(true);
23220
+ if (isContentEditableFalse(node) || isCaretContainerBlock(node)) {
23221
+ node = skipCaretContainers(walker.next, true);
22436
23222
  if (predicateFn(node)) {
22437
23223
  return node;
22438
23224
  }
22439
23225
  }
22440
23226
 
22441
- while ((node = walker.next(shallow))) {
23227
+ while ((node = skipCaretContainers(walker.next, shallow))) {
22442
23228
  if (predicateFn(node)) {
22443
23229
  return node;
22444
23230
  }
@@ -23803,8 +24589,8 @@ define("tinymce/EditorCommands", [
23803
24589
  // Override unlink command
23804
24590
  unlink: function() {
23805
24591
  if (selection.isCollapsed()) {
23806
- var elm = selection.getNode();
23807
- if (elm.tagName == 'A') {
24592
+ var elm = editor.dom.getParent(editor.selection.getStart(), 'a');
24593
+ if (elm) {
23808
24594
  editor.dom.remove(elm, true);
23809
24595
  }
23810
24596
 
@@ -23970,9 +24756,9 @@ define("tinymce/EditorCommands", [
23970
24756
  return;
23971
24757
  }
23972
24758
 
23973
- if (element.nodeName != "LI") {
24759
+ if (element.nodeName !== "LI") {
23974
24760
  var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
23975
-
24761
+ indentStyleName = element.nodeName === 'TABLE' ? 'margin' : indentStyleName;
23976
24762
  indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
23977
24763
 
23978
24764
  if (command == 'outdent') {
@@ -25262,7 +26048,8 @@ define("tinymce/util/Observable", [
25262
26048
 
25263
26049
  return {
25264
26050
  /**
25265
- * Fires the specified event by name.
26051
+ * Fires the specified event by name. Consult the
26052
+ * <a href="/advanced/events">event reference</a> for more details on each event.
25266
26053
  *
25267
26054
  * @method fire
25268
26055
  * @param {String} name Name of the event to fire.
@@ -25295,7 +26082,8 @@ define("tinymce/util/Observable", [
25295
26082
  },
25296
26083
 
25297
26084
  /**
25298
- * Binds an event listener to a specific event by name.
26085
+ * Binds an event listener to a specific event by name. Consult the
26086
+ * <a href="/advanced/events">event reference</a> for more details on each event.
25299
26087
  *
25300
26088
  * @method on
25301
26089
  * @param {String} name Event name or space separated list of events to bind.
@@ -25312,7 +26100,8 @@ define("tinymce/util/Observable", [
25312
26100
  },
25313
26101
 
25314
26102
  /**
25315
- * Unbinds an event listener to a specific event by name.
26103
+ * Unbinds an event listener to a specific event by name. Consult the
26104
+ * <a href="/advanced/events">event reference</a> for more details on each event.
25316
26105
  *
25317
26106
  * @method off
25318
26107
  * @param {String?} name Name of the event to unbind.
@@ -25333,7 +26122,8 @@ define("tinymce/util/Observable", [
25333
26122
  },
25334
26123
 
25335
26124
  /**
25336
- * Bind the event callback and once it fires the callback is removed.
26125
+ * Bind the event callback and once it fires the callback is removed. Consult the
26126
+ * <a href="/advanced/events">event reference</a> for more details on each event.
25337
26127
  *
25338
26128
  * @method once
25339
26129
  * @param {String} name Name of the event to bind.
@@ -28388,7 +29178,7 @@ define("tinymce/ui/KeyboardNavigation", [
28388
29178
  var elements = [];
28389
29179
 
28390
29180
  function collect(elm) {
28391
- if (elm.nodeType != 1 || elm.style.display == 'none') {
29181
+ if (elm.nodeType != 1 || elm.style.display == 'none' || elm.disabled) {
28392
29182
  return;
28393
29183
  }
28394
29184
 
@@ -29264,7 +30054,7 @@ define("tinymce/ui/DragHelper", [
29264
30054
  cursor = handleElm.runtimeStyle.cursor;
29265
30055
  }
29266
30056
 
29267
- $eventOverlay = $('<div>').css({
30057
+ $eventOverlay = $('<div></div>').css({
29268
30058
  position: "absolute",
29269
30059
  top: 0, left: 0,
29270
30060
  width: docSize.width,
@@ -30278,14 +31068,19 @@ define("tinymce/ui/Window", [
30278
31068
  viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue);
30279
31069
  }
30280
31070
 
30281
- function toggleBodyFullScreenClasses(classPrefix) {
31071
+ function toggleBodyFullScreenClasses(classPrefix, state) {
31072
+ if (checkFullscreenWindows() && state === false) {
31073
+ $([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen');
31074
+ }
31075
+ }
31076
+
31077
+ function checkFullscreenWindows() {
30282
31078
  for (var i = 0; i < windows.length; i++) {
30283
31079
  if (windows[i]._fullscreen) {
30284
- return;
31080
+ return true;
30285
31081
  }
30286
31082
  }
30287
-
30288
- $([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen');
31083
+ return false;
30289
31084
  }
30290
31085
 
30291
31086
  function handleWindowResize() {
@@ -30672,6 +31467,8 @@ define("tinymce/ui/Window", [
30672
31467
  this.statusbar.remove();
30673
31468
  }
30674
31469
 
31470
+ toggleBodyFullScreenClasses(self.classPrefix, false);
31471
+
30675
31472
  i = windows.length;
30676
31473
  while (i--) {
30677
31474
  if (windows[i] === self) {
@@ -30680,7 +31477,6 @@ define("tinymce/ui/Window", [
30680
31477
  }
30681
31478
 
30682
31479
  toggleFullScreenState(windows.length > 0);
30683
- toggleBodyFullScreenClasses(self.classPrefix);
30684
31480
  },
30685
31481
 
30686
31482
  /**
@@ -31652,7 +32448,10 @@ define("tinymce/ui/Notification", [
31652
32448
 
31653
32449
  style.left = rect.x + 'px';
31654
32450
  style.top = rect.y + 'px';
31655
- style.zIndex = 0xFFFF + 0xFFFF;
32451
+
32452
+ // Hardcoded arbitrary z-value because we want the
32453
+ // notifications under the other windows
32454
+ style.zIndex = 0xFFFF - 1;
31656
32455
  }
31657
32456
  });
31658
32457
  });
@@ -31682,8 +32481,9 @@ define("tinymce/ui/Notification", [
31682
32481
  */
31683
32482
  define("tinymce/NotificationManager", [
31684
32483
  "tinymce/ui/Notification",
31685
- "tinymce/util/Delay"
31686
- ], function(Notification, Delay) {
32484
+ "tinymce/util/Delay",
32485
+ "tinymce/util/Tools"
32486
+ ], function(Notification, Delay, Tools) {
31687
32487
  return function(editor) {
31688
32488
  var self = this, notifications = [];
31689
32489
 
@@ -31741,39 +32541,50 @@ define("tinymce/NotificationManager", [
31741
32541
  * @param {Object} args Optional name/value settings collection contains things like timeout/color/message etc.
31742
32542
  */
31743
32543
  self.open = function(args) {
32544
+ // Never open notification if editor has been removed.
32545
+ if (editor.removed) {
32546
+ return;
32547
+ }
32548
+
31744
32549
  var notif;
31745
32550
 
31746
32551
  editor.editorManager.setActive(editor);
31747
32552
 
31748
- notif = new Notification(args);
31749
- notifications.push(notif);
32553
+ var duplicate = findDuplicateMessage(notifications, args);
31750
32554
 
31751
- //If we have a timeout value
31752
- if (args.timeout > 0) {
31753
- notif.timer = setTimeout(function() {
31754
- notif.close();
31755
- }, args.timeout);
31756
- }
31757
-
31758
- notif.on('close', function() {
31759
- var i = notifications.length;
32555
+ if (duplicate === null) {
32556
+ notif = new Notification(args);
32557
+ notifications.push(notif);
31760
32558
 
31761
- if (notif.timer) {
31762
- editor.getWin().clearTimeout(notif.timer);
32559
+ //If we have a timeout value
32560
+ if (args.timeout > 0) {
32561
+ notif.timer = setTimeout(function() {
32562
+ notif.close();
32563
+ }, args.timeout);
31763
32564
  }
31764
32565
 
31765
- while (i--) {
31766
- if (notifications[i] === notif) {
31767
- notifications.splice(i, 1);
32566
+ notif.on('close', function() {
32567
+ var i = notifications.length;
32568
+
32569
+ if (notif.timer) {
32570
+ editor.getWin().clearTimeout(notif.timer);
31768
32571
  }
31769
- }
31770
32572
 
31771
- positionNotifications();
31772
- });
32573
+ while (i--) {
32574
+ if (notifications[i] === notif) {
32575
+ notifications.splice(i, 1);
32576
+ }
32577
+ }
32578
+
32579
+ positionNotifications();
32580
+ });
31773
32581
 
31774
- notif.renderTo();
32582
+ notif.renderTo();
31775
32583
 
31776
- positionNotifications();
32584
+ positionNotifications();
32585
+ } else {
32586
+ notif = duplicate;
32587
+ }
31777
32588
 
31778
32589
  return notif;
31779
32590
  };
@@ -31812,6 +32623,49 @@ define("tinymce/NotificationManager", [
31812
32623
  }
31813
32624
  });
31814
32625
 
32626
+ /**
32627
+ * Finds any existing notification with the same properties as the new one.
32628
+ * Returns either the found notification or null.
32629
+ *
32630
+ * @param {Notification[]} notificationArray - Array of current notifications
32631
+ * @param {type: string, } newNotification - New notification object
32632
+ * @returns {?Notification}
32633
+ */
32634
+ function findDuplicateMessage(notificationArray, newNotification) {
32635
+ if (!isPlainTextNotification(newNotification)) {
32636
+ return null;
32637
+ }
32638
+
32639
+ var filteredNotifications = Tools.grep(notificationArray, function (notification) {
32640
+ return isSameNotification(newNotification, notification);
32641
+ });
32642
+
32643
+ return filteredNotifications.length === 0 ? null : filteredNotifications[0];
32644
+ }
32645
+
32646
+ /**
32647
+ * Checks if the passed in args object has the same
32648
+ * type and text properties as the sent in notification.
32649
+ *
32650
+ * @param {type: string, text: string} a - New notification args object
32651
+ * @param {Notification} b - Old notification
32652
+ * @returns {boolean}
32653
+ */
32654
+ function isSameNotification(a, b) {
32655
+ return a.type === b.settings.type && a.text === b.settings.text;
32656
+ }
32657
+
32658
+ /**
32659
+ * Checks that the notification does not have a progressBar
32660
+ * or timeour property.
32661
+ *
32662
+ * @param {Notification} notification - Notification to check
32663
+ * @returns {boolean}
32664
+ */
32665
+ function isPlainTextNotification(notification) {
32666
+ return !notification.progressBar && !notification.timeout;
32667
+ }
32668
+
31815
32669
  //self.positionNotifications = positionNotifications;
31816
32670
  };
31817
32671
  });
@@ -32116,6 +32970,15 @@ define("tinymce/util/Quirks", [
32116
32970
  function findCaretNode(node, forward, startNode) {
32117
32971
  var walker, current, nonEmptyElements;
32118
32972
 
32973
+ // Protect against the possibility we are asked to find a caret node relative
32974
+ // to a node that is no longer in the DOM tree. In this case attempting to
32975
+ // select on any match leads to a scenario where selection is completely removed
32976
+ // from the editor. This scenario is met in real world at a minimum on
32977
+ // WebKit browsers when selecting all and Cmd-X cutting to delete content.
32978
+ if (!dom.isChildOf(node, editor.getBody())) {
32979
+ return;
32980
+ }
32981
+
32119
32982
  nonEmptyElements = dom.schema.getNonEmptyElements();
32120
32983
 
32121
32984
  walker = new TreeWalker(startNode || node, node);
@@ -32215,10 +33078,11 @@ define("tinymce/util/Quirks", [
32215
33078
  }
32216
33079
  }
32217
33080
 
32218
- caretNode = RangeUtils.getNode(rng.startContainer, rng.startOffset);
33081
+ caretNode = RangeUtils.getNode(container, offset);
32219
33082
  textBlock = dom.getParent(caretNode, dom.isBlock);
32220
33083
  targetCaretNode = findCaretNode(editor.getBody(), isForward, caretNode);
32221
33084
  targetTextBlock = dom.getParent(targetCaretNode, dom.isBlock);
33085
+ var isAfter = container.nodeType === 1 && offset > container.childNodes.length - 1;
32222
33086
 
32223
33087
  if (!caretNode || !targetCaretNode) {
32224
33088
  return rng;
@@ -32241,7 +33105,11 @@ define("tinymce/util/Quirks", [
32241
33105
  }
32242
33106
 
32243
33107
  if (caretNode.nodeType == 1) {
32244
- rng.setEnd(caretNode, 0);
33108
+ if (isAfter) {
33109
+ rng.setEndAfter(caretNode);
33110
+ } else {
33111
+ rng.setEndBefore(caretNode);
33112
+ }
32245
33113
  } else {
32246
33114
  rng.setEndBefore(caretNode);
32247
33115
  }
@@ -32464,6 +33332,12 @@ define("tinymce/util/Quirks", [
32464
33332
  });
32465
33333
  }
32466
33334
 
33335
+ function transactCustomDelete(isForward) {
33336
+ editor.undoManager.transact(function () {
33337
+ customDelete(isForward);
33338
+ });
33339
+ }
33340
+
32467
33341
  editor.on('keydown', function(e) {
32468
33342
  var isForward = e.keyCode == DELETE, isMetaOrCtrl = e.ctrlKey || e.metaKey;
32469
33343
 
@@ -32590,9 +33464,9 @@ define("tinymce/util/Quirks", [
32590
33464
  if (dragStartRng) {
32591
33465
  selection.setRng(dragStartRng);
32592
33466
  dragStartRng = null;
33467
+ transactCustomDelete();
32593
33468
  }
32594
33469
 
32595
- customDelete();
32596
33470
  selection.setRng(pointRng);
32597
33471
  insertClipboardContents(internalContent.html);
32598
33472
  });
@@ -32611,7 +33485,7 @@ define("tinymce/util/Quirks", [
32611
33485
  // Nested delete/forwardDelete not allowed on execCommand("cut")
32612
33486
  // This is ugly but not sure how to work around it otherwise
32613
33487
  Delay.setEditorTimeout(editor, function() {
32614
- customDelete(true);
33488
+ transactCustomDelete(true);
32615
33489
  });
32616
33490
  }
32617
33491
  });
@@ -33508,18 +34382,7 @@ define("tinymce/util/Quirks", [
33508
34382
  }
33509
34383
 
33510
34384
  function refreshContentEditable() {
33511
- var body, parent;
33512
-
33513
- // Check if the editor was hidden and the re-initialize contentEditable mode by removing and adding the body again
33514
- if (isHidden()) {
33515
- body = editor.getBody();
33516
- parent = body.parentNode;
33517
-
33518
- parent.removeChild(body);
33519
- parent.appendChild(body);
33520
-
33521
- body.focus();
33522
- }
34385
+ // No-op since Mozilla seems to have fixed the caret repaint issues
33523
34386
  }
33524
34387
 
33525
34388
  function isHidden() {
@@ -34062,7 +34925,7 @@ define("tinymce/Shortcuts", [
34062
34925
  }
34063
34926
 
34064
34927
  function isFunctionKey(e) {
34065
- return e.keyCode >= 112 && e.keyCode <= 123;
34928
+ return e.type === "keydown" && e.keyCode >= 112 && e.keyCode <= 123;
34066
34929
  }
34067
34930
 
34068
34931
  function matchShortcut(e, shortcut) {
@@ -34119,7 +34982,7 @@ define("tinymce/Shortcuts", [
34119
34982
  /**
34120
34983
  * Adds a keyboard shortcut for some command or function.
34121
34984
  *
34122
- * @method addShortcut
34985
+ * @method add
34123
34986
  * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
34124
34987
  * @param {String} desc Text description for the command.
34125
34988
  * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
@@ -34208,7 +35071,7 @@ define("tinymce/file/Uploader", [
34208
35071
  return function(uploadStatus, settings) {
34209
35072
  var pendingPromises = {};
34210
35073
 
34211
- function fileName(blobInfo) {
35074
+ function filename(blobInfo) {
34212
35075
  var ext, extensions;
34213
35076
 
34214
35077
  extensions = {
@@ -34220,7 +35083,7 @@ define("tinymce/file/Uploader", [
34220
35083
 
34221
35084
  ext = extensions[blobInfo.blob().type.toLowerCase()] || 'dat';
34222
35085
 
34223
- return blobInfo.id() + '.' + ext;
35086
+ return blobInfo.filename() + '.' + ext;
34224
35087
  }
34225
35088
 
34226
35089
  function pathJoin(path1, path2) {
@@ -34236,7 +35099,7 @@ define("tinymce/file/Uploader", [
34236
35099
  id: blobInfo.id,
34237
35100
  blob: blobInfo.blob,
34238
35101
  base64: blobInfo.base64,
34239
- filename: Fun.constant(fileName(blobInfo))
35102
+ filename: Fun.constant(filename(blobInfo))
34240
35103
  };
34241
35104
  }
34242
35105
 
@@ -34274,7 +35137,7 @@ define("tinymce/file/Uploader", [
34274
35137
  };
34275
35138
 
34276
35139
  formData = new FormData();
34277
- formData.append('file', blobInfo.blob(), fileName(blobInfo));
35140
+ formData.append('file', blobInfo.blob(), blobInfo.filename());
34278
35141
 
34279
35142
  xhr.send(formData);
34280
35143
  }
@@ -34682,9 +35545,10 @@ define("tinymce/file/BlobCache", [
34682
35545
  return function() {
34683
35546
  var cache = [], constant = Fun.constant;
34684
35547
 
34685
- function create(id, blob, base64) {
35548
+ function create(id, blob, base64, filename) {
34686
35549
  return {
34687
35550
  id: constant(id),
35551
+ filename: constant(filename || id),
34688
35552
  blob: constant(blob),
34689
35553
  base64: constant(base64),
34690
35554
  blobUri: constant(URL.createObjectURL(blob))
@@ -34861,6 +35725,10 @@ define("tinymce/EditorUpload", [
34861
35725
  };
34862
35726
  }
34863
35727
 
35728
+ function cacheInvalidator() {
35729
+ return '?' + (new Date()).getTime();
35730
+ }
35731
+
34864
35732
  // Replaces strings without regexps to avoid FF regexp to big issue
34865
35733
  function replaceString(content, search, replace) {
34866
35734
  var index = 0;
@@ -34886,7 +35754,13 @@ define("tinymce/EditorUpload", [
34886
35754
 
34887
35755
  function replaceUrlInUndoStack(targetUrl, replacementUrl) {
34888
35756
  Arr.each(editor.undoManager.data, function(level) {
34889
- level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
35757
+ if (level.type === 'fragmented') {
35758
+ level.fragments = Arr.map(level.fragments, function (fragment) {
35759
+ return replaceImageUrl(fragment, targetUrl, replacementUrl);
35760
+ });
35761
+ } else {
35762
+ level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
35763
+ }
34890
35764
  });
34891
35765
  }
34892
35766
 
@@ -34904,7 +35778,7 @@ define("tinymce/EditorUpload", [
34904
35778
  replaceUrlInUndoStack(image.src, resultUri);
34905
35779
 
34906
35780
  editor.$(image).attr({
34907
- src: resultUri,
35781
+ src: settings.images_reuse_filename ? resultUri + cacheInvalidator() : resultUri,
34908
35782
  'data-mce-src': editor.convertURL(resultUri, 'src')
34909
35783
  });
34910
35784
  }
@@ -35152,7 +36026,7 @@ define("tinymce/caret/FakeCaret", [
35152
36026
  }
35153
36027
 
35154
36028
  function show(before, node) {
35155
- var clientRect, rng, container;
36029
+ var clientRect, rng;
35156
36030
 
35157
36031
  hide();
35158
36032
 
@@ -35170,9 +36044,8 @@ define("tinymce/caret/FakeCaret", [
35170
36044
  startBlink();
35171
36045
 
35172
36046
  rng = node.ownerDocument.createRange();
35173
- container = caretContainerNode.firstChild;
35174
- rng.setStart(container, 0);
35175
- rng.setEnd(container, 1);
36047
+ rng.setStart(caretContainerNode, 0);
36048
+ rng.setEnd(caretContainerNode, 0);
35176
36049
  } else {
35177
36050
  caretContainerNode = CaretContainer.insertInline(node, before);
35178
36051
  rng = node.ownerDocument.createRange();
@@ -35893,10 +36766,17 @@ define("tinymce/DragDropOverrides", [
35893
36766
  };
35894
36767
  };
35895
36768
 
36769
+ // Returns the raw element instead of the fake cE=false element
36770
+ var getRawTarget = function (selection) {
36771
+ var rng = selection.getSel().getRangeAt(0);
36772
+ var startContainer = rng.startContainer;
36773
+ return startContainer.nodeType === 3 ? startContainer.parentNode : startContainer;
36774
+ };
36775
+
35896
36776
  var drop = function (state, editor) {
35897
36777
  return function (e) {
35898
36778
  if (state.dragging) {
35899
- if (isValidDropTarget(editor, editor.selection.getNode(), state.element)) {
36779
+ if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) {
35900
36780
  var targetClone = cloneElement(state.element);
35901
36781
 
35902
36782
  var args = editor.fire('drop', {
@@ -36021,11 +36901,10 @@ define("tinymce/SelectionOverrides", [
36021
36901
  "tinymce/util/Fun",
36022
36902
  "tinymce/util/Arr",
36023
36903
  "tinymce/util/Delay",
36024
- "tinymce/DragDropOverrides",
36025
- "tinymce/text/Zwsp"
36904
+ "tinymce/DragDropOverrides"
36026
36905
  ], function(
36027
36906
  Env, CaretWalker, CaretPosition, CaretContainer, CaretUtils, FakeCaret, LineWalker,
36028
- LineUtils, NodeType, RangeUtils, ClientRect, VK, Fun, Arr, Delay, DragDropOverrides, Zwsp
36907
+ LineUtils, NodeType, RangeUtils, ClientRect, VK, Fun, Arr, Delay, DragDropOverrides
36029
36908
  ) {
36030
36909
  var curry = Fun.curry,
36031
36910
  isContentEditableTrue = NodeType.isContentEditableTrue,
@@ -36053,6 +36932,10 @@ define("tinymce/SelectionOverrides", [
36053
36932
  realSelectionId = 'sel-' + editor.dom.uniqueId(),
36054
36933
  selectedContentEditableNode, $ = editor.$;
36055
36934
 
36935
+ function isFakeSelectionElement(elm) {
36936
+ return editor.dom.hasClass(elm, 'mce-offscreen-selection');
36937
+ }
36938
+
36056
36939
  function getRealSelectionElement() {
36057
36940
  var container = editor.dom.get(realSelectionId);
36058
36941
  return container ? container.getElementsByTagName('*')[0] : container;
@@ -36098,8 +36981,6 @@ define("tinymce/SelectionOverrides", [
36098
36981
  function selectNode(node) {
36099
36982
  var e;
36100
36983
 
36101
- fakeCaret.hide();
36102
-
36103
36984
  e = editor.fire('BeforeObjectSelected', {target: node});
36104
36985
  if (e.isDefaultPrevented()) {
36105
36986
  return null;
@@ -36313,16 +37194,9 @@ define("tinymce/SelectionOverrides", [
36313
37194
  }
36314
37195
 
36315
37196
  function showBlockCaretContainer(blockCaretContainer) {
36316
- blockCaretContainer = $(blockCaretContainer);
36317
-
36318
- if (blockCaretContainer.attr('data-mce-caret')) {
36319
- fakeCaret.hide();
36320
- blockCaretContainer.removeAttr('data-mce-caret');
36321
- blockCaretContainer.removeAttr('data-mce-bogus');
36322
- blockCaretContainer.removeAttr('style');
36323
-
36324
- // Removes control rect on IE
36325
- setRange(getRange());
37197
+ if (blockCaretContainer.hasAttribute('data-mce-caret')) {
37198
+ CaretContainer.showCaretContainerBlock(blockCaretContainer);
37199
+ setRange(getRange()); // Removes control rect on IE
36326
37200
  scrollIntoView(blockCaretContainer[0]);
36327
37201
  }
36328
37202
  }
@@ -36347,8 +37221,6 @@ define("tinymce/SelectionOverrides", [
36347
37221
  return showCaret(1, ceRoot, false);
36348
37222
  }
36349
37223
 
36350
- fakeCaret.hide();
36351
-
36352
37224
  return null;
36353
37225
  }
36354
37226
 
@@ -36390,7 +37262,6 @@ define("tinymce/SelectionOverrides", [
36390
37262
  CaretContainer.remove(node.previousSibling);
36391
37263
  CaretContainer.remove(node.nextSibling);
36392
37264
  editor.dom.remove(node);
36393
- clearContentEditableSelection();
36394
37265
 
36395
37266
  if (editor.dom.isEmpty(editor.getBody())) {
36396
37267
  editor.setContent('');
@@ -36559,16 +37430,28 @@ define("tinymce/SelectionOverrides", [
36559
37430
  editor.on('click', function(e) {
36560
37431
  var contentEditableRoot;
36561
37432
 
36562
- // Prevent clicks on links in a cE=false element
36563
37433
  contentEditableRoot = getContentEditableRoot(e.target);
36564
37434
  if (contentEditableRoot) {
37435
+ // Prevent clicks on links in a cE=false element
36565
37436
  if (isContentEditableFalse(contentEditableRoot)) {
36566
37437
  e.preventDefault();
36567
37438
  editor.focus();
36568
37439
  }
37440
+
37441
+ // Removes fake selection if a cE=true is clicked within a cE=false like the toc title
37442
+ if (isContentEditableTrue(contentEditableRoot)) {
37443
+ if (editor.dom.isChildOf(contentEditableRoot, editor.selection.getNode())) {
37444
+ removeContentEditableSelection();
37445
+ }
37446
+ }
36569
37447
  }
36570
37448
  });
36571
37449
 
37450
+ editor.on('blur NewBlock', function () {
37451
+ removeContentEditableSelection();
37452
+ hideFakeCaret();
37453
+ });
37454
+
36572
37455
  function handleTouchSelect(editor) {
36573
37456
  var moved = false;
36574
37457
 
@@ -36588,8 +37471,6 @@ define("tinymce/SelectionOverrides", [
36588
37471
  e.preventDefault();
36589
37472
  setContentEditableSelection(selectNode(contentEditableRoot));
36590
37473
  }
36591
- } else {
36592
- clearContentEditableSelection();
36593
37474
  }
36594
37475
  });
36595
37476
  }
@@ -36613,6 +37494,14 @@ define("tinymce/SelectionOverrides", [
36613
37494
  return block1 === block2;
36614
37495
  };
36615
37496
 
37497
+ var isContentKey = function (e) {
37498
+ if (e.keyCode >= 112 && e.keyCode <= 123) {
37499
+ return false;
37500
+ }
37501
+
37502
+ return true;
37503
+ };
37504
+
36616
37505
  // Checks if the target node is in a block and if that block has a caret position better than the
36617
37506
  // suggested caretNode this is to prevent the caret from being sucked in towards a cE=false block if
36618
37507
  // they are adjacent on the vertical axis
@@ -36634,15 +37523,15 @@ define("tinymce/SelectionOverrides", [
36634
37523
  e.preventDefault();
36635
37524
  setContentEditableSelection(selectNode(contentEditableRoot));
36636
37525
  } else {
36637
- clearContentEditableSelection();
36638
-
36639
37526
  if (!isXYWithinRange(e.clientX, e.clientY, editor.selection.getRng())) {
36640
37527
  editor.selection.placeCaretAt(e.clientX, e.clientY);
36641
37528
  }
36642
37529
  }
36643
37530
  } else {
36644
- clearContentEditableSelection();
36645
- fakeCaret.hide();
37531
+ // Remove needs to be called here since the mousedown might alter the selection without calling selection.setRng
37532
+ // and therefore not fire the AfterSetSelectionRange event.
37533
+ removeContentEditableSelection();
37534
+ hideFakeCaret();
36646
37535
 
36647
37536
  var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY);
36648
37537
  if (caretInfo) {
@@ -36686,7 +37575,7 @@ define("tinymce/SelectionOverrides", [
36686
37575
  break;
36687
37576
 
36688
37577
  default:
36689
- if (isContentEditableFalse(editor.selection.getNode())) {
37578
+ if (isContentEditableFalse(editor.selection.getNode()) && isContentKey(e)) {
36690
37579
  e.preventDefault();
36691
37580
  }
36692
37581
  break;
@@ -36717,7 +37606,7 @@ define("tinymce/SelectionOverrides", [
36717
37606
  return;
36718
37607
  }
36719
37608
 
36720
- if (blockCaretContainer.innerHTML != '&nbsp;') {
37609
+ if (CaretContainer.hasContent(blockCaretContainer)) {
36721
37610
  showBlockCaretContainer(blockCaretContainer);
36722
37611
  }
36723
37612
  }
@@ -36780,6 +37669,18 @@ define("tinymce/SelectionOverrides", [
36780
37669
  }
36781
37670
  });
36782
37671
 
37672
+ editor.on('AfterSetSelectionRange', function(e) {
37673
+ var rng = e.range;
37674
+
37675
+ if (!isRangeInCaretContainer(rng)) {
37676
+ hideFakeCaret();
37677
+ }
37678
+
37679
+ if (!isFakeSelectionElement(rng.startContainer.parentNode)) {
37680
+ removeContentEditableSelection();
37681
+ }
37682
+ });
37683
+
36783
37684
  editor.on('focus', function() {
36784
37685
  // Make sure we have a proper fake caret on focus
36785
37686
  Delay.setEditorTimeout(editor, function() {
@@ -36833,13 +37734,10 @@ define("tinymce/SelectionOverrides", [
36833
37734
  startContainer, startOffset, endOffset, e, caretPosition, targetClone, origTargetClone;
36834
37735
 
36835
37736
  if (!range) {
36836
- clearContentEditableSelection();
36837
37737
  return null;
36838
37738
  }
36839
37739
 
36840
37740
  if (range.collapsed) {
36841
- clearContentEditableSelection();
36842
-
36843
37741
  if (!isRangeInCaretContainer(range)) {
36844
37742
  caretPosition = getNormalizedRangeEndPoint(1, range);
36845
37743
 
@@ -36867,7 +37765,6 @@ define("tinymce/SelectionOverrides", [
36867
37765
  }
36868
37766
 
36869
37767
  if (startContainer.nodeType != 1) {
36870
- clearContentEditableSelection();
36871
37768
  return null;
36872
37769
  }
36873
37770
 
@@ -36876,14 +37773,12 @@ define("tinymce/SelectionOverrides", [
36876
37773
  }
36877
37774
 
36878
37775
  if (!isContentEditableFalse(node)) {
36879
- clearContentEditableSelection();
36880
37776
  return null;
36881
37777
  }
36882
37778
 
36883
37779
  targetClone = origTargetClone = node.cloneNode(true);
36884
37780
  e = editor.fire('ObjectSelected', {target: node, targetClone: targetClone});
36885
37781
  if (e.isDefaultPrevented()) {
36886
- clearContentEditableSelection();
36887
37782
  return null;
36888
37783
  }
36889
37784
 
@@ -36900,10 +37795,12 @@ define("tinymce/SelectionOverrides", [
36900
37795
  range = editor.dom.createRng();
36901
37796
 
36902
37797
  // WHY is IE making things so hard! Copy on <i contentEditable="false">x</i> produces: <em>x</em>
37798
+ // This is a ridiculous hack where we place the selection from a block over the inline element
37799
+ // so that just the inline element is copied as is and not converted.
36903
37800
  if (targetClone === origTargetClone && Env.ie) {
36904
- $realSelectionContainer.empty().append(Zwsp.ZWSP).append(targetClone).append(Zwsp.ZWSP);
36905
- range.setStart($realSelectionContainer[0].firstChild, 0);
36906
- range.setEnd($realSelectionContainer[0].lastChild, 1);
37801
+ $realSelectionContainer.empty().append('<p style="font-size: 0" data-mce-bogus="all">\u00a0</p>').append(targetClone);
37802
+ range.setStartAfter($realSelectionContainer[0].firstChild.firstChild);
37803
+ range.setEndAfter(targetClone);
36907
37804
  } else {
36908
37805
  $realSelectionContainer.empty().append('\u00a0').append(targetClone).append('\u00a0');
36909
37806
  range.setStart($realSelectionContainer[0].firstChild, 1);
@@ -36926,7 +37823,7 @@ define("tinymce/SelectionOverrides", [
36926
37823
  return range;
36927
37824
  }
36928
37825
 
36929
- function clearContentEditableSelection() {
37826
+ function removeContentEditableSelection() {
36930
37827
  if (selectedContentEditableNode) {
36931
37828
  selectedContentEditableNode.removeAttribute('data-mce-selected');
36932
37829
  editor.$('#' + realSelectionId).remove();
@@ -36998,6 +37895,38 @@ define("tinymce/util/Uuid", [
36998
37895
  };
36999
37896
  });
37000
37897
 
37898
+ // Included from: js/tinymce/classes/ui/Sidebar.js
37899
+
37900
+ /**
37901
+ * Sidebar.js
37902
+ *
37903
+ * Released under LGPL License.
37904
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
37905
+ *
37906
+ * License: http://www.tinymce.com/license
37907
+ * Contributing: http://www.tinymce.com/contributing
37908
+ */
37909
+
37910
+ /**
37911
+ * This module handle sidebar instances for the editor.
37912
+ *
37913
+ * @class tinymce.ui.Sidebar
37914
+ * @private
37915
+ */
37916
+ define("tinymce/ui/Sidebar", [
37917
+ ], function(
37918
+ ) {
37919
+ var add = function (editor, name, settings) {
37920
+ var sidebars = editor.sidebars ? editor.sidebars : [];
37921
+ sidebars.push({name: name, settings: settings});
37922
+ editor.sidebars = sidebars;
37923
+ };
37924
+
37925
+ return {
37926
+ add: add
37927
+ };
37928
+ });
37929
+
37001
37930
  // Included from: js/tinymce/classes/Editor.js
37002
37931
 
37003
37932
  /**
@@ -37072,13 +38001,14 @@ define("tinymce/Editor", [
37072
38001
  "tinymce/Shortcuts",
37073
38002
  "tinymce/EditorUpload",
37074
38003
  "tinymce/SelectionOverrides",
37075
- "tinymce/util/Uuid"
38004
+ "tinymce/util/Uuid",
38005
+ "tinymce/ui/Sidebar"
37076
38006
  ], function(
37077
38007
  DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
37078
38008
  Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
37079
38009
  URI, ScriptLoader, EventUtils, WindowManager, NotificationManager,
37080
38010
  Schema, DomParser, Quirks, Env, Tools, Delay, EditorObservable, Mode, Shortcuts, EditorUpload,
37081
- SelectionOverrides, Uuid
38011
+ SelectionOverrides, Uuid, Sidebar
37082
38012
  ) {
37083
38013
  // Shorten these names
37084
38014
  var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
@@ -37246,11 +38176,6 @@ define("tinymce/Editor", [
37246
38176
  self.shortcuts = new Shortcuts(self);
37247
38177
  self.loadedCSS = {};
37248
38178
  self.editorCommands = new EditorCommands(self);
37249
-
37250
- if (settings.target) {
37251
- self.targetElm = settings.target;
37252
- }
37253
-
37254
38179
  self.suffix = editorManager.suffix;
37255
38180
  self.editorManager = editorManager;
37256
38181
  self.inline = settings.inline;
@@ -37594,14 +38519,12 @@ define("tinymce/Editor", [
37594
38519
  } else {
37595
38520
  o = settings.theme(self, elm);
37596
38521
 
37597
- // Convert element type to id:s
37598
38522
  if (o.editorContainer.nodeType) {
37599
- o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
38523
+ o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
37600
38524
  }
37601
38525
 
37602
- // Convert element type to id:s
37603
38526
  if (o.iframeContainer.nodeType) {
37604
- o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
38527
+ o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
37605
38528
  }
37606
38529
 
37607
38530
  // Use specified iframe height or the targets offsetHeight
@@ -38314,6 +39237,31 @@ define("tinymce/Editor", [
38314
39237
  self.buttons[name] = settings;
38315
39238
  },
38316
39239
 
39240
+ /**
39241
+ * Adds a sidebar for the editor instance.
39242
+ *
39243
+ * @method addSidebar
39244
+ * @param {String} name Sidebar name to add.
39245
+ * @param {Object} settings Settings object with icon, onshow etc.
39246
+ * @example
39247
+ * // Adds a custom sidebar that when clicked logs the panel element
39248
+ * tinymce.init({
39249
+ * ...
39250
+ * setup: function(ed) {
39251
+ * ed.addSidebar('example', {
39252
+ * tooltip: 'My sidebar',
39253
+ * icon: 'my-side-bar',
39254
+ * onshow: function(api) {
39255
+ * console.log(api.element());
39256
+ * }
39257
+ * });
39258
+ * }
39259
+ * });
39260
+ */
39261
+ addSidebar: function (name, settings) {
39262
+ return Sidebar.add(this, name, settings);
39263
+ },
39264
+
38317
39265
  /**
38318
39266
  * Adds a menu item to be used in the menus of the theme. There might be multiple instances
38319
39267
  * of this menu item for example it might be used in the main menus of the theme but also in
@@ -39225,7 +40173,9 @@ define("tinymce/Editor", [
39225
40173
  *
39226
40174
  * @class tinymce.util.I18n
39227
40175
  */
39228
- define("tinymce/util/I18n", [], function() {
40176
+ define("tinymce/util/I18n", [
40177
+ "tinymce/util/Tools"
40178
+ ], function(Tools) {
39229
40179
  "use strict";
39230
40180
 
39231
40181
  var data = {}, code = "en";
@@ -39296,30 +40246,52 @@ define("tinymce/util/I18n", [], function() {
39296
40246
  * @return {String} String that got translated.
39297
40247
  */
39298
40248
  translate: function(text) {
39299
- var langData;
40249
+ var langData = data[code] || {};
39300
40250
 
39301
- langData = data[code];
39302
- if (!langData) {
39303
- langData = {};
40251
+ /**
40252
+ * number - string
40253
+ * null, undefined and empty string - empty string
40254
+ * array - comma-delimited string
40255
+ * object - in [object Object]
40256
+ * function - in [object Function]
40257
+ *
40258
+ * @param obj
40259
+ * @returns {string}
40260
+ */
40261
+ function toString(obj) {
40262
+ if (Tools.is(obj, 'function')) {
40263
+ return Object.prototype.toString.call(obj);
40264
+ }
40265
+ return !isEmpty(obj) ? '' + obj : '';
39304
40266
  }
39305
40267
 
39306
- if (typeof text == "undefined") {
39307
- return text;
40268
+ function isEmpty(text) {
40269
+ return text === '' || text === null || Tools.is(text, 'undefined');
39308
40270
  }
39309
40271
 
39310
- if (typeof text != "string" && text.raw) {
39311
- return text.raw;
40272
+ function getLangData(text) {
40273
+ // make sure we work on a string and return a string
40274
+ text = toString(text);
40275
+ return Tools.hasOwn(langData, text) ? toString(langData[text]) : text;
39312
40276
  }
39313
40277
 
39314
- if (text.push) {
39315
- var values = text.slice(1);
39316
40278
 
39317
- text = (langData[text[0]] || text[0]).replace(/\{([0-9]+)\}/g, function(match1, match2) {
39318
- return values[match2];
40279
+ if (isEmpty(text)) {
40280
+ return '';
40281
+ }
40282
+
40283
+ if (Tools.is(text, 'object') && Tools.hasOwn(text, 'raw')) {
40284
+ return toString(text.raw);
40285
+ }
40286
+
40287
+ if (Tools.is(text, 'array')) {
40288
+ var values = text.slice(1);
40289
+ text = getLangData(text[0]).replace(/\{([0-9]+)\}/g, function($1, $2) {
40290
+ return Tools.hasOwn(values, $2) ? toString(values[$2]) : $1;
39319
40291
  });
39320
40292
  }
39321
40293
 
39322
- return (langData[text] || text).replace(/{context:\w+}$/, '');
40294
+ return getLangData(text).replace(/{context:\w+}$/, '');
39323
40295
  },
39324
40296
 
39325
40297
  data: data
@@ -39629,8 +40601,9 @@ define("tinymce/EditorManager", [
39629
40601
  "tinymce/util/Promise",
39630
40602
  "tinymce/util/Observable",
39631
40603
  "tinymce/util/I18n",
39632
- "tinymce/FocusManager"
39633
- ], function(Editor, $, DOMUtils, URI, Env, Tools, Promise, Observable, I18n, FocusManager) {
40604
+ "tinymce/FocusManager",
40605
+ "tinymce/AddOnManager"
40606
+ ], function(Editor, $, DOMUtils, URI, Env, Tools, Promise, Observable, I18n, FocusManager, AddOnManager) {
39634
40607
  var DOM = DOMUtils.DOM;
39635
40608
  var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
39636
40609
  var instanceCounter = 0, beforeUnloadDelegate, EditorManager, boundGlobalEvents = false;
@@ -39719,7 +40692,7 @@ define("tinymce/EditorManager", [
39719
40692
  * @property minorVersion
39720
40693
  * @type String
39721
40694
  */
39722
- minorVersion: '4.3',
40695
+ minorVersion: '5.0',
39723
40696
 
39724
40697
  /**
39725
40698
  * Release date of TinyMCE build.
@@ -39727,7 +40700,7 @@ define("tinymce/EditorManager", [
39727
40700
  * @property releaseDate
39728
40701
  * @type String
39729
40702
  */
39730
- releaseDate: '2016-09-01',
40703
+ releaseDate: '2016-11-23',
39731
40704
 
39732
40705
  /**
39733
40706
  * Collection of editor instances.
@@ -39870,6 +40843,11 @@ define("tinymce/EditorManager", [
39870
40843
  }
39871
40844
 
39872
40845
  this.defaultSettings = defaultSettings;
40846
+
40847
+ var pluginBaseUrls = defaultSettings.plugin_base_urls;
40848
+ for (var name in pluginBaseUrls) {
40849
+ AddOnManager.PluginManager.urls[name] = pluginBaseUrls[name];
40850
+ }
39873
40851
  },
39874
40852
 
39875
40853
  /**
@@ -40122,12 +41100,6 @@ define("tinymce/EditorManager", [
40122
41100
  // to fire a bunch of activate/deactivate calls while initializing
40123
41101
  self.activeEditor = editor;
40124
41102
 
40125
- /**
40126
- * Fires when an editor is added to the EditorManager collection.
40127
- *
40128
- * @event AddEditor
40129
- * @param {Object} e Event arguments.
40130
- */
40131
41103
  self.fire('AddEditor', {editor: editor});
40132
41104
 
40133
41105
  if (!beforeUnloadDelegate) {
@@ -40208,12 +41180,6 @@ define("tinymce/EditorManager", [
40208
41180
  return null;
40209
41181
  }
40210
41182
 
40211
- /**
40212
- * Fires when an editor is removed from EditorManager collection.
40213
- *
40214
- * @event RemoveEditor
40215
- * @param {Object} e Event arguments.
40216
- */
40217
41183
  if (removeEditorFromList(editor)) {
40218
41184
  self.fire('RemoveEditor', {editor: editor});
40219
41185
  }
@@ -41072,9 +42038,12 @@ define("tinymce/Compat", [
41072
42038
  tinymce.dom = tinymce.dom || {};
41073
42039
  tinymce.dom.Event = EventUtils.Event;
41074
42040
 
41075
- Tools.each(Tools, function(func, key) {
41076
- tinymce[key] = func;
41077
- });
42041
+ Tools.each(
42042
+ 'trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix'.split(' '),
42043
+ function(key) {
42044
+ tinymce[key] = Tools[key];
42045
+ }
42046
+ );
41078
42047
 
41079
42048
  Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
41080
42049
  tinymce[name] = Env[name.substr(2).toLowerCase()];
@@ -41449,7 +42418,7 @@ define("tinymce/ui/Button", [
41449
42418
  textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>';
41450
42419
  }
41451
42420
 
41452
- icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
42421
+ icon = icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
41453
42422
 
41454
42423
  return (
41455
42424
  '<div id="' + id + '" class="' + self.classes + '" tabindex="-1" aria-labelledby="' + id + '">' +
@@ -41766,8 +42735,10 @@ define("tinymce/ui/ComboBox", [
41766
42735
  "tinymce/ui/Widget",
41767
42736
  "tinymce/ui/Factory",
41768
42737
  "tinymce/ui/DomUtils",
41769
- "tinymce/dom/DomQuery"
41770
- ], function(Widget, Factory, DomUtils, $) {
42738
+ "tinymce/dom/DomQuery",
42739
+ "tinymce/util/VK",
42740
+ "tinymce/util/Tools"
42741
+ ], function(Widget, Factory, DomUtils, $, VK, Tools) {
41771
42742
  "use strict";
41772
42743
 
41773
42744
  return Widget.extend({
@@ -41820,31 +42791,66 @@ define("tinymce/ui/ComboBox", [
41820
42791
 
41821
42792
  // TODO: Rework this
41822
42793
  self.on('keydown', function(e) {
41823
- if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
41824
- self.parents().reverse().each(function(ctrl) {
41825
- var stateValue = self.state.get('value'), inputValue = self.getEl('inp').value;
41826
-
41827
- e.preventDefault();
41828
-
41829
- self.state.set('value', inputValue);
42794
+ var rootControl;
41830
42795
 
41831
- if (stateValue != inputValue) {
41832
- self.fire('change');
41833
- }
42796
+ if (e.keyCode == 13 && e.target.nodeName === 'INPUT') {
42797
+ e.preventDefault();
41834
42798
 
41835
- if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
41836
- ctrl.fire('submit', {data: ctrl.toJSON()});
42799
+ // Find root control that we can do toJSON on
42800
+ self.parents().reverse().each(function(ctrl) {
42801
+ if (ctrl.toJSON) {
42802
+ rootControl = ctrl;
41837
42803
  return false;
41838
42804
  }
41839
42805
  });
42806
+
42807
+ // Fire event on current text box with the serialized data of the whole form
42808
+ self.fire('submit', {data: rootControl.toJSON()});
41840
42809
  }
41841
42810
  });
41842
42811
 
41843
42812
  self.on('keyup', function(e) {
41844
42813
  if (e.target.nodeName == "INPUT") {
41845
- self.state.set('value', e.target.value);
42814
+ var oldValue = self.state.get('value');
42815
+ var newValue = e.target.value;
42816
+
42817
+ if (newValue !== oldValue) {
42818
+ self.state.set('value', newValue);
42819
+ self.fire('autocomplete', e);
42820
+ }
41846
42821
  }
41847
42822
  });
42823
+
42824
+ self.on('mouseover', function(e) {
42825
+ var tooltip = self.tooltip().moveTo(-0xFFFF);
42826
+
42827
+ if (self.statusLevel() && e.target.className.indexOf(self.classPrefix + 'status') !== -1) {
42828
+ var statusMessage = self.statusMessage() || 'Ok';
42829
+ var rel = tooltip.text(statusMessage).show().testMoveRel(e.target, ['bc-tc', 'bc-tl', 'bc-tr']);
42830
+
42831
+ tooltip.classes.toggle('tooltip-n', rel == 'bc-tc');
42832
+ tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl');
42833
+ tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr');
42834
+
42835
+ tooltip.moveRel(e.target, rel);
42836
+ }
42837
+ });
42838
+ },
42839
+
42840
+ statusLevel: function (value) {
42841
+ if (arguments.length > 0) {
42842
+ this.state.set('statusLevel', value);
42843
+ }
42844
+
42845
+ return this.state.get('statusLevel');
42846
+ },
42847
+
42848
+ statusMessage: function (value) {
42849
+ if (arguments.length > 0) {
42850
+ this.state.set('statusMessage', value);
42851
+ }
42852
+
42853
+ return this.state.get('statusMessage');
41848
42854
  },
41849
42855
 
41850
42856
  showMenu: function() {
@@ -41912,7 +42918,14 @@ define("tinymce/ui/ComboBox", [
41912
42918
  */
41913
42919
  repaint: function() {
41914
42920
  var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
41915
- var width, lineHeight;
42921
+ var width, lineHeight, innerPadding = 0, inputElm = elm.firstChild;
42922
+
42923
+ if (self.statusLevel() && self.statusLevel() !== 'none') {
42924
+ innerPadding = (
42925
+ parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-right'), 10) -
42926
+ parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-left'), 10)
42927
+ );
42928
+ }
41916
42929
 
41917
42930
  if (openElm) {
41918
42931
  width = rect.w - DomUtils.getSize(openElm).width - 10;
@@ -41926,8 +42939,8 @@ define("tinymce/ui/ComboBox", [
41926
42939
  lineHeight = (self.layoutRect().h - 2) + 'px';
41927
42940
  }
41928
42941
 
41929
- $(elm.firstChild).css({
41930
- width: width,
42942
+ $(inputElm).css({
42943
+ width: width - innerPadding,
41931
42944
  lineHeight: lineHeight
41932
42945
  });
41933
42946
 
@@ -41962,7 +42975,7 @@ define("tinymce/ui/ComboBox", [
41962
42975
  renderHtml: function() {
41963
42976
  var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
41964
42977
  var value = self.state.get('value') || '';
41965
- var icon, text, openBtnHtml = '', extraAttrs = '';
42978
+ var icon, text, openBtnHtml = '', extraAttrs = '', statusHtml = '';
41966
42979
 
41967
42980
  if ("spellcheck" in settings) {
41968
42981
  extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
@@ -41980,6 +42993,8 @@ define("tinymce/ui/ComboBox", [
41980
42993
  extraAttrs += ' type="' + settings.subtype + '"';
41981
42994
  }
41982
42995
 
42996
+ statusHtml = '<i id="' + id + '-status" class="mce-status mce-ico" style="display: none"></i>';
42997
+
41983
42998
  if (self.disabled()) {
41984
42999
  extraAttrs += ' disabled="disabled"';
41985
43000
  }
@@ -42009,6 +43024,7 @@ define("tinymce/ui/ComboBox", [
42009
43024
  '<input id="' + id + '-inp" class="' + prefix + 'textbox" value="' +
42010
43025
  self.encode(value, false) + '" hidefocus="1"' + extraAttrs + ' placeholder="' +
42011
43026
  self.encode(settings.placeholder) + '" />' +
43027
+ statusHtml +
42012
43028
  openBtnHtml +
42013
43029
  '</div>'
42014
43030
  );
@@ -42028,6 +43044,71 @@ define("tinymce/ui/ComboBox", [
42028
43044
  return this.state.get('value');
42029
43045
  },
42030
43046
 
43047
+ showAutoComplete: function (items, term) {
43048
+ var self = this;
43049
+
43050
+ if (items.length === 0) {
43051
+ self.hideMenu();
43052
+ return;
43053
+ }
43054
+
43055
+ var insert = function (value, title) {
43056
+ return function () {
43057
+ self.fire('selectitem', {
43058
+ title: title,
43059
+ value: value
43060
+ });
43061
+ };
43062
+ };
43063
+
43064
+ if (self.menu) {
43065
+ self.menu.items().remove();
43066
+ } else {
43067
+ self.menu = Factory.create({
43068
+ type: 'menu',
43069
+ classes: 'combobox-menu',
43070
+ layout: 'flow'
43071
+ }).parent(self).renderTo();
43072
+ }
43073
+
43074
+ Tools.each(items, function (item) {
43075
+ self.menu.add({
43076
+ text: item.title,
43077
+ url: item.previewUrl,
43078
+ match: term,
43079
+ classes: 'menu-item-ellipsis',
43080
+ onclick: insert(item.value, item.title)
43081
+ });
43082
+ });
43083
+
43084
+ self.menu.renderNew();
43085
+ self.hideMenu();
43086
+
43087
+ self.menu.on('cancel', function(e) {
43088
+ if (e.control.parent() === self.menu) {
43089
+ e.stopPropagation();
43090
+ self.focus();
43091
+ self.hideMenu();
43092
+ }
43093
+ });
43094
+
43095
+ self.menu.on('select', function() {
43096
+ self.focus();
43097
+ });
43098
+
43099
+ var maxW = self.layoutRect().w;
43100
+ self.menu.layoutRect({w: maxW, minW: 0, maxW: maxW});
43101
+ self.menu.reflow();
43102
+ self.menu.show();
43103
+ self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
43104
+ },
43105
+
43106
+ hideMenu: function() {
43107
+ if (this.menu) {
43108
+ this.menu.hide();
43109
+ }
43110
+ },
43111
+
42031
43112
  bindStates: function() {
42032
43113
  var self = this;
42033
43114
 
@@ -42041,11 +43122,60 @@ define("tinymce/ui/ComboBox", [
42041
43122
  self.getEl('inp').disabled = e.value;
42042
43123
  });
42043
43124
 
43125
+ self.state.on('change:statusLevel', function(e) {
43126
+ var statusIconElm = self.getEl('status');
43127
+ var prefix = self.classPrefix, value = e.value;
43128
+
43129
+ DomUtils.css(statusIconElm, 'display', value === 'none' ? 'none' : '');
43130
+ DomUtils.toggleClass(statusIconElm, prefix + 'i-checkmark', value === 'ok');
43131
+ DomUtils.toggleClass(statusIconElm, prefix + 'i-warning', value === 'warn');
43132
+ DomUtils.toggleClass(statusIconElm, prefix + 'i-error', value === 'error');
43133
+ self.classes.toggle('has-status', value !== 'none');
43134
+ self.repaint();
43135
+ });
43136
+
43137
+ DomUtils.on(self.getEl('status'), 'mouseleave', function () {
43138
+ self.tooltip().hide();
43139
+ });
43140
+
43141
+ self.on('cancel', function (e) {
43142
+ if (self.menu && self.menu.visible()) {
43143
+ e.stopPropagation();
43144
+ self.hideMenu();
43145
+ }
43146
+ });
43147
+
43148
+ var focusIdx = function (idx, menu) {
43149
+ if (menu && menu.items().length > 0) {
43150
+ menu.items().eq(idx)[0].focus();
43151
+ }
43152
+ };
43153
+
43154
+ self.on('keydown', function (e) {
43155
+ var keyCode = e.keyCode;
43156
+
43157
+ if (e.target.nodeName === 'INPUT') {
43158
+ if (keyCode === VK.DOWN) {
43159
+ e.preventDefault();
43160
+ self.fire('autocomplete');
43161
+ focusIdx(0, self.menu);
43162
+ } else if (keyCode === VK.UP) {
43163
+ e.preventDefault();
43164
+ focusIdx(-1, self.menu);
43165
+ }
43166
+ }
43167
+ });
43168
+
42044
43169
  return self._super();
42045
43170
  },
42046
43171
 
42047
43172
  remove: function() {
42048
43173
  $(this.getEl('inp')).off();
43174
+
43175
+ if (this.menu) {
43176
+ this.menu.remove();
43177
+ }
43178
+
42049
43179
  this._super();
42050
43180
  }
42051
43181
  });
@@ -42101,7 +43231,8 @@ define("tinymce/ui/ColorBox", [
42101
43231
  },
42102
43232
 
42103
43233
  repaintColor: function(value) {
42104
- var elm = this.getEl().getElementsByTagName('i')[0];
43234
+ var openElm = this.getEl('open');
43235
+ var elm = openElm ? openElm.getElementsByTagName('i')[0] : null;
42105
43236
 
42106
43237
  if (elm) {
42107
43238
  try {
@@ -43309,6 +44440,142 @@ define("tinymce/ui/FieldSet", [
43309
44440
  });
43310
44441
  });
43311
44442
 
44443
+ // Included from: js/tinymce/classes/content/LinkTargets.js
44444
+
44445
+ /**
44446
+ * LinkTargets.js
44447
+ *
44448
+ * Released under LGPL License.
44449
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
44450
+ *
44451
+ * License: http://www.tinymce.com/license
44452
+ * Contributing: http://www.tinymce.com/contributing
44453
+ */
44454
+
44455
+ /**
44456
+ * This module is enables you to get anything that you can link to in a element.
44457
+ *
44458
+ * @private
44459
+ * @class tinymce.content.LinkTargets
44460
+ */
44461
+ define('tinymce/content/LinkTargets', [
44462
+ 'tinymce/dom/DOMUtils',
44463
+ 'tinymce/util/Fun',
44464
+ 'tinymce/util/Arr',
44465
+ 'tinymce/util/Uuid',
44466
+ 'tinymce/util/Tools',
44467
+ 'tinymce/dom/NodeType'
44468
+ ], function(
44469
+ DOMUtils,
44470
+ Fun,
44471
+ Arr,
44472
+ Uuid,
44473
+ Tools,
44474
+ NodeType
44475
+ ) {
44476
+ var trim = Tools.trim;
44477
+
44478
+ var create = function (type, title, url, level, attach) {
44479
+ return {
44480
+ type: type,
44481
+ title: title,
44482
+ url: url,
44483
+ level: level,
44484
+ attach: attach
44485
+ };
44486
+ };
44487
+
44488
+ var isChildOfContentEditableTrue = function (node) {
44489
+ while ((node = node.parentNode)) {
44490
+ var value = node.contentEditable;
44491
+ if (value && value !== 'inherit') {
44492
+ return NodeType.isContentEditableTrue(node);
44493
+ }
44494
+ }
44495
+
44496
+ return false;
44497
+ };
44498
+
44499
+ var select = function (selector, root) {
44500
+ return DOMUtils.DOM.select(selector, root);
44501
+ };
44502
+
44503
+ var getElementText = function (elm) {
44504
+ return elm.innerText || elm.textContent;
44505
+ };
44506
+
44507
+ var getOrGenerateId = function (elm) {
44508
+ return elm.id ? elm.id : Uuid.uuid('h');
44509
+ };
44510
+
44511
+ var isAnchor = function (elm) {
44512
+ return elm && elm.nodeName === 'A' && (elm.id || elm.name);
44513
+ };
44514
+
44515
+ var isValidAnchor = function (elm) {
44516
+ return isAnchor(elm) && isEditable(elm);
44517
+ };
44518
+
44519
+ var isHeader = function (elm) {
44520
+ return elm && /^(H[1-6])$/.test(elm.nodeName);
44521
+ };
44522
+
44523
+ var isEditable = function (elm) {
44524
+ return isChildOfContentEditableTrue(elm) && !NodeType.isContentEditableFalse(elm);
44525
+ };
44526
+
44527
+ var isValidHeader = function (elm) {
44528
+ return isHeader(elm) && isEditable(elm);
44529
+ };
44530
+
44531
+ var getLevel = function (elm) {
44532
+ return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0;
44533
+ };
44534
+
44535
+ var headerTarget = function (elm) {
44536
+ var headerId = getOrGenerateId(elm);
44537
+
44538
+ var attach = function () {
44539
+ elm.id = headerId;
44540
+ };
44541
+
44542
+ return create('header', getElementText(elm), '#' + headerId, getLevel(elm), attach);
44543
+ };
44544
+
44545
+ var anchorTarget = function (elm) {
44546
+ var anchorId = elm.id || elm.name;
44547
+ var anchorText = getElementText(elm);
44548
+
44549
+ return create('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, Fun.noop);
44550
+ };
44551
+
44552
+ var getHeaderTargets = function (elms) {
44553
+ return Arr.map(Arr.filter(elms, isValidHeader), headerTarget);
44554
+ };
44555
+
44556
+ var getAnchorTargets = function (elms) {
44557
+ return Arr.map(Arr.filter(elms, isValidAnchor), anchorTarget);
44558
+ };
44559
+
44560
+ var getTargetElements = function (elm) {
44561
+ var elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm);
44562
+ return elms;
44563
+ };
44564
+
44565
+ var hasTitle = function (target) {
44566
+ return trim(target.title).length > 0;
44567
+ };
44568
+
44569
+ var find = function (elm) {
44570
+ var elms = getTargetElements(elm);
44571
+ return Arr.filter(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle);
44572
+ };
44573
+
44574
+ return {
44575
+ find: find
44576
+ };
44577
+ });
44578
+
43312
44579
  // Included from: js/tinymce/classes/ui/FilePicker.js
43313
44580
 
43314
44581
  /**
@@ -43331,10 +44598,229 @@ define("tinymce/ui/FieldSet", [
43331
44598
  */
43332
44599
  define("tinymce/ui/FilePicker", [
43333
44600
  "tinymce/ui/ComboBox",
43334
- "tinymce/util/Tools"
43335
- ], function(ComboBox, Tools) {
44601
+ "tinymce/util/Tools",
44602
+ "tinymce/util/Arr",
44603
+ "tinymce/util/Fun",
44604
+ "tinymce/util/VK",
44605
+ "tinymce/content/LinkTargets"
44606
+ ], function(ComboBox, Tools, Arr, Fun, VK, LinkTargets) {
43336
44607
  "use strict";
43337
44608
 
44609
+ var history = {};
44610
+ var HISTORY_LENGTH = 5;
44611
+
44612
+ var toMenuItem = function (target) {
44613
+ return {
44614
+ title: target.title,
44615
+ value: {
44616
+ title: {raw: target.title},
44617
+ url: target.url,
44618
+ attach: target.attach
44619
+ }
44620
+ };
44621
+ };
44622
+
44623
+ var toMenuItems = function (targets) {
44624
+ return Tools.map(targets, toMenuItem);
44625
+ };
44626
+
44627
+ var staticMenuItem = function (title, url) {
44628
+ return {
44629
+ title: title,
44630
+ value: {
44631
+ title: title,
44632
+ url: url,
44633
+ attach: Fun.noop
44634
+ }
44635
+ };
44636
+ };
44637
+
44638
+ var isUniqueUrl = function (url, targets) {
44639
+ var foundTarget = Arr.find(targets, function (target) {
44640
+ return target.url === url;
44641
+ });
44642
+
44643
+ return !foundTarget;
44644
+ };
44645
+
44646
+ var getSetting = function (editorSettings, name, defaultValue) {
44647
+ var value = name in editorSettings ? editorSettings[name] : defaultValue;
44648
+ return value === false ? null : value;
44649
+ };
44650
+
44651
+ var createMenuItems = function (term, targets, fileType, editorSettings) {
44652
+ var separator = {title: '-'};
44653
+
44654
+ var fromHistoryMenuItems = function (history) {
44655
+ var uniqueHistory = Arr.filter(history[fileType], function (url) {
44656
+ return isUniqueUrl(url, targets);
44657
+ });
44658
+
44659
+ return Tools.map(uniqueHistory, function (url) {
44660
+ return {
44661
+ title: url,
44662
+ value: {
44663
+ title: url,
44664
+ url: url,
44665
+ attach: Fun.noop
44666
+ }
44667
+ };
44668
+ });
44669
+ };
44670
+
44671
+ var fromMenuItems = function (type) {
44672
+ var filteredTargets = Arr.filter(targets, function (target) {
44673
+ return target.type == type;
44674
+ });
44675
+
44676
+ return toMenuItems(filteredTargets);
44677
+ };
44678
+
44679
+ var anchorMenuItems = function () {
44680
+ var anchorMenuItems = fromMenuItems('anchor');
44681
+ var topAnchor = getSetting(editorSettings, 'anchor_top', '#top');
44682
+ var bottomAchor = getSetting(editorSettings, 'anchor_bottom', '#bottom');
44683
+
44684
+ if (topAnchor !== null) {
44685
+ anchorMenuItems.unshift(staticMenuItem('<top>', topAnchor));
44686
+ }
44687
+
44688
+ if (bottomAchor !== null) {
44689
+ anchorMenuItems.push(staticMenuItem('<bottom>', bottomAchor));
44690
+ }
44691
+
44692
+ return anchorMenuItems;
44693
+ };
44694
+
44695
+ var join = function (items) {
44696
+ return Arr.reduce(items, function (a, b) {
44697
+ var bothEmpty = a.length === 0 || b.length === 0;
44698
+ return bothEmpty ? a.concat(b) : a.concat(separator, b);
44699
+ }, []);
44700
+ };
44701
+
44702
+ if (editorSettings.typeahead_urls === false) {
44703
+ return [];
44704
+ }
44705
+
44706
+ return fileType === 'file' ? join([
44707
+ filterByQuery(term, fromHistoryMenuItems(history)),
44708
+ filterByQuery(term, fromMenuItems('header')),
44709
+ filterByQuery(term, anchorMenuItems())
44710
+ ]) : filterByQuery(term, fromHistoryMenuItems(history));
44711
+ };
44712
+
44713
+ var addToHistory = function (url, fileType) {
44714
+ var items = history[fileType];
44715
+
44716
+ if (!/^https?/.test(url)) {
44717
+ return;
44718
+ }
44719
+
44720
+ if (items) {
44721
+ if (Arr.indexOf(items, url) === -1) {
44722
+ history[fileType] = items.slice(0, HISTORY_LENGTH).concat(url);
44723
+ }
44724
+ } else {
44725
+ history[fileType] = [url];
44726
+ }
44727
+ };
44728
+
44729
+ var filterByQuery = function (term, menuItems) {
44730
+ var lowerCaseTerm = term.toLowerCase();
44731
+ var result = Tools.grep(menuItems, function (item) {
44732
+ return item.title.toLowerCase().indexOf(lowerCaseTerm) !== -1;
44733
+ });
44734
+
44735
+ return result.length === 1 && result[0].title === term ? [] : result;
44736
+ };
44737
+
44738
+ var getTitle = function (linkDetails) {
44739
+ var title = linkDetails.title;
44740
+ return title.raw ? title.raw : title;
44741
+ };
44742
+
44743
+ var setupAutoCompleteHandler = function (ctrl, editorSettings, bodyElm, fileType) {
44744
+ var autocomplete = function (term) {
44745
+ var linkTargets = LinkTargets.find(bodyElm);
44746
+ var menuItems = createMenuItems(term, linkTargets, fileType, editorSettings);
44747
+ ctrl.showAutoComplete(menuItems, term);
44748
+ };
44749
+
44750
+ ctrl.on('autocomplete', function () {
44751
+ autocomplete(ctrl.value());
44752
+ });
44753
+
44754
+ ctrl.on('selectitem', function (e) {
44755
+ var linkDetails = e.value;
44756
+
44757
+ ctrl.value(linkDetails.url);
44758
+ var title = getTitle(linkDetails);
44759
+
44760
+ if (fileType === 'image') {
44761
+ ctrl.fire('change', {meta: {alt: title, attach: linkDetails.attach}});
44762
+ } else {
44763
+ ctrl.fire('change', {meta: {text: title, attach: linkDetails.attach}});
44764
+ }
44765
+
44766
+ ctrl.focus();
44767
+ });
44768
+
44769
+ ctrl.on('click', function () {
44770
+ if (ctrl.value().length === 0) {
44771
+ autocomplete('');
44772
+ }
44773
+ });
44774
+
44775
+ ctrl.on('PostRender', function () {
44776
+ ctrl.getRoot().on('submit', function (e) {
44777
+ if (!e.isDefaultPrevented()) {
44778
+ addToHistory(ctrl.value(), fileType);
44779
+ }
44780
+ });
44781
+ });
44782
+ };
44783
+
44784
+ var statusToUiState = function (result) {
44785
+ var status = result.status, message = result.message;
44786
+
44787
+ if (status === 'valid') {
44788
+ return {status: 'ok', message: message};
44789
+ } else if (status === 'unknown') {
44790
+ return {status: 'warn', message: message};
44791
+ } else if (status === 'invalid') {
44792
+ return {status: 'warn', message: message};
44793
+ } else {
44794
+ return {status: 'none', message: ''};
44795
+ }
44796
+ };
44797
+
44798
+ var setupLinkValidatorHandler = function (ctrl, editorSettings, fileType) {
44799
+ var validatorHandler = editorSettings.filepicker_validator_handler;
44800
+ if (validatorHandler) {
44801
+ var validateUrl = function (url) {
44802
+ if (url.length === 0) {
44803
+ ctrl.statusLevel('none');
44804
+ return;
44805
+ }
44806
+
44807
+ validatorHandler({
44808
+ url: url,
44809
+ type: fileType
44810
+ }, function (result) {
44811
+ var uiState = statusToUiState(result);
44812
+
44813
+ ctrl.statusMessage(uiState.message);
44814
+ ctrl.statusLevel(uiState.status);
44815
+ });
44816
+ };
44817
+
44818
+ ctrl.state.on('change:value', function (e) {
44819
+ validateUrl(e.value);
44820
+ });
44821
+ }
44822
+ };
44823
+
43338
44824
  return ComboBox.extend({
43339
44825
  /**
43340
44826
  * Constructs a new control instance with the specified settings.
@@ -43345,6 +44831,7 @@ define("tinymce/ui/FilePicker", [
43345
44831
  init: function(settings) {
43346
44832
  var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings;
43347
44833
  var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes;
44834
+ var fileType = settings.filetype;
43348
44835
 
43349
44836
  settings.spellcheck = false;
43350
44837
 
@@ -43353,13 +44840,13 @@ define("tinymce/ui/FilePicker", [
43353
44840
  fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
43354
44841
  }
43355
44842
 
43356
- if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype]) {
44843
+ if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType]) {
43357
44844
  fileBrowserCallback = editorSettings.file_picker_callback;
43358
- if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
44845
+ if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) {
43359
44846
  actionCallback = function() {
43360
44847
  var meta = self.fire('beforecall').meta;
43361
44848
 
43362
- meta = Tools.extend({filetype: settings.filetype}, meta);
44849
+ meta = Tools.extend({filetype: fileType}, meta);
43363
44850
 
43364
44851
  // file_picker_callback(callback, currentValue, metaData)
43365
44852
  fileBrowserCallback.call(
@@ -43374,12 +44861,12 @@ define("tinymce/ui/FilePicker", [
43374
44861
  } else {
43375
44862
  // Legacy callback: file_picker_callback(id, currentValue, filetype, window)
43376
44863
  fileBrowserCallback = editorSettings.file_browser_callback;
43377
- if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
44864
+ if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) {
43378
44865
  actionCallback = function() {
43379
44866
  fileBrowserCallback(
43380
44867
  self.getEl('inp').id,
43381
44868
  self.value(),
43382
- settings.filetype,
44869
+ fileType,
43383
44870
  window
43384
44871
  );
43385
44872
  };
@@ -43393,6 +44880,9 @@ define("tinymce/ui/FilePicker", [
43393
44880
  }
43394
44881
 
43395
44882
  self._super(settings);
44883
+
44884
+ setupAutoCompleteHandler(self, editorSettings, editor.getBody(), fileType);
44885
+ setupLinkValidatorHandler(self, editorSettings, fileType);
43396
44886
  }
43397
44887
  });
43398
44888
  });
@@ -43769,12 +45259,19 @@ define("tinymce/ui/FormatControls", [
43769
45259
  "tinymce/ui/Widget",
43770
45260
  "tinymce/ui/FloatPanel",
43771
45261
  "tinymce/util/Tools",
45262
+ "tinymce/util/Arr",
43772
45263
  "tinymce/dom/DOMUtils",
43773
45264
  "tinymce/EditorManager",
43774
45265
  "tinymce/Env"
43775
- ], function(Control, Widget, FloatPanel, Tools, DOMUtils, EditorManager, Env) {
45266
+ ], function(Control, Widget, FloatPanel, Tools, Arr, DOMUtils, EditorManager, Env) {
43776
45267
  var each = Tools.each;
43777
45268
 
45269
+ var flatten = function (ar) {
45270
+ return Arr.reduce(ar, function (result, item) {
45271
+ return result.concat(item);
45272
+ }, []);
45273
+ };
45274
+
43778
45275
  EditorManager.on('AddEditor', function(e) {
43779
45276
  var editor = e.editor;
43780
45277
 
@@ -44053,8 +45550,6 @@ define("tinymce/ui/FormatControls", [
44053
45550
  // Simple command controls with format state
44054
45551
  each({
44055
45552
  blockquote: ['Blockquote', 'mceBlockQuote'],
44056
- numlist: ['Numbered list', 'InsertOrderedList'],
44057
- bullist: ['Bullet list', 'InsertUnorderedList'],
44058
45553
  subscript: ['Subscript', 'Subscript'],
44059
45554
  superscript: ['Superscript', 'Superscript'],
44060
45555
  alignleft: ['Align left', 'JustifyLeft'],
@@ -44097,6 +45592,71 @@ define("tinymce/ui/FormatControls", [
44097
45592
  self.active(editor.hasVisual);
44098
45593
  }
44099
45594
 
45595
+ var trimMenuItems = function (menuItems) {
45596
+ var outputMenuItems = menuItems;
45597
+
45598
+ if (outputMenuItems.length > 0 && outputMenuItems[0].text === '-') {
45599
+ outputMenuItems = outputMenuItems.slice(1);
45600
+ }
45601
+
45602
+ if (outputMenuItems.length > 0 && outputMenuItems[outputMenuItems.length - 1].text === '-') {
45603
+ outputMenuItems = outputMenuItems.slice(0, outputMenuItems.length - 1);
45604
+ }
45605
+
45606
+ return outputMenuItems;
45607
+ };
45608
+
45609
+ var createCustomMenuItems = function (names) {
45610
+ var items, nameList;
45611
+
45612
+ if (typeof names === 'string') {
45613
+ nameList = names.split(' ');
45614
+ } else if (Tools.isArray(names)) {
45615
+ return flatten(Tools.map(names, createCustomMenuItems));
45616
+ }
45617
+
45618
+ items = Tools.grep(nameList, function (name) {
45619
+ return name === '|' || name in editor.menuItems;
45620
+ });
45621
+
45622
+ return Tools.map(items, function (name) {
45623
+ return name === '|' ? {text: '-'} : editor.menuItems[name];
45624
+ });
45625
+ };
45626
+
45627
+ var createContextMenuItems = function (context) {
45628
+ var outputMenuItems = [{text: '-'}];
45629
+ var menuItems = Tools.grep(editor.menuItems, function (menuItem) {
45630
+ return menuItem.context === context;
45631
+ });
45632
+
45633
+ Tools.each(menuItems, function (menuItem) {
45634
+ if (menuItem.separator == 'before') {
45635
+ outputMenuItems.push({text: '|'});
45636
+ }
45637
+
45638
+ if (menuItem.prependToContext) {
45639
+ outputMenuItems.unshift(menuItem);
45640
+ } else {
45641
+ outputMenuItems.push(menuItem);
45642
+ }
45643
+
45644
+ if (menuItem.separator == 'after') {
45645
+ outputMenuItems.push({text: '|'});
45646
+ }
45647
+ });
45648
+
45649
+ return outputMenuItems;
45650
+ };
45651
+
45652
+ var createInsertMenu = function (editorSettings) {
45653
+ if (editorSettings.insert_button_items) {
45654
+ return trimMenuItems(createCustomMenuItems(editorSettings.insert_button_items));
45655
+ } else {
45656
+ return trimMenuItems(createContextMenuItems('insert'));
45657
+ }
45658
+ };
45659
+
44100
45660
  editor.addButton('undo', {
44101
45661
  tooltip: 'Undo',
44102
45662
  onPostRender: toggleUndoRedoState('undo'),
@@ -44144,6 +45704,16 @@ define("tinymce/ui/FormatControls", [
44144
45704
  cmd: 'Delete'
44145
45705
  });
44146
45706
 
45707
+ editor.addButton('insert', {
45708
+ type: 'menubutton',
45709
+ icon: 'insert',
45710
+ menu: [],
45711
+ oncreatemenu: function () {
45712
+ this.menu.add(createInsertMenu(editor.settings));
45713
+ this.menu.renderNew();
45714
+ }
45715
+ });
45716
+
44147
45717
  each({
44148
45718
  cut: ['Cut', 'Cut', 'Meta+X'],
44149
45719
  copy: ['Copy', 'Copy', 'Meta+C'],
@@ -44179,10 +45749,61 @@ define("tinymce/ui/FormatControls", [
44179
45749
  }
44180
45750
  }
44181
45751
 
45752
+ function hideMenuObjects(menu) {
45753
+ var count = menu.length;
45754
+
45755
+ Tools.each(menu, function (item) {
45756
+ if (item.menu) {
45757
+ item.hidden = hideMenuObjects(item.menu) === 0;
45758
+ }
45759
+
45760
+ var formatName = item.format;
45761
+ if (formatName) {
45762
+ item.hidden = !editor.formatter.canApply(formatName);
45763
+ }
45764
+
45765
+ if (item.hidden) {
45766
+ count--;
45767
+ }
45768
+ });
45769
+
45770
+ return count;
45771
+ }
45772
+
45773
+ function hideFormatMenuItems(menu) {
45774
+ var count = menu.items().length;
45775
+
45776
+ menu.items().each(function (item) {
45777
+ if (item.menu) {
45778
+ item.visible(hideFormatMenuItems(item.menu) > 0);
45779
+ }
45780
+
45781
+ if (!item.menu && item.settings.menu) {
45782
+ item.visible(hideMenuObjects(item.settings.menu) > 0);
45783
+ }
45784
+
45785
+ var formatName = item.settings.format;
45786
+ if (formatName) {
45787
+ item.visible(editor.formatter.canApply(formatName));
45788
+ }
45789
+
45790
+ if (!item.visible()) {
45791
+ count--;
45792
+ }
45793
+ });
45794
+
45795
+ return count;
45796
+ }
45797
+
44182
45798
  editor.addButton('styleselect', {
44183
45799
  type: 'menubutton',
44184
45800
  text: 'Formats',
44185
- menu: formatMenu
45801
+ menu: formatMenu,
45802
+ onShowMenu: function () {
45803
+ if (editor.settings.style_formats_autohide) {
45804
+ hideFormatMenuItems(this.menu);
45805
+ }
45806
+ }
44186
45807
  });
44187
45808
 
44188
45809
  editor.addButton('formatselect', function() {
@@ -45084,6 +46705,7 @@ define("tinymce/ui/MenuButton", [
45084
46705
  self.menu.show();
45085
46706
  self.menu.layoutRect({w: self.layoutRect().w});
45086
46707
  self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
46708
+ self.fire('showmenu');
45087
46709
  },
45088
46710
 
45089
46711
  /**
@@ -45172,7 +46794,7 @@ define("tinymce/ui/MenuButton", [
45172
46794
  self.showMenu();
45173
46795
 
45174
46796
  if (e.aria) {
45175
- self.menu.items()[0].focus();
46797
+ self.menu.items().filter(':visible')[0].focus();
45176
46798
  }
45177
46799
  }
45178
46800
  });
@@ -45369,9 +46991,11 @@ define("tinymce/ui/MenuItem", [
45369
46991
  menu.hide();
45370
46992
  });
45371
46993
  menu.on('show hide', function(e) {
45372
- e.control.items().each(function(ctrl) {
45373
- ctrl.active(ctrl.settings.selected);
45374
- });
46994
+ if (e.control.items) {
46995
+ e.control.items().each(function(ctrl) {
46996
+ ctrl.active(ctrl.settings.selected);
46997
+ });
46998
+ }
45375
46999
  }).fire('show');
45376
47000
 
45377
47001
  menu.on('hide', function(e) {
@@ -45435,8 +47059,9 @@ define("tinymce/ui/MenuItem", [
45435
47059
  * @return {String} HTML representing the control.
45436
47060
  */
45437
47061
  renderHtml: function() {
45438
- var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self.state.get('text'));
47062
+ var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.state.get('text');
45439
47063
  var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
47064
+ var url = self.encode(settings.url), iconHtml = '';
45440
47065
 
45441
47066
  // Converts shortcut format to Mac/PC variants
45442
47067
  function convertShortcut(shortcut) {
@@ -45468,6 +47093,24 @@ define("tinymce/ui/MenuItem", [
45468
47093
  return shortcut.join('+');
45469
47094
  }
45470
47095
 
47096
+ function escapeRegExp(str) {
47097
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
47098
+ }
47099
+
47100
+ function markMatches(text) {
47101
+ var match = settings.match || '';
47102
+
47103
+ return match ? text.replace(new RegExp(escapeRegExp(match), 'gi'), function (match) {
47104
+ return '!mce~match[' + match + ']mce~match!';
47105
+ }) : text;
47106
+ }
47107
+
47108
+ function boldMatches(text) {
47109
+ return text.
47110
+ replace(new RegExp(escapeRegExp('!mce~match['), 'g'), '<b>').
47111
+ replace(new RegExp(escapeRegExp(']mce~match!'), 'g'), '</b>');
47112
+ }
47113
+
45471
47114
  if (icon) {
45472
47115
  self.parent().classes.add('menu-has-icons');
45473
47116
  }
@@ -45481,13 +47124,18 @@ define("tinymce/ui/MenuItem", [
45481
47124
  }
45482
47125
 
45483
47126
  icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
47127
+ iconHtml = (text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '');
47128
+
47129
+ text = boldMatches(self.encode(markMatches(text)));
47130
+ url = boldMatches(self.encode(markMatches(url)));
45484
47131
 
45485
47132
  return (
45486
47133
  '<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' +
45487
- (text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
47134
+ iconHtml +
45488
47135
  (text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
45489
47136
  (shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
45490
47137
  (settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
47138
+ (url ? '<div class="' + prefix + 'menu-item-link">' + url + '</div>' : '') +
45491
47139
  '</div>'
45492
47140
  );
45493
47141
  },
@@ -46181,7 +47829,7 @@ define("tinymce/ui/SelectBox", [
46181
47829
  *
46182
47830
  * @constructor
46183
47831
  * @param {Object} settings Name/value object with settings.
46184
- * @setting {Array} values Array with values to add to list box.
47832
+ * @setting {Array} options Array with options to add to the select box.
46185
47833
  */
46186
47834
  init: function(settings) {
46187
47835
  var self = this;
@@ -46828,7 +48476,7 @@ define("tinymce/ui/TabPanel", [
46828
48476
  this.on('click', function(e) {
46829
48477
  var targetParent = e.target.parentNode;
46830
48478
 
46831
- if (e.target.parentNode.id == self._id + '-head') {
48479
+ if (targetParent && targetParent.id == self._id + '-head') {
46832
48480
  var i = targetParent.childNodes.length;
46833
48481
 
46834
48482
  while (i--) {
@@ -47129,6 +48777,11 @@ define("tinymce/Register", [
47129
48777
  }
47130
48778
  }
47131
48779
 
48780
+ if (typeof module === 'object') {
48781
+ /* global module */
48782
+ module.exports = window.tinymce;
48783
+ }
48784
+
47132
48785
  return {};
47133
48786
  });
47134
48787