tinymce-rails 4.3.12 → 4.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5398fec14da6871a2fbc62b1523d6fbe42988ac
4
- data.tar.gz: d6bbbfd2a8d3c2d20fd6795d589896dab8eeed4e
3
+ metadata.gz: 7a80d0da6700b959691c745e966153c3e9d9f995
4
+ data.tar.gz: 043a0a889f0bad84d5d0fb34dbe24796ca6d324b
5
5
  SHA512:
6
- metadata.gz: 0ab1350552111c75a21d5e7ed164555b9a67d0ca6140ee6b5d77857df663363dc0f93a3f1542b3a30a54d066daf72340ccdeb8f17c8f0bf4304cd5f149213846
7
- data.tar.gz: 42e1d8ec35f69a26b4a717be27c402f4abf008c4c937704adb1f75b3fb874f266bfebf688a814515ae1713e4bd690f3d830f25d8068be94e184150394b78b08b
6
+ metadata.gz: a14a5162ed2111fdbd4d1fb5d19d00915b5ec620b172642bde30ed89f2443ec857242fbb6759a8bd53d67f210b04a777fd67bd5fae7b9f660144ebcfff2b698f
7
+ data.tar.gz: 15aed49452e2171bae4bfd48e4b84df9868ba00551430686a94795a17499e94d637704357a6e5bf841fb646d2589ce7407572a4ea63324670beee464bc205a47
@@ -1,4 +1,4 @@
1
- // 4.3.12 (2016-05-10)
1
+ // 4.3.13 (2016-06-08)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -921,7 +921,10 @@ define("tinymce/dom/EventUtils", [
921
921
 
922
922
  var eventExpandoPrefix = "mce-data-";
923
923
  var mouseEventRe = /^(?:mouse|contextmenu)|click/;
924
- var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1, webkitMovementX: 1, webkitMovementY: 1};
924
+ var deprecated = {
925
+ keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1,
926
+ webkitMovementX: 1, webkitMovementY: 1, keyIdentifier: 1
927
+ };
925
928
 
926
929
  /**
927
930
  * Binds a native event to a callback on the speified target.
@@ -9507,6 +9510,11 @@ define("tinymce/dom/ScriptLoader", [
9507
9510
  }
9508
9511
  };
9509
9512
 
9513
+ this.remove = function(url) {
9514
+ delete states[url];
9515
+ delete scriptLoadedCallbacks[url];
9516
+ };
9517
+
9510
9518
  /**
9511
9519
  * Starts the loading of the queue.
9512
9520
  *
@@ -9710,6 +9718,11 @@ define("tinymce/AddOnManager", [
9710
9718
  return addOn;
9711
9719
  },
9712
9720
 
9721
+ remove: function(name) {
9722
+ delete this.urls[name];
9723
+ delete this.lookup[name];
9724
+ },
9725
+
9713
9726
  createUrl: function(baseUrl, dep) {
9714
9727
  if (typeof dep === "object") {
9715
9728
  return dep;
@@ -10709,7 +10722,7 @@ define("tinymce/dom/RangeUtils", [
10709
10722
  element = doc.elementFromPoint(clientX, clientY);
10710
10723
  rng = doc.body.createTextRange();
10711
10724
 
10712
- if (element.tagName == 'HTML') {
10725
+ if (!element || element.tagName == 'HTML') {
10713
10726
  element = doc.body;
10714
10727
  }
10715
10728
 
@@ -22826,6 +22839,560 @@ define("tinymce/caret/CaretWalker", [
22826
22839
  };
22827
22840
  });
22828
22841
 
22842
+ // Included from: js/tinymce/classes/InsertList.js
22843
+
22844
+ /**
22845
+ * InsertList.js
22846
+ *
22847
+ * Released under LGPL License.
22848
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
22849
+ *
22850
+ * License: http://www.tinymce.com/license
22851
+ * Contributing: http://www.tinymce.com/contributing
22852
+ */
22853
+
22854
+ /**
22855
+ * Handles inserts of lists into the editor instance.
22856
+ *
22857
+ * @class tinymce.InsertList
22858
+ * @private
22859
+ */
22860
+ define("tinymce/InsertList", [
22861
+ "tinymce/util/Tools",
22862
+ "tinymce/caret/CaretWalker",
22863
+ "tinymce/caret/CaretPosition"
22864
+ ], function(Tools, CaretWalker, CaretPosition) {
22865
+ var isListFragment = function(fragment) {
22866
+ var firstChild = fragment.firstChild;
22867
+ var lastChild = fragment.lastChild;
22868
+
22869
+ // Skip meta since it's likely <meta><ul>..</ul>
22870
+ if (firstChild && firstChild.name === 'meta') {
22871
+ firstChild = firstChild.next;
22872
+ }
22873
+
22874
+ // Skip mce_marker since it's likely <ul>..</ul><span id="mce_marker"></span>
22875
+ if (lastChild && lastChild.attr('id') === 'mce_marker') {
22876
+ lastChild = lastChild.prev;
22877
+ }
22878
+
22879
+ if (!firstChild || firstChild !== lastChild) {
22880
+ return false;
22881
+ }
22882
+
22883
+ return firstChild.name === 'ul' || firstChild.name === 'ol';
22884
+ };
22885
+
22886
+ var cleanupDomFragment = function (domFragment) {
22887
+ var firstChild = domFragment.firstChild;
22888
+ var lastChild = domFragment.lastChild;
22889
+
22890
+ // TODO: remove the meta tag from paste logic
22891
+ if (firstChild && firstChild.nodeName === 'META') {
22892
+ firstChild.parentNode.removeChild(firstChild);
22893
+ }
22894
+
22895
+ if (lastChild && lastChild.id === 'mce_marker') {
22896
+ lastChild.parentNode.removeChild(lastChild);
22897
+ }
22898
+
22899
+ return domFragment;
22900
+ };
22901
+
22902
+ var toDomFragment = function(dom, serializer, fragment) {
22903
+ var html = serializer.serialize(fragment);
22904
+ var domFragment = dom.createFragment(html);
22905
+
22906
+ return cleanupDomFragment(domFragment);
22907
+ };
22908
+
22909
+ var listItems = function(elm) {
22910
+ return Tools.grep(elm.childNodes, function(child) {
22911
+ return child.nodeName === 'LI';
22912
+ });
22913
+ };
22914
+
22915
+ var isEmpty = function (elm) {
22916
+ return !elm.firstChild;
22917
+ };
22918
+
22919
+ var trimListItems = function(elms) {
22920
+ return elms.length > 0 && isEmpty(elms[elms.length - 1]) ? elms.slice(0, -1) : elms;
22921
+ };
22922
+
22923
+ var getParentLi = function(dom, node) {
22924
+ var parentBlock = dom.getParent(node, dom.isBlock);
22925
+ return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null;
22926
+ };
22927
+
22928
+ var isParentBlockLi = function(dom, node) {
22929
+ return !!getParentLi(dom, node);
22930
+ };
22931
+
22932
+ var getSplit = function(parentNode, rng) {
22933
+ var beforeRng = rng.cloneRange();
22934
+ var afterRng = rng.cloneRange();
22935
+
22936
+ beforeRng.setStartBefore(parentNode);
22937
+ afterRng.setEndAfter(parentNode);
22938
+
22939
+ return [
22940
+ beforeRng.cloneContents(),
22941
+ afterRng.cloneContents()
22942
+ ];
22943
+ };
22944
+
22945
+ var findFirstIn = function(node, rootNode) {
22946
+ var caretPos = CaretPosition.before(node);
22947
+ var caretWalker = new CaretWalker(rootNode);
22948
+ var newCaretPos = caretWalker.next(caretPos);
22949
+
22950
+ return newCaretPos ? newCaretPos.toRange() : null;
22951
+ };
22952
+
22953
+ var findLastOf = function(node, rootNode) {
22954
+ var caretPos = CaretPosition.after(node);
22955
+ var caretWalker = new CaretWalker(rootNode);
22956
+ var newCaretPos = caretWalker.prev(caretPos);
22957
+
22958
+ return newCaretPos ? newCaretPos.toRange() : null;
22959
+ };
22960
+
22961
+ var insertMiddle = function(target, elms, rootNode, rng) {
22962
+ var parts = getSplit(target, rng);
22963
+ var parentElm = target.parentNode;
22964
+
22965
+ parentElm.insertBefore(parts[0], target);
22966
+ Tools.each(elms, function(li) {
22967
+ parentElm.insertBefore(li, target);
22968
+ });
22969
+ parentElm.insertBefore(parts[1], target);
22970
+ parentElm.removeChild(target);
22971
+
22972
+ return findLastOf(elms[elms.length - 1], rootNode);
22973
+ };
22974
+
22975
+ var insertBefore = function(target, elms, rootNode) {
22976
+ var parentElm = target.parentNode;
22977
+
22978
+ Tools.each(elms, function(elm) {
22979
+ parentElm.insertBefore(elm, target);
22980
+ });
22981
+
22982
+ return findFirstIn(target, rootNode);
22983
+ };
22984
+
22985
+ var insertAfter = function(target, elms, rootNode, dom) {
22986
+ dom.insertAfter(elms.reverse(), target);
22987
+ return findLastOf(elms[0], rootNode);
22988
+ };
22989
+
22990
+ var insertAtCaret = function(serializer, dom, rng, fragment) {
22991
+ var domFragment = toDomFragment(dom, serializer, fragment);
22992
+ var liTarget = getParentLi(dom, rng.startContainer);
22993
+ var liElms = trimListItems(listItems(domFragment.firstChild));
22994
+ var BEGINNING = 1, END = 2;
22995
+ var rootNode = dom.getRoot();
22996
+
22997
+ var isAt = function(location) {
22998
+ var caretPos = CaretPosition.fromRangeStart(rng);
22999
+ var caretWalker = new CaretWalker(dom.getRoot());
23000
+ var newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos);
23001
+
23002
+ return newPos ? getParentLi(dom, newPos.getNode()) !== liTarget : true;
23003
+ };
23004
+
23005
+ if (isAt(BEGINNING)) {
23006
+ return insertBefore(liTarget, liElms, rootNode);
23007
+ } else if (isAt(END)) {
23008
+ return insertAfter(liTarget, liElms, rootNode, dom);
23009
+ }
23010
+
23011
+ return insertMiddle(liTarget, liElms, rootNode, rng);
23012
+ };
23013
+
23014
+ return {
23015
+ isListFragment: isListFragment,
23016
+ insertAtCaret: insertAtCaret,
23017
+ isParentBlockLi: isParentBlockLi,
23018
+ trimListItems: trimListItems,
23019
+ listItems: listItems
23020
+ };
23021
+ });
23022
+
23023
+ // Included from: js/tinymce/classes/InsertContent.js
23024
+
23025
+ /**
23026
+ * InsertContent.js
23027
+ *
23028
+ * Released under LGPL License.
23029
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
23030
+ *
23031
+ * License: http://www.tinymce.com/license
23032
+ * Contributing: http://www.tinymce.com/contributing
23033
+ */
23034
+
23035
+ /**
23036
+ * Handles inserts of contents into the editor instance.
23037
+ *
23038
+ * @class tinymce.InsertContent
23039
+ * @private
23040
+ */
23041
+ define("tinymce/InsertContent", [
23042
+ "tinymce/Env",
23043
+ "tinymce/util/Tools",
23044
+ "tinymce/html/Serializer",
23045
+ "tinymce/caret/CaretWalker",
23046
+ "tinymce/caret/CaretPosition",
23047
+ "tinymce/dom/ElementUtils",
23048
+ "tinymce/dom/NodeType",
23049
+ "tinymce/InsertList"
23050
+ ], function(Env, Tools, Serializer, CaretWalker, CaretPosition, ElementUtils, NodeType, InsertList) {
23051
+ var isTableCell = NodeType.matchNodeNames('td th');
23052
+
23053
+ var insertAtCaret = function(editor, value) {
23054
+ var parser, serializer, parentNode, rootNode, fragment, args;
23055
+ var marker, rng, node, node2, bookmarkHtml, merge, data;
23056
+ var textInlineElements = editor.schema.getTextInlineElements();
23057
+ var selection = editor.selection, dom = editor.dom;
23058
+
23059
+ function trimOrPaddLeftRight(html) {
23060
+ var rng, container, offset;
23061
+
23062
+ rng = selection.getRng(true);
23063
+ container = rng.startContainer;
23064
+ offset = rng.startOffset;
23065
+
23066
+ function hasSiblingText(siblingName) {
23067
+ return container[siblingName] && container[siblingName].nodeType == 3;
23068
+ }
23069
+
23070
+ if (container.nodeType == 3) {
23071
+ if (offset > 0) {
23072
+ html = html.replace(/^&nbsp;/, ' ');
23073
+ } else if (!hasSiblingText('previousSibling')) {
23074
+ html = html.replace(/^ /, '&nbsp;');
23075
+ }
23076
+
23077
+ if (offset < container.length) {
23078
+ html = html.replace(/&nbsp;(<br>|)$/, ' ');
23079
+ } else if (!hasSiblingText('nextSibling')) {
23080
+ html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
23081
+ }
23082
+ }
23083
+
23084
+ return html;
23085
+ }
23086
+
23087
+ // Removes &nbsp; from a [b] c -> a &nbsp;c -> a c
23088
+ function trimNbspAfterDeleteAndPaddValue() {
23089
+ var rng, container, offset;
23090
+
23091
+ rng = selection.getRng(true);
23092
+ container = rng.startContainer;
23093
+ offset = rng.startOffset;
23094
+
23095
+ if (container.nodeType == 3 && rng.collapsed) {
23096
+ if (container.data[offset] === '\u00a0') {
23097
+ container.deleteData(offset, 1);
23098
+
23099
+ if (!/[\u00a0| ]$/.test(value)) {
23100
+ value += ' ';
23101
+ }
23102
+ } else if (container.data[offset - 1] === '\u00a0') {
23103
+ container.deleteData(offset - 1, 1);
23104
+
23105
+ if (!/[\u00a0| ]$/.test(value)) {
23106
+ value = ' ' + value;
23107
+ }
23108
+ }
23109
+ }
23110
+ }
23111
+
23112
+ function markInlineFormatElements(fragment) {
23113
+ if (merge) {
23114
+ for (node = fragment.firstChild; node; node = node.walk(true)) {
23115
+ if (textInlineElements[node.name]) {
23116
+ node.attr('data-mce-new', "true");
23117
+ }
23118
+ }
23119
+ }
23120
+ }
23121
+
23122
+ function reduceInlineTextElements() {
23123
+ if (merge) {
23124
+ var root = editor.getBody(), elementUtils = new ElementUtils(dom);
23125
+
23126
+ Tools.each(dom.select('*[data-mce-new]'), function(node) {
23127
+ node.removeAttribute('data-mce-new');
23128
+
23129
+ for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
23130
+ if (elementUtils.compare(testNode, node)) {
23131
+ dom.remove(node, true);
23132
+ }
23133
+ }
23134
+ });
23135
+ }
23136
+ }
23137
+
23138
+ function markFragmentElements(fragment) {
23139
+ var node = fragment;
23140
+
23141
+ while ((node = node.walk())) {
23142
+ if (node.type === 1) {
23143
+ node.attr('data-mce-fragment', '1');
23144
+ }
23145
+ }
23146
+ }
23147
+
23148
+ function umarkFragmentElements(elm) {
23149
+ Tools.each(elm.getElementsByTagName('*'), function(elm) {
23150
+ elm.removeAttribute('data-mce-fragment');
23151
+ });
23152
+ }
23153
+
23154
+ function isPartOfFragment(node) {
23155
+ return !!node.getAttribute('data-mce-fragment');
23156
+ }
23157
+
23158
+ function canHaveChildren(node) {
23159
+ return node && !editor.schema.getShortEndedElements()[node.nodeName];
23160
+ }
23161
+
23162
+ function moveSelectionToMarker(marker) {
23163
+ var parentEditableFalseElm, parentBlock, nextRng;
23164
+
23165
+ function getContentEditableFalseParent(node) {
23166
+ var root = editor.getBody();
23167
+
23168
+ for (; node && node !== root; node = node.parentNode) {
23169
+ if (editor.dom.getContentEditable(node) === 'false') {
23170
+ return node;
23171
+ }
23172
+ }
23173
+
23174
+ return null;
23175
+ }
23176
+
23177
+ if (!marker) {
23178
+ return;
23179
+ }
23180
+
23181
+ selection.scrollIntoView(marker);
23182
+
23183
+ // If marker is in cE=false then move selection to that element instead
23184
+ parentEditableFalseElm = getContentEditableFalseParent(marker);
23185
+ if (parentEditableFalseElm) {
23186
+ dom.remove(marker);
23187
+ selection.select(parentEditableFalseElm);
23188
+ return;
23189
+ }
23190
+
23191
+ // Move selection before marker and remove it
23192
+ rng = dom.createRng();
23193
+
23194
+ // If previous sibling is a text node set the selection to the end of that node
23195
+ node = marker.previousSibling;
23196
+ if (node && node.nodeType == 3) {
23197
+ rng.setStart(node, node.nodeValue.length);
23198
+
23199
+ // TODO: Why can't we normalize on IE
23200
+ if (!Env.ie) {
23201
+ node2 = marker.nextSibling;
23202
+ if (node2 && node2.nodeType == 3) {
23203
+ node.appendData(node2.data);
23204
+ node2.parentNode.removeChild(node2);
23205
+ }
23206
+ }
23207
+ } else {
23208
+ // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
23209
+ rng.setStartBefore(marker);
23210
+ rng.setEndBefore(marker);
23211
+ }
23212
+
23213
+ function findNextCaretRng(rng) {
23214
+ var caretPos = CaretPosition.fromRangeStart(rng);
23215
+ var caretWalker = new CaretWalker(editor.getBody());
23216
+
23217
+ caretPos = caretWalker.next(caretPos);
23218
+ if (caretPos) {
23219
+ return caretPos.toRange();
23220
+ }
23221
+ }
23222
+
23223
+ // Remove the marker node and set the new range
23224
+ parentBlock = dom.getParent(marker, dom.isBlock);
23225
+ dom.remove(marker);
23226
+
23227
+ if (parentBlock && dom.isEmpty(parentBlock)) {
23228
+ editor.$(parentBlock).empty();
23229
+
23230
+ rng.setStart(parentBlock, 0);
23231
+ rng.setEnd(parentBlock, 0);
23232
+
23233
+ if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) {
23234
+ rng = nextRng;
23235
+ dom.remove(parentBlock);
23236
+ } else {
23237
+ dom.add(parentBlock, dom.create('br', {'data-mce-bogus': '1'}));
23238
+ }
23239
+ }
23240
+
23241
+ selection.setRng(rng);
23242
+ }
23243
+
23244
+ if (typeof value != 'string') {
23245
+ merge = value.merge;
23246
+ data = value.data;
23247
+ value = value.content;
23248
+ }
23249
+
23250
+ // Check for whitespace before/after value
23251
+ if (/^ | $/.test(value)) {
23252
+ value = trimOrPaddLeftRight(value);
23253
+ }
23254
+
23255
+ // Setup parser and serializer
23256
+ parser = editor.parser;
23257
+ serializer = new Serializer({
23258
+ validate: editor.settings.validate
23259
+ }, editor.schema);
23260
+ bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
23261
+
23262
+ // Run beforeSetContent handlers on the HTML to be inserted
23263
+ args = {content: value, format: 'html', selection: true};
23264
+ editor.fire('BeforeSetContent', args);
23265
+ value = args.content;
23266
+
23267
+ // Add caret at end of contents if it's missing
23268
+ if (value.indexOf('{$caret}') == -1) {
23269
+ value += '{$caret}';
23270
+ }
23271
+
23272
+ // Replace the caret marker with a span bookmark element
23273
+ value = value.replace(/\{\$caret\}/, bookmarkHtml);
23274
+
23275
+ // If selection is at <body>|<p></p> then move it into <body><p>|</p>
23276
+ rng = selection.getRng();
23277
+ var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
23278
+ var body = editor.getBody();
23279
+ if (caretElement === body && selection.isCollapsed()) {
23280
+ if (dom.isBlock(body.firstChild) && canHaveChildren(body.firstChild) && dom.isEmpty(body.firstChild)) {
23281
+ rng = dom.createRng();
23282
+ rng.setStart(body.firstChild, 0);
23283
+ rng.setEnd(body.firstChild, 0);
23284
+ selection.setRng(rng);
23285
+ }
23286
+ }
23287
+
23288
+ // Insert node maker where we will insert the new HTML and get it's parent
23289
+ if (!selection.isCollapsed()) {
23290
+ // Fix for #2595 seems that delete removes one extra character on
23291
+ // WebKit for some odd reason if you double click select a word
23292
+ editor.selection.setRng(editor.selection.getRng());
23293
+ editor.getDoc().execCommand('Delete', false, null);
23294
+ trimNbspAfterDeleteAndPaddValue();
23295
+ }
23296
+
23297
+ parentNode = selection.getNode();
23298
+
23299
+ // Parse the fragment within the context of the parent node
23300
+ var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: data};
23301
+ fragment = parser.parse(value, parserArgs);
23302
+
23303
+ // Custom handling of lists
23304
+ if (InsertList.isListFragment(fragment) && InsertList.isParentBlockLi(dom, parentNode)) {
23305
+ rng = InsertList.insertAtCaret(serializer, dom, editor.selection.getRng(), fragment);
23306
+ editor.selection.setRng(rng);
23307
+ editor.fire('SetContent', args);
23308
+ return;
23309
+ }
23310
+
23311
+ markFragmentElements(fragment);
23312
+ markInlineFormatElements(fragment);
23313
+
23314
+ // Move the caret to a more suitable location
23315
+ node = fragment.lastChild;
23316
+ if (node.attr('id') == 'mce_marker') {
23317
+ marker = node;
23318
+
23319
+ for (node = node.prev; node; node = node.walk(true)) {
23320
+ if (node.type == 3 || !dom.isBlock(node.name)) {
23321
+ if (editor.schema.isValidChild(node.parent.name, 'span')) {
23322
+ node.parent.insert(marker, node, node.name === 'br');
23323
+ }
23324
+ break;
23325
+ }
23326
+ }
23327
+ }
23328
+
23329
+ editor._selectionOverrides.showBlockCaretContainer(parentNode);
23330
+
23331
+ // If parser says valid we can insert the contents into that parent
23332
+ if (!parserArgs.invalid) {
23333
+ value = serializer.serialize(fragment);
23334
+
23335
+ // Check if parent is empty or only has one BR element then set the innerHTML of that parent
23336
+ node = parentNode.firstChild;
23337
+ node2 = parentNode.lastChild;
23338
+ if (!node || (node === node2 && node.nodeName === 'BR')) {
23339
+ dom.setHTML(parentNode, value);
23340
+ } else {
23341
+ selection.setContent(value);
23342
+ }
23343
+ } else {
23344
+ // If the fragment was invalid within that context then we need
23345
+ // to parse and process the parent it's inserted into
23346
+
23347
+ // Insert bookmark node and get the parent
23348
+ selection.setContent(bookmarkHtml);
23349
+ parentNode = selection.getNode();
23350
+ rootNode = editor.getBody();
23351
+
23352
+ // Opera will return the document node when selection is in root
23353
+ if (parentNode.nodeType == 9) {
23354
+ parentNode = node = rootNode;
23355
+ } else {
23356
+ node = parentNode;
23357
+ }
23358
+
23359
+ // Find the ancestor just before the root element
23360
+ while (node !== rootNode) {
23361
+ parentNode = node;
23362
+ node = node.parentNode;
23363
+ }
23364
+
23365
+ // Get the outer/inner HTML depending on if we are in the root and parser and serialize that
23366
+ value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
23367
+ value = serializer.serialize(
23368
+ parser.parse(
23369
+ // Need to replace by using a function since $ in the contents would otherwise be a problem
23370
+ value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
23371
+ return serializer.serialize(fragment);
23372
+ })
23373
+ )
23374
+ );
23375
+
23376
+ // Set the inner/outer HTML depending on if we are in the root or not
23377
+ if (parentNode == rootNode) {
23378
+ dom.setHTML(rootNode, value);
23379
+ } else {
23380
+ dom.setOuterHTML(parentNode, value);
23381
+ }
23382
+ }
23383
+
23384
+ reduceInlineTextElements();
23385
+ moveSelectionToMarker(dom.get('mce_marker'));
23386
+ umarkFragmentElements(editor.getBody());
23387
+ editor.fire('SetContent', args);
23388
+ editor.addVisual();
23389
+ };
23390
+
23391
+ return {
23392
+ insertAtCaret: insertAtCaret
23393
+ };
23394
+ });
23395
+
22829
23396
  // Included from: js/tinymce/classes/EditorCommands.js
22830
23397
 
22831
23398
  /**
@@ -22845,21 +23412,17 @@ define("tinymce/caret/CaretWalker", [
22845
23412
  * @class tinymce.EditorCommands
22846
23413
  */
22847
23414
  define("tinymce/EditorCommands", [
22848
- "tinymce/html/Serializer",
22849
23415
  "tinymce/Env",
22850
23416
  "tinymce/util/Tools",
22851
- "tinymce/dom/ElementUtils",
22852
23417
  "tinymce/dom/RangeUtils",
22853
23418
  "tinymce/dom/TreeWalker",
22854
- "tinymce/caret/CaretWalker",
22855
- "tinymce/caret/CaretPosition",
22856
- "tinymce/dom/NodeType"
22857
- ], function(Serializer, Env, Tools, ElementUtils, RangeUtils, TreeWalker, CaretWalker, CaretPosition, NodeType) {
23419
+ "tinymce/InsertContent"
23420
+ ], function(Env, Tools, RangeUtils, TreeWalker, InsertContent) {
22858
23421
  // Added for compression purposes
22859
23422
  var each = Tools.each, extend = Tools.extend;
22860
23423
  var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
22861
- var isIE = Env.ie, isOldIE = Env.ie && Env.ie < 11;
22862
- var TRUE = true, FALSE = false, isTableCell = NodeType.matchNodeNames('td th');
23424
+ var isOldIE = Env.ie && Env.ie < 11;
23425
+ var TRUE = true, FALSE = false;
22863
23426
 
22864
23427
  return function(editor) {
22865
23428
  var dom, selection, formatter,
@@ -23278,332 +23841,7 @@ define("tinymce/EditorCommands", [
23278
23841
  },
23279
23842
 
23280
23843
  mceInsertContent: function(command, ui, value) {
23281
- var parser, serializer, parentNode, rootNode, fragment, args;
23282
- var marker, rng, node, node2, bookmarkHtml, merge, data;
23283
- var textInlineElements = editor.schema.getTextInlineElements();
23284
-
23285
- function trimOrPaddLeftRight(html) {
23286
- var rng, container, offset;
23287
-
23288
- rng = selection.getRng(true);
23289
- container = rng.startContainer;
23290
- offset = rng.startOffset;
23291
-
23292
- function hasSiblingText(siblingName) {
23293
- return container[siblingName] && container[siblingName].nodeType == 3;
23294
- }
23295
-
23296
- if (container.nodeType == 3) {
23297
- if (offset > 0) {
23298
- html = html.replace(/^&nbsp;/, ' ');
23299
- } else if (!hasSiblingText('previousSibling')) {
23300
- html = html.replace(/^ /, '&nbsp;');
23301
- }
23302
-
23303
- if (offset < container.length) {
23304
- html = html.replace(/&nbsp;(<br>|)$/, ' ');
23305
- } else if (!hasSiblingText('nextSibling')) {
23306
- html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
23307
- }
23308
- }
23309
-
23310
- return html;
23311
- }
23312
-
23313
- // Removes &nbsp; from a [b] c -> a &nbsp;c -> a c
23314
- function trimNbspAfterDeleteAndPaddValue() {
23315
- var rng, container, offset;
23316
-
23317
- rng = selection.getRng(true);
23318
- container = rng.startContainer;
23319
- offset = rng.startOffset;
23320
-
23321
- if (container.nodeType == 3 && rng.collapsed) {
23322
- if (container.data[offset] === '\u00a0') {
23323
- container.deleteData(offset, 1);
23324
-
23325
- if (!/[\u00a0| ]$/.test(value)) {
23326
- value += ' ';
23327
- }
23328
- } else if (container.data[offset - 1] === '\u00a0') {
23329
- container.deleteData(offset - 1, 1);
23330
-
23331
- if (!/[\u00a0| ]$/.test(value)) {
23332
- value = ' ' + value;
23333
- }
23334
- }
23335
- }
23336
- }
23337
-
23338
- function markInlineFormatElements(fragment) {
23339
- if (merge) {
23340
- for (node = fragment.firstChild; node; node = node.walk(true)) {
23341
- if (textInlineElements[node.name]) {
23342
- node.attr('data-mce-new', "true");
23343
- }
23344
- }
23345
- }
23346
- }
23347
-
23348
- function reduceInlineTextElements() {
23349
- if (merge) {
23350
- var root = editor.getBody(), elementUtils = new ElementUtils(dom);
23351
-
23352
- each(dom.select('*[data-mce-new]'), function(node) {
23353
- node.removeAttribute('data-mce-new');
23354
-
23355
- for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
23356
- if (elementUtils.compare(testNode, node)) {
23357
- dom.remove(node, true);
23358
- }
23359
- }
23360
- });
23361
- }
23362
- }
23363
-
23364
- function markFragmentElements(fragment) {
23365
- var node = fragment;
23366
-
23367
- while ((node = node.walk())) {
23368
- if (node.type === 1) {
23369
- node.attr('data-mce-fragment', '1');
23370
- }
23371
- }
23372
- }
23373
-
23374
- function umarkFragmentElements(elm) {
23375
- Tools.each(elm.getElementsByTagName('*'), function(elm) {
23376
- elm.removeAttribute('data-mce-fragment');
23377
- });
23378
- }
23379
-
23380
- function isPartOfFragment(node) {
23381
- return !!node.getAttribute('data-mce-fragment');
23382
- }
23383
-
23384
- function canHaveChildren(node) {
23385
- return node && !editor.schema.getShortEndedElements()[node.nodeName];
23386
- }
23387
-
23388
- function moveSelectionToMarker(marker) {
23389
- var parentEditableFalseElm, parentBlock, nextRng;
23390
-
23391
- function getContentEditableFalseParent(node) {
23392
- var root = editor.getBody();
23393
-
23394
- for (; node && node !== root; node = node.parentNode) {
23395
- if (editor.dom.getContentEditable(node) === 'false') {
23396
- return node;
23397
- }
23398
- }
23399
-
23400
- return null;
23401
- }
23402
-
23403
- if (!marker) {
23404
- return;
23405
- }
23406
-
23407
- selection.scrollIntoView(marker);
23408
-
23409
- // If marker is in cE=false then move selection to that element instead
23410
- parentEditableFalseElm = getContentEditableFalseParent(marker);
23411
- if (parentEditableFalseElm) {
23412
- dom.remove(marker);
23413
- selection.select(parentEditableFalseElm);
23414
- return;
23415
- }
23416
-
23417
- // Move selection before marker and remove it
23418
- rng = dom.createRng();
23419
-
23420
- // If previous sibling is a text node set the selection to the end of that node
23421
- node = marker.previousSibling;
23422
- if (node && node.nodeType == 3) {
23423
- rng.setStart(node, node.nodeValue.length);
23424
-
23425
- // TODO: Why can't we normalize on IE
23426
- if (!isIE) {
23427
- node2 = marker.nextSibling;
23428
- if (node2 && node2.nodeType == 3) {
23429
- node.appendData(node2.data);
23430
- node2.parentNode.removeChild(node2);
23431
- }
23432
- }
23433
- } else {
23434
- // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
23435
- rng.setStartBefore(marker);
23436
- rng.setEndBefore(marker);
23437
- }
23438
-
23439
- function findNextCaretRng(rng) {
23440
- var caretPos = CaretPosition.fromRangeStart(rng);
23441
- var caretWalker = new CaretWalker(editor.getBody());
23442
-
23443
- caretPos = caretWalker.next(caretPos);
23444
- if (caretPos) {
23445
- return caretPos.toRange();
23446
- }
23447
- }
23448
-
23449
- // Remove the marker node and set the new range
23450
- parentBlock = dom.getParent(marker, dom.isBlock);
23451
- dom.remove(marker);
23452
-
23453
- if (parentBlock && dom.isEmpty(parentBlock)) {
23454
- editor.$(parentBlock).empty();
23455
-
23456
- rng.setStart(parentBlock, 0);
23457
- rng.setEnd(parentBlock, 0);
23458
-
23459
- if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) {
23460
- rng = nextRng;
23461
- dom.remove(parentBlock);
23462
- } else {
23463
- dom.add(parentBlock, dom.create('br', {'data-mce-bogus': '1'}));
23464
- }
23465
- }
23466
-
23467
- selection.setRng(rng);
23468
- }
23469
-
23470
- if (typeof value != 'string') {
23471
- merge = value.merge;
23472
- data = value.data;
23473
- value = value.content;
23474
- }
23475
-
23476
- // Check for whitespace before/after value
23477
- if (/^ | $/.test(value)) {
23478
- value = trimOrPaddLeftRight(value);
23479
- }
23480
-
23481
- // Setup parser and serializer
23482
- parser = editor.parser;
23483
- serializer = new Serializer({
23484
- validate: settings.validate
23485
- }, editor.schema);
23486
- bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
23487
-
23488
- // Run beforeSetContent handlers on the HTML to be inserted
23489
- args = {content: value, format: 'html', selection: true};
23490
- editor.fire('BeforeSetContent', args);
23491
- value = args.content;
23492
-
23493
- // Add caret at end of contents if it's missing
23494
- if (value.indexOf('{$caret}') == -1) {
23495
- value += '{$caret}';
23496
- }
23497
-
23498
- // Replace the caret marker with a span bookmark element
23499
- value = value.replace(/\{\$caret\}/, bookmarkHtml);
23500
-
23501
- // If selection is at <body>|<p></p> then move it into <body><p>|</p>
23502
- rng = selection.getRng();
23503
- var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
23504
- var body = editor.getBody();
23505
- if (caretElement === body && selection.isCollapsed()) {
23506
- if (dom.isBlock(body.firstChild) && canHaveChildren(body.firstChild) && dom.isEmpty(body.firstChild)) {
23507
- rng = dom.createRng();
23508
- rng.setStart(body.firstChild, 0);
23509
- rng.setEnd(body.firstChild, 0);
23510
- selection.setRng(rng);
23511
- }
23512
- }
23513
-
23514
- // Insert node maker where we will insert the new HTML and get it's parent
23515
- if (!selection.isCollapsed()) {
23516
- // Fix for #2595 seems that delete removes one extra character on
23517
- // WebKit for some odd reason if you double click select a word
23518
- editor.selection.setRng(editor.selection.getRng());
23519
- editor.getDoc().execCommand('Delete', false, null);
23520
- trimNbspAfterDeleteAndPaddValue();
23521
- }
23522
-
23523
- parentNode = selection.getNode();
23524
-
23525
- // Parse the fragment within the context of the parent node
23526
- var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: data};
23527
- fragment = parser.parse(value, parserArgs);
23528
- markFragmentElements(fragment);
23529
-
23530
- markInlineFormatElements(fragment);
23531
-
23532
- // Move the caret to a more suitable location
23533
- node = fragment.lastChild;
23534
- if (node.attr('id') == 'mce_marker') {
23535
- marker = node;
23536
-
23537
- for (node = node.prev; node; node = node.walk(true)) {
23538
- if (node.type == 3 || !dom.isBlock(node.name)) {
23539
- if (editor.schema.isValidChild(node.parent.name, 'span')) {
23540
- node.parent.insert(marker, node, node.name === 'br');
23541
- }
23542
- break;
23543
- }
23544
- }
23545
- }
23546
-
23547
- editor._selectionOverrides.showBlockCaretContainer(parentNode);
23548
-
23549
- // If parser says valid we can insert the contents into that parent
23550
- if (!parserArgs.invalid) {
23551
- value = serializer.serialize(fragment);
23552
-
23553
- // Check if parent is empty or only has one BR element then set the innerHTML of that parent
23554
- node = parentNode.firstChild;
23555
- node2 = parentNode.lastChild;
23556
- if (!node || (node === node2 && node.nodeName === 'BR')) {
23557
- dom.setHTML(parentNode, value);
23558
- } else {
23559
- selection.setContent(value);
23560
- }
23561
- } else {
23562
- // If the fragment was invalid within that context then we need
23563
- // to parse and process the parent it's inserted into
23564
-
23565
- // Insert bookmark node and get the parent
23566
- selection.setContent(bookmarkHtml);
23567
- parentNode = selection.getNode();
23568
- rootNode = editor.getBody();
23569
-
23570
- // Opera will return the document node when selection is in root
23571
- if (parentNode.nodeType == 9) {
23572
- parentNode = node = rootNode;
23573
- } else {
23574
- node = parentNode;
23575
- }
23576
-
23577
- // Find the ancestor just before the root element
23578
- while (node !== rootNode) {
23579
- parentNode = node;
23580
- node = node.parentNode;
23581
- }
23582
-
23583
- // Get the outer/inner HTML depending on if we are in the root and parser and serialize that
23584
- value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
23585
- value = serializer.serialize(
23586
- parser.parse(
23587
- // Need to replace by using a function since $ in the contents would otherwise be a problem
23588
- value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
23589
- return serializer.serialize(fragment);
23590
- })
23591
- )
23592
- );
23593
-
23594
- // Set the inner/outer HTML depending on if we are in the root or not
23595
- if (parentNode == rootNode) {
23596
- dom.setHTML(rootNode, value);
23597
- } else {
23598
- dom.setOuterHTML(parentNode, value);
23599
- }
23600
- }
23601
-
23602
- reduceInlineTextElements();
23603
- moveSelectionToMarker(dom.get('mce_marker'));
23604
- umarkFragmentElements(editor.getBody());
23605
- editor.fire('SetContent', args);
23606
- editor.addVisual();
23844
+ InsertContent.insertAtCaret(editor, value);
23607
23845
  },
23608
23846
 
23609
23847
  mceInsertRawHTML: function(command, ui, value) {
@@ -33825,8 +34063,8 @@ define("tinymce/file/Uploader", [
33825
34063
  "tinymce/util/Tools",
33826
34064
  "tinymce/util/Fun"
33827
34065
  ], function(Promise, Tools, Fun) {
33828
- return function(settings) {
33829
- var cachedPromises = {};
34066
+ return function(uploadStatus, settings) {
34067
+ var pendingPromises = {};
33830
34068
 
33831
34069
  function fileName(blobInfo) {
33832
34070
  var ext, extensions;
@@ -33860,30 +34098,24 @@ define("tinymce/file/Uploader", [
33860
34098
  };
33861
34099
  }
33862
34100
 
33863
- function defaultHandler(blobInfo, success, failure, openNotification) {
33864
- var xhr, formData, notification;
34101
+ function defaultHandler(blobInfo, success, failure, progress) {
34102
+ var xhr, formData;
33865
34103
 
33866
34104
  xhr = new XMLHttpRequest();
33867
34105
  xhr.open('POST', settings.url);
33868
34106
  xhr.withCredentials = settings.credentials;
33869
34107
 
33870
- notification = openNotification();
33871
-
33872
34108
  xhr.upload.onprogress = function(e) {
33873
- var percentLoaded = Math.round(e.loaded / e.total * 100);
33874
- notification.progressBar.value(percentLoaded);
34109
+ progress(e.loaded / e.total * 100);
33875
34110
  };
33876
34111
 
33877
34112
  xhr.onerror = function() {
33878
- notification.close();
33879
34113
  failure("Image upload failed due to a XHR Transport error. Code: " + xhr.status);
33880
34114
  };
33881
34115
 
33882
34116
  xhr.onload = function() {
33883
34117
  var json;
33884
34118
 
33885
- notification.close();
33886
-
33887
34119
  if (xhr.status != 200) {
33888
34120
  failure("HTTP Error: " + xhr.status);
33889
34121
  return;
@@ -33911,66 +34143,107 @@ define("tinymce/file/Uploader", [
33911
34143
  });
33912
34144
  }
33913
34145
 
33914
- function interpretResult(promise) {
33915
- return promise.then(function(result) {
33916
- return result;
33917
- })['catch'](function(error) {
33918
- return error;
34146
+ function handlerSuccess(blobInfo, url) {
34147
+ return {
34148
+ url: url,
34149
+ blobInfo: blobInfo,
34150
+ status: true
34151
+ };
34152
+ }
34153
+
34154
+ function handlerFailure(blobInfo, error) {
34155
+ return {
34156
+ url: '',
34157
+ blobInfo: blobInfo,
34158
+ status: false,
34159
+ error: error
34160
+ };
34161
+ }
34162
+
34163
+ function resolvePending(blobUri, result) {
34164
+ Tools.each(pendingPromises[blobUri], function(resolve) {
34165
+ resolve(result);
33919
34166
  });
34167
+
34168
+ delete pendingPromises[blobUri];
33920
34169
  }
33921
34170
 
33922
- function registerPromise(handler, id, blobInfo) {
33923
- var response = handler(blobInfo);
33924
- var promise = interpretResult(response);
33925
- delete cachedPromises[id];
33926
- cachedPromises[id] = promise;
33927
- return promise;
34171
+ function uploadBlobInfo(blobInfo, handler, openNotification) {
34172
+ uploadStatus.markPending(blobInfo.blobUri());
34173
+
34174
+ return new Promise(function(resolve) {
34175
+ var notification, progress;
34176
+
34177
+ var noop = function() {
34178
+ };
34179
+
34180
+ try {
34181
+ var closeNotification = function() {
34182
+ if (notification) {
34183
+ notification.close();
34184
+ progress = noop; // Once it's closed it's closed
34185
+ }
34186
+ };
34187
+
34188
+ var success = function(url) {
34189
+ closeNotification();
34190
+ uploadStatus.markUploaded(blobInfo.blobUri(), url);
34191
+ resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url));
34192
+ resolve(handlerSuccess(blobInfo, url));
34193
+ };
34194
+
34195
+ var failure = function() {
34196
+ closeNotification();
34197
+ uploadStatus.removeFailed(blobInfo.blobUri());
34198
+ resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, failure));
34199
+ resolve(handlerFailure(blobInfo, failure));
34200
+ };
34201
+
34202
+ progress = function(percent) {
34203
+ if (percent < 0 || percent > 100) {
34204
+ return;
34205
+ }
34206
+
34207
+ if (!notification) {
34208
+ notification = openNotification();
34209
+ }
34210
+
34211
+ notification.progressBar.value(percent);
34212
+ };
34213
+
34214
+ handler(blobInfoToData(blobInfo), success, failure, progress);
34215
+ } catch (ex) {
34216
+ resolve(handlerFailure(blobInfo, ex.message));
34217
+ }
34218
+ });
34219
+ }
34220
+
34221
+ function isDefaultHandler(handler) {
34222
+ return handler === defaultHandler;
33928
34223
  }
33929
34224
 
33930
- function collectUploads(blobInfos, uploadBlobInfo) {
33931
- return Tools.map(blobInfos, function(blobInfo) {
33932
- var id = blobInfo.id();
33933
- return cachedPromises[id] ? cachedPromises[id] : registerPromise(uploadBlobInfo, id, blobInfo);
34225
+ function pendingUploadBlobInfo(blobInfo) {
34226
+ var blobUri = blobInfo.blobUri();
34227
+
34228
+ return new Promise(function(resolve) {
34229
+ pendingPromises[blobUri] = pendingPromises[blobUri] || [];
34230
+ pendingPromises[blobUri].push(resolve);
33934
34231
  });
33935
34232
  }
33936
34233
 
33937
34234
  function uploadBlobs(blobInfos, openNotification) {
33938
- function uploadBlobInfo(blobInfo) {
33939
- return new Promise(function(resolve) {
33940
- var handler = settings.handler;
33941
-
33942
- try {
33943
- handler(blobInfoToData(blobInfo), function(url) {
33944
- resolve({
33945
- url: url,
33946
- blobInfo: blobInfo,
33947
- status: true
33948
- });
33949
- }, function(failure) {
33950
- resolve({
33951
- url: '',
33952
- blobInfo: blobInfo,
33953
- status: false,
33954
- error: failure
33955
- });
33956
- }, openNotification);
33957
- } catch (ex) {
33958
- resolve({
33959
- url: '',
33960
- blobInfo: blobInfo,
33961
- status: false,
33962
- error: ex.message
33963
- });
33964
- }
33965
- });
33966
- }
34235
+ blobInfos = Tools.grep(blobInfos, function(blobInfo) {
34236
+ return !uploadStatus.isUploaded(blobInfo.blobUri());
34237
+ });
33967
34238
 
33968
- var promises = collectUploads(blobInfos, uploadBlobInfo);
33969
- return Promise.all(promises);
34239
+ return Promise.all(Tools.map(blobInfos, function(blobInfo) {
34240
+ return uploadStatus.isPending(blobInfo.blobUri()) ?
34241
+ pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, settings.handler, openNotification);
34242
+ }));
33970
34243
  }
33971
34244
 
33972
34245
  function upload(blobInfos, openNotification) {
33973
- return (!settings.url && settings.handler === defaultHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification);
34246
+ return (!settings.url && isDefaultHandler(settings.handler)) ? noUpload() : uploadBlobs(blobInfos, openNotification);
33974
34247
  }
33975
34248
 
33976
34249
  settings = Tools.extend({
@@ -34121,7 +34394,7 @@ define("tinymce/file/ImageScanner", [
34121
34394
  ], function(Promise, Arr, Fun, Conversions, Env) {
34122
34395
  var count = 0;
34123
34396
 
34124
- return function(blobCache) {
34397
+ return function(uploadStatus, blobCache) {
34125
34398
  var cachedPromises = {};
34126
34399
 
34127
34400
  function findAll(elm, predicate) {
@@ -34192,7 +34465,7 @@ define("tinymce/file/ImageScanner", [
34192
34465
  }
34193
34466
 
34194
34467
  if (src.indexOf('blob:') === 0) {
34195
- return true;
34468
+ return !uploadStatus.isUploaded(src);
34196
34469
  }
34197
34470
 
34198
34471
  if (src.indexOf('data:') === 0) {
@@ -34298,6 +34571,17 @@ define("tinymce/file/BlobCache", [
34298
34571
  });
34299
34572
  }
34300
34573
 
34574
+ function removeByUri(blobUri) {
34575
+ cache = Arr.filter(cache, function(blobInfo) {
34576
+ if (blobInfo.blobUri() === blobUri) {
34577
+ URL.revokeObjectURL(blobInfo.blobUri());
34578
+ return false;
34579
+ }
34580
+
34581
+ return true;
34582
+ });
34583
+ }
34584
+
34301
34585
  function destroy() {
34302
34586
  Arr.each(cache, function(cachedBlobInfo) {
34303
34587
  URL.revokeObjectURL(cachedBlobInfo.blobUri());
@@ -34312,6 +34596,85 @@ define("tinymce/file/BlobCache", [
34312
34596
  get: get,
34313
34597
  getByUri: getByUri,
34314
34598
  findFirst: findFirst,
34599
+ removeByUri: removeByUri,
34600
+ destroy: destroy
34601
+ };
34602
+ };
34603
+ });
34604
+
34605
+ // Included from: js/tinymce/classes/file/UploadStatus.js
34606
+
34607
+ /**
34608
+ * UploadStatus.js
34609
+ *
34610
+ * Released under LGPL License.
34611
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
34612
+ *
34613
+ * License: http://www.tinymce.com/license
34614
+ * Contributing: http://www.tinymce.com/contributing
34615
+ */
34616
+
34617
+ /**
34618
+ * Holds the current status of a blob uri, if it's pending or uploaded and what the result urls was.
34619
+ *
34620
+ * @private
34621
+ * @class tinymce.file.UploadStatus
34622
+ */
34623
+ define("tinymce/file/UploadStatus", [
34624
+ ], function() {
34625
+ return function() {
34626
+ var PENDING = 1, UPLOADED = 2;
34627
+ var blobUriStatuses = {};
34628
+
34629
+ function createStatus(status, resultUri) {
34630
+ return {
34631
+ status: status,
34632
+ resultUri: resultUri
34633
+ };
34634
+ }
34635
+
34636
+ function hasBlobUri(blobUri) {
34637
+ return blobUri in blobUriStatuses;
34638
+ }
34639
+
34640
+ function getResultUri(blobUri) {
34641
+ var result = blobUriStatuses[blobUri];
34642
+
34643
+ return result ? result.resultUri : null;
34644
+ }
34645
+
34646
+ function isPending(blobUri) {
34647
+ return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false;
34648
+ }
34649
+
34650
+ function isUploaded(blobUri) {
34651
+ return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false;
34652
+ }
34653
+
34654
+ function markPending(blobUri) {
34655
+ blobUriStatuses[blobUri] = createStatus(PENDING, null);
34656
+ }
34657
+
34658
+ function markUploaded(blobUri, resultUri) {
34659
+ blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri);
34660
+ }
34661
+
34662
+ function removeFailed(blobUri) {
34663
+ delete blobUriStatuses[blobUri];
34664
+ }
34665
+
34666
+ function destroy() {
34667
+ blobUriStatuses = {};
34668
+ }
34669
+
34670
+ return {
34671
+ hasBlobUri: hasBlobUri,
34672
+ getResultUri: getResultUri,
34673
+ isPending: isPending,
34674
+ isUploaded: isUploaded,
34675
+ markPending: markPending,
34676
+ markUploaded: markUploaded,
34677
+ removeFailed: removeFailed,
34315
34678
  destroy: destroy
34316
34679
  };
34317
34680
  };
@@ -34339,10 +34702,12 @@ define("tinymce/EditorUpload", [
34339
34702
  "tinymce/util/Arr",
34340
34703
  "tinymce/file/Uploader",
34341
34704
  "tinymce/file/ImageScanner",
34342
- "tinymce/file/BlobCache"
34343
- ], function(Arr, Uploader, ImageScanner, BlobCache) {
34705
+ "tinymce/file/BlobCache",
34706
+ "tinymce/file/UploadStatus"
34707
+ ], function(Arr, Uploader, ImageScanner, BlobCache, UploadStatus) {
34344
34708
  return function(editor) {
34345
34709
  var blobCache = new BlobCache(), uploader, imageScanner, settings = editor.settings;
34710
+ var uploadStatus = new UploadStatus();
34346
34711
 
34347
34712
  function aliveGuard(callback) {
34348
34713
  return function(result) {
@@ -34392,9 +34757,19 @@ define("tinymce/EditorUpload", [
34392
34757
  });
34393
34758
  }
34394
34759
 
34760
+ function replaceImageUri(image, resultUri) {
34761
+ blobCache.removeByUri(image.src);
34762
+ replaceUrlInUndoStack(image.src, resultUri);
34763
+
34764
+ editor.$(image).attr({
34765
+ src: resultUri,
34766
+ 'data-mce-src': editor.convertURL(resultUri, 'src')
34767
+ });
34768
+ }
34769
+
34395
34770
  function uploadImages(callback) {
34396
34771
  if (!uploader) {
34397
- uploader = new Uploader({
34772
+ uploader = new Uploader(uploadStatus, {
34398
34773
  url: settings.images_upload_url,
34399
34774
  basePath: settings.images_upload_base_path,
34400
34775
  credentials: settings.images_upload_credentials,
@@ -34413,13 +34788,8 @@ define("tinymce/EditorUpload", [
34413
34788
  result = Arr.map(result, function(uploadInfo, index) {
34414
34789
  var image = imageInfos[index].image;
34415
34790
 
34416
- if (uploadInfo.status) {
34417
- replaceUrlInUndoStack(image.src, uploadInfo.url);
34418
-
34419
- editor.$(image).attr({
34420
- src: uploadInfo.url,
34421
- 'data-mce-src': editor.convertURL(uploadInfo.url, 'src')
34422
- });
34791
+ if (uploadInfo.status && editor.settings.images_replace_blob_uris !== false) {
34792
+ replaceImageUri(image, uploadInfo.url);
34423
34793
  }
34424
34794
 
34425
34795
  return {
@@ -34443,15 +34813,20 @@ define("tinymce/EditorUpload", [
34443
34813
  }
34444
34814
  }
34445
34815
 
34816
+ function isValidDataUriImage(imgElm) {
34817
+ return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true;
34818
+ }
34819
+
34446
34820
  function scanForImages() {
34447
34821
  if (!imageScanner) {
34448
- imageScanner = new ImageScanner(blobCache);
34822
+ imageScanner = new ImageScanner(uploadStatus, blobCache);
34449
34823
  }
34450
34824
 
34451
- return imageScanner.findAll(editor.getBody(), settings.images_dataimg_filter).then(aliveGuard(function(result) {
34825
+ return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(function(result) {
34452
34826
  Arr.each(result, function(resultItem) {
34453
34827
  replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());
34454
34828
  resultItem.image.src = resultItem.blobInfo.blobUri();
34829
+ resultItem.image.removeAttribute('data-mce-src');
34455
34830
  });
34456
34831
 
34457
34832
  return result;
@@ -34460,11 +34835,18 @@ define("tinymce/EditorUpload", [
34460
34835
 
34461
34836
  function destroy() {
34462
34837
  blobCache.destroy();
34838
+ uploadStatus.destroy();
34463
34839
  imageScanner = uploader = null;
34464
34840
  }
34465
34841
 
34466
- function replaceBlobWithBase64(content) {
34842
+ function replaceBlobUris(content) {
34467
34843
  return content.replace(/src="(blob:[^"]+)"/g, function(match, blobUri) {
34844
+ var resultUri = uploadStatus.getResultUri(blobUri);
34845
+
34846
+ if (resultUri) {
34847
+ return 'src="' + resultUri + '"';
34848
+ }
34849
+
34468
34850
  var blobInfo = blobCache.getByUri(blobUri);
34469
34851
 
34470
34852
  if (!blobInfo) {
@@ -34490,7 +34872,7 @@ define("tinymce/EditorUpload", [
34490
34872
  });
34491
34873
 
34492
34874
  editor.on('RawSaveContent', function(e) {
34493
- e.content = replaceBlobWithBase64(e.content);
34875
+ e.content = replaceBlobUris(e.content);
34494
34876
  });
34495
34877
 
34496
34878
  editor.on('getContent', function(e) {
@@ -34498,7 +34880,24 @@ define("tinymce/EditorUpload", [
34498
34880
  return;
34499
34881
  }
34500
34882
 
34501
- e.content = replaceBlobWithBase64(e.content);
34883
+ e.content = replaceBlobUris(e.content);
34884
+ });
34885
+
34886
+ editor.on('PostRender', function() {
34887
+ editor.parser.addNodeFilter('img', function(images) {
34888
+ Arr.each(images, function(img) {
34889
+ var src = img.attr('src');
34890
+
34891
+ if (blobCache.getByUri(src)) {
34892
+ return;
34893
+ }
34894
+
34895
+ var resultUri = uploadStatus.getResultUri(src);
34896
+ if (resultUri) {
34897
+ img.attr('src', resultUri);
34898
+ }
34899
+ });
34900
+ });
34502
34901
  });
34503
34902
 
34504
34903
  return {
@@ -35859,6 +36258,35 @@ define("tinymce/SelectionOverrides", [
35859
36258
  }
35860
36259
  });
35861
36260
 
36261
+ var hasNormalCaretPosition = function (elm) {
36262
+ var caretWalker = new CaretWalker(elm);
36263
+
36264
+ if (!elm.firstChild) {
36265
+ return false;
36266
+ }
36267
+
36268
+ var startPos = CaretPosition.before(elm.firstChild);
36269
+ var newPos = caretWalker.next(startPos);
36270
+
36271
+ return newPos && !isBeforeContentEditableFalse(newPos) && !isAfterContentEditableFalse(newPos);
36272
+ };
36273
+
36274
+ var isInSameBlock = function (node1, node2) {
36275
+ var block1 = editor.dom.getParent(node1, editor.dom.isBlock);
36276
+ var block2 = editor.dom.getParent(node2, editor.dom.isBlock);
36277
+ return block1 === block2;
36278
+ };
36279
+
36280
+ // Checks if the target node is in a block and if that block has a caret position better than the
36281
+ // suggested caretNode this is to prevent the caret from being sucked in towards a cE=false block if
36282
+ // they are adjacent on the vertical axis
36283
+ var hasBetterMouseTarget = function (targetNode, caretNode) {
36284
+ var targetBlock = editor.dom.getParent(targetNode, editor.dom.isBlock);
36285
+ var caretBlock = editor.dom.getParent(caretNode, editor.dom.isBlock);
36286
+
36287
+ return targetBlock && !isInSameBlock(targetBlock, caretBlock) && hasNormalCaretPosition(targetBlock);
36288
+ };
36289
+
35862
36290
  editor.on('mousedown', function(e) {
35863
36291
  var contentEditableRoot;
35864
36292
 
@@ -35880,9 +36308,11 @@ define("tinymce/SelectionOverrides", [
35880
36308
 
35881
36309
  var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY);
35882
36310
  if (caretInfo) {
35883
- e.preventDefault();
35884
- editor.getBody().focus();
35885
- setRange(showCaret(1, caretInfo.node, caretInfo.before));
36311
+ if (!hasBetterMouseTarget(e.target, caretInfo.node)) {
36312
+ e.preventDefault();
36313
+ editor.getBody().focus();
36314
+ setRange(showCaret(1, caretInfo.node, caretInfo.before));
36315
+ }
35886
36316
  }
35887
36317
  }
35888
36318
  });
@@ -36657,6 +37087,7 @@ define("tinymce/Editor", [
36657
37087
  });
36658
37088
  }
36659
37089
 
37090
+ self.editorManager.add(self);
36660
37091
  loadScripts();
36661
37092
  },
36662
37093
 
@@ -36673,7 +37104,6 @@ define("tinymce/Editor", [
36673
37104
 
36674
37105
  this.editorManager.i18n.setCode(settings.language);
36675
37106
  self.rtl = settings.rtl_ui || this.editorManager.i18n.rtl;
36676
- self.editorManager.add(self);
36677
37107
 
36678
37108
  settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
36679
37109
 
@@ -38854,7 +39284,7 @@ define("tinymce/EditorManager", [
38854
39284
 
38855
39285
  function purgeDestroyedEditor(editor) {
38856
39286
  // User has manually destroyed the editor lets clean up the mess
38857
- if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
39287
+ if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) {
38858
39288
  removeEditorFromList(editor);
38859
39289
  editor.unbindAllNativeEvents();
38860
39290
  editor.destroy(true);
@@ -38888,7 +39318,7 @@ define("tinymce/EditorManager", [
38888
39318
  * @property minorVersion
38889
39319
  * @type String
38890
39320
  */
38891
- minorVersion: '3.12',
39321
+ minorVersion: '3.13',
38892
39322
 
38893
39323
  /**
38894
39324
  * Release date of TinyMCE build.
@@ -38896,7 +39326,7 @@ define("tinymce/EditorManager", [
38896
39326
  * @property releaseDate
38897
39327
  * @type String
38898
39328
  */
38899
- releaseDate: '2016-05-10',
39329
+ releaseDate: '2016-06-08',
38900
39330
 
38901
39331
  /**
38902
39332
  * Collection of editor instances.
@@ -39163,20 +39593,18 @@ define("tinymce/EditorManager", [
39163
39593
  var initCount = 0, editors = [], targets;
39164
39594
 
39165
39595
  function createEditor(id, settings, targetElm) {
39166
- if (!purgeDestroyedEditor(self.get(id))) {
39167
- var editor = new Editor(id, settings, self);
39596
+ var editor = new Editor(id, settings, self);
39168
39597
 
39169
- editors.push(editor);
39598
+ editors.push(editor);
39170
39599
 
39171
- editor.on('init', function() {
39172
- if (++initCount === targets.length) {
39173
- provideResults(editors);
39174
- }
39175
- });
39600
+ editor.on('init', function() {
39601
+ if (++initCount === targets.length) {
39602
+ provideResults(editors);
39603
+ }
39604
+ });
39176
39605
 
39177
- editor.targetElm = editor.targetElm || targetElm;
39178
- editor.render();
39179
- }
39606
+ editor.targetElm = editor.targetElm || targetElm;
39607
+ editor.render();
39180
39608
  }
39181
39609
 
39182
39610
  DOM.unbind(window, 'ready', initEditors);
@@ -39200,6 +39628,14 @@ define("tinymce/EditorManager", [
39200
39628
  return;
39201
39629
  }
39202
39630
 
39631
+ Tools.each(targets, function(elm) {
39632
+ purgeDestroyedEditor(self.get(elm.id));
39633
+ });
39634
+
39635
+ targets = Tools.grep(targets, function(elm) {
39636
+ return !self.get(elm.id);
39637
+ });
39638
+
39203
39639
  each(targets, function(elm) {
39204
39640
  createEditor(createId(elm), settings, elm);
39205
39641
  });