From 4f925af06fd3cebfcfb56eb947ab68c8a2ca4490 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 7 Jun 2017 17:45:15 +0200 Subject: [MOV] web_editor, website: reorganize files --- addons/web_editor/static/src/js/editor/editor.js | 149 ++ addons/web_editor/static/src/js/editor/rte.js | 637 ++++++ .../static/src/js/editor/rte.summernote.js | 983 ++++++++ .../static/src/js/editor/snippets.editor.js | 1154 ++++++++++ .../static/src/js/editor/snippets.options.js | 993 ++++++++ .../web_editor/static/src/js/editor/summernote.js | 2383 ++++++++++++++++++++ .../web_editor/static/src/js/editor/transcoder.js | 234 ++ .../web_editor/static/src/js/editor/translator.js | 319 +++ 8 files changed, 6852 insertions(+) create mode 100644 addons/web_editor/static/src/js/editor/editor.js create mode 100644 addons/web_editor/static/src/js/editor/rte.js create mode 100644 addons/web_editor/static/src/js/editor/rte.summernote.js create mode 100644 addons/web_editor/static/src/js/editor/snippets.editor.js create mode 100644 addons/web_editor/static/src/js/editor/snippets.options.js create mode 100644 addons/web_editor/static/src/js/editor/summernote.js create mode 100644 addons/web_editor/static/src/js/editor/transcoder.js create mode 100644 addons/web_editor/static/src/js/editor/translator.js (limited to 'addons/web_editor/static/src/js/editor') diff --git a/addons/web_editor/static/src/js/editor/editor.js b/addons/web_editor/static/src/js/editor/editor.js new file mode 100644 index 00000000000..240a0a36121 --- /dev/null +++ b/addons/web_editor/static/src/js/editor/editor.js @@ -0,0 +1,149 @@ +odoo.define('web_editor.editor', function (require) { +"use strict"; + +var ajax = require('web.ajax'); +var Dialog = require('web.Dialog'); +var Widget = require('web.Widget'); +var core = require('web.core'); +var base = require('web_editor.base'); +var rte = require('web_editor.rte'); + +var qweb = core.qweb; +var _t = core._t; +var editor = {}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +editor.dummy = function () {return true;}; // used for snippets, options... + +editor.editable = !!($('html').data('editable') || $("[data-oe-model]").length); // temporary hack, this should be done in python + +ajax.loadXML('/web_editor/static/src/xml/editor.xml', qweb); + +$(document).on('click', '.note-editable', function (ev) { + ev.preventDefault(); +}); + +$(document).on('submit', '.note-editable form .btn', function (ev) { + ev.preventDefault(); // Disable form submition in editable mode +}); + +$(document).on('hide.bs.dropdown', '.dropdown', function (ev) { + // Prevent dropdown closing when a contenteditable children is focused + if (ev.originalEvent + && $(ev.target).has(ev.originalEvent.target).length + && $(ev.originalEvent.target).is('[contenteditable]')) { + ev.preventDefault(); + } +}); + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +editor.reload = function () { + location.hash = "scrollTop=" + window.document.body.scrollTop; + if (location.search.indexOf("enable_editor") > -1) { + window.location.href = window.location.href.replace(/&?enable_editor(=[^&]*)?/g, ''); + } else { + window.location.reload(true); + } +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* ----- TOP EDITOR BAR FOR ADMIN ---- */ + +base.ready().then(function () { + if (editor.editable && location.search.indexOf("enable_editor") >= 0) { + editor.editor_bar = new editor.Class(); + editor.editor_bar.prependTo(document.body); + } +}); + +editor.Class = Widget.extend({ + template: 'web_editor.editorbar', + events: { + 'click button[data-action=save]': 'save', + 'click button[data-action=cancel]': 'cancel', + }, + init: function (parent) { + var self = this; + var res = this._super.apply(this, arguments); + this.parent = parent; + this.rte = new rte.Class(this); + this.rte.on('rte:start', this, function () { + self.trigger('rte:start'); + }); + return res; + }, + start: function () { + $('.dropdown-toggle').dropdown(); + + this.display_placeholder(); + + this.rte.on('change', this, this.proxy('rte_changed')); + this.rte.start(); + + var flag = false; + window.onbeforeunload = function (event) { + if (rte.history.getEditableHasUndo().length && !flag) { + flag = true; + _.defer(function () { flag=false; }); + return _t('This document is not saved!'); + } + }; + + return this._super.apply(this, arguments); + }, + display_placeholder: function () { + var $area = $("#wrapwrap").find("[data-oe-model] .oe_structure.oe_empty, [data-oe-model].oe_structure.oe_empty, [data-oe-type=html]") + .filter(".oe_not_editable") + .filter(".oe_no_empty"); + + this.on('rte:start', this, function () { + $area.attr("data-oe-placeholder", _t("Write Your Text Here")); + }); + this.on("snippets:ready", this, function () { + if ($("body").hasClass("editor_has_snippets")) { + $area.attr("data-oe-placeholder", _t("Write Your Text or Drag a Block Here")); + } + }); + + $(document).on("keyup", function (event) { + if((event.keyCode === 8 || event.keyCode === 46)) { + var $target = $(event.target).closest(".o_editable"); + if(!$target.is(":has(*:not(p):not(br))") && !$target.text().match(/\S/)) { + $target.empty(); + } + } + }); + }, + rte_changed: function () {}, + save: function () { + return this.rte.save().then(function () { + editor.reload(); + }); + }, + /** + * Saves an RTE content, which always corresponds to a view section (?). + */ + save_without_reload: function () { + return this.rte.save(); + }, + cancel: function () { + return new $.Deferred(function (d) { + if (!rte.history.getEditableHasUndo().length) { + return d.resolve(); + } + var confirm = Dialog.confirm(null, _t("If you discard the current edition, all unsaved changes will be lost. You can cancel to return to the edition mode."), { + confirm_callback: d.resolve.bind(d) + }); + confirm.on("closed", d, d.reject); + }).then(function () { + window.onbeforeunload = null; + editor.reload(); + }); + }, +}); + +return editor; +}); diff --git a/addons/web_editor/static/src/js/editor/rte.js b/addons/web_editor/static/src/js/editor/rte.js new file mode 100644 index 00000000000..92ab133263a --- /dev/null +++ b/addons/web_editor/static/src/js/editor/rte.js @@ -0,0 +1,637 @@ +odoo.define('web_editor.rte', function (require) { +'use strict'; + +var ajax = require('web.ajax'); +var core = require('web.core'); +var concurrency = require('web.concurrency'); +var Widget = require('web.Widget'); +var summernote = require('web_editor.summernote'); +var base = require('web_editor.base'); + +var _t = core._t; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Summernote Lib (neek change to make accessible: method and object) */ + +var dom = summernote.core.dom; +var range = summernote.core.range; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Change History to have a global History for all summernote instances */ + +var History = function History ($editable) { + var aUndo = []; + var pos = 0; + + this.makeSnap = function (event, rng) { + rng = rng || range.create(); + var elEditable = $(rng && rng.sc).closest('.o_editable')[0]; + if (!elEditable) { + return false; + } + return { + event: event, + editable: elEditable, + contents: elEditable.innerHTML, + bookmark: rng && rng.bookmark(elEditable), + scrollTop: $(elEditable).scrollTop() + }; + }; + + this.applySnap = function (oSnap) { + var $editable = $(oSnap.editable); + + if (document.documentMode) { + $editable.removeAttr("contentEditable").removeProp("contentEditable"); + } + + $editable.html(oSnap.contents).scrollTop(oSnap.scrollTop); + $(".oe_overlay").remove(); + $(".note-control-selection").hide(); + + $editable.trigger("content_changed"); + + try { + var r = oSnap.editable.innerHTML === "" ? range.create(oSnap.editable, 0) : range.createFromBookmark(oSnap.editable, oSnap.bookmark); + r.select(); + } catch(e) { + console.error(e); + return; + } + + $(document).trigger("click"); + $(".o_editable *").filter(function () { + var $el = $(this); + if($el.data('snippet-editor')) { + $el.removeData(); + } + }); + + + _.defer(function () { + var target = dom.isBR(r.sc) ? r.sc.parentNode : dom.node(r.sc); + if (!target) { + return; + } + + $editable.trigger("applySnap"); + + var evt = document.createEvent("MouseEvents"); + evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, target); + target.dispatchEvent(evt); + + $editable.trigger("keyup"); + }); + }; + + this.undo = function () { + if (!pos) { return; } + var _toSnap = toSnap; + if (_toSnap) { + this.saveSnap(); + } + if (!aUndo[pos] && (!aUndo[pos] || aUndo[pos].event !== 'undo')) { + var temp = this.makeSnap('undo'); + if (temp && (!pos || temp.contents !== aUndo[pos-1].contents)) { + aUndo[pos] = temp; + } else { + pos--; + } + } else if (_toSnap) { + pos--; + } + this.applySnap(aUndo[Math.max(--pos,0)]); + while (pos && (aUndo[pos].event === "blur" || (aUndo[pos+1].editable === aUndo[pos].editable && aUndo[pos+1].contents === aUndo[pos].contents))) { + this.applySnap(aUndo[--pos]); + } + }; + this.hasUndo = function () { + return (toSnap && (toSnap.event !== "blur" && toSnap.event !== "activate" && toSnap.event !== "undo")) || + !!_.find(aUndo.slice(0, pos+1), function (undo) { + return undo.event !== "blur" && undo.event !== "activate" && undo.event !== "undo"; + }); + }; + + this.getEditableHasUndo = function () { + var editable = []; + if ((toSnap && (toSnap.event !== "blur" && toSnap.event !== "activate" && toSnap.event !== "undo"))) { + editable.push(toSnap.editable); + } + _.each(aUndo.slice(0, pos+1), function (undo) { + if (undo.event !== "blur" && undo.event !== "activate" && undo.event !== "undo") { + editable.push(undo.editable); + } + }); + return _.uniq(editable); + }; + + this.redo = function () { + if (!aUndo[pos+1]) { return; } + this.applySnap(aUndo[++pos]); + while (aUndo[pos+1] && aUndo[pos].event === "active") { + this.applySnap(aUndo[pos++]); + } + }; + this.hasRedo = function () { + return aUndo.length > pos+1; + }; + + var toSnap; + this.recordUndo = function ($editable, event, internal_history) { + var self = this; + if (!$editable) { + var rng = range.create(); + if(!rng) return; + $editable = $(rng.sc).closest(".o_editable"); + } + + if (aUndo[pos] && (event === "applySnap" || event === "activate")) { + return; + } + + if (!internal_history) { + if (!event || !toSnap || !aUndo[pos-1] || toSnap.event === "activate") { // don't trigger change for all keypress + setTimeout(function () { + $editable.trigger("content_changed"); + },0); + } + } + + if (aUndo[pos]) { + pos = Math.min(pos, aUndo.length); + aUndo.splice(Math.max(pos,1), aUndo.length); + } + + // => make a snap when the user change editable zone (because: don't make snap for each keydown) + if (toSnap && (toSnap.split || !event || toSnap.event !== event || toSnap.editable !== $editable[0])) { + this.saveSnap(); + } + + if (pos && aUndo[pos-1].editable !== $editable[0]) { + var snap = this.makeSnap('blur', range.create(aUndo[pos-1].editable, 0)); + pos++; + aUndo.push(snap); + } + + if (range.create()) { + toSnap = self.makeSnap(event); + } else { + toSnap = false; + } + }; + + this.splitNext = function () { + if (toSnap) { + toSnap.split = true; + } + }; + + this.saveSnap = function () { + if (toSnap) { + if (!aUndo[pos]) { + pos++; + } + aUndo.push(toSnap); + delete toSnap.split; + toSnap = null; + } + }; +}; +var history = new History(); + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// add focusIn to jQuery to allow to move caret into a div of a contentEditable area + +$.extend($.expr[':'],{ + o_editable: function (node,i,m) { + while (node) { + if (node.className && _.isString(node.className)) { + if (node.className.indexOf('o_not_editable')!==-1 ) { + return false; + } + if (node.className.indexOf('o_editable')!==-1 ) { + return true; + } + } + node = node.parentNode; + } + return false; + } +}); + +$.fn.extend({ + focusIn: function () { + if (this.length) { + range.create(dom.firstChild(this[0]), 0).select(); + } + return this; + }, + focusInEnd: function () { + if (this.length) { + var last = dom.lastChild(this[0]); + range.create(last, dom.nodeLength(last)).select(); + } + return this; + }, + selectContent: function () { + if (this.length) { + var next = dom.lastChild(this[0]); + range.create(dom.firstChild(this[0]), 0, next, next.textContent.length).select(); + } + return this; + } +}); + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* ----- RICH TEXT EDITOR ---- */ + +var RTE = Widget.extend({ + init: function (EditorBar, config) { + var self = this; + this.EditorBar = EditorBar; + data.rte = this; + this._super.apply(this, arguments); + + this.init_bootstrap_carousel = $.fn.carousel; + this.edit_bootstrap_carousel = function () { + var res = self.init_bootstrap_carousel.apply(this, arguments); + // off bootstrap keydown event to remove event.preventDefault() + // and allow to change cursor position + $(this).off('keydown.bs.carousel'); + return res; + }; + + if (config) { + this.config = config; + } + }, + /** + * Add a record undo to history + * @param {DOM} target where the dom is changed is editable zone + */ + historyRecordUndo: function ($target, event, internal_history) { + var rng = range.create(); + var $editable = $(rng && rng.sc).closest(".o_editable"); + if (!rng || !$editable.length) { + $editable = $($target).closest(".o_editable"); + rng = range.create($target.closest("*")[0],0); + } else { + rng = $editable.data('range') || rng; + } + rng.select(); + history.recordUndo($editable, event, internal_history); + }, + /** + * Makes the page editable + * + * @param {Boolean} [restart=false] in case the edition was already set + * up once and is being re-enabled. + * @returns {$.Deferred} deferred indicating when the RTE is ready + */ + start: function () { + var self = this; + + this.saving_mutex = new concurrency.Mutex(); + + $.fn.carousel = this.edit_bootstrap_carousel; + + $(document).on("keydown.rte", this, this.onKeydown.bind(this)); + $(document).on("mousedown.rte activate.rte", this, this.onMousedown.bind(this)); + $(document).on("mouseup.rte", this, this.onMouseup.bind(this)); + + $('.o_not_editable').attr("contentEditable", false); + + var $editable = this.editable(); + + $editable.addClass('o_editable') + .data('rte', this) + .each(function () { + var $node = $(this); + + // add class to display inline-block for empty t-field + if (window.getComputedStyle(this).display === "inline" && $node.data('oe-type') !== "image") { + $node.addClass('o_is_inline_editable'); + } + $node.data('initInnerHTML', $node.html()); + }); + + // start element observation + $(document).on('content_changed', '.o_editable', function (event) { + self.trigger('change', this); + $(this).addClass('o_dirty'); + }); + + $('#wrapwrap, .o_editable').on('click.rte', '*', this, this.onClick.bind(this)); + + $('body').addClass("editor_enable"); + + $(document) + .tooltip({ + selector: '[data-oe-readonly]', + container: 'body', + trigger: 'hover', + delay: { "show": 1000, "hide": 100 }, + placement: 'bottom', + title: _t("Readonly field") + }) + .on('click', function () { + $(this).tooltip('hide'); + }); + + $(document).trigger('mousedown'); + this.trigger('rte:start'); + }, + + save: function (context) { + var self = this; + + this.__saved = {}; // list of allready saved views and data + + var defs = $('.o_dirty') + .removeAttr('contentEditable') + .removeClass('o_dirty oe_carlos_danger o_is_inline_editable') + .map(function () { + var $el = $(this); + + $el.find('[class]').filter(function () { + if (!this.className.match(/\S/)) { + this.removeAttribute("class"); + } + }); + + // TODO: Add a queue with concurrency limit in webclient + // https://github.com/medikoo/deferred/blob/master/lib/ext/function/gate.js + return self.saving_mutex.exec(function () { + return self.saveElement($el, context) + .then(undefined, function (thing, response) { + // because ckeditor regenerates all the dom, + // we can't just setup the popover here as + // everything will be destroyed by the DOM + // regeneration. Add markings instead, and + // returns a new rejection with all relevant + // info + var id = _.uniqueId('carlos_danger_'); + $el.addClass('o_dirty oe_carlos_danger ' + id); + return $.Deferred().reject({ + id: id, + error: response.data, + }); + }); + }); + }).get(); + + return $.when.apply(null, defs).then(function () { + window.onbeforeunload = null; + }, function (failed) { + // If there were errors, re-enable edition + self.stop(); + self.start(); + // jquery's deferred being a pain in the ass + if (!_.isArray(failed)) { failed = [failed]; } + + _(failed).each(function (failure) { + var html = failure.error.exception_type === "except_osv"; + if (html) { + var msg = $("
").text(failure.error.message).html(); + var data = msg.substring(3,msg.length-2).split(/', u'/); + failure.error.message = '' + data[0] + '' + data[1]; + } + $('.o_editable.' + failure.id) + .removeClass(failure.id) + .popover({ + html: html, + trigger: 'hover', + content: failure.error.message, + placement: 'auto top', + }) + // Force-show popovers so users will notice them. + .popover('show'); + }); + }); + }, + + /** + * Get HTML cloned element with text nodes escaped for XML storage + */ + getEscapedElement: function ($el) { + var escaped_el = $el.clone(); + var to_escape = escaped_el.find('*').addBack(); + to_escape = to_escape.not(to_escape.filter('object,iframe,script,style,[data-oe-model][data-oe-model!="ir.ui.view"]').find('*').addBack()); + to_escape.contents().each(function () { + if(this.nodeType === 3) { + this.nodeValue = $('
').text(this.nodeValue).html(); + } + }); + return escaped_el; + }, + + saveElement: function ($el, context) { + // remove multi edition + if ($el.data('oe-model')) { + var key = $el.data('oe-model')+":"+$el.data('oe-id')+":"+$el.data('oe-field')+":"+$el.data('oe-type')+":"+$el.data('oe-expression'); + if (this.__saved[key]) return $.when(); + this.__saved[key] = true; + } + var markup = this.getEscapedElement($el).prop('outerHTML'); + + return ajax.jsonRpc('/web/dataset/call', 'call', { + model: 'ir.ui.view', + method: 'save', + args: [ + $el.data('oe-id'), + markup, + $el.data('oe-xpath') || null, + _.omit(context || base.get_context(), 'lang') + ], + }); + }, + + stop: function () { + if (this.$last) { + this.$last.destroy(); + this.$last = null; + } + + $.fn.carousel = this.init_bootstrap_carousel; + + $(document).off(".rte"); + $('#wrapwrap, .o_editable').off('.rte'); + + $('.o_not_editable').removeAttr("contentEditable"); + $(document).off('content_changed').removeClass('o_is_inline_editable').removeData('rte'); + $(document).tooltip('destroy'); + $('body').removeClass("editor_enable"); + this.trigger('rte:stop'); + }, + + cancel: function () { + $('.o_editable').each(function () { + var $node = $(this); + $node.data('initInnerHTML', $node.html()); + }); + this.stop(); + }, + + onClick: function (e) { + e.preventDefault(); + }, + + // handler for cancel editor + onKeydown: function (event) { + if (event.keyCode === 27 && !$('.modal-content:visible').length) { + _.defer(function () { + $('#editor-top-navbar [data-action="cancel"]').click(); + var $modal = $('.modal-content > .modal-body').parents(".modal:first"); + $modal.off('keyup.dismiss.bs.modal'); + _.delay(function () { + $modal.on('keyup.dismiss.bs.modal', function () { + $(this).modal('hide'); + }); + }, 500); + }); + } + }, + + // activate editor + onMousedown: function (event) { + var $target = $(event.target); + var $editable = $target.closest('.o_editable'); + + if (!$editable.length) { + return; + } + + if ($target.is('a')) { + /** + * Remove content editable everywhere and add it on the link only so that characters can be added + * and removed at the start and at the end of it. + */ + $target.attr('contenteditable', true); + _.defer(function () { + $editable.not($target).attr('contenteditable', false); + $target.focus(); + }); + + // Once clicked outside, remove contenteditable on link and reactive all + $(document).on('mousedown.reactivate_contenteditable', function (e) { + if ($target.is(e.target)) return; + $target.removeAttr('contenteditable'); + $editable.attr('contenteditable', true); + $(document).off('mousedown.reactivate_contenteditable'); + }); + } + + if (this && this.$last && (!$editable.length || this.$last[0] !== $editable[0])) { + var $destroy = this.$last; + history.splitNext(); + + _.delay(function () { + var id = $destroy.data('note-id'); + $destroy.destroy().removeData('note-id').removeAttr('data-note-id'); + $('#note-popover-'+id+', #note-handle-'+id+', #note-dialog-'+id+'').remove(); + }, 150); // setTimeout to remove flickering when change to editable zone (re-create an editor) + this.$last = null; + } + if ($editable.length && (!this.$last || this.$last[0] !== $editable[0]) && + ($target.closest('[contenteditable]').attr('contenteditable') || "").toLowerCase() !== 'false') { + $editable.summernote(this.config($editable)); + + $editable.data('NoteHistory', history); + this.$last = $editable; + + // firefox & IE fix + try { + document.execCommand('enableObjectResizing', false, false); + document.execCommand('enableInlineTableEditing', false, false); + document.execCommand('2D-position', false, false); + } catch (e) {} + document.body.addEventListener('resizestart', function (evt) {evt.preventDefault(); return false;}); + document.body.addEventListener('movestart', function (evt) {evt.preventDefault(); return false;}); + document.body.addEventListener('dragstart', function (evt) {evt.preventDefault(); return false;}); + + if (!range.create()) { + $editable.focusIn(); + } + + if (dom.isImg($target[0])) { + $target.trigger('mousedown'); // for activate selection on picture + } + + this.onEnableEditableArea($editable); + } + }, + + onEnableEditableArea: function ($editable) { + }, + + onMouseup: function (event) { + var $target = $(event.target); + var $editable = $target.closest('.o_editable'); + + if (!$editable.length) { + return; + } + + var self = this; + _.defer(function () { + self.historyRecordUndo($target, 'activate', true); + }); + + // To Fix Google Chrome Tripleclick Issue, which selects the ending + // whitespace characters (so Tripleclicking then typing text will remove + // the whole paragraph instead of its content). + // http://stackoverflow.com/questions/38467334/why-does-google-chrome-always-add-space-after-selected-text + if ($.browser.chrome === true && event.originalEvent.detail === 3) { + var currentSelection = range.create(); + if (currentSelection.sc.parentNode === currentSelection.ec) { + range.create(currentSelection.sc, currentSelection.so, currentSelection.sc, currentSelection.sc.length).select(); + } + } + }, + + editable: function () { + return $('#wrapwrap [data-oe-model]') + .not('.o_not_editable') + .filter(function () { + return !$(this).closest('.o_not_editable').length; + }) + .not('link, script') + .not('[data-oe-readonly]') + .not('img[data-oe-field="arch"], br[data-oe-field="arch"], input[data-oe-field="arch"]') + .not('.oe_snippet_editor') + .add('.o_editable'); + }, + + config: function ($editable) { + return { + 'airMode' : true, + 'focus': false, + 'airPopover': [ + ['style', ['style']], + ['font', ['bold', 'italic', 'underline', 'clear']], + ['fontsize', ['fontsize']], + ['color', ['color']], + ['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ['insert', ['link', 'picture']], + ['history', ['undo', 'redo']], + ], + 'styleWithSpan': false, + 'inlinemedia' : ['p'], + 'lang': "odoo", + 'onChange': function (html, $editable) { + $editable.trigger("content_changed"); + } + }; + }, +}); + + +var data = { + history: history, + Class: RTE +}; +return data; + +}); diff --git a/addons/web_editor/static/src/js/editor/rte.summernote.js b/addons/web_editor/static/src/js/editor/rte.summernote.js new file mode 100644 index 00000000000..979211cd4d7 --- /dev/null +++ b/addons/web_editor/static/src/js/editor/rte.summernote.js @@ -0,0 +1,983 @@ +odoo.define('web_editor.rte.summernote', function (require) { +'use strict'; + +var core = require('web.core'); +var ajax = require('web.ajax'); +var base = require('web_editor.base'); +var widgets = require('web_editor.widget'); +var rte = require('web_editor.rte'); + +var QWeb = core.qweb; +var _t = core._t; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ajax.jsonRpc('/web/dataset/call', 'call', { + 'model': 'ir.ui.view', + 'method': 'read_template', + 'args': ['web_editor.colorpicker', base.get_context()] +}).done(function (data) { + QWeb.add_template(data); +}); + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Summernote Lib (neek change to make accessible: method and object) */ + +var dom = $.summernote.core.dom; +var range = $.summernote.core.range; +var eventHandler = $.summernote.eventHandler; +var renderer = $.summernote.renderer; +// var options = $.summernote.options; + +var tplButton = renderer.getTemplate().button; +var tplIconButton = renderer.getTemplate().iconButton; +var tplDropdown = renderer.getTemplate().dropdown; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* update and change the popovers content, and add history button */ + +var fn_createPalette = renderer.createPalette; +renderer.createPalette = function ($container, options) { + fn_createPalette.call(this, $container, options); + + if (!QWeb.has_template('web_editor.colorpicker')) { + return; + } + + var $clpicker = $(QWeb.render('web_editor.colorpicker')); + + var groups; + if ($clpicker.is("colorpicker")) { + groups = _.map($clpicker.children(), function (el) { + return $(el).find("button").empty(); + }); + } else { + groups = [$clpicker.find("button").empty()]; + } + + var html = "
" + _t("Theme colors") + "
" + _.map(groups, function ($group) { + var $row = $("
", {"class": "note-color-row mb8"}).append($group); + var $after_breaks = $row.find(".o_small + :not(.o_small)"); + if ($after_breaks.length === 0) { + $after_breaks = $row.find(":nth-child(8n+9)"); + } + $after_breaks.addClass("o_clear"); + return $row[0].outerHTML; + }).join("") + "
" + _t("Common colors") + "
"; + var $palettes = $container.find(".note-color .note-color-palette"); + $palettes.prepend(html); + + var $bg = $palettes.filter(":even").find("button:not(.note-color-btn)").addClass("note-color-btn"); + var $fore = $palettes.filter(":odd").find("button:not(.note-color-btn)").addClass("note-color-btn"); + $bg.each(function () { + var $el = $(this); + var className = 'bg-' + $el.data('color'); + $el.attr('data-event', 'backColor').attr('data-value', className).addClass(className); + }); + $fore.each(function () { + var $el = $(this); + var className = 'text-' + $el.data('color'); + $el.attr('data-event', 'foreColor').attr('data-value', className).addClass('bg-' + $el.data('color')); + }); +}; + +var fn_tplPopovers = renderer.tplPopovers; +renderer.tplPopovers = function (lang, options) { + var $popover = $(fn_tplPopovers.call(this, lang, options)); + + var $imagePopover = $popover.find('.note-image-popover'); + var $linkPopover = $popover.find('.note-link-popover'); + var $airPopover = $popover.find('.note-air-popover'); + + if (window === window.top) { + $popover.children().addClass("hidden-xs"); + } + + //////////////// image popover + + // add center button for images + $(tplIconButton('fa fa-align-center', { + title: _t('Center'), + event: 'floatMe', + value: 'center' + })).insertAfter($imagePopover.find('[data-event="floatMe"][data-value="left"]')); + $imagePopover.find('button[data-event="removeMedia"]').parent().remove(); + $imagePopover.find('button[data-event="floatMe"][data-value="none"]').remove(); + + // padding button + var $padding = $('
'); + $padding.insertBefore($imagePopover.find('.btn-group:first')); + var dropdown_content = [ + '
  • '+_t('None')+'
  • ', + '
  • '+_t('Small')+'
  • ', + '
  • '+_t('Medium')+'
  • ', + '
  • '+_t('Large')+'
  • ', + '
  • '+_t('Xl')+'
  • ', + ]; + $(tplIconButton('fa fa-plus-square-o', { + title: _t('Padding'), + dropdown: tplDropdown(dropdown_content) + })).appendTo($padding); + + // circle, boxed... options became toggled + $imagePopover.find('[data-event="imageShape"]:not([data-value])').remove(); + var $button = $(tplIconButton('fa fa-sun-o', { + title: _t('Shadow'), + event: 'imageShape', + value: 'shadow' + })).insertAfter($imagePopover.find('[data-event="imageShape"][data-value="img-circle"]')); + + // add spin for fa + var $spin = $('