Fixes #34331.

git-svn-id: https://develop.svn.wordpress.org/trunk@35306 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2015-10-20 22:05:31 +00:00
parent 261aa51182
commit 73d755b67a
22 changed files with 383 additions and 143 deletions

View File

@ -218,6 +218,7 @@ tinymce.PluginManager.add('image', function(editor) {
dom.setAttrib(imgElm, 'id', null);
} else {
dom.setAttribs(imgElm, data);
editor.editorUpload.uploadImagesAuto();
}
waitLoad(imgElm);

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,16 @@ tinymce.PluginManager.add('lists', function(editor) {
editor.on('init', function() {
var dom = editor.dom, selection = editor.selection;
function isEmpty(elm, keepBookmarks) {
var empty = dom.isEmpty(elm);
if (keepBookmarks && dom.select('span[data-mce-type=bookmark]').length > 0) {
return false;
}
return empty;
}
/**
* Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with
* index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans
@ -237,13 +247,13 @@ tinymce.PluginManager.add('lists', function(editor) {
dom.insertAfter(newBlock, ul);
if (dom.isEmpty(li.parentNode)) {
if (isEmpty(li.parentNode)) {
removeAndKeepBookmarks(li.parentNode);
}
dom.remove(li);
if (dom.isEmpty(ul)) {
if (isEmpty(ul)) {
dom.remove(ul);
}
}
@ -283,7 +293,7 @@ tinymce.PluginManager.add('lists', function(editor) {
if (sibling && sibling.nodeName == 'LI') {
sibling.appendChild(ul);
if (dom.isEmpty(parentNode)) {
if (isEmpty(parentNode)) {
dom.remove(parentNode);
}
}
@ -303,7 +313,7 @@ tinymce.PluginManager.add('lists', function(editor) {
var ul = li.parentNode, ulParent = ul.parentNode, newBlock;
function removeEmptyLi(li) {
if (dom.isEmpty(li)) {
if (isEmpty(li)) {
dom.remove(li);
}
}
@ -592,7 +602,7 @@ tinymce.PluginManager.add('lists', function(editor) {
tinymce.each(getSelectedListItems(), function(li) {
var node, rootList;
if (dom.isEmpty(li)) {
if (isEmpty(li)) {
outdent(li);
return;
}
@ -672,11 +682,11 @@ tinymce.PluginManager.add('lists', function(editor) {
dom.remove(node);
}
if (dom.isEmpty(toElm)) {
if (isEmpty(toElm, true)) {
dom.$(toElm).empty();
}
if (!dom.isEmpty(fromElm)) {
if (!isEmpty(fromElm, true)) {
while ((node = fromElm.firstChild)) {
toElm.appendChild(node);
}
@ -688,7 +698,7 @@ tinymce.PluginManager.add('lists', function(editor) {
dom.remove(fromElm);
if (dom.isEmpty(ul)) {
if (isEmpty(ul)) {
dom.remove(ul);
}
}

File diff suppressed because one or more lines are too long

View File

@ -789,5 +789,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
prependToContext: true
});
editor.addCommand('mceMedia', showDialog);
this.showDialog = showDialog;
});

File diff suppressed because one or more lines are too long

View File

@ -512,6 +512,56 @@ define("tinymce/pasteplugin/Clipboard", [
return html;
}
/**
* Some Windows 10/Edge versions will return a double encoded string. This checks if the
* content has this odd encoding and decodes it.
*/
function decodeEdgeData(data) {
var i, out, fingerprint, code;
// Check if data is encoded
fingerprint = [25942, 29554, 28521, 14958];
for (i = 0; i < fingerprint.length; i++) {
if (data.charCodeAt(i) != fingerprint[i]) {
return data;
}
}
// Decode UTF-16 to UTF-8
out = '';
for (i = 0; i < data.length; i++) {
code = data.charCodeAt(i);
/*eslint no-bitwise:0*/
out += String.fromCharCode((code & 0x00FF));
out += String.fromCharCode((code & 0xFF00) >> 8);
}
// Decode UTF-8
return decodeURIComponent(escape(out));
}
/**
* Extracts HTML contents from within a fragment.
*/
function extractFragment(data) {
var idx, startFragment, endFragment;
startFragment = '<!--StartFragment-->';
idx = data.indexOf(startFragment);
if (idx !== -1) {
data = data.substr(idx + startFragment.length);
}
endFragment = '<!--EndFragment-->';
idx = data.indexOf(endFragment);
if (idx !== -1) {
data = data.substr(0, idx);
}
return data;
}
/**
* Gets various content types out of a datatransfer object.
*
@ -519,7 +569,7 @@ define("tinymce/pasteplugin/Clipboard", [
* @return {Object} Object with mime types and data for those mime types.
*/
function getDataTransferItems(dataTransfer) {
var data = {};
var items = {};
if (dataTransfer) {
// Use old WebKit/IE API
@ -527,20 +577,26 @@ define("tinymce/pasteplugin/Clipboard", [
var legacyText = dataTransfer.getData('Text');
if (legacyText && legacyText.length > 0) {
if (legacyText.indexOf(mceInternalUrlPrefix) == -1) {
data['text/plain'] = legacyText;
items['text/plain'] = legacyText;
}
}
}
if (dataTransfer.types) {
for (var i = 0; i < dataTransfer.types.length; i++) {
var contentType = dataTransfer.types[i];
data[contentType] = dataTransfer.getData(contentType);
var contentType = dataTransfer.types[i],
data = dataTransfer.getData(contentType);
if (contentType == 'text/html') {
data = extractFragment(decodeEdgeData(data));
}
items[contentType] = data;
}
}
}
return data;
return items;
}
/**

File diff suppressed because one or more lines are too long

View File

@ -552,7 +552,7 @@ tinymce.ThemeManager.add('modern', function(editor) {
return null;
}
editor.on('click keyup blur', function() {
editor.on('click keyup', function() {
// Needs to be delayed to avoid Chrome img focus out bug
window.setTimeout(function() {
var match;
@ -570,6 +570,8 @@ tinymce.ThemeManager.add('modern', function(editor) {
}, 0);
});
editor.on('blur hide', hideAllContextToolbars);
editor.on('ObjectResizeStart', function() {
var match = findFrontMostMatch(editor.selection.getNode());

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// 4.2.5 (2015-08-31)
// 4.2.6 (2015-09-28)
/**
* Compiled inline version. (Library mode)
@ -3489,6 +3489,7 @@ define("tinymce/dom/DomQuery", [
var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
var Event = EventUtils.Event, undef;
var skipUniques = Tools.makeMap('children,contents,next,prev');
function isDefined(obj) {
return typeof obj !== 'undefined';
@ -4817,7 +4818,9 @@ define("tinymce/dom/DomQuery", [
// If traversing on multiple elements we might get the same elements twice
if (this.length > 1) {
result = DomQuery.unique(result);
if (!skipUniques[name]) {
result = DomQuery.unique(result);
}
if (name.indexOf('parents') === 0) {
result = result.reverse();
@ -6819,6 +6822,18 @@ define("tinymce/dom/DOMUtils", [
return attrHooks;
}
function updateInternalStyleAttr(domUtils, $elm) {
var value = $elm.attr('style');
value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
if (!value) {
value = null;
}
$elm.attr('data-mce-style', value);
}
/**
* Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
*
@ -7359,7 +7374,7 @@ define("tinymce/dom/DOMUtils", [
* or the CSS style name like background-color.
*
* @method setStyle
* @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
* @param {String/Element/Array} n HTML element/Array of elements to set CSS style value on.
* @param {String} na Name of the style value to set.
* @param {String} v Value to set on the style.
* @example
@ -7373,7 +7388,7 @@ define("tinymce/dom/DOMUtils", [
elm = this.$$(elm).css(name, value);
if (this.settings.update_styles) {
elm.attr('data-mce-style', null);
updateInternalStyleAttr(this, elm);
}
},
@ -7422,7 +7437,7 @@ define("tinymce/dom/DOMUtils", [
elm = this.$$(elm).css(styles);
if (this.settings.update_styles) {
elm.attr('data-mce-style', null);
updateInternalStyleAttr(this, elm);
}
},
@ -12993,6 +13008,33 @@ define("tinymce/dom/Serializer", [
var each = Tools.each, trim = Tools.trim;
var DOM = DOMUtils.DOM;
/**
* IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
* the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
* with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
* but not as the lastChild of the body. So this fix simply removes the last two
* BR elements at the end of the document.
*
* Example of what happens: <body>text</body> becomes <body>text<br><br></body>
*/
function trimTrailingBr(rootNode) {
var brNode1, brNode2;
function isBr(node) {
return node && node.name === 'br';
}
brNode1 = rootNode.lastChild;
if (isBr(brNode1)) {
brNode2 = brNode1.prev;
if (isBr(brNode2)) {
brNode1.remove();
brNode2.remove();
}
}
}
/**
* Constructs a new DOM serializer class.
*
@ -13250,7 +13292,7 @@ define("tinymce/dom/Serializer", [
* @param {Object} args Arguments option that gets passed to event handlers.
*/
serialize: function(node, args) {
var self = this, impl, doc, oldDoc, htmlSerializer, content;
var self = this, impl, doc, oldDoc, htmlSerializer, content, rootNode;
// Explorer won't clone contents of script and style and the
// selected index of select elements are cleared on a clone operation.
@ -13300,13 +13342,13 @@ define("tinymce/dom/Serializer", [
self.onPreProcess(args);
}
// Setup serializer
htmlSerializer = new Serializer(settings, schema);
// Parse HTML
rootNode = htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args);
trimTrailingBr(rootNode);
// Parse and serialize HTML
args.content = htmlSerializer.serialize(
htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
);
// Serialize HTML
htmlSerializer = new Serializer(settings, schema);
args.content = htmlSerializer.serialize(rootNode);
// Replace all BOM characters for now until we can find a better solution
if (!args.cleanup) {
@ -14495,7 +14537,7 @@ define("tinymce/dom/ControlSelection", [
}
});
editor.on('hide', hideResizeRect);
editor.on('hide blur', hideResizeRect);
// Hide rect on focusout since it would float on top of windows otherwise
//editor.on('focusout', hideResizeRect);
@ -18640,7 +18682,7 @@ define("tinymce/UndoManager", [
}
});
editor.on('ObjectResizeStart', function() {
editor.on('ObjectResizeStart Cut', function() {
self.beforeChange();
});
@ -18650,6 +18692,12 @@ define("tinymce/UndoManager", [
editor.on('KeyUp', function(e) {
var keyCode = e.keyCode;
// If key is prevented then don't add undo level
// This would happen on keyboard shortcuts for example
if (e.isDefaultPrevented()) {
return;
}
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
addNonTypingUndoLevel();
editor.nodeChanged();
@ -18680,6 +18728,12 @@ define("tinymce/UndoManager", [
editor.on('KeyDown', function(e) {
var keyCode = e.keyCode;
// If key is prevented then don't add undo level
// This would happen on keyboard shortcuts for example
if (e.isDefaultPrevented()) {
return;
}
// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
if (self.typing) {
@ -19114,6 +19168,11 @@ define("tinymce/EnterKey", [
}
}
function emptyBlock(elm) {
// BR is needed in empty blocks on non IE browsers
elm.innerHTML = !isIE ? '<br data-mce-bogus="1">' : '';
}
// Creates a new block element by cloning the current one or creating a new one if the name is specified
// This function will also copy any text formatting from the parent block and add it to the new one
function createNewBlock(name) {
@ -19549,6 +19608,10 @@ define("tinymce/EnterKey", [
trimInlineElementsOnLeftSideOfBlock(newBlock);
addBrToBlockIfNeeded(parentBlock);
if (dom.isEmpty(parentBlock)) {
emptyBlock(parentBlock);
}
// New block might become empty if it's <p><b>a |</b></p>
if (dom.isEmpty(newBlock)) {
dom.remove(newBlock);
@ -28065,6 +28128,11 @@ define("tinymce/util/Quirks", [
if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
// Shift+Delete is cut
if (isForward && e.shiftKey) {
return;
}
// Ignore non meta delete in the where there is text before/after the caret
if (!isMetaOrCtrl && rng.collapsed && container.nodeType == 3) {
if (isForward ? offset < container.data.length : offset > 0) {
@ -28918,8 +28986,15 @@ define("tinymce/util/Quirks", [
if (!editor.inline) {
editor.contentStyles.push('body {min-height: 150px}');
editor.on('click', function(e) {
var rng;
if (e.target.nodeName == 'HTML') {
var rng;
// Edge seems to only need focus if we set the range
// the caret will become invisible and moved out of the iframe!!
if (Env.ie > 11) {
editor.getBody().focus();
return;
}
// Need to store away non collapsed ranges since the focus call will mess that up see #7382
rng = editor.selection.getRng();
@ -28954,25 +29029,6 @@ define("tinymce/util/Quirks", [
setEditorCommandState("AutoUrlDetect", false);
}
/**
* IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
* the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
* with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
* but not as the lastChild of the body. However is we add a BR element to the body then remove it
* it doesn't seem to add these BR elements makes sence right?!
*
* Example of what happens: <body>text</body> becomes <body>text<br><br></body>
*/
function doubleTrailingBrElements() {
if (!editor.inline) {
editor.on('focus blur beforegetcontent', function() {
var br = editor.dom.create('br');
editor.getBody().appendChild(br);
br.parentNode.removeChild(br);
}, true);
}
}
/**
* iOS 7.1 introduced two new bugs:
* 1) It's possible to open links within a contentEditable area by clicking on them.
@ -29146,7 +29202,6 @@ define("tinymce/util/Quirks", [
if (Env.ie >= 11) {
bodyHeight();
doubleTrailingBrElements();
disableBackspaceIntoATable();
}
@ -30299,6 +30354,16 @@ define("tinymce/EditorUpload", [
return function(editor) {
var blobCache = new BlobCache(), uploader, imageScanner;
function aliveGuard(callback) {
return function(result) {
if (editor.selection) {
return callback(result);
}
return [];
};
}
// Replaces strings without regexps to avoid FF regexp to big issue
function replaceString(content, search, replace) {
var index = 0;
@ -30338,14 +30403,14 @@ define("tinymce/EditorUpload", [
});
}
return scanForImages().then(function(imageInfos) {
return scanForImages().then(aliveGuard(function(imageInfos) {
var blobInfos;
blobInfos = Arr.map(imageInfos, function(imageInfo) {
return imageInfo.blobInfo;
});
return uploader.upload(blobInfos).then(function(result) {
return uploader.upload(blobInfos).then(aliveGuard(function(result) {
result = Arr.map(result, function(uploadInfo, index) {
var image = imageInfos[index].image;
@ -30367,8 +30432,14 @@ define("tinymce/EditorUpload", [
}
return result;
});
});
}));
}));
}
function uploadImagesAuto(callback) {
if (editor.settings.automatic_uploads !== false) {
return uploadImages(callback);
}
}
function scanForImages() {
@ -30376,14 +30447,14 @@ define("tinymce/EditorUpload", [
imageScanner = new ImageScanner(blobCache);
}
return imageScanner.findAll(editor.getBody()).then(function(result) {
return imageScanner.findAll(editor.getBody()).then(aliveGuard(function(result) {
Arr.each(result, function(resultItem) {
replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());
resultItem.image.src = resultItem.blobInfo.blobUri();
});
return result;
});
}));
}
function destroy() {
@ -30405,11 +30476,17 @@ define("tinymce/EditorUpload", [
return 'src="data:' + blobInfo.blob().type + ';base64,' + blobInfo.base64() + '"';
}
return match[0];
return match;
});
}
editor.on('setContent paste', scanForImages);
editor.on('setContent', function() {
if (editor.settings.automatic_uploads !== false) {
uploadImagesAuto();
} else {
scanForImages();
}
});
editor.on('RawSaveContent', function(e) {
e.content = replaceBlobWithBase64(e.content);
@ -30426,6 +30503,7 @@ define("tinymce/EditorUpload", [
return {
blobCache: blobCache,
uploadImages: uploadImages,
uploadImagesAuto: uploadImagesAuto,
scanForImages: scanForImages,
destroy: destroy
};
@ -31096,7 +31174,10 @@ define("tinymce/Editor", [
// Domain relaxing is required since the user has messed around with document.domain
if (document.domain != location.hostname) {
url = domainRelaxUrl;
// Edge seems to be able to handle domain relaxing
if (Env.ie && Env.ie < 12) {
url = domainRelaxUrl;
}
}
// Create iframe
@ -33065,7 +33146,7 @@ define("tinymce/EditorManager", [
* @property minorVersion
* @type String
*/
minorVersion: '2.5',
minorVersion: '2.6',
/**
* Release date of TinyMCE build.
@ -33073,7 +33154,7 @@ define("tinymce/EditorManager", [
* @property releaseDate
* @type String
*/
releaseDate: '2015-08-31',
releaseDate: '2015-09-28',
/**
* Collection of editor instances.
@ -33798,6 +33879,12 @@ define("tinymce/util/XHR", [
xhr.setRequestHeader('Content-Type', settings.content_type);
}
if (settings.requestheaders) {
Tools.each(settings.requestheaders, function(header) {
xhr.setRequestHeader(header.key, header.value);
});
}
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ $wp_db_version = 34978;
*
* @global string $tinymce_version
*/
$tinymce_version = '4205-20150908';
$tinymce_version = '4206-20151020';
/**
* Holds the required PHP version

View File

@ -17,8 +17,7 @@
<div id="qunit-fixture"></div>
<div id="view" style="position: absolute; right: 0; top: 0"></div>
<script src="http://www.google.com/jsapi"></script>
<script>google.load("jquery", "1");</script>
<script src="../../../src/wp-includes/js/jquery/jquery.js"></script>
<script src="js/module_loader.js"></script>
<script src="js/qunit/qunit.js"></script>
<script src="../../../build/wp-includes/js/tinymce/tinymce.min.js"></script>

View File

@ -161,7 +161,7 @@
test("Image recognizes relative src url and prepends absolute image_prepend_url setting.", function () {
var win, elementId, element;
editor.settings.image_prepend_url = 'http://testing.com/images/';
editor.settings.image_prepend_url = 'http://localhost/images/';
editor.setContent('');
editor.execCommand('mceImage', true);

View File

@ -891,7 +891,7 @@ test('Remove indented list with multiple items', function() {
'<li>a</li>' +
'</ul>' +
'<p>b</p>' +
'<p>c</p>' +
'<p>c</p>' +
'<ul>' +
'<li>d</li>' +
'</ul>'
@ -1738,6 +1738,29 @@ test('Backspace at beginning of LI with BR padded empty LI above in UL', functio
equal(editor.selection.getNode().innerHTML, 'b');
});
test('Backspace at empty LI (IE)', function() {
editor.getBody().innerHTML = (
'<ul>' +
'<li>a</li>' +
'<li></li>' +
'<li>b</li>' +
'</ul>'
);
editor.focus();
Utils.setSelection('li:nth-child(2)', 0);
editor.plugins.lists.backspaceDelete();
equal(editor.getContent(),
'<ul>' +
'<li>a</li>' +
'<li>b</li>' +
'</ul>'
);
equal(editor.selection.getNode().innerHTML, 'a');
});
test('Backspace at beginning of LI with empty LI with STRING and BR above in UL', function() {
editor.getBody().innerHTML = (
'<ul>' +

View File

@ -351,7 +351,7 @@ test('paste nested (UL) word list', function() {
equal(
editor.getContent(),
'<ul>'+
'<ul>' +
'<li>a' +
'<ul>' +
'<li>b' +
@ -391,7 +391,7 @@ test('paste nested (OL) word list', function() {
equal(
editor.getContent(),
'<ol>'+
'<ol>' +
'<li>a' +
'<ol>' +
'<li>b' +
@ -535,10 +535,10 @@ test('paste plain text with paragraphs', function() {
test('paste data image with paste_data_images: false', function() {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {content: '<img src="data:image/png;base64,...">'});
editor.execCommand('mceInsertClipboardContent', false, {content: '<img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">'});
equal(editor.getContent(), '');
editor.execCommand('mceInsertClipboardContent', false, {content: '<img alt="alt" src="data:image/png;base64,...">'});
editor.execCommand('mceInsertClipboardContent', false, {content: '<img alt="alt" src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">'});
equal(editor.getContent(), '');
});
@ -546,9 +546,9 @@ test('paste data image with paste_data_images: true', function() {
editor.settings.paste_data_images = true;
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {content: '<img src="data:image/png;base64,...">'});
editor.execCommand('mceInsertClipboardContent', false, {content: '<img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">'});
equal(editor.getContent(), '<p><img src="data:image/png;base64,..." alt="" /></p>');
equal(editor.getContent(), '<p><img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="" /></p>');
});
test('paste pre process text (event)', function() {

View File

@ -2,7 +2,7 @@ ModuleLoader.require([
"tinymce/file/Conversions",
"tinymce/Env"
], function(Conversions, Env) {
var testBlob, testBlobDataUri;
var testBlobDataUri;
if (!tinymce.Env.fileApi) {
return;
@ -19,6 +19,7 @@ ModuleLoader.require([
skin: false,
entities: 'raw',
indent: false,
automatic_uploads: false,
init_instance_callback: function(ed) {
var canvas, context;
@ -40,8 +41,7 @@ ModuleLoader.require([
testBlobDataUri = canvas.toDataURL();
Conversions.uriToBlob(testBlobDataUri).then(function(blob) {
testBlob = blob;
Conversions.uriToBlob(testBlobDataUri).then(function() {
QUnit.start();
});
}
@ -50,6 +50,7 @@ ModuleLoader.require([
teardown: function() {
editor.editorUpload.destroy();
editor.settings.automatic_uploads = false;
}
});
@ -133,6 +134,7 @@ ModuleLoader.require([
editor.settings.images_upload_handler = function(data, success) {
uploadCount++;
success();
};
editor.uploadImages(done);
@ -150,8 +152,14 @@ ModuleLoader.require([
editor.settings.images_upload_handler = function(data, success) {
uploadCount++;
success();
};
editor.uploadImages(done);
});
test('Retain blobs not in blob cache', function() {
editor.getBody().innerHTML = '<img src="blob:http%3A//host/f8d1e462-8646-485f-87c5-f9bcee5873c6">';
QUnit.equal('<p><img src="blob:http%3A//host/f8d1e462-8646-485f-87c5-f9bcee5873c6" alt="" /></p>', editor.getContent());
});
});

View File

@ -64,6 +64,14 @@ test('Enter before first IMG in P', function() {
equal(editor.getContent(), '<p>\u00a0</p><p><img src="about:blank" alt="" /></p>');
});
test('Enter before first wrapped IMG in P', function() {
editor.setContent('<p><b><img alt="" src="about:blank" /></b></p>');
editor.selection.setCursorLocation(editor.getBody().firstChild.firstChild, 0);
Utils.pressEnter();
equal(editor.getBody().firstChild.innerHTML, (tinymce.isIE && tinymce.Env.ie < 11) ? '' : '<br data-mce-bogus="1">');
equal(editor.getContent(), '<p>\u00a0</p><p><b><img src=\"about:blank\" alt=\"\" /></b></p>');
});
test('Enter before last IMG in P with text', function() {
editor.setContent('<p>abc<img alt="" src="about:blank" /></p>');
editor.selection.setCursorLocation(editor.getBody().firstChild, 1);

View File

@ -87,11 +87,11 @@ test('One undo levels and one redo', function() {
});
test('Typing state', function() {
var selectAllFlags;
editor.undoManager.clear();
editor.setContent('test');
expect(4);
ok(!editor.undoManager.typing);
editor.dom.fire(editor.getBody(), 'keydown', {keyCode: 65});
@ -163,6 +163,36 @@ test('Events', function() {
ok(redo.bookmark);
});
test('No undo/redo cmds on Undo/Redo shortcut', function() {
var evt, commands = [], added = false;
editor.undoManager.clear();
editor.setContent('test');
editor.on('BeforeExecCommand', function(e) {
commands.push(e.command);
});
editor.on('BeforeAddUndo', function() {
added = true;
});
evt = {
keyCode: 90,
metaKey: tinymce.Env.mac,
ctrlKey: !tinymce.Env.mac,
shiftKey: false,
altKey: false
};
editor.dom.fire(editor.getBody(), 'keydown', evt);
editor.dom.fire(editor.getBody(), 'keypress', evt);
editor.dom.fire(editor.getBody(), 'keyup', evt);
strictEqual(added, false);
deepEqual(commands, ["Undo"]);
});
test('Transact', function() {
var count = 0;

View File

@ -79,8 +79,6 @@
});
test(prefix + 'Constructor selector and context', function() {
var $selector;
$('#view').html('<div><b>a</b></div><div><b>b</b></div>');
$('b', $('#view div')[0]).html('x');
equal($('#view').html().toLowerCase().replace(/[\r\n]/g, ''), '<div><b>x</b></div><div><b>b</b></div>');
@ -141,7 +139,9 @@
});
test(prefix + 'static grep()', function() {
deepEqual($.grep([1, 2, 3], function(v) {return v > 1;}), [2, 3]);
deepEqual($.grep([1, 2, 3], function(v) {
return v > 1;
}), [2, 3]);
});
test(prefix + 'static isArray()', function() {
@ -578,9 +578,9 @@
test(prefix + 'filter()', function() {
strictEqual($('<b></b><i></i><u></u>').filter('b,i').length, 2);
strictEqual($('<b></b><i></i><u></u>').filter(function(i, elm) {
return elm.tagName != 'U';
return elm.tagName != 'U';
}).length, 2);
strictEqual($('<b></b><i></i><u></u>').filter(function(i, elm) {
strictEqual($('<b></b><i></i><u></u>').filter(function(i) {
return i != 2;
}).length, 2);
strictEqual($([document, window, document.createTextNode('x')]).filter('*').length, 0);
@ -610,7 +610,7 @@
test(prefix + 'on()/off()/trigger()', function() {
var lastArgs1, lastArgs2;
$elm = $('<b />')
$elm = $('<b />');
// Single listener
$elm.on('click', function(e) {
@ -660,8 +660,8 @@
});
test(prefix + 'show()/hide() element', function() {
equal(normalizeStyleValue($('<b></b>').hide().attr('style')), 'display: none');
ok(!$('<b></b>').show().attr('style'));
equal(normalizeStyleValue($('<b></b>').appendTo('#view').hide().attr('style')), 'display: none');
equal(normalizeStyleValue($('<b></b>').empty().appendTo('#view').show().attr('style')), undefined);
});
test(prefix + 'slice/eq/first/last() on collection', function() {
@ -798,9 +798,9 @@
});
test(prefix + 'parents()', function() {
var $result, html;
var $result;
html = $('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$result = splitAtView($('#view i, #view b').parents());
strictEqual($result.length, 3);
@ -817,9 +817,9 @@
});
test(prefix + 'parentsUntil(selector)', function() {
var $result, html;
var $result;
html = $('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$result = $('#view i, #view b').parentsUntil('#view');
strictEqual($result.length, 3);
@ -833,9 +833,9 @@
});
test(prefix + 'parentsUntil(element)', function() {
var $result, html;
var $result;
html = $('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$result = $('#view i, #view b').parentsUntil(document.getElementById('view'));
strictEqual($result.length, 3);
@ -849,9 +849,9 @@
});
test(prefix + 'parentsUntil(query)', function() {
var $result, html;
var $result;
html = $('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$('<div><em><i>1</i></em><strong><b>2</b></strong></div>').appendTo('#view');
$result = $('#view i, #view b').parentsUntil($('#view'));
strictEqual($result.length, 3);
@ -870,7 +870,7 @@
html = $('<b>1</b>2<i>3</i>');
$result = html.next();
strictEqual($result.length, 1);
strictEqual($result.length, 2);
strictEqual($result[0].tagName, 'I');
});
@ -880,7 +880,7 @@
html = $('<b>1</b>2<i>3</i>');
$result = $(html).prev();
strictEqual($result.length, 1);
strictEqual($result.length, 2);
strictEqual($result[0].tagName, 'B');
});
@ -1028,5 +1028,7 @@
// Run tests against jQuery/DomQuery so we know that we are compatible
addTests('DomQuery: ', tinymce.dom.DomQuery);
/*global jQuery*/
addTests('jQuery: ', jQuery);
})();

View File

@ -268,128 +268,128 @@ test('Script with non JS type attribute', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<s'+'cript type="mylanguage"></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript type="mylanguage"></s'+'cript>');
DOM.setHTML('test', '<s' + 'cript type="mylanguage"></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript type="mylanguage"></s' + 'cript>');
});
test('Script with tags inside a comment', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<s'+'cript>// <img src="test"><a href="#"></a></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n// <img src="test"><a href="#"></a>\n// ]]></s'+'cript>');
DOM.setHTML('test', '<s' + 'cript>// <img src="test"><a href="#"></a></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n// <img src="test"><a href="#"></a>\n// ]]></s' + 'cript>');
});
test('Script with less than', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<s'+'cript>1 < 2;</s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<s' + 'cript>1 < 2;</s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with type attrib and less than', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<s'+'cript type="text/javascript">1 < 2;</s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<script type="text/javascript">// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<s' + 'cript type="text/javascript">1 < 2;</s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<script type="text/javascript">// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with whitespace in beginning/end', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>\n\t1 < 2;\n\t if (2 < 1)\n\t\talert(1);\n</s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n\t1 < 2;\n\t if (2 < 1)\n\t\talert(1);\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>\n\t1 < 2;\n\t if (2 < 1)\n\t\talert(1);\n</s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n\t1 < 2;\n\t if (2 < 1)\n\t\talert(1);\n// ]]></s' + 'cript>');
});
test('Script with a HTML comment and less than', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script><!-- 1 < 2; // --></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script><!-- 1 < 2; // --></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with white space in beginning, comment and less than', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>\n\n<!-- 1 < 2;\n\n--></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>\n\n<!-- 1 < 2;\n\n--></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with comments and cdata', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>// <![CDATA[1 < 2; // ]]></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>// <![CDATA[1 < 2; // ]]></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with cdata', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script><![CDATA[1 < 2; ]]></s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script><![CDATA[1 < 2; ]]></s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script whitespace in beginning/end and cdata', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>\n\n<![CDATA[\n\n1 < 2;\n\n]]>\n\n</s'+'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s'+'cript>// <![CDATA[\n1 < 2;\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>\n\n<![CDATA[\n\n1 < 2;\n\n]]>\n\n</s' + 'cript>');
equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '<s' + 'cript>// <![CDATA[\n1 < 2;\n// ]]></s' + 'cript>');
});
test('Script with src attr', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script src="test.js" data-mce-src="test.js"></s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<s'+'cript src="test.js"></s'+'cript>');
DOM.setHTML('test', '<script src="test.js" data-mce-src="test.js"></s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<s' + 'cript src="test.js"></s' + 'cript>');
});
test('Script with HTML comment, comment and CDATA', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script><!--// <![CDATA[var hi = "hello";// ]]>--></s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script><!--// <![CDATA[var hi = "hello";// ]]>--></s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s' + 'cript>');
});
test('Script with block comment around cdata', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>/* <![CDATA[ */\nvar hi = "hello";\n/* ]]> */</s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>/* <![CDATA[ */\nvar hi = "hello";\n/* ]]> */</s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s' + 'cript>');
});
test('Script with html comment and block comment around cdata', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script><!-- /* <![CDATA[ */\nvar hi = "hello";\n/* ]]>*/--></s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script><!-- /* <![CDATA[ */\nvar hi = "hello";\n/* ]]>*/--></s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s' + 'cript>');
});
test('Script with line comment and html comment', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>// <!--\nvar hi = "hello";\n// --></s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>// <!--\nvar hi = "hello";\n// --></s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s' + 'cript>');
});
test('Script with block comment around html comment', 1, function() {
var ser = new tinymce.dom.Serializer({fix_list_elements : true});
ser.setRules('script[type|language|src]');
DOM.setHTML('test', '<script>/* <!-- */\nvar hi = "hello";\n/*-->*/</s'+'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s'+'cript>');
DOM.setHTML('test', '<script>/* <!-- */\nvar hi = "hello";\n/*-->*/</s' + 'cript>');
equal(ser.serialize(DOM.get('test')), '<script>// <![CDATA[\nvar hi = \"hello\";\n// ]]></s' + 'cript>');
});
test('Protected blocks', function() {
@ -499,3 +499,15 @@ test('Restore tabindex', function() {
DOM.setHTML('test', '<span data-mce-tabindex="42"></span>');
equal(ser.serialize(DOM.get('test')), '<span tabindex="42"></span>');
});
test('Trailing BR (IE11)', function() {
var ser = new tinymce.dom.Serializer({
valid_elements: 'p,br'
});
DOM.setHTML('test', '<p>a</p><br><br>');
equal(ser.serialize(DOM.get('test')), '<p>a</p>');
DOM.setHTML('test', 'a<br><br>');
equal(ser.serialize(DOM.get('test')), 'a');
});