summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/lib/summernote/src/js/Renderer.js
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/Renderer.js
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/Renderer.js')
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/Renderer.js1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/addons/web_editor/static/lib/summernote/src/js/Renderer.js b/addons/web_editor/static/lib/summernote/src/js/Renderer.js
new file mode 100644
index 00000000000..a57dab67573
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/src/js/Renderer.js
@@ -0,0 +1,1026 @@
+define([
+ 'summernote/core/agent',
+ 'summernote/core/dom',
+ 'summernote/core/func',
+ 'summernote/core/list'
+], function (agent, dom, func, list) {
+ /**
+ * @class Renderer
+ *
+ * renderer
+ *
+ * rendering toolbar and editable
+ */
+ var Renderer = function () {
+
+ /**
+ * bootstrap button template
+ * @private
+ * @param {String} label button name
+ * @param {Object} [options] button options
+ * @param {String} [options.event] data-event
+ * @param {String} [options.className] button's class name
+ * @param {String} [options.value] data-value
+ * @param {String} [options.title] button's title for popup
+ * @param {String} [options.dropdown] dropdown html
+ * @param {String} [options.hide] data-hide
+ */
+ var tplButton = function (label, options) {
+ var event = options.event;
+ var value = options.value;
+ var title = options.title;
+ var className = options.className;
+ var dropdown = options.dropdown;
+ var hide = options.hide;
+
+ return (dropdown ? '<div class="btn-group' +
+ (className ? ' ' + className : '') + '">' : '') +
+ '<button type="button"' +
+ ' class="btn btn-secondary btn-sm' +
+ ((!dropdown && className) ? ' ' + className : '') +
+ (dropdown ? ' dropdown-toggle' : '') +
+ '"' +
+ (dropdown ? ' data-toggle="dropdown"' : '') +
+ (title ? ' title="' + title + '"' : '') +
+ (event ? ' data-event="' + event + '"' : '') +
+ (value ? ' data-value=\'' + value + '\'' : '') +
+ (hide ? ' data-hide=\'' + hide + '\'' : '') +
+ ' tabindex="-1">' +
+ label +
+ (dropdown ? ' <span class="caret"></span>' : '') +
+ '</button>' +
+ (dropdown || '') +
+ (dropdown ? '</div>' : '');
+ };
+
+ /**
+ * bootstrap icon button template
+ * @private
+ * @param {String} iconClassName
+ * @param {Object} [options]
+ * @param {String} [options.event]
+ * @param {String} [options.value]
+ * @param {String} [options.title]
+ * @param {String} [options.dropdown]
+ */
+ var tplIconButton = function (iconClassName, options) {
+ var label = '<i class="' + iconClassName + '"></i>';
+ return tplButton(label, options);
+ };
+
+ /**
+ * bootstrap popover template
+ * @private
+ * @param {String} className
+ * @param {String} content
+ */
+ var tplPopover = function (className, content) {
+ var $popover = $('<div class="' + className + ' popover bottom in" style="display: none;">' +
+ '<div class="arrow"></div>' +
+ '<div class="popover-body">' +
+ '</div>' +
+ '</div>');
+
+ $popover.find('.popover-body').append(content);
+ return $popover;
+ };
+
+ /**
+ * bootstrap dialog template
+ *
+ * @param {String} className
+ * @param {String} [title='']
+ * @param {String} body
+ * @param {String} [footer='']
+ */
+ var tplDialog = function (className, title, body, footer) {
+ return '<div class="' + className + ' modal" role="dialog" aria-hidden="false">' +
+ '<div class="modal-dialog">' +
+ '<div class="modal-content">' +
+ (title ?
+ '<header class="modal-header">' +
+ '<h4 class="modal-title">' + title + '</h4>' +
+ '<button type="button" class="close" aria-hidden="true" tabindex="-1">&times;</button>' +
+ '</header>' : ''
+ ) +
+ '<main class="modal-body">' + body + '</main>' +
+ (footer ?
+ '<header class="modal-footer">' + footer + '</header>' : ''
+ ) +
+ '</div>' +
+ '</div>' +
+ '</div>';
+ };
+
+ /**
+ * bootstrap dropdown template
+ *
+ * @param {String|String[]} contents
+ * @param {String} [className='']
+ * @param {String} [nodeName='']
+ */
+ var tplDropdown = function (contents, className, nodeName) {
+ var classes = 'dropdown-menu' + (className ? ' ' + className : '');
+ nodeName = nodeName || 'ul';
+ if (contents instanceof Array) {
+ contents = contents.join('');
+ }
+
+ return '<' + nodeName + ' class="' + classes + '">' + contents + '</' + nodeName + '>';
+ };
+
+ var tplButtonInfo = {
+ picture: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.image.image, {
+ event: 'showImageDialog',
+ title: lang.image.image,
+ hide: true
+ });
+ },
+ link: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.link.link, {
+ event: 'showLinkDialog',
+ title: lang.link.link,
+ hide: true
+ });
+ },
+ table: function (lang, options) {
+ var dropdown = [
+ '<div class="note-dimension-picker">',
+ '<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>',
+ '<div class="note-dimension-picker-highlighted"></div>',
+ '<div class="note-dimension-picker-unhighlighted"></div>',
+ '</div>',
+ '<div class="note-dimension-display"> 1 x 1 </div>'
+ ];
+
+ return tplIconButton(options.iconPrefix + options.icons.table.table, {
+ title: lang.table.table,
+ dropdown: tplDropdown(dropdown, 'note-table')
+ });
+ },
+ style: function (lang, options) {
+ var items = options.styleTags.reduce(function (memo, v) {
+ var label = lang.style[v === 'p' ? 'normal' : v];
+ return memo + '<li><a class="dropdown-item" data-event="formatBlock" href="#" data-value="' + v + '">' +
+ (
+ (v === 'p' || v === 'pre') ? label :
+ '<' + v + '>' + label + '</' + v + '>'
+ ) +
+ '</a></li>';
+ }, '');
+
+ return tplIconButton(options.iconPrefix + options.icons.style.style, {
+ title: lang.style.style,
+ dropdown: tplDropdown(items)
+ });
+ },
+ fontname: function (lang, options) {
+ var realFontList = [];
+ var items = options.fontNames.reduce(function (memo, v) {
+ if (!agent.isFontInstalled(v) && !list.contains(options.fontNamesIgnoreCheck, v)) {
+ return memo;
+ }
+ realFontList.push(v);
+ return memo + '<li><a data-event="fontName" href="#" data-value="' + v + '" style="font-family:\'' + v + '\'">' +
+ '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
+ '</a></li>';
+ }, '');
+
+ var hasDefaultFont = agent.isFontInstalled(options.defaultFontName);
+ var defaultFontName = (hasDefaultFont) ? options.defaultFontName : realFontList[0];
+
+ var label = '<span class="note-current-fontname">' +
+ defaultFontName +
+ '</span>';
+ return tplButton(label, {
+ title: lang.font.name,
+ className: 'note-fontname',
+ dropdown: tplDropdown(items, 'note-check')
+ });
+ },
+ fontsize: function (lang, options) {
+ var items = options.fontSizes.reduce(function (memo, v) {
+ return memo + '<li><a data-event="fontSize" href="#" data-value="' + v + '">' +
+ '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
+ '</a></li>';
+ }, '');
+
+ var label = '<span class="note-current-fontsize">11</span>';
+ return tplButton(label, {
+ title: lang.font.size,
+ className: 'note-fontsize',
+ dropdown: tplDropdown(items, 'note-check')
+ });
+ },
+ color: function (lang, options) {
+ var colorButtonLabel = '<i class="' +
+ options.iconPrefix + options.icons.color.recent +
+ '" id="colors_preview" style="color:white;background-color:#B35E9B"></i>';
+ var colorButton = tplButton(colorButtonLabel, {
+ className: 'note-recent-color',
+ title: lang.color.recent,
+ event: 'color',
+ value: '{"backColor":"#B35E9B"}'
+ });
+
+ var items = [
+ '<li class="flex"><div class="btn-group flex-column">',
+ '<div class="note-palette-title">' + lang.color.background + '</div>',
+ '<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>',
+ '</div><div class="btn-group flex-column">',
+ '<div class="note-palette-title">' + lang.color.foreground + '</div>',
+ '<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">',
+ lang.color.resetToDefault,
+ '</div>',
+ '<div class="note-color-palette" data-target-event="foreColor"></div>',
+ '</div></li>'
+ ];
+
+ var moreButton = tplButton('', {
+ title: lang.color.more,
+ dropdown: tplDropdown(items)
+ });
+
+ return colorButton + moreButton;
+ },
+ bold: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.bold, {
+ event: 'bold',
+ title: lang.font.bold
+ });
+ },
+ italic: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.italic, {
+ event: 'italic',
+ title: lang.font.italic
+ });
+ },
+ underline: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.underline, {
+ event: 'underline',
+ title: lang.font.underline
+ });
+ },
+ strikethrough: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.strikethrough, {
+ event: 'strikethrough',
+ title: lang.font.strikethrough
+ });
+ },
+ superscript: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.superscript, {
+ event: 'superscript',
+ title: lang.font.superscript
+ });
+ },
+ subscript: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.subscript, {
+ event: 'subscript',
+ title: lang.font.subscript
+ });
+ },
+ clear: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.font.clear, {
+ event: 'removeFormat',
+ title: lang.font.clear
+ });
+ },
+ ul: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.lists.unordered, {
+ event: 'insertUnorderedList',
+ title: lang.lists.unordered
+ });
+ },
+ ol: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.lists.ordered, {
+ event: 'insertOrderedList',
+ title: lang.lists.ordered
+ });
+ },
+ paragraph: function (lang, options) {
+ var leftButton = tplIconButton(options.iconPrefix + options.icons.paragraph.left, {
+ title: lang.paragraph.left,
+ event: 'justifyLeft'
+ });
+ var centerButton = tplIconButton(options.iconPrefix + options.icons.paragraph.center, {
+ title: lang.paragraph.center,
+ event: 'justifyCenter'
+ });
+ var rightButton = tplIconButton(options.iconPrefix + options.icons.paragraph.right, {
+ title: lang.paragraph.right,
+ event: 'justifyRight'
+ });
+ var justifyButton = tplIconButton(options.iconPrefix + options.icons.paragraph.justify, {
+ title: lang.paragraph.justify,
+ event: 'justifyFull'
+ });
+
+ var outdentButton = tplIconButton(options.iconPrefix + options.icons.paragraph.outdent, {
+ title: lang.paragraph.outdent,
+ event: 'outdent'
+ });
+ var indentButton = tplIconButton(options.iconPrefix + options.icons.paragraph.indent, {
+ title: lang.paragraph.indent,
+ event: 'indent'
+ });
+
+ var dropdown = [
+ '<div class="note-align btn-group">',
+ leftButton + centerButton + rightButton + justifyButton,
+ '</div><div class="note-list btn-group">',
+ indentButton + outdentButton,
+ '</div>'
+ ];
+
+ return tplIconButton(options.iconPrefix + options.icons.paragraph.paragraph, {
+ title: lang.paragraph.paragraph,
+ dropdown: tplDropdown(dropdown, '', 'div')
+ });
+ },
+ height: function (lang, options) {
+ var items = options.lineHeights.reduce(function (memo, v) {
+ return memo + '<li><a data-event="lineHeight" href="#" data-value="' + parseFloat(v) + '">' +
+ '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
+ '</a></li>';
+ }, '');
+
+ return tplIconButton(options.iconPrefix + options.icons.font.height, {
+ title: lang.font.height,
+ dropdown: tplDropdown(items, 'note-check')
+ });
+
+ },
+ help: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.options.help, {
+ event: 'showHelpDialog',
+ title: lang.options.help,
+ hide: true
+ });
+ },
+ fullscreen: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.options.fullscreen, {
+ event: 'fullscreen',
+ title: lang.options.fullscreen
+ });
+ },
+ codeview: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.options.codeview, {
+ event: 'codeview',
+ title: lang.options.codeview
+ });
+ },
+ undo: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.history.undo, {
+ event: 'undo',
+ title: lang.history.undo
+ });
+ },
+ redo: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.history.redo, {
+ event: 'redo',
+ title: lang.history.redo
+ });
+ },
+ hr: function (lang, options) {
+ return tplIconButton(options.iconPrefix + options.icons.hr.insert, {
+ event: 'insertHorizontalRule',
+ title: lang.hr.insert
+ });
+ }
+ };
+
+ var tplPopovers = function (lang, options) {
+ var tplLinkPopover = function () {
+ var linkButton = tplIconButton(options.iconPrefix + options.icons.link.edit, {
+ title: lang.link.edit,
+ event: 'showLinkDialog',
+ hide: true
+ });
+ var unlinkButton = tplIconButton(options.iconPrefix + options.icons.link.unlink, {
+ title: lang.link.unlink,
+ event: 'unlink'
+ });
+ var content = '<a href="http://www.google.com" target="_blank">www.google.com</a>&nbsp;&nbsp;' +
+ '<div class="note-insert btn-group">' +
+ linkButton + unlinkButton +
+ '</div>';
+ return tplPopover('note-link-popover', content);
+ };
+
+ var tplImagePopover = function () {
+ var autoButton = tplButton('<span class="note-fontsize-10">Auto</span>', {
+ event: 'resize',
+ value: 'auto'
+ });
+ var fullButton = tplButton('<span class="note-fontsize-10">100%</span>', {
+ title: lang.image.resizeFull,
+ event: 'resize',
+ value: '1'
+ });
+ var halfButton = tplButton('<span class="note-fontsize-10">50%</span>', {
+ title: lang.image.resizeHalf,
+ event: 'resize',
+ value: '0.5'
+ });
+ var quarterButton = tplButton('<span class="note-fontsize-10">25%</span>', {
+ title: lang.image.resizeQuarter,
+ event: 'resize',
+ value: '0.25'
+ });
+
+ var leftButton = tplIconButton(options.iconPrefix + options.icons.image.floatLeft, {
+ title: lang.image.floatLeft,
+ event: 'floatMe',
+ value: 'left'
+ });
+ var rightButton = tplIconButton(options.iconPrefix + options.icons.image.floatRight, {
+ title: lang.image.floatRight,
+ event: 'floatMe',
+ value: 'right'
+ });
+ var justifyButton = tplIconButton(options.iconPrefix + options.icons.image.floatNone, {
+ title: lang.image.floatNone,
+ event: 'floatMe',
+ value: 'none'
+ });
+
+ var roundedButton = tplIconButton(options.iconPrefix + options.icons.image.shapeRounded, {
+ title: lang.image.shapeRounded,
+ event: 'imageShape',
+ value: 'rounded'
+ });
+ var circleButton = tplIconButton(options.iconPrefix + options.icons.image.shapeCircle, {
+ title: lang.image.shapeCircle,
+ event: 'imageShape',
+ value: 'rounded-circle'
+ });
+ var thumbnailButton = tplIconButton(options.iconPrefix + options.icons.image.shapeThumbnail, {
+ title: lang.image.shapeThumbnail,
+ event: 'imageShape',
+ value: 'img-thumbnail'
+ });
+ var noneButton = tplIconButton(options.iconPrefix + options.icons.image.shapeNone, {
+ title: lang.image.shapeNone,
+ event: 'imageShape',
+ value: ''
+ });
+
+ var removeButton = tplIconButton(options.iconPrefix + options.icons.image.remove, {
+ title: lang.image.remove,
+ event: 'removeMedia',
+ value: 'none'
+ });
+
+ var content = (options.disableResizeImage ? '' : '<div class="btn-group">' + autoButton + fullButton + halfButton + quarterButton + '</div>') +
+ '<div class="btn-group">' + leftButton + rightButton + justifyButton + '</div>' +
+ '<div class="btn-group">' + roundedButton + circleButton + thumbnailButton + noneButton + '</div>' +
+ '<div class="btn-group">' + removeButton + '</div>';
+ return tplPopover('note-image-popover', content);
+ };
+
+ var tplAirPopover = function () {
+ var $content = $('<div />');
+ for (var idx = 0, len = options.airPopover.length; idx < len; idx ++) {
+ var group = options.airPopover[idx];
+
+ var $group = $('<div class="note-' + group[0] + ' btn-group">');
+ for (var i = 0, lenGroup = group[1].length; i < lenGroup; i++) {
+ var $button = $(tplButtonInfo[group[1][i]](lang, options));
+
+ $button.attr('data-name', group[1][i]);
+
+ $group.append($button);
+ }
+ $content.append($group);
+ }
+
+ return tplPopover('note-air-popover', $content.children());
+ };
+
+ var $notePopover = $('<div class="note-popover" />');
+
+ $notePopover.append(tplLinkPopover());
+ $notePopover.append(tplImagePopover());
+
+ if (options.airMode) {
+ $notePopover.append(tplAirPopover());
+ }
+
+ return $notePopover;
+ };
+
+ this.tplButtonInfo = tplButtonInfo; // ODOO: allow access for override
+ this.tplPopovers = tplPopovers; // ODOO: allow access for override
+
+ var tplHandles = function (options) {
+ return '<div class="note-handle">' +
+ '<div class="note-control-selection">' +
+ '<div class="note-control-selection-bg"></div>' +
+ '<div class="note-control-holder note-control-nw"></div>' +
+ '<div class="note-control-holder note-control-ne"></div>' +
+ '<div class="note-control-holder note-control-sw"></div>' +
+ '<div class="' +
+ (options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing') +
+ ' note-control-se"></div>' +
+ (options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>') +
+ '</div>' +
+ '</div>';
+ };
+
+ /**
+ * shortcut table template
+ * @param {String} title
+ * @param {String} body
+ */
+ var tplShortcut = function (title, keys) {
+ var keyClass = 'note-shortcut-col col-6 note-shortcut-';
+ var body = [];
+
+ for (var i in keys) {
+ if (keys.hasOwnProperty(i)) {
+ body.push(
+ '<div class="' + keyClass + 'key">' + keys[i].kbd + '</div>' +
+ '<div class="' + keyClass + 'name">' + keys[i].text + '</div>'
+ );
+ }
+ }
+
+ return '<div class="note-shortcut-row row"><div class="' + keyClass + 'title offset-6">' + title + '</div></div>' +
+ '<div class="note-shortcut-row row">' + body.join('</div><div class="note-shortcut-row row">') + '</div>';
+ };
+
+ var tplShortcutText = function (lang) {
+ var keys = [
+ { kbd: '⌘ + B', text: lang.font.bold },
+ { kbd: '⌘ + I', text: lang.font.italic },
+ { kbd: '⌘ + U', text: lang.font.underline },
+ { kbd: '⌘ + \\', text: lang.font.clear }
+ ];
+
+ return tplShortcut(lang.shortcut.textFormatting, keys);
+ };
+
+ var tplShortcutAction = function (lang) {
+ var keys = [
+ { kbd: '⌘ + Z', text: lang.history.undo },
+ { kbd: '⌘ + ⇧ + Z', text: lang.history.redo },
+ { kbd: '⌘ + ]', text: lang.paragraph.indent },
+ { kbd: '⌘ + [', text: lang.paragraph.outdent },
+ { kbd: '⌘ + ENTER', text: lang.hr.insert }
+ ];
+
+ return tplShortcut(lang.shortcut.action, keys);
+ };
+
+ var tplShortcutPara = function (lang) {
+ var keys = [
+ { kbd: '⌘ + ⇧ + L', text: lang.paragraph.left },
+ { kbd: '⌘ + ⇧ + E', text: lang.paragraph.center },
+ { kbd: '⌘ + ⇧ + R', text: lang.paragraph.right },
+ { kbd: '⌘ + ⇧ + J', text: lang.paragraph.justify },
+ { kbd: '⌘ + ⇧ + NUM7', text: lang.lists.ordered },
+ { kbd: '⌘ + ⇧ + NUM8', text: lang.lists.unordered }
+ ];
+
+ return tplShortcut(lang.shortcut.paragraphFormatting, keys);
+ };
+
+ var tplShortcutStyle = function (lang) {
+ var keys = [
+ { kbd: '⌘ + NUM0', text: lang.style.normal },
+ { kbd: '⌘ + NUM1', text: lang.style.h1 },
+ { kbd: '⌘ + NUM2', text: lang.style.h2 },
+ { kbd: '⌘ + NUM3', text: lang.style.h3 },
+ { kbd: '⌘ + NUM4', text: lang.style.h4 },
+ { kbd: '⌘ + NUM5', text: lang.style.h5 },
+ { kbd: '⌘ + NUM6', text: lang.style.h6 }
+ ];
+
+ return tplShortcut(lang.shortcut.documentStyle, keys);
+ };
+
+ var tplExtraShortcuts = function (lang, options) {
+ var extraKeys = options.extraKeys;
+ var keys = [];
+
+ for (var key in extraKeys) {
+ if (extraKeys.hasOwnProperty(key)) {
+ keys.push({ kbd: key, text: extraKeys[key] });
+ }
+ }
+
+ return tplShortcut(lang.shortcut.extraKeys, keys);
+ };
+
+ var tplShortcutTable = function (lang, options) {
+ var colClass = 'class="note-shortcut note-shortcut-col col-md-6 col-12"';
+ var template = [
+ '<div ' + colClass + '>' + tplShortcutAction(lang, options) + '</div>' +
+ '<div ' + colClass + '>' + tplShortcutText(lang, options) + '</div>',
+ '<div ' + colClass + '>' + tplShortcutStyle(lang, options) + '</div>' +
+ '<div ' + colClass + '>' + tplShortcutPara(lang, options) + '</div>'
+ ];
+
+ if (options.extraKeys) {
+ template.push('<div ' + colClass + '>' + tplExtraShortcuts(lang, options) + '</div>');
+ }
+
+ return '<div class="note-shortcut-row row">' +
+ template.join('</div><div class="note-shortcut-row row">') +
+ '</div>';
+ };
+
+ var replaceMacKeys = function (sHtml) {
+ return sHtml.replace(/⌘/g, 'Ctrl').replace(/⇧/g, 'Shift');
+ };
+
+ var tplDialogInfo = {
+ image: function (lang, options) {
+ var imageLimitation = '';
+ if (options.maximumImageFileSize) {
+ var unit = Math.floor(Math.log(options.maximumImageFileSize) / Math.log(1024));
+ var readableSize = (options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
+ ' ' + ' KMGTP'[unit] + 'B';
+ imageLimitation = '<small>' + lang.image.maximumFileSize + ' : ' + readableSize + '</small>';
+ }
+
+ var body = '<div class="form-group row note-group-select-from-files">' +
+ '<label>' + lang.image.selectFromFiles + '</label>' +
+ '<input class="note-image-input form-control" type="file" name="files" accept="image/*" multiple="multiple" />' +
+ imageLimitation +
+ '</div>' +
+ '<div class="form-group row">' +
+ '<label>' + lang.image.url + '</label>' +
+ '<input class="note-image-url form-control col-md-12" type="text" />' +
+ '</div>';
+ var footer = '<button href="#" class="btn btn-primary note-image-btn disabled" disabled>' + lang.image.insert + '</button>';
+ return tplDialog('note-image-dialog', lang.image.insert, body, footer);
+ },
+
+ link: function (lang, options) {
+ var body = '<div class="form-group row">' +
+ '<label>' + lang.link.textToDisplay + '</label>' +
+ '<input class="note-link-text form-control col-md-12" type="text" />' +
+ '</div>' +
+ '<div class="form-group row">' +
+ '<label>' + lang.link.url + '</label>' +
+ '<input class="note-link-url form-control col-md-12" type="text" value="http://" />' +
+ '</div>' +
+ (!options.disableLinkTarget ?
+ '<div class="checkbox">' +
+ '<label>' + '<input type="checkbox" checked> ' +
+ lang.link.openInNewWindow +
+ '</label>' +
+ '</div>' : ''
+ );
+ var footer = '<button href="#" class="btn btn-primary note-link-btn disabled" disabled>' + lang.link.insert + '</button>';
+ return tplDialog('note-link-dialog', lang.link.insert, body, footer);
+ },
+
+ help: function (lang, options) {
+ var body = '<a class="modal-close float-right" aria-hidden="true" tabindex="-1">' + lang.shortcut.close + '</a>' +
+ '<div class="title">' + lang.shortcut.shortcuts + '</div>' +
+ (agent.isMac ? tplShortcutTable(lang, options) : replaceMacKeys(tplShortcutTable(lang, options))) +
+ '<p class="text-center">' +
+ '<a href="//summernote.org/" target="_blank">Summernote @VERSION</a> · ' +
+ '<a href="//github.com/summernote/summernote" target="_blank">Project</a> · ' +
+ '<a href="//github.com/summernote/summernote/issues" target="_blank">Issues</a>' +
+ '</p>';
+ return tplDialog('note-help-dialog', '', body, '');
+ }
+ };
+
+ var tplDialogs = function (lang, options) {
+ var dialogs = '';
+
+ $.each(tplDialogInfo, function (idx, tplDialog) {
+ dialogs += tplDialog(lang, options);
+ });
+
+ return '<div class="note-dialog">' + dialogs + '</div>';
+ };
+
+ var tplStatusbar = function () {
+ return '<div class="note-resizebar">' +
+ '<div class="note-icon-bar"></div>' +
+ '<div class="note-icon-bar"></div>' +
+ '<div class="note-icon-bar"></div>' +
+ '</div>';
+ };
+
+ var representShortcut = function (str) {
+ if (agent.isMac) {
+ str = str.replace('CMD', '⌘').replace('SHIFT', '⇧');
+ }
+
+ return str.replace('BACKSLASH', '\\')
+ .replace('SLASH', '/')
+ .replace('LEFTBRACKET', '[')
+ .replace('RIGHTBRACKET', ']');
+ };
+
+ /**
+ * createTooltip
+ *
+ * @param {jQuery} $container
+ * @param {Object} keyMap
+ * @param {String} [sPlacement]
+ */
+ var createTooltip = function ($container, keyMap, sPlacement) {
+ var invertedKeyMap = func.invertObject(keyMap);
+ var $buttons = $container.find('button');
+
+ $buttons.each(function (i, elBtn) {
+ var $btn = $(elBtn);
+ var sShortcut = invertedKeyMap[$btn.data('event')];
+ if (sShortcut) {
+ $btn.attr('title', function (i, v) {
+ return v + ' (' + representShortcut(sShortcut) + ')';
+ });
+ }
+ // bootstrap tooltip on btn-group bug
+ // https://github.com/twbs/bootstrap/issues/5687
+ }).tooltip({
+ container: 'body',
+ trigger: 'hover',
+ placement: sPlacement || 'top'
+ }).on('click', function () {
+ $(this).tooltip('hide');
+ });
+ };
+
+ // createPalette
+ var createPalette = function ($container, options) {
+ var colorInfo = options.colors;
+ $container.find('.note-color-palette').each(function () {
+ var $palette = $(this), eventName = $palette.attr('data-target-event');
+ var paletteContents = [];
+ for (var row = 0, lenRow = colorInfo.length; row < lenRow; row++) {
+ var colors = colorInfo[row];
+ var buttons = [];
+ for (var col = 0, lenCol = colors.length; col < lenCol; col++) {
+ var color = colors[col];
+ buttons.push(['<button type="button" class="note-color-btn" style="background-color:', color,
+ ';" data-event="', eventName,
+ '" data-value="', color,
+ '" title="', color,
+ '" data-toggle="button" tabindex="-1"></button>'].join(''));
+ }
+ paletteContents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
+ }
+ $palette.html(paletteContents.join(''));
+ });
+ };
+
+ this.createPalette = createPalette; // ODOO: allow access for override
+
+ /**
+ * create summernote layout (air mode)
+ *
+ * @param {jQuery} $holder
+ * @param {Object} options
+ */
+ this.createLayoutByAirMode = function ($holder, options) {
+ var langInfo = options.langInfo;
+ var keyMap = options.keyMap[agent.isMac ? 'mac' : 'pc'];
+ var id = func.uniqueId();
+
+ $holder.addClass('note-air-editor note-editable'); // ODOO: removing panel-body class to remove unwanted style
+ $holder.attr({
+ 'data-note-id': id, // ODOO: we use [data-note-id="{id}"] instead of [id="{id}"]
+ // 'id': 'note-editor-' + id,
+ 'contentEditable': true
+ });
+
+ var body = document.body;
+ var $container = $('#web_editor-toolbars')
+
+ // create Popover
+ var $popover = $(this.tplPopovers(langInfo, options)); // ODOO: user (maybe) overrided method
+ $popover.addClass('note-air-layout');
+ $popover.attr('id', 'note-popover-' + id);
+ $popover.appendTo($container);
+ createTooltip($popover, keyMap);
+ this.createPalette($popover, options); // ODOO: use (maybe) overrided method
+
+ // create Handle
+ var $handle = $(tplHandles(options));
+ $handle.addClass('note-air-layout');
+ $handle.attr('id', 'note-handle-' + id);
+ $handle.appendTo($container);
+
+ // create Dialog
+ var $dialog = $(tplDialogs(langInfo, options));
+ $dialog.addClass('note-air-layout');
+ $dialog.attr('id', 'note-dialog-' + id);
+ $dialog.find('button.close, a.modal-close').click(function () {
+ $(this).closest('.modal').modal('hide');
+ });
+ $dialog.appendTo($container);
+ };
+
+ /**
+ * create summernote layout (normal mode)
+ *
+ * @param {jQuery} $holder
+ * @param {Object} options
+ */
+ this.createLayoutByFrame = function ($holder, options) {
+ var langInfo = options.langInfo;
+
+ //01. create Editor
+ var $editor = $('<div class="note-editor panel panel-default" />');
+ if (options.width) {
+ $editor.width(options.width);
+ }
+
+ //02. statusbar (resizebar)
+ if (options.height > 0) {
+ $('<div class="note-statusbar">' + (options.disableResizeEditor ? '' : tplStatusbar()) + '</div>').prependTo($editor);
+ }
+
+ //03 editing area
+ var $editingArea = $('<div class="note-editing-area" />');
+ //03. create editable
+ var isContentEditable = !$holder.is(':disabled');
+ var $editable = $('<div class="note-editable panel-body" contentEditable="' + isContentEditable + '"></div>').prependTo($editingArea);
+
+ if (options.height) {
+ $editable.height(options.height);
+ }
+ if (options.direction) {
+ $editable.attr('dir', options.direction);
+ }
+ var placeholder = $holder.attr('placeholder') || options.placeholder;
+ if (placeholder) {
+ $editable.attr('data-placeholder', placeholder);
+ }
+
+ $editable.html(dom.html($holder) || dom.emptyPara);
+
+ //031. create codable
+ $('<textarea class="note-codable"></textarea>').prependTo($editingArea);
+
+ //04. create Popover
+ var $popover = $(this.tplPopovers(langInfo, options)).prependTo($editingArea); // ODOO: use (maybe) overrided method
+ this.createPalette($popover, options); // ODOO: use (maybe) overrided method
+ createTooltip($popover, keyMap);
+
+ //05. handle(control selection, ...)
+ $(tplHandles(options)).prependTo($editingArea);
+
+ $editingArea.prependTo($editor);
+
+ //06. create Toolbar
+ var $toolbar = $('<div class="note-toolbar panel-heading" />');
+ for (var idx = 0, len = options.toolbar.length; idx < len; idx ++) {
+ var groupName = options.toolbar[idx][0];
+ var groupButtons = options.toolbar[idx][1];
+
+ var $group = $('<div class="note-' + groupName + ' btn-group" />');
+ for (var i = 0, btnLength = groupButtons.length; i < btnLength; i++) {
+ var buttonInfo = tplButtonInfo[groupButtons[i]];
+ // continue creating toolbar even if a button doesn't exist
+ if (!$.isFunction(buttonInfo)) { continue; }
+
+ var $button = $(buttonInfo(langInfo, options));
+ $button.attr('data-name', groupButtons[i]); // set button's alias, becuase to get button element from $toolbar
+ $group.append($button);
+ }
+ $toolbar.append($group);
+ }
+
+ var keyMap = options.keyMap[agent.isMac ? 'mac' : 'pc'];
+ this.createPalette($toolbar, options); // ODOO: use (maybe) overrided method
+ createTooltip($toolbar, keyMap, 'bottom');
+ $toolbar.prependTo($editor);
+
+ //07. create Dropzone
+ $('<div class="note-dropzone"><div class="note-dropzone-message"></div></div>').prependTo($editor);
+
+ //08. create Dialog
+ var $dialogContainer = options.dialogsInBody ? $(document.body) : $editor;
+ var $dialog = $(tplDialogs(langInfo, options)).prependTo($dialogContainer);
+ $dialog.find('button.close, a.modal-close').click(function () {
+ $(this).closest('.modal').modal('hide');
+ });
+
+ //09. Editor/Holder switch
+ $editor.insertAfter($holder);
+ $holder.hide();
+ };
+
+ this.hasNoteEditor = function ($holder) {
+ return this.noteEditorFromHolder($holder).length > 0;
+ };
+
+ this.noteEditorFromHolder = function ($holder) {
+ if ($holder.hasClass('note-air-editor')) {
+ return $holder;
+ } else if ($holder.next().hasClass('note-editor')) {
+ return $holder.next();
+ } else {
+ return $();
+ }
+ };
+
+ /**
+ * create summernote layout
+ *
+ * @param {jQuery} $holder
+ * @param {Object} options
+ */
+ this.createLayout = function ($holder, options) {
+ if (options.airMode) {
+ this.createLayoutByAirMode($holder, options);
+ } else {
+ this.createLayoutByFrame($holder, options);
+ }
+ };
+
+ /**
+ * returns layoutInfo from holder
+ *
+ * @param {jQuery} $holder - placeholder
+ * @return {Object}
+ */
+ this.layoutInfoFromHolder = function ($holder) {
+ var $editor = this.noteEditorFromHolder($holder);
+ if (!$editor.length) {
+ return;
+ }
+
+ // connect $holder to $editor
+ $editor.data('holder', $holder);
+
+ return dom.buildLayoutInfo($editor);
+ };
+
+ /**
+ * removeLayout
+ *
+ * @param {jQuery} $holder - placeholder
+ * @param {Object} layoutInfo
+ * @param {Object} options
+ *
+ */
+ this.removeLayout = function ($holder, layoutInfo, options) {
+ if (options.airMode) {
+ $holder.removeClass('note-air-editor note-editable')
+ .removeAttr('contentEditable'); // ODOO: removed id 'id contentEditable'
+
+ layoutInfo.popover().remove();
+ layoutInfo.handle().remove();
+ layoutInfo.dialog().remove();
+ } else {
+ $holder.html(layoutInfo.editable().html());
+
+ if (options.dialogsInBody) {
+ layoutInfo.dialog().remove();
+ }
+ layoutInfo.editor().remove();
+ $holder.show();
+ }
+ };
+
+ /**
+ *
+ * @return {Object}
+ * @return {function(label, options=):string} return.button {@link #tplButton function to make text button}
+ * @return {function(iconClass, options=):string} return.iconButton {@link #tplIconButton function to make icon button}
+ * @return {function(className, title=, body=, footer=):string} return.dialog {@link #tplDialog function to make dialog}
+ */
+ this.getTemplate = function () {
+ return {
+ button: tplButton,
+ iconButton: tplIconButton,
+ dialog: tplDialog,
+ dropdown: tplDropdown // ODOO: suggest upstream
+ };
+ };
+
+ /**
+ * add button information
+ *
+ * @param {String} name button name
+ * @param {Function} buttonInfo function to make button, reference to {@link #tplButton},{@link #tplIconButton}
+ */
+ this.addButtonInfo = function (name, buttonInfo) {
+ tplButtonInfo[name] = buttonInfo;
+ };
+
+ /**
+ *
+ * @param {String} name
+ * @param {Function} dialogInfo function to make dialog, reference to {@link #tplDialog}
+ */
+ this.addDialogInfo = function (name, dialogInfo) {
+ tplDialogInfo[name] = dialogInfo;
+ };
+ };
+
+ return Renderer;
+});