summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/lib/summernote/src/js/editing
diff options
context:
space:
mode:
authorGorash <chm@odoo.com>2019-08-19 09:51:34 +0000
committerDavid Monjoie <dmo@odoo.com>2019-08-21 18:52:19 +0000
commitf14cecfc0a7f401533213aaa968ee233e19d1325 (patch)
treee037e1372e7d0c426fc067e18a55b9d347faeeaa /addons/web_editor/static/lib/summernote/src/js/editing
parent3dccf0dffd80ac8c4dfa884b55b62a76f23ae341 (diff)
[REV] web_editor: revert summernote update
Summernote was updated alongside the new editor of saas-12.2. However, since we are putting back the editor of 12.0, summernote needs to be downgraded back to the version that was compatible with the 12.0 editor. This is a revert of 17c6d005. Part of PR 35677. Co-authored-by: Nicolas Bayet <nby@odoo.com> Co-authored-by: Antoine Guenet <age@odoo.com> Co-authored-by: Christophe Matthieu <chm@odoo.com> Co-authored-by: David Monjoie <dmo@odoo.com>
Diffstat (limited to 'addons/web_editor/static/lib/summernote/src/js/editing')
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js235
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/History.js118
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Style.js161
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Table.js52
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Typing.js86
5 files changed, 652 insertions, 0 deletions
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js b/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js
new file mode 100644
index 00000000000..6a115edaa90
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js
@@ -0,0 +1,235 @@
+define([
+ 'summernote/core/list',
+ 'summernote/core/func',
+ 'summernote/core/dom',
+ 'summernote/core/range'
+], function (list, func, dom, range) {
+
+ /**
+ * @class editing.Bullet
+ *
+ * @alternateClassName Bullet
+ */
+ var Bullet = function () {
+ /**
+ * @method insertOrderedList
+ *
+ * toggle ordered list
+ *
+ * @type command
+ */
+ this.insertOrderedList = function () {
+ this.toggleList('OL');
+ };
+
+ /**
+ * @method insertUnorderedList
+ *
+ * toggle unordered list
+ *
+ * @type command
+ */
+ this.insertUnorderedList = function () {
+ this.toggleList('UL');
+ };
+
+ /**
+ * @method indent
+ *
+ * indent
+ *
+ * @type command
+ */
+ this.indent = function () {
+ var self = this;
+ var rng = range.create().wrapBodyInlineWithPara();
+
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
+
+ $.each(clustereds, function (idx, paras) {
+ var head = list.head(paras);
+ if (dom.isLi(head)) {
+ self.wrapList(paras, head.parentNode.nodeName);
+ } else {
+ $.each(paras, function (idx, para) {
+ $(para).css('marginLeft', function (idx, val) {
+ return (parseInt(val, 10) || 0) + 25;
+ });
+ });
+ }
+ });
+
+ rng.select();
+ };
+
+ /**
+ * @method outdent
+ *
+ * outdent
+ *
+ * @type command
+ */
+ this.outdent = function () {
+ var self = this;
+ var rng = range.create().wrapBodyInlineWithPara();
+
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
+
+ $.each(clustereds, function (idx, paras) {
+ var head = list.head(paras);
+ if (dom.isLi(head)) {
+ self.releaseList([paras]);
+ } else {
+ $.each(paras, function (idx, para) {
+ $(para).css('marginLeft', function (idx, val) {
+ val = (parseInt(val, 10) || 0);
+ return val > 25 ? val - 25 : '';
+ });
+ });
+ }
+ });
+
+ rng.select();
+ };
+
+ /**
+ * @method toggleList
+ *
+ * toggle list
+ *
+ * @param {String} listName - OL or UL
+ */
+ this.toggleList = function (listName) {
+ var self = this;
+ var rng = range.create().wrapBodyInlineWithPara();
+
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var bookmark = rng.paraBookmark(paras);
+ var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
+
+ // paragraph to list
+ if (list.find(paras, dom.isPurePara)) {
+ var wrappedParas = [];
+ $.each(clustereds, function (idx, paras) {
+ wrappedParas = wrappedParas.concat(self.wrapList(paras, listName));
+ });
+ paras = wrappedParas;
+ // list to paragraph or change list style
+ } else {
+ var diffLists = rng.nodes(dom.isList, {
+ includeAncestor: true
+ }).filter(function (listNode) {
+ return !$.nodeName(listNode, listName);
+ });
+
+ if (diffLists.length) {
+ $.each(diffLists, function (idx, listNode) {
+ dom.replace(listNode, listName);
+ });
+ } else {
+ paras = this.releaseList(clustereds, true);
+ }
+ }
+
+ range.createFromParaBookmark(bookmark, paras).select();
+ };
+
+ /**
+ * @method wrapList
+ *
+ * @param {Node[]} paras
+ * @param {String} listName
+ * @return {Node[]}
+ */
+ this.wrapList = function (paras, listName) {
+ var head = list.head(paras);
+ var last = list.last(paras);
+
+ var prevList = dom.isList(head.previousSibling) && head.previousSibling;
+ var nextList = dom.isList(last.nextSibling) && last.nextSibling;
+
+ var listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
+
+ // P to LI
+ paras = paras.map(function (para) {
+ return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
+ });
+
+ // append to list(<ul>, <ol>)
+ dom.appendChildNodes(listNode, paras);
+
+ if (nextList) {
+ dom.appendChildNodes(listNode, list.from(nextList.childNodes));
+ dom.remove(nextList);
+ }
+
+ return paras;
+ };
+
+ /**
+ * @method releaseList
+ *
+ * @param {Array[]} clustereds
+ * @param {Boolean} isEscapseToBody
+ * @return {Node[]}
+ */
+ this.releaseList = function (clustereds, isEscapseToBody) {
+ var releasedParas = [];
+
+ $.each(clustereds, function (idx, paras) {
+ var head = list.head(paras);
+ var last = list.last(paras);
+
+ var headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) :
+ head.parentNode;
+ var lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
+ node: last.parentNode,
+ offset: dom.position(last) + 1
+ }, {
+ isSkipPaddingBlankHTML: true
+ }) : null;
+
+ var middleList = dom.splitTree(headList, {
+ node: head.parentNode,
+ offset: dom.position(head)
+ }, {
+ isSkipPaddingBlankHTML: true
+ });
+
+ paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi) :
+ list.from(middleList.childNodes).filter(dom.isLi);
+
+ // LI to P
+ if (isEscapseToBody || !dom.isList(headList.parentNode)) {
+ paras = paras.map(function (para) {
+ return dom.replace(para, 'P');
+ });
+ }
+
+ $.each(list.from(paras).reverse(), function (idx, para) {
+ dom.insertAfter(para, headList);
+ });
+
+ // remove empty lists
+ var rootLists = list.compact([headList, middleList, lastList]);
+ $.each(rootLists, function (idx, rootList) {
+ var listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
+ $.each(listNodes.reverse(), function (idx, listNode) {
+ if (!dom.nodeLength(listNode)) {
+ dom.remove(listNode, true);
+ }
+ });
+ });
+
+ releasedParas = releasedParas.concat(paras);
+ });
+
+ return releasedParas;
+ };
+ };
+
+ return Bullet;
+});
+
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/History.js b/addons/web_editor/static/lib/summernote/src/js/editing/History.js
new file mode 100644
index 00000000000..2cc946488c2
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/editing/History.js
@@ -0,0 +1,118 @@
+define(['summernote/core/range', 'summernote/core/dom'], function (range, dom) { // ODOO: suggest upstream
+ /**
+ * @class editing.History
+ *
+ * Editor History
+ *
+ */
+ var History = function ($editable) {
+ var stack = [], stackOffset = -1;
+ var editable = $editable[0];
+
+ var makeSnapshot = function () {
+ var rng = range.create();
+ var emptyBookmark = {s: {path: [], offset: 0}, e: {path: [], offset: 0}};
+
+ return {
+ contents: $editable.html(),
+ bookmark: (rng && dom.ancestor(rng.sc, dom.isEditable) ? rng.bookmark(editable) : emptyBookmark)
+ // ODOO: suggest upstream added " && dom.ancestor(rng.sc, dom.isEditable) "
+ };
+ };
+
+ var applySnapshot = function (snapshot) {
+ if (snapshot.contents !== null) {
+ $editable.html(snapshot.contents);
+ }
+ if (snapshot.bookmark !== null) {
+ range.createFromBookmark(editable, snapshot.bookmark).select();
+ }
+ };
+
+ /**
+ * undo
+ */
+ this.undo = function () {
+ // Create snap shot if not yet recorded
+ if ($editable.html() !== stack[stackOffset].contents) {
+ this.recordUndo();
+ }
+
+ if (0 < stackOffset) {
+ stackOffset--;
+ applySnapshot(stack[stackOffset]);
+ }
+ };
+
+ /* ODOO: to suggest upstream */
+ this.hasUndo = function () {
+ return 0 < stackOffset;
+ };
+
+ /**
+ * redo
+ */
+ this.redo = function () {
+ if (stack.length - 1 > stackOffset) {
+ stackOffset++;
+ applySnapshot(stack[stackOffset]);
+ }
+ };
+
+ /* ODOO: to suggest upstream */
+ this.hasRedo = function () {
+ return stack.length - 1 > stackOffset;
+ };
+
+ var last; // ODOO: to suggest upstream (since we may have several editor)
+ /**
+ * recorded undo
+ */
+ this.recordUndo = function () {
+ // ODOO: method totally rewritten
+ // test event for firefox: remove stack of history because event doesn't exists
+ var key = typeof event !== 'undefined' ? event : false;
+ if (key && !event.metaKey && !event.ctrlKey && !event.altKey && event.type === "keydown") {
+ key = event.type + "-";
+ if (event.which === 8 || event.which === 46) key += 'delete';
+ else if (event.which === 13) key += 'enter';
+ else key += 'other';
+ if (key === last) return;
+ hasUndo = true;
+ }
+ last = key;
+
+ // Wash out stack after stackOffset
+ if (stack.length > stackOffset+1) {
+ stack = stack.slice(0, stackOffset+1);
+ }
+
+ if (stack[stackOffset] && stack[stackOffset].contents === $editable.html()) {
+ return;
+ }
+
+ stackOffset++;
+
+ // Create new snapshot and push it to the end
+ stack.push(makeSnapshot());
+ };
+
+ /* ODOO: to suggest upstream */
+ this.splitNext = function () {
+ last = false;
+ };
+
+ /* ODOO: to suggest upstream */
+ this.reset = function () {
+ last = false;
+ stack = [];
+ stackOffset = -1;
+ this.recordUndo();
+ };
+
+ // Create first undo stack
+ this.recordUndo();
+ };
+
+ return History;
+});
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Style.js b/addons/web_editor/static/lib/summernote/src/js/editing/Style.js
new file mode 100644
index 00000000000..bf749cd817b
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/editing/Style.js
@@ -0,0 +1,161 @@
+define([
+ 'summernote/core/agent',
+ 'summernote/core/func',
+ 'summernote/core/list',
+ 'summernote/core/dom'
+], function (agent, func, list, dom) {
+ /**
+ * @class editing.Style
+ *
+ * Style
+ *
+ */
+ var Style = function () {
+ /**
+ * @method jQueryCSS
+ *
+ * [workaround] for old jQuery
+ * passing an array of style properties to .css()
+ * will result in an object of property-value pairs.
+ * (compability with version < 1.9)
+ *
+ * @private
+ * @param {jQuery} $obj
+ * @param {Array} propertyNames - An array of one or more CSS properties.
+ * @return {Object}
+ */
+ var jQueryCSS = function ($obj, propertyNames) {
+ if (agent.jqueryVersion < 1.9) {
+ var result = {};
+ $.each(propertyNames, function (idx, propertyName) {
+ result[propertyName] = $obj.css(propertyName);
+ });
+ return result;
+ }
+ return $obj.css.call($obj, propertyNames);
+ };
+
+ /**
+ * returns style object from node
+ *
+ * @param {jQuery} $node
+ * @return {Object}
+ */
+ this.fromNode = function ($node) {
+ var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
+ var styleInfo = jQueryCSS($node, properties) || {};
+ styleInfo['font-size'] = parseInt(styleInfo['font-size'], 10);
+ return styleInfo;
+ };
+
+ /**
+ * paragraph level style
+ *
+ * @param {WrappedRange} rng
+ * @param {Object} styleInfo
+ */
+ this.stylePara = function (rng, styleInfo) {
+ $.each(rng.nodes(dom.isPara, {
+ includeAncestor: true
+ }), function (idx, para) {
+ $(para).css(styleInfo);
+ });
+ };
+
+ /**
+ * insert and returns styleNodes on range.
+ *
+ * @param {WrappedRange} rng
+ * @param {Object} [options] - options for styleNodes
+ * @param {String} [options.nodeName] - default: `SPAN`
+ * @param {Boolean} [options.expandClosestSibling] - default: `false`
+ * @param {Boolean} [options.onlyPartialContains] - default: `false`
+ * @return {Node[]}
+ */
+ this.styleNodes = function (rng, options) {
+ rng = rng.splitText();
+
+ var nodeName = options && options.nodeName || 'SPAN';
+ var expandClosestSibling = !!(options && options.expandClosestSibling);
+ var onlyPartialContains = !!(options && options.onlyPartialContains);
+
+ if (rng.isCollapsed()) {
+ return [rng.insertNode(dom.create(nodeName))];
+ }
+
+ var pred = dom.makePredByNodeName(nodeName);
+ var nodes = rng.nodes(dom.isText, {
+ fullyContains: true
+ }).map(function (text) {
+ return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
+ });
+
+ if (expandClosestSibling) {
+ if (onlyPartialContains) {
+ var nodesInRange = rng.nodes();
+ // compose with partial contains predication
+ pred = func.and(pred, function (node) {
+ return list.contains(nodesInRange, node);
+ });
+ }
+
+ return nodes.map(function (node) {
+ var siblings = dom.withClosestSiblings(node, pred);
+ var head = list.head(siblings);
+ var tails = list.tail(siblings);
+ $.each(tails, function (idx, elem) {
+ dom.appendChildNodes(head, elem.childNodes);
+ dom.remove(elem);
+ });
+ return list.head(siblings);
+ });
+ } else {
+ return nodes;
+ }
+ };
+
+ /**
+ * get current style on cursor
+ *
+ * @param {WrappedRange} rng
+ * @return {Object} - object contains style properties.
+ */
+ this.current = function (rng) {
+ var $cont = $(dom.isText(rng.sc) ? rng.sc.parentNode : rng.sc);
+ var styleInfo = this.fromNode($cont);
+
+ // document.queryCommandState for toggle state
+ styleInfo['font-bold'] = document.queryCommandState('bold') ? 'bold' : 'normal';
+ styleInfo['font-italic'] = document.queryCommandState('italic') ? 'italic' : 'normal';
+ styleInfo['font-underline'] = document.queryCommandState('underline') ? 'underline' : 'normal';
+ styleInfo['font-strikethrough'] = document.queryCommandState('strikeThrough') ? 'strikethrough' : 'normal';
+ styleInfo['font-superscript'] = document.queryCommandState('superscript') ? 'superscript' : 'normal';
+ styleInfo['font-subscript'] = document.queryCommandState('subscript') ? 'subscript' : 'normal';
+
+ // list-style-type to list-style(unordered, ordered)
+ if (!rng.isOnList()) {
+ styleInfo['list-style'] = 'none';
+ } else {
+ var aOrderedType = ['circle', 'disc', 'disc-leading-zero', 'square'];
+ var isUnordered = $.inArray(styleInfo['list-style-type'], aOrderedType) > -1;
+ styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
+ }
+
+ var para = dom.ancestor(rng.sc, dom.isPara);
+ if (para && para.style['line-height']) {
+ styleInfo['line-height'] = para.style.lineHeight;
+ } else {
+ var lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
+ styleInfo['line-height'] = lineHeight.toFixed(1);
+ }
+
+ styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
+ styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
+ styleInfo.range = rng;
+
+ return styleInfo;
+ };
+ };
+
+ return Style;
+});
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Table.js b/addons/web_editor/static/lib/summernote/src/js/editing/Table.js
new file mode 100644
index 00000000000..634d5a79d7e
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/editing/Table.js
@@ -0,0 +1,52 @@
+define([
+ 'summernote/core/dom', 'summernote/core/range', 'summernote/core/list'
+], function (dom, range, list) {
+ /**
+ * @class editing.Table
+ *
+ * Table
+ *
+ */
+ var Table = function () {
+ /**
+ * handle tab key
+ *
+ * @param {WrappedRange} rng
+ * @param {Boolean} isShift
+ */
+ this.tab = function (rng, isShift) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var table = dom.ancestor(cell, dom.isTable);
+ var cells = dom.listDescendant(table, dom.isCell);
+
+ var nextCell = list[isShift ? 'prev' : 'next'](cells, cell);
+ if (nextCell) {
+ range.create(nextCell, 0).select();
+ }
+ };
+
+ /**
+ * create empty table element
+ *
+ * @param {Number} rowCount
+ * @param {Number} colCount
+ * @return {Node}
+ */
+ this.createTable = function (colCount, rowCount) {
+ var tds = [], tdHTML;
+ for (var idxCol = 0; idxCol < colCount; idxCol++) {
+ tds.push('<td>' + dom.blank + '</td>');
+ }
+ tdHTML = tds.join('');
+
+ var trs = [], trHTML;
+ for (var idxRow = 0; idxRow < rowCount; idxRow++) {
+ trs.push('<tr>' + tdHTML + '</tr>');
+ }
+ trHTML = trs.join('');
+ return $('<table class="table table-bordered">' + trHTML + '</table>')[0];
+ };
+ };
+ return Table;
+});
+
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js b/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js
new file mode 100644
index 00000000000..f95eba9e9f3
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js
@@ -0,0 +1,86 @@
+define([
+ 'summernote/core/dom',
+ 'summernote/core/range',
+ 'summernote/editing/Bullet'
+], function (dom, range, Bullet) {
+
+ /**
+ * @class editing.Typing
+ *
+ * Typing
+ *
+ */
+ var Typing = function () {
+
+ // a Bullet instance to toggle lists off
+ var bullet = new Bullet();
+
+ /**
+ * insert tab
+ *
+ * @param {jQuery} $editable
+ * @param {WrappedRange} rng
+ * @param {Number} tabsize
+ */
+ this.insertTab = function ($editable, rng, tabsize) {
+ var tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
+ rng = rng.deleteContents();
+ rng.insertNode(tab, true);
+
+ rng = range.create(tab, tabsize);
+ rng.select();
+ };
+
+ /**
+ * insert paragraph
+ */
+ this.insertParagraph = function () {
+ var rng = range.create();
+
+ // deleteContents on range.
+ rng = rng.deleteContents();
+
+ // Wrap range if it needs to be wrapped by paragraph
+ rng = rng.wrapBodyInlineWithPara();
+
+ // finding paragraph
+ var splitRoot = dom.ancestor(rng.sc, dom.isPara);
+
+ var nextPara;
+ // on paragraph: split paragraph
+ if (splitRoot) {
+ // if it is an empty line with li
+ if (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
+ // disable UL/OL and escape!
+ bullet.toggleList(splitRoot.parentNode.nodeName);
+ return;
+ // if new line has content (not a line break)
+ } else {
+ nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
+
+ var emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
+ emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
+
+ $.each(emptyAnchors, function (idx, anchor) {
+ dom.remove(anchor);
+ });
+ }
+ // no paragraph: insert empty paragraph
+ } else {
+ var next = rng.sc.childNodes[rng.so];
+ nextPara = $(dom.emptyPara)[0];
+ if (next) {
+ rng.sc.insertBefore(nextPara, next);
+ } else {
+ rng.sc.appendChild(nextPara);
+ }
+ }
+
+ range.create(nextPara, 0).normalize().select();
+
+ };
+
+ };
+
+ return Typing;
+});