tinymce-rails 4.3.12 → 4.3.13

Sign up to get free protection for your applications and to get access to all the features.
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
  });