diff options
Diffstat (limited to 'addons/web_editor/static/lib/summernote/src/js/EventHandler.js')
| -rw-r--r-- | addons/web_editor/static/lib/summernote/src/js/EventHandler.js | 829 |
1 files changed, 518 insertions, 311 deletions
diff --git a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js index 09a7b00082a..068ceff735f 100644 --- a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js +++ b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js @@ -1,131 +1,92 @@ define([ - 'summernote/core/agent', - 'summernote/core/func', - 'summernote/core/dom', - 'summernote/core/async', - 'summernote/core/key', - 'summernote/core/list', - 'summernote/editing/History', - 'summernote/module/Editor', - 'summernote/module/Toolbar', - 'summernote/module/Statusbar', - 'summernote/module/Popover', - 'summernote/module/Handle', - 'summernote/module/Fullscreen', - 'summernote/module/Codeview', - 'summernote/module/DragAndDrop', - 'summernote/module/Clipboard', - 'summernote/module/LinkDialog', - 'summernote/module/ImageDialog', - 'summernote/module/HelpDialog' -], function (agent, func, dom, async, key, list, History, - Editor, Toolbar, Statusbar, Popover, Handle, Fullscreen, Codeview, - DragAndDrop, Clipboard, LinkDialog, ImageDialog, HelpDialog) { + 'summernote/core/agent', 'summernote/core/dom', 'summernote/core/async', 'summernote/core/key', 'summernote/core/list', 'summernote/core/range', + 'summernote/editing/Style', 'summernote/editing/Editor', 'summernote/editing/History', + 'summernote/module/Toolbar', 'summernote/module/Popover', 'summernote/module/Handle', 'summernote/module/Dialog' +], function (agent, dom, async, key, list, range, + Style, Editor, History, + Toolbar, Popover, Handle, Dialog) { + + var CodeMirror; + if (agent.hasCodeMirror) { + if (agent.isSupportAmd) { + require(['CodeMirror'], function (cm) { + CodeMirror = cm; + }); + } else { + CodeMirror = window.CodeMirror; + } + } /** - * @class EventHandler - * * EventHandler - * - TODO: new instance per a editor */ var EventHandler = function () { - var self = this; + var $window = $(window); + var $document = $(document); + var $scrollbar = $('html, body'); - /** - * Modules - */ - var modules = this.modules = { - editor: new Editor(this), - toolbar: new Toolbar(this), - statusbar: new Statusbar(this), - popover: new Popover(this), - handle: new Handle(this), - fullscreen: new Fullscreen(this), - codeview: new Codeview(this), - dragAndDrop: new DragAndDrop(this), - clipboard: new Clipboard(this), - linkDialog: new LinkDialog(this), - imageDialog: new ImageDialog(this), - helpDialog: new HelpDialog(this) + var editor = new Editor(); + var toolbar = new Toolbar(), popover = new Popover(); + var handle = new Handle(), dialog = new Dialog(); + + this.getEditor = function () { + return editor; }; /** - * invoke module's method + * returns makeLayoutInfo from editor's descendant node. * - * @param {String} moduleAndMethod - ex) 'editor.redo' - * @param {...*} arguments - arguments of method - * @return {*} + * @param {Node} descendant + * @returns {Object} */ - this.invoke = function () { - var moduleAndMethod = list.head(list.from(arguments)); - var args = list.tail(list.from(arguments)); - - var splits = moduleAndMethod.split('.'); - var hasSeparator = splits.length > 1; - var moduleName = hasSeparator && list.head(splits); - var methodName = hasSeparator ? list.last(splits) : list.head(splits); - - var module = this.getModule(moduleName); - var method = module[methodName]; + var makeLayoutInfo = function (descendant) { + var $target = $(descendant).closest('.note-editor, .note-air-editor, .note-air-layout'); - return method && method.apply(module, args); - }; + if (!$target.length) { return null; } - /** - * returns module - * - * @param {String} moduleName - name of module - * @return {Module} - defaults is editor - */ - this.getModule = function (moduleName) { - return this.modules[moduleName] || this.modules.editor; - }; + var $editor; + if ($target.is('.note-editor, .note-air-editor')) { + $editor = $target; + } else { + $editor = $('[data-note-id="' + list.last($target.attr('id').split('-')) + '"]'); + } - /** - * @param {jQuery} $holder - * @param {Object} callbacks - * @param {String} eventNamespace - * @returns {Function} - */ - var bindCustomEvent = this.bindCustomEvent = function ($holder, callbacks, eventNamespace) { - return function () { - var callback = callbacks[func.namespaceToCamel(eventNamespace, 'on')]; - if (callback) { - callback.apply($holder[0], arguments); - } - return $holder.trigger('summernote.' + eventNamespace, arguments); - }; + return dom.buildLayoutInfo($editor); }; /** * insert Images from file array. * - * @private * @param {Object} layoutInfo * @param {File[]} files */ - this.insertImages = function (layoutInfo, files) { + var insertImages = function (layoutInfo, files) { var $editor = layoutInfo.editor(), - $editable = layoutInfo.editable(), - $holder = layoutInfo.holder(); + $editable = layoutInfo.editable(); var callbacks = $editable.data('callbacks'); var options = $editor.data('options'); // If onImageUpload options setted if (callbacks.onImageUpload) { - bindCustomEvent($holder, callbacks, 'image.upload')(files); + callbacks.onImageUpload(files, editor, $editable); // else insert Image as dataURL } else { $.each(files, function (idx, file) { var filename = file.name; if (options.maximumImageFileSize && options.maximumImageFileSize < file.size) { - bindCustomEvent($holder, callbacks, 'image.upload.error')(options.langInfo.image.maximumFileSizeError); + if (callbacks.onImageUploadError) { + callbacks.onImageUploadError(options.langInfo.image.maximumFileSizeError); + } else { + alert(options.langInfo.image.maximumFileSizeError); + } } else { async.readFileAsDataURL(file).then(function (sDataURL) { - modules.editor.insertImage($editable, sDataURL, filename); + editor.insertImage($editable, sDataURL, filename); }).fail(function () { - bindCustomEvent($holder, callbacks, 'image.upload.error')(options.langInfo.image.maximumFileSizeError); + if (callbacks.onImageUploadError) { + callbacks.onImageUploadError(); + } }); } }); @@ -137,90 +98,292 @@ define([ * @param {Object} layoutInfo */ showLinkDialog: function (layoutInfo) { - modules.linkDialog.show(layoutInfo); + var $editor = layoutInfo.editor(), + $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(), + linkInfo = editor.getLinkInfo($editable); + + var options = $editor.data('options'); + + editor.saveRange($editable); + dialog.showLinkDialog($editable, $dialog, linkInfo).then(function (linkInfo) { + editor.restoreRange($editable); + editor.createLink($editable, linkInfo, options); + // hide popover after creating link + popover.hide(layoutInfo.popover()); + }).fail(function () { + editor.restoreRange($editable); + }); }, /** * @param {Object} layoutInfo */ showImageDialog: function (layoutInfo) { - modules.imageDialog.show(layoutInfo); + var $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(); + + editor.saveRange($editable); + dialog.showImageDialog($editable, $dialog).then(function (data) { + editor.restoreRange($editable); + + if (typeof data === 'string') { + // image url + editor.insertImage($editable, data); + } else { + // array of files + insertImages(layoutInfo, data); + } + }).fail(function () { + editor.restoreRange($editable); + }); }, /** * @param {Object} layoutInfo */ showHelpDialog: function (layoutInfo) { - modules.helpDialog.show(layoutInfo); + var $dialog = layoutInfo.dialog(), + $editable = layoutInfo.editable(); + + editor.saveRange($editable, true); + dialog.showHelpDialog($editable, $dialog).then(function () { + editor.restoreRange($editable); + }); }, - /** - * @param {Object} layoutInfo - */ fullscreen: function (layoutInfo) { - modules.fullscreen.toggle(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'); + } + + toolbar.updateFullscreen($toolbar, isFullscreen); }, - /** - * @param {Object} layoutInfo - */ codeview: function (layoutInfo) { - modules.codeview.toggle(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'); + + var cmEditor, server; + + $editor.toggleClass('codeview'); + + var isCodeview = $editor.hasClass('codeview'); + if (isCodeview) { + $codable.val(dom.html($editable, true)); + $codable.height($editable.height()); + toolbar.deactivate($toolbar); + popover.hide($popover); + handle.hide($handle); + $codable.focus(); + + // activate CodeMirror as codable + if (agent.hasCodeMirror) { + cmEditor = CodeMirror.fromTextArea($codable[0], options.codemirror); + + // CodeMirror TernServer + if (options.codemirror.tern) { + 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); + } + } else { + // deactivate CodeMirror as codable + if (agent.hasCodeMirror) { + cmEditor = $codable.data('cmEditor'); + $codable.val(cmEditor.getValue()); + cmEditor.toTextArea(); + } + + $editable.html(dom.value($codable) || dom.emptyPara); + $editable.height(options.height ? $codable.height() : 'auto'); + + toolbar.activate($toolbar); + $editable.focus(); + } + + toolbar.updateCodeview(layoutInfo.toolbar(), isCodeview); } }; + this.commands = commands; // odoo change for overwrite + this.editor = editor; // odoo change for overwrite + this.toolbar = toolbar; // odoo change for overwrite + this.popover = popover; // odoo change for overwrite + this.handle = handle; // odoo change for overwrite + this.dialog = dialog; // odoo change for overwrite + var hMousedown = function (event) { //preventDefault Selection for FF, IE8+ if (dom.isImg(event.target)) { event.preventDefault(); + range.createFromNode(event.target).select(); } }; - var hKeyupAndMouseup = function (event) { - var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); - modules.editor.removeBogus(layoutInfo.editable()); - hToolbarAndPopoverUpdate(event); + var hToolbarAndPopoverUpdate = function (event) { + // delay for range after mouseup + setTimeout(function () { + var layoutInfo = makeLayoutInfo(event.target); + var $editable = layoutInfo.editable(); + editor.saveRange($editable); + var styleInfo = editor.currentStyle(); + if (!styleInfo) { return; } + + var isAirMode = layoutInfo.editor().data('options').airMode; + if (!isAirMode) { + toolbar.update(layoutInfo.toolbar(), styleInfo); + } + + popover.update(layoutInfo.popover(), styleInfo, isAirMode); + handle.update(layoutInfo.handle(), styleInfo, isAirMode); + }, 0); + }; + + var hScroll = function (event) { + var layoutInfo = makeLayoutInfo(event.currentTarget || event.target); + //hide popover and handle when scrolled + popover.hide(layoutInfo.popover()); + handle.hide(layoutInfo.handle()); }; /** - * update sytle info - * @param {Object} styleInfo - * @param {Object} layoutInfo + * paste clipboard image + * + * @param {Event} event */ - this.updateStyleInfo = function (styleInfo, layoutInfo) { - if (!styleInfo) { + var hPasteClipboardImage = function (event) { + var clipboardData = event.originalEvent.clipboardData; + var layoutInfo = makeLayoutInfo(event.currentTarget || event.target); + var $editable = layoutInfo.editable(); + + if (!clipboardData || !clipboardData.items || !clipboardData.items.length) { + var callbacks = $editable.data('callbacks'); + // only can run if it has onImageUpload method + if (!callbacks.onImageUpload) { + return; + } + + // save cursor + editor.saveNode($editable); + editor.saveRange($editable); + + $editable.html(''); + + setTimeout(function () { + var $img = $editable.find('img'); + var datauri = $img[0].src; + + var data = atob(datauri.split(',')[1]); + var array = new Uint8Array(data.length); + for (var i = 0; i < data.length; i++) { + array[i] = data.charCodeAt(i); + } + + var blob = new Blob([array], { type : 'image/png'}); + blob.name = 'clipboard.png'; + + editor.restoreNode($editable); + editor.restoreRange($editable); + insertImages(layoutInfo, [blob]); + + editor.afterCommand($editable); + }, 0); + return; } - var isAirMode = layoutInfo.editor().data('options').airMode; - if (!isAirMode) { - modules.toolbar.update(layoutInfo.toolbar(), styleInfo); + + var item = list.head(clipboardData.items); + var isClipboardImage = item.kind === 'file' && item.type.indexOf('image/') !== -1; + + if (isClipboardImage) { + insertImages(layoutInfo, [item.getAsFile()]); } - modules.popover.update(layoutInfo.popover(), styleInfo, isAirMode); - modules.handle.update(layoutInfo.handle(), styleInfo, isAirMode); + editor.afterCommand($editable); }; - var hToolbarAndPopoverUpdate = function (event) { - var target = event.target; - // delay for range after mouseup - setTimeout(function () { - var layoutInfo = dom.makeLayoutInfo(target); - /* ODOO: (start_modification */ - var $editable = layoutInfo.editable(); - if (!event.isDefaultPrevented()) { - modules.editor.saveRange($editable); - } - /* ODOO: end_modification) */ - var styleInfo = modules.editor.currentStyle(target); - self.updateStyleInfo(styleInfo, layoutInfo); - }, 0); - }; + /** + * `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 = 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) { + editor.resizeTo({ + x: event.clientX - posStart.left, + y: event.clientY - (posStart.top - scrollTop) + }, $target, !event.shiftKey); + + handle.update($handle, {image: target}, isAirMode); + popover.update($popover, {image: target}, isAirMode); + }).one('mouseup', function () { + $document.off('mousemove'); + editor.afterCommand($editable); + }); - var hScroll = function (event) { - var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); - //hide popover and handle when scrolled - modules.popover.hide(layoutInfo.popover()); - modules.handle.hide(layoutInfo.handle()); + if (!$target.data('ratio')) { // original ratio. + $target.data('ratio', $target.height() / $target.width()); + } + } }; var hToolbarAndPopoverMousedown = function (event) { @@ -234,49 +397,76 @@ define([ var hToolbarAndPopoverClick = function (event) { var $btn = $(event.target).closest('[data-event]'); - if (!$btn.length) { - return; - } + if ($btn.length && !$btn.is(':disabled')) { + var eventName = $btn.attr('data-event'), + value = $btn.attr('data-value'), + hide = $btn.attr('data-hide'); - var eventName = $btn.attr('data-event'), - value = $btn.attr('data-value'), - hide = $btn.attr('data-hide'); + var layoutInfo = makeLayoutInfo(event.target); - var layoutInfo = dom.makeLayoutInfo(event.target); + var $editable = layoutInfo.editable(); + editor.restoreRange($editable); - // before command: detect control selection element($target) - var $target; - if ($.inArray(eventName, ['resize', 'floatMe', 'removeMedia', 'imageShape']) !== -1) { - var $selection = layoutInfo.handle().find('.note-control-selection'); - $target = $($selection.data('target')); - } + // before command: detect control selection element($target) + var $target; + if ($.inArray(eventName, ['resize', 'floatMe', 'removeMedia', 'imageShape']) !== -1) { + var $selection = layoutInfo.handle().find('.note-control-selection'); + $target = $($selection.data('target')); + } - // If requested, hide the popover when the button is clicked. - // Useful for things like showHelpDialog. - if (hide) { - $btn.parents('.popover').hide(); - } + // If requested, hide the popover when the button is clicked. + // Useful for things like showHelpDialog. + if (hide) { + $btn.parents('.popover').hide(); + } - if ($.isFunction($.summernote.pluginEvents[eventName])) { - $.summernote.pluginEvents[eventName](event, modules.editor, layoutInfo, value); - } else if (modules.editor[eventName]) { // on command - var $editable = layoutInfo.editable(); - $editable.focus(); - modules.editor[eventName]($editable, value, $target); - event.preventDefault(); - } else if (commands[eventName]) { - commands[eventName].call(this, layoutInfo); - event.preventDefault(); - } + if ($.isFunction($.summernote.pluginEvents[eventName])) { + $.summernote.pluginEvents[eventName](event, editor, layoutInfo, value); + } else if (editor[eventName]) { // on command + $editable.trigger('focus'); + editor[eventName]($editable, value, $target); + event.preventDefault(); + } else if (commands[eventName]) { + commands[eventName].call(this, layoutInfo); + event.preventDefault(); + } - // after command - if ($.inArray(eventName, ['backColor', 'foreColor']) !== -1) { - var options = layoutInfo.editor().data('options', options); - var module = options.airMode ? modules.popover : modules.toolbar; - module.updateRecentColor(list.head($btn), eventName, value); + // after command + if ($.inArray(eventName, ['backColor', 'foreColor']) !== -1) { + var options = layoutInfo.editor().data('options', options); + var module = options.airMode ? popover : toolbar; + module.updateRecentColor(list.head($btn), eventName, value); + } + + hToolbarAndPopoverUpdate(event); } + }; + + var EDITABLE_PADDING = 24; + /** + * `mousedown` event handler on statusbar + * + * @param {MouseEvent} event + */ + var hStatusbarMousedown = function (event) { + event.preventDefault(); + event.stopPropagation(); + + var layoutInfo = makeLayoutInfo(event.target); + var $editable = layoutInfo.editable(); + var nEditableTop = $editable.offset().top - $document.scrollTop(); + var options = layoutInfo.editor().data('options'); - hToolbarAndPopoverUpdate(event); + $document.on('mousemove', function (event) { + var nHeight = event.clientY - (nEditableTop + 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'); + }); }; var PX_PER_EM = 18; @@ -320,7 +510,81 @@ define([ $dimensionDisplay.html(dim.c + ' x ' + dim.r); }; - + + /** + * Drag and Drop Events + * + * @param {Object} layoutInfo - layout Informations + * @param {Object} options + */ + var handleDragAndDropEvent = function (layoutInfo, options) { + if (options.disableDragAndDrop) { + // prevent default drop event + $document.on('drop', function (e) { + e.preventDefault(); + }); + } else { + attachDragAndDropEvent(layoutInfo, options); + } + }; + + /** + * attach Drag and Drop Events + * + * @param {Object} layoutInfo - layout Informations + * @param {Object} options + */ + var attachDragAndDropEvent = function (layoutInfo, options) { + var collection = $(), + $dropzone = layoutInfo.dropzone, + $dropzoneMessage = layoutInfo.dropzone.find('.note-dropzone-message'); + + // show dropzone on dragenter when dragging a object to document. + $document.on('dragenter', function (e) { + var isCodeview = layoutInfo.editor.hasClass('codeview'); + if (!isCodeview && !collection.length) { + layoutInfo.editor.addClass('dragover'); + $dropzone.width(layoutInfo.editor.width()); + $dropzone.height(layoutInfo.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) { + layoutInfo.editor.removeClass('dragover'); + } + }).on('drop', function () { + collection = $(); + layoutInfo.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) { + event.preventDefault(); + + var dataTransfer = event.originalEvent.dataTransfer; + var text = dataTransfer.getData('text/plain'); + var layoutInfo = makeLayoutInfo(event.currentTarget || event.target); + layoutInfo.editable().focus(); + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + insertImages(layoutInfo, dataTransfer.files); + } else if (text) { + editor.insertText(layoutInfo.editable(), text); + } + }).on('dragover', false); // prevent default dragover event + }; + + /** * bind KeyMap on keydown * @@ -328,28 +592,25 @@ define([ * @param {Object} keyMap */ this.bindKeyMap = function (layoutInfo, keyMap) { - var $editor = layoutInfo.editor(); - var $editable = layoutInfo.editable(); + var $editor = layoutInfo.editor; + var $editable = layoutInfo.editable; + + layoutInfo = makeLayoutInfo($editable); $editable.on('keydown', function (event) { - var keys = []; + var aKey = []; // modifier - if (event.metaKey) { keys.push('CMD'); } - if (event.ctrlKey && !event.altKey) { keys.push('CTRL'); } - if (event.shiftKey) { keys.push('SHIFT'); } + if (event.metaKey) { aKey.push('CMD'); } + if (event.ctrlKey && !event.altKey) { aKey.push('CTRL'); } + if (event.shiftKey) { aKey.push('SHIFT'); } // keycode var keyName = key.nameFromCode[event.keyCode]; - if (keyName) { - keys.push(keyName); - } + if (keyName) { aKey.push(keyName); } - var pluginEvent; - var keyString = keys.join('+'); - var eventName = keyMap[keyString]; + var eventName = keyMap[aKey.join('+')]; - // ODOO: (start_modification // odoo change: add visible event to overwrite the browser comportment var keycode = event.keyCode; if (!eventName && @@ -362,33 +623,27 @@ define([ (keycode > 218 && keycode < 223))) { // [\]' (in order)) eventName = 'visible'; } else if (!keycode) { - self.invoke('restoreRange', $editable); + editor.restoreRange($editable); } - // ODOO: end_modification) if (eventName) { - // FIXME Summernote doesn't support event pipeline yet. - // - Plugin -> Base Code - pluginEvent = $.summernote.pluginEvents[keyString]; - if ($.isFunction(pluginEvent)) { - if (pluginEvent(event, modules.editor, layoutInfo)) { - return false; + if ($.summernote.pluginEvents[eventName]) { + var plugin = $.summernote.pluginEvents[eventName]; + if ($.isFunction(plugin)) { + plugin(event, editor, layoutInfo); } - } - - pluginEvent = $.summernote.pluginEvents[eventName]; - - if ($.isFunction(pluginEvent)) { - pluginEvent(event, modules.editor, layoutInfo); - } else if (modules.editor[eventName]) { - modules.editor[eventName]($editable, $editor.data('options')); + } else if (editor[eventName]) { + editor[eventName]($editable, $editor.data('options')); event.preventDefault(); } else if (commands[eventName]) { commands[eventName].call(this, layoutInfo); event.preventDefault(); } + if (keycode) { + editor.saveRange($editable); + } } else if (key.isEdit(event.keyCode)) { - modules.editor.afterCommand($editable); + editor.afterCommand($editable); } }); }; @@ -398,40 +653,41 @@ define([ * * @param {Object} layoutInfo - layout Informations * @param {Object} options - user options include custom event handlers + * @param {Function} options.enter - enter key handler */ this.attach = function (layoutInfo, options) { // handlers for editable if (options.shortcuts) { this.bindKeyMap(layoutInfo, options.keyMap[agent.isMac ? 'mac' : 'pc']); } - layoutInfo.editable().on('mousedown', hMousedown); - layoutInfo.editable().on('keyup mouseup', hKeyupAndMouseup); - layoutInfo.editable().on('scroll', hScroll); - - // handler for clipboard - modules.clipboard.attach(layoutInfo, options); + layoutInfo.editable.on('mousedown', hMousedown); + layoutInfo.editable.on('keyup mouseup', hToolbarAndPopoverUpdate); + layoutInfo.editable.on('scroll', hScroll); + layoutInfo.editable.on('paste', hPasteClipboardImage); // handler for handle and popover - modules.handle.attach(layoutInfo, options); - layoutInfo.popover().on('click', hToolbarAndPopoverClick); - layoutInfo.popover().on('mousedown', hToolbarAndPopoverMousedown); - - // handler for drag and drop - modules.dragAndDrop.attach(layoutInfo, options); + layoutInfo.handle.on('mousedown', hHandleMousedown); + layoutInfo.popover.on('click', hToolbarAndPopoverClick); + layoutInfo.popover.on('mousedown', hToolbarAndPopoverMousedown); // handlers for frame mode (toolbar, statusbar) if (!options.airMode) { + // handler for drag and drop + handleDragAndDropEvent(layoutInfo, options); + // handler for toolbar - layoutInfo.toolbar().on('click', hToolbarAndPopoverClick); - layoutInfo.toolbar().on('mousedown', hToolbarAndPopoverMousedown); + layoutInfo.toolbar.on('click', hToolbarAndPopoverClick); + layoutInfo.toolbar.on('mousedown', hToolbarAndPopoverMousedown); // handler for statusbar - modules.statusbar.attach(layoutInfo, options); + if (!options.disableResizeEditor) { + layoutInfo.statusbar.on('mousedown', hStatusbarMousedown); + } } // handler for table dimension - var $catcherContainer = options.airMode ? layoutInfo.popover() : - layoutInfo.toolbar(); + var $catcherContainer = options.airMode ? layoutInfo.popover : + layoutInfo.toolbar; var $catcher = $catcherContainer.find('.note-dimension-picker-mousecatcher'); $catcher.css({ width: options.insertTableMaxSize.col + 'em', @@ -441,121 +697,72 @@ define([ }); // save options on editor - layoutInfo.editor().data('options', options); + layoutInfo.editor.data('options', options); // ret styleWithCSS for backColor / foreColor clearing with 'inherit'. if (!agent.isMSIE) { - // [workaround] for Firefox - // - protect FF Error: NS_ERROR_FAILURE: Failure + // protect FF Error: NS_ERROR_FAILURE: Failure setTimeout(function () { document.execCommand('styleWithCSS', 0, options.styleWithSpan); }, 0); } // History - var history = new History(layoutInfo.editable()); - layoutInfo.editable().data('NoteHistory', history); + var history = new History(layoutInfo.editable); + layoutInfo.editable.data('NoteHistory', history); + + // basic event callbacks (lowercase) + // enter, focus, blur, keyup, keydown + if (options.onenter) { + layoutInfo.editable.keypress(function (event) { + if (event.keyCode === key.ENTER) { options.onenter(event); } + }); + } + + if (options.onfocus) { layoutInfo.editable.focus(options.onfocus); } + if (options.onblur) { layoutInfo.editable.blur(options.onblur); } + if (options.onkeyup) { layoutInfo.editable.keyup(options.onkeyup); } + if (options.onkeydown) { layoutInfo.editable.keydown(options.onkeydown); } + if (options.onpaste) { layoutInfo.editable.on('paste', options.onpaste); } + + // callbacks for advanced features (camel) + if (options.onToolbarClick) { layoutInfo.toolbar.click(options.onToolbarClick); } + if (options.onChange) { + var hChange = function () { + editor.triggerOnChange(layoutInfo.editable); + }; + + if (agent.isMSIE) { + var sDomEvents = 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted'; + layoutInfo.editable.on(sDomEvents, hChange); + } else { + layoutInfo.editable.on('input', hChange); + } + } // All editor status will be saved on editable with jquery's data // for support multiple editor with singleton object. - layoutInfo.editable().data('callbacks', { - onInit: options.onInit, - onFocus: options.onFocus, - onBlur: options.onBlur, - onKeydown: options.onKeydown, - onKeyup: options.onKeyup, - onMousedown: options.onMousedown, - onEnter: options.onEnter, - onPaste: options.onPaste, - onBeforeCommand: options.onBeforeCommand, + layoutInfo.editable.data('callbacks', { onChange: options.onChange, + onAutoSave: options.onAutoSave, onImageUpload: options.onImageUpload, onImageUploadError: options.onImageUploadError, - onMediaDelete: options.onMediaDelete, - onToolbarClick: options.onToolbarClick + onFileUpload: options.onFileUpload, + onFileUploadError: options.onFileUpload }); - - var styleInfo = modules.editor.styleFromNode(layoutInfo.editable()); - this.updateStyleInfo(styleInfo, layoutInfo); }; - /** - * attach jquery custom event - * - * @param {Object} layoutInfo - layout Informations - */ - this.attachCustomEvent = function (layoutInfo, options) { - var $holder = layoutInfo.holder(); - var $editable = layoutInfo.editable(); - var callbacks = $editable.data('callbacks'); - - $editable.focus(bindCustomEvent($holder, callbacks, 'focus')); - $editable.blur(bindCustomEvent($holder, callbacks, 'blur')); - - $editable.keydown(function (event) { - if (event.keyCode === key.code.ENTER) { - bindCustomEvent($holder, callbacks, 'enter').call(this, event); - } - bindCustomEvent($holder, callbacks, 'keydown').call(this, event); - }); - $editable.keyup(bindCustomEvent($holder, callbacks, 'keyup')); - - $editable.on('mousedown', bindCustomEvent($holder, callbacks, 'mousedown')); - $editable.on('mouseup', bindCustomEvent($holder, callbacks, 'mouseup')); - $editable.on('scroll', bindCustomEvent($holder, callbacks, 'scroll')); - - $editable.on('paste', bindCustomEvent($holder, callbacks, 'paste')); - - // [workaround] IE doesn't have input events for contentEditable - // - see: https://goo.gl/4bfIvA - var changeEventName = agent.isMSIE ? 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted' : 'input'; - $editable.on(changeEventName, function () { - bindCustomEvent($holder, callbacks, 'change')($editable.html(), $editable); - }); - - if (!options.airMode) { - layoutInfo.toolbar().click(bindCustomEvent($holder, callbacks, 'toolbar.click')); - layoutInfo.popover().click(bindCustomEvent($holder, callbacks, 'popover.click')); - } - - // Textarea: auto filling the code before form submit. - if (dom.isTextarea(list.head($holder))) { - $holder.closest('form').submit(function (e) { - layoutInfo.holder().val(layoutInfo.holder().code()); - bindCustomEvent($holder, callbacks, 'submit').call(this, e, $holder.code()); - }); - } - - // textarea auto sync - if (dom.isTextarea(list.head($holder)) && options.textareaAutoSync) { - $holder.on('summernote.change', function () { - layoutInfo.holder().val(layoutInfo.holder().code()); - }); - } - - // fire init event - bindCustomEvent($holder, callbacks, 'init')(layoutInfo); - - // fire plugin init event - for (var i = 0, len = $.summernote.plugins.length; i < len; i++) { - if ($.isFunction($.summernote.plugins[i].init)) { - $.summernote.plugins[i].init(layoutInfo); - } - } - }; - this.detach = function (layoutInfo, options) { - layoutInfo.holder().off(); - layoutInfo.editable().off(); + layoutInfo.editable.off(); - layoutInfo.popover().off(); - layoutInfo.handle().off(); - layoutInfo.dialog().off(); + layoutInfo.popover.off(); + layoutInfo.handle.off(); + layoutInfo.dialog.off(); if (!options.airMode) { - layoutInfo.dropzone().off(); - layoutInfo.toolbar().off(); - layoutInfo.statusbar().off(); + layoutInfo.dropzone.off(); + layoutInfo.toolbar.off(); + layoutInfo.statusbar.off(); } }; }; |
