]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/tinymce/plugins/lists/plugin.js
WordPress 4.7-scripts
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / lists / plugin.js
index f5f32d9f52e66110725f076b7467745c9b00b686..ae9a22976aea1449ae9f24ad80ed6144259b3f68 100644 (file)
 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 <li>|<br></li> 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'));