X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/8ab4a4532479e8db471032b51042ec8c4716d091..ef91a7f4f3c6468973e192335a27ec0e0faca0b5:/wp-includes/js/tinymce/plugins/lists/plugin.js diff --git a/wp-includes/js/tinymce/plugins/lists/plugin.js b/wp-includes/js/tinymce/plugins/lists/plugin.js index a13c8ec5..ae9a2297 100644 --- a/wp-includes/js/tinymce/plugins/lists/plugin.js +++ b/wp-includes/js/tinymce/plugins/lists/plugin.js @@ -1,8 +1,8 @@ /** * plugin.js * - * Copyright, Moxiecode Systems AB * 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 @@ -14,8 +14,20 @@ tinymce.PluginManager.add('lists', function(editor) { var self = this; + function isChildOfBody(elm) { + return editor.$.contains(editor.getBody(), elm); + } + + function isBr(node) { + return node && node.nodeName == 'BR'; + } + function isListNode(node) { - return node && (/^(OL|UL|DL)$/).test(node.nodeName); + return node && (/^(OL|UL|DL)$/).test(node.nodeName) && isChildOfBody(node); + } + + function isListItemNode(node) { + return node && /^(LI|DT|DD)$/.test(node.nodeName); } function isFirstChild(node) { @@ -30,9 +42,50 @@ tinymce.PluginManager.add('lists', function(editor) { return node && !!editor.schema.getTextBlockElements()[node.nodeName]; } + function isEditorBody(elm) { + return elm === editor.getBody(); + } + + function isTextNode(node) { + return node && node.nodeType === 3; + } + + function getNormalizedEndPoint(container, offset) { + var node = tinymce.dom.RangeUtils.getNode(container, offset); + + if (isListItemNode(container) && isTextNode(node)) { + var textNodeOffset = offset >= container.childNodes.length ? node.data.length : 0; + return {container: node, offset: textNodeOffset}; + } + + return {container: container, offset: offset}; + } + + function normalizeRange(rng) { + var outRng = rng.cloneRange(); + + var rangeStart = getNormalizedEndPoint(rng.startContainer, rng.startOffset); + outRng.setStart(rangeStart.container, rangeStart.offset); + + var rangeEnd = getNormalizedEndPoint(rng.endContainer, rng.endOffset); + outRng.setEnd(rangeEnd.container, rangeEnd.offset); + + return outRng; + } + 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 @@ -141,7 +194,7 @@ tinymce.PluginManager.add('lists', function(editor) { rng.setEnd(bookmark.endContainer, bookmark.endOffset); } - selection.setRng(rng); + selection.setRng(normalizeRange(rng)); } function createNewTextBlock(contentNode, blockName) { @@ -202,7 +255,7 @@ tinymce.PluginManager.add('lists', function(editor) { function getSelectedListItems() { return tinymce.grep(selection.getSelectedBlocks(), function(block) { - return /^(LI|DT|DD)$/.test(block.nodeName); + return isListItemNode(block); }); } @@ -237,22 +290,28 @@ 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); } } + 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); } @@ -261,7 +320,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); } @@ -270,44 +329,49 @@ tinymce.PluginManager.add('lists', function(editor) { } } - /** - * Normalizes the all lists in the specified element. - */ - function normalizeList(element) { - tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) { - var sibling, parentNode = ul.parentNode; - - // Move UL/OL to previous LI if it's the only child of a LI - if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) { - sibling = parentNode.previousSibling; - if (sibling && sibling.nodeName == 'LI') { - sibling.appendChild(ul); - - if (dom.isEmpty(parentNode)) { - dom.remove(parentNode); - } + function normalizeLists(element) { + tinymce.each(tinymce.grep(dom.select('ol,ul', element)), normalizeList); + } + + function normalizeList(ul) { + var sibling, parentNode = ul.parentNode; + + // Move UL/OL to previous LI if it's the only child of a LI + if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) { + sibling = parentNode.previousSibling; + if (sibling && sibling.nodeName == 'LI') { + sibling.appendChild(ul); + + if (isEmpty(parentNode)) { + dom.remove(parentNode); } + } else { + dom.setStyle(parentNode, 'listStyleType', 'none'); } + } - // Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4 - if (isListNode(parentNode)) { - sibling = parentNode.previousSibling; - if (sibling && sibling.nodeName == 'LI') { - sibling.appendChild(ul); - } + // Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4 + if (isListNode(parentNode)) { + sibling = parentNode.previousSibling; + if (sibling && sibling.nodeName == 'LI') { + sibling.appendChild(ul); } - }); + } } function outdent(li) { var ul = li.parentNode, ulParent = ul.parentNode, newBlock; function removeEmptyLi(li) { - if (dom.isEmpty(li)) { + if (isEmpty(li)) { dom.remove(li); } } + if (isEditorBody(ul)) { + return true; + } + if (li.nodeName == 'DD') { dom.rename(li, 'DT'); return true; @@ -350,27 +414,25 @@ tinymce.PluginManager.add('lists', function(editor) { } return true; - } else { - if (ulParent.nodeName == 'LI') { - ul = ulParent; - newBlock = createNewTextBlock(li, 'LI'); - } else if (isListNode(ulParent)) { - newBlock = createNewTextBlock(li, 'LI'); - } else { - newBlock = createNewTextBlock(li); - } - - splitList(ul, li, newBlock); - normalizeList(ul.parentNode); + } - return true; + if (ulParent.nodeName == 'LI') { + ul = ulParent; + newBlock = createNewTextBlock(li, 'LI'); + } else if (isListNode(ulParent)) { + newBlock = createNewTextBlock(li, 'LI'); + } else { + newBlock = createNewTextBlock(li); } - return false; + splitList(ul, li, newBlock); + normalizeLists(ul.parentNode); + + return true; } function indent(li) { - var sibling, newList; + var sibling, newList, listStyle; function mergeLists(from, to) { var node; @@ -409,13 +471,17 @@ tinymce.PluginManager.add('lists', function(editor) { return true; } - if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) { + /*if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) { return false; - } + }*/ 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); @@ -481,8 +547,12 @@ tinymce.PluginManager.add('lists', function(editor) { } } - function applyList(listName) { - var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI'; + function applyList(listName, detail) { + var rng = selection.getRng(true), bookmark, listItemName = 'LI'; + + if (dom.getContentEditable(selection.getNode()) === "false") { + return; + } listName = listName.toUpperCase(); @@ -538,8 +608,8 @@ tinymce.PluginManager.add('lists', function(editor) { return; } - if (dom.isBlock(node) || node.nodeName == 'BR') { - if (node.nodeName == 'BR') { + if (dom.isBlock(node) || isBr(node)) { + if (isBr(node)) { dom.remove(node); } @@ -567,11 +637,22 @@ tinymce.PluginManager.add('lists', function(editor) { return textBlocks; } + bookmark = createBookmark(rng); + 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'] : ''; + + detailStyle = detailStyle === null ? '' : detailStyle; + + 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); @@ -582,19 +663,28 @@ 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(); tinymce.each(getSelectedListItems(), function(li) { var node, rootList; - if (dom.isEmpty(li)) { + if (isEditorBody(li.parentNode)) { + return; + } + + if (isEmpty(li)) { outdent(li); return; } @@ -606,24 +696,31 @@ tinymce.PluginManager.add('lists', function(editor) { } splitList(rootList, li); + normalizeLists(rootList.parentNode); }); moveToBookmark(bookmark); } - function toggleList(listName) { + function toggleList(listName, detail) { var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL'); + if (isEditorBody(parentList)) { + return; + } + if (parentList) { if (parentList.nodeName == listName) { 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); } } @@ -635,92 +732,147 @@ tinymce.PluginManager.add('lists', function(editor) { }; } - self.backspaceDelete = function(isForward) { - function findNextCaretContainer(rng, isForward) { - var node = rng.startContainer, offset = rng.startOffset; - var nonEmptyBlocks, walker; + function isBogusBr(node) { + if (!isBr(node)) { + return false; + } - if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) { - return node; - } + if (dom.isBlock(node.nextSibling) && !isBr(node.previousSibling)) { + return true; + } - nonEmptyBlocks = editor.schema.getNonEmptyElements(); - walker = new tinymce.dom.TreeWalker(rng.startContainer); + return false; + } - while ((node = walker[isForward ? 'next' : 'prev']())) { - if (node.nodeName == 'LI' && !node.hasChildNodes()) { - return node; - } + function findNextCaretContainer(rng, isForward) { + var node = rng.startContainer, offset = rng.startOffset; + var nonEmptyBlocks, walker; - if (nonEmptyBlocks[node.nodeName]) { - return node; - } + if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) { + return node; + } - if (node.nodeType == 3 && node.data.length > 0) { - return node; - } - } + nonEmptyBlocks = editor.schema.getNonEmptyElements(); + if (node.nodeType == 1) { + node = tinymce.dom.RangeUtils.getNode(node, offset); } - function mergeLiElements(fromElm, toElm) { - var node, listNode, ul = fromElm.parentNode; + walker = new tinymce.dom.TreeWalker(node, editor.getBody()); - if (isListNode(toElm.lastChild)) { - listNode = toElm.lastChild; + // Delete at
  • |
  • then jump over the bogus br + if (isForward) { + if (isBogusBr(node)) { + walker.next(); } + } - node = toElm.lastChild; - if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) { - dom.remove(node); + while ((node = walker[isForward ? 'next' : 'prev2']())) { + if (node.nodeName == 'LI' && !node.hasChildNodes()) { + return node; } - if (dom.isEmpty(toElm)) { - dom.$(toElm).empty(); + if (nonEmptyBlocks[node.nodeName]) { + return node; } - if (!dom.isEmpty(fromElm)) { - while ((node = fromElm.firstChild)) { - toElm.appendChild(node); - } + if (node.nodeType == 3 && node.data.length > 0) { + return node; } + } + } + + function mergeLiElements(fromElm, toElm) { + var node, listNode, ul = fromElm.parentNode; + + if (!isChildOfBody(fromElm) || !isChildOfBody(toElm)) { + return; + } + + if (isListNode(toElm.lastChild)) { + listNode = toElm.lastChild; + } - if (listNode) { - toElm.appendChild(listNode); + if (ul == toElm.lastChild) { + if (isBr(ul.previousSibling)) { + dom.remove(ul.previousSibling); } + } - dom.remove(fromElm); + node = toElm.lastChild; + if (node && isBr(node) && fromElm.hasChildNodes()) { + dom.remove(node); + } - if (dom.isEmpty(ul)) { - dom.remove(ul); + if (isEmpty(toElm, true)) { + dom.$(toElm).empty(); + } + + if (!isEmpty(fromElm, true)) { + while ((node = fromElm.firstChild)) { + toElm.appendChild(node); } } - if (selection.isCollapsed()) { - var li = dom.getParent(selection.getStart(), 'LI'); + if (listNode) { + toElm.appendChild(listNode); + } + + dom.remove(fromElm); + + if (isEmpty(ul) && !isEditorBody(ul)) { + dom.remove(ul); + } + } + + function backspaceDeleteCaret(isForward) { + 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'); + if (li) { + ul = li.parentNode; + if (isEditorBody(ul) && dom.isEmpty(ul)) { + return true; + } - if (otherLi && otherLi != li) { - var bookmark = createBookmark(rng); + rng = normalizeRange(selection.getRng(true)); + otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI'); - if (isForward) { - mergeLiElements(otherLi, li); - } else { - mergeLiElements(li, otherLi); - } + if (otherLi && otherLi != li) { + var bookmark = createBookmark(rng); + + if (isForward) { + mergeLiElements(otherLi, li); + } else { + mergeLiElements(li, otherLi); + } - moveToBookmark(bookmark); + moveToBookmark(bookmark); + return true; + } else if (!otherLi) { + if (!isForward && removeList(ul.nodeName)) { return true; - } else if (!otherLi) { - if (!isForward && removeList(li.parentNode.nodeName)) { - return true; - } } } } + } + + function backspaceDeleteRange() { + var startListParent = editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD'); + + if (startListParent || getSelectedListItems().length > 0) { + editor.undoManager.transact(function() { + editor.execCommand('Delete'); + normalizeLists(editor.getBody()); + }); + + return true; + } + + return false; + } + + self.backspaceDelete = function(isForward) { + return selection.isCollapsed() ? backspaceDeleteCaret(isForward) : backspaceDeleteRange(); }; editor.on('BeforeExecCommand', function(e) { @@ -743,16 +895,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'));