summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src/js/rte.js
diff options
context:
space:
mode:
authorqsm-odoo <qsm@odoo.com>2017-06-07 17:45:15 +0200
committerqsm-odoo <qsm@odoo.com>2017-08-16 10:59:39 +0200
commit4f925af06fd3cebfcfb56eb947ab68c8a2ca4490 (patch)
treeb13bd049b6b3e7bbdfb537d19b53c95fc4025ea5 /addons/web_editor/static/src/js/rte.js
parentf205bfcd4ddcf3f1d8676ee87ee0b29941bd6773 (diff)
[MOV] web_editor, website: reorganize files
Diffstat (limited to 'addons/web_editor/static/src/js/rte.js')
-rw-r--r--addons/web_editor/static/src/js/rte.js637
1 files changed, 0 insertions, 637 deletions
diff --git a/addons/web_editor/static/src/js/rte.js b/addons/web_editor/static/src/js/rte.js
deleted file mode 100644
index 92ab133263a..00000000000
--- a/addons/web_editor/static/src/js/rte.js
+++ /dev/null
@@ -1,637 +0,0 @@
-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 = $("<div/>").text(failure.error.message).html();
- var data = msg.substring(3,msg.length-2).split(/', u'/);
- failure.error.message = '<b>' + data[0] + '</b>' + 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 = $('<div />').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;
-
-});