Fixes #34620.

git-svn-id: https://develop.svn.wordpress.org/trunk@35574 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2015-11-08 02:31:31 +00:00
parent 5acf79fa7e
commit 71d22131e6
14 changed files with 436 additions and 120 deletions

View File

@ -30,6 +30,10 @@ tinymce.PluginManager.add('lists', function(editor) {
return node && !!editor.schema.getTextBlockElements()[node.nodeName];
}
function isEditorBody(elm) {
return elm === editor.getBody();
}
editor.on('init', function() {
var dom = editor.dom, selection = editor.selection;
@ -318,6 +322,10 @@ tinymce.PluginManager.add('lists', function(editor) {
}
}
if (isEditorBody(ul)) {
return true;
}
if (li.nodeName == 'DD') {
dom.rename(li, 'DT');
return true;
@ -602,6 +610,10 @@ tinymce.PluginManager.add('lists', function(editor) {
tinymce.each(getSelectedListItems(), function(li) {
var node, rootList;
if (isEditorBody(li.parentNode)) {
return;
}
if (isEmpty(li)) {
outdent(li);
return;
@ -622,6 +634,10 @@ tinymce.PluginManager.add('lists', function(editor) {
function toggleList(listName) {
var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
if (isEditorBody(parentList)) {
return;
}
if (parentList) {
if (parentList.nodeName == listName) {
removeList(listName);
@ -698,17 +714,22 @@ tinymce.PluginManager.add('lists', function(editor) {
dom.remove(fromElm);
if (isEmpty(ul)) {
if (isEmpty(ul) && !isEditorBody(ul)) {
dom.remove(ul);
}
}
if (selection.isCollapsed()) {
var li = dom.getParent(selection.getStart(), 'LI');
var li = dom.getParent(selection.getStart(), 'LI'), ul, rng, otherLi;
if (li) {
var rng = selection.getRng(true);
var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
ul = li.parentNode;
if (isEditorBody(ul) && dom.isEmpty(ul)) {
return true;
}
rng = selection.getRng(true);
otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
if (otherLi && otherLi != li) {
var bookmark = createBookmark(rng);
@ -723,7 +744,7 @@ tinymce.PluginManager.add('lists', function(editor) {
return true;
} else if (!otherLi) {
if (!isForward && removeList(li.parentNode.nodeName)) {
if (!isForward && removeList(ul.nodeName)) {
return true;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1411,7 +1411,9 @@ define("tinymce/pasteplugin/WordFilter", [
}
// Serialize DOM back to HTML
e.content = new Serializer({}, schema).serialize(rootNode);
e.content = new Serializer({
validate: settings.validate
}, schema).serialize(rootNode);
}
});
}

File diff suppressed because one or more lines are too long

View File

@ -41,8 +41,8 @@ tinymce.ThemeManager.add('modern', function(editor) {
function bindSelectorChanged() {
var selection = editor.selection;
if (itemName == "bullist") {
selection.selectorChanged('ul > li', function(state, args) {
function setActiveItem(name) {
return function(state, args) {
var nodeName, i = args.parents.length;
while (i--) {
@ -52,23 +52,16 @@ tinymce.ThemeManager.add('modern', function(editor) {
}
}
item.active(state && nodeName == "UL");
});
item.active(state && nodeName == name);
};
}
if (itemName == "bullist") {
selection.selectorChanged('ul > li', setActiveItem("UL"));
}
if (itemName == "numlist") {
selection.selectorChanged('ol > li', function(state, args) {
var nodeName, i = args.parents.length;
while (i--) {
nodeName = args.parents[i].nodeName;
if (nodeName == "OL" || nodeName == "UL") {
break;
}
}
item.active(state && nodeName == "OL");
});
selection.selectorChanged('ol > li', setActiveItem("OL"));
}
if (item.settings.stateSelector) {

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// 4.2.6 (2015-09-28)
// 4.2.7 (2015-10-27)
/**
* Compiled inline version. (Library mode)
@ -6392,7 +6392,7 @@ define("tinymce/html/Entities", [
var Entities = {
/**
* Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
* Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
*
* @method encodeRaw
* @param {String} text Text to encode.
@ -6834,6 +6834,27 @@ define("tinymce/dom/DOMUtils", [
$elm.attr('data-mce-style', value);
}
function nodeIndex(node, normalized) {
var idx = 0, lastNodeType, nodeType;
if (node) {
for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
nodeType = node.nodeType;
// Normalize text nodes
if (normalized && nodeType == 3) {
if (nodeType == lastNodeType || !node.nodeValue.length) {
continue;
}
}
idx++;
lastNodeType = nodeType;
}
}
return idx;
}
/**
* Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
*
@ -8250,26 +8271,7 @@ define("tinymce/dom/DOMUtils", [
* @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
* @return {Number} Index of the specified node.
*/
nodeIndex: function(node, normalized) {
var idx = 0, lastNodeType, nodeType;
if (node) {
for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
nodeType = node.nodeType;
// Normalize text nodes
if (normalized && nodeType == 3) {
if (nodeType == lastNodeType || !node.nodeValue.length) {
continue;
}
}
idx++;
lastNodeType = nodeType;
}
}
return idx;
},
nodeIndex: nodeIndex,
/**
* Splits an element into two new elements and places the specified split
@ -8578,6 +8580,7 @@ define("tinymce/dom/DOMUtils", [
* tinymce.DOM.addClass('someid', 'someclass');
*/
DOMUtils.DOM = new DOMUtils(document);
DOMUtils.nodeIndex = nodeIndex;
return DOMUtils;
});
@ -9381,6 +9384,10 @@ define("tinymce/dom/RangeUtils", [
var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
var directionLeft, isAfterNode;
function isTableCell(node) {
return node && /^(TD|TH|CAPTION)$/.test(node.nodeName);
}
function hasBrBeforeAfter(node, left) {
var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
@ -9494,7 +9501,7 @@ define("tinymce/dom/RangeUtils", [
}
// Found a BR/IMG element that we can place the caret before
if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCell(node)) {
offset = dom.nodeIndex(node);
container = node.parentNode;
@ -12926,27 +12933,29 @@ define("tinymce/html/Serializer", [
sortedAttrs.map = {};
elementRule = schema.getElementRule(node.name);
for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
attrName = elementRule.attributesOrder[i];
if (elementRule) {
for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
attrName = elementRule.attributesOrder[i];
if (attrName in attrs.map) {
attrValue = attrs.map[attrName];
sortedAttrs.map[attrName] = attrValue;
sortedAttrs.push({name: attrName, value: attrValue});
if (attrName in attrs.map) {
attrValue = attrs.map[attrName];
sortedAttrs.map[attrName] = attrValue;
sortedAttrs.push({name: attrName, value: attrValue});
}
}
}
for (i = 0, l = attrs.length; i < l; i++) {
attrName = attrs[i].name;
for (i = 0, l = attrs.length; i < l; i++) {
attrName = attrs[i].name;
if (!(attrName in sortedAttrs.map)) {
attrValue = attrs.map[attrName];
sortedAttrs.map[attrName] = attrValue;
sortedAttrs.push({name: attrName, value: attrValue});
if (!(attrName in sortedAttrs.map)) {
attrValue = attrs.map[attrName];
sortedAttrs.map[attrName] = attrValue;
sortedAttrs.push({name: attrName, value: attrValue});
}
}
}
attrs = sortedAttrs;
attrs = sortedAttrs;
}
}
writer.start(node.name, attrs, isEmpty);
@ -14061,6 +14070,10 @@ define("tinymce/dom/ControlSelection", [
return false;
}
if (elm == editor.getBody()) {
return false;
}
return editor.dom.is(elm, selector);
}
@ -18502,9 +18515,13 @@ define("tinymce/Formatter", [
removeCaretContainer();
// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
// Backspace key needs to check if the range is collapsed due to bug #6780
if ((keyCode == 8 && selection.isCollapsed()) || keyCode == 37 || keyCode == 39) {
// Remove caret container if it's empty
if (keyCode == 8 && selection.isCollapsed() && selection.getStart().innerHTML == INVISIBLE_CHAR) {
removeCaretContainer(getParentCaretContainer(selection.getStart()));
}
// Remove caret container on keydown and it's left/right arrow keys
if (keyCode == 37 || keyCode == 39) {
removeCaretContainer(getParentCaretContainer(selection.getStart()));
}
@ -19018,6 +19035,10 @@ define("tinymce/EnterKey", [
dom.getContentEditable(node) !== "true";
}
function isTableCell(node) {
return node && /^(TD|TH|CAPTION)$/.test(node.nodeName);
}
// Renders empty block on IE
function renderBlockOnIE(block) {
var oldRng;
@ -19284,10 +19305,15 @@ define("tinymce/EnterKey", [
// Not in a block element or in a table cell or caption
parentBlock = dom.getParent(container, dom.isBlock);
rootBlockName = editor.getBody().nodeName.toLowerCase();
if (!parentBlock || !canSplitBlock(parentBlock)) {
parentBlock = parentBlock || editableRoot;
if (parentBlock == editor.getBody() || isTableCell(parentBlock)) {
rootBlockName = parentBlock.nodeName.toLowerCase();
} else {
rootBlockName = parentBlock.parentNode.nodeName.toLowerCase();
}
if (!parentBlock.hasChildNodes()) {
newBlock = dom.create(blockName);
setForcedBlockAttrs(newBlock);
@ -19358,6 +19384,10 @@ define("tinymce/EnterKey", [
return containerBlock;
}
if (containerBlock == editor.getBody()) {
return;
}
// Check if we are in an nested list
var containerBlockParentName = containerBlock.parentNode.nodeName;
if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
@ -20321,7 +20351,9 @@ define("tinymce/EditorCommands", [
// Setup parser and serializer
parser = editor.parser;
serializer = new Serializer({}, editor.schema);
serializer = new Serializer({
validate: settings.validate
}, editor.schema);
bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
// Run beforeSetContent handlers on the HTML to be inserted
@ -27637,6 +27669,59 @@ define("tinymce/WindowManager", [
};
});
// Included from: js/tinymce/classes/dom/NodePath.js
/**
* NodePath.js
*
* Released under LGPL License.
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/**
* Handles paths of nodes within an element.
*
* @private
* @class tinymce.dom.NodePath
*/
define("tinymce/dom/NodePath", [
"tinymce/dom/DOMUtils"
], function(DOMUtils) {
function create(rootNode, targetNode, normalized) {
var path = [];
for (; targetNode && targetNode != rootNode; targetNode = targetNode.parentNode) {
path.push(DOMUtils.nodeIndex(targetNode, normalized));
}
return path;
}
function resolve(rootNode, path) {
var i, node, children;
for (node = rootNode, i = path.length - 1; i >= 0; i--) {
children = node.childNodes;
if (path[i] > children.length - 1) {
return null;
}
node = children[path[i]];
}
return node;
}
return {
create: create,
resolve: resolve
};
});
// Included from: js/tinymce/classes/util/Quirks.js
/**
@ -27661,11 +27746,12 @@ define("tinymce/util/Quirks", [
"tinymce/util/VK",
"tinymce/dom/RangeUtils",
"tinymce/dom/TreeWalker",
"tinymce/dom/NodePath",
"tinymce/html/Node",
"tinymce/html/Entities",
"tinymce/Env",
"tinymce/util/Tools"
], function(VK, RangeUtils, TreeWalker, Node, Entities, Env, Tools) {
], function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools) {
return function(editor) {
var each = Tools.each, $ = editor.$;
var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
@ -28044,6 +28130,111 @@ define("tinymce/util/Quirks", [
}
}
/**
* This retains the formatting if the last character is to be deleted.
*
* Backspace on this: <p><b><i>a|</i></b></p> would become <p>|</p> in WebKit.
* With this patch: <p><b><i>|<br></i></b></p>
*/
function handleLastBlockCharacterDelete(isForward, rng) {
var path, blockElm, newBlockElm, clonedBlockElm, sibling,
container, offset, br, currentFormatNodes;
function cloneTextBlockWithFormats(blockElm, node) {
currentFormatNodes = $(node).parents().filter(function(idx, node) {
return !!editor.schema.getTextInlineElements()[node.nodeName];
});
newBlockElm = blockElm.cloneNode(false);
currentFormatNodes = Tools.map(currentFormatNodes, function(formatNode) {
formatNode = formatNode.cloneNode(false);
if (newBlockElm.hasChildNodes()) {
formatNode.appendChild(newBlockElm.firstChild);
newBlockElm.appendChild(formatNode);
} else {
newBlockElm.appendChild(formatNode);
}
newBlockElm.appendChild(formatNode);
return formatNode;
});
if (currentFormatNodes.length) {
br = dom.create('br');
currentFormatNodes[0].appendChild(br);
dom.replace(newBlockElm, blockElm);
rng.setStartBefore(br);
rng.setEndBefore(br);
editor.selection.setRng(rng);
return br;
}
return null;
}
function isTextBlock(node) {
return node && editor.schema.getTextBlockElements()[node.tagName];
}
if (!rng.collapsed) {
return;
}
container = rng.startContainer;
offset = rng.startOffset;
blockElm = dom.getParent(container, dom.isBlock);
if (!isTextBlock(blockElm)) {
return;
}
if (container.nodeType == 1) {
container = container.childNodes[offset];
if (container && container.tagName != 'BR') {
return;
}
if (isForward) {
sibling = blockElm.nextSibling;
} else {
sibling = blockElm.previousSibling;
}
if (dom.isEmpty(blockElm) && isTextBlock(sibling) && dom.isEmpty(sibling)) {
if (cloneTextBlockWithFormats(blockElm, container)) {
dom.remove(sibling);
return true;
}
}
} else if (container.nodeType == 3) {
path = NodePath.create(blockElm, container);
clonedBlockElm = blockElm.cloneNode(true);
container = NodePath.resolve(clonedBlockElm, path);
if (isForward) {
if (offset >= container.data.length) {
return;
}
container.deleteData(offset, 1);
} else {
if (offset <= 0) {
return;
}
container.deleteData(offset - 1, 1);
}
if (dom.isEmpty(clonedBlockElm)) {
return cloneTextBlockWithFormats(blockElm, container);
}
}
}
function customDelete(isForward) {
var mutationObserver, rng, caretElement;
@ -28133,6 +28324,11 @@ define("tinymce/util/Quirks", [
return;
}
if (handleLastBlockCharacterDelete(isForward, rng)) {
e.preventDefault();
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) {
@ -28152,7 +28348,7 @@ define("tinymce/util/Quirks", [
// Handle case where text is deleted by typing over
editor.on('keypress', function(e) {
if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode && !VK.metaKeyPressed(e)) {
if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) {
var rng, currentFormatNodes, fragmentNode, blockParent, caretNode, charText;
rng = editor.selection.getRng();
@ -31371,11 +31567,14 @@ define("tinymce/Editor", [
// Keep scripts from executing
self.parser.addNodeFilter('script', function(nodes) {
var i = nodes.length, node;
var i = nodes.length, node, type;
while (i--) {
node = nodes[i];
node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
type = node.attr('type') || 'no/type';
if (type.indexOf('mce-') !== 0) {
node.attr('type', 'mce-' + type);
}
}
});
@ -32178,7 +32377,7 @@ define("tinymce/Editor", [
* tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
*/
setContent: function(content, args) {
var self = this, body = self.getBody(), forcedRootBlockName;
var self = this, body = self.getBody(), forcedRootBlockName, padd;
// Setup args object
args = args || {};
@ -32196,14 +32395,24 @@ define("tinymce/Editor", [
// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
// It will also be impossible to place the caret in the editor unless there is a BR element present
if (content.length === 0 || /^\s+$/.test(content)) {
padd = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
// Todo: There is a lot more root elements that need special padding
// so separate this and add all of them at some point.
if (body.nodeName == 'TABLE') {
content = '<tr><td>' + padd + '</td></tr>';
} else if (/^(UL|OL)$/.test(body.nodeName)) {
content = '<li>' + padd + '</li>';
}
forcedRootBlockName = self.settings.forced_root_block;
// Check if forcedRootBlock is configured and that the block is a valid child of the body
if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
content = padd;
content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
} else if (!ie) {
} else if (!ie && !content) {
// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
content = '<br data-mce-bogus="1">';
}
@ -32214,7 +32423,9 @@ define("tinymce/Editor", [
} else {
// Parse and serialize the html
if (args.format !== 'raw') {
content = new Serializer({}, self.schema).serialize(
content = new Serializer({
validate: self.validate
}, self.schema).serialize(
self.parser.parse(content, {isRootContent: true})
);
}
@ -33146,7 +33357,7 @@ define("tinymce/EditorManager", [
* @property minorVersion
* @type String
*/
minorVersion: '2.6',
minorVersion: '2.7',
/**
* Release date of TinyMCE build.
@ -33154,7 +33365,7 @@ define("tinymce/EditorManager", [
* @property releaseDate
* @type String
*/
releaseDate: '2015-09-28',
releaseDate: '2015-10-27',
/**
* Collection of editor instances.
@ -37558,6 +37769,23 @@ define("tinymce/ui/FormatControls", [
formatMenu = createFormatMenu();
function initOnPostRender() {
var self = this;
// TODO: Fix this
if (editor.formatter) {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
} else {
editor.on('init', function() {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
});
}
}
// Simple format controls <control/format>:<UI text>
each({
bold: 'Bold',
@ -37570,20 +37798,7 @@ define("tinymce/ui/FormatControls", [
editor.addButton(name, {
tooltip: text,
onPostRender: function() {
var self = this;
// TODO: Fix this
if (editor.formatter) {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
} else {
editor.on('init', function() {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
});
}
initOnPostRender();
},
onclick: function() {
toggleFormat(name);
@ -37627,20 +37842,7 @@ define("tinymce/ui/FormatControls", [
tooltip: item[0],
cmd: item[1],
onPostRender: function() {
var self = this;
// TODO: Fix this
if (editor.formatter) {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
} else {
editor.on('init', function() {
editor.formatter.formatChanged(name, function(state) {
self.active(state);
});
});
}
initOnPostRender();
}
});
});

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ $wp_db_version = 35465;
*
* @global string $tinymce_version
*/
$tinymce_version = '4206-20151020';
$tinymce_version = '4207-20151107';
/**
* Holds the required PHP version

View File

@ -47,6 +47,7 @@
<script src="tinymce/dom/Selection.js"></script>
<script src="tinymce/dom/Serializer.js"></script>
<script src="tinymce/dom/TridentSelection.js"></script>
<script src="tinymce/dom/NodePath.js"></script>
<!-- tinymce.html.* -->
<script src="tinymce/html/DomParser.js"></script>

View File

@ -15,7 +15,7 @@ module("tinymce.Editor", {
'*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,float,margin,margin-top,margin-right,margin-bottom,margin-left,display'
},
custom_elements: 'custom1,~custom2',
extended_valid_elements: 'custom1,custom2',
extended_valid_elements: 'custom1,custom2,script[*]',
init_instance_callback: function(ed) {
window.editor = ed;
@ -403,6 +403,22 @@ test('addQueryStateHandler', function() {
ok(lastScope === editor, "Scope is not editor");
});
test('Block script execution', function() {
editor.setContent('<script></script><script type="x"></script><script type="mce-x"></script>');
equal(
Utils.cleanHtml(editor.getBody().innerHTML),
'<script type="mce-no/type"></script>' +
'<script type="mce-x"></script>' +
'<script type="mce-x"></script>'
);
equal(
editor.getContent(),
'<script></script>' +
'<script type="x"></script>' +
'<script type="x"></script>'
);
});
test('addQueryValueHandler', function() {
var scope = {}, lastScope, currentValue;

View File

@ -0,0 +1,29 @@
ModuleLoader.require([
"tinymce/dom/NodePath"
], function(NodePath) {
module("tinymce.dom.NodePath", {});
function getRoot() {
return document.getElementById('view');
}
function setupHtml(html) {
getRoot().innerHTML = html;
}
test("create", function() {
setupHtml('<p>a<b>12<input></b></p>');
deepEqual(NodePath.create(getRoot(), getRoot().firstChild), [0]);
deepEqual(NodePath.create(getRoot(), getRoot().firstChild.firstChild), [0, 0]);
deepEqual(NodePath.create(getRoot(), getRoot().firstChild.lastChild.lastChild), [1, 1, 0]);
});
test("resolve", function() {
setupHtml('<p>a<b>12<input></b></p>');
deepEqual(NodePath.resolve(getRoot(), NodePath.create(getRoot(), getRoot().firstChild)), getRoot().firstChild);
deepEqual(NodePath.resolve(getRoot(), NodePath.create(getRoot(), getRoot().firstChild.firstChild)), getRoot().firstChild.firstChild);
deepEqual(NodePath.resolve(getRoot(), NodePath.create(getRoot(), getRoot().firstChild.lastChild.lastChild)), getRoot().firstChild.lastChild.lastChild);
});
});

View File

@ -20,3 +20,13 @@ test('Sorting of attributes', function() {
equal(serializer.serialize(new tinymce.html.DomParser().parse('<b class="class" id="id">x</b>')), '<strong id="id" class="class">x</strong>');
});
test('Serialize with validate: true, when parsing with validate:false bug', function() {
var schema = new tinymce.html.Schema({valid_elements: 'b'});
var serializer = new tinymce.html.Serializer({}, schema);
equal(
serializer.serialize(new tinymce.html.DomParser({validate: false}, schema).parse('<b a="1" b="2">a</b><i a="1" b="2">b</i>')),
'<b a="1" b="2">a</b><i a="1" b="2">b</i>'
);
});

View File

@ -268,6 +268,48 @@ if (tinymce.isWebKit) {
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><i>1<b>a</b>3</i></p>');
equal(editor.selection.getStart().nodeName, 'B');
});
test('Delete last character in formats', function() {
editor.getBody().innerHTML = '<p><b><i>b</i></b></p>';
Utils.setSelection('i', 1);
editor.fire("keydown", {keyCode: 8});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><b><i><br></i></b></p>');
equal(editor.selection.getStart(true).nodeName, 'I');
});
test('ForwardDelete last character in formats', function() {
editor.getBody().innerHTML = '<p><b><i>b</i></b></p>';
Utils.setSelection('i', 0);
editor.fire("keydown", {keyCode: 46});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p><b><i><br></i></b></p>');
equal(editor.selection.getStart(true).nodeName, 'I');
});
test('Delete in empty in formats text block', function() {
var rng;
editor.getBody().innerHTML = '<p>a</p><p><b><i><br></i></b></p><p><b><i><br></i></b></p>';
rng = editor.dom.createRng();
rng.setStartBefore(editor.$('br:last')[0]);
rng.setEndBefore(editor.$('br:last')[0]);
editor.selection.setRng(rng);
editor.fire("keydown", {keyCode: 8});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p>a</p><p><b><i><br></i></b></p>');
equal(editor.selection.getStart(true).nodeName, 'I');
});
test('ForwardDelete in empty formats text block', function() {
var rng;
editor.getBody().innerHTML = '<p>a</p><p><b><i><br></i></b></p><p><b><i><br></i></b></p>';
rng = editor.dom.createRng();
rng.setStartBefore(editor.$('br:first')[0]);
rng.setEndBefore(editor.$('br:first')[0]);
editor.selection.setRng(rng);
editor.fire("keydown", {keyCode: 46});
equal(Utils.cleanHtml(editor.getBody().innerHTML), '<p>a</p><p><b><i><br></i></b></p>');
equal(editor.selection.getStart(true).nodeName, 'I');
});
} else {
test("Skipped since the browser isn't WebKit", function() {
ok(true, "Skipped");