TinyMCE: update to 4.4.0, changelog: https://github.com/tinymce/tinymce/blob/master/changelog.txt. Includes two bugfixes for #36434.

Fixes #37327.

git-svn-id: https://develop.svn.wordpress.org/trunk@38034 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2016-07-12 00:19:47 +00:00
parent 68cff6ac40
commit f11d5ef4d8
33 changed files with 3506 additions and 143 deletions

View File

@ -270,11 +270,17 @@ tinymce.PluginManager.add('lists', function(editor) {
}
}
var shouldMerge = function (listBlock, sibling) {
var targetStyle = editor.dom.getStyle(listBlock, 'list-style-type', true);
var style = editor.dom.getStyle(sibling, 'list-style-type', true);
return targetStyle === style;
};
function mergeWithAdjacentLists(listBlock) {
var sibling, node;
sibling = listBlock.nextSibling;
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName && shouldMerge(listBlock, sibling)) {
while ((node = sibling.firstChild)) {
listBlock.appendChild(node);
}
@ -283,7 +289,7 @@ tinymce.PluginManager.add('lists', function(editor) {
}
sibling = listBlock.previousSibling;
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName && shouldMerge(listBlock, sibling)) {
while ((node = sibling.firstChild)) {
listBlock.insertBefore(node, listBlock.firstChild);
}
@ -394,7 +400,7 @@ tinymce.PluginManager.add('lists', function(editor) {
}
function indent(li) {
var sibling, newList;
var sibling, newList, listStyle;
function mergeLists(from, to) {
var node;
@ -440,6 +446,10 @@ tinymce.PluginManager.add('lists', function(editor) {
sibling = li.previousSibling;
if (sibling && sibling.nodeName == 'LI') {
newList = dom.create(li.parentNode.nodeName);
listStyle = dom.getStyle(li.parentNode, 'listStyleType');
if (listStyle) {
dom.setStyle(newList, 'listStyleType', listStyle);
}
sibling.appendChild(newList);
newList.appendChild(li);
mergeLists(li.lastChild, newList);
@ -505,7 +515,7 @@ tinymce.PluginManager.add('lists', function(editor) {
}
}
function applyList(listName) {
function applyList(listName, detail) {
var rng = selection.getRng(true), bookmark, listItemName = 'LI';
if (dom.getContentEditable(selection.getNode()) === "false") {
@ -600,8 +610,14 @@ tinymce.PluginManager.add('lists', function(editor) {
tinymce.each(getSelectedTextBlocks(), function(block) {
var listBlock, sibling;
var hasCompatibleStyle = function (sib) {
var sibStyle = dom.getStyle(sib, 'list-style-type');
var detailStyle = detail ? detail['list-style-type'] : '';
return sibStyle === detailStyle;
};
sibling = block.previousSibling;
if (sibling && isListNode(sibling) && sibling.nodeName == listName) {
if (sibling && isListNode(sibling) && sibling.nodeName == listName && hasCompatibleStyle(sibling)) {
listBlock = sibling;
block = dom.rename(block, listItemName);
sibling.appendChild(block);
@ -612,12 +628,17 @@ tinymce.PluginManager.add('lists', function(editor) {
block = dom.rename(block, listItemName);
}
updateListStyle(listBlock, detail);
mergeWithAdjacentLists(listBlock);
});
moveToBookmark(bookmark);
}
var updateListStyle = function (el, detail) {
dom.setStyle(el, 'list-style-type', detail ? detail['list-style-type'] : null);
};
function removeList() {
var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody();
@ -645,7 +666,7 @@ tinymce.PluginManager.add('lists', function(editor) {
moveToBookmark(bookmark);
}
function toggleList(listName) {
function toggleList(listName, detail) {
var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
if (isEditorBody(parentList)) {
@ -657,11 +678,13 @@ tinymce.PluginManager.add('lists', function(editor) {
removeList(listName);
} else {
var bookmark = createBookmark(selection.getRng(true));
updateListStyle(parentList, detail);
mergeWithAdjacentLists(dom.rename(parentList, listName));
moveToBookmark(bookmark);
}
} else {
applyList(listName);
applyList(listName, detail);
}
}
@ -819,16 +842,16 @@ tinymce.PluginManager.add('lists', function(editor) {
}
});
editor.addCommand('InsertUnorderedList', function() {
toggleList('UL');
editor.addCommand('InsertUnorderedList', function(ui, detail) {
toggleList('UL', detail);
});
editor.addCommand('InsertOrderedList', function() {
toggleList('OL');
editor.addCommand('InsertOrderedList', function(ui, detail) {
toggleList('OL', detail);
});
editor.addCommand('InsertDefinitionList', function() {
toggleList('DL');
editor.addCommand('InsertDefinitionList', function(ui, detail) {
toggleList('DL', detail);
});
editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL'));

File diff suppressed because one or more lines are too long

View File

@ -240,6 +240,95 @@ define("tinymce/pasteplugin/Utils", [
};
});
// Included from: js/tinymce/plugins/paste/classes/SmartPaste.js
/**
* SmartPaste.js
*
* Released under LGPL License.
* Copyright (c) 1999-2016 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/**
* Tries to be smart depending on what the user pastes if it looks like an url
* it will make a link out of the current selection. If it's an image url that looks
* like an image it will check if it's an image and insert it as an image.
*
* @class tinymce.pasteplugin.SmartPaste
* @private
*/
define("tinymce/pasteplugin/SmartPaste", [
"tinymce/util/Tools"
], function (Tools) {
var isAbsoluteUrl = function (url) {
return /^https?:\/\/[\w\?\-\/+=.&%]+$/i.test(url);
};
var isImageUrl = function (url) {
return isAbsoluteUrl(url) && /.(gif|jpe?g|jpng)$/.test(url);
};
var createImage = function (editor, url, pasteHtml) {
editor.undoManager.extra(function () {
pasteHtml(url);
}, function () {
editor.insertContent('<img src="' + url + '">');
});
return true;
};
var createLink = function (editor, url, pasteHtml) {
editor.undoManager.extra(function () {
pasteHtml(url);
}, function () {
editor.execCommand('mceInsertLink', false, url);
});
return true;
};
var linkSelection = function (editor, html, pasteHtml) {
return editor.selection.isCollapsed() === false && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtml) : false;
};
var insertImage = function (editor, html, pasteHtml) {
return isImageUrl(html) ? createImage(editor, html, pasteHtml) : false;
};
var insertContent = function (editor, html) {
var pasteHtml = function (html) {
editor.insertContent(html, {
merge: editor.settings.paste_merge_formats !== false,
paste: true
});
return true;
};
var fallback = function (editor, html) {
pasteHtml(html);
};
Tools.each([
linkSelection,
insertImage,
fallback
], function (action) {
return action(editor, html, pasteHtml) !== true;
});
};
return {
isImageUrl: isImageUrl,
isAbsoluteUrl: isAbsoluteUrl,
insertContent: insertContent
};
});
// Included from: js/tinymce/plugins/paste/classes/Clipboard.js
/**
@ -276,8 +365,9 @@ define("tinymce/pasteplugin/Clipboard", [
"tinymce/dom/RangeUtils",
"tinymce/util/VK",
"tinymce/pasteplugin/Utils",
"tinymce/pasteplugin/SmartPaste",
"tinymce/util/Delay"
], function(Env, RangeUtils, VK, Utils, Delay) {
], function(Env, RangeUtils, VK, Utils, SmartPaste, Delay) {
return function(editor) {
var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0, draggingInternally = false;
var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState;
@ -311,7 +401,7 @@ define("tinymce/pasteplugin/Clipboard", [
}
if (!args.isDefaultPrevented()) {
editor.insertContent(html, {merge: editor.settings.paste_merge_formats !== false, data: {paste: true}});
SmartPaste.insertContent(editor, html);
}
}
}
@ -571,6 +661,55 @@ define("tinymce/pasteplugin/Clipboard", [
return hasContentType(content, 'text/html') || hasContentType(content, 'text/plain');
}
function getBase64FromUri(uri) {
var idx;
idx = uri.indexOf(',');
if (idx !== -1) {
return uri.substr(idx + 1);
}
return null;
}
function isValidDataUriImage(settings, imgElm) {
return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true;
}
function pasteImage(rng, reader, blob) {
if (rng) {
editor.selection.setRng(rng);
rng = null;
}
var dataUri = reader.result;
var base64 = getBase64FromUri(dataUri);
var img = new Image();
img.src = dataUri;
// TODO: Move the bulk of the cache logic to EditorUpload
if (isValidDataUriImage(editor.settings, img)) {
var blobCache = editor.editorUpload.blobCache;
var blobInfo, existingBlobInfo;
existingBlobInfo = blobCache.findFirst(function(cachedBlobInfo) {
return cachedBlobInfo.base64() === base64;
});
if (!existingBlobInfo) {
blobInfo = blobCache.create(uniqueId(), blob, base64);
blobCache.add(blobInfo);
} else {
blobInfo = existingBlobInfo;
}
pasteHtml('<img src="' + blobInfo.blobUri() + '">');
} else {
pasteHtml('<img src="' + dataUri + '">');
}
}
/**
* Checks if the clipboard contains image data if it does it will take that data
* and convert it into a data url image and paste that image at the caret location.
@ -582,33 +721,9 @@ define("tinymce/pasteplugin/Clipboard", [
function pasteImageData(e, rng) {
var dataTransfer = e.clipboardData || e.dataTransfer;
function getBase64FromUri(uri) {
var idx;
idx = uri.indexOf(',');
if (idx !== -1) {
return uri.substr(idx + 1);
}
return null;
}
function processItems(items) {
var i, item, reader, hadImage = false;
function pasteImage(reader, blob) {
if (rng) {
editor.selection.setRng(rng);
rng = null;
}
var blobCache = editor.editorUpload.blobCache;
var blobInfo = blobCache.create(uniqueId(), blob, getBase64FromUri(reader.result));
blobCache.add(blobInfo);
pasteHtml('<img src="' + blobInfo.blobUri() + '">');
}
if (items) {
for (i = 0; i < items.length; i++) {
item = items[i];
@ -617,7 +732,7 @@ define("tinymce/pasteplugin/Clipboard", [
var blob = item.getAsFile ? item.getAsFile() : item;
reader = new FileReader();
reader.onload = pasteImage.bind(null, reader, blob);
reader.onload = pasteImage.bind(null, rng, reader, blob);
reader.readAsDataURL(blob);
e.preventDefault();
@ -874,6 +989,7 @@ define("tinymce/pasteplugin/Clipboard", [
self.pasteHtml = pasteHtml;
self.pasteText = pasteText;
self.pasteImageData = pasteImageData;
editor.on('preInit', function() {
registerEventHandlers();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -439,7 +439,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
}
function reposition(match) {
var relPos, panelRect, elementRect, contentAreaRect, panel, relRect, testPositions;
var relPos, panelRect, elementRect, contentAreaRect, panel, relRect, testPositions, smallElementWidthThreshold;
if (editor.removed) {
return;
@ -462,6 +462,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
elementRect = getElementRect(match.element);
panelRect = tinymce.DOM.getRect(panel.getEl());
contentAreaRect = tinymce.DOM.getRect(editor.getContentAreaContainer() || editor.getBody());
smallElementWidthThreshold = 25;
// We need to use these instead of the rect values since the style
// size properites might not be the same as the real size for a table
@ -473,7 +474,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
}
// Inflate the elementRect so it doesn't get placed above resize handles
if (editor.selection.controlSelection.isResizable(match.element)) {
if (editor.selection.controlSelection.isResizable(match.element) && elementRect.w < smallElementWidthThreshold) {
elementRect = Rect.inflate(elementRect, 0, 8);
}
@ -485,7 +486,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
movePanelTo(panel, userConstrain(relRect.x, relRect.y, elementRect, contentAreaRect, panelRect));
} else {
// Allow overflow below the editor to avoid placing toolbars ontop of tables
contentAreaRect.h += 40;
contentAreaRect.h += panelRect.h;
elementRect = Rect.intersect(contentAreaRect, elementRect);
if (elementRect) {
@ -505,7 +506,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
}
togglePositionClass(panel, relPos, function(pos1, pos2) {
return (!elementRect || elementRect.w > 40) && pos1 === pos2;
return pos1 === pos2;
});
//drawRect(contentAreaRect, 'blue');

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// 4.3.13 (2016-06-08)
// 4.4.0 (2016-06-30)
/**
* Compiled inline version. (Library mode)
@ -10580,6 +10580,11 @@ define("tinymce/dom/RangeUtils", [
container = container.childNodes[offset];
offset = 0;
// Don't normalize non collapsed selections like <p>[a</p><table></table>]
if (!collapsed && container === body.lastChild && container.nodeName === 'TABLE') {
return;
}
if (hasContentEditableFalseParent(container) || isCaretContainer(container)) {
return;
}
@ -17356,8 +17361,9 @@ define("tinymce/dom/Selection", [
"tinymce/dom/BookmarkManager",
"tinymce/dom/NodeType",
"tinymce/Env",
"tinymce/util/Tools"
], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, NodeType, Env, Tools) {
"tinymce/util/Tools",
"tinymce/caret/CaretPosition"
], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, NodeType, Env, Tools, CaretPosition) {
var each = Tools.each, trim = Tools.trim;
var isIE = Env.ie;
@ -18333,6 +18339,11 @@ define("tinymce/dom/Selection", [
}
},
getBoundingClientRect: function() {
var rng = this.getRng();
return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect();
},
destroy: function() {
this.win = null;
this.controlSelection.destroy();
@ -21420,6 +21431,7 @@ define("tinymce/UndoManager", [
data = [];
index = 0;
self.typing = false;
self.data = data;
editor.fire('ClearUndos');
},
@ -21445,13 +21457,14 @@ define("tinymce/UndoManager", [
},
/**
* Executes the specified function in an undo translation. The selection
* Executes the specified mutator function as an undo transaction. The selection
* before the modification will be stored to the undo stack and if the DOM changes
* it will add a new undo level. Any methods within the translation that adds undo levels will
* be ignored. So a translation can include calls to execCommand or editor.insertContent.
*
* @method transact
* @param {function} callback Function to execute dom manipulation logic in.
* @param {function} callback Function that gets executed and has dom manipulation logic in it.
* @return {Object} Undo level that got added or null it a level wasn't needed.
*/
transact: function(callback) {
self.beforeChange();
@ -21463,7 +21476,31 @@ define("tinymce/UndoManager", [
locks--;
}
self.add();
return self.add();
},
/**
* Adds an extra "hidden" undo level by first applying the first mutation and store that to the undo stack
* then roll back that change and do the second mutation on top of the stack. This will produce an extra
* undo level that the user doesn't see until they undo.
*
* @method extra
* @param {function} callback1 Function that does mutation but gets stored as a "hidden" extra undo level.
* @param {function} callback2 Function that does mutation but gets displayed to the user.
*/
extra: function (callback1, callback2) {
var lastLevel, bookmark;
if (self.transact(callback1)) {
bookmark = data[index].bookmark;
lastLevel = data[index - 1];
editor.setContent(lastLevel.content, {format: 'raw'});
editor.selection.moveToBookmark(lastLevel.beforeBookmark);
if (self.transact(callback2)) {
data[index - 1].beforeBookmark = bookmark;
}
}
}
};
@ -23050,9 +23087,9 @@ define("tinymce/InsertContent", [
], function(Env, Tools, Serializer, CaretWalker, CaretPosition, ElementUtils, NodeType, InsertList) {
var isTableCell = NodeType.matchNodeNames('td th');
var insertAtCaret = function(editor, value) {
var insertHtmlAtCaret = function(editor, value, details) {
var parser, serializer, parentNode, rootNode, fragment, args;
var marker, rng, node, node2, bookmarkHtml, merge, data;
var marker, rng, node, node2, bookmarkHtml, merge;
var textInlineElements = editor.schema.getTextInlineElements();
var selection = editor.selection, dom = editor.dom;
@ -23109,25 +23146,13 @@ define("tinymce/InsertContent", [
}
}
function markInlineFormatElements(fragment) {
if (merge) {
for (node = fragment.firstChild; node; node = node.walk(true)) {
if (textInlineElements[node.name]) {
node.attr('data-mce-new', "true");
}
}
}
}
function reduceInlineTextElements() {
if (merge) {
var root = editor.getBody(), elementUtils = new ElementUtils(dom);
Tools.each(dom.select('*[data-mce-new]'), function(node) {
node.removeAttribute('data-mce-new');
Tools.each(dom.select('*[data-mce-fragment]'), function(node) {
for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
if (elementUtils.compare(testNode, node)) {
if (textInlineElements[node.nodeName.toLowerCase()] && elementUtils.compare(testNode, node)) {
dom.remove(node, true);
}
}
@ -23241,12 +23266,6 @@ define("tinymce/InsertContent", [
selection.setRng(rng);
}
if (typeof value != 'string') {
merge = value.merge;
data = value.data;
value = value.content;
}
// Check for whitespace before/after value
if (/^ | $/.test(value)) {
value = trimOrPaddLeftRight(value);
@ -23254,6 +23273,8 @@ define("tinymce/InsertContent", [
// Setup parser and serializer
parser = editor.parser;
merge = details.merge;
serializer = new Serializer({
validate: editor.settings.validate
}, editor.schema);
@ -23297,7 +23318,7 @@ define("tinymce/InsertContent", [
parentNode = selection.getNode();
// Parse the fragment within the context of the parent node
var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: data};
var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: details.data};
fragment = parser.parse(value, parserArgs);
// Custom handling of lists
@ -23309,7 +23330,6 @@ define("tinymce/InsertContent", [
}
markFragmentElements(fragment);
markInlineFormatElements(fragment);
// Move the caret to a more suitable location
node = fragment.lastChild;
@ -23388,6 +23408,34 @@ define("tinymce/InsertContent", [
editor.addVisual();
};
var processValue = function (value) {
var details;
if (typeof value !== 'string') {
details = Tools.extend({
paste: value.paste,
data: {
paste: value.paste
}
}, value);
return {
content: value.content,
details: details
};
}
return {
content: value,
details: {}
};
};
var insertAtCaret = function (editor, value) {
var result = processValue(value);
insertHtmlAtCaret(editor, result.content, result.details);
};
return {
insertAtCaret: insertAtCaret
};
@ -31802,8 +31850,10 @@ define("tinymce/util/Quirks", [
"tinymce/Env",
"tinymce/util/Tools",
"tinymce/util/Delay",
"tinymce/caret/CaretContainer"
], function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer) {
"tinymce/caret/CaretContainer",
"tinymce/caret/CaretPosition",
"tinymce/caret/CaretWalker"
], function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer, CaretPosition, CaretWalker) {
return function(editor) {
var each = Tools.each, $ = editor.$;
var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
@ -33435,6 +33485,46 @@ define("tinymce/util/Quirks", [
return (!sel || !sel.rangeCount || sel.rangeCount === 0);
}
/**
* Properly empties the editor if all contents is selected and deleted this to
* prevent empty paragraphs from being produced at beginning/end of contents.
*/
function emptyEditorOnDeleteEverything() {
function isEverythingSelected(editor) {
var caretWalker = new CaretWalker(editor.getBody());
var rng = editor.selection.getRng();
var startCaretPos = CaretPosition.fromRangeStart(rng);
var endCaretPos = CaretPosition.fromRangeEnd(rng);
return !editor.selection.isCollapsed() && !caretWalker.prev(startCaretPos) && !caretWalker.next(endCaretPos);
}
// Type over case delete and insert this won't cover typeover with a IME but at least it covers the common case
editor.on('keypress', function (e) {
if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) {
if (isEverythingSelected(editor)) {
e.preventDefault();
editor.setContent(String.fromCharCode(e.charCode));
editor.selection.select(editor.getBody(), true);
editor.selection.collapse(false);
editor.nodeChanged();
}
}
});
editor.on('keydown', function (e) {
var keyCode = e.keyCode;
if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
if (isEverythingSelected(editor)) {
e.preventDefault();
editor.setContent('');
editor.nodeChanged();
}
}
});
}
// All browsers
removeBlockQuoteOnBackSpace();
emptyEditorWhenDeleting();
@ -33447,6 +33537,7 @@ define("tinymce/util/Quirks", [
// WebKit
if (isWebKit) {
emptyEditorOnDeleteEverything();
cleanupStylesWhenDeleting();
inputMethodFocus();
selectControlElements();
@ -33454,6 +33545,7 @@ define("tinymce/util/Quirks", [
blockFormSubmitInsideEditor();
disableBackspaceIntoATable();
removeAppleInterchangeBrs();
//touchClickEvent();
// iOS
@ -33491,6 +33583,7 @@ define("tinymce/util/Quirks", [
// Gecko
if (isGecko) {
emptyEditorOnDeleteEverything();
removeHrOnBackspace();
focusBody();
removeStylesWhenDeletingAcrossBlockElements();
@ -35584,6 +35677,7 @@ define("tinymce/DragDropOverrides", [
}
if (state.dragging) {
editor._selectionOverrides.hideFakeCaret();
editor.selection.placeCaretAt(e.clientX, e.clientY);
clientX = state.clientX + deltaX - state.relX;
@ -35612,8 +35706,8 @@ define("tinymce/DragDropOverrides", [
}
}
function drop() {
var evt;
function drop(evt) {
var dropEvt;
if (state.dragging) {
// Hack for IE since it doesn't sync W3C Range with IE Specific range
@ -35622,12 +35716,18 @@ define("tinymce/DragDropOverrides", [
if (isValidDropTarget(editor.selection.getNode())) {
var targetClone = state.element;
evt = editor.fire('drop', {targetClone: targetClone});
if (evt.isDefaultPrevented()) {
// Pass along clientX, clientY if we have them
dropEvt = editor.fire('drop', {
targetClone: targetClone,
clientX: evt.clientX,
clientY: evt.clientY
});
if (dropEvt.isDefaultPrevented()) {
return;
}
targetClone = evt.targetClone;
targetClone = dropEvt.targetClone;
editor.undoManager.transact(function() {
editor.insertContent(dom.getOuterHTML(targetClone));
@ -35693,7 +35793,8 @@ define("tinymce/DragDropOverrides", [
// Blocks drop inside cE=false on IE
editor.on('drop', function(e) {
var realTarget = editor.getDoc().elementFromPoint(e.clientX, e.clientY);
// FF doesn't pass out clientX/clientY for drop since this is for IE we just use null instead
var realTarget = typeof e.clientX !== 'undefined' ? editor.getDoc().elementFromPoint(e.clientX, e.clientY) : null;
if (isContentEditableFalse(realTarget) || isContentEditableFalse(editor.dom.getContentEditableParent(realTarget))) {
e.preventDefault();
@ -36169,8 +36270,8 @@ define("tinymce/SelectionOverrides", [
return toCaretPosition.toRange();
}
function backspaceDelete(direction, beforeFn, range) {
var node, caretPosition, peekCaretPosition;
function backspaceDelete(direction, beforeFn, afterFn, range) {
var node, caretPosition, peekCaretPosition, newCaretPosition;
if (!range.collapsed) {
node = getSelectedNode(range);
@ -36181,6 +36282,11 @@ define("tinymce/SelectionOverrides", [
caretPosition = getNormalizedRangeEndPoint(direction, range);
if (afterFn(caretPosition) && CaretContainer.isCaretContainerBlock(range.startContainer)) {
newCaretPosition = direction == -1 ? caretWalker.prev(caretPosition) : caretWalker.next(caretPosition);
return newCaretPosition ? renderRangeCaret(newCaretPosition.toRange()) : range;
}
if (beforeFn(caretPosition)) {
return renderRangeCaret(deleteContentEditableNode(caretPosition.getNode(direction == -1)));
}
@ -36198,8 +36304,8 @@ define("tinymce/SelectionOverrides", [
function registerEvents() {
var right = curry(moveH, 1, getNextVisualCaretPosition, isBeforeContentEditableFalse);
var left = curry(moveH, -1, getPrevVisualCaretPosition, isAfterContentEditableFalse);
var deleteForward = curry(backspaceDelete, 1, isBeforeContentEditableFalse);
var backspace = curry(backspaceDelete, -1, isAfterContentEditableFalse);
var deleteForward = curry(backspaceDelete, 1, isBeforeContentEditableFalse, isAfterContentEditableFalse);
var backspace = curry(backspaceDelete, -1, isAfterContentEditableFalse, isBeforeContentEditableFalse);
var up = curry(moveV, -1, LineWalker.upUntil);
var down = curry(moveV, 1, LineWalker.downUntil);
@ -36562,7 +36668,6 @@ define("tinymce/SelectionOverrides", [
top: dom.getPos(node, editor.getBody()).y
});
editor.getBody().focus();
$realSelectionContainer[0].focus();
sel = editor.selection.getSel();
sel.removeAllRanges();
@ -36588,6 +36693,10 @@ define("tinymce/SelectionOverrides", [
selectedContentEditableNode = null;
}
function hideFakeCaret() {
fakeCaret.hide();
}
if (Env.ceFalse) {
registerEvents();
addCss();
@ -36595,6 +36704,7 @@ define("tinymce/SelectionOverrides", [
return {
showBlockCaretContainer: showBlockCaretContainer,
hideFakeCaret: hideFakeCaret,
destroy: destroy
};
}
@ -36602,6 +36712,45 @@ define("tinymce/SelectionOverrides", [
return SelectionOverrides;
});
// Included from: js/tinymce/classes/util/Uuid.js
/**
* Uuid.js
*
* Released under LGPL License.
* Copyright (c) 1999-2016 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/**
* Generates unique ids.
*
* @class tinymce.util.Uuid
* @private
*/
define("tinymce/util/Uuid", [
], function() {
var count = 0;
var seed = function () {
var rnd = function () {
return Math.round(Math.random() * 0xFFFFFFFF).toString(36);
};
return 's' + Date.now().toString(36) + rnd() + rnd() + rnd();
};
var uuid = function (prefix) {
return prefix + (count++) + seed();
};
return {
uuid: uuid
};
});
// Included from: js/tinymce/classes/Editor.js
/**
@ -36675,13 +36824,14 @@ define("tinymce/Editor", [
"tinymce/Mode",
"tinymce/Shortcuts",
"tinymce/EditorUpload",
"tinymce/SelectionOverrides"
"tinymce/SelectionOverrides",
"tinymce/util/Uuid"
], function(
DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
URI, ScriptLoader, EventUtils, WindowManager, NotificationManager,
Schema, DomParser, Quirks, Env, Tools, Delay, EditorObservable, Mode, Shortcuts, EditorUpload,
SelectionOverrides
SelectionOverrides, Uuid
) {
// Shorten these names
var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
@ -36857,6 +37007,7 @@ define("tinymce/Editor", [
self.suffix = editorManager.suffix;
self.editorManager = editorManager;
self.inline = settings.inline;
self.settings.content_editable = self.inline;
if (settings.cache_suffix) {
Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
@ -37974,6 +38125,7 @@ define("tinymce/Editor", [
}
self.contextToolbars.push({
id: Uuid.uuid('mcet'),
predicate: predicate,
items: items
});
@ -39149,9 +39301,9 @@ define("tinymce/FocusManager", [
// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
if (editor.inline && !documentMouseUpHandler) {
documentMouseUpHandler = function(e) {
var activeEditor = editorManager.activeEditor;
var activeEditor = editorManager.activeEditor, dom = activeEditor.dom;
if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
if (activeEditor.inline && dom && !dom.isChildOf(e.target, activeEditor.getBody())) {
var rng = activeEditor.selection.getRng();
if (!rng.collapsed) {
@ -39318,7 +39470,7 @@ define("tinymce/EditorManager", [
* @property minorVersion
* @type String
*/
minorVersion: '3.13',
minorVersion: '4.0',
/**
* Release date of TinyMCE build.
@ -39326,7 +39478,7 @@ define("tinymce/EditorManager", [
* @property releaseDate
* @type String
*/
releaseDate: '2016-06-08',
releaseDate: '2016-06-30',
/**
* Collection of editor instances.
@ -39491,7 +39643,24 @@ define("tinymce/EditorManager", [
* });
*/
init: function(settings) {
var self = this, result;
var self = this, result, invalidInlineTargets;
invalidInlineTargets = Tools.makeMap(
'area base basefont br col frame hr img input isindex link meta param embed source wbr track ' +
'colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu',
' '
);
function isInvalidInlineTarget(settings, elm) {
return settings.inline && elm.tagName.toLowerCase() in invalidInlineTargets;
}
function report(msg, elm) {
// Log in a non test environment
if (window.console && !window.test) {
window.console.log(msg, elm);
}
}
function createId(elm) {
var id = elm.id;
@ -39637,7 +39806,11 @@ define("tinymce/EditorManager", [
});
each(targets, function(elm) {
createEditor(createId(elm), settings, elm);
if (isInvalidInlineTarget(settings, elm)) {
report('Could not initialize inline editor on invalid inline target element', elm);
} else {
createEditor(createId(elm), settings, elm);
}
});
}

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ $wp_db_version = 37965;
*
* @global string $tinymce_version
*/
$tinymce_version = '4313-20160629';
$tinymce_version = '4400-20160711';
/**
* Holds the required PHP version

View File

@ -22,6 +22,7 @@
<script src="js/qunit/qunit.js"></script>
<script src="js/tinymce_loader.js"></script>
<!--<script src="../js/tinymce/classes/jquery.tinymce.js"></script> -->
<script src="../../../src/wp-includes/js/tinymce/plugins/paste/plugin.js"></script>
<script src="js/utils.js"></script>
<script src="js/init.js"></script>
@ -46,6 +47,7 @@
<!-- tinymce.file.* -->
<script src="tinymce/file/Conversions.js"></script>
<script src="tinymce/file/ImageScanner.js"></script>
<script src="tinymce/file/UploadStatus.js"></script>
<!-- tinymce.fmt.* -->
<script src="tinymce/fmt/Hooks.js"></script>
@ -126,6 +128,8 @@
<script src="tinymce/UndoManager.js"></script>
<script src="tinymce/SelectionOverrides.js"></script>
<script src="tinymce/WindowManager.js"></script>
<script src="tinymce/InsertContent.js"></script>
<script src="tinymce/InsertList.js"></script>
<!-- tinymce.plugins.* -->
<!--<script src="plugins/autolink.js"></script>-->
@ -142,6 +146,8 @@
<script src="plugins/media.js"></script>
<!--<script src="plugins/noneditable.js"></script> -->
<script src="plugins/paste.js"></script>
<script src="plugins/paste_images.js"></script>
<script src="plugins/paste_smart.js"></script>
<!--<script src="plugins/searchreplace.js"></script>-->
<!--<script src="plugins/spellchecker.js"></script>-->
<!--<script src="plugins/table.js"></script>-->

View File

@ -35,9 +35,9 @@ ModuleLoader.require([
indent: false,
schema: 'html5',
entities: 'raw',
valid_elements: 'li,ol,ul,dl,dt,dd,em,strong,span,#p,div,br',
valid_elements: 'li,ol[style],ul[style],dl,dt,dd,em,strong,span,#p,div,br',
valid_styles: {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left'
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left,list-style-type'
},
disable_nodechange: true,
init_instance_callback: function(ed) {
@ -56,7 +56,11 @@ ModuleLoader.require([
init_instance_callback: function(ed) {
window.inlineEditor = ed;
wait();
}
},
valid_styles: {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left,list-style-type'
},
});
tinymce.init({
@ -69,7 +73,11 @@ ModuleLoader.require([
init_instance_callback: function(ed) {
inlineEditor2 = ed;
wait();
}
},
valid_styles: {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display,position,top,left,list-style-type'
},
});
},
@ -87,9 +95,9 @@ ModuleLoader.require([
return html.toLowerCase().replace(/<br[^>]*>|[\r\n]+/gi, '');
}
function execCommand(cmd) {
function execCommand(cmd, ui, obj) {
if (editor.editorCommands.hasCustomCommand(cmd)) {
editor.execCommand(cmd);
editor.execCommand(cmd, ui, obj);
}
}
@ -379,6 +387,166 @@ ModuleLoader.require([
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to UL and DO not merge with adjacent lists because styles are different (exec has style)', function() {
editor.getBody().innerHTML = trimBrs(
'<ol>' +
'<li>a</li>' +
'</ol>' +
'<ul><li>b</li></ul>' +
'<ol>' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('ul li', 1);
execCommand('InsertOrderedList', null, { 'list-style-type': 'lower-alpha' });
equal(editor.getContent(),
'<ol>' +
'<li>a</li>' +
'</ol>' +
'<ol style="list-style-type: lower-alpha;"><li>b</li></ol>' +
'<ol>' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to P and DO not merge with adjacent lists because styles are different (exec has style)', function() {
editor.getBody().innerHTML = trimBrs(
'<ol>' +
'<li>a</li>' +
'</ol>' +
'<p>b</p>' +
'<ol>' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('p', 1);
execCommand('InsertOrderedList', null, { 'list-style-type': 'lower-alpha' });
equal(editor.getContent(),
'<ol>' +
'<li>a</li>' +
'</ol>' +
'<ol style="list-style-type: lower-alpha;"><li>b</li></ol>' +
'<ol>' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to UL and DO not merge with adjacent lists because styles are different (original has style)', function() {
editor.getBody().innerHTML = trimBrs(
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ul><li>b</li></ul>' +
'<ol style="list-style-type: upper-roman;">' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('ul li', 1);
execCommand('InsertOrderedList');
equal(editor.getContent(),
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ol><li>b</li></ol>' +
'<ol style="list-style-type: upper-roman;">' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to UL should merge with adjacent lists because styles are the same (both have roman)', function() {
editor.getBody().innerHTML = trimBrs(
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ul><li>b</li></ul>' +
'<ol style="list-style-type: upper-roman;">' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('ul li', 1);
execCommand('InsertOrderedList', false, { 'list-style-type': 'upper-roman' });
equal(editor.getContent(),
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'<li>b</li>' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to UL should merge with above list because styles are the same (both have lower-roman), but not below list', function() {
editor.getBody().innerHTML = trimBrs(
'<ol style="list-style-type: lower-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ul><li>b</li></ul>' +
'<ol style="list-style-type: upper-roman;">' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('ul li', 1);
execCommand('InsertOrderedList', false, { 'list-style-type': 'lower-roman' });
equal(editor.getContent(),
'<ol style="list-style-type: lower-roman;">' +
'<li>a</li>' +
'<li>b</li>' +
'</ol>' +
'<ol style="list-style-type: upper-roman;">' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply OL to UL should merge with below lists because styles are the same (both have roman), but not above list', function() {
editor.getBody().innerHTML = trimBrs(
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ul><li>b</li></ul>' +
'<ol style="list-style-type: lower-roman;">' +
'<li>c</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('ul li', 1);
execCommand('InsertOrderedList', false, { 'list-style-type': 'lower-roman' });
equal(editor.getContent(),
'<ol style="list-style-type: upper-roman;">' +
'<li>a</li>' +
'</ol>' +
'<ol style="list-style-type: lower-roman;">' +
'<li>b</li>' +
'<li>c</li>' +
'</ol>'
);
equal(editor.selection.getStart().nodeName, 'LI');
});
test('Apply UL list to single text line', function() {
editor.settings.forced_root_block = false;
@ -1302,6 +1470,31 @@ ModuleLoader.require([
equal(editor.selection.getNode().nodeName, 'LI');
});
test('Indent single LI in OL and retain OLs list style in the new OL', function() {
editor.getBody().innerHTML = trimBrs(
'<ol style="list-style-type: lower-alpha;">' +
'<li>a</li>' +
'<li>b</li>' +
'</ol>'
);
editor.focus();
Utils.setSelection('li:nth-child(2)', 0);
execCommand('Indent');
equal(editor.getContent(),
'<ol style="list-style-type: lower-alpha;">' +
'<li>a' +
'<ol style="list-style-type: lower-alpha;">' +
'<li>b</li>' +
'</ol>' +
'</li>' +
'</ol>'
);
});
test('Indent last LI in OL', function() {
editor.getBody().innerHTML = trimBrs(
'<ol>' +

View File

@ -0,0 +1,188 @@
ModuleLoader.require([
"tinymce/pasteplugin/Clipboard",
"tinymce/Env",
"tinymce/util/Delay",
"tinymce/util/Promise"
], function(Clipboard, Env, Delay, Promise) {
var base64ImgSrc = [
'R0lGODdhZABkAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgAAACwAAAAAZABkAIEAAAD78jY/',
'P3SsMjIC/4SPqcvtD6OctNqLs968+w+G4kiW5ommR8C27gvHrxrK9g3TIM7f+tcL5n4doZFFLB6F',
'Sc6SCRFIp9SqVTp6BiPXbjer5XG95Ck47IuWy2e0bLz2tt3DR5w8p7vgd2tej6TW5ycCGMM3aFZo',
'OCOYqFjDuOf4KPAHiPh4qZeZuEnXOfjpFto3ilZ6dxqWGreq1br2+hTLtigZaFcJuYOb67DLC+Qb',
'UIt3i2sshyzZtEFc7JwBLT1NXI2drb3N3e39DR4uPk5ebn6Onq6+zu488A4fLz9P335Aj58fb2+g',
'71/P759AePwADBxY8KDAhAr9MWyY7yFEgPYmRgxokWK7jEYa2XGcJ/HjgJAfSXI0mRGlRZUTWUJ0',
'2RCmQpkHaSLEKPKdzYU4c+78VzCo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9eqEAADs='
].join('');
if (!Env.fileApi) {
return;
}
module("tinymce.plugins.Paste - Images", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
disable_nodechange: true,
skin: false,
entities: 'raw',
indent: false,
automatic_uploads: false,
plugins: "paste",
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
},
teardown: function() {
delete editor.settings.paste_data_images;
delete editor.settings.images_dataimg_filter;
editor.editorUpload.destroy();
}
});
var base64ToBlob = function (base64, type) {
var buff = atob(base64);
var bytes = new Uint8Array(buff.length);
for (var i = 0; i < bytes.length; i++) {
bytes[i] = buff.charCodeAt(i);
}
return new Blob([bytes], {type: type});
};
var noop = function () {
};
var mockEvent = function (type) {
var event, transferName;
event = {
type: type,
preventDefault: noop
};
transferName = type === 'drop' ? 'dataTransfer' : 'clipboardData';
event[transferName] = {
files: [
base64ToBlob(base64ImgSrc, 'image/gif')
]
};
return event;
};
var setupContent = function () {
editor.setContent('<p>a</p>');
Utils.setSelection('p', 0);
return editor.selection.getRng();
};
var waitForSelector = function (selector) {
return new Promise(function (resolve, reject) {
var check = function (time, count) {
var result = editor.dom.select(selector);
if (result.length > 0) {
resolve(result);
} else {
if (count === 0) {
reject();
} else {
Delay.setTimeout(function () {
check(time, count--);
}, time);
}
}
};
check(10, 100);
});
};
var fail = function () {
ok(false, 'Failed to get image due to timeout.');
QUnit.start();
};
asyncTest('dropImages', function() {
var rng, event, clipboard = new Clipboard(editor);
editor.settings.paste_data_images = true;
rng = setupContent();
event = mockEvent('drop');
clipboard.pasteImageData(event, rng);
waitForSelector('img').then(function () {
equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
strictEqual(editor.dom.select('img')[0].src.indexOf('blob:'), 0);
QUnit.start();
}, fail);
});
asyncTest('pasteImages', function() {
var rng, event, clipboard = new Clipboard(editor);
editor.settings.paste_data_images = true;
rng = setupContent();
event = mockEvent('paste');
clipboard.pasteImageData(event, rng);
waitForSelector('img').then(function () {
equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
strictEqual(editor.dom.select('img')[0].src.indexOf('blob:'), 0);
QUnit.start();
}, fail);
});
asyncTest('dropImages - images_dataimg_filter', function() {
var rng, event, clipboard = new Clipboard(editor);
editor.settings.paste_data_images = true;
editor.settings.images_dataimg_filter = function (img) {
strictEqual(img.src, 'data:image/gif;base64,' + base64ImgSrc);
return false;
};
rng = setupContent();
event = mockEvent('drop');
clipboard.pasteImageData(event, rng);
waitForSelector('img').then(function () {
equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
strictEqual(editor.dom.select('img')[0].src.indexOf('data:'), 0);
QUnit.start();
}, fail);
});
asyncTest('pasteImages - images_dataimg_filter', function() {
var rng, event, clipboard = new Clipboard(editor);
editor.settings.paste_data_images = true;
editor.settings.images_dataimg_filter = function (img) {
strictEqual(img.src, 'data:image/gif;base64,' + base64ImgSrc);
return false;
};
rng = setupContent();
event = mockEvent('paste');
clipboard.pasteImageData(event, rng);
waitForSelector('img').then(function () {
equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
strictEqual(editor.dom.select('img')[0].src.indexOf('data:'), 0);
QUnit.start();
}, fail);
});
});

View File

@ -0,0 +1,68 @@
ModuleLoader.require([
"tinymce/pasteplugin/SmartPaste"
], function (SmartPaste) {
module("tinymce.plugins.Paste_smart", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
skin: false,
indent: false,
plugins: 'paste',
setup: function(ed) {
ed.on('NodeChange', false);
},
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
}
});
test('isAbsoluteUrl', function() {
equal(SmartPaste.isAbsoluteUrl('http://www.site.com'), true);
equal(SmartPaste.isAbsoluteUrl('https://www.site.com'), true);
equal(SmartPaste.isAbsoluteUrl('http://www.site.com/dir-name/file.gif?query=%42'), true);
equal(SmartPaste.isAbsoluteUrl('https://www.site.com/dir-name/file.gif?query=%42'), true);
equal(SmartPaste.isAbsoluteUrl('file.gif'), false);
equal(SmartPaste.isAbsoluteUrl(''), false);
});
test('isImageUrl', function() {
equal(SmartPaste.isImageUrl('http://www.site.com'), false);
equal(SmartPaste.isImageUrl('https://www.site.com'), false);
equal(SmartPaste.isImageUrl('http://www.site.com/dir-name/file.gif'), true);
equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.gif'), true);
equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.gif?query=%42'), false);
equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.html?query=%42'), false);
equal(SmartPaste.isImageUrl('file.gif'), false);
equal(SmartPaste.isImageUrl(''), false);
});
test('smart paste url on selection', function() {
editor.focus();
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
Utils.setSelection('p', 0, 'p', 3);
editor.undoManager.add();
editor.execCommand('mceInsertClipboardContent', false, {content: 'http://www.site.com'});
equal(editor.getContent(), '<p><a href="http://www.site.com">abc</a></p>');
equal(editor.undoManager.data.length, 3);
});
test('smart paste image url', function() {
editor.focus();
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
Utils.setSelection('p', 1);
editor.undoManager.add();
editor.execCommand('mceInsertClipboardContent', false, {content: 'http://www.site.com/my.jpg'});
equal(editor.getContent(), '<p>a<img src="http://www.site.com/my.jpg" />bc</p>');
equal(editor.undoManager.data.length, 3);
});
});

View File

@ -505,6 +505,157 @@
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>1</td><td>2</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table>');
});
test("mceTableInsertRowAfter command on merged cells", function() {
editor.setContent(
'<table>' +
'<tr><td>1</td><td>2</td><td>3</td></tr>' +
'<tr><td>4</td><td colspan="2" rowspan="2">5</td></tr>' +
'<tr><td>6</td></tr>' +
'</table>'
);
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTableInsertRowAfter');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td><td>3</td></tr>' +
'<tr><td>4</td><td colspan="2" rowspan="3">5</td></tr>' +
'<tr><td>&nbsp;</td></tr>' +
'<tr><td>6</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("mceTablePasteRowBefore command", function() {
editor.setContent(
'<table>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'</table>'
);
Utils.setSelection('tr:nth-child(1) td', 0);
editor.execCommand('mceTableCopyRow');
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTablePasteRowBefore');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'</tbody>' +
'</table>'
);
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTablePasteRowBefore');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("mceTablePasteRowAfter command", function() {
editor.setContent(
'<table>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'</table>'
);
Utils.setSelection('tr:nth-child(1) td', 0);
editor.execCommand('mceTableCopyRow');
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTablePasteRowAfter');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'</tbody>' +
'</table>'
);
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTablePasteRowAfter');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("row clipboard api", function() {
var clipboardRows;
editor.setContent(
'<table>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'</table>'
);
Utils.setSelection('tr:nth-child(1) td', 0);
editor.execCommand('mceTableCopyRow');
clipboardRows = editor.plugins.table.getClipboardRows();
equal(clipboardRows.length, 1);
equal(clipboardRows[0].tagName, 'TR');
editor.plugins.table.setClipboardRows(clipboardRows.concat([
editor.dom.create('tr', {}, '<td>a</td><td>b</td>'),
editor.dom.create('tr', {}, '<td>c</td><td>d</td>')
]));
Utils.setSelection('tr:nth-child(2) td', 0);
editor.execCommand('mceTablePasteRowAfter');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>2</td><td>3</td></tr>' +
'<tr><td>1</td><td>2</td></tr>' +
'<tr><td>a</td><td>b</td></tr>' +
'<tr><td>c</td><td>d</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("mceTableInsertRowBefore command", function() {
editor.setContent('<table><tr><td>1</td><td>2</td></tr></table>');
Utils.setSelection('td', 0);
@ -516,7 +667,55 @@
editor.getBody().innerHTML = '<table><tr><td data-mce-selected="1">1</td><td data-mce-selected="1">2</td></tr></table>';
Utils.setSelection('td', 0);
editor.execCommand('mceTableMergeCells');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td colspan="2">12</td></tr></tbody></table>');
equal(cleanTableHtml(editor.getContent()), '<table><tbody><tr><td>12</td></tr></tbody></table>');
});
test("mceTableMergeCells command with all cells selected", function() {
editor.getBody().innerHTML = (
'<table>' +
'<tbody>' +
'<tr><td data-mce-selected="1">1</td><td data-mce-selected="1">2</td></tr>' +
'<tr><td data-mce-selected="1">3</td><td data-mce-selected="1">4</td></tr>' +
'</tbody>' +
'</table>'
);
Utils.setSelection('td', 0);
editor.execCommand('mceTableMergeCells');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td>1234</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("mceTableMergeCells command with whole rows selected", function() {
editor.getBody().innerHTML = (
'<table>' +
'<tbody>' +
'<tr><td data-mce-selected="1">1</td><td data-mce-selected="1">2</td></tr>' +
'<tr><td data-mce-selected="1">3</td><td data-mce-selected="1">4</td></tr>' +
'<tr><td>5</td><td>6</td></tr>' +
'</tbody>' +
'</table>'
);
Utils.setSelection('td', 0);
editor.execCommand('mceTableMergeCells');
equal(
cleanTableHtml(editor.getContent()),
'<table>' +
'<tbody>' +
'<tr><td colspan="2">1234</td></tr>' +
'<tr><td>5</td><td>6</td></tr>' +
'</tbody>' +
'</table>'
);
});
test("mceTableSplitCells command", function() {
@ -1022,4 +1221,67 @@
equal(cells[cells.length - 1].getAttribute('data-counter'), "8");
equal(rows[rows.length - 1].getAttribute('data-counter'), "6");
});
function assertTableSelection(tableHtml, selectCells, cellContents) {
function selectRangeXY(table, startTd, endTd) {
editor.fire('mousedown', {target: startTd});
editor.fire('mouseover', {target: endTd});
editor.fire('mouseup', {target: endTd});
}
function getCells(table) {
return editor.$(table).find('td').toArray();
}
function getSelectedCells(table) {
return editor.$(table).find('td[data-mce-selected]').toArray();
}
editor.setContent(tableHtml);
var table = editor.$('table')[0];
var cells = getCells(table);
var startTd = tinymce.grep(cells, function(elm) {
return elm.innerHTML === selectCells[0];
})[0];
var endTd = tinymce.grep(cells, function(elm) {
return elm.innerHTML === selectCells[1];
})[0];
selectRangeXY(table, startTd, endTd);
var selection = getSelectedCells(table);
selection = tinymce.map(selection, function(elm) {
return elm.innerHTML;
});
deepEqual(selection, cellContents);
}
test("Table grid selection", function() {
assertTableSelection('<table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>', ['1', '2'], ['1', '2']);
assertTableSelection('<table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>', ['1', '3'], ['1', '3']);
assertTableSelection('<table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>', ['1', '4'], ['1', '2', '3', '4']);
assertTableSelection('<table><tr><td colspan="2" rowspan="2">1</td><td>3</td></tr><tr><td>6</td></tr></table>', ['1', '6'], ['1', '3', '6']);
assertTableSelection(
'<table>' +
'<tr>' +
'<td>1</td>' +
'<td>2</td>' +
'<td>3</td>' +
'</tr>' +
'<tr>' +
'<td colspan="2" rowspan="2">4</td>' +
'<td>5</td>' +
'</tr>' +
'<tr>' +
'<td>6</td>' +
'</tr>' +
'</table>',
['2', '6'],
['2', '3', '4', '5', '6']
);
});
})();

View File

@ -482,4 +482,12 @@ test('setMode', function() {
editor.dom.fire(editor.getBody(), 'click');
equal(editor.theme.panel.find('button:last')[2].disabled(), false);
equal(clickCount, 2);
});
test('translate', function() {
tinymce.addI18n('en_US', {
'input i18n': 'output i18n'
});
equal(editor.translate('input i18n'), 'output i18n');
});

View File

@ -78,7 +78,25 @@ test('mceInsertContent HR at end of H1 with P sibling', function() {
equal(getContent(), '<h1>abc</h1><hr /><p>def</p>');
});
test('mceInsertContent HR at end of H1 with P sibling', function() {
test('mceInsertContent HR at end of H1 with inline elements with P sibling', function() {
editor.setContent('<h1><strong>abc</strong></h1><p>def</p>');
Utils.setSelection('strong', 3);
editor.execCommand('mceInsertContent', false, '<hr>');
equal(editor.selection.getNode(), editor.getBody().lastChild);
equal(editor.selection.getNode().nodeName, 'P');
equal(getContent(), '<h1><strong>abc</strong></h1><hr /><p>def</p>');
});
test('mceInsertContent empty block', function() {
editor.setContent('<h1>abc</h1>');
Utils.setSelection('h1', 1);
editor.execCommand('mceInsertContent', false, '<p></p>');
equal(editor.selection.getNode(), editor.getBody().childNodes[1]);
equal(editor.selection.getNode().nodeName, 'P');
equal(getContent(), '<h1>a</h1><p>\u00a0</p><h1>bc</h1>');
});
test('mceInsertContent table at end of H1 with P sibling', function() {
editor.setContent('<h1>abc</h1><p>def</p>');
Utils.setSelection('h1', 3);
editor.execCommand('mceInsertContent', false, '<table><tr><td></td></tr></table>');

View File

@ -89,6 +89,7 @@ asyncTest('Init/remove on same id', function() {
tinymce.remove('#' + tinymce.get(1).id);
strictEqual(tinymce.get().length, 1);
strictEqual(tinymce.get(0), tinymce.activeEditor);
textArea.parentNode.removeChild(textArea);
}, 0);
}
});
@ -96,6 +97,36 @@ asyncTest('Init/remove on same id', function() {
strictEqual(tinymce.get().length, 2);
});
asyncTest('Init editor async with proper editors state', function() {
var unloadTheme = function(name) {
var url = tinymce.baseURI.toAbsolute('themes/' + name + '/theme.js');
tinymce.dom.ScriptLoader.ScriptLoader.remove(url);
tinymce.ThemeManager.remove(name);
};
tinymce.remove();
var init = function() {
tinymce.init({
selector: "textarea",
init_instance_callback: function() {
tinymce.util.Delay.setTimeout(function() {
QUnit.start();
}, 0);
}
});
};
unloadTheme("modern");
strictEqual(tinymce.get().length, 0);
init();
strictEqual(tinymce.get().length, 1);
init();
strictEqual(tinymce.get().length, 1);
});
test('overrideDefaults', function() {
var oldBaseURI, oldBaseUrl, oldSuffix;
@ -139,4 +170,27 @@ test('overrideDefaults', function() {
tinymce.suffix = oldSuffix;
tinymce.overrideDefaults({});
});
});
test('Init inline editor on invalid targets', function() {
var invalidNames;
invalidNames = (
'area base basefont br col frame hr img input isindex link meta param embed source wbr track ' +
'colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu'
);
tinymce.remove();
tinymce.each(invalidNames.split(' '), function (invalidName) {
var elm = tinymce.DOM.add(document.body, invalidName, {'class': 'targetEditor'}, null);
tinymce.init({
selector: invalidName + '.targetEditor',
inline: true
});
strictEqual(tinymce.get().length, 0, 'Should not have created an editor');
tinymce.DOM.remove(elm);
});
});

View File

@ -51,6 +51,7 @@ ModuleLoader.require([
teardown: function() {
editor.editorUpload.destroy();
editor.settings.automatic_uploads = false;
delete editor.settings.images_replace_blob_uris;
delete editor.settings.images_dataimg_filter;
}
});
@ -59,6 +60,19 @@ ModuleLoader.require([
return tinymce.DOM.createHTML('img', {src: uri});
}
function assertResult(uploadedBlobInfo, result) {
QUnit.strictEqual(result.length, 1);
QUnit.strictEqual(result[0].status, true);
QUnit.ok(result[0].element.src.indexOf(uploadedBlobInfo.id() + '.png') !== -1);
QUnit.equal('<p><img src="' + uploadedBlobInfo.filename() + '" /></p>', editor.getContent());
return result;
}
function hasBlobAsSource(elm) {
return elm.src.indexOf('blob:') === 0;
}
asyncTest('_scanForImages', function() {
editor.setContent(imageHtml(testBlobDataUri));
@ -72,12 +86,94 @@ ModuleLoader.require([
}).then(QUnit.start);
});
asyncTest('uploadImages', function() {
asyncTest('replace uploaded blob uri with result uri (copy/paste of an uploaded blob uri)', function() {
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success) {
success('file.png');
};
editor._scanForImages().then(function(result) {
var blobUri = result[0].blobInfo.blobUri();
editor.uploadImages(function() {
editor.setContent(imageHtml(blobUri));
QUnit.strictEqual(hasBlobAsSource(editor.$('img')[0]), false);
QUnit.strictEqual(editor.getContent(), '<p><img src="file.png" /></p>');
QUnit.start();
});
});
});
asyncTest('don\'t replace uploaded blob uri with result uri (copy/paste of an uploaded blob uri) since blob uris are retained', function() {
editor.settings.images_replace_blob_uris = false;
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success) {
success('file.png');
};
editor._scanForImages().then(function(result) {
var blobUri = result[0].blobInfo.blobUri();
editor.uploadImages(function() {
editor.setContent(imageHtml(blobUri));
QUnit.strictEqual(hasBlobAsSource(editor.$('img')[0]), true);
QUnit.strictEqual(editor.getContent(), '<p><img src="file.png" /></p>');
QUnit.start();
});
});
});
asyncTest('uploadImages (callback)', function() {
var uploadedBlobInfo;
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success) {
uploadedBlobInfo = data;
success(data.id() + '.png');
};
editor.uploadImages(function(result) {
assertResult(uploadedBlobInfo, result);
editor.uploadImages(function(result) {
QUnit.strictEqual(result.length, 0);
QUnit.start();
});
});
});
asyncTest('uploadImages (promise)', function() {
var uploadedBlobInfo;
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success) {
uploadedBlobInfo = data;
success(data.id() + '.png');
};
editor.uploadImages().then(function(result) {
assertResult(uploadedBlobInfo, result);
}).then(function() {
uploadedBlobInfo = null;
return editor.uploadImages().then(function(result) {
QUnit.strictEqual(result.length, 0);
QUnit.strictEqual(uploadedBlobInfo, null);
QUnit.start();
});
});
});
asyncTest('uploadImages retain blob urls after upload', function() {
var uploadedBlobInfo;
function assertResult(result) {
QUnit.strictEqual(result[0].status, true);
QUnit.ok(result[0].element.src.indexOf(uploadedBlobInfo.id() + '.png') !== -1);
QUnit.ok(hasBlobAsSource(result[0].element), 'Not a blob url');
QUnit.equal('<p><img src="' + uploadedBlobInfo.filename() + '" /></p>', editor.getContent());
return result;
@ -85,6 +181,7 @@ ModuleLoader.require([
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_replace_blob_uris = false;
editor.settings.images_upload_handler = function(data, success) {
uploadedBlobInfo = data;
success(data.id() + '.png');
@ -103,20 +200,56 @@ ModuleLoader.require([
asyncTest('uploadConcurrentImages', function() {
var uploadCount = 0, callCount = 0;
function done() {
function done(result) {
callCount++;
if (callCount == 2) {
QUnit.start();
equal(uploadCount, 1, 'Should only be one upload.');
}
equal(editor.getContent(), '<p><img src="myimage.png" /></p>');
equal(result[0].element, editor.$('img')[0]);
equal(result[0].status, true);
}
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success) {
uploadCount++;
success(data.id() + '.png');
setTimeout(function() {
success('myimage.png');
}, 0);
};
editor.uploadImages(done);
editor.uploadImages(done);
});
asyncTest('uploadConcurrentImages (fail)', function() {
var uploadCount = 0, callCount = 0;
function done(result) {
callCount++;
if (callCount == 2) {
QUnit.start();
equal(uploadCount, 1, 'Should only be one upload.');
}
equal(result[0].element, editor.$('img')[0]);
equal(result[0].status, false);
}
editor.setContent(imageHtml(testBlobDataUri));
editor.settings.images_upload_handler = function(data, success, failure) {
uploadCount++;
setTimeout(function() {
failure('Error');
}, 0);
};
editor.uploadImages(done);

View File

@ -0,0 +1,110 @@
ModuleLoader.require([
"tinymce/InsertContent"
], function(InsertContent) {
module("tinymce.InsertContent", {
setupModule: function() {
QUnit.stop();
tinymce.init({
selector: "textarea",
add_unload_trigger: false,
disable_nodechange: true,
skin: false,
entities: 'raw',
indent: false,
init_instance_callback: function(ed) {
window.editor = ed;
QUnit.start();
}
});
}
});
function assertSelection(selector, offset) {
var node = editor.$(selector)[0];
var rng = editor.selection.getRng();
equal(rng.startContainer, node.firstChild);
equal(rng.startOffset, offset);
equal(rng.collapsed, true);
}
test('insertAtCaret - i inside text, converts to em', function() {
editor.setContent('<p>1234</p>');
editor.focus();
Utils.setSelection('p', 2);
InsertContent.insertAtCaret(editor, '<i>a</i>');
equal(editor.getContent(), '<p>12<em>a</em>34</p>');
});
test('insertAtCaret - ul at beginning of li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 0);
InsertContent.insertAtCaret(editor, '<ul><li>a</li></ul>');
equal(editor.getContent(), '<ul><li>a</li><li>12</li></ul>');
assertSelection('li:nth-child(2)', 0);
});
test('insertAtCaret - ul with multiple items at beginning of li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 0);
InsertContent.insertAtCaret(editor, '<ul><li>a</li><li>b</li></ul>');
equal(editor.getContent(), '<ul><li>a</li><li>b</li><li>12</li></ul>');
assertSelection('li:nth-child(3)', 0);
});
test('insertAtCaret - ul at end of li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 2);
InsertContent.insertAtCaret(editor, '<ul><li>a</li></ul>');
equal(editor.getContent(), '<ul><li>12</li><li>a</li></ul>');
assertSelection('li:nth-child(2)', 1);
});
test('insertAtCaret - ul with multiple items at end of li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 2);
InsertContent.insertAtCaret(editor, '<ul><li>a</li><li>b</li><li>c</li></ul>');
equal(editor.getContent(), '<ul><li>12</li><li>a</li><li>b</li><li>c</li></ul>');
assertSelection('li:nth-child(4)', 1);
});
test('insertAtCaret - ul with multiple items in middle of li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 1);
InsertContent.insertAtCaret(editor, '<ul><li>a</li><li>b</li></ul>');
equal(editor.getContent(), '<ul><li>1</li><li>a</li><li>b</li><li>2</li></ul>');
assertSelection('li:nth-child(4)', 1);
});
test('insertAtCaret - ul in middle of li with formatting', function() {
editor.setContent('<ul><li><em><strong>12</strong></em></li></ul>');
editor.focus();
Utils.setSelection('strong', 1);
InsertContent.insertAtCaret(editor, '<ul><li>a</li></ul>');
equal(editor.getContent(), '<ul><li><em><strong>1</strong></em></li><li>a</li><li><em><strong>2</strong></em></li></ul>');
assertSelection('li:nth-child(3) strong', 1);
});
test('insertAtCaret - ul at beginning of li with empty end li', function() {
editor.setContent('<ul><li>12</li></ul>');
editor.focus();
Utils.setSelection('li', 0);
InsertContent.insertAtCaret(editor, '<ul><li>a</li><li></li></ul>');
equal(editor.getContent(), '<ul><li>a</li><li>12</li></ul>');
assertSelection('li:nth-child(2)', 0);
});
test('insertAtCaret - merge inline elements', function() {
editor.setContent('<strong><em>abc</em></strong>');
editor.focus();
Utils.setSelection('em', 1);
InsertContent.insertAtCaret(editor, {content: '<em><strong>123</strong></em>', merge: true});
equal(editor.getContent(), '<p><strong><em>a123bc</em></strong></p>');
});
});

View File

@ -0,0 +1,41 @@
ModuleLoader.require([
"tinymce/InsertList",
"tinymce/html/Node",
"tinymce/html/DomParser",
"tinymce/dom/DOMUtils"
], function(InsertList, Node, DomParser, DOMUtils) {
module("tinymce.InsertList", {});
var createFragment = function(html) {
var parser = new DomParser({validate: false});
var fragment = parser.parse(html);
return fragment;
};
var createDomFragment = function(html) {
return DOMUtils.DOM.createFragment(html);
};
test('isListFragment', function() {
equal(InsertList.isListFragment(createFragment('<ul><li>x</li></ul>')), true);
equal(InsertList.isListFragment(createFragment('<ol><li>x</li></ol>')), true);
equal(InsertList.isListFragment(createFragment('<meta><ul><li>x</li></ul>')), true);
equal(InsertList.isListFragment(createFragment('<ul><li>x</li></ul><span id="mce_marker"></span>')), true);
equal(InsertList.isListFragment(createFragment('<div></div>')), false);
});
test('listItems', function() {
var list = createDomFragment('<ul><li>a</li><li>b</li><li>c</li></ul>').firstChild;
equal(InsertList.listItems(list).length, 3);
equal(InsertList.listItems(list)[0].nodeName, 'LI');
});
test('trimListItems', function() {
var list = createDomFragment('<ul><li>a</li><li>b</li><li></li></ul>').firstChild;
equal(InsertList.listItems(list).length, 3);
equal(InsertList.trimListItems(InsertList.listItems(list)).length, 2);
});
});

View File

@ -1,8 +1,9 @@
ModuleLoader.require([
"tinymce/caret/CaretPosition",
"tinymce/caret/CaretContainer",
"tinymce/util/VK"
], function(CaretPosition, CaretContainer, VK) {
"tinymce/util/VK",
"tinymce/text/Zwsp"
], function(CaretPosition, CaretContainer, VK, Zwsp) {
module("tinymce.SelectionOverrides", {
setupModule: function() {
QUnit.stop();
@ -44,6 +45,11 @@ ModuleLoader.require([
};
}
function assertCaretInCaretBlockContainer() {
var beforeRng = editor.selection.getRng();
equal(CaretContainer.isCaretContainerBlock(beforeRng.startContainer.parentNode), true, 'Not in caret block container.');
}
var leftArrow = pressKey(VK.LEFT);
var rightArrow = pressKey(VK.RIGHT);
var backspace = pressKey(VK.BACKSPACE);
@ -160,8 +166,148 @@ ModuleLoader.require([
equal(editor.selection.getRng().startContainer, editor.$('div div')[0]);
});
test('backspace from block to after cE=false inline', function() {
editor.setContent('<p>1<span contenteditable="false">2</span></p><p>3</p>');
Utils.setSelection('p:nth-child(2)', 0);
Utils.type('\b');
equal(editor.getContent(), '<p>1<span contenteditable="false">2</span>3</p>');
ok(Zwsp.isZwsp(editor.selection.getRng().startContainer.data));
equal(editor.selection.getRng().startContainer.previousSibling.nodeName, 'SPAN');
});
test('delete from block to before cE=false inline', function() {
editor.setContent('<p>1</p><p><span contenteditable="false">2</span>3</p>');
Utils.setSelection('p:nth-child(1)', 1);
forwardDelete();
equal(editor.getContent(), '<p>1<span contenteditable="false">2</span>3</p>');
ok(Zwsp.isZwsp(editor.selection.getRng().startContainer.data));
equal(editor.selection.getRng().startContainer.nextSibling.nodeName, 'SPAN');
});
test('backspace from before cE=false block to text', function() {
editor.setContent('<p>1</p><p contenteditable="false">2</p><p>3</p>');
editor.selection.select(editor.dom.select('p')[1]);
editor.selection.collapse(true);
assertCaretInCaretBlockContainer();
Utils.type('\b');
var rng = editor.selection.getRng();
equal(editor.getContent(), '<p>1</p><p contenteditable="false">2</p><p>3</p>');
equal(rng.startContainer, editor.dom.select('p')[0].firstChild);
equal(rng.startOffset, 1);
equal(rng.collapsed, true);
});
test('backspace from before first cE=false block', function() {
editor.setContent('<p contenteditable="false">1</p><p>2</p>');
editor.selection.select(editor.dom.select('p')[0]);
editor.selection.collapse(true);
assertCaretInCaretBlockContainer();
Utils.type('\b');
equal(editor.getContent(), '<p contenteditable="false">1</p><p>2</p>');
assertCaretInCaretBlockContainer();
});
test('backspace from before cE=false block to after cE=false block', function() {
editor.setContent('<p contenteditable="false">1</p><p contenteditable="false">2</p>');
editor.selection.select(editor.dom.select('p')[1]);
editor.selection.collapse(true);
assertCaretInCaretBlockContainer();
Utils.type('\b');
var rng = editor.selection.getRng();
equal(editor.getContent(), '<p contenteditable="false">1</p><p contenteditable="false">2</p>');
assertCaretInCaretBlockContainer();
equal(rng.startContainer.parentNode.previousSibling, editor.dom.select('p')[0]);
});
test('delete from after cE=false block to text', function() {
editor.setContent('<p>1</p><p contenteditable="false">2</p><p>3</p>');
editor.selection.select(editor.dom.select('p')[1]);
editor.selection.collapse(false);
assertCaretInCaretBlockContainer();
forwardDelete();
var rng = editor.selection.getRng();
equal(editor.getContent(), '<p>1</p><p contenteditable="false">2</p><p>3</p>');
equal(rng.startContainer, editor.dom.select('p')[2].firstChild);
equal(rng.startOffset, 0);
equal(rng.collapsed, true);
});
test('delete from after last cE=false block', function() {
editor.setContent('<p>1</p><p contenteditable="false">2</p>');
editor.selection.select(editor.dom.select('p')[1]);
editor.selection.collapse(false);
assertCaretInCaretBlockContainer();
forwardDelete();
equal(editor.getContent(), '<p>1</p><p contenteditable="false">2</p>');
assertCaretInCaretBlockContainer();
});
test('delete from after cE=false block to before cE=false block', function() {
editor.setContent('<p contenteditable="false">1</p><p contenteditable="false">2</p>');
editor.selection.select(editor.dom.select('p')[0]);
rightArrow();
assertCaretInCaretBlockContainer();
forwardDelete();
var rng = editor.selection.getRng();
equal(editor.getContent(), '<p contenteditable="false">1</p><p contenteditable="false">2</p>');
assertCaretInCaretBlockContainer();
equal(rng.startContainer.parentNode.nextSibling, editor.dom.select('p')[2]);
});
test('delete from block to before cE=false inline', function() {
editor.setContent('<p>1</p><p><span contenteditable="false">2</span>3</p>');
Utils.setSelection('p:nth-child(1)', 1);
forwardDelete();
equal(editor.getContent(), '<p>1<span contenteditable="false">2</span>3</p>');
ok(Zwsp.isZwsp(editor.selection.getRng().startContainer.data));
equal(editor.selection.getRng().startContainer.nextSibling.nodeName, 'SPAN');
});
test('exit pre block (up)', exitPreTest(upArrow, 0, '<p>\u00a0</p><pre>abc</pre>'));
test('exit pre block (left)', exitPreTest(leftArrow, 0, '<p>\u00a0</p><pre>abc</pre>'));
test('exit pre block (down)', exitPreTest(downArrow, 3, '<pre>abc</pre><p>\u00a0</p>'));
test('exit pre block (right)', exitPreTest(rightArrow, 3, '<pre>abc</pre><p>\u00a0</p>'));
test('click on link in cE=false', function() {
editor.setContent('<p contentEditable="false"><a href="#"><strong>link</strong></a></p>');
var evt = editor.fire('click', {target: editor.$('strong')[0]});
equal(evt.isDefaultPrevented(), true);
});
test('click next to cE=false block', function() {
editor.setContent(
'<table style="width: 100%">' +
'<tr>' +
'<td style="vertical-align: top">1</td>' +
'<td><div contentEditable="false" style="width: 100px; height: 100px">2</div></td>' +
'</tr>' +
'</table>'
);
var firstTd = editor.dom.select('td')[0];
var rect = editor.dom.getRect(firstTd);
editor.fire('mousedown', {
target: firstTd,
clientX: rect.x + rect.w,
clientY: rect.y + 10
});
// Since we can't do a real click we need to check if it gets sucked in towards the cE=false block
equal(editor.selection.getNode().nodeName !== 'P', true);
});
});

View File

@ -193,7 +193,7 @@ test('No undo/redo cmds on Undo/Redo shortcut', function() {
});
test('Transact', function() {
var count = 0;
var count = 0, level;
editor.undoManager.clear();
@ -201,12 +201,32 @@ test('Transact', function() {
count++;
});
editor.undoManager.transact(function() {
level = editor.undoManager.transact(function() {
editor.undoManager.add();
editor.undoManager.add();
});
equal(count, 1);
equal(level !== null, true);
});
test('Transact no change', function() {
editor.undoManager.add();
var level = editor.undoManager.transact(function() {
});
equal(level, null);
});
test('Transact with change', function() {
editor.undoManager.add();
var level = editor.undoManager.transact(function() {
editor.setContent('x');
});
equal(level !== null, true);
});
test('Transact nested', function() {
@ -253,6 +273,34 @@ test('Transact exception', function() {
equal(count, 1);
});
test('Extra with changes', function() {
var data;
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
editor.undoManager.add();
editor.undoManager.extra(function() {
Utils.setSelection('p', 1, 'p', 2);
editor.insertContent('1');
}, function () {
Utils.setSelection('p', 1, 'p', 2);
editor.insertContent('2');
});
data = editor.undoManager.data;
equal(data.length, 3);
equal(data[0].content, '<p>abc</p>');
deepEqual(data[0].bookmark, {start: [0]});
deepEqual(data[0].beforeBookmark, {start: [0]});
equal(data[1].content, '<p>a1c</p>');
deepEqual(data[1].bookmark, {start: [2, 0, 0]});
deepEqual(data[1].beforeBookmark, {start: [2, 0, 0]});
equal(data[2].content, '<p>a2c</p>');
deepEqual(data[2].bookmark, {start: [2, 0, 0]});
deepEqual(data[1].beforeBookmark, data[2].bookmark);
});
test('Exclude internal elements', function() {
var count = 0, lastLevel;

View File

@ -1,4 +1,6 @@
(function() {
ModuleLoader.require([
"tinymce/dom/Range"
], function(Range) {
module("tinymce.dom.Range", {
setup: function() {
document.getElementById('view').innerHTML = (
@ -23,7 +25,7 @@
});
function createRng() {
return document.createRange ? document.createRange() : new tinymce.dom.Range(tinymce.DOM);
return document.createRange ? document.createRange() : new Range(tinymce.DOM);
}
function getHTML(co) {
@ -477,7 +479,7 @@
test("test_compareBoundaryPoints", function() {
var r1 = createRng(), r2 = createRng(), START_TO_START = 0, START_TO_END = 1, END_TO_END = 2, END_TO_START = 3;
r1.setStartBefore(document.getElementById('strong'));
r1.setEndAfter(document.getElementById('strong'));
r2.setStartBefore(document.getElementById('strong'));
@ -545,4 +547,4 @@
equal(rng.toString().replace(/\r\n/g, ''), "strong second em strong.barsome text");
});
}
})();
});

View File

@ -646,6 +646,11 @@ ModuleLoader.require([
equal(rng.collapsed, true);
equal(rng.startContainer.nodeType, 3);
equal(rng.startContainer.data, 'b');
// WebKit is in some state state here, so lets restore it
rng.setStart(editor.getBody(), 0);
rng.setEnd(editor.getBody(), 0);
editor.selection.setRng(rng);
});
test('normalize with contentEditable:true parent and contentEditable:false child element', function() {
@ -657,7 +662,7 @@ ModuleLoader.require([
if (Env.ie && Env.ie < 12) {
// IE automatically normalizes
equal(rng.startContainer.data, 'a');
ok(rng.startContainer.parentNode.contentEditable != 'false');
} else {
equal(CaretContainer.isCaretContainer(rng.startContainer), true);
}
@ -939,6 +944,26 @@ ModuleLoader.require([
equal(rng.endOffset, 0, 'endOffset offset');
});
test('normalize after table should not move', function() {
var rng;
if (tinymce.isOpera || tinymce.isIE) {
ok(true, "Skipped on Opera/IE since Opera doesn't let you to set the range to document and IE will steal focus.");
return;
}
editor.setContent('a<table><tr><td>b</td></tr></table>');
rng = editor.dom.createRng();
rng.setStart(editor.getBody(), 0);
rng.setEnd(editor.getBody(), 1);
editor.selection.setRng(rng);
editor.selection.normalize();
rng = editor.selection.getRng(true);
equal(rng.endContainer, editor.getBody());
equal(rng.endOffset, 1);
});
/*
test('normalize caret after last BR in block', function() {
var rng;

View File

@ -1,8 +1,9 @@
ModuleLoader.require([
"tinymce/file/ImageScanner",
"tinymce/file/UploadStatus",
"tinymce/file/BlobCache",
"tinymce/Env"
], function(ImageScanner, BlobCache, Env) {
], function(ImageScanner, UploadStatus, BlobCache, Env) {
if (!tinymce.Env.fileApi) {
return;
}
@ -12,7 +13,7 @@ ModuleLoader.require([
var base64Src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==';
QUnit.asyncTest("findAll", function() {
var imageScanner = new ImageScanner(new BlobCache());
var imageScanner = new ImageScanner(new UploadStatus(), new BlobCache());
document.getElementById('view').innerHTML = (
'<img src="' + base64Src + '">' +
@ -30,7 +31,7 @@ ModuleLoader.require([
});
QUnit.asyncTest("findAll (filtered)", function() {
var imageScanner = new ImageScanner(new BlobCache());
var imageScanner = new ImageScanner(new UploadStatus(), new BlobCache());
function predicate(img) {
return !img.hasAttribute('data-skip');

View File

@ -0,0 +1,32 @@
ModuleLoader.require([
"tinymce/file/UploadStatus"
], function(UploadStatus) {
module("tinymce.file.UploadStatus");
QUnit.test("hasBlobUri/markPending", function() {
var status = new UploadStatus();
strictEqual(status.hasBlobUri("nonexisting_uri"), false);
status.markPending("existing_uri");
strictEqual(status.isPending("existing_uri"), true);
strictEqual(status.isUploaded("existing_uri"), false);
strictEqual(status.hasBlobUri("existing_uri"), true);
status.markUploaded("existing_uri", "uri");
strictEqual(status.isPending("existing_uri"), false);
strictEqual(status.isUploaded("existing_uri"), true);
strictEqual(status.hasBlobUri("existing_uri"), true);
strictEqual(status.getResultUri("existing_uri"), "uri");
status.markUploaded("existing_uri2", "uri2");
strictEqual(status.isPending("existing_uri"), false);
strictEqual(status.isUploaded("existing_uri"), true);
strictEqual(status.hasBlobUri("existing_uri2"), true);
strictEqual(status.getResultUri("existing_uri2"), "uri2");
status.markPending("existing_uri");
strictEqual(status.hasBlobUri("existing_uri"), true);
status.removeFailed("existing_uri");
strictEqual(status.hasBlobUri("existing_uri"), false);
});
});

View File

@ -16,9 +16,10 @@
}
test("textbox text, size chars: 5", function() {
var textBox = createTextBox({text: 'X', size: 5});
var textBox1 = createTextBox({text: 'X', size: 5});
var textBox2 = createTextBox({text: 'X', size: 6});
Utils.nearlyEqualRects(Utils.size(textBox), [69, 30], 30);
ok(Utils.size(textBox1)[0] < Utils.size(textBox2)[0]);
});
test("textbox text, size 100x100", function() {

View File

@ -245,10 +245,10 @@ if (tinymce.isWebKit) {
});
test('Type over bold text in fully selected block and keep bold', function() {
editor.getBody().innerHTML = '<p><i><b>x</b></i></p>';
editor.getBody().innerHTML = '<p><i><b>x</b></i></p><p>y</p>';
Utils.setSelection('b', 0, 'b', 1);
editor.fire("keypress", {keyCode: 65, charCode: 65});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><i><b>a</b></i></p>');
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><i><b>a</b></i></p><p>y</p>');
equal(editor.selection.getStart().nodeName, 'B');
});
@ -309,6 +309,31 @@ if (tinymce.isWebKit) {
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p>a</p><p><b><i><br></i></b></p>');
equal(editor.selection.getStart(true).nodeName, 'I');
});
test('Type over all contents', function() {
editor.getBody().innerHTML = '<p>abc</p>';
Utils.setSelection('p', 0, 'p', 3);
editor.fire('keypress', {charCode: 97});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p>a</p>');
equal(editor.selection.getRng().startContainer.data, 'a');
equal(editor.selection.getRng().startOffset, 1);
});
test('ForwardDelete all contents', function() {
editor.getBody().innerHTML = '<p>abc</p>';
Utils.setSelection('p', 0, 'p', 3);
editor.fire('keydown', {keyCode: 46});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><br data-mce-bogus="1"></p>');
equal(editor.selection.getStart(true).nodeName, 'P');
});
test('Delete all contents', function() {
editor.getBody().innerHTML = '<p>abc</p>';
Utils.setSelection('p', 0, 'p', 3);
editor.fire('keydown', {keyCode: 8});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><br data-mce-bogus="1"></p>');
equal(editor.selection.getStart(true).nodeName, 'P');
});
} else {
test("Skipped since the browser isn't WebKit", function() {
ok(true, "Skipped");

View File

@ -102,3 +102,17 @@ test('isSameOrigin', function() {
ok(new tinymce.util.URI('https://www.site.com:8080').isSameOrigin(new tinymce.util.URI('https://www.site.com')) == false);
ok(new tinymce.util.URI('ftp://www.site.com:1021').isSameOrigin(new tinymce.util.URI('ftp://www.site.com')) == false);
});
test('getDocumentBaseUrl', function() {
var getDocumentBaseUrl = tinymce.util.URI.getDocumentBaseUrl;
equal(getDocumentBaseUrl({protocol: 'file:', host: '', pathname: '/dir/path1/path2'}), 'file:///dir/path1/');
equal(getDocumentBaseUrl({protocol: 'http:', host: 'localhost', pathname: '/dir/path1/path2'}), 'http://localhost/dir/path1/');
equal(getDocumentBaseUrl({protocol: 'https:', host: 'localhost', pathname: '/dir/path1/path2'}), 'https://localhost/dir/path1/');
equal(getDocumentBaseUrl({protocol: 'https:', host: 'localhost', pathname: '/dir/path1/path2/'}), 'https://localhost/dir/path1/path2/');
equal(getDocumentBaseUrl({protocol: 'http:', host: 'localhost:8080', pathname: '/dir/path1/path2'}), 'http://localhost:8080/dir/path1/');
equal(getDocumentBaseUrl({protocol: 'http:', host: 'localhost', pathname: '/dir/path1/path2/file.html'}), 'http://localhost/dir/path1/path2/');
equal(getDocumentBaseUrl({protocol: 'http:', host: 'localhost', pathname: '/'}), 'http://localhost/');
equal(getDocumentBaseUrl({protocol: 'applewebdata:', href: 'applewebdata://something//dir/path1#hash'}), 'applewebdata://something//dir/');
equal(getDocumentBaseUrl({protocol: 'applewebdata:', href: 'applewebdata://something//dir/path1'}), 'applewebdata://something//dir/');
});