diff options
Diffstat (limited to 'addons/web_editor/static')
10 files changed, 690 insertions, 27 deletions
diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/bullet.js b/addons/web_editor/static/src/js/wysiwyg/plugin/bullet.js index 506646d4697..dc2a444384b 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/bullet.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/bullet.js @@ -173,6 +173,7 @@ var BulletPlugin = AbstractPlugin.extend({ } else { ul = this.document.createElement(sorted === "ol" ? "ol" : "ul"); ul.className = ol.className; + $(ul).removeClass('o_checklist'); $(ul).insertBefore(ol).append(lis); parent.removeChild(ol); } @@ -312,6 +313,10 @@ var BulletPlugin = AbstractPlugin.extend({ if (!range) { return; } + // list groups shouldn't be outdented like regular lists + if ($(dom.ancestor(range.sc, dom.isList)).hasClass('list-group')) { + return; + } var self = this; var nodes = []; diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/dropzone.js b/addons/web_editor/static/src/js/wysiwyg/plugin/dropzone.js index 74703b90e45..255561b02be 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/dropzone.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/dropzone.js @@ -19,6 +19,7 @@ var DropzonePlugin = Plugins.dropzone.extend({ attachDragAndDropEvent: function () { this._super.apply(this, arguments); this.$dropzone.off('drop'); + this.$dropzone.on('dragenter', this._onDragenter.bind(this)); this.$dropzone.on('drop', this._onDrop.bind(this)); }, @@ -71,7 +72,7 @@ var DropzonePlugin = Plugins.dropzone.extend({ if (range.so >= dom.nodeLength(range.sc)) { $(range.sc).append(spinner); } else { - $(range.sc).before(range.sc.childNodes[range.so]); + $(range.sc.childNodes[range.so]).before(spinner); } } else { range.sc.splitText(range.so); @@ -151,7 +152,7 @@ var DropzonePlugin = Plugins.dropzone.extend({ route: '/web_editor/add_image_base64', params: { res_model: options.res_model, - res_id: options.res_id, + res_id: options.res_id || 0, image_base64: imageBase64.split(';base64,').pop(), filename: fileName, }, @@ -161,9 +162,26 @@ var DropzonePlugin = Plugins.dropzone.extend({ * @private * @param {JQueryEvent} e */ + _onDragenter: function (e) { + var range = this.context.invoke('editor.createRange'); + if (!this.editable.contains(range.sc)) { + var node = this.editable; + if (node && node.firstChild) { + node = node.firstChild; + } + range = this.context.invoke('editor.setRange', node, 0, node, 0); + range.select(); + } + }, + /** + * @private + * @param {JQueryEvent} e + */ _onDrop: function (e) { e.preventDefault(); + this.$editor.removeClass('dragover'); + if (this.options.disableDragAndDrop) { return; } diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js b/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js index 4ba44fe3c9b..521afe6137a 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js @@ -109,12 +109,14 @@ var HelperPlugin = AbstractPlugin.extend({ this.orderStyle(node); this.orderClass(otherNode); this.orderStyle(otherNode); - if (node.attributes.length !== otherNode.attributes.length) { + var nodeAttributes = this._getAttrsNoID(node); + var otherNodeAttributes = this._getAttrsNoID(otherNode); + if (nodeAttributes.length !== otherNodeAttributes.length) { return false; } - for (var i = 0; i < node.attributes.length; i++) { - var attr = node.attributes[i]; - var otherAttr = otherNode.attributes[i]; + for (var i = 0; i < nodeAttributes.length; i++) { + var attr = nodeAttributes[i]; + var otherAttr = otherNodeAttributes[i]; if (attr.name !== otherAttr.name || attr.value !== otherAttr.value) { return false; } @@ -306,6 +308,7 @@ var HelperPlugin = AbstractPlugin.extend({ var range = this.context.invoke('editor.createRange'); var spaceToRemove = []; + var offset; while ((node = nodes.pop())) { next = node[prevOrNext]; while (next && !next.tagName) { @@ -316,6 +319,31 @@ var HelperPlugin = AbstractPlugin.extend({ } break; } + + // handle merge of text with p (useful in case of <li>text</li> with <li><p>text</p></li>) + if (next && dom.isText(node) && this.isNodeBlockType(next)) { + node = [next, next = node][0]; // swap node and next + direction = direction === 'next' ? 'prev' : 'next'; // switch direction + } + if (next && dom.isText(next) && this.isNodeBlockType(node)) { + if (this.isBlankNode(node, true)) { + $(node).empty(); + } + $(node)[direction === 'next' ? 'append' : 'prepend'](next); + offset = direction === 'next' ? 0 : dom.nodeLength(next); + if (dom.isText(next[prevOrNext])) { + // merge text if needed + var sib = next[prevOrNext]; + var firstText = (direction === 'next' ? sib : next).textContent; + var secondText = (direction === 'next' ? next : sib).textContent; + next.textContent = firstText + secondText; + $(sib).remove(); + offset = firstText.length; + } + result = this.makePoint(next, offset); + break; + } + if ( !next || !(node.tagName || next.tagName === 'BR') || @@ -328,7 +356,7 @@ var HelperPlugin = AbstractPlugin.extend({ var newNext = next[prevOrNext]; $(next).remove(); next = newNext; - var offset = (next ? direction === 'prev' : direction === 'next') ? dom.nodeLength(next) : 0; + offset = (next ? direction === 'prev' : direction === 'next') ? dom.nodeLength(next) : 0; result = this.makePoint(next || node, offset); if (!ifBrRemovedAndMerge) { continue; @@ -455,6 +483,11 @@ var HelperPlugin = AbstractPlugin.extend({ return this.makePoint(this.firstLeaf(blockToMergeFrom), 0); } + // UL/OL can only have LI, UL or OR as direct children + if (dom.isList(blockToMergeInto) && !dom.isLi(blockToMergeFrom) && !dom.isList(blockToMergeFrom)) { + return; + } + return this.mergeNonSimilarBlocks(blockToMergeFrom, blockToMergeInto); }, /** @@ -601,22 +634,24 @@ var HelperPlugin = AbstractPlugin.extend({ var li = dom.ancestor(node, function (n) { return n !== node && self.isNodeBlockType(n) || dom.isLi(n); }); + var nextElementSibling = direction === 'next' ? 'nextElementSibling' : 'previousElementSibling'; li = li && dom.isLi(li) ? li : undefined; - if (li && direction === 'next') { - if (li.nextElementSibling) { + if (li) { + if (li[nextElementSibling]) { node = li; } else { node = dom.ancestor(node, function (n) { - return ((n.tagName === 'UL' || n.tagName === 'OL') && n.nextElementSibling); + return ((n.tagName === 'UL' || n.tagName === 'OL') && n[nextElementSibling] && + (direction === 'next' || dom.isList(n[nextElementSibling]))); }); } } - if (!node || !node[direction === 'next' ? 'nextElementSibling' : 'previousElementSibling']) { + if (!node || !node[nextElementSibling]) { return false; } - node = node[direction === 'next' ? 'nextElementSibling' : 'previousElementSibling']; + node = node[nextElementSibling]; var ulFoldedSnippetNode = dom.ancestor(node, function (n) { return $(n).hasClass('o_ul_folded'); @@ -633,7 +668,7 @@ var HelperPlugin = AbstractPlugin.extend({ node = this.firstBlockAncestor(node); - li = dom.ancestor(node, function (n) { + li = dom.isList(node) ? undefined : dom.ancestor(node, function (n) { return n !== node && self.isNodeBlockType(n) || dom.isLi(n); }); li = li && dom.isLi(li) ? li : undefined; @@ -1051,9 +1086,15 @@ var HelperPlugin = AbstractPlugin.extend({ * but without any. * * @param {Node} node + * @param {Boolean} brIsBlank true to consider nodes with BR element + * as ultimate only child to be blank * @returns {Boolean} */ - isBlankNode: function (node) { + isBlankNode: function (node, brIsBlank) { + var self = this; + if (brIsBlank && dom.isBR(node)) { + return true; + } if (dom.isVoid(node) || dom.isIcon(node)) { return false; } @@ -1062,7 +1103,10 @@ var HelperPlugin = AbstractPlugin.extend({ }).test(node[dom.isText(node) ? 'textContent' : 'innerHTML'])) { return true; } - if (node.childNodes.length && _.all(node.childNodes, this.isBlankNode.bind(this))) { + var areAllChildrenBlank = _.all(node.childNodes, function (node) { + return self.isBlankNode(node, brIsBlank); + }); + if (node.childNodes.length && areAllChildrenBlank) { return true; } return false; @@ -1306,7 +1350,7 @@ var HelperPlugin = AbstractPlugin.extend({ $lastContents.after($contents); } } - while (mergeFromBlock.parentNode && this.isBlankNode(mergeFromBlock.parentNode)) { + while (mergeFromBlock.parentNode && this.isBlankNode(mergeFromBlock.parentNode, true)) { mergeFromBlock = mergeFromBlock.parentNode; } $(mergeFromBlock).remove(); @@ -1478,7 +1522,7 @@ var HelperPlugin = AbstractPlugin.extend({ if (isAfterBlank) { $(node.previousSibling).remove(); } - var isBeforeBlank = node.nextSibling && this.isBlankNode(node.nextSibling) + var isBeforeBlank = node.nextSibling && this.isBlankNode(node.nextSibling); if (isBeforeBlank) { $(node.nextSibling).remove(); } @@ -1733,6 +1777,15 @@ var HelperPlugin = AbstractPlugin.extend({ // Private //-------------------------------------------------------------------------- + _getAttrsNoID: function (node) { + var nodeAttributes = []; + for (var i = 0; i < node.attributes.length; i++) { + if (node.attributes[i].name !== 'id') { + nodeAttributes.push(node.attributes[i]); + } + } + return nodeAttributes; + }, _insertTextNodeInEditableArea: function (range, text) { // try to insert the text node in editable area var textNode = this.document.createTextNode(text); diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/keyboard.js b/addons/web_editor/static/src/js/wysiwyg/plugin/keyboard.js index 3ab6a5f0317..94ec9306f7d 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/keyboard.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/keyboard.js @@ -296,6 +296,7 @@ var KeyboardPlugin = AbstractPlugin.extend({ var next = this.context.invoke('HelperPlugin.splitTree', ancestor, point, { isSkipPaddingBlankHTML: !this.context.invoke('HelperPlugin.isNodeBlockType', point.node.parentNode) && !!point.node.parentNode.nextSibling }); + $(next).removeClass('o_checked'); while (next.firstChild) { next = next.firstChild; } diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/link.js b/addons/web_editor/static/src/js/wysiwyg/plugin/link.js index 1b75339bf54..d8d261df980 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/link.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/link.js @@ -358,12 +358,12 @@ var LinkPopover = Plugins.linkPopover.extend({ var $target = $(anchor); - if (!$target.data('show_tooltip')) { + if (!$target.data('show_tooltip') && this.context.options.tooltip) { $target.data('show_tooltip', true); setTimeout(function () { $target.tooltip({ title: _t('Double-click to edit'), - trigger: 'manuel', + trigger: 'manual', container: this.document.body, placement: 'top' }).tooltip('show'); diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/media.js b/addons/web_editor/static/src/js/wysiwyg/plugin/media.js index ce58a4eac86..e6a598a2813 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/media.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/media.js @@ -43,6 +43,8 @@ var MediaPlugin = AbstractPlugin.extend({ 'dblclick .note-editable': '_onDblclick', }, + mousePosition: {}, + //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- @@ -114,12 +116,12 @@ var MediaPlugin = AbstractPlugin.extend({ this.context.invoke('editor.saveTarget', target); var $target = $(target); - if (!$target.data('show_tooltip')) { + if (!$target.data('show_tooltip') && this.context.options.tooltip) { $target.data('show_tooltip', true); setTimeout(function () { $target.tooltip({ title: _t('Double-click to edit'), - trigger: 'manuel', + trigger: 'manual', container: this.document.body, }).tooltip('show'); setTimeout(function () { diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/text.js b/addons/web_editor/static/src/js/wysiwyg/plugin/text.js index d3ac908c507..16e22939cd4 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/text.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/text.js @@ -85,7 +85,7 @@ var TextPlugin = AbstractPlugin.extend({ return; } nodes = textOnly ? this.document.createTextNode($(nodes).text()) : nodes; - nodes = this._mergeAdjacentULs(nodes); + nodes = textOnly ? nodes : this._mergeAdjacentULs(nodes); var point = this._getPastePoint(); // Prevent pasting HTML within a link: diff --git a/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.editor.js b/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.editor.js index 63692848eb1..9bc9124688b 100644 --- a/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.editor.js +++ b/addons/web_editor/static/src/js/wysiwyg_snippets/snippets.editor.js @@ -918,7 +918,7 @@ var SnippetsMenu = Widget.extend({ && (float_next === 'left' || float_next === 'right')) { zone.remove(); } else if (disp_prev !== null && disp_next !== null - && disp_prev !== 'block' && disp_next !== 'block') { + && disp_prev.indexOf('inline') >= 0 && disp_next.indexOf('inline') >= 0) { zone.remove(); } }); diff --git a/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js b/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js index 56ceb27a025..6c08fec0d98 100644 --- a/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js @@ -242,6 +242,9 @@ QUnit.test('Unbreakable selection and edition', function (assert) { weTestUtils.createWysiwyg({ data: this.data, useOnlyTestUnbreakable: true, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, UnbreakableTests).then(function () { @@ -817,6 +820,9 @@ QUnit.test('Char', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsChar).then(function () { @@ -1179,6 +1185,30 @@ var keyboardTestsEnter = [{ start: "br->0", }, }, + { + name: "in ul.o_checklist > li.o_checked > p: ENTER at end", + content: '<ul class="o_checklist"><li class="o_checked"><p>test</p></li></ul>', + steps: [{ + start: "p:contents()[0]->4", + key: 'ENTER', + }], + test: { + content: '<ul class="o_checklist"><li class="o_checked"><p>test</p></li><li><p><br></p></li></ul>', + start: "br->0", + }, + }, + { + name: "in ul.o_checklist > li.o_checked > p > b: ENTER within text", + content: '<ul class="o_checklist"><li class="o_checked"><p><b>test</b></p></li></ul>', + steps: [{ + start: "b:contents()[0]->2", + key: 'ENTER', + }], + test: { + content: '<ul class="o_checklist"><li class="o_checked"><p><b>te</b></p></li><li><p><b>st</b></p></li></ul>', + start: "b:eq(1):contents()[0]->0", + }, + }, // end list UL / OL @@ -1791,6 +1821,9 @@ QUnit.test('Enter', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsEnter).then(function () { @@ -2079,12 +2112,151 @@ var keyboardTestsComplex = [{ start: "h1:contents()[0]->15", }, }, + { + name: "in ul > li > ul > 1st li > empty p (other ul li ul before): BACKSPACE at start", + content: '<ul><li><p>1</p></li><li class="o_indent"><ul><li><p>2</p></li></ul><ul class="o_checklist"><li id="checklist-id-1"><p><br></p></li></ul></li><li><p>3</p></li></ul>', + steps: [{ + start: "p:eq(2)->0", + key: 'BACKSPACE', + }], + test: { + content: '<ul><li><p>1</p></li><li class="o_indent"><ul><li><p>2</p></li></ul></li><li><p>3</p></li></ul>', + start: "p:eq(1):contents()[0]->1", + }, + }, + { + name: "in ul > li > p > text (li > text before): BACKSPACE at start", + content: '<ul><li>1</li><li>2</li><li><p><br></p></li><li>3</li></ul>', + steps: [{ + start: "p:contents()[0]->0", + key: 'BACKSPACE', + }], + test: { + content: '<ul><li>1</li><li><p>2</p></li><li>3</li></ul>', + start: "p:contents()[0]->1", + }, + }, + { + name: "in ul > li > p > text (li > text before): 3x BACKSPACE after first character", + content: '<ul><li>1</li><li>2</li><li><p>34</p></li><li>5</li></ul>', + steps: [{ + start: "p:contents()[0]->1", + key: 'BACKSPACE', + }, { + key: 'BACKSPACE', + }, { + key: 'BACKSPACE', + }], + test: { + content: '<ul><li>1</li><li><p>4</p></li><li>5</li></ul>', + start: "p:contents()[0]->0", + }, + }, + { + name: "in ul > li > text (li > p > text before): BACKSPACE at start", + content: '<ul><li>1</li><li><p>2</p></li><li>3</li><li>4</li></ul>', + steps: [{ + start: "li:eq(2):contents()[0]->0", + key: 'BACKSPACE', + }], + test: { + content: '<ul><li>1</li><li><p>23</p></li><li>4</li></ul>', + start: "p:contents()[0]->1", + }, + }, + { + name: "in ul > li > text (li > p > text before): 3x BACKSPACE after first character", + content: '<ul><li>1</li><li><p>2</p></li><li>34</li><li>5</li></ul>', + steps: [{ + start: "li:eq(2):contents()[0]->1", + key: 'BACKSPACE', + }, { + key: 'BACKSPACE', + }, { + key: 'BACKSPACE', + }], + test: { + content: '<ul><li>1</li><li><p>4</p></li><li>5</li></ul>', + start: "p:contents()[0]->0", + }, + }, + { + name: "in ul > li > p > text (li > text after): DELETE at end", + content: '<ul><li>1</li><li><p><br></p></li><li>2</li><li>3</li></ul>', + steps: [{ + start: "p->0", + key: 'DELETE', + }], + test: { + content: '<ul><li>1</li><li><p>2</p></li><li>3</li></ul>', + start: "p:contents()[0]->0", + }, + }, + { + name: "in ul > li > p > text (li > text after): 3x DELETE before last character", + content: '<ul><li>1</li><li><p>23</p></li><li>4</li><li>5</li></ul>', + steps: [{ + start: "p:contents()[0]->1", + key: 'DELETE', + }, { + key: 'DELETE', + }, { + key: 'DELETE', + }], + test: { + content: '<ul><li>1</li><li><p>2</p></li><li>5</li></ul>', + start: "p:contents()[0]->1", + }, + }, + { + name: "in ul > li > text (li > p > text after): DELETE at end", + content: '<ul><li>1</li><li>2</li><li><p>3</p></li><li>4</li></ul>', + steps: [{ + start: "li:eq(1):contents()[0]->1", + key: 'DELETE', + }], + test: { + content: '<ul><li>1</li><li><p>23</p></li><li>4</li></ul>', + start: "p:contents()[0]->1", + }, + }, + { + name: "in ul > li > text (li > p > text after): 3x DELETE before last character", + content: '<ul><li>1</li><li>23</li><li><p>4</p></li><li>5</li></ul>', + steps: [{ + start: "li:eq(1):contents()[0]->1", + key: 'DELETE', + }, { + key: 'DELETE', + }, { + key: 'DELETE', + }], + test: { + content: '<ul><li>1</li><li><p>2</p></li><li>5</li></ul>', + start: "p:contents()[0]->1", + }, + }, + { + name: "in ul.list-group (div > p > text before): BACKSPACE at start", + content: '<div><p>1</p></div><ul class="list-group list-group-flush"><li class="list-group-item"><p><br></p></li></ul>', + steps: [{ + start: "p:eq(1)->0", + key: 'BACKSPACE', + }], + test: { + content: '<div><p>1</p></div><ul class="list-group list-group-flush"><li class="list-group-item"><p><br></p></li></ul>', + start: "p:eq(1)->0", + }, + }, ]; QUnit.test('Complex', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsComplex).then(function () { @@ -2227,6 +2399,9 @@ QUnit.test('Tab', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsTab).then(function () { @@ -3280,6 +3455,9 @@ QUnit.test('Backspace', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsBackspace).then(function () { @@ -4197,6 +4375,9 @@ QUnit.test('Delete', function (assert) { var done = assert.async(); weTestUtils.createWysiwyg({ data: this.data, + wysiwygOptions: { + tooltip: false, + }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); weTestUtils.testKeyboard($editable, assert, keyboardTestsDelete).then(function () { diff --git a/addons/web_editor/static/tests/wysiwyg_tests.js b/addons/web_editor/static/tests/wysiwyg_tests.js index bb95a6776d4..7462f5a330b 100644 --- a/addons/web_editor/static/tests/wysiwyg_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_tests.js @@ -1141,7 +1141,7 @@ QUnit.test('Text bgcolor', function (assert) { }); QUnit.test('Unordered list', function (assert) { - assert.expect(10); + assert.expect(34); return weTestUtils.createWysiwyg({ debug: false, @@ -1223,6 +1223,176 @@ QUnit.test('Unordered list', function (assert) { end: 'p:eq(2):contents()[0]->5', }, }, + // Conversion from OL + { + name: "Click UL: ol -> ul", + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + }, + }, + { + name: "Click UL: ol -> ul (across li's)", + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ol -> ul (from second li)", + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><li><p>dom to edit</p></li></ol>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li></ol><ul><li><p>dom to edit</p></li></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ol -> ul ul (from indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ol><li><p>dom to edit</p></li></ol></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul><li><p>dom to edit</p></li></ul></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ol -> ul ul (across several indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ol><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ol></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ul></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ol -> ul ul (from second indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ol><li><p>dom not to edit</p></li><li><p>dom to edit</p></li><li><p>dom not to edit</p></li></ol></ul>', + start: 'li:eq(2) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ol><li><p>dom not to edit</p></li></ol><ul><li><p>dom to edit</p></li></ul><ol><li><p>dom not to edit</p></li></ol></ul>', + start: 'p:eq(3):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + // Conversion from Checklist + { + name: "Click UL: ul.o_checklist -> ul", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + }, + }, + { + name: "Click UL: ul.o_checklist -> ul (across li's)", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ul.o_checklist -> ul (from second li)", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>xxx</p></li><li><p>dom to edit</p></li></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>xxx</p></li></ul><ul><li><p>dom to edit</p></li></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ul.o_checklist -> ul ul (from indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom to edit</p></li></ul></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul><li><p>dom to edit</p></li></ul></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ul.o_checklist -> ul ul (across several indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ul></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ul></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + { + name: "Click UL: ul ul.o_checklist -> ul ul (from second indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom not to edit</p></li><li><p>dom to edit</p></li><li><p>dom not to edit</p></li></ul></ul>', + start: 'li:eq(2) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnUL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom not to edit</p></li></ul><ul><li><p>dom to edit</p></li></ul><ul class="o_checklist"><li><p>dom not to edit</p></li></ul></ul>', + start: 'p:eq(3):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, ]; var def = Promise.resolve(); @@ -1244,7 +1414,7 @@ QUnit.test('Unordered list', function (assert) { }); QUnit.test('Ordered list', function (assert) { - assert.expect(32); + assert.expect(56); return weTestUtils.createWysiwyg({ debug: false, @@ -1675,6 +1845,176 @@ QUnit.test('Ordered list', function (assert) { start: 'font:contents(0)->11', }, }, + // Conversion from UL + { + name: "Click OL: ul -> ol", + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + }, + }, + { + name: "Click OL: ul -> ol (across li's)", + content: '<p>dom not to edit</p><ul><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ul -> ol (from second li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><li><p>dom to edit</p></li></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li></ul><ol><li><p>dom to edit</p></li></ol>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ol ul -> ol ol (from indented li)", + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ul><li><p>dom to edit</p></li></ul></ol>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ol><li><p>dom to edit</p></li></ol></ol>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ol ul -> ol ol (across several indented li)", + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ul><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ul></ol>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ol><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ol></ol>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + { + name: "Click OL: ol ul -> ol ol (from second indented li)", + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ul><li><p>dom not to edit</p></li><li><p>dom to edit</p></li><li><p>dom not to edit</p></li></ul></ol>', + start: 'li:eq(2) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ul><li><p>dom not to edit</p></li></ul><ol><li><p>dom to edit</p></li></ol><ul><li><p>dom not to edit</p></li></ul></ol>', + start: 'p:eq(3):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + // Conversion from Checklist + { + name: "Click OL: ul.o_checklist -> ol", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(1):contents()[0]->5', + }, + }, + { + name: "Click OL: ul.o_checklist -> ol (across li's)", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ul>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>dom to edit</p></li><li><p>dom to edit</p></li></ol>', + start: 'p:eq(1):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ul.o_checklist -> ol (from second li)", + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>xxx</p></li><li><p>dom to edit</p></li></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul class="o_checklist"><li><p>xxx</p></li></ul><ol><li><p>dom to edit</p></li></ol>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ol ul.o_checklist -> ol ol (from indented li)", + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom to edit</p></li></ul></ol>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(1) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ol><li><p>xxx</p></li><ol><li><p>dom to edit</p></li></ol></ol>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(2):contents()[0]->5', + }, + }, + { + name: "Click OL: ul ul.o_checklist -> ul ol (across several indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ul></ul>', + start: 'li:eq(1) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ol><li><p>dom to edit 1</p></li><li><p>dom to edit 2</p></li></ol></ul>', + start: 'p:eq(2):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, + { + name: "Click OL: ul ul.o_checklist -> ul ol (from second indented li)", + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom not to edit</p></li><li><p>dom to edit</p></li><li><p>dom not to edit</p></li></ul></ul>', + start: 'li:eq(2) p:contents()[0]->1', + end: 'li:eq(2) p:contents()[0]->5', + do: function () { + $btnOL.mousedown().click(); + }, + test: { + content: '<p>dom not to edit</p><ul><li><p>xxx</p></li><ul class="o_checklist"><li><p>dom not to edit</p></li></ul><ol><li><p>dom to edit</p></li></ol><ul class="o_checklist"><li><p>dom not to edit</p></li></ul></ul>', + start: 'p:eq(3):contents()[0]->1', + end: 'p:eq(3):contents()[0]->5', + }, + }, ]; @@ -2116,16 +2456,20 @@ QUnit.test('Indent/outdent', function (assert) { }); QUnit.test('checklist', function (assert) { - assert.expect(10); + assert.expect(11); return weTestUtils.createWysiwyg({ debug: false, wysiwygOptions: { - + generateOptions: function (options) { + options.toolbar[4][1] = ['ul', 'ol', 'checklist', 'paragraph']; + }, }, }).then(function (wysiwyg) { var $editable = wysiwyg.$('.note-editable'); + var $btnChecklist = wysiwyg.$('.note-para .fa-check-square'); + var checklistTests = [ { name: "check checkbox in checklist with children", @@ -2477,6 +2821,60 @@ QUnit.test('checklist', function (assert) { '<p>y</p>', }, }, + { + name: "convert 2 ul li ul li into two ul li ul.o_checklist li", + content: '<ul>' + + '<li>' + + '<p>1</p>' + + '</li>' + + '<li class="o_indent">' + + '<ul>' + + '<li>' + + '<p>2</p>' + + '</li>' + + '<li>' + + '<p>3</p>' + + '</li>' + + '<li>' + + '<p>4</p>' + + '</li>' + + '</ul>' + + '</li>' + + '<li>' + + '<p>5</p>' + + '</li>' + + '</ul>', + start: 'p:eq(2):contents()[0]->0', + end: 'p:eq(3):contents()[0]->1', + do: function () { + $btnChecklist.mousedown().click(); + }, + test: { + content: '<ul>' + + '<li>' + + '<p>1</p>' + + '</li>' + + '<li class="o_indent">' + + '<ul>' + + '<li>' + + '<p>2</p>' + + '</li>' + + '</ul>' + + '<ul class="o_checklist">' + + '<li>' + + '<p>3</p>' + + '</li>' + + '<li>' + + '<p>4</p>' + + '</li>' + + '</ul>' + + '</li>' + + '<li>' + + '<p>5</p>' + + '</li>' + + '</ul>', + }, + }, ]; var def = Promise.resolve(); @@ -2485,6 +2883,11 @@ QUnit.test('checklist', function (assert) { def = def.then(async function(){ testName = test.name; wysiwyg.setValue(test.content); + if (test.start) { + var range = weTestUtils.select(test.start, test.end || test.start, $editable); + $(range.sc).mousedown(); + Wysiwyg.setRange(range.sc, range.so, range.ec, range.eo); + } await test.do(); assert.deepEqual(wysiwyg.getValue(), test.test.content, testName); }); |
