summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src/js/editor/summernote.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web_editor/static/src/js/editor/summernote.js')
-rw-r--r--addons/web_editor/static/src/js/editor/summernote.js2436
1 files changed, 0 insertions, 2436 deletions
diff --git a/addons/web_editor/static/src/js/editor/summernote.js b/addons/web_editor/static/src/js/editor/summernote.js
deleted file mode 100644
index 36f5becce06..00000000000
--- a/addons/web_editor/static/src/js/editor/summernote.js
+++ /dev/null
@@ -1,2436 +0,0 @@
-odoo.define('web_editor.summernote', function (require) {
-'use strict';
-
-var core = require('web.core');
-require('summernote/summernote'); // wait that summernote is loaded
-
-var _t = core._t;
-
-// Summernote Lib (neek hack to make accessible: method and object)
-// var agent = $.summernote.core.agent;
-var dom = $.summernote.core.dom;
-var range = $.summernote.core.range;
-var list = $.summernote.core.list;
-var key = $.summernote.core.key;
-var eventHandler = $.summernote.eventHandler;
-var editor = eventHandler.modules.editor;
-var renderer = $.summernote.renderer;
-var options = $.summernote.options;
-
-// Browser-unify execCommand
-var oldJustify = {};
-_.each(['Left', 'Right', 'Full', 'Center'], function (align) {
- oldJustify[align] = editor['justify' + align];
- editor['justify' + align] = function ($editable, value) {
- // Before calling the standard function, check all elements which have
- // an 'align' attribute and mark them with their value
- var $align = $editable.find('[align]');
- _.each($align, function (el) {
- var $el = $(el);
- $el.data('__align', $el.attr('align'));
- });
-
- // Call the standard function
- oldJustify[align].apply(this, arguments);
-
- // Then:
-
- // Remove the text-align of elements which lost the 'align' attribute
- var $newAlign = $editable.find('[align]');
- $align.not($newAlign).css('text-align', '');
-
- // Transform the 'align' attribute into the 'text-align' css
- // property for elements which received the 'align' attribute or whose
- // 'align' attribute changed
- _.each($newAlign, function (el) {
- var $el = $(el);
-
- var oldAlignValue = $align.data('__align');
- var alignValue = $el.attr('align');
- if (oldAlignValue === alignValue) {
- // If the element already had an 'align' attribute and that it
- // did not changed, do nothing (compatibility)
- return;
- }
-
- $el.removeAttr('align');
- $el.css('text-align', alignValue);
-
- // Note the first step (removing the text-align of elemnts which
- // lost the 'align' attribute) is kinda the same as this one, but
- // this one handles the elements which have been edited with chrome
- // or with this new system
- $el.find('*').css('text-align', '');
- });
-
- // Unmark the elements
- $align.removeData('__align');
- };
-});
-
-
-// Add methods to summernote
-
-dom.hasContentAfter = function (node) {
- var next;
- if (dom.isEditable(node)) return;
- while (node.nextSibling) {
- next = node.nextSibling;
- if (next.tagName || dom.isVisibleText(next) || dom.isBR(next)) return next;
- node = next;
- }
-};
-dom.hasContentBefore = function (node) {
- var prev;
- if (dom.isEditable(node)) return;
- while (node.previousSibling) {
- prev = node.previousSibling;
- if (prev.tagName || dom.isVisibleText(prev) || dom.isBR(prev)) return prev;
- node = prev;
- }
-};
-dom.ancestorHaveNextSibling = function (node, pred) {
- pred = pred || dom.hasContentAfter;
- while (!dom.isEditable(node) && (!node.nextSibling || !pred(node))) { node = node.parentNode; }
- return node;
-};
-dom.ancestorHavePreviousSibling = function (node, pred) {
- pred = pred || dom.hasContentBefore;
- while (!dom.isEditable(node) && (!node.previousSibling || !pred(node))) { node = node.parentNode; }
- return node;
-};
-dom.nextElementSibling = function (node) {
- while (node) {
- node = node.nextSibling;
- if (node && node.tagName) {
- break;
- }
- }
- return node;
-};
-dom.previousElementSibling = function (node) {
- while (node) {
- node = node.previousSibling;
- if (node && node.tagName) {
- break;
- }
- }
- return node;
-};
-dom.lastChild = function (node) {
- while (node.lastChild) { node = node.lastChild; }
- return node;
-};
-dom.firstChild = function (node) {
- while (node.firstChild) { node = node.firstChild; }
- return node;
-};
-dom.lastElementChild = function (node, deep) {
- node = deep ? dom.lastChild(node) : node.lastChild;
- return !node || node.tagName ? node : dom.previousElementSibling(node);
-};
-dom.firstElementChild = function (node, deep) {
- node = deep ? dom.firstChild(node) : node.firstChild;
- return !node || node.tagName ? node : dom.nextElementSibling(node);
-};
-dom.isEqual = function (prev, cur) {
- if (prev.tagName !== cur.tagName) {
- return false;
- }
- if ((prev.attributes ? prev.attributes.length : 0) !== (cur.attributes ? cur.attributes.length : 0)) {
- return false;
- }
-
- function strip(text) {
- return text && text.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
- }
- var att, att2;
- loop_prev:
- for (var a in prev.attributes) {
- att = prev.attributes[a];
- for (var b in cur.attributes) {
- att2 = cur.attributes[b];
- if (att.name === att2.name) {
- if (strip(att.value) !== strip(att2.value)) return false;
- continue loop_prev;
- }
- }
- return false;
- }
- return true;
-};
-dom.hasOnlyStyle = function (node) {
- for (var i = 0; i < node.attributes.length; i++) {
- var attr = node.attributes[i];
- if (attr.attributeName !== 'style') {
- return false;
- }
- }
- return true;
-};
-dom.hasProgrammaticStyle = function (node) {
- var styles = ["float", "display", "position", "top", "left", "right", "bottom"];
- for (var i = 0; i < node.style.length; i++) {
- var style = node.style[i];
- if (styles.indexOf(style) !== -1) {
- return true;
- }
- }
- return false;
-};
-dom.mergeFilter = function (prev, cur, parent) {
- // merge text nodes
- if (prev && (dom.isText(prev) || (['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'P'].indexOf(prev.tagName) !== -1 && prev !== cur.parentNode)) && dom.isText(cur)) {
- return true;
- }
- if (prev && prev.tagName === "P" && dom.isText(cur)) {
- return true;
- }
- if (prev && dom.isText(cur) && !dom.isVisibleText(cur) && (dom.isText(prev) || dom.isVisibleText(prev))) {
- return true;
- }
- if (prev && !dom.isBR(prev) && dom.isEqual(prev, cur) &&
- ((prev.tagName && dom.getComputedStyle(prev).display === "inline" &&
- cur.tagName && dom.getComputedStyle(cur).display === "inline"))) {
- return true;
- }
- if (dom.isEqual(parent, cur) &&
- ((parent.tagName && dom.getComputedStyle(parent).display === "inline" &&
- cur.tagName && dom.getComputedStyle(cur).display === "inline"))) {
- return true;
- }
- if (parent && cur.tagName === "FONT" && (!cur.firstChild || (!cur.attributes.getNamedItem('style') && !cur.className.length))) {
- return true;
- }
- // On backspace, webkit browsers create a <span> with a bunch of
- // inline styles "remembering" where they come from.
- // chances are we had e.g.
- // <p>foo</p>
- // <p>bar</p>
- // merged the lines getting this in webkit
- // <p>foo<span>bar</span></p>
- if (parent && cur.tagName === "SPAN" && dom.hasOnlyStyle(cur) && !dom.hasProgrammaticStyle(cur)) {
- return true;
- }
-};
-dom.doMerge = function (prev, cur) {
- if (prev.tagName) {
- if (prev.childNodes.length && !prev.textContent.match(/\S/) && dom.firstElementChild(prev) && dom.isBR(dom.firstElementChild(prev))) {
- prev.removeChild(dom.firstElementChild(prev));
- }
- if (cur.tagName) {
- while (cur.firstChild) {
- prev.appendChild(cur.firstChild);
- }
- cur.parentNode.removeChild(cur);
- } else {
- prev.appendChild(cur);
- }
- } else {
- if (cur.tagName) {
- var deep = cur;
- while (deep.tagName && deep.firstChild) {deep = deep.firstChild;}
- prev.appendData(deep.textContent);
- cur.parentNode.removeChild(cur);
- } else {
- prev.appendData(cur.textContent);
- cur.parentNode.removeChild(cur);
- }
- }
-};
-dom.merge = function (node, begin, so, end, eo, mergeFilter, all) {
- mergeFilter = mergeFilter || dom.mergeFilter;
- var _merged = false;
- var add = all || false;
-
- if (!begin) {
- begin = node;
- while (begin.firstChild) {begin = begin.firstChild;}
- so = 0;
- } else if (begin.tagName && begin.childNodes[so]) {
- begin = begin.childNodes[so];
- so = 0;
- }
- if (!end) {
- end = node;
- while (end.lastChild) {end = end.lastChild;}
- eo = end.textContent.length-1;
- } else if (end.tagName && end.childNodes[so]) {
- end = end.childNodes[so];
- so = 0;
- }
-
- begin = dom.firstChild(begin);
- if (dom.isText(begin) && so > begin.textContent.length) {
- so = 0;
- }
- end = dom.firstChild(end);
- if (dom.isText(end) && eo > end.textContent.length) {
- eo = 0;
- }
-
- function __merge(node) {
- var merged = false;
- var prev;
- for (var k=0; k<node.childNodes.length; k++) {
- var cur = node.childNodes[k];
-
- if (cur === begin) {
- if (!all) add = true;
- }
-
- __merge(cur);
- dom.orderClass(dom.node(cur));
-
- if (!add || !cur) continue;
- if (cur === end) {
- if (!all) add = false;
- }
-
- // create the first prev value
- if (!prev) {
- if (mergeFilter.call(dom, prev, cur, node)) {
- prev = prev || cur.previousSibling;
- dom.moveTo(cur, cur.parentNode, cur);
- k--;
- } else {
- prev = cur;
- }
- continue;
- } else if (mergeFilter.call(dom, null, cur, node)) { // merge with parent
- prev = prev || cur.previousSibling;
- dom.moveTo(cur, cur.parentNode, cur);
- k--;
- continue;
- }
-
- // merge nodes
- if (mergeFilter.call(dom, prev, cur, node)) {
- var p = prev;
- var c = cur;
- // compute prev/end and offset
- if (prev.tagName) {
- if (cur.tagName) {
- if (cur === begin) begin = prev;
- if (cur === end) end = prev;
- }
- } else {
- if (cur.tagName) {
- var deep = cur;
- while (deep.tagName && deep.lastChild) {deep = deep.lastChild;}
- if (deep === begin) {
- so += prev.textContent.length;
- begin = prev;
- }
- if (deep === end) {
- eo += prev.textContent.length;
- end = prev;
- }
- } else {
- // merge text nodes
- if (cur === begin) {
- so += prev.textContent.length;
- begin = prev;
- }
- if (cur === end) {
- eo += prev.textContent.length;
- end = prev;
- }
- }
- }
-
- dom.doMerge(p, c);
-
- merged = true;
- k--;
- continue;
- }
-
- prev = cur;
- }
-
- // an other loop to merge the new shibbing nodes
- if (merged) {
- _merged = true;
- __merge(node);
- }
- }
- if (node) {
- __merge(node);
- }
-
- return {
- merged: _merged,
- sc: begin,
- ec: end,
- so: so,
- eo: eo
- };
-};
-dom.autoMerge = function (target, previous) {
- var node = dom.lastChild(target);
- var nodes = [];
- var temp;
-
- while (node) {
- nodes.push(node);
- temp = (previous ? dom.hasContentBefore(node) : dom.hasContentAfter(node));
- if (temp) {
- if (!dom.isText(node) && !dom.isMergable(node) && temp.tagName !== node.tagName) {
- nodes = [];
- }
- break;
- }
- node = node.parentNode;
- }
-
- while (nodes.length) {
- node = nodes.pop();
- if (node && (temp = (previous ? dom.hasContentBefore(node) : dom.hasContentAfter(node))) &&
- temp.tagName === node.tagName &&
- !dom.isText(node) &&
- dom.isMergable(node) &&
- !dom.isNotBreakable(node) && !dom.isNotBreakable(previous ? dom.previousElementSibling(node) : dom.nextElementSibling(node))) {
-
- if (previous) {
- dom.doMerge(temp, node);
- } else {
- dom.doMerge(node, temp);
- }
- }
- }
-};
-dom.removeSpace = function (node, begin, so, end, eo) {
- var removed = false;
- var add = node === begin;
-
- if (node === begin && begin === end && dom.isBR(node)) {
- return {
- removed: removed,
- sc: begin,
- ec: end,
- so: so,
- eo: eo
- };
- }
-
- (function __remove_space(node) {
- if (!node) return;
- var t_begin, t_end;
- for (var k=0; k<node.childNodes.length; k++) {
- var cur = node.childNodes[k];
-
- if (cur === begin) add = true;
-
- if (cur.tagName && cur.tagName !== "SCRIPT" && cur.tagName !== "STYLE" && dom.getComputedStyle(cur).whiteSpace !== "pre") {
- __remove_space(cur);
- }
-
- if (!add) continue;
- if (cur === end) add = false;
-
- // remove begin empty text node
- if (node.childNodes.length > 1 && dom.isText(cur) && !dom.isVisibleText(cur)) {
- removed = true;
- if (cur === begin) {
- t_begin = dom.hasContentBefore(dom.ancestorHavePreviousSibling(cur));
- if (t_begin) {
- so = 0;
- begin = dom.lastChild(t_begin);
- }
- }
- if (cur === end) {
- t_end = dom.hasContentAfter(dom.ancestorHaveNextSibling(cur));
- if (t_end) {
- eo = 1;
- end = dom.firstChild(t_end);
- if (dom.isText(end)) {
- eo = end.textContent.length;
- }
- }
- }
- cur.parentNode.removeChild(cur);
- begin = dom.lastChild(begin);
- end = dom.lastChild(end);
- k--;
- continue;
- }
-
- // convert HTML space
- if (dom.isText(cur)) {
- var text;
- var temp;
- var _temp;
- var exp1 = /[\t\n\r ]+/g;
- var exp2 = /(?!([ ]|\u00A0)|^)\u00A0(?!([ ]|\u00A0)|$)/g;
- if (cur === begin) {
- temp = cur.textContent.substr(0, so);
- _temp = temp.replace(exp1, ' ').replace(exp2, ' ');
- so -= temp.length - _temp.length;
- }
- if (cur === end) {
- temp = cur.textContent.substr(0, eo);
- _temp = temp.replace(exp1, ' ').replace(exp2, ' ');
- eo -= temp.length - _temp.length;
- }
- text = cur.textContent.replace(exp1, ' ').replace(exp2, ' ');
- removed = removed || cur.textContent.length !== text.length;
- cur.textContent = text;
- }
- }
- })(node);
-
- return {
- removed: removed,
- sc: begin,
- ec: end,
- so: !dom.isBR(begin) && so > 0 ? so : 0,
- eo: dom.isBR(end) ? 0 : eo
- };
-};
-dom.removeBetween = function (sc, so, ec, eo, towrite) {
- var text;
- if (ec.tagName) {
- if (ec.childNodes[eo]) {
- ec = ec.childNodes[eo];
- eo = 0;
- } else {
- ec = dom.lastChild(ec);
- eo = dom.nodeLength(ec);
- }
- }
- if (sc.tagName) {
- sc = sc.childNodes[so] || dom.firstChild(ec);
- so = 0;
- if (!dom.hasContentBefore(sc) && towrite) {
- sc.parentNode.insertBefore(document.createTextNode('\u00A0'), sc);
- }
- }
- if (!eo && sc !== ec) {
- ec = dom.lastChild(dom.hasContentBefore(dom.ancestorHavePreviousSibling(ec)) || ec);
- eo = ec.textContent.length;
- }
-
- var ancestor = dom.commonAncestor(sc.tagName ? sc.parentNode : sc, ec.tagName ? ec.parentNode : ec) || dom.ancestor(sc, dom.isEditable);
-
- if (!dom.isContentEditable(ancestor)) {
- return {
- sc: sc,
- so: so,
- ec: sc,
- eo: eo
- };
- }
-
- if (ancestor.tagName) {
- var ancestor_sc = sc;
- var ancestor_ec = ec;
- while (ancestor !== ancestor_sc && ancestor !== ancestor_sc.parentNode) { ancestor_sc = ancestor_sc.parentNode; }
- while (ancestor !== ancestor_ec && ancestor !== ancestor_ec.parentNode) { ancestor_ec = ancestor_ec.parentNode; }
-
-
- var node = dom.node(sc);
- if (!dom.isNotBreakable(node) && !dom.isVoid(sc)) {
- sc = dom.splitTree(ancestor_sc, {'node': sc, 'offset': so});
- }
- var before = dom.hasContentBefore(dom.ancestorHavePreviousSibling(sc));
-
- var after;
- if (ec.textContent.slice(eo, Infinity).match(/\S|\u00A0/)) {
- after = dom.splitTree(ancestor_ec, {'node': ec, 'offset': eo});
- } else {
- after = dom.hasContentAfter(dom.ancestorHaveNextSibling(ec));
- }
-
- var nodes = dom.listBetween(sc, ec);
-
- var ancestor_first_last = function (node) {
- return node === before || node === after;
- };
-
- for (var i=0; i<nodes.length; i++) {
- if (!dom.ancestor(nodes[i], ancestor_first_last) && !$.contains(nodes[i], before) && !$.contains(nodes[i], after) && !dom.isEditable(nodes[i])) {
- nodes[i].parentNode.removeChild(nodes[i]);
- }
- }
-
- if (dom.listAncestor(after).length <= dom.listAncestor(before).length) {
- sc = dom.lastChild(before || ancestor);
- so = dom.nodeLength(sc);
- } else {
- sc = dom.firstChild(after);
- so = 0;
- }
-
- if (dom.isVoid(node)) {
- // we don't need to append a br
- } else if (towrite && !node.firstChild && node.parentNode && !dom.isNotBreakable(node)) {
- var br = $("<br/>")[0];
- node.appendChild(sc);
- sc = br;
- so = 0;
- } else if (!ancestor.children.length && !ancestor.textContent.match(/\S|\u00A0/)) {
- sc = $("<br/>")[0];
- so = 0;
- $(ancestor).prepend(sc);
- } else if (dom.isText(sc)) {
- text = sc.textContent.replace(/[ \t\n\r]+$/, '\u00A0');
- so = Math.min(so, text.length);
- sc.textContent = text;
- }
- } else {
- text = ancestor.textContent;
- ancestor.textContent = text.slice(0, so) + text.slice(eo, Infinity).replace(/^[ \t\n\r]+/, '\u00A0');
- }
-
- eo = so;
- if (!dom.isBR(sc) && !dom.isVisibleText(sc) && !dom.isText(dom.hasContentBefore(sc)) && !dom.isText(dom.hasContentAfter(sc))) {
- ancestor = dom.node(sc);
- text = document.createTextNode('\u00A0');
- $(sc).before(text);
- sc = text;
- so = 0;
- eo = 1;
- }
-
- var parentNode = sc && sc.parentNode;
- if (parentNode && sc.tagName === 'BR') {
- sc = parentNode;
- ec = parentNode;
- }
-
- return {
- sc: sc,
- so: so,
- ec: sc,
- eo: eo
- };
-};
-dom.indent = function (node) {
- var style = dom.isCell(node) ? 'paddingLeft' : 'marginLeft';
- var margin = parseFloat(node.style[style] || 0)+1.5;
- node.style[style] = margin + "em";
- return margin;
-};
-dom.outdent = function (node) {
- var style = dom.isCell(node) ? 'paddingLeft' : 'marginLeft';
- var margin = parseFloat(node.style[style] || 0)-1.5;
- node.style[style] = margin > 0 ? margin + "em" : "";
- return margin;
-};
-dom.scrollIntoViewIfNeeded = function (node) {
- node = dom.node(node);
-
- var $span;
- if (dom.isBR(node)) {
- $span = $('<span/>').text('\u00A0');
- $(node).after($span);
- node = $span[0];
- }
-
- if (node.scrollIntoViewIfNeeded) {
- node.scrollIntoViewIfNeeded(false);
- } else {
- var offsetParent = node.offsetParent;
- while (offsetParent) {
- var elY = 0;
- var elH = node.offsetHeight;
- var parent = node;
-
- while (offsetParent && parent) {
- elY += node.offsetTop;
-
- // get if a parent have a scrollbar
- parent = node.parentNode;
- while (parent !== offsetParent &&
- (parent.tagName === "BODY" || ["auto", "scroll"].indexOf(dom.getComputedStyle(parent).overflowY) === -1)) {
- parent = parent.parentNode;
- }
- node = parent;
-
- if (parent !== offsetParent) {
- elY -= parent.offsetTop;
- parent = null;
- }
-
- offsetParent = node.offsetParent;
- }
-
- if ((node.tagName === "BODY" || ["auto", "scroll"].indexOf(dom.getComputedStyle(node).overflowY) !== -1) &&
- (node.scrollTop + node.clientHeight) < (elY + elH)) {
- node.scrollTop = (elY + elH) - node.clientHeight;
- }
- }
- }
-
- if ($span) {
- $span.remove();
- }
-
- return;
-};
-dom.moveTo = function (node, target, before) {
- var nodes = [];
- while (node.firstChild) {
- nodes.push(node.firstChild);
- if (before) {
- target.insertBefore(node.firstChild, before);
- } else {
- target.appendChild(node.firstChild);
- }
- }
- node.parentNode.removeChild(node);
- return nodes;
-};
-dom.isMergable = function (node) {
- return node.tagName && "h1 h2 h3 h4 h5 h6 p b bold i u code sup strong small li a ul ol font".indexOf(node.tagName.toLowerCase()) !== -1;
-};
-dom.isSplitable = function (node) {
- return node.tagName && "h1 h2 h3 h4 h5 h6 p b bold i u code sup strong small li a font".indexOf(node.tagName.toLowerCase()) !== -1;
-};
-dom.isRemovableEmptyNode = function (node) {
- return "h1 h2 h3 h4 h5 h6 p b bold i u code sup strong small li a ul ol font span br".indexOf(node.tagName.toLowerCase()) !== -1;
-};
-dom.isForbiddenNode = function (node) {
- return node.tagName === "BR" || $(node).is(".fa, img");
-};
-/**
- * @todo 'so' and 'eo' were added as a bugfix and are not given everytime. They
- * however should be as the function may be wrong without them (for example,
- * when asking the list between an element and its parent, as there is no path
- * from the beginning of the former to the beginning of the later).
- */
-dom.listBetween = function (sc, ec, so, eo) {
- var nodes = [];
- var ancestor = dom.commonAncestor(sc, ec);
- dom.walkPoint({'node': sc, 'offset': so || 0}, {'node': ec, 'offset': eo || 0}, function (point) {
- if (ancestor !== point.node || ancestor === sc || ancestor === ec) {
- nodes.push(point.node);
- }
- });
- return list.unique(nodes);
-};
-dom.isNotBreakable = function (node) {
- // avoid triple click => crappy dom
- return !dom.isText(node) && !dom.isBR(dom.firstChild(node)) && dom.isVoid(dom.firstChild(node));
-};
-dom.isContentEditable = function (node) {
- return $(node).closest('[contenteditable]').prop('contenteditable') === 'true';
-};
-dom.isContentEditableFalse = function (node) {
- return $(node).closest('[contenteditable]').prop('contenteditable') === 'false';
-};
-dom.isFont = function (node) {
- var nodeName = node && node.nodeName.toUpperCase();
- return node && (nodeName === "FONT" ||
- (nodeName === "SPAN" && (
- node.className.match(/(^|\s)fa(\s|$)/i) ||
- node.className.match(/(^|\s)(text|bg)-/i) ||
- (node.attributes.style && node.attributes.style.value.match(/(^|\s)(color|background-color|font-size):/i)))) );
-};
-dom.isVisibleText = function (textNode) {
- return !!textNode.textContent.match(/\S|\u00A0/);
-};
-var old_isVisiblePoint = dom.isVisiblePoint;
-dom.isVisiblePoint = function (point) {
- return point.node.nodeType !== 8 && old_isVisiblePoint.apply(this, arguments);
-};
-dom.orderStyle = function (node) {
- var style = node.getAttribute('style');
- if (!style) return null;
- style = style.replace(/[\s\n\r]+/, ' ').replace(/^ ?;? ?| ?;? ?$/g, '').replace(/ ?; ?/g, ';');
- if (!style.length) {
- node.removeAttribute("style");
- return null;
- }
- style = style.split(";");
- style.sort();
- style = style.join("; ")+";";
- node.setAttribute('style', style);
- return style;
-};
-dom.orderClass = function (node) {
- var className = node.getAttribute && node.getAttribute('class');
- if (!className) return null;
- className = className.replace(/[\s\n\r]+/, ' ').replace(/^ | $/g, '').replace(/ +/g, ' ');
- if (!className.length) {
- node.removeAttribute("class");
- return null;
- }
- className = className.split(" ");
- className.sort();
- className = className.join(" ");
- node.setAttribute('class', className);
- return className;
-};
-dom.node = function (node) {
- return dom.isText(node) ? node.parentNode : node;
-};
-dom.moveContent = function (from, to) {
- if (from === to) {
- return;
- }
- if (from.parentNode === to) {
- while (from.lastChild) {
- dom.insertAfter(from.lastChild, from);
- }
- } else {
- while (from.firstChild && from.firstChild !== to) {
- to.appendChild(from.firstChild);
- }
- }
-};
-dom.getComputedStyle = function (node) {
- return node.nodeType === Node.COMMENT_NODE ? {} : window.getComputedStyle(node);
-};
-
-//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
-range.WrappedRange.prototype.reRange = function (keep_end, isNotBreakable) {
- var sc = this.sc;
- var so = this.so;
- var ec = this.ec;
- var eo = this.eo;
- isNotBreakable = isNotBreakable || dom.isNotBreakable;
-
- // search the first snippet editable node
- var start = keep_end ? ec : sc;
- while (start) {
- if (isNotBreakable(start, sc, so, ec, eo)) {
- break;
- }
- start = start.parentNode;
- }
-
- // check if the end caret have the same node
- var lastFilterEnd;
- var end = keep_end ? sc : ec;
- while (end) {
- if (start === end) {
- break;
- }
- if (isNotBreakable(end, sc, so, ec, eo)) {
- lastFilterEnd = end;
- }
- end = end.parentNode;
- }
- if (lastFilterEnd) {
- end = lastFilterEnd;
- }
- if (!end) {
- end = document.getElementsByTagName('body')[0];
- }
-
- // if same node, keep range
- if (start === end || !start) {
- return this;
- }
-
- // reduce or extend the range to don't break a isNotBreakable area
- if ($.contains(start, end)) {
-
- if (keep_end) {
- sc = dom.lastChild(dom.hasContentBefore(dom.ancestorHavePreviousSibling(end)) || sc);
- so = sc.textContent.length;
- } else if (!eo) {
- ec = dom.lastChild(dom.hasContentBefore(dom.ancestorHavePreviousSibling(end)) || ec);
- eo = ec.textContent.length;
- } else {
- ec = dom.firstChild(dom.hasContentAfter(dom.ancestorHaveNextSibling(end)) || ec);
- eo = 0;
- }
- } else {
-
- if (keep_end) {
- sc = dom.firstChild(start);
- so = 0;
- } else {
- ec = dom.lastChild(start);
- eo = ec.textContent.length;
- }
- }
-
- return new range.WrappedRange(sc, so, ec, eo);
-};
-/**
- * Returns the image the range is in or matches (if any, false otherwise).
- *
- * @todo this implementation may not cover all corner cases but should do the
- * trick for all reproductible ones
- * @returns {DOMElement|boolean}
- */
-range.WrappedRange.prototype.isOnImg = function () {
- // If not a selection but a cursor position, just check if a point's
- // ancestor is an image or not
- if (this.sc === this.ec && this.so === this.eo) {
- return dom.ancestor(this.sc, dom.isImg);
- }
-
- var startPoint = {node: this.sc, offset: this.so};
- var endPoint = {node: this.ec, offset: this.eo};
-
- var nb = 0;
- var image;
- var textNode;
- dom.walkPoint(startPoint, endPoint, function (point) {
- // If the element has children (not a text node and not empty node),
- // the element cannot be considered as selected (these children will
- // be processed to determine that)
- if (dom.hasChildren(point.node)) {
- return;
- }
-
- // Check if an ancestor of the current point is an image
- var pointImg = dom.ancestor(point.node, dom.isImg);
- var isText = dom.isText(point.node);
-
- // Check if a visible element is selected, i.e.
- // - If an ancestor of the current is an image we did not see yet
- // - If the point is not in a br or a text (so a node with no children)
- // - If the point is in a non empty text node we already saw
- if (pointImg ?
- (image !== pointImg) :
- ((!dom.isBR(point.node) && !isText) || (textNode === point.node && point.node.textContent.match(/\S|\u00A0/)))) {
- nb++;
- }
-
- // If an ancestor of the current point is an image, then save it as the
- // image we are looking for
- if (pointImg) {
- image = pointImg;
- }
- // If the current point is a text node save it as the last text node
- // seen (if we see it again, this might mean it is selected)
- if (isText) {
- textNode = point.node;
- }
- });
-
- return nb === 1 && image;
-};
-range.WrappedRange.prototype.deleteContents = function (towrite) {
- if (this.sc === this.ec && this.so === this.eo) {
- return this;
- }
-
- var r;
- var image = this.isOnImg();
- if (image) {
- // If the range matches/is in an image, then the image is to be removed
- // and the cursor moved to its previous position
- var parentNode = image.parentNode;
- var index = _.indexOf(parentNode.childNodes, image);
- parentNode.removeChild(image);
- r = new range.WrappedRange(parentNode, index, parentNode, index);
- } else {
- r = dom.removeBetween(this.sc, this.so, this.ec, this.eo, towrite);
- }
-
- $(dom.node(r.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler
- return new range.WrappedRange(r.sc, r.so, r.ec, r.eo);
-};
-range.WrappedRange.prototype.clean = function (mergeFilter, all) {
- var node = dom.node(this.sc === this.ec ? this.sc : this.commonAncestor());
- node = node || $(this.sc).closest('[contenteditable]')[0];
- if (node.childNodes.length <=1) {
- return this;
- }
-
- var merge = dom.merge(node, this.sc, this.so, this.ec, this.eo, mergeFilter, all);
- var rem = dom.removeSpace(node.parentNode, merge.sc, merge.so, merge.ec, merge.eo);
-
- if (merge.merged || rem.removed) {
- return range.create(rem.sc, rem.so, rem.ec, rem.eo);
- }
- return this;
-};
-range.WrappedRange.prototype.remove = function (mergeFilter) {
-};
-range.WrappedRange.prototype.isOnCellFirst = function () {
- var node = dom.ancestor(this.sc, function (node) {return ["LI", "DIV", "TD","TH"].indexOf(node.tagName) !== -1;});
- return node && ["TD","TH"].indexOf(node.tagName) !== -1;
-};
-range.WrappedRange.prototype.isContentEditable = function () {
- return dom.isContentEditable(this.sc) && (this.sc === this.ec || dom.isContentEditable(this.ec));
-};
-
-//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
-renderer.tplButtonInfo.fontsize = function (lang, options) {
- var items = options.fontSizes.reduce(function (memo, v) {
- return memo + '<a data-event="fontSize" href="#" class="dropdown-item" data-value="' + v + '">' +
- '<i class="fa fa-check"></i> ' + v +
- '</a>';
- }, '');
-
- var sLabel = '<span class="note-current-fontsize">11</span>';
- return renderer.getTemplate().button(sLabel, {
- title: lang.font.size,
- dropdown: '<div class="dropdown-menu">' + items + '</div>'
- });
-};
-
-renderer.tplButtonInfo.color = function (lang, options) {
- var foreColorButtonLabel = '<i class="' + options.iconPrefix + options.icons.color.recent + '"></i>';
- var backColorButtonLabel = '<i class="' + options.iconPrefix + 'paint-brush"></i>';
- // TODO Remove recent color button if possible.
- // It is still put to avoid JS errors when clicking other buttons as the
- // editor still expects it to exist.
- var recentColorButton = renderer.getTemplate().button(foreColorButtonLabel, {
- className: 'note-recent-color d-none',
- title: lang.color.foreground,
- event: 'color',
- value: '{"backColor":"#B35E9B"}'
- });
- var foreColorItems = [
- '<li><div class="btn-group flex-column">',
- '<div class="note-color-palette" data-target-event="foreColor"></div>',
- '<h6 class="note-custom-color mt8" data-event="customColor" data-value="foreColor" title="' + lang.color.custom + '">',
- lang.color.custom + '</h6>',
- '<div class="note-custom-color-palette" data-target-event="foreColor"></div>',
- '</div></li>',
- ];
- var backColorItems = [
- '<li><div class="btn-group flex-column">',
- '<div class="note-color-reset" data-event="backColor" data-value="inherit" title="' + lang.color.transparent + '">',
- lang.color.setTransparent + '</div>',
- '<div class="note-color-palette" data-target-event="backColor"></div>',
- '<h6 class="note-custom-color mt8" data-event="customColor" data-value="backColor" title="' + lang.color.custom + '">',
- lang.color.custom + '</h6>',
- '<div class="note-custom-color-palette" data-target-event="backColor"></div>',
- '</div></li>',
- ];
- var foreColorButton = renderer.getTemplate().button(foreColorButtonLabel, {
- className: 'note-fore-color-preview mx-1',
- title: lang.color.foreground,
- dropdown: renderer.getTemplate().dropdown(foreColorItems)
- });
- var backColorButton = renderer.getTemplate().button(backColorButtonLabel, {
- className: 'note-back-color-preview mx-1',
- title: lang.color.background,
- dropdown: renderer.getTemplate().dropdown(backColorItems)
- });
- return recentColorButton + foreColorButton + backColorButton;
-},
-
-//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
-key.nameFromCode[46] = 'DELETE';
-key.nameFromCode[27] = 'ESCAPE';
-
-options.keyMap.pc['BACKSPACE'] = 'backspace';
-options.keyMap.pc['DELETE'] = 'delete';
-options.keyMap.pc['ENTER'] = 'enter';
-options.keyMap.pc['ESCAPE'] = 'cancel';
-options.keyMap.mac['SHIFT+TAB'] = 'untab';
-options.keyMap.pc['UP'] = 'up';
-options.keyMap.pc['DOWN'] = 'down';
-
-options.keyMap.mac['BACKSPACE'] = 'backspace';
-options.keyMap.mac['DELETE'] = 'delete';
-options.keyMap.mac['ENTER'] = 'enter';
-options.keyMap.mac['ESCAPE'] = 'cancel';
-options.keyMap.mac['UP'] = 'up';
-options.keyMap.mac['DOWN'] = 'down';
-
-options.styleTags = ['p', 'pre', 'small', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote'];
-
-$.summernote.pluginEvents.insertTable = function (event, editor, layoutInfo, sDim) {
- var $editable = layoutInfo.editable();
- var dimension = sDim.split('x');
- var r = range.create();
- if (!r) return;
- r = r.deleteContents(true);
-
- var table = editor.table.createTable(dimension[0], dimension[1]);
- var parent = r.sc;
- while (dom.isText(parent.parentNode) || dom.isRemovableEmptyNode(parent.parentNode)) {
- parent = parent.parentNode;
- }
- var node = dom.splitTree(parent, {'node': r.sc, 'offset': r.so}) || r.sc;
- node.parentNode.insertBefore(table, node);
-
- if ($(node).text() === '' || node.textContent === '\u00A0') {
- node.parentNode.removeChild(node);
- }
-
- editor.afterCommand($editable);
- event.preventDefault();
- return false;
-};
-$.summernote.pluginEvents.tab = function (event, editor, layoutInfo, outdent) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable, 'tab');
- var r = range.create();
- outdent = outdent || false;
- event.preventDefault();
-
- if (r && (dom.ancestor(r.sc, dom.isCell) || dom.ancestor(r.ec, dom.isCell))) {
- if (r.isCollapsed() && r.isOnCell() && r.isOnCellFirst()) {
- var td = dom.ancestor(r.sc, dom.isCell);
- if (!outdent && !dom.nextElementSibling(td) && !dom.nextElementSibling(td.parentNode)) {
- var last = dom.lastChild(td);
- range.create(last, dom.nodeLength(last), last, dom.nodeLength(last)).select();
- $.summernote.pluginEvents.enter(event, editor, layoutInfo);
- } else if (outdent && !dom.previousElementSibling(td) && !$(td.parentNode).text().match(/\S/)) {
- $.summernote.pluginEvents.backspace(event, editor, layoutInfo);
- } else {
- editor.table.tab(r, outdent);
- }
- } else {
- $.summernote.pluginEvents.indent(event, editor, layoutInfo, outdent);
- }
- } else if (r && r.isCollapsed()) {
- if (!r.sc.textContent.slice(0,r.so).match(/\S/) && r.isOnList()) {
- if (outdent) {
- $.summernote.pluginEvents.outdent(event, editor, layoutInfo);
- } else {
- $.summernote.pluginEvents.indent(event, editor, layoutInfo);
- }
- } else {
- var next;
- if (!outdent) {
- if (dom.isText(r.sc)) {
- next = r.sc.splitText(r.so);
- } else {
- next = document.createTextNode('');
- $(r.sc.childNodes[r.so]).before(next);
- }
- editor.typing.insertTab($editable, r, options.tabsize);
- r = range.create(next, 0, next, 0);
- r = dom.merge(r.sc.parentNode, r.sc, r.so, r.ec, r.eo, null, true);
- range.create(r.sc, r.so, r.ec, r.eo).select();
- } else {
- r = dom.merge(r.sc.parentNode, r.sc, r.so, r.ec, r.eo, null, true);
- r = range.create(r.sc, r.so, r.ec, r.eo);
- if (r.sc.splitText) {
- next = r.sc.splitText(r.so);
- r.sc.textContent = r.sc.textContent.replace(/(\u00A0)+$/g, '');
- next.textContent = next.textContent.replace(/^(\u00A0)+/g, '');
- range.create(r.sc, r.sc.textContent.length, r.sc, r.sc.textContent.length).select();
- }
- }
- }
- }
- return false;
-};
-$.summernote.pluginEvents.untab = function (event, editor, layoutInfo) {
- return $.summernote.pluginEvents.tab(event, editor, layoutInfo, true);
-};
-$.summernote.pluginEvents.up = function (event, editor, layoutInfo) {
- var r = range.create();
- var node = dom.firstChild(r.sc.childNodes[r.so] || r.sc);
- if (!r.isOnCell()) {
- return;
- }
- // check if an ancestor between node and cell has content before
- var ancestor = dom.ancestor(node, function (ancestorNode) {
- return dom.hasContentBefore(ancestorNode) || dom.isCell(ancestorNode);
- });
- if (!dom.isCell(ancestor) && (!dom.isBR(dom.hasContentBefore(ancestor)) || !dom.isText(node) || dom.isVisibleText(node) || dom.hasContentBefore(dom.hasContentBefore(ancestor)))) {
- return;
- }
- event.preventDefault();
- var td = dom.ancestor(r.sc, dom.isCell);
- var tr = td.parentNode;
- var target = tr.previousElementSibling && tr.previousElementSibling.children[_.indexOf(tr.children, td)];
- if (!target) {
- target = (dom.ancestorHavePreviousSibling(tr) || tr).previousSibling;
- }
- if (target) {
- range.create(dom.lastChild(target), dom.lastChild(target).textContent.length).select();
- }
-};
-$.summernote.pluginEvents.down = function (event, editor, layoutInfo) {
- var r = range.create();
- var node = dom.firstChild(r.sc.childNodes[r.so] || r.sc);
- if (!r.isOnCell()) {
- return;
- }
- // check if an ancestor between node and cell has content after
- var ancestor = dom.ancestor(node, function (ancestorNode) {
- return dom.hasContentAfter(ancestorNode) || dom.isCell(ancestorNode);
- });
- if (!dom.isCell(ancestor) && (!dom.isBR(dom.hasContentAfter(ancestor)) || !dom.isText(node) || dom.isVisibleText(node) || dom.hasContentAfter(dom.hasContentAfter(ancestor)))) {
- return;
- }
- event.preventDefault();
- var td = dom.ancestor(r.sc, dom.isCell);
- var tr = td.parentNode;
- var target = tr.nextElementSibling && tr.nextElementSibling.children[_.indexOf(tr.children, td)];
- if (!target) {
- target = (dom.ancestorHaveNextSibling(tr) || tr).nextSibling;
- }
- if (target) {
- range.create(dom.firstChild(target), 0).select();
- }
-};
-$.summernote.pluginEvents.enter = function (event, editor, layoutInfo) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable, 'enter');
-
- var r = range.create();
- if (!r.isContentEditable()) {
- event.preventDefault();
- return false;
- }
- if (!r.isCollapsed()) {
- r = r.deleteContents();
- r.select();
- }
-
- var br = $("<br/>")[0];
-
- var node;
- var $node;
- var $clone;
- var contentBefore = r.sc.textContent.slice(0,r.so).match(/\S|\u00A0/);
- if (!contentBefore && dom.isText(r.sc)) {
- node = r.sc.previousSibling;
- while (!contentBefore && node && dom.isText(node)) {
- contentBefore = dom.isVisibleText(node);
- node = node.previousSibling;
- }
- }
-
- node = dom.node(r.sc);
- var exist = r.sc.childNodes[r.so] || r.sc;
- exist = dom.isVisibleText(exist) || dom.isBR(exist) ? exist : dom.hasContentAfter(exist) || (dom.hasContentBefore(exist) || exist);
-
- // table: add a tr
- var td = dom.ancestor(node, dom.isCell);
- if (td && !dom.nextElementSibling(node) && !dom.nextElementSibling(td) && !dom.nextElementSibling(td.parentNode) && (!dom.isText(r.sc) || !r.sc.textContent.slice(r.so).match(/\S|\u00A0/))) {
- $node = $(td.parentNode);
- $clone = $node.clone();
- $clone.children().html(dom.blank);
- $node.after($clone);
- node = dom.firstElementChild($clone[0]) || $clone[0];
- range.create(node, 0, node, 0).select();
- dom.scrollIntoViewIfNeeded(br);
- event.preventDefault();
- return false;
- }
-
- var last = node;
- while (node && dom.isSplitable(node) && !dom.isList(node)) {
- last = node;
- node = node.parentNode;
- }
-
- if (last === node && !dom.isBR(node)) {
- node = r.insertNode(br, true);
- if (isFormatNode(last.firstChild) && $(last).closest(options.styleTags.join(',')).length) {
- dom.moveContent(last.firstChild, last);
- last.removeChild(last.firstChild);
- }
- do {
- node = dom.hasContentAfter(node);
- } while (node && dom.isBR(node));
-
- // create an other br because the user can't see the new line with only br in a block
- if (!node && (!br.nextElementSibling || !dom.isBR(br.nextElementSibling))) {
- $(br).before($("<br/>")[0]);
- }
- node = br.nextSibling || br;
- } else if (last === node && dom.isBR(node)) {
- $(node).after(br);
- node = br;
- } else if (!r.so && r.isOnList() && !r.sc.textContent.length && !dom.ancestor(r.sc, dom.isLi).nextElementSibling) {
- // double enter on the end of a list = new line out of the list
- $('<p></p>').append(br).insertAfter(dom.ancestor(r.sc, dom.isList));
- node = br;
- } else if (dom.isBR(exist) && $(r.sc).closest('blockquote, pre').length && !dom.hasContentAfter($(exist.parentNode).closest('blockquote *, pre *').length ? exist.parentNode : exist)) {
- // double enter on the end of a blockquote & pre = new line out of the list
- $('<p></p>').append(br).insertAfter($(r.sc).closest('blockquote, pre'));
- node = br;
- } else if (dom.isEditable(dom.node(r.sc))) {
- // if we are directly in an editable, only SHIFT + ENTER should add a newline
- node = null;
- } else if (last === r.sc) {
- if (dom.isBR(last)) {
- last = last.parentNode;
- }
- $node = $(last);
- $clone = $node.clone().text("");
- $node.after($clone);
- node = dom.node(dom.firstElementChild($clone[0]) || $clone[0]);
- $(node).html(br);
- node = br;
- } else {
- node = dom.splitTree(last, {'node': r.sc, 'offset': r.so}) || r.sc;
- if (!contentBefore) {
- var cur = dom.node(dom.lastChild(node.previousSibling));
- if (!dom.isBR(cur)) {
- $(cur).html(br);
- }
- }
- if (!dom.isVisibleText(node)) {
- node = dom.firstChild(node);
- $(dom.node( dom.isBR(node) ? node.parentNode : node )).html(br);
- node = br;
- }
- }
-
- if (node) {
- node = dom.firstChild(node);
- if (dom.isBR(node)) {
- range.createFromNode(node).select();
- } else {
- range.create(node,0).select();
- }
- dom.scrollIntoViewIfNeeded(node);
- }
- event.preventDefault();
- return false;
-};
-$.summernote.pluginEvents.visible = function (event, editor, layoutInfo) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable, "visible");
-
- var r = range.create();
- if (!r) return;
-
- if (!r.isCollapsed()) {
- if ((dom.isCell(dom.node(r.sc)) || dom.isCell(dom.node(r.ec))) && dom.node(r.sc) !== dom.node(r.ec)) {
- remove_table_content(r);
- r = range.create(r.ec, 0);
- }
- r.select();
- }
-
- // don't write in forbidden tag (like span for font awsome)
- var node = dom.firstChild(r.sc.tagName && r.so ? r.sc.childNodes[r.so] || r.sc : r.sc);
- while (node.parentNode) {
- if (dom.isForbiddenNode(node)) {
- var text = node.previousSibling;
- if (text && dom.isText(text) && dom.isVisibleText(text)) {
- range.create(text, text.textContent.length, text, text.textContent.length).select();
- } else {
- text = node.parentNode.insertBefore(document.createTextNode( "." ), node);
- range.create(text, 1, text, 1).select();
- setTimeout(function () {
- var text = range.create().sc;
- text.textContent = text.textContent.replace(/^./, '');
- range.create(text, text.textContent.length, text, text.textContent.length).select();
- },0);
- }
- break;
- }
- node = node.parentNode;
- }
-
- return true;
-};
-
-function remove_table_content(r) {
- var nodes = dom.listBetween(r.sc, r.ec, r.so, r.eo);
- if (dom.isText(r.sc)) {
- r.sc.textContent = r.sc.textContent.slice(0, r.so);
- }
- if (dom.isText(r.ec)) {
- r.ec.textContent = r.ec.textContent.slice(r.eo);
- }
- for (var i in nodes) {
- var node = nodes[i];
- if (node === r.sc || node === r.ec || $.contains(node, r.sc) || $.contains(node, r.ec)) {
- continue;
- } else if (dom.isCell(node)) {
- $(node).html("<br/>");
- } else if (node.parentNode) {
- do {
- var parent = node.parentNode;
- parent.removeChild(node);
- node = parent;
- } while (!dom.isVisibleText(node) && !dom.firstElementChild(node) &&
- !dom.isCell(node) &&
- node.parentNode && !$(node.parentNode).hasClass('o_editable'));
- }
- }
- return false;
-}
-
-$.summernote.pluginEvents.delete = function (event, editor, layoutInfo) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable, "delete");
-
- var r = range.create();
- if (!r) return;
- if (!r.isContentEditable()) {
- event.preventDefault();
- return false;
- }
- if (!r.isCollapsed()) {
- if (dom.isCell(dom.node(r.sc)) || dom.isCell(dom.node(r.ec))) {
- remove_table_content(r);
- range.create(r.ec, 0).select();
- } else {
- r = r.deleteContents();
- r.select();
- }
- event.preventDefault();
- return false;
- }
-
- var target = r.ec;
- var offset = r.eo;
- if (target.tagName && target.childNodes[offset]) {
- target = target.childNodes[offset];
- offset = 0;
- }
-
- var node = dom.node(target);
- var data = dom.merge(node, target, offset, target, offset, null, true);
- data = dom.removeSpace(node.parentNode, data.sc, data.so, data.ec, data.eo);
- r = range.create(data.sc, data.so);
- r.select();
- target = r.sc;
- offset = r.so;
-
- while (!dom.hasContentAfter(node) && !dom.hasContentBefore(node) && !dom.isImg(node)) {node = node.parentNode;}
-
- var contentAfter = target.textContent.slice(offset,Infinity).match(/\S|\u00A0/);
- var content = target.textContent.replace(/[ \t\r\n]+$/, '');
- var temp;
- var temp2;
- var next;
-
- // media
- if (dom.isImg(node) || (!contentAfter && dom.isImg(dom.hasContentAfter(node)))) {
- var parent;
- var index;
- if (!dom.isImg(node)) {
- node = dom.hasContentAfter(node);
- }
- while (dom.isImg(node)) {
- parent = node.parentNode;
- index = dom.position(node);
- if (index>0) {
- next = node.previousSibling;
- r = range.create(next, next.textContent.length);
- } else {
- r = range.create(parent, 0);
- }
- if (!dom.hasContentAfter(node) && !dom.hasContentBefore(node)) {
- parent.appendChild($('<br/>')[0]);
- }
- parent.removeChild(node);
- node = parent;
- r.select();
- }
- }
- // empty tag
- else if (!content.length && target.tagName && dom.isRemovableEmptyNode(dom.isBR(target) ? target.parentNode : target)) {
- if (node === $editable[0] || $.contains(node, $editable[0])) {
- event.preventDefault();
- return false;
- }
- var before = false;
- next = dom.hasContentAfter(dom.ancestorHaveNextSibling(node));
- if (!dom.isContentEditable(next)) {
- before = true;
- next = dom.hasContentBefore(dom.ancestorHavePreviousSibling(node));
- }
- dom.removeSpace(next.parentNode, next, 0, next, 0); // clean before jump for not select invisible space between 2 tag
- next = dom.firstChild(next);
- node.parentNode.removeChild(node);
- range.create(next, before ? next.textContent.length : 0).select();
- }
- // normal feature if same tag and not the end
- else if (contentAfter) {
- return true;
- }
- // merge with the next text node
- else if (dom.isText(target) && (temp = dom.hasContentAfter(target)) && dom.isText(temp)) {
- return true;
- }
- //merge with the next block
- else if ((temp = dom.ancestorHaveNextSibling(target)) &&
- !r.isOnCell() &&
- dom.isMergable(temp) &&
- dom.isMergable(temp2 = dom.hasContentAfter(temp)) &&
- temp.tagName === temp2.tagName &&
- (temp.tagName !== "LI" || !$('ul,ol', temp).length) && (temp2.tagName !== "LI" || !$('ul,ol', temp2).length) && // protect li
- !dom.isNotBreakable(temp) &&
- !dom.isNotBreakable(temp2)) {
- dom.autoMerge(target, false);
- next = dom.firstChild(dom.hasContentAfter(dom.ancestorHaveNextSibling(target)));
- if (dom.isBR(next)) {
- if (dom.position(next) === 0) {
- range.create(next.parentNode, 0).select();
- }
- else {
- range.create(next.previousSibling, next.previousSibling.textContent.length).select();
- }
- next.parentNode.removeChild(next);
- } else {
- range.create(next, 0).select();
- }
- }
- // jump to next node for delete
- else if ((temp = dom.ancestorHaveNextSibling(target)) && (temp2 = dom.hasContentAfter(temp)) && dom.isContentEditable(temp2)) {
-
- dom.removeSpace(temp2.parentNode, temp2, 0, temp, 0); // clean before jump for not select invisible space between 2 tag
- temp2 = dom.firstChild(temp2);
-
- r = range.create(temp2, 0);
- r.select();
-
- if ((dom.isText(temp) || dom.getComputedStyle(temp).display === "inline") && (dom.isText(temp2) || dom.getComputedStyle(temp2).display === "inline")) {
- if (dom.isText(temp2)) {
- temp2.textContent = temp2.textContent.replace(/^\s*\S/, '');
- } else {
- $.summernote.pluginEvents.delete(event, editor, layoutInfo);
- }
- }
- }
-
- $(dom.node(r.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler
- event.preventDefault();
- return false;
-};
-$.summernote.pluginEvents.backspace = function (event, editor, layoutInfo) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable, "backspace");
-
- var r = range.create();
- if (!r) return;
- if (!r.isContentEditable()) {
- event.preventDefault();
- return false;
- }
- if (!r.isCollapsed()) {
- if (dom.isCell(dom.node(r.sc)) || dom.isCell(dom.node(r.ec))) {
- remove_table_content(r);
- range.create(r.sc, dom.nodeLength(r.sc)).select();
- } else {
- r = r.deleteContents();
- r.select();
- }
- event.preventDefault();
- return false;
- }
-
- var target = r.sc;
- var offset = r.so;
- if (target.tagName && target.childNodes[offset]) {
- target = target.childNodes[offset];
- offset = 0;
- }
-
- var node = dom.node(target);
- var data = dom.merge(node, target, offset, target, offset, null, true);
- data = dom.removeSpace(node.parentNode, data.sc, data.so, data.ec, data.eo);
- r = dom.isVoid(data.sc) ? range.createFromNode(data.sc) : range.create(data.sc, data.so);
- r.select();
- target = r.sc;
- offset = r.so;
- if (target.tagName && target.childNodes[offset]) {
- target = target.childNodes[offset];
- offset = 0;
- node = dom.node(target);
- }
-
- while (node.parentNode && !dom.hasContentAfter(node) && !dom.hasContentBefore(node) && !dom.isImg(node)) {node = node.parentNode;}
-
- var contentBefore = target.textContent.slice(0,offset).match(/\S|\u00A0/);
- var content = target.textContent.replace(/[ \t\r\n]+$/, '');
- var temp;
- var temp2;
- var prev;
-
- // delete media
- if (dom.isImg(node) || (!contentBefore && dom.isImg(dom.hasContentBefore(node)))) {
- if (!dom.isImg(node)) {
- node = dom.hasContentBefore(node);
- }
- range.createFromNode(node).select();
- $.summernote.pluginEvents.delete(event, editor, layoutInfo);
- }
- // table tr td
- else if (r.isOnCell() && !offset && (target === (temp = dom.ancestor(target, dom.isCell)) || target === temp.firstChild || (dom.isText(temp.firstChild) && !dom.isVisibleText(temp.firstChild) && target === temp.firstChild.nextSibling))) {
- if (dom.previousElementSibling(temp)) {
- var td = dom.previousElementSibling(temp);
- node = td.lastChild || td;
- } else {
- var tr = temp.parentNode;
- var prevTr = dom.previousElementSibling(tr);
- if (!$(temp.parentNode).text().match(/\S|\u00A0/)) {
- if (prevTr) {
- node = dom.lastChild(dom.lastElementChild(prevTr));
- } else {
- node = dom.lastChild(dom.hasContentBefore(dom.ancestorHavePreviousSibling(tr)) || $editable.get(0));
- }
- $(tr).empty();
- if (!$(tr).closest('table').has('td, th').length) {
- $(tr).closest('table').remove();
- }
- $(tr).remove();
- range.create(node, node.textContent.length, node, node.textContent.length).select();
- } else {
- node = dom.lastElementChild(prevTr).lastChild || dom.lastElementChild(prevTr);
- }
- }
- if (dom.isBR(node)) {
- range.createFromNode(node).select();
- } else {
- range.create(node, dom.nodeLength(node)).select();
- }
- }
- // empty tag
- else if (!content.length && target.tagName && dom.isRemovableEmptyNode(target)) {
- if (node === $editable[0] || $.contains(node, $editable[0])) {
- event.preventDefault();
- return false;
- }
- var before = true;
- prev = dom.hasContentBefore(dom.ancestorHavePreviousSibling(node));
- if (!dom.isContentEditable(prev)) {
- before = false;
- prev = dom.hasContentAfter(dom.ancestorHaveNextSibling(node));
- }
- dom.removeSpace(prev.parentNode, prev, 0, prev, 0); // clean before jump for not select invisible space between 2 tag
- prev = dom.lastChild(prev);
- node.parentNode.removeChild(node);
- range.createFromNode(prev).select();
- range.create(prev, before ? prev.textContent.length : 0).select();
- }
- // normal feature if same tag and not the begin
- else if (contentBefore) {
- return true;
- }
- // merge with the previous text node
- else if (dom.isText(target) && (temp = dom.hasContentBefore(target)) && (dom.isText(temp) || dom.isBR(temp))) {
- return true;
- }
- //merge with the previous block
- else if ((temp = dom.ancestorHavePreviousSibling(target)) &&
- dom.isMergable(temp) &&
- dom.isMergable(temp2 = dom.hasContentBefore(temp)) &&
- temp.tagName === temp2.tagName &&
- (temp.tagName !== "LI" || !$('ul,ol', temp).length) && (temp2.tagName !== "LI" || !$('ul,ol', temp2).length) && // protect li
- !dom.isNotBreakable(temp) &&
- !dom.isNotBreakable(temp2)) {
- prev = dom.firstChild(target);
- dom.autoMerge(target, true);
- range.create(prev, 0).select();
- }
- // jump to previous node for delete
- else if ((temp = dom.ancestorHavePreviousSibling(target)) && (temp2 = dom.hasContentBefore(temp)) && dom.isContentEditable(temp2)) {
-
- dom.removeSpace(temp2.parentNode, temp2, 0, temp, 0); // clean before jump for not select invisible space between 2 tag
- temp2 = dom.lastChild(temp2);
-
- r = range.create(temp2, temp2.textContent.length, temp2, temp2.textContent.length);
- r.select();
-
- if ((dom.isText(temp) || dom.getComputedStyle(temp).display === "inline") && (dom.isText(temp2) || dom.getComputedStyle(temp2).display === "inline")) {
- if (dom.isText(temp2)) {
- temp2.textContent = temp2.textContent.replace(/\S\s*$/, '');
- } else {
- $.summernote.pluginEvents.backspace(event, editor, layoutInfo);
- }
- }
- }
-
- r = range.create();
- if (r) {
- $(dom.node(r.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler
- dom.scrollIntoViewIfNeeded(r.sc.parentNode.previousElementSibling || r.sc);
- }
-
- event.preventDefault();
- return false;
-};
-
-function isFormatNode(node) {
- return node.tagName && options.styleTags.indexOf(node.tagName.toLowerCase()) !== -1;
-}
-
-$.summernote.pluginEvents.insertUnorderedList = function (event, editor, layoutInfo, sorted) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable);
-
- var parent;
- var r = range.create();
- if (!r) return;
- var node = r.sc;
- while (node && node !== $editable[0]) {
-
- parent = node.parentNode;
- if (node.tagName === (sorted ? "UL" : "OL")) {
-
- var ul = document.createElement(sorted ? "ol" : "ul");
- ul.className = node.className;
- parent.insertBefore(ul, node);
- while (node.firstChild) {
- ul.appendChild(node.firstChild);
- }
- parent.removeChild(node);
- r.select();
- return;
-
- } else if (node.tagName === (sorted ? "OL" : "UL")) {
-
- var lis = [];
- for (var i=0; i<node.children.length; i++) {
- lis.push(node.children[i]);
- }
-
- if (parent.tagName === "LI") {
- node = parent;
- parent = node.parentNode;
- _.each(lis, function (li) {
- parent.insertBefore(li, node);
- });
- } else {
- _.each(lis, function (li) {
- while (li.firstChild) {
- parent.insertBefore(li.firstChild, node);
- }
- });
- }
-
- parent.removeChild(node);
- r.select();
- return;
-
- }
- node = parent;
- }
-
- var p0 = r.sc;
- while (p0 && p0.parentNode && p0.parentNode !== $editable[0] && !isFormatNode(p0)) {
- p0 = p0.parentNode;
- }
- if (!p0) return;
- var p1 = r.ec;
- while (p1 && p1.parentNode && p1.parentNode !== $editable[0] && !isFormatNode(p1)) {
- p1 = p1.parentNode;
- }
- if (!p0.parentNode || p0.parentNode !== p1.parentNode) {
- return;
- }
-
- parent = p0.parentNode;
- ul = document.createElement(sorted ? "ol" : "ul");
- parent.insertBefore(ul, p0);
- var childNodes = parent.childNodes;
- var brs = [];
- var begin = false;
- for (i = 0; i < childNodes.length; i++) {
- if (begin && dom.isBR(childNodes[i])) {
- parent.removeChild(childNodes[i]);
- i--;
- }
- if ((!dom.isText(childNodes[i]) && !isFormatNode(childNodes[i])) || (!ul.firstChild && childNodes[i] !== p0) ||
- $.contains(ul, childNodes[i]) || (dom.isText(childNodes[i]) && !childNodes[i].textContent.match(/\S|u00A0/))) {
- continue;
- }
- begin = true;
- var li = document.createElement('li');
- ul.appendChild(li);
- li.appendChild(childNodes[i]);
- if (li.firstChild === p1) {
- break;
- }
- i--;
- }
- if (dom.isBR(childNodes[i])) {
- parent.removeChild(childNodes[i]);
- }
-
- for (i = 0; i < brs.length ; i++) {
- parent.removeChild(brs[i]);
- }
- r.clean().select();
- event.preventDefault();
- return false;
-};
-$.summernote.pluginEvents.insertOrderedList = function (event, editor, layoutInfo) {
- return $.summernote.pluginEvents.insertUnorderedList(event, editor, layoutInfo, true);
-};
-$.summernote.pluginEvents.indent = function (event, editor, layoutInfo, outdent) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable);
- var r = range.create();
- if (!r) return;
-
- var flag = false;
- function indentUL(UL, start, end) {
- var next;
- var tagName = UL.tagName;
- var node = UL.firstChild;
- var ul = document.createElement(tagName);
- var li = document.createElement("li");
- li.style.listStyle = "none";
- li.appendChild(ul);
-
- if (flag) {
- flag = 1;
- }
-
- // create and fill ul into a li
- while (node) {
- if (flag === 1 || node === start || $.contains(node, start)) {
- flag = true;
- node.parentNode.insertBefore(li, node);
- }
- next = dom.nextElementSibling(node);
- if (flag) {
- ul.appendChild(node);
- }
- if (node === end || $.contains(node, end)) {
- flag = false;
- break;
- }
- node = next;
- }
-
- var temp;
- var prev = dom.previousElementSibling(li);
- if (prev && prev.tagName === "LI" && (temp = dom.firstElementChild(prev)) && temp.tagName === tagName && ((dom.firstElementChild(prev) || prev.firstChild) !== ul)) {
- dom.doMerge(dom.firstElementChild(prev) || prev.firstChild, ul);
- li = prev;
- li.parentNode.removeChild(dom.nextElementSibling(li));
- }
- next = dom.nextElementSibling(li);
- if (next && next.tagName === "LI" && (temp = dom.firstElementChild(next)) && temp.tagName === tagName && (dom.firstElementChild(li) !== dom.firstElementChild(next))) {
- dom.doMerge(dom.firstElementChild(li), dom.firstElementChild(next));
- li.parentNode.removeChild(dom.nextElementSibling(li));
- }
- }
- function outdenttUL(UL, start, end) {
- var next;
- var node = UL.firstChild;
- var parent = UL.parentNode;
- var li = UL.parentNode.tagName === "LI" ? UL.parentNode : UL;
- var ul = UL.parentNode.tagName === "LI" ? UL.parentNode.parentNode : UL.parentNode;
- start = dom.ancestor(start, dom.isLi);
- end = dom.ancestor(end, dom.isLi);
-
- if (ul.tagName !== "UL" && ul.tagName !== "OL") return;
-
- // create and fill ul into a li
- while (node) {
- if (node === start || $.contains(node, start)) {
- flag = true;
- if (dom.previousElementSibling(node) && li.tagName === "LI") {
- li = dom.splitTree(li, dom.prevPoint({'node': node, 'offset': 0}));
- }
- }
- next = dom.nextElementSibling(node);
- if (flag) {
- ul = node.parentNode;
- li.parentNode.insertBefore(node, li);
- if (!ul.children.length) {
- if (ul.parentNode.tagName === "LI") {
- ul = ul.parentNode;
- }
- ul.parentNode.removeChild(ul);
- }
- }
-
- if (node === end || $.contains(node, end)) {
- flag = false;
- break;
- }
- node = next;
- }
-
- dom.merge(parent, start, 0, end, 1, null, true);
- }
- function indentOther(p, start, end) {
- if (p === start || $.contains(p, start) || $.contains(start, p)) {
- flag = true;
- }
- if (flag) {
- if (outdent) {
- dom.outdent(p);
- } else {
- dom.indent(p);
- }
- }
- if (p === end || $.contains(p, end) || $.contains(end, p)) {
- flag = false;
- }
- }
-
- var ancestor = r.commonAncestor();
- var $dom = $(ancestor);
-
- if (!dom.isList(ancestor)) {
- // to indent a selection, we indent the child nodes of the common
- // ancestor that contains this selection
- $dom = $(dom.node(ancestor)).children();
- }
- if (!$dom.not('br').length) {
- // if selection is inside a list, we indent its list items
- $dom = $(dom.ancestor(r.sc, dom.isList));
- if (!$dom.length) {
- // if the selection is contained in a single HTML node, we indent
- // the first ancestor 'content block' (P, H1, PRE, ...) or TD
- $dom = $(r.sc).closest(options.styleTags.join(',')+',td');
- }
- }
-
- // if select tr, take the first td
- $dom = $dom.map(function () { return this.tagName === "TR" ? dom.firstElementChild(this) : this; });
-
- $dom.each(function () {
- if (flag || $.contains(this, r.sc)) {
- if (dom.isList(this)) {
- if (outdent) {
- outdenttUL(this, r.sc, r.ec);
- } else {
- indentUL(this, r.sc, r.ec);
- }
- } else if (isFormatNode(this) || dom.ancestor(this, dom.isCell)) {
- indentOther(this, r.sc, r.ec);
- }
- }
- });
-
- if ($dom.length) {
- var $parent = $dom.parent();
-
- // remove text nodes between lists
- var $ul = $parent.find('ul, ol');
- if (!$ul.length) {
- $ul = $(dom.ancestor(r.sc, dom.isList));
- }
- $ul.each(function () {
- if (this.previousSibling &&
- this.previousSibling !== dom.previousElementSibling(this) &&
- !this.previousSibling.textContent.match(/\S/)) {
- this.parentNode.removeChild(this.previousSibling);
- }
- if (this.nextSibling &&
- this.nextSibling !== dom.nextElementSibling(this) &&
- !this.nextSibling.textContent.match(/\S/)) {
- this.parentNode.removeChild(this.nextSibling);
- }
- });
-
- // merge same ul or ol
- r = dom.merge($parent[0], r.sc, r.so, r.ec, r.eo, function (prev, cur) {
- if (prev && dom.isList(prev) && dom.isEqual(prev, cur)) {
- return true;
- }
- }, true);
- range.create(r.sc, r.so, r.ec, r.eo).select();
- }
- event.preventDefault();
- return false;
-};
-$.summernote.pluginEvents.outdent = function (event, editor, layoutInfo) {
- return $.summernote.pluginEvents.indent(event, editor, layoutInfo, true);
-};
-
-$.summernote.pluginEvents.formatBlock = function (event, editor, layoutInfo, sTagName) {
- $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, null, null, "Default");
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable);
- event.preventDefault();
-
- var r = range.create();
- if (!r) {
- return;
- }
- // select content since container (that firefox selects) may be removed
- if (r.so === 0) {
- r.sc = dom.firstChild(r.sc);
- }
- if (dom.nodeLength(r.ec) >= r.eo) {
- r.ec = dom.lastChild(r.ec);
- r.eo = dom.nodeLength(r.ec);
- }
- r = range.create(r.sc, r.so, r.ec, r.eo);
- r.reRange().select();
-
- if (sTagName === "blockquote" || sTagName === "pre") {
- sTagName = $.summernote.core.agent.isMSIE ? '<' + sTagName + '>' : sTagName;
- document.execCommand('FormatBlock', false, sTagName);
- return;
- }
-
- // fix by odoo because if you select a style in a li with no p tag all the ul is wrapped by the style tag
- var nodes = dom.listBetween(r.sc, r.ec, r.so, r.eo);
- for (var i=0; i<nodes.length; i++) {
- if (dom.isBR(nodes[i]) || (dom.isText(nodes[i]) && dom.isVisibleText(nodes[i])) || dom.isB(nodes[i]) || dom.isU(nodes[i]) || dom.isS(nodes[i]) || dom.isI(nodes[i]) || dom.isFont(nodes[i])) {
- var ancestor = dom.ancestor(nodes[i], isFormatNode);
- if ($(ancestor).parent().is('blockquote')) {
- // firefox may wrap formatting block in blockquote
- $(ancestor).unwrap();
- }
- if (!ancestor) {
- dom.wrap(nodes[i], sTagName);
- } else if (ancestor.tagName.toLowerCase() !== sTagName) {
- var tag = document.createElement(sTagName);
- ancestor.parentNode.insertBefore(tag, ancestor);
- dom.moveContent(ancestor, tag);
- if (ancestor.className) {
- tag.className = ancestor.className;
- }
- ancestor.parentNode.removeChild(ancestor);
- }
- }
- }
- r.select();
-};
-$.summernote.pluginEvents.removeFormat = function (event, editor, layoutInfo, value) {
- var $editable = layoutInfo.editable();
- $editable.data('NoteHistory').recordUndo($editable);
- var r = range.create();
- if (!r) return;
- var node = range.create().sc.parentNode;
- document.execCommand('removeFormat');
- document.execCommand('removeFormat');
- r = range.create();
- if (!r) return;
- r = dom.merge(node, r.sc, r.so, r.ec, r.eo, null, true);
- range.create(r.sc, r.so, r.ec, r.eo).select();
- event.preventDefault();
- return false;
-};
-
-eventHandler.modules.editor.undo = function ($popover) {
- if (!$popover.attr('disabled')) $popover.data('NoteHistory').undo();
-};
-eventHandler.modules.editor.redo = function ($popover) {
- if (!$popover.attr('disabled')) $popover.data('NoteHistory').redo();
-};
-
-// Get color and background color of node to update recent color button
-var fn_from_node = eventHandler.modules.editor.style.fromNode;
-eventHandler.modules.editor.style.fromNode = function ($node) {
- var styleInfo = fn_from_node.apply(this, arguments);
- styleInfo['color'] = $node.css('color');
- styleInfo['background-color'] = $node.css('background-color');
- return styleInfo;
-};
-
-// use image toolbar if current range is on image
-var fn_editor_currentstyle = eventHandler.modules.editor.currentStyle;
-eventHandler.modules.editor.currentStyle = function (target) {
- var styleInfo = fn_editor_currentstyle.apply(this, arguments);
- // with our changes for inline editor, the targeted element could be a button of the editor
- if (!styleInfo.image || !dom.isEditable(styleInfo.image)) {
- styleInfo.image = undefined;
- var r = range.create();
- if (r)
- styleInfo.image = r.isOnImg();
- }
- // Fix when the target is a link: the text-align buttons state should
- // indicate the alignment of the link in the parent, not the text inside
- // the link (which is not possible to customize with summernote). Summernote fixed
- // this in their newest version... by just not showing the active button
- // for alignments.
- if (styleInfo.anchor) {
- styleInfo['text-align'] = $(styleInfo.anchor).parent().css('text-align');
- }
- return styleInfo;
-};
-
-options.fontSizes = [_t('Default'), 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 62];
-$.summernote.pluginEvents.applyFont = function (event, editor, layoutInfo, color, bgcolor, size) {
- var r = range.create();
- if (!r) return;
- var startPoint = r.getStartPoint();
- var endPoint = r.getEndPoint();
-
- if (r.isCollapsed() && !dom.isFont(r.sc)) {
- return {
- sc: startPoint.node,
- so: startPoint.offset,
- ec: endPoint.node,
- offset: endPoint.offset
- };
- }
-
- if (startPoint.node.tagName && startPoint.node.childNodes[startPoint.offset]) {
- startPoint.node = startPoint.node.childNodes[startPoint.offset];
- startPoint.offset = 0;
- }
- if (endPoint.node.tagName && endPoint.node.childNodes[endPoint.offset]) {
- endPoint.node = endPoint.node.childNodes[endPoint.offset];
- endPoint.offset = 0;
- }
-
- // get first and last point
- var ancestor;
- var node;
- if (endPoint.offset && endPoint.offset !== dom.nodeLength(endPoint.node)) {
- ancestor = dom.ancestor(endPoint.node, dom.isFont) || endPoint.node;
- dom.splitTree(ancestor, endPoint);
- }
- if (startPoint.offset && startPoint.offset !== dom.nodeLength(startPoint.node)) {
- ancestor = dom.ancestor(startPoint.node, dom.isFont) || startPoint.node;
- node = dom.splitTree(ancestor, startPoint);
- if (endPoint.node === startPoint.node) {
- endPoint.node = node;
- endPoint.offset = dom.nodeLength(node);
- }
- startPoint.node = node;
- startPoint.offset = 0;
- }
-
- // get list of nodes to change
- var nodes = [];
- dom.walkPoint(startPoint, endPoint, function (point) {
- var node = point.node;
- if (((dom.isText(node) && dom.isVisibleText(node)) ||
- (dom.isFont(node) && !dom.isVisibleText(node))) &&
- (node !== endPoint.node || endPoint.offset)) {
-
- nodes.push(point.node);
-
- }
- });
- nodes = list.unique(nodes);
-
- // If ico fa
- if (r.isCollapsed()) {
- nodes.push(startPoint.node);
- }
-
- // apply font: foreColor, backColor, size (the color can be use a class text-... or bg-...)
- var font, $font, fonts = [], className;
- var i;
- if (color || bgcolor || size) {
- for (i=0; i<nodes.length; i++) {
- node = nodes[i];
-
- font = dom.ancestor(node, dom.isFont);
- if (!font) {
- if (node.textContent.match(/^[ ]|[ ]$/)) {
- node.textContent = node.textContent.replace(/^[ ]|[ ]$/g, '\u00A0');
- }
-
- font = dom.create("font");
- node.parentNode.insertBefore(font, node);
- font.appendChild(node);
- }
-
- fonts.push(font);
-
- className = font.className.split(/\s+/);
-
- var k;
- if (color) {
- for (k=0; k<className.length; k++) {
- if (className[k].length && className[k].slice(0,5) === "text-") {
- className.splice(k,1);
- k--;
- }
- }
-
- if (color.indexOf('text-') !== -1) {
- font.className = className.join(" ") + " " + color;
- font.style.color = "inherit";
- } else {
- font.className = className.join(" ");
- font.style.color = color;
- }
- }
- if (bgcolor) {
- for (k=0; k<className.length; k++) {
- if (className[k].length && className[k].slice(0,3) === "bg-") {
- className.splice(k,1);
- k--;
- }
- }
-
- if (bgcolor.indexOf('bg-') !== -1) {
- font.className = className.join(" ") + " " + bgcolor;
- font.style.backgroundColor = "inherit";
- } else {
- font.className = className.join(" ");
- font.style.backgroundColor = bgcolor;
- }
- }
- if (size) {
- font.style.fontSize = "inherit";
- if (!isNaN(size) && Math.abs(parseInt(dom.getComputedStyle(font).fontSize, 10)-size)/size > 0.05) {
- font.style.fontSize = size + "px";
- }
- }
- }
- }
-
- // remove empty values
- // we must remove the value in 2 steps (applay inherit then remove) because some
- // browser like chrome have some time an error for the rendering and/or keep inherit
- for (i=0; i<fonts.length; i++) {
- font = fonts[i];
- if (font.style.backgroundColor === "inherit") {
- font.style.backgroundColor = "";
- }
- if (font.style.color === "inherit") {
- font.style.color = "";
- }
- if (font.style.fontSize === "inherit") {
- font.style.fontSize = "";
- }
-
- $font = $(font);
-
- if (!$font.css("color") && !$font.css("background-color") && !$font.css("font-size")) {
- $font.removeAttr("style");
- }
- if (!font.className.length) {
- $font.removeAttr("class");
- }
- }
-
- // select nodes to clean (to remove empty font and merge same nodes)
- nodes = [];
- dom.walkPoint(startPoint, endPoint, function (point) {
- nodes.push(point.node);
- });
- nodes = list.unique(nodes);
-
- function remove(node, to) {
- if (node === endPoint.node) {
- endPoint = dom.prevPoint(endPoint);
- }
- if (to) {
- dom.moveContent(node, to);
- }
- dom.remove(node);
- }
-
- // remove node without attributes (move content), and merge the same nodes
- var className2, style, style2;
- for (i=0; i<nodes.length; i++) {
- node = nodes[i];
-
- if ((dom.isText(node) || dom.isBR(node)) && !dom.isVisibleText(node)) {
- remove(node);
- nodes.splice(i,1);
- i--;
- continue;
- }
-
- font = dom.ancestor(node, dom.isFont);
- node = font || dom.ancestor(node, dom.isSpan);
-
- if (!node) {
- continue;
- }
-
- $font = $(node);
- className = dom.orderClass(node);
- style = dom.orderStyle(node);
-
- if (!className && !style) {
- remove(node, node.parentNode);
- nodes.splice(i,1);
- i--;
- continue;
- }
-
- if (i>0 && (font = dom.ancestor(nodes[i-1], dom.isFont))) {
- className2 = font.getAttribute('class');
- style2 = font.getAttribute('style');
- if (node !== font && className === className2 && style === style2) {
- remove(node, font);
- nodes.splice(i,1);
- i--;
- continue;
- }
- }
- }
-
- range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset).select();
-};
-$.summernote.pluginEvents.fontSize = function (event, editor, layoutInfo, value) {
- var $editable = layoutInfo.editable();
- event.preventDefault();
- $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, null, null, value);
- editor.afterCommand($editable);
-};
-$.summernote.pluginEvents.color = function (event, editor, layoutInfo, sObjColor) {
- var oColor = JSON.parse(sObjColor);
- var foreColor = oColor.foreColor, backColor = oColor.backColor;
-
- if (foreColor) { $.summernote.pluginEvents.foreColor(event, editor, layoutInfo, foreColor); }
- if (backColor) { $.summernote.pluginEvents.backColor(event, editor, layoutInfo, backColor); }
-};
-$.summernote.pluginEvents.foreColor = function (event, editor, layoutInfo, foreColor) {
- var $editable = layoutInfo.editable();
- $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, foreColor, null, null);
- editor.afterCommand($editable);
-};
-$.summernote.pluginEvents.backColor = function (event, editor, layoutInfo, backColor) {
- var $editable = layoutInfo.editable();
- var r = range.create();
- if (!r) return;
- if (r.isCollapsed() && r.isOnCell()) {
- var cell = dom.ancestor(r.sc, dom.isCell);
- cell.className = cell.className.replace(new RegExp('(^|\\s+)bg-[^\\s]+(\\s+|$)', 'gi'), '');
- cell.style.backgroundColor = "";
- if (backColor.indexOf('bg-') !== -1) {
- cell.className += ' ' + backColor;
- } else if (backColor !== 'inherit') {
- cell.style.backgroundColor = backColor;
- }
- return;
- }
- $.summernote.pluginEvents.applyFont(event, editor, layoutInfo, null, backColor, null);
- editor.afterCommand($editable);
-};
-
-options.onCreateLink = function (sLinkUrl) {
- if (sLinkUrl.indexOf('mailto:') === 0 || sLinkUrl.indexOf('tel:') === 0) {
- // pass
- } else if (sLinkUrl.indexOf('@') !== -1 && sLinkUrl.indexOf(':') === -1) {
- sLinkUrl = 'mailto:' + sLinkUrl;
- } else if (sLinkUrl.indexOf('://') === -1 && sLinkUrl[0] !== '/'
- && sLinkUrl[0] !== '#' && sLinkUrl.slice(0, 2) !== '${') {
- sLinkUrl = 'http://' + sLinkUrl;
- }
- return sLinkUrl;
-};
-
-function summernote_table_scroll(event) {
- var r = range.create();
- if (r && r.isOnCell()) {
- $('.o_table_handler').remove();
- }
-}
-function summernote_table_update(oStyle) {
- var r = range.create();
- if (!oStyle.range || !r || !r.isOnCell() || !r.isOnCellFirst()) {
- $('.o_table_handler').remove();
- return;
- }
- var table = dom.ancestor(oStyle.range.sc, dom.isTable);
- if (!table) { // if the editable tag is inside the table
- return;
- }
- var $editable = $(table).closest('.o_editable');
-
- $('.o_table_handler').remove();
-
- var $dels = $();
- var $adds = $();
- var $tds = $('tr:first', table).children();
- $tds.each(function () {
- var $td = $(this);
- var pos = $td.offset();
-
- var $del = $('<span class="o_table_handler fa fa-minus-square"/>').appendTo('body');
- $del.data('td', this);
- $dels = $dels.add($del);
- $del.css({
- left: ((pos.left + $td.outerWidth()/2)-6) + "px",
- top: (pos.top-6) + "px"
- });
-
- var $add = $('<span class="o_table_handler fa fa-plus-square"/>').appendTo('body');
- $add.data('td', this);
- $adds = $adds.add($add);
- $add.css({
- left: (pos.left-6) + "px",
- top: (pos.top-6) + "px"
- });
- });
-
- var $last = $tds.last();
- var pos = $last.offset();
- var $add = $('<span class="o_table_handler fa fa-plus-square"/>').appendTo('body');
- $adds = $adds.add($add);
- $add.css({
- left: (pos.left+$last.outerWidth()-6) + "px",
- top: (pos.top-6) + "px"
- });
-
- var $table = $(table);
- $dels.data('table', table).on('mousedown', function (event) {
- var td = $(this).data('td');
- $editable.data('NoteHistory').recordUndo($editable);
-
- var newTd;
- if ($(td).siblings().length) {
- var eq = $(td).index();
- $table.find('tr').each(function () {
- $('> td:eq('+eq+')', this).remove();
- });
- newTd = $table.find('tr:first > td:eq('+eq+'), tr:first > td:last').first();
- } else {
- var prev = dom.lastChild(dom.hasContentBefore(dom.ancestorHavePreviousSibling($table[0])));
- $table.remove();
- $('.o_table_handler').remove();
- r = range.create(prev, prev.textContent.length);
- r.select();
- $(r.sc).trigger('mouseup');
- return;
- }
-
- $('.o_table_handler').remove();
- range.create(newTd[0], 0, newTd[0], 0).select();
- newTd.trigger('mouseup');
- });
- $adds.data('table', table).on('mousedown', function (event) {
- var td = $(this).data('td');
- $editable.data('NoteHistory').recordUndo($editable);
-
- var newTd;
- if (td) {
- var eq = $(td).index();
- $table.find('tr').each(function () {
- $('td:eq('+eq+')', this).before('<td>'+dom.blank+'</td>');
- });
- newTd = $table.find('tr:first td:eq('+eq+')');
- } else {
- $table.find('tr').each(function () {
- $(this).append('<td>'+dom.blank+'</td>');
- });
- newTd = $table.find('tr:first td:last');
- }
-
- $('.o_table_handler').remove();
- range.create(newTd[0], 0, newTd[0], 0).select();
- newTd.trigger('mouseup');
- });
-
- $dels.css({
- 'position': 'absolute',
- 'cursor': 'pointer',
- 'background-color': '#fff',
- 'color': '#ff0000'
- });
- $adds.css({
- 'position': 'absolute',
- 'cursor': 'pointer',
- 'background-color': '#fff',
- 'color': '#00ff00'
- });
-}
-var fn_popover_update = eventHandler.modules.popover.update;
-eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) {
- fn_popover_update.call(this, $popover, oStyle, isAirMode);
- if ((isAirMode ? $popover : $popover.parent()).find('.note-table').length) {
- summernote_table_update(oStyle);
- }
-};
-
-var fn_attach = eventHandler.attach;
-eventHandler.attach = function (oLayoutInfo, options) {
- var $editable = oLayoutInfo.editor().hasClass('note-editable') ? oLayoutInfo.editor() : oLayoutInfo.editor().find('.note-editable');
- fn_attach.call(this, oLayoutInfo, options);
- $editable.on("scroll", summernote_table_scroll);
-};
-var fn_detach = eventHandler.detach;
-eventHandler.detach = function (oLayoutInfo, options) {
- var $editable = oLayoutInfo.editor().hasClass('note-editable') ? oLayoutInfo.editor() : oLayoutInfo.editor().find('.note-editable');
- fn_detach.call(this, oLayoutInfo, options);
- $editable.off("scroll", summernote_table_scroll);
- $('.o_table_handler').remove();
-};
-
-options.icons.image.image = "file-image-o";
-$.summernote.lang['en-US'].image.image = "File / Image";
-
-return $.summernote;
-});