diff options
| author | Nicolas Lempereur <nle@odoo.com> | 2015-08-28 17:22:17 +0200 |
|---|---|---|
| committer | Nicolas Lempereur <nle@odoo.com> | 2015-09-25 08:29:16 +0200 |
| commit | 7747e0751f7a0409132d3171162d67d0885a70cb (patch) | |
| tree | 7da063ae03180622c1e2e1b193f7d2c5c0477318 /addons/web_editor/static/lib/summernote/src/js/module | |
| parent | 9798b57b12c5f943fc0f8ee7b82a9eb04434a635 (diff) | |
[IMP] web_editor: update summernote up to 2015-08-31
update summernote library up to revision:
https://github.com/summernote/summernote/commit/a0f26e300
see https://github.com/summernote/summernote/ for history
closes #8704
Diffstat (limited to 'addons/web_editor/static/lib/summernote/src/js/module')
13 files changed, 855 insertions, 183 deletions
diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Button.js b/addons/web_editor/static/lib/summernote/src/js/module/Button.js index 512822aeba8..b7a1614d4db 100644 --- a/addons/web_editor/static/lib/summernote/src/js/module/Button.js +++ b/addons/web_editor/static/lib/summernote/src/js/module/Button.js @@ -1,7 +1,10 @@ define([ - 'summernote/core/list' -], function (list) { + 'summernote/core/list', + 'summernote/core/agent' +], function (list, agent) { /** + * @class module.Button + * * Button */ var Button = function () { @@ -28,6 +31,7 @@ define([ /** * update button state(active or not). * + * @private * @param {String} selector * @param {Function} pred */ @@ -40,30 +44,30 @@ define([ var $img = $(styleInfo.image); btnState('button[data-event="imageShape"][data-value="img-rounded"]', function () { - return $img.hasClass("img-rounded"); + return $img.hasClass('img-rounded'); }); btnState('button[data-event="imageShape"][data-value="img-circle"]', function () { - return $img.hasClass("img-circle"); + return $img.hasClass('img-circle'); }); btnState('button[data-event="imageShape"][data-value="img-thumbnail"]', function () { - return $img.hasClass("img-thumbnail"); + return $img.hasClass('img-thumbnail'); }); btnState('button[data-event="imageShape"]:not([data-value])', function () { - return !$img.is(".img-rounded, .img-circle, .img-thumbnail"); + return !$img.is('.img-rounded, .img-circle, .img-thumbnail'); }); - var imgFloat = $img.css("float"); + var imgFloat = $img.css('float'); btnState('button[data-event="floatMe"][data-value="left"]', function () { - return imgFloat === "left"; + return imgFloat === 'left'; }); btnState('button[data-event="floatMe"][data-value="right"]', function () { - return imgFloat === "right"; + return imgFloat === 'right'; }); btnState('button[data-event="floatMe"][data-value="none"]', function () { - return imgFloat !== "left" && imgFloat !== "right"; + return imgFloat !== 'left' && imgFloat !== 'right'; }); - var style = $img.attr("style"); + var style = $img.attr('style'); btnState('button[data-event="resize"][data-value="1"]', function () { return !!/(^|\s)(max-)?width\s*:\s*100%/.test(style); }); @@ -81,10 +85,18 @@ define([ if ($fontname.length) { var selectedFont = styleInfo['font-family']; if (!!selectedFont) { - selectedFont = list.head(selectedFont.split(',')); - selectedFont = selectedFont.replace(/\'/g, ''); + + var list = selectedFont.split(','); + for (var i = 0, len = list.length; i < len; i++) { + selectedFont = list[i].replace(/[\'\"]/g, '').replace(/\s+$/, '').replace(/^\s+/, ''); + if (agent.isFontInstalled(selectedFont)) { + break; + } + } + $fontname.find('.note-current-fontname').text(selectedFont); checkDropdownMenu($fontname, selectedFont); + } } @@ -140,7 +152,7 @@ define([ * * @param {Node} button * @param {String} eventName - * @param {value} value + * @param {Mixed} value */ this.updateRecentColor = function (button, eventName, value) { var $color = $(button).closest('.note-color'); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js b/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js new file mode 100644 index 00000000000..e2d234c9dbb --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js @@ -0,0 +1,90 @@ +define([ + 'summernote/core/list', + 'summernote/core/dom', + 'summernote/core/key', + 'summernote/core/agent' +], function (list, dom, key, agent) { + var Clipboard = function (handler) { + var $paste; + + this.attach = function (layoutInfo) { + // [workaround] getting image from clipboard + // - IE11 and Firefox: CTRL+v hook + // - Webkit: event.clipboardData + if ((agent.isMSIE && agent.browserVersion > 10) || agent.isFF) { + $paste = $('<div />').attr('contenteditable', true).css({ + position : 'absolute', + left : -100000, + opacity : 0 + }); + + layoutInfo.editable().on('keydown', function (e) { + if (e.ctrlKey && e.keyCode === key.code.V) { + handler.invoke('saveRange', layoutInfo.editable()); + $paste.focus(); + + setTimeout(function () { + pasteByHook(layoutInfo); + }, 0); + } + }); + + layoutInfo.editable().before($paste); + } else { + layoutInfo.editable().on('paste', pasteByEvent); + } + }; + + var pasteByHook = function (layoutInfo) { + var $editable = layoutInfo.editable(); + var node = $paste[0].firstChild; + + if (dom.isImg(node)) { + var dataURI = node.src; + var decodedData = atob(dataURI.split(',')[1]); + var array = new Uint8Array(decodedData.length); + for (var i = 0; i < decodedData.length; i++) { + array[i] = decodedData.charCodeAt(i); + } + + var blob = new Blob([array], { type : 'image/png' }); + blob.name = 'clipboard.png'; + + handler.invoke('restoreRange', $editable); + handler.invoke('focus', $editable); + handler.insertImages(layoutInfo, [blob]); + } else { + var pasteContent = $('<div />').html($paste.html()).html(); + handler.invoke('restoreRange', $editable); + handler.invoke('focus', $editable); + + if (pasteContent) { + handler.invoke('pasteHTML', $editable, pasteContent); + } + } + + $paste.empty(); + }; + + /** + * paste by clipboard event + * + * @param {Event} event + */ + var pasteByEvent = function (event) { + var clipboardData = event.originalEvent.clipboardData; + var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); + var $editable = layoutInfo.editable(); + + if (clipboardData && clipboardData.items && clipboardData.items.length) { + var item = list.head(clipboardData.items); + if (item.kind === 'file' && item.type.indexOf('image/') !== -1) { + handler.insertImages(layoutInfo, [item.getAsFile()]); + } + handler.invoke('editor.afterCommand', $editable); + } + }; + }; + + return Clipboard; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Codeview.js b/addons/web_editor/static/lib/summernote/src/js/module/Codeview.js new file mode 100644 index 00000000000..46f3d96ab12 --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/Codeview.js @@ -0,0 +1,137 @@ +define([ + 'summernote/core/agent', + 'summernote/core/dom' +], function (agent, dom) { + + var CodeMirror; + if (agent.hasCodeMirror) { + if (agent.isSupportAmd) { + require(['CodeMirror'], function (cm) { + CodeMirror = cm; + }); + } else { + CodeMirror = window.CodeMirror; + } + } + + /** + * @class Codeview + */ + var Codeview = function (handler) { + + this.sync = function (layoutInfo) { + var isCodeview = handler.invoke('codeview.isActivated', layoutInfo); + if (isCodeview && agent.hasCodeMirror) { + layoutInfo.codable().data('cmEditor').save(); + } + }; + + /** + * @param {Object} layoutInfo + * @return {Boolean} + */ + this.isActivated = function (layoutInfo) { + var $editor = layoutInfo.editor(); + return $editor.hasClass('codeview'); + }; + + /** + * toggle codeview + * + * @param {Object} layoutInfo + */ + this.toggle = function (layoutInfo) { + if (this.isActivated(layoutInfo)) { + this.deactivate(layoutInfo); + } else { + this.activate(layoutInfo); + } + }; + + /** + * activate code view + * + * @param {Object} layoutInfo + */ + this.activate = function (layoutInfo) { + var $editor = layoutInfo.editor(), + $toolbar = layoutInfo.toolbar(), + $editable = layoutInfo.editable(), + $codable = layoutInfo.codable(), + $popover = layoutInfo.popover(), + $handle = layoutInfo.handle(); + + var options = $editor.data('options'); + + $codable.val(dom.html($editable, options.prettifyHtml)); + $codable.height($editable.height()); + + handler.invoke('toolbar.updateCodeview', $toolbar, true); + handler.invoke('popover.hide', $popover); + handler.invoke('handle.hide', $handle); + + $editor.addClass('codeview'); + + $codable.focus(); + + // activate CodeMirror as codable + if (agent.hasCodeMirror) { + var cmEditor = CodeMirror.fromTextArea($codable[0], options.codemirror); + + // CodeMirror TernServer + if (options.codemirror.tern) { + var server = new CodeMirror.TernServer(options.codemirror.tern); + cmEditor.ternServer = server; + cmEditor.on('cursorActivity', function (cm) { + server.updateArgHints(cm); + }); + } + + // CodeMirror hasn't Padding. + cmEditor.setSize(null, $editable.outerHeight()); + $codable.data('cmEditor', cmEditor); + } + }; + + /** + * deactivate code view + * + * @param {Object} layoutInfo + */ + this.deactivate = function (layoutInfo) { + var $holder = layoutInfo.holder(), + $editor = layoutInfo.editor(), + $toolbar = layoutInfo.toolbar(), + $editable = layoutInfo.editable(), + $codable = layoutInfo.codable(); + + var options = $editor.data('options'); + + // deactivate CodeMirror as codable + if (agent.hasCodeMirror) { + var cmEditor = $codable.data('cmEditor'); + $codable.val(cmEditor.getValue()); + cmEditor.toTextArea(); + } + + var value = dom.value($codable, options.prettifyHtml) || dom.emptyPara; + var isChange = $editable.html() !== value; + + $editable.html(value); + $editable.height(options.height ? $codable.height() : 'auto'); + $editor.removeClass('codeview'); + + if (isChange) { + handler.bindCustomEvent( + $holder, $editable.data('callbacks'), 'change' + )($editable.html(), $editable); + } + + $editable.focus(); + + handler.invoke('toolbar.updateCodeview', $toolbar, false); + }; + }; + + return Codeview; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Dialog.js b/addons/web_editor/static/lib/summernote/src/js/module/Dialog.js index 8e7cf07b323..d4f4d700218 100644 --- a/addons/web_editor/static/lib/summernote/src/js/module/Dialog.js +++ b/addons/web_editor/static/lib/summernote/src/js/module/Dialog.js @@ -1,158 +1 @@ -define('summernote/module/Dialog', function () { - /** - * Dialog - * - * @class - */ - var Dialog = function () { - - /** - * toggle button status - * - * @param {jQuery} $btn - * @param {Boolean} isEnable - */ - var toggleBtn = function ($btn, isEnable) { - $btn.toggleClass('disabled', !isEnable); - $btn.attr('disabled', !isEnable); - }; - - /** - * show image dialog - * - * @param {jQuery} $editable - * @param {jQuery} $dialog - * @return {Promise} - */ - this.showImageDialog = function ($editable, $dialog) { - return $.Deferred(function (deferred) { - var $imageDialog = $dialog.find('.note-image-dialog'); - - var $imageInput = $dialog.find('.note-image-input'), - $imageUrl = $dialog.find('.note-image-url'), - $imageBtn = $dialog.find('.note-image-btn'); - - $imageDialog.one('shown.bs.modal', function () { - // Cloning imageInput to clear element. - $imageInput.replaceWith($imageInput.clone() - .on('change', function () { - deferred.resolve(this.files || this.value); - $imageDialog.modal('hide'); - }) - .val('') - ); - - $imageBtn.click(function (event) { - event.preventDefault(); - - deferred.resolve($imageUrl.val()); - $imageDialog.modal('hide'); - }); - - $imageUrl.on('keyup paste', function (event) { - var url; - - if (event.type === 'paste') { - url = event.originalEvent.clipboardData.getData('text'); - } else { - url = $imageUrl.val(); - } - - toggleBtn($imageBtn, url); - }).val('').trigger('focus'); - }).one('hidden.bs.modal', function () { - $imageInput.off('change'); - $imageUrl.off('keyup paste'); - $imageBtn.off('click'); - - if (deferred.state() === 'pending') { - deferred.reject(); - } - }).modal('show'); - }); - }; - - /** - * Show link dialog and set event handlers on dialog controls. - * - * @param {jQuery} $dialog - * @param {Object} linkInfo - * @return {Promise} - */ - this.showLinkDialog = function ($editable, $dialog, linkInfo) { - return $.Deferred(function (deferred) { - var $linkDialog = $dialog.find('.note-link-dialog'); - - var $linkText = $linkDialog.find('.note-link-text'), - $linkUrl = $linkDialog.find('.note-link-url'), - $linkBtn = $linkDialog.find('.note-link-btn'), - $openInNewWindow = $linkDialog.find('input[type=checkbox]'); - - $linkDialog.one('shown.bs.modal', function () { - $linkText.val(linkInfo.text); - - $linkText.on('input', function () { - // if linktext was modified by keyup, - // stop cloning text from linkUrl - linkInfo.text = $linkText.val(); - }); - - // if no url was given, copy text to url - if (!linkInfo.url) { - linkInfo.url = linkInfo.text; - toggleBtn($linkBtn, linkInfo.text); - } - - $linkUrl.on('input', function () { - toggleBtn($linkBtn, $linkUrl.val()); - // display same link on `Text to display` input - // when create a new link - if (!linkInfo.text) { - $linkText.val($linkUrl.val()); - } - }).val(linkInfo.url).trigger('focus').trigger('select'); - - $openInNewWindow.prop('checked', linkInfo.newWindow); - - $linkBtn.one('click', function (event) { - event.preventDefault(); - - deferred.resolve({ - range: linkInfo.range, - url: $linkUrl.val(), - text: $linkText.val(), - newWindow: $openInNewWindow.is(':checked') - }); - $linkDialog.modal('hide'); - }); - }).one('hidden.bs.modal', function () { - // detach events - $linkText.off('input'); - $linkUrl.off('input'); - $linkBtn.off('click'); - - if (deferred.state() === 'pending') { - deferred.reject(); - } - }).modal('show'); - }).promise(); - }; - - /** - * show help dialog - * - * @param {jQuery} $dialog - */ - this.showHelpDialog = function ($editable, $dialog) { - return $.Deferred(function (deferred) { - var $helpDialog = $dialog.find('.note-help-dialog'); - - $helpDialog.one('hidden.bs.modal', function () { - deferred.resolve(); - }).modal('show'); - }).promise(); - }; - }; - - return Dialog; -}); +/** TODO master: drop this file (splitted in several files) */ diff --git a/addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js b/addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js new file mode 100644 index 00000000000..b8871599c6f --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js @@ -0,0 +1,98 @@ +define([ + 'summernote/core/dom' +], function (dom) { + var DragAndDrop = function (handler) { + var $document = $(document); + + /** + * attach Drag and Drop Events + * + * @param {Object} layoutInfo - layout Informations + * @param {Object} options + */ + this.attach = function (layoutInfo, options) { + if (options.airMode || options.disableDragAndDrop) { + // prevent default drop event + $document.on('drop', function (e) { + e.preventDefault(); + }); + } else { + this.attachDragAndDropEvent(layoutInfo, options); + } + }; + + /** + * attach Drag and Drop Events + * + * @param {Object} layoutInfo - layout Informations + * @param {Object} options + */ + this.attachDragAndDropEvent = function (layoutInfo, options) { + var collection = $(), + $editor = layoutInfo.editor(), + $dropzone = layoutInfo.dropzone(), + $dropzoneMessage = $dropzone.find('.note-dropzone-message'); + + // show dropzone on dragenter when dragging a object to document + // -but only if the editor is visible, i.e. has a positive width and height + $document.on('dragenter', function (e) { + var isCodeview = handler.invoke('codeview.isActivated', layoutInfo); + var hasEditorSize = $editor.width() > 0 && $editor.height() > 0; + if (!isCodeview && !collection.length && hasEditorSize) { + $editor.addClass('dragover'); + $dropzone.width($editor.width()); + $dropzone.height($editor.height()); + $dropzoneMessage.text(options.langInfo.image.dragImageHere); + } + collection = collection.add(e.target); + }).on('dragleave', function (e) { + collection = collection.not(e.target); + if (!collection.length) { + $editor.removeClass('dragover'); + } + }).on('drop', function () { + collection = $(); + $editor.removeClass('dragover'); + }); + + // change dropzone's message on hover. + $dropzone.on('dragenter', function () { + $dropzone.addClass('hover'); + $dropzoneMessage.text(options.langInfo.image.dropImage); + }).on('dragleave', function () { + $dropzone.removeClass('hover'); + $dropzoneMessage.text(options.langInfo.image.dragImageHere); + }); + + // attach dropImage + $dropzone.on('drop', function (event) { + + var dataTransfer = event.originalEvent.dataTransfer; + var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); + + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + event.preventDefault(); + layoutInfo.editable().focus(); + handler.insertImages(layoutInfo, dataTransfer.files); + } else { + var insertNodefunc = function () { + layoutInfo.holder().summernote('insertNode', this); + }; + + for (var i = 0, len = dataTransfer.types.length; i < len; i++) { + var type = dataTransfer.types[i]; + var content = dataTransfer.getData(type); + + if (type.toLowerCase().indexOf('text') > -1) { + layoutInfo.holder().summernote('pasteHTML', content); + } else { + $(content).each(insertNodefunc); + } + } + } + }).on('dragover', false); // prevent default dragover event + }; + }; + + return DragAndDrop; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Fullscreen.js b/addons/web_editor/static/lib/summernote/src/js/module/Fullscreen.js new file mode 100644 index 00000000000..fd837483b3b --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/Fullscreen.js @@ -0,0 +1,51 @@ +define([], function () { // ODOO: remove error from Odoo define + var Fullscreen = function (handler) { + var $window = $(window); + var $scrollbar = $('html, body'); + + /** + * toggle fullscreen + * + * @param {Object} layoutInfo + */ + this.toggle = function (layoutInfo) { + + var $editor = layoutInfo.editor(), + $toolbar = layoutInfo.toolbar(), + $editable = layoutInfo.editable(), + $codable = layoutInfo.codable(); + + var resize = function (size) { + $editable.css('height', size.h); + $codable.css('height', size.h); + if ($codable.data('cmeditor')) { + $codable.data('cmeditor').setsize(null, size.h); + } + }; + + $editor.toggleClass('fullscreen'); + var isFullscreen = $editor.hasClass('fullscreen'); + if (isFullscreen) { + $editable.data('orgheight', $editable.css('height')); + + $window.on('resize', function () { + resize({ + h: $window.height() - $toolbar.outerHeight() + }); + }).trigger('resize'); + + $scrollbar.css('overflow', 'hidden'); + } else { + $window.off('resize'); + resize({ + h: $editable.data('orgheight') + }); + $scrollbar.css('overflow', 'visible'); + } + + handler.invoke('toolbar.updateFullscreen', $toolbar, isFullscreen); + }; + }; + + return Fullscreen; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Handle.js b/addons/web_editor/static/lib/summernote/src/js/module/Handle.js index 375428faa32..3b62304d706 100644 --- a/addons/web_editor/static/lib/summernote/src/js/module/Handle.js +++ b/addons/web_editor/static/lib/summernote/src/js/module/Handle.js @@ -1,8 +1,60 @@ -define('summernote/module/Handle', function () { +define([ + 'summernote/core/dom' +], function (dom) { /** + * @class module.Handle + * * Handle */ - var Handle = function () { + var Handle = function (handler) { + var $document = $(document); + + /** + * `mousedown` event handler on $handle + * - controlSizing: resize image + * + * @param {MouseEvent} event + */ + var hHandleMousedown = function (event) { + if (dom.isControlSizing(event.target)) { + event.preventDefault(); + event.stopPropagation(); + + var layoutInfo = dom.makeLayoutInfo(event.target), + $handle = layoutInfo.handle(), + $popover = layoutInfo.popover(), + $editable = layoutInfo.editable(), + $editor = layoutInfo.editor(); + + var target = $handle.find('.note-control-selection').data('target'), + $target = $(target), posStart = $target.offset(), + scrollTop = $document.scrollTop(); + + var isAirMode = $editor.data('options').airMode; + + $document.on('mousemove', function (event) { + handler.invoke('editor.resizeTo', { + x: event.clientX - posStart.left, + y: event.clientY - (posStart.top - scrollTop) + }, $target, !event.shiftKey); + + handler.invoke('handle.update', $handle, {image: target}, isAirMode); + handler.invoke('popover.update', $popover, {image: target}, isAirMode); + }).one('mouseup', function () { + $document.off('mousemove'); + handler.invoke('editor.afterCommand', $editable); + }); + + if (!$target.data('ratio')) { // original ratio. + $target.data('ratio', $target.height() / $target.width()); + } + } + }; + + this.attach = function (layoutInfo) { + layoutInfo.handle().on('mousedown', hHandleMousedown); + }; + /** * update handle * @param {jQuery} $handle @@ -35,6 +87,11 @@ define('summernote/module/Handle', function () { } }; + /** + * hide + * + * @param {jQuery} $handle + */ this.hide = function ($handle) { $handle.children().hide(); }; diff --git a/addons/web_editor/static/lib/summernote/src/js/module/HelpDialog.js b/addons/web_editor/static/lib/summernote/src/js/module/HelpDialog.js new file mode 100644 index 00000000000..e4ff7b775ed --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/HelpDialog.js @@ -0,0 +1,35 @@ +define([], function () { // ODOO: remove error from Odoo define + var HelpDialog = function (handler) { + /** + * show help dialog + * + * @param {jQuery} $editable + * @param {jQuery} $dialog + * @return {Promise} + */ + this.showHelpDialog = function ($editable, $dialog) { + return $.Deferred(function (deferred) { + var $helpDialog = $dialog.find('.note-help-dialog'); + + $helpDialog.one('hidden.bs.modal', function () { + deferred.resolve(); + }).modal('show'); + }).promise(); + }; + + /** + * @param {Object} layoutInfo + */ + this.show = function (layoutInfo) { + var $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(); + + handler.invoke('editor.saveRange', $editable, true); + this.showHelpDialog($editable, $dialog).then(function () { + handler.invoke('editor.restoreRange', $editable); + }); + }; + }; + + return HelpDialog; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/ImageDialog.js b/addons/web_editor/static/lib/summernote/src/js/module/ImageDialog.js new file mode 100644 index 00000000000..042921183cc --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/ImageDialog.js @@ -0,0 +1,110 @@ +define([ + 'summernote/core/key' +], function (key) { + var ImageDialog = function (handler) { + /** + * toggle button status + * + * @private + * @param {jQuery} $btn + * @param {Boolean} isEnable + */ + var toggleBtn = function ($btn, isEnable) { + $btn.toggleClass('disabled', !isEnable); + $btn.attr('disabled', !isEnable); + }; + + /** + * bind enter key + * + * @private + * @param {jQuery} $input + * @param {jQuery} $btn + */ + var bindEnterKey = function ($input, $btn) { + $input.on('keypress', function (event) { + if (event.keyCode === key.code.ENTER) { + $btn.trigger('click'); + } + }); + }; + + this.show = function (layoutInfo) { + var $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(); + + handler.invoke('editor.saveRange', $editable); + this.showImageDialog($editable, $dialog).then(function (data) { + handler.invoke('editor.restoreRange', $editable); + + if (typeof data === 'string') { + // image url + handler.invoke('editor.insertImage', $editable, data); + } else { + // array of files + handler.insertImages(layoutInfo, data); + } + }).fail(function () { + handler.invoke('editor.restoreRange', $editable); + }); + }; + + /** + * show image dialog + * + * @param {jQuery} $editable + * @param {jQuery} $dialog + * @return {Promise} + */ + this.showImageDialog = function ($editable, $dialog) { + return $.Deferred(function (deferred) { + var $imageDialog = $dialog.find('.note-image-dialog'); + + var $imageInput = $dialog.find('.note-image-input'), + $imageUrl = $dialog.find('.note-image-url'), + $imageBtn = $dialog.find('.note-image-btn'); + + $imageDialog.one('shown.bs.modal', function () { + // Cloning imageInput to clear element. + $imageInput.replaceWith($imageInput.clone() + .on('change', function () { + deferred.resolve(this.files || this.value); + $imageDialog.modal('hide'); + }) + .val('') + ); + + $imageBtn.click(function (event) { + event.preventDefault(); + + deferred.resolve($imageUrl.val()); + $imageDialog.modal('hide'); + }); + + $imageUrl.on('keyup paste', function (event) { + var url; + + if (event.type === 'paste') { + url = event.originalEvent.clipboardData.getData('text'); + } else { + url = $imageUrl.val(); + } + + toggleBtn($imageBtn, url); + }).val('').trigger('focus'); + bindEnterKey($imageUrl, $imageBtn); + }).one('hidden.bs.modal', function () { + $imageInput.off('change'); + $imageUrl.off('keyup paste keypress'); + $imageBtn.off('click'); + + if (deferred.state() === 'pending') { + deferred.reject(); + } + }).modal('show'); + }); + }; + }; + + return ImageDialog; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/LinkDialog.js b/addons/web_editor/static/lib/summernote/src/js/module/LinkDialog.js new file mode 100644 index 00000000000..045119ee68a --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/LinkDialog.js @@ -0,0 +1,129 @@ +define([ + 'summernote/core/key' +], function (key) { + var LinkDialog = function (handler) { + + /** + * toggle button status + * + * @private + * @param {jQuery} $btn + * @param {Boolean} isEnable + */ + var toggleBtn = function ($btn, isEnable) { + $btn.toggleClass('disabled', !isEnable); + $btn.attr('disabled', !isEnable); + }; + + /** + * bind enter key + * + * @private + * @param {jQuery} $input + * @param {jQuery} $btn + */ + var bindEnterKey = function ($input, $btn) { + $input.on('keypress', function (event) { + if (event.keyCode === key.code.ENTER) { + $btn.trigger('click'); + } + }); + }; + + /** + * Show link dialog and set event handlers on dialog controls. + * + * @param {jQuery} $editable + * @param {jQuery} $dialog + * @param {Object} linkInfo + * @return {Promise} + */ + this.showLinkDialog = function ($editable, $dialog, linkInfo) { + return $.Deferred(function (deferred) { + var $linkDialog = $dialog.find('.note-link-dialog'); + + var $linkText = $linkDialog.find('.note-link-text'), + $linkUrl = $linkDialog.find('.note-link-url'), + $linkBtn = $linkDialog.find('.note-link-btn'), + $openInNewWindow = $linkDialog.find('input[type=checkbox]'); + + $linkDialog.one('shown.bs.modal', function () { + $linkText.val(linkInfo.text); + + $linkText.on('input', function () { + toggleBtn($linkBtn, $linkText.val() && $linkUrl.val()); + // if linktext was modified by keyup, + // stop cloning text from linkUrl + linkInfo.text = $linkText.val(); + }); + + // if no url was given, copy text to url + if (!linkInfo.url) { + linkInfo.url = linkInfo.text || 'http://'; + toggleBtn($linkBtn, linkInfo.text); + } + + $linkUrl.on('input', function () { + toggleBtn($linkBtn, $linkText.val() && $linkUrl.val()); + // display same link on `Text to display` input + // when create a new link + if (!linkInfo.text) { + $linkText.val($linkUrl.val()); + } + }).val(linkInfo.url).trigger('focus').trigger('select'); + + bindEnterKey($linkUrl, $linkBtn); + bindEnterKey($linkText, $linkBtn); + + $openInNewWindow.prop('checked', linkInfo.isNewWindow); + + $linkBtn.one('click', function (event) { + event.preventDefault(); + + deferred.resolve({ + range: linkInfo.range, + url: $linkUrl.val(), + text: $linkText.val(), + isNewWindow: $openInNewWindow.is(':checked') + }); + $linkDialog.modal('hide'); + }); + }).one('hidden.bs.modal', function () { + // detach events + $linkText.off('input keypress'); + $linkUrl.off('input keypress'); + $linkBtn.off('click'); + + if (deferred.state() === 'pending') { + deferred.reject(); + } + }).modal('show'); + }).promise(); + }; + + /** + * @param {Object} layoutInfo + */ + this.show = function (layoutInfo) { + var $editor = layoutInfo.editor(), + $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(), + $popover = layoutInfo.popover(), + linkInfo = handler.invoke('editor.getLinkInfo', $editable); + + var options = $editor.data('options'); + + handler.invoke('editor.saveRange', $editable); + this.showLinkDialog($editable, $dialog, linkInfo).then(function (linkInfo) { + handler.invoke('editor.restoreRange', $editable); + handler.invoke('editor.createLink', $editable, linkInfo, options); + // hide popover after creating link + handler.invoke('popover.hide', $popover); + }).fail(function () { + handler.invoke('editor.restoreRange', $editable); + }); + }; + }; + + return LinkDialog; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Popover.js b/addons/web_editor/static/lib/summernote/src/js/module/Popover.js index e0531f06c45..f669569f6ba 100644 --- a/addons/web_editor/static/lib/summernote/src/js/module/Popover.js +++ b/addons/web_editor/static/lib/summernote/src/js/module/Popover.js @@ -4,22 +4,32 @@ define([ 'summernote/module/Button' ], function (func, list, Button) { /** + * @class module.Popover + * * Popover (http://getbootstrap.com/javascript/#popovers) + * */ var Popover = function () { var button = new Button(); - this.button = button; // odoo change for overwrite + this.button = button; // ODOO: allow access for override /** * returns position from placeholder + * + * @private * @param {Node} placeholder - * @param {Boolean} isAirMode + * @param {Object} options + * @param {Boolean} options.isAirMode + * @return {Position} */ - var posFromPlaceholder = function (placeholder, isAirMode) { + var posFromPlaceholder = function (placeholder, options) { + var isAirMode = options && options.isAirMode; + var isLeftTop = options && options.isLeftTop; + var $placeholder = $(placeholder); var pos = isAirMode ? $placeholder.offset() : $placeholder.position(); - var height = $placeholder.outerHeight(true); // include margin + var height = isLeftTop ? 0 : $placeholder.outerHeight(true); // include margin // popover below placeholder. return { @@ -30,6 +40,8 @@ define([ /** * show popover + * + * @private * @param {jQuery} popover * @param {Position} pos */ @@ -56,21 +68,32 @@ define([ if (styleInfo.anchor) { var $anchor = $linkPopover.find('a'); var href = $(styleInfo.anchor).attr('href'); + var target = $(styleInfo.anchor).attr('target'); $anchor.attr('href', href).html(href); - showPopover($linkPopover, posFromPlaceholder(styleInfo.anchor, isAirMode)); + if (!target) { + $anchor.removeAttr('target'); + } else { + $anchor.attr('target', '_blank'); + } + showPopover($linkPopover, posFromPlaceholder(styleInfo.anchor, { + isAirMode: isAirMode + })); } else { $linkPopover.hide(); } var $imagePopover = $popover.find('.note-image-popover'); if (styleInfo.image) { - showPopover($imagePopover, posFromPlaceholder(styleInfo.image, isAirMode)); + showPopover($imagePopover, posFromPlaceholder(styleInfo.image, { + isAirMode: isAirMode, + isLeftTop: true + })); } else { $imagePopover.hide(); } var $airPopover = $popover.find('.note-air-popover'); - if (isAirMode && !styleInfo.range.isCollapsed()) { + if (isAirMode && styleInfo.range && !styleInfo.range.isCollapsed()) { var rect = list.last(styleInfo.range.getClientRects()); if (rect) { var bnd = func.rect2bnd(rect); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Statusbar.js b/addons/web_editor/static/lib/summernote/src/js/module/Statusbar.js new file mode 100644 index 00000000000..b50fce458eb --- /dev/null +++ b/addons/web_editor/static/lib/summernote/src/js/module/Statusbar.js @@ -0,0 +1,44 @@ +define([ + 'summernote/core/dom' +], function (dom) { + var EDITABLE_PADDING = 24; + + var Statusbar = function () { + var $document = $(document); + + this.attach = function (layoutInfo, options) { + if (!options.disableResizeEditor) { + layoutInfo.statusbar().on('mousedown', hStatusbarMousedown); + } + }; + + /** + * `mousedown` event handler on statusbar + * + * @param {MouseEvent} event + */ + var hStatusbarMousedown = function (event) { + event.preventDefault(); + event.stopPropagation(); + + var $editable = dom.makeLayoutInfo(event.target).editable(); + var editableTop = $editable.offset().top - $document.scrollTop(); + + var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); + var options = layoutInfo.editor().data('options'); + + $document.on('mousemove', function (event) { + var nHeight = event.clientY - (editableTop + EDITABLE_PADDING); + + nHeight = (options.minHeight > 0) ? Math.max(nHeight, options.minHeight) : nHeight; + nHeight = (options.maxHeight > 0) ? Math.min(nHeight, options.maxHeight) : nHeight; + + $editable.height(nHeight); + }).one('mouseup', function () { + $document.off('mousemove'); + }); + }; + }; + + return Statusbar; +}); diff --git a/addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js b/addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js index 832b7c4c561..5e5e5721b63 100644 --- a/addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js +++ b/addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js @@ -1,14 +1,17 @@ define([ 'summernote/core/list', + 'summernote/core/dom', 'summernote/module/Button' -], function (list, Button) { +], function (list, dom, Button) { /** + * @class module.Toolbar + * * Toolbar */ var Toolbar = function () { var button = new Button(); - this.button = button; // odoo change for overwrite + this.button = button; // ODOO: allow access for override this.update = function ($toolbar, styleInfo) { button.update($toolbar, styleInfo); @@ -43,14 +46,54 @@ define([ .addClass('disabled'); }; + /** + * @param {jQuery} $container + * @param {Boolean} [bFullscreen=false] + */ this.updateFullscreen = function ($container, bFullscreen) { var $btn = $container.find('button[data-event="fullscreen"]'); $btn.toggleClass('active', bFullscreen); }; + /** + * @param {jQuery} $container + * @param {Boolean} [isCodeview=false] + */ this.updateCodeview = function ($container, isCodeview) { var $btn = $container.find('button[data-event="codeview"]'); $btn.toggleClass('active', isCodeview); + + if (isCodeview) { + this.deactivate($container); + } else { + this.activate($container); + } + }; + + /** + * get button in toolbar + * + * @param {jQuery} $editable + * @param {String} name + * @return {jQuery} + */ + this.get = function ($editable, name) { + var $toolbar = dom.makeLayoutInfo($editable).toolbar(); + + return $toolbar.find('[data-name=' + name + ']'); + }; + + /** + * set button state + * @param {jQuery} $editable + * @param {String} name + * @param {Boolean} [isActive=true] + */ + this.setButtonState = function ($editable, name, isActive) { + isActive = (isActive === false) ? false : true; + + var $button = this.get($editable, name); + $button.toggleClass('active', isActive); }; }; |
