summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/lib/summernote
diff options
context:
space:
mode:
authorRobot Odoo <xmo+roboto@odoo.com>2019-01-17 19:17:22 +0100
committerGitHub <noreply@github.com>2019-01-17 19:17:22 +0100
commit22efdfbecc1e8c688000be98903a5a355d1ece8a (patch)
treee097625171b2b67ed5f5288ced67446608b9f633 /addons/web_editor/static/lib/summernote
parentf50fbc6d168f0d17fa1e932b4ecc968a92b02b65 (diff)
parenteb0ea8efbe932c140302e9f8da33a839bcd73146 (diff)
[IMP] web_editor,*: Refactoring the wysiwyg editor and 'html' field
_Task #1878502_ - The refactoring allows for the update of the _Summernote_ library. - A new structure was created by transforming all the plugins in the library using the odoo inheritance system. Plugins are easier to implement with the `AbstractPlugin` to add Odoo behaviors. - The cleanup allowed us to remove some of the code that was in `web_editor` but only used in `website` or in `mass_mailing`. Some parts are still in `web_editor` but will be moved at a later time. - The refactoring also had to be done in order to create a set of unit tests. Each behavior can be tested, including the behaviors performed as a consequence of keyboard interactions (for instance: Enter, Tab...). - From now on, the methods of the library (in this case _Summernote_) can no longer be called by other modules or files. Only the `wysiwyg` widgets can access it, to simplify the updating process. The `wysiwyg` object serves as an interface. A lot of tests are done, ensuring the consistency of outside behavior. - Depending on the options the snippets will be loaded or not, the editor will be in an iframe or not... all of this is transparent from the outside. - Regarding iframes, all controllers related to editing have been removed: the new API no longer needs them. This speeds up loading, eases testing and removes complexity for the same features. **PUBLIC FEATURES** There are several public methods on the `Wysiwyg` class: - `Wysiwyg.prepare (WidgetParent)` which returns a deferred resolved when the library (xml, lazy, assets...) is loaded. - `Wysiwyg.getRange (DOM)` returns the range (selection in the dom) - `Wysiwyg.setRange (startNode, startOffset, endNode, endOffset)` which creates a range (selection in the dom) - `Wysiwyg.setRangeFromNode (DOM, options)` that creates a range from an element (option available to select all, start or end) A jQuery selector was added: `:o_editable`, which indicates whether the current element is editable. That is, if it is contained in a tag with the attribute `contentEditable = "true"` or in a tag with the class `o_editable`. Several methods are also present: - `focusIn`: makes a focus and places the cursor at the beginning of the element - `focusInEnd`: makes a focus and places the cursor at the end of the element - `selectContent`: makes a focus and selects the content **HTML FIELD** The HTML field can receive different options: - `style-inline:` {boolean} transforms a class into an inline style when saving and vice versa when reading. - `no-attachment`: {boolean} prevents the use of attachments (in media dialog) - `cssEdit`: {xml_id} to use a template containing the css to loaded in an iframe when editing - `cssReadonly`: {xml_id} to use a template containing the css to load into an iframe when viewing in readonly - `snippets`: {xml_id} snippets template (can be used with or without `cssEdit`) - `wrapper`: {template} qweb static template (containing a tag: `id = "wrapper"`) that will include the content during editing (removed on save) **MASS MAILING** A widget was created for `mass mailing`. There are now two fields: `body_html` and `body_arch`. `body_arch` contains the code with the class without conversion into inline style, useful when editing and one with the inline style that is visible in readonly mode and sent by email. Advantage: no spreading errors, able to update css/theme, able to do more changes when converting to inline style so that a maximum of mail clients have an impeccable rendering. **REMAINING KNOW ISSUES** These will be fixed in a subsequent commit. - A few committed changes made during the development of this refactoring were lost in the process and should be reapplied. - Some minor (mostly range) issues remain with Firefox. -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr closes odoo/odoo#29775
Diffstat (limited to 'addons/web_editor/static/lib/summernote')
-rw-r--r--addons/web_editor/static/lib/summernote/font/summernote.eotbin0 -> 16570 bytes
-rw-r--r--addons/web_editor/static/lib/summernote/font/summernote.ttfbin0 -> 16384 bytes
-rw-r--r--addons/web_editor/static/lib/summernote/font/summernote.woffbin0 -> 10184 bytes
-rwxr-xr-xaddons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.css16
-rwxr-xr-xaddons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.js292
-rw-r--r--addons/web_editor/static/lib/summernote/plugin/hello/summernote-ext-hello.js81
-rw-r--r--addons/web_editor/static/lib/summernote/plugin/specialchars/summernote-ext-specialchars.js312
-rw-r--r--addons/web_editor/static/lib/summernote/src/css/summernote.css447
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/EventHandler.js569
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/Renderer.js1026
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/app.js42
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/agent.js170
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/async.js68
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/dom.js1120
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/func.js130
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/key.js96
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/list.js191
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/core/range.js790
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/defaults.js416
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js235
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/History.js118
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Style.js161
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Table.js52
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/editing/Typing.js86
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/intro.js21
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Button.js169
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js41
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Codeview.js137
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js98
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Editor.js865
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Fullscreen.js51
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Handle.js101
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/HelpDialog.js35
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/ImageDialog.js110
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/LinkDialog.js129
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Popover.js129
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Statusbar.js44
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js101
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/outro.js1
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/summernote.js328
-rw-r--r--addons/web_editor/static/lib/summernote/src/less/summernote.less467
-rw-r--r--addons/web_editor/static/lib/summernote/summernote.css1
-rw-r--r--addons/web_editor/static/lib/summernote/summernote.js7288
43 files changed, 7990 insertions, 8544 deletions
diff --git a/addons/web_editor/static/lib/summernote/font/summernote.eot b/addons/web_editor/static/lib/summernote/font/summernote.eot
new file mode 100644
index 00000000000..4b6f0f6b9fc
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/font/summernote.eot
Binary files differ
diff --git a/addons/web_editor/static/lib/summernote/font/summernote.ttf b/addons/web_editor/static/lib/summernote/font/summernote.ttf
new file mode 100644
index 00000000000..514099b602e
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/font/summernote.ttf
Binary files differ
diff --git a/addons/web_editor/static/lib/summernote/font/summernote.woff b/addons/web_editor/static/lib/summernote/font/summernote.woff
new file mode 100644
index 00000000000..63e49510b88
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/font/summernote.woff
Binary files differ
diff --git a/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.css b/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.css
new file mode 100755
index 00000000000..6232dde991f
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.css
@@ -0,0 +1,16 @@
+.ext-databasic {
+ position: relative;
+ display: block;
+ min-height: 50px;
+ background-color: cyan;
+ text-align: center;
+ padding: 20px;
+ border: 1px solid white;
+ border-radius: 10px;
+}
+
+.ext-databasic p {
+ color: white;
+ font-size: 1.2em;
+ margin: 0;
+}
diff --git a/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.js b/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.js
new file mode 100755
index 00000000000..efe0d4dc33e
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/plugin/databasic/summernote-ext-databasic.js
@@ -0,0 +1,292 @@
+(function(factory) {
+ /* global define */
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ // Node/CommonJS
+ module.exports = factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(window.jQuery);
+ }
+}(function($) {
+ // pull in some summernote core functions
+ var ui = $.summernote.ui;
+ var dom = $.summernote.dom;
+
+ // define the popover plugin
+ var DataBasicPlugin = function(context) {
+ var self = this;
+ var options = context.options;
+ var lang = options.langInfo;
+
+ self.icon = '<i class="fa fa-object-group"/>';
+
+ // add context menu button for dialog
+ context.memo('button.databasic', function() {
+ return ui.button({
+ contents: self.icon,
+ tooltip: lang.databasic.insert,
+ click: context.createInvokeHandler('databasic.showDialog')
+ }).render();
+ });
+
+ // add popover edit button
+ context.memo('button.databasicDialog', function() {
+ return ui.button({
+ contents: self.icon,
+ tooltip: lang.databasic.edit,
+ click: context.createInvokeHandler('databasic.showDialog')
+ }).render();
+ });
+
+ // add popover size buttons
+ context.memo('button.databasicSize100', function() {
+ return ui.button({
+ contents: '<span class="note-fontsize-10">100%</span>',
+ tooltip: lang.image.resizeFull,
+ click: context.createInvokeHandler('editor.resize', '1')
+ }).render();
+ });
+ context.memo('button.databasicSize50', function() {
+ return ui.button({
+ contents: '<span class="note-fontsize-10">50%</span>',
+ tooltip: lang.image.resizeHalf,
+ click: context.createInvokeHandler('editor.resize', '0.5')
+ }).render();
+ });
+ context.memo('button.databasicSize25', function() {
+ return ui.button({
+ contents: '<span class="note-fontsize-10">25%</span>',
+ tooltip: lang.image.resizeQuarter,
+ click: context.createInvokeHandler('editor.resize', '0.25')
+ }).render();
+ });
+
+ self.events = {
+ 'summernote.init': function(we, e) {
+ // update existing containers
+ $('data.ext-databasic', e.editable).each(function() { self.setContent($(this)); });
+ // TODO: make this an undo snapshot...
+ },
+ 'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function() {
+ self.update();
+ },
+ 'summernote.dialog.shown': function() {
+ self.hidePopover();
+ }
+ };
+
+ self.initialize = function() {
+ // create dialog markup
+ var $container = options.dialogsInBody ? $(document.body) : context.layoutInfo.editor;
+
+ var body = '<div class="form-group row-fluid">' +
+ '<label>' + lang.databasic.testLabel + '</label>' +
+ '<input class="ext-databasic-test form-control" type="text" />' +
+ '</div>';
+ var footer = '<button href="#" class="btn btn-primary ext-databasic-save">' + lang.databasic.insert + '</button>';
+
+ self.$dialog = ui.dialog({
+ title: lang.databasic.name,
+ fade: options.dialogsFade,
+ body: body,
+ footer: footer
+ }).render().appendTo($container);
+
+ // create popover
+ self.$popover = ui.popover({
+ className: 'ext-databasic-popover'
+ }).render().appendTo('body');
+ var $content = self.$popover.find('.popover-content');
+
+ context.invoke('buttons.build', $content, options.popover.databasic);
+ };
+
+ self.destroy = function() {
+ self.$popover.remove();
+ self.$popover = null;
+ self.$dialog.remove();
+ self.$dialog = null;
+ };
+
+ self.update = function() {
+ // Prevent focusing on editable when invoke('code') is executed
+ if (!context.invoke('editor.hasFocus')) {
+ self.hidePopover();
+ return;
+ }
+
+ var rng = context.invoke('editor.createRange');
+ var visible = false;
+
+ if (rng.isOnData()) {
+ var $data = $(rng.sc).closest('data.ext-databasic');
+
+ if ($data.length) {
+ var pos = dom.posFromPlaceholder($data[0]);
+
+ self.$popover.css({
+ display: 'block',
+ left: pos.left,
+ top: pos.top
+ });
+
+ // save editor target to let size buttons resize the container
+ context.invoke('editor.saveTarget', $data[0]);
+
+ visible = true;
+ }
+ }
+
+ // hide if not visible
+ if (!visible) {
+ self.hidePopover();
+ }
+ };
+
+ self.hidePopover = function() {
+ self.$popover.hide();
+ };
+
+ // define plugin dialog
+ self.getInfo = function() {
+ var rng = context.invoke('editor.createRange');
+
+ if (rng.isOnData()) {
+ var $data = $(rng.sc).closest('data.ext-databasic');
+
+ if ($data.length) {
+ // Get the first node on range(for edit).
+ return {
+ node: $data,
+ test: $data.attr('data-test')
+ };
+ }
+ }
+
+ return {};
+ };
+
+ self.setContent = function($node) {
+ $node.html('<p contenteditable="false">' + self.icon + ' ' + lang.databasic.name + ': ' +
+ $node.attr('data-test') + '</p>');
+ };
+
+ self.updateNode = function(info) {
+ self.setContent(info.node
+ .attr('data-test', info.test));
+ };
+
+ self.createNode = function(info) {
+ var $node = $('<data class="ext-databasic"></data>');
+
+ if ($node) {
+ // save node to info structure
+ info.node = $node;
+ // insert node into editor dom
+ context.invoke('editor.insertNode', $node[0]);
+ }
+
+ return $node;
+ };
+
+ self.showDialog = function() {
+ var info = self.getInfo();
+ var newNode = !info.node;
+ context.invoke('editor.saveRange');
+
+ self
+ .openDialog(info)
+ .then(function(dialogInfo) {
+ // [workaround] hide dialog before restore range for IE range focus
+ ui.hideDialog(self.$dialog);
+ context.invoke('editor.restoreRange');
+
+ // insert a new node
+ if (newNode) {
+ self.createNode(info);
+ }
+
+ // update info with dialog info
+ $.extend(info, dialogInfo);
+
+ self.updateNode(info);
+ })
+ .fail(function() {
+ context.invoke('editor.restoreRange');
+ });
+ };
+
+ self.openDialog = function(info) {
+ return $.Deferred(function(deferred) {
+ var $inpTest = self.$dialog.find('.ext-databasic-test');
+ var $saveBtn = self.$dialog.find('.ext-databasic-save');
+ var onKeyup = function(event) {
+ if (event.keyCode === 13) {
+ $saveBtn.trigger('click');
+ }
+ };
+
+ ui.onDialogShown(self.$dialog, function() {
+ context.triggerEvent('dialog.shown');
+
+ $inpTest.val(info.test).on('input', function() {
+ ui.toggleBtn($saveBtn, $inpTest.val());
+ }).trigger('focus').on('keyup', onKeyup);
+
+ $saveBtn
+ .text(info.node ? lang.databasic.edit : lang.databasic.insert)
+ .click(function(event) {
+ event.preventDefault();
+
+ deferred.resolve({ test: $inpTest.val() });
+ });
+
+ // init save button
+ ui.toggleBtn($saveBtn, $inpTest.val());
+ });
+
+ ui.onDialogHidden(self.$dialog, function() {
+ $inpTest.off('input keyup');
+ $saveBtn.off('click');
+
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+
+ ui.showDialog(self.$dialog);
+ });
+ };
+ };
+
+ // Extends summernote
+ $.extend(true, $.summernote, {
+ plugins: {
+ databasic: DataBasicPlugin
+ },
+
+ options: {
+ popover: {
+ databasic: [
+ ['databasic', ['databasicDialog', 'databasicSize100', 'databasicSize50', 'databasicSize25']]
+ ]
+ }
+ },
+
+ // add localization texts
+ lang: {
+ 'en-US': {
+ databasic: {
+ name: 'Basic Data Container',
+ insert: 'insert basic data container',
+ edit: 'edit basic data container',
+ testLabel: 'test input'
+ }
+ }
+ }
+
+ });
+}));
diff --git a/addons/web_editor/static/lib/summernote/plugin/hello/summernote-ext-hello.js b/addons/web_editor/static/lib/summernote/plugin/hello/summernote-ext-hello.js
new file mode 100644
index 00000000000..22e4f6ac034
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/plugin/hello/summernote-ext-hello.js
@@ -0,0 +1,81 @@
+(function(factory) {
+ /* global define */
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ // Node/CommonJS
+ module.exports = factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(window.jQuery);
+ }
+}(function($) {
+ // Extends plugins for adding hello.
+ // - plugin is external module for customizing.
+ $.extend($.summernote.plugins, {
+ /**
+ * @param {Object} context - context object has status of editor.
+ */
+ 'hello': function(context) {
+ var self = this;
+
+ // ui has renders to build ui elements.
+ // - you can create a button with `ui.button`
+ var ui = $.summernote.ui;
+
+ // add hello button
+ context.memo('button.hello', function() {
+ // create button
+ var button = ui.button({
+ contents: '<i class="fa fa-child"/> Hello',
+ tooltip: 'hello',
+ click: function() {
+ self.$panel.show();
+ self.$panel.hide(500);
+ // invoke insertText method with 'hello' on editor module.
+ context.invoke('editor.insertText', 'hello');
+ }
+ });
+
+ // create jQuery object from button instance.
+ var $hello = button.render();
+ return $hello;
+ });
+
+ // This events will be attached when editor is initialized.
+ this.events = {
+ // This will be called after modules are initialized.
+ 'summernote.init': function(we, e) {
+ console.log('summernote initialized', we, e);
+ },
+ // This will be called when user releases a key on editable.
+ 'summernote.keyup': function(we, e) {
+ console.log('summernote keyup', we, e);
+ }
+ };
+
+ // This method will be called when editor is initialized by $('..').summernote();
+ // You can create elements for plugin
+ this.initialize = function() {
+ this.$panel = $('<div class="hello-panel"/>').css({
+ position: 'absolute',
+ width: 100,
+ height: 100,
+ left: '50%',
+ top: '50%',
+ background: 'red'
+ }).hide();
+
+ this.$panel.appendTo('body');
+ };
+
+ // This methods will be called when editor is destroyed by $('..').summernote('destroy');
+ // You should remove elements on `initialize`.
+ this.destroy = function() {
+ this.$panel.remove();
+ this.$panel = null;
+ };
+ }
+ });
+}));
diff --git a/addons/web_editor/static/lib/summernote/plugin/specialchars/summernote-ext-specialchars.js b/addons/web_editor/static/lib/summernote/plugin/specialchars/summernote-ext-specialchars.js
new file mode 100644
index 00000000000..2ee4e512efd
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/plugin/specialchars/summernote-ext-specialchars.js
@@ -0,0 +1,312 @@
+(function(factory) {
+ /* global define */
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ // Node/CommonJS
+ module.exports = factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(window.jQuery);
+ }
+}(function($) {
+ $.extend($.summernote.plugins, {
+ 'specialchars': function(context) {
+ var self = this;
+ var ui = $.summernote.ui;
+
+ var $editor = context.layoutInfo.editor;
+ var options = context.options;
+ var lang = options.langInfo;
+
+ var KEY = {
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ ENTER: 13
+ };
+ var COLUMN_LENGTH = 15;
+ var COLUMN_WIDTH = 35;
+
+ var currentColumn = 0;
+ var currentRow = 0;
+ var totalColumn = 0;
+ var totalRow = 0;
+
+ // special characters data set
+ var specialCharDataSet = [
+ '&quot;', '&amp;', '&lt;', '&gt;', '&iexcl;', '&cent;',
+ '&pound;', '&curren;', '&yen;', '&brvbar;', '&sect;',
+ '&uml;', '&copy;', '&ordf;', '&laquo;', '&not;',
+ '&reg;', '&macr;', '&deg;', '&plusmn;', '&sup2;',
+ '&sup3;', '&acute;', '&micro;', '&para;', '&middot;',
+ '&cedil;', '&sup1;', '&ordm;', '&raquo;', '&frac14;',
+ '&frac12;', '&frac34;', '&iquest;', '&times;', '&divide;',
+ '&fnof;', '&circ;', '&tilde;', '&ndash;', '&mdash;',
+ '&lsquo;', '&rsquo;', '&sbquo;', '&ldquo;', '&rdquo;',
+ '&bdquo;', '&dagger;', '&Dagger;', '&bull;', '&hellip;',
+ '&permil;', '&prime;', '&Prime;', '&lsaquo;', '&rsaquo;',
+ '&oline;', '&frasl;', '&euro;', '&image;', '&weierp;',
+ '&real;', '&trade;', '&alefsym;', '&larr;', '&uarr;',
+ '&rarr;', '&darr;', '&harr;', '&crarr;', '&lArr;',
+ '&uArr;', '&rArr;', '&dArr;', '&hArr;', '&forall;',
+ '&part;', '&exist;', '&empty;', '&nabla;', '&isin;',
+ '&notin;', '&ni;', '&prod;', '&sum;', '&minus;',
+ '&lowast;', '&radic;', '&prop;', '&infin;', '&ang;',
+ '&and;', '&or;', '&cap;', '&cup;', '&int;',
+ '&there4;', '&sim;', '&cong;', '&asymp;', '&ne;',
+ '&equiv;', '&le;', '&ge;', '&sub;', '&sup;',
+ '&nsub;', '&sube;', '&supe;', '&oplus;', '&otimes;',
+ '&perp;', '&sdot;', '&lceil;', '&rceil;', '&lfloor;',
+ '&rfloor;', '&loz;', '&spades;', '&clubs;', '&hearts;',
+ '&diams;'
+ ];
+
+ context.memo('button.specialchars', function() {
+ return ui.button({
+ contents: '<i class="fa fa-font fa-flip-vertical">',
+ tooltip: lang.specialChar.specialChar,
+ click: function() {
+ self.show();
+ }
+ }).render();
+ });
+
+ /**
+ * Make Special Characters Table
+ *
+ * @member plugin.specialChar
+ * @private
+ * @return {jQuery}
+ */
+ this.makeSpecialCharSetTable = function() {
+ var $table = $('<table/>');
+ $.each(specialCharDataSet, function(idx, text) {
+ var $td = $('<td/>').addClass('note-specialchar-node');
+ var $tr = (idx % COLUMN_LENGTH === 0) ? $('<tr/>') : $table.find('tr').last();
+
+ var $button = ui.button({
+ callback: function($node) {
+ $node.html(text);
+ $node.attr('title', text);
+ $node.attr('data-value', encodeURIComponent(text));
+ $node.css({
+ width: COLUMN_WIDTH,
+ 'margin-right': '2px',
+ 'margin-bottom': '2px'
+ });
+ }
+ }).render();
+
+ $td.append($button);
+
+ $tr.append($td);
+ if (idx % COLUMN_LENGTH === 0) {
+ $table.append($tr);
+ }
+ });
+
+ totalRow = $table.find('tr').length;
+ totalColumn = COLUMN_LENGTH;
+
+ return $table;
+ };
+
+ this.initialize = function() {
+ var $container = options.dialogsInBody ? $(document.body) : $editor;
+
+ var body = '<div class="form-group row-fluid">' + this.makeSpecialCharSetTable()[0].outerHTML + '</div>';
+
+ this.$dialog = ui.dialog({
+ title: lang.specialChar.select,
+ body: body
+ }).render().appendTo($container);
+ };
+
+ this.show = function() {
+ var text = context.invoke('editor.getSelectedText');
+ context.invoke('editor.saveRange');
+ this.showSpecialCharDialog(text).then(function(selectChar) {
+ context.invoke('editor.restoreRange');
+
+ // build node
+ var $node = $('<span></span>').html(selectChar)[0];
+
+ if ($node) {
+ // insert video node
+ context.invoke('editor.insertNode', $node);
+ }
+ }).fail(function() {
+ context.invoke('editor.restoreRange');
+ });
+ };
+
+ /**
+ * show image dialog
+ *
+ * @param {jQuery} $dialog
+ * @return {Promise}
+ */
+ this.showSpecialCharDialog = function(text) {
+ return $.Deferred(function(deferred) {
+ var $specialCharDialog = self.$dialog;
+ var $specialCharNode = $specialCharDialog.find('.note-specialchar-node');
+ var $selectedNode = null;
+ var ARROW_KEYS = [KEY.UP, KEY.DOWN, KEY.LEFT, KEY.RIGHT];
+ var ENTER_KEY = KEY.ENTER;
+
+ function addActiveClass($target) {
+ if (!$target) {
+ return;
+ }
+ $target.find('button').addClass('active');
+ $selectedNode = $target;
+ }
+
+ function removeActiveClass($target) {
+ $target.find('button').removeClass('active');
+ $selectedNode = null;
+ }
+
+ // find next node
+ function findNextNode(row, column) {
+ var findNode = null;
+ $.each($specialCharNode, function(idx, $node) {
+ var findRow = Math.ceil((idx + 1) / COLUMN_LENGTH);
+ var findColumn = ((idx + 1) % COLUMN_LENGTH === 0) ? COLUMN_LENGTH : (idx + 1) % COLUMN_LENGTH;
+ if (findRow === row && findColumn === column) {
+ findNode = $node;
+ return false;
+ }
+ });
+ return $(findNode);
+ }
+
+ function arrowKeyHandler(keyCode) {
+ // left, right, up, down key
+ var $nextNode;
+ var lastRowColumnLength = $specialCharNode.length % totalColumn;
+
+ if (KEY.LEFT === keyCode) {
+ if (currentColumn > 1) {
+ currentColumn = currentColumn - 1;
+ } else if (currentRow === 1 && currentColumn === 1) {
+ currentColumn = lastRowColumnLength;
+ currentRow = totalRow;
+ } else {
+ currentColumn = totalColumn;
+ currentRow = currentRow - 1;
+ }
+ } else if (KEY.RIGHT === keyCode) {
+ if (currentRow === totalRow && lastRowColumnLength === currentColumn) {
+ currentColumn = 1;
+ currentRow = 1;
+ } else if (currentColumn < totalColumn) {
+ currentColumn = currentColumn + 1;
+ } else {
+ currentColumn = 1;
+ currentRow = currentRow + 1;
+ }
+ } else if (KEY.UP === keyCode) {
+ if (currentRow === 1 && lastRowColumnLength < currentColumn) {
+ currentRow = totalRow - 1;
+ } else {
+ currentRow = currentRow - 1;
+ }
+ } else if (KEY.DOWN === keyCode) {
+ currentRow = currentRow + 1;
+ }
+
+ if (currentRow === totalRow && currentColumn > lastRowColumnLength) {
+ currentRow = 1;
+ } else if (currentRow > totalRow) {
+ currentRow = 1;
+ } else if (currentRow < 1) {
+ currentRow = totalRow;
+ }
+
+ $nextNode = findNextNode(currentRow, currentColumn);
+
+ if ($nextNode) {
+ removeActiveClass($selectedNode);
+ addActiveClass($nextNode);
+ }
+ }
+
+ function enterKeyHandler() {
+ if (!$selectedNode) {
+ return;
+ }
+
+ deferred.resolve(decodeURIComponent($selectedNode.find('button').attr('data-value')));
+ $specialCharDialog.modal('hide');
+ }
+
+ function keyDownEventHandler(event) {
+ event.preventDefault();
+ var keyCode = event.keyCode;
+ if (keyCode === undefined || keyCode === null) {
+ return;
+ }
+ // check arrowKeys match
+ if (ARROW_KEYS.indexOf(keyCode) > -1) {
+ if ($selectedNode === null) {
+ addActiveClass($specialCharNode.eq(0));
+ currentColumn = 1;
+ currentRow = 1;
+ return;
+ }
+ arrowKeyHandler(keyCode);
+ } else if (keyCode === ENTER_KEY) {
+ enterKeyHandler();
+ }
+ return false;
+ }
+
+ // remove class
+ removeActiveClass($specialCharNode);
+
+ // find selected node
+ if (text) {
+ for (var i = 0; i < $specialCharNode.length; i++) {
+ var $checkNode = $($specialCharNode[i]);
+ if ($checkNode.text() === text) {
+ addActiveClass($checkNode);
+ currentRow = Math.ceil((i + 1) / COLUMN_LENGTH);
+ currentColumn = (i + 1) % COLUMN_LENGTH;
+ }
+ }
+ }
+
+ ui.onDialogShown(self.$dialog, function() {
+ $(document).on('keydown', keyDownEventHandler);
+
+ self.$dialog.find('button').tooltip();
+
+ $specialCharNode.on('click', function(event) {
+ event.preventDefault();
+ deferred.resolve(decodeURIComponent($(event.currentTarget).find('button').attr('data-value')));
+ ui.hideDialog(self.$dialog);
+ });
+ });
+
+ ui.onDialogHidden(self.$dialog, function() {
+ $specialCharNode.off('click');
+
+ self.$dialog.find('button').tooltip('destroy');
+
+ $(document).off('keydown', keyDownEventHandler);
+
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+
+ ui.showDialog(self.$dialog);
+ });
+ };
+ }
+ });
+}));
diff --git a/addons/web_editor/static/lib/summernote/src/css/summernote.css b/addons/web_editor/static/lib/summernote/src/css/summernote.css
deleted file mode 100644
index 7604c9272de..00000000000
--- a/addons/web_editor/static/lib/summernote/src/css/summernote.css
+++ /dev/null
@@ -1,447 +0,0 @@
-/* Theme Variables
- ------------------------------------------*/
-/* Frame Mode Layout
- ------------------------------------------*/
-.note-editor {
- border: 1px solid #a9a9a9;
- position: relative;
- /* overflow: hidden; ODOO: removed for embedded editor */
- /* dropzone */
- /* codeview mode */
- /* fullscreen mode */
- /* statusbar */
-}
-.note-editor .note-dropzone {
- position: absolute;
- display: none;
- z-index: 100;
- color: lightskyblue;
- background-color: white;
- opacity: 0.95;
- pointer-event: none;
-}
-.note-editor .note-dropzone .note-dropzone-message {
- display: table-cell;
- vertical-align: middle;
- text-align: center;
- font-size: 28px;
- font-weight: bold;
-}
-.note-editor .note-dropzone.hover {
- color: #098ddf;
-}
-.note-editor.dragover .note-dropzone {
- display: table;
-}
-.note-editor.codeview .note-editing-area .note-editable {
- display: none;
-}
-.note-editor.codeview .note-editing-area .note-codable {
- display: block;
-}
-.note-editor.fullscreen {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- z-index: 1050;
- /* bs3 modal-backdrop: 1030, bs2: 1040 */
-}
-.note-editor.fullscreen .note-editable {
- background-color: white;
-}
-.note-editor.fullscreen .note-resizebar {
- display: none;
-}
-.note-editor .note-editing-area {
- position: relative;
- overflow: hidden;
- /* editable */
- /* codeable */
-}
-.note-editor .note-editing-area .note-editable {
- background-color: #fff;
- color: #000;
- padding: 10px;
- overflow: auto;
- outline: none;
-}
-.note-editor .note-editing-area .note-editable[contenteditable=true]:empty:not(:focus):before {
- content: attr(data-placeholder);
-}
-.note-editor .note-editing-area .note-editable[contenteditable="false"] {
- background-color: #e5e5e5;
-}
-.note-editor .note-editing-area .note-codable {
- display: none;
- width: 100%;
- padding: 10px;
- border: none;
- box-shadow: none;
- font-family: Menlo, Monaco, monospace, sans-serif;
- font-size: 14px;
- color: #ccc;
- background-color: #222;
- resize: none;
- /* override BS2 default style */
- box-sizing: border-box;
- border-radius: 0;
- margin-bottom: 0;
-}
-.note-editor .note-statusbar {
- background-color: #f5f5f5;
-}
-.note-editor .note-statusbar .note-resizebar {
- padding-top: 1px;
- height: 8px;
- width: 100%;
- cursor: ns-resize;
-}
-.note-editor .note-statusbar .note-resizebar .note-icon-bar {
- width: 20px;
- margin: 1px auto;
- border-top: 1px solid #a9a9a9;
-}
-/* Air Mode Layout
-------------------------------------------*/
-.note-air-editor {
- outline: none;
-}
-/* Popover
-------------------------------------------*/
-.note-popover .popover {
- max-width: none;
-}
-.note-popover .popover .popover-body a {
- display: inline-block;
- max-width: 200px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- /* for FF */
- vertical-align: middle;
- /* for FF */
-}
-.note-popover .popover .arrow {
- left: 20px;
-}
-/* Popover and Toolbar (Button container)
-------------------------------------------*/
-.note-popover .popover .popover-body,
-.panel-heading.note-toolbar {
- margin: 0;
- padding: 0 0 5px 5px;
- /* dropdown-menu for toolbar and popover */
- /* color palette for toolbar and popover */
-}
-.note-popover .popover .popover-body > .btn-group,
-.panel-heading.note-toolbar > .btn-group {
- margin-top: 5px;
- margin-left: 0;
- margin-right: 5px;
-}
-.note-popover .popover .popover-body .btn-group .note-table,
-.panel-heading.note-toolbar .btn-group .note-table {
- min-width: 0;
- padding: 5px;
-}
-.note-popover .popover .popover-body .btn-group .note-table .note-dimension-picker,
-.panel-heading.note-toolbar .btn-group .note-table .note-dimension-picker {
- font-size: 18px;
-}
-.note-popover .popover .popover-body .btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher,
-.panel-heading.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher {
- position: absolute !important;
- z-index: 3;
- width: 10em;
- height: 10em;
- cursor: pointer;
-}
-.note-popover .popover .popover-body .btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted,
-.panel-heading.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted {
- position: relative !important;
- z-index: 1;
- width: 5em;
- height: 5em;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIj4+Pjp6ekKlAqjAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKhmnaJzPAAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat;
-}
-.note-popover .popover .popover-body .btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted,
-.panel-heading.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted {
- position: absolute !important;
- z-index: 2;
- width: 1em;
- height: 1em;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIjd6vvD2f9LKLW+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKwNDEVT0AAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat;
-}
-.note-popover .popover .popover-body .note-style h1,
-.panel-heading.note-toolbar .note-style h1,
-.note-popover .popover .popover-body .note-style h2,
-.panel-heading.note-toolbar .note-style h2,
-.note-popover .popover .popover-body .note-style h3,
-.panel-heading.note-toolbar .note-style h3,
-.note-popover .popover .popover-body .note-style h4,
-.panel-heading.note-toolbar .note-style h4,
-.note-popover .popover .popover-body .note-style h5,
-.panel-heading.note-toolbar .note-style h5,
-.note-popover .popover .popover-body .note-style h6,
-.panel-heading.note-toolbar .note-style h6,
-.note-popover .popover .popover-body .note-style blockquote,
-.panel-heading.note-toolbar .note-style blockquote {
- margin: 0;
-}
-.note-popover .popover .popover-body .note-color .dropdown-toggle,
-.panel-heading.note-toolbar .note-color .dropdown-toggle {
- width: 20px;
- padding-left: 5px;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu,
-.panel-heading.note-toolbar .note-color .dropdown-menu {
- min-width: 340px;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group {
- margin: 0;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group:first-child,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group:first-child {
- margin: 0 5px;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group .note-palette-title,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group .note-palette-title {
- font-size: 12px;
- margin: 2px 7px;
- text-align: center;
- border-bottom: 1px solid #eee;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group .note-color-reset,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group .note-color-reset {
- font-size: 11px;
- margin: 3px;
- padding: 0 3px;
- cursor: pointer;
- border-radius: 5px;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group .note-color-row,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group .note-color-row {
- height: 20px;
-}
-.note-popover .popover .popover-body .note-color .dropdown-menu .btn-group .note-color-reset:hover,
-.panel-heading.note-toolbar .note-color .dropdown-menu .btn-group .note-color-reset:hover {
- background: #eee;
-}
-.note-popover .popover .popover-body .note-para .dropdown-menu,
-.panel-heading.note-toolbar .note-para .dropdown-menu {
- min-width: 216px;
- padding: 5px;
-}
-.note-popover .popover .popover-body .note-para .dropdown-menu > div:first-child,
-.panel-heading.note-toolbar .note-para .dropdown-menu > div:first-child {
- margin-right: 5px;
-}
-.note-popover .popover .popover-body .dropdown-menu,
-.panel-heading.note-toolbar .dropdown-menu {
- min-width: 90px;
- /* dropdown-menu right position */
- /* http://forrst.com/posts/Bootstrap_right_positioned_dropdown-2KB */
- /* dropdown-menu for selectbox */
-}
-.note-popover .popover .popover-body .dropdown-menu.right,
-.panel-heading.note-toolbar .dropdown-menu.right {
- right: 0;
- left: auto;
-}
-.note-popover .popover .popover-body .dropdown-menu.right::before,
-.panel-heading.note-toolbar .dropdown-menu.right::before {
- right: 9px;
- left: auto !important;
-}
-.note-popover .popover .popover-body .dropdown-menu.right::after,
-.panel-heading.note-toolbar .dropdown-menu.right::after {
- right: 10px;
- left: auto !important;
-}
-.note-popover .popover .popover-body .dropdown-menu.note-check li a i,
-.panel-heading.note-toolbar .dropdown-menu.note-check li a i {
- color: deepskyblue;
- visibility: hidden;
-}
-.note-popover .popover .popover-body .dropdown-menu.note-check li a.checked i,
-.panel-heading.note-toolbar .dropdown-menu.note-check li a.checked i {
- visibility: visible;
-}
-.note-popover .popover .popover-body .note-fontsize-10,
-.panel-heading.note-toolbar .note-fontsize-10 {
- font-size: 10px;
-}
-.note-popover .popover .popover-body .note-color-palette,
-.panel-heading.note-toolbar .note-color-palette {
- line-height: 1;
-}
-.note-popover .popover .popover-body .note-color-palette div .note-color-btn,
-.panel-heading.note-toolbar .note-color-palette div .note-color-btn {
- width: 20px;
- height: 20px;
- padding: 0;
- margin: 0;
- border: 1px solid #fff;
-}
-.note-popover .popover .popover-body .note-color-palette div .note-color-btn:hover,
-.panel-heading.note-toolbar .note-color-palette div .note-color-btn:hover {
- border: 1px solid #000;
-}
-/* Dialog
-------------------------------------------*/
-.note-dialog > div {
- display: none;
- /* BS2's hide pacth. */
-}
-.note-dialog .form-group {
- /* overwrite BS's form-horizontal minus margins */
- margin-left: 0;
- margin-right: 0;
-}
-.note-dialog .note-modal-form {
- margin: 0;
- /* overwrite BS2's form margin bottom */
-}
-.note-dialog .note-image-dialog .note-dropzone {
- min-height: 100px;
- font-size: 30px;
- line-height: 4;
- /* vertical-align */
- color: lightgray;
- text-align: center;
- border: 4px dashed lightgray;
- margin-bottom: 10px;
-}
-.note-dialog .note-help-dialog {
- font-size: 12px;
- color: #ccc;
- background-color: #222 !important;
- opacity: 0.9;
- /* BS2's background pacth. */
- background: transparent;
- border: none;
-}
-.note-dialog .note-help-dialog .modal-content {
- background: transparent;
- border: 1px solid white;
- box-shadow: none;
- border-radius: 5px;
-}
-.note-dialog .note-help-dialog a {
- font-size: 12px;
- color: white;
-}
-.note-dialog .note-help-dialog .title {
- color: white;
- font-size: 14px;
- font-weight: bold;
- padding-bottom: 5px;
- margin-bottom: 10px;
- border-bottom: white 1px solid;
-}
-.note-dialog .note-help-dialog .modal-close {
- font-size: 14px;
- color: #dd0;
- cursor: pointer;
-}
-.note-dialog .note-help-dialog .text-center {
- margin: 10px 0 0;
-}
-.note-dialog .note-help-dialog .note-shortcut {
- padding-top: 8px;
- padding-bottom: 8px;
-}
-.note-dialog .note-help-dialog .note-shortcut-row {
- margin-right: -5px;
- margin-left: -5px;
-}
-.note-dialog .note-help-dialog .note-shortcut-col {
- padding-right: 5px;
- padding-left: 5px;
-}
-.note-dialog .note-help-dialog .note-shortcut-title {
- font-size: 13px;
- font-weight: bold;
- color: #dd0;
-}
-.note-dialog .note-help-dialog .note-shortcut-key {
- font-family: "Courier New";
- color: #dd0;
- text-align: right;
-}
-/* Handle
-------------------------------------------*/
-.note-handle {
- /* control selection */
-}
-.note-handle .note-control-selection {
- position: absolute;
- display: none;
- border: 1px solid black;
-}
-.note-handle .note-control-selection > div {
- position: absolute;
-}
-.note-handle .note-control-selection .note-control-selection-bg {
- width: 100%;
- height: 100%;
- background-color: black;
- opacity: 0.3;
-}
-.note-handle .note-control-selection .note-control-handle {
- width: 7px;
- height: 7px;
- border: 1px solid black;
-}
-.note-handle .note-control-selection .note-control-holder {
- width: 7px;
- height: 7px;
- border: 1px solid black;
-}
-.note-handle .note-control-selection .note-control-sizing {
- width: 7px;
- height: 7px;
- border: 1px solid black;
- background-color: white;
-}
-.note-handle .note-control-selection .note-control-nw {
- top: -5px;
- left: -5px;
- border-right: none;
- border-bottom: none;
-}
-.note-handle .note-control-selection .note-control-ne {
- top: -5px;
- right: -5px;
- border-bottom: none;
- border-left: none;
-}
-.note-handle .note-control-selection .note-control-sw {
- bottom: -5px;
- left: -5px;
- border-top: none;
- border-right: none;
-}
-.note-handle .note-control-selection .note-control-se {
- right: -5px;
- bottom: -5px;
- cursor: se-resize;
-}
-.note-handle .note-control-selection .note-control-se.note-control-holder {
- cursor: default;
- border-top: none;
- border-left: none;
-}
-.note-handle .note-control-selection .note-control-selection-info {
- right: 0;
- bottom: 0;
- padding: 5px;
- margin: 5px;
- color: white;
- background-color: black;
- font-size: 12px;
- border-radius: 5px;
- opacity: 0.7;
-}
diff --git a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js
deleted file mode 100644
index a924e7def8a..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js
+++ /dev/null
@@ -1,569 +0,0 @@
-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) {
-
- /**
- * @class EventHandler
- *
- * EventHandler
- * - TODO: new instance per a editor
- */
- var EventHandler = function () {
- var self = this;
-
- /**
- * 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)
- };
-
- /**
- * invoke module's method
- *
- * @param {String} moduleAndMethod - ex) 'editor.redo'
- * @param {...*} arguments - arguments of method
- * @return {*}
- */
- 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];
-
- return method && method.apply(module, args);
- };
-
- /**
- * returns module
- *
- * @param {String} moduleName - name of module
- * @return {Module} - defaults is editor
- */
- this.getModule = function (moduleName) {
- return this.modules[moduleName] || this.modules.editor;
- };
-
- /**
- * @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);
- };
- };
-
- /**
- * insert Images from file array.
- *
- * @private
- * @param {Object} layoutInfo
- * @param {File[]} files
- */
- this.insertImages = function (layoutInfo, files) {
- var $editor = layoutInfo.editor(),
- $editable = layoutInfo.editable(),
- $holder = layoutInfo.holder();
-
- var callbacks = $editable.data('callbacks');
- var options = $editor.data('options');
-
- // If onImageUpload options setted
- if (callbacks.onImageUpload) {
- bindCustomEvent($holder, callbacks, 'image.upload')(files);
- // 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);
- } else {
- async.readFileAsDataURL(file).then(function (sDataURL) {
- modules.editor.insertImage($editable, sDataURL, filename);
- }).fail(function () {
- bindCustomEvent($holder, callbacks, 'image.upload.error')(options.langInfo.image.maximumFileSizeError);
- });
- }
- });
- }
- };
-
- var commands = {
- /**
- * @param {Object} layoutInfo
- */
- showLinkDialog: function (layoutInfo) {
- modules.linkDialog.show(layoutInfo);
- },
-
- /**
- * @param {Object} layoutInfo
- */
- showImageDialog: function (layoutInfo) {
- modules.imageDialog.show(layoutInfo);
- },
-
- /**
- * @param {Object} layoutInfo
- */
- showHelpDialog: function (layoutInfo) {
- modules.helpDialog.show(layoutInfo);
- },
-
- /**
- * @param {Object} layoutInfo
- */
- fullscreen: function (layoutInfo) {
- modules.fullscreen.toggle(layoutInfo);
- },
-
- /**
- * @param {Object} layoutInfo
- */
- codeview: function (layoutInfo) {
- modules.codeview.toggle(layoutInfo);
- }
- };
-
- var hMousedown = function (event) {
- //preventDefault Selection for FF, IE8+
- if (dom.isImg(event.target)) {
- event.preventDefault();
- }
- };
-
- var hKeyupAndMouseup = function (event) {
- var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target);
- modules.editor.removeBogus(layoutInfo.editable());
- hToolbarAndPopoverUpdate(event);
- };
-
- /**
- * update sytle info
- * @param {Object} styleInfo
- * @param {Object} layoutInfo
- */
- this.updateStyleInfo = function (styleInfo, layoutInfo) {
- if (!styleInfo) {
- return;
- }
- var isAirMode = (layoutInfo.editor().data('options') || {}).airMode;
- if (!isAirMode) {
- modules.toolbar.update(layoutInfo.toolbar(), styleInfo);
- }
-
- modules.popover.update(layoutInfo.popover(), styleInfo, isAirMode);
- modules.handle.update(layoutInfo.handle(), styleInfo, isAirMode);
- };
-
- 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.setStyleInfoFromEditable) {
- var styleInfo = modules.editor.styleFromNode($editable);
- } else {
- if (!event.isDefaultPrevented()) {
- modules.editor.saveRange($editable);
- }
- var styleInfo = modules.editor.currentStyle(target);
- }
- /* ODOO: end_modification) */
- self.updateStyleInfo(styleInfo, layoutInfo);
- }, 0);
- };
-
- 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());
- };
-
- var hToolbarAndPopoverMousedown = function (event) {
- // prevent default event when insertTable (FF, Webkit)
- var $btn = $(event.target).closest('[data-event]');
- if ($btn.length) {
- event.preventDefault();
- }
- };
-
- var hToolbarAndPopoverClick = function (event) {
- var $btn = $(event.target).closest('[data-event]');
-
- if (!$btn.length) {
- return;
- }
-
- var eventName = $btn.attr('data-event'),
- value = $btn.attr('data-value'),
- hide = $btn.attr('data-hide');
-
- var layoutInfo = dom.makeLayoutInfo(event.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 ($.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();
- }
-
- // 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);
- }
-
- hToolbarAndPopoverUpdate(event);
- };
-
- var PX_PER_EM = 18;
- var hDimensionPickerMove = function (event, options) {
- var $picker = $(event.target.parentNode); // target is mousecatcher
- var $dimensionDisplay = $picker.next();
- var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
- var $highlighted = $picker.find('.note-dimension-picker-highlighted');
- var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
-
- var posOffset;
- // HTML5 with jQuery - e.offsetX is undefined in Firefox
- if (event.offsetX === undefined) {
- var posCatcher = $(event.target).offset();
- posOffset = {
- x: event.pageX - posCatcher.left,
- y: event.pageY - posCatcher.top
- };
- } else {
- posOffset = {
- x: event.offsetX,
- y: event.offsetY
- };
- }
-
- var dim = {
- c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
- r: Math.ceil(posOffset.y / PX_PER_EM) || 1
- };
-
- $highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
- $catcher.attr('data-value', dim.c + 'x' + dim.r);
-
- if (3 < dim.c && dim.c < options.insertTableMaxSize.col) {
- $unhighlighted.css({ width: dim.c + 1 + 'em'});
- }
-
- if (3 < dim.r && dim.r < options.insertTableMaxSize.row) {
- $unhighlighted.css({ height: dim.r + 1 + 'em'});
- }
-
- $dimensionDisplay.html(dim.c + ' x ' + dim.r);
- };
-
- /**
- * bind KeyMap on keydown
- *
- * @param {Object} layoutInfo
- * @param {Object} keyMap
- */
- this.bindKeyMap = function (layoutInfo, keyMap) {
- var $editor = layoutInfo.editor();
- var $editable = layoutInfo.editable();
-
- $editable.on('keydown', function (event) {
- var keys = [];
-
- // modifier
- if (event.metaKey) { keys.push('CMD'); }
- if (event.ctrlKey && !event.altKey) { keys.push('CTRL'); }
- if (event.shiftKey) { keys.push('SHIFT'); }
-
- // keycode
- var keyName = key.nameFromCode[event.keyCode];
- if (keyName) {
- keys.push(keyName);
- }
-
- var pluginEvent;
- var keyString = keys.join('+');
- var eventName = keyMap[keyString];
-
- // ODOO: (start_modification
- // odoo change: add visible event to overwrite the browser comportment
- var keycode = event.keyCode;
- if (!eventName &&
- !event.ctrlKey && !event.metaKey && ( // special code/command
- (keycode > 47 && keycode < 58) || // number keys
- keycode == 32 || keycode == 13 || // spacebar & return
- (keycode > 64 && keycode < 91) || // letter keys
- (keycode > 95 && keycode < 112) || // numpad keys
- (keycode > 185 && keycode < 193) || // ;=,-./` (in order)
- (keycode > 218 && keycode < 223))) { // [\]' (in order))
- eventName = 'visible';
- } else if (!keycode && event.key !== 'Dead') {
- self.invoke('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;
- }
- }
-
- pluginEvent = $.summernote.pluginEvents[eventName];
-
- if ($.isFunction(pluginEvent)) {
- pluginEvent(event, modules.editor, layoutInfo);
- } else if (modules.editor[eventName]) {
- modules.editor[eventName]($editable, $editor.data('options'));
- event.preventDefault();
- } else if (commands[eventName]) {
- commands[eventName].call(this, layoutInfo);
- event.preventDefault();
- }
- } else if (key.isEdit(event.keyCode)) {
- modules.editor.afterCommand($editable);
- }
- });
- };
-
- /**
- * attach eventhandler
- *
- * @param {Object} layoutInfo - layout Informations
- * @param {Object} options - user options include custom event handlers
- */
- 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);
-
- // 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);
-
- // handlers for frame mode (toolbar, statusbar)
- if (!options.airMode) {
- // handler for toolbar
- layoutInfo.toolbar().on('click', hToolbarAndPopoverClick);
- layoutInfo.toolbar().on('mousedown', hToolbarAndPopoverMousedown);
-
- // handler for statusbar
- modules.statusbar.attach(layoutInfo, options);
- }
-
- // handler for table dimension
- var $catcherContainer = options.airMode ? layoutInfo.popover() :
- layoutInfo.toolbar();
- var $catcher = $catcherContainer.find('.note-dimension-picker-mousecatcher');
- $catcher.css({
- width: options.insertTableMaxSize.col + 'em',
- height: options.insertTableMaxSize.row + 'em'
- }).on('mousemove', function (event) {
- hDimensionPickerMove(event, options);
- });
-
- // save options on editor
- 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
- setTimeout(function () {
- document.execCommand('styleWithCSS', 0, options.styleWithSpan);
- }, 0);
- }
-
- // History
- var history = new History(layoutInfo.editable());
- layoutInfo.editable().data('NoteHistory', history);
-
- // 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,
- onChange: options.onChange,
- onImageUpload: options.onImageUpload,
- onImageUploadError: options.onImageUploadError,
- onMediaDelete: options.onMediaDelete,
- onToolbarClick: options.onToolbarClick,
- onUpload: options.onUpload,
- });
-
- 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.popover().off();
- layoutInfo.handle().off();
- layoutInfo.dialog().off();
-
- if (!options.airMode) {
- layoutInfo.dropzone().off();
- layoutInfo.toolbar().off();
- layoutInfo.statusbar().off();
- }
- };
- };
-
- return EventHandler;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/Renderer.js b/addons/web_editor/static/lib/summernote/src/js/Renderer.js
deleted file mode 100644
index a57dab67573..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/Renderer.js
+++ /dev/null
@@ -1,1026 +0,0 @@
-define([
- 'summernote/core/agent',
- 'summernote/core/dom',
- 'summernote/core/func',
- 'summernote/core/list'
-], function (agent, dom, func, list) {
- /**
- * @class Renderer
- *
- * renderer
- *
- * rendering toolbar and editable
- */
- var Renderer = function () {
-
- /**
- * bootstrap button template
- * @private
- * @param {String} label button name
- * @param {Object} [options] button options
- * @param {String} [options.event] data-event
- * @param {String} [options.className] button's class name
- * @param {String} [options.value] data-value
- * @param {String} [options.title] button's title for popup
- * @param {String} [options.dropdown] dropdown html
- * @param {String} [options.hide] data-hide
- */
- var tplButton = function (label, options) {
- var event = options.event;
- var value = options.value;
- var title = options.title;
- var className = options.className;
- var dropdown = options.dropdown;
- var hide = options.hide;
-
- return (dropdown ? '<div class="btn-group' +
- (className ? ' ' + className : '') + '">' : '') +
- '<button type="button"' +
- ' class="btn btn-secondary btn-sm' +
- ((!dropdown && className) ? ' ' + className : '') +
- (dropdown ? ' dropdown-toggle' : '') +
- '"' +
- (dropdown ? ' data-toggle="dropdown"' : '') +
- (title ? ' title="' + title + '"' : '') +
- (event ? ' data-event="' + event + '"' : '') +
- (value ? ' data-value=\'' + value + '\'' : '') +
- (hide ? ' data-hide=\'' + hide + '\'' : '') +
- ' tabindex="-1">' +
- label +
- (dropdown ? ' <span class="caret"></span>' : '') +
- '</button>' +
- (dropdown || '') +
- (dropdown ? '</div>' : '');
- };
-
- /**
- * bootstrap icon button template
- * @private
- * @param {String} iconClassName
- * @param {Object} [options]
- * @param {String} [options.event]
- * @param {String} [options.value]
- * @param {String} [options.title]
- * @param {String} [options.dropdown]
- */
- var tplIconButton = function (iconClassName, options) {
- var label = '<i class="' + iconClassName + '"></i>';
- return tplButton(label, options);
- };
-
- /**
- * bootstrap popover template
- * @private
- * @param {String} className
- * @param {String} content
- */
- var tplPopover = function (className, content) {
- var $popover = $('<div class="' + className + ' popover bottom in" style="display: none;">' +
- '<div class="arrow"></div>' +
- '<div class="popover-body">' +
- '</div>' +
- '</div>');
-
- $popover.find('.popover-body').append(content);
- return $popover;
- };
-
- /**
- * bootstrap dialog template
- *
- * @param {String} className
- * @param {String} [title='']
- * @param {String} body
- * @param {String} [footer='']
- */
- var tplDialog = function (className, title, body, footer) {
- return '<div class="' + className + ' modal" role="dialog" aria-hidden="false">' +
- '<div class="modal-dialog">' +
- '<div class="modal-content">' +
- (title ?
- '<header class="modal-header">' +
- '<h4 class="modal-title">' + title + '</h4>' +
- '<button type="button" class="close" aria-hidden="true" tabindex="-1">&times;</button>' +
- '</header>' : ''
- ) +
- '<main class="modal-body">' + body + '</main>' +
- (footer ?
- '<header class="modal-footer">' + footer + '</header>' : ''
- ) +
- '</div>' +
- '</div>' +
- '</div>';
- };
-
- /**
- * bootstrap dropdown template
- *
- * @param {String|String[]} contents
- * @param {String} [className='']
- * @param {String} [nodeName='']
- */
- var tplDropdown = function (contents, className, nodeName) {
- var classes = 'dropdown-menu' + (className ? ' ' + className : '');
- nodeName = nodeName || 'ul';
- if (contents instanceof Array) {
- contents = contents.join('');
- }
-
- return '<' + nodeName + ' class="' + classes + '">' + contents + '</' + nodeName + '>';
- };
-
- var tplButtonInfo = {
- picture: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.image.image, {
- event: 'showImageDialog',
- title: lang.image.image,
- hide: true
- });
- },
- link: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.link.link, {
- event: 'showLinkDialog',
- title: lang.link.link,
- hide: true
- });
- },
- table: function (lang, options) {
- var dropdown = [
- '<div class="note-dimension-picker">',
- '<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>',
- '<div class="note-dimension-picker-highlighted"></div>',
- '<div class="note-dimension-picker-unhighlighted"></div>',
- '</div>',
- '<div class="note-dimension-display"> 1 x 1 </div>'
- ];
-
- return tplIconButton(options.iconPrefix + options.icons.table.table, {
- title: lang.table.table,
- dropdown: tplDropdown(dropdown, 'note-table')
- });
- },
- style: function (lang, options) {
- var items = options.styleTags.reduce(function (memo, v) {
- var label = lang.style[v === 'p' ? 'normal' : v];
- return memo + '<li><a class="dropdown-item" data-event="formatBlock" href="#" data-value="' + v + '">' +
- (
- (v === 'p' || v === 'pre') ? label :
- '<' + v + '>' + label + '</' + v + '>'
- ) +
- '</a></li>';
- }, '');
-
- return tplIconButton(options.iconPrefix + options.icons.style.style, {
- title: lang.style.style,
- dropdown: tplDropdown(items)
- });
- },
- fontname: function (lang, options) {
- var realFontList = [];
- var items = options.fontNames.reduce(function (memo, v) {
- if (!agent.isFontInstalled(v) && !list.contains(options.fontNamesIgnoreCheck, v)) {
- return memo;
- }
- realFontList.push(v);
- return memo + '<li><a data-event="fontName" href="#" data-value="' + v + '" style="font-family:\'' + v + '\'">' +
- '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
- '</a></li>';
- }, '');
-
- var hasDefaultFont = agent.isFontInstalled(options.defaultFontName);
- var defaultFontName = (hasDefaultFont) ? options.defaultFontName : realFontList[0];
-
- var label = '<span class="note-current-fontname">' +
- defaultFontName +
- '</span>';
- return tplButton(label, {
- title: lang.font.name,
- className: 'note-fontname',
- dropdown: tplDropdown(items, 'note-check')
- });
- },
- fontsize: function (lang, options) {
- var items = options.fontSizes.reduce(function (memo, v) {
- return memo + '<li><a data-event="fontSize" href="#" data-value="' + v + '">' +
- '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
- '</a></li>';
- }, '');
-
- var label = '<span class="note-current-fontsize">11</span>';
- return tplButton(label, {
- title: lang.font.size,
- className: 'note-fontsize',
- dropdown: tplDropdown(items, 'note-check')
- });
- },
- color: function (lang, options) {
- var colorButtonLabel = '<i class="' +
- options.iconPrefix + options.icons.color.recent +
- '" id="colors_preview" style="color:white;background-color:#B35E9B"></i>';
- var colorButton = tplButton(colorButtonLabel, {
- className: 'note-recent-color',
- title: lang.color.recent,
- event: 'color',
- value: '{"backColor":"#B35E9B"}'
- });
-
- var items = [
- '<li class="flex"><div class="btn-group flex-column">',
- '<div class="note-palette-title">' + lang.color.background + '</div>',
- '<div class="note-color-reset" data-event="backColor"',
- ' data-value="inherit" title="' + lang.color.transparent + '">' + lang.color.setTransparent + '</div>',
- '<div class="note-color-palette" data-target-event="backColor"></div>',
- '</div><div class="btn-group flex-column">',
- '<div class="note-palette-title">' + lang.color.foreground + '</div>',
- '<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">',
- lang.color.resetToDefault,
- '</div>',
- '<div class="note-color-palette" data-target-event="foreColor"></div>',
- '</div></li>'
- ];
-
- var moreButton = tplButton('', {
- title: lang.color.more,
- dropdown: tplDropdown(items)
- });
-
- return colorButton + moreButton;
- },
- bold: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.bold, {
- event: 'bold',
- title: lang.font.bold
- });
- },
- italic: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.italic, {
- event: 'italic',
- title: lang.font.italic
- });
- },
- underline: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.underline, {
- event: 'underline',
- title: lang.font.underline
- });
- },
- strikethrough: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.strikethrough, {
- event: 'strikethrough',
- title: lang.font.strikethrough
- });
- },
- superscript: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.superscript, {
- event: 'superscript',
- title: lang.font.superscript
- });
- },
- subscript: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.subscript, {
- event: 'subscript',
- title: lang.font.subscript
- });
- },
- clear: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.font.clear, {
- event: 'removeFormat',
- title: lang.font.clear
- });
- },
- ul: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.lists.unordered, {
- event: 'insertUnorderedList',
- title: lang.lists.unordered
- });
- },
- ol: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.lists.ordered, {
- event: 'insertOrderedList',
- title: lang.lists.ordered
- });
- },
- paragraph: function (lang, options) {
- var leftButton = tplIconButton(options.iconPrefix + options.icons.paragraph.left, {
- title: lang.paragraph.left,
- event: 'justifyLeft'
- });
- var centerButton = tplIconButton(options.iconPrefix + options.icons.paragraph.center, {
- title: lang.paragraph.center,
- event: 'justifyCenter'
- });
- var rightButton = tplIconButton(options.iconPrefix + options.icons.paragraph.right, {
- title: lang.paragraph.right,
- event: 'justifyRight'
- });
- var justifyButton = tplIconButton(options.iconPrefix + options.icons.paragraph.justify, {
- title: lang.paragraph.justify,
- event: 'justifyFull'
- });
-
- var outdentButton = tplIconButton(options.iconPrefix + options.icons.paragraph.outdent, {
- title: lang.paragraph.outdent,
- event: 'outdent'
- });
- var indentButton = tplIconButton(options.iconPrefix + options.icons.paragraph.indent, {
- title: lang.paragraph.indent,
- event: 'indent'
- });
-
- var dropdown = [
- '<div class="note-align btn-group">',
- leftButton + centerButton + rightButton + justifyButton,
- '</div><div class="note-list btn-group">',
- indentButton + outdentButton,
- '</div>'
- ];
-
- return tplIconButton(options.iconPrefix + options.icons.paragraph.paragraph, {
- title: lang.paragraph.paragraph,
- dropdown: tplDropdown(dropdown, '', 'div')
- });
- },
- height: function (lang, options) {
- var items = options.lineHeights.reduce(function (memo, v) {
- return memo + '<li><a data-event="lineHeight" href="#" data-value="' + parseFloat(v) + '">' +
- '<i class="' + options.iconPrefix + options.icons.misc.check + '"></i> ' + v +
- '</a></li>';
- }, '');
-
- return tplIconButton(options.iconPrefix + options.icons.font.height, {
- title: lang.font.height,
- dropdown: tplDropdown(items, 'note-check')
- });
-
- },
- help: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.options.help, {
- event: 'showHelpDialog',
- title: lang.options.help,
- hide: true
- });
- },
- fullscreen: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.options.fullscreen, {
- event: 'fullscreen',
- title: lang.options.fullscreen
- });
- },
- codeview: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.options.codeview, {
- event: 'codeview',
- title: lang.options.codeview
- });
- },
- undo: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.history.undo, {
- event: 'undo',
- title: lang.history.undo
- });
- },
- redo: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.history.redo, {
- event: 'redo',
- title: lang.history.redo
- });
- },
- hr: function (lang, options) {
- return tplIconButton(options.iconPrefix + options.icons.hr.insert, {
- event: 'insertHorizontalRule',
- title: lang.hr.insert
- });
- }
- };
-
- var tplPopovers = function (lang, options) {
- var tplLinkPopover = function () {
- var linkButton = tplIconButton(options.iconPrefix + options.icons.link.edit, {
- title: lang.link.edit,
- event: 'showLinkDialog',
- hide: true
- });
- var unlinkButton = tplIconButton(options.iconPrefix + options.icons.link.unlink, {
- title: lang.link.unlink,
- event: 'unlink'
- });
- var content = '<a href="http://www.google.com" target="_blank">www.google.com</a>&nbsp;&nbsp;' +
- '<div class="note-insert btn-group">' +
- linkButton + unlinkButton +
- '</div>';
- return tplPopover('note-link-popover', content);
- };
-
- var tplImagePopover = function () {
- var autoButton = tplButton('<span class="note-fontsize-10">Auto</span>', {
- event: 'resize',
- value: 'auto'
- });
- var fullButton = tplButton('<span class="note-fontsize-10">100%</span>', {
- title: lang.image.resizeFull,
- event: 'resize',
- value: '1'
- });
- var halfButton = tplButton('<span class="note-fontsize-10">50%</span>', {
- title: lang.image.resizeHalf,
- event: 'resize',
- value: '0.5'
- });
- var quarterButton = tplButton('<span class="note-fontsize-10">25%</span>', {
- title: lang.image.resizeQuarter,
- event: 'resize',
- value: '0.25'
- });
-
- var leftButton = tplIconButton(options.iconPrefix + options.icons.image.floatLeft, {
- title: lang.image.floatLeft,
- event: 'floatMe',
- value: 'left'
- });
- var rightButton = tplIconButton(options.iconPrefix + options.icons.image.floatRight, {
- title: lang.image.floatRight,
- event: 'floatMe',
- value: 'right'
- });
- var justifyButton = tplIconButton(options.iconPrefix + options.icons.image.floatNone, {
- title: lang.image.floatNone,
- event: 'floatMe',
- value: 'none'
- });
-
- var roundedButton = tplIconButton(options.iconPrefix + options.icons.image.shapeRounded, {
- title: lang.image.shapeRounded,
- event: 'imageShape',
- value: 'rounded'
- });
- var circleButton = tplIconButton(options.iconPrefix + options.icons.image.shapeCircle, {
- title: lang.image.shapeCircle,
- event: 'imageShape',
- value: 'rounded-circle'
- });
- var thumbnailButton = tplIconButton(options.iconPrefix + options.icons.image.shapeThumbnail, {
- title: lang.image.shapeThumbnail,
- event: 'imageShape',
- value: 'img-thumbnail'
- });
- var noneButton = tplIconButton(options.iconPrefix + options.icons.image.shapeNone, {
- title: lang.image.shapeNone,
- event: 'imageShape',
- value: ''
- });
-
- var removeButton = tplIconButton(options.iconPrefix + options.icons.image.remove, {
- title: lang.image.remove,
- event: 'removeMedia',
- value: 'none'
- });
-
- var content = (options.disableResizeImage ? '' : '<div class="btn-group">' + autoButton + fullButton + halfButton + quarterButton + '</div>') +
- '<div class="btn-group">' + leftButton + rightButton + justifyButton + '</div>' +
- '<div class="btn-group">' + roundedButton + circleButton + thumbnailButton + noneButton + '</div>' +
- '<div class="btn-group">' + removeButton + '</div>';
- return tplPopover('note-image-popover', content);
- };
-
- var tplAirPopover = function () {
- var $content = $('<div />');
- for (var idx = 0, len = options.airPopover.length; idx < len; idx ++) {
- var group = options.airPopover[idx];
-
- var $group = $('<div class="note-' + group[0] + ' btn-group">');
- for (var i = 0, lenGroup = group[1].length; i < lenGroup; i++) {
- var $button = $(tplButtonInfo[group[1][i]](lang, options));
-
- $button.attr('data-name', group[1][i]);
-
- $group.append($button);
- }
- $content.append($group);
- }
-
- return tplPopover('note-air-popover', $content.children());
- };
-
- var $notePopover = $('<div class="note-popover" />');
-
- $notePopover.append(tplLinkPopover());
- $notePopover.append(tplImagePopover());
-
- if (options.airMode) {
- $notePopover.append(tplAirPopover());
- }
-
- return $notePopover;
- };
-
- this.tplButtonInfo = tplButtonInfo; // ODOO: allow access for override
- this.tplPopovers = tplPopovers; // ODOO: allow access for override
-
- var tplHandles = function (options) {
- return '<div class="note-handle">' +
- '<div class="note-control-selection">' +
- '<div class="note-control-selection-bg"></div>' +
- '<div class="note-control-holder note-control-nw"></div>' +
- '<div class="note-control-holder note-control-ne"></div>' +
- '<div class="note-control-holder note-control-sw"></div>' +
- '<div class="' +
- (options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing') +
- ' note-control-se"></div>' +
- (options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>') +
- '</div>' +
- '</div>';
- };
-
- /**
- * shortcut table template
- * @param {String} title
- * @param {String} body
- */
- var tplShortcut = function (title, keys) {
- var keyClass = 'note-shortcut-col col-6 note-shortcut-';
- var body = [];
-
- for (var i in keys) {
- if (keys.hasOwnProperty(i)) {
- body.push(
- '<div class="' + keyClass + 'key">' + keys[i].kbd + '</div>' +
- '<div class="' + keyClass + 'name">' + keys[i].text + '</div>'
- );
- }
- }
-
- return '<div class="note-shortcut-row row"><div class="' + keyClass + 'title offset-6">' + title + '</div></div>' +
- '<div class="note-shortcut-row row">' + body.join('</div><div class="note-shortcut-row row">') + '</div>';
- };
-
- var tplShortcutText = function (lang) {
- var keys = [
- { kbd: '⌘ + B', text: lang.font.bold },
- { kbd: '⌘ + I', text: lang.font.italic },
- { kbd: '⌘ + U', text: lang.font.underline },
- { kbd: '⌘ + \\', text: lang.font.clear }
- ];
-
- return tplShortcut(lang.shortcut.textFormatting, keys);
- };
-
- var tplShortcutAction = function (lang) {
- var keys = [
- { kbd: '⌘ + Z', text: lang.history.undo },
- { kbd: '⌘ + ⇧ + Z', text: lang.history.redo },
- { kbd: '⌘ + ]', text: lang.paragraph.indent },
- { kbd: '⌘ + [', text: lang.paragraph.outdent },
- { kbd: '⌘ + ENTER', text: lang.hr.insert }
- ];
-
- return tplShortcut(lang.shortcut.action, keys);
- };
-
- var tplShortcutPara = function (lang) {
- var keys = [
- { kbd: '⌘ + ⇧ + L', text: lang.paragraph.left },
- { kbd: '⌘ + ⇧ + E', text: lang.paragraph.center },
- { kbd: '⌘ + ⇧ + R', text: lang.paragraph.right },
- { kbd: '⌘ + ⇧ + J', text: lang.paragraph.justify },
- { kbd: '⌘ + ⇧ + NUM7', text: lang.lists.ordered },
- { kbd: '⌘ + ⇧ + NUM8', text: lang.lists.unordered }
- ];
-
- return tplShortcut(lang.shortcut.paragraphFormatting, keys);
- };
-
- var tplShortcutStyle = function (lang) {
- var keys = [
- { kbd: '⌘ + NUM0', text: lang.style.normal },
- { kbd: '⌘ + NUM1', text: lang.style.h1 },
- { kbd: '⌘ + NUM2', text: lang.style.h2 },
- { kbd: '⌘ + NUM3', text: lang.style.h3 },
- { kbd: '⌘ + NUM4', text: lang.style.h4 },
- { kbd: '⌘ + NUM5', text: lang.style.h5 },
- { kbd: '⌘ + NUM6', text: lang.style.h6 }
- ];
-
- return tplShortcut(lang.shortcut.documentStyle, keys);
- };
-
- var tplExtraShortcuts = function (lang, options) {
- var extraKeys = options.extraKeys;
- var keys = [];
-
- for (var key in extraKeys) {
- if (extraKeys.hasOwnProperty(key)) {
- keys.push({ kbd: key, text: extraKeys[key] });
- }
- }
-
- return tplShortcut(lang.shortcut.extraKeys, keys);
- };
-
- var tplShortcutTable = function (lang, options) {
- var colClass = 'class="note-shortcut note-shortcut-col col-md-6 col-12"';
- var template = [
- '<div ' + colClass + '>' + tplShortcutAction(lang, options) + '</div>' +
- '<div ' + colClass + '>' + tplShortcutText(lang, options) + '</div>',
- '<div ' + colClass + '>' + tplShortcutStyle(lang, options) + '</div>' +
- '<div ' + colClass + '>' + tplShortcutPara(lang, options) + '</div>'
- ];
-
- if (options.extraKeys) {
- template.push('<div ' + colClass + '>' + tplExtraShortcuts(lang, options) + '</div>');
- }
-
- return '<div class="note-shortcut-row row">' +
- template.join('</div><div class="note-shortcut-row row">') +
- '</div>';
- };
-
- var replaceMacKeys = function (sHtml) {
- return sHtml.replace(/⌘/g, 'Ctrl').replace(/⇧/g, 'Shift');
- };
-
- var tplDialogInfo = {
- image: function (lang, options) {
- var imageLimitation = '';
- if (options.maximumImageFileSize) {
- var unit = Math.floor(Math.log(options.maximumImageFileSize) / Math.log(1024));
- var readableSize = (options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
- ' ' + ' KMGTP'[unit] + 'B';
- imageLimitation = '<small>' + lang.image.maximumFileSize + ' : ' + readableSize + '</small>';
- }
-
- var body = '<div class="form-group row note-group-select-from-files">' +
- '<label>' + lang.image.selectFromFiles + '</label>' +
- '<input class="note-image-input form-control" type="file" name="files" accept="image/*" multiple="multiple" />' +
- imageLimitation +
- '</div>' +
- '<div class="form-group row">' +
- '<label>' + lang.image.url + '</label>' +
- '<input class="note-image-url form-control col-md-12" type="text" />' +
- '</div>';
- var footer = '<button href="#" class="btn btn-primary note-image-btn disabled" disabled>' + lang.image.insert + '</button>';
- return tplDialog('note-image-dialog', lang.image.insert, body, footer);
- },
-
- link: function (lang, options) {
- var body = '<div class="form-group row">' +
- '<label>' + lang.link.textToDisplay + '</label>' +
- '<input class="note-link-text form-control col-md-12" type="text" />' +
- '</div>' +
- '<div class="form-group row">' +
- '<label>' + lang.link.url + '</label>' +
- '<input class="note-link-url form-control col-md-12" type="text" value="http://" />' +
- '</div>' +
- (!options.disableLinkTarget ?
- '<div class="checkbox">' +
- '<label>' + '<input type="checkbox" checked> ' +
- lang.link.openInNewWindow +
- '</label>' +
- '</div>' : ''
- );
- var footer = '<button href="#" class="btn btn-primary note-link-btn disabled" disabled>' + lang.link.insert + '</button>';
- return tplDialog('note-link-dialog', lang.link.insert, body, footer);
- },
-
- help: function (lang, options) {
- var body = '<a class="modal-close float-right" aria-hidden="true" tabindex="-1">' + lang.shortcut.close + '</a>' +
- '<div class="title">' + lang.shortcut.shortcuts + '</div>' +
- (agent.isMac ? tplShortcutTable(lang, options) : replaceMacKeys(tplShortcutTable(lang, options))) +
- '<p class="text-center">' +
- '<a href="//summernote.org/" target="_blank">Summernote @VERSION</a> · ' +
- '<a href="//github.com/summernote/summernote" target="_blank">Project</a> · ' +
- '<a href="//github.com/summernote/summernote/issues" target="_blank">Issues</a>' +
- '</p>';
- return tplDialog('note-help-dialog', '', body, '');
- }
- };
-
- var tplDialogs = function (lang, options) {
- var dialogs = '';
-
- $.each(tplDialogInfo, function (idx, tplDialog) {
- dialogs += tplDialog(lang, options);
- });
-
- return '<div class="note-dialog">' + dialogs + '</div>';
- };
-
- var tplStatusbar = function () {
- return '<div class="note-resizebar">' +
- '<div class="note-icon-bar"></div>' +
- '<div class="note-icon-bar"></div>' +
- '<div class="note-icon-bar"></div>' +
- '</div>';
- };
-
- var representShortcut = function (str) {
- if (agent.isMac) {
- str = str.replace('CMD', '⌘').replace('SHIFT', '⇧');
- }
-
- return str.replace('BACKSLASH', '\\')
- .replace('SLASH', '/')
- .replace('LEFTBRACKET', '[')
- .replace('RIGHTBRACKET', ']');
- };
-
- /**
- * createTooltip
- *
- * @param {jQuery} $container
- * @param {Object} keyMap
- * @param {String} [sPlacement]
- */
- var createTooltip = function ($container, keyMap, sPlacement) {
- var invertedKeyMap = func.invertObject(keyMap);
- var $buttons = $container.find('button');
-
- $buttons.each(function (i, elBtn) {
- var $btn = $(elBtn);
- var sShortcut = invertedKeyMap[$btn.data('event')];
- if (sShortcut) {
- $btn.attr('title', function (i, v) {
- return v + ' (' + representShortcut(sShortcut) + ')';
- });
- }
- // bootstrap tooltip on btn-group bug
- // https://github.com/twbs/bootstrap/issues/5687
- }).tooltip({
- container: 'body',
- trigger: 'hover',
- placement: sPlacement || 'top'
- }).on('click', function () {
- $(this).tooltip('hide');
- });
- };
-
- // createPalette
- var createPalette = function ($container, options) {
- var colorInfo = options.colors;
- $container.find('.note-color-palette').each(function () {
- var $palette = $(this), eventName = $palette.attr('data-target-event');
- var paletteContents = [];
- for (var row = 0, lenRow = colorInfo.length; row < lenRow; row++) {
- var colors = colorInfo[row];
- var buttons = [];
- for (var col = 0, lenCol = colors.length; col < lenCol; col++) {
- var color = colors[col];
- buttons.push(['<button type="button" class="note-color-btn" style="background-color:', color,
- ';" data-event="', eventName,
- '" data-value="', color,
- '" title="', color,
- '" data-toggle="button" tabindex="-1"></button>'].join(''));
- }
- paletteContents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
- }
- $palette.html(paletteContents.join(''));
- });
- };
-
- this.createPalette = createPalette; // ODOO: allow access for override
-
- /**
- * create summernote layout (air mode)
- *
- * @param {jQuery} $holder
- * @param {Object} options
- */
- this.createLayoutByAirMode = function ($holder, options) {
- var langInfo = options.langInfo;
- var keyMap = options.keyMap[agent.isMac ? 'mac' : 'pc'];
- var id = func.uniqueId();
-
- $holder.addClass('note-air-editor note-editable'); // ODOO: removing panel-body class to remove unwanted style
- $holder.attr({
- 'data-note-id': id, // ODOO: we use [data-note-id="{id}"] instead of [id="{id}"]
- // 'id': 'note-editor-' + id,
- 'contentEditable': true
- });
-
- var body = document.body;
- var $container = $('#web_editor-toolbars')
-
- // create Popover
- var $popover = $(this.tplPopovers(langInfo, options)); // ODOO: user (maybe) overrided method
- $popover.addClass('note-air-layout');
- $popover.attr('id', 'note-popover-' + id);
- $popover.appendTo($container);
- createTooltip($popover, keyMap);
- this.createPalette($popover, options); // ODOO: use (maybe) overrided method
-
- // create Handle
- var $handle = $(tplHandles(options));
- $handle.addClass('note-air-layout');
- $handle.attr('id', 'note-handle-' + id);
- $handle.appendTo($container);
-
- // create Dialog
- var $dialog = $(tplDialogs(langInfo, options));
- $dialog.addClass('note-air-layout');
- $dialog.attr('id', 'note-dialog-' + id);
- $dialog.find('button.close, a.modal-close').click(function () {
- $(this).closest('.modal').modal('hide');
- });
- $dialog.appendTo($container);
- };
-
- /**
- * create summernote layout (normal mode)
- *
- * @param {jQuery} $holder
- * @param {Object} options
- */
- this.createLayoutByFrame = function ($holder, options) {
- var langInfo = options.langInfo;
-
- //01. create Editor
- var $editor = $('<div class="note-editor panel panel-default" />');
- if (options.width) {
- $editor.width(options.width);
- }
-
- //02. statusbar (resizebar)
- if (options.height > 0) {
- $('<div class="note-statusbar">' + (options.disableResizeEditor ? '' : tplStatusbar()) + '</div>').prependTo($editor);
- }
-
- //03 editing area
- var $editingArea = $('<div class="note-editing-area" />');
- //03. create editable
- var isContentEditable = !$holder.is(':disabled');
- var $editable = $('<div class="note-editable panel-body" contentEditable="' + isContentEditable + '"></div>').prependTo($editingArea);
-
- if (options.height) {
- $editable.height(options.height);
- }
- if (options.direction) {
- $editable.attr('dir', options.direction);
- }
- var placeholder = $holder.attr('placeholder') || options.placeholder;
- if (placeholder) {
- $editable.attr('data-placeholder', placeholder);
- }
-
- $editable.html(dom.html($holder) || dom.emptyPara);
-
- //031. create codable
- $('<textarea class="note-codable"></textarea>').prependTo($editingArea);
-
- //04. create Popover
- var $popover = $(this.tplPopovers(langInfo, options)).prependTo($editingArea); // ODOO: use (maybe) overrided method
- this.createPalette($popover, options); // ODOO: use (maybe) overrided method
- createTooltip($popover, keyMap);
-
- //05. handle(control selection, ...)
- $(tplHandles(options)).prependTo($editingArea);
-
- $editingArea.prependTo($editor);
-
- //06. create Toolbar
- var $toolbar = $('<div class="note-toolbar panel-heading" />');
- for (var idx = 0, len = options.toolbar.length; idx < len; idx ++) {
- var groupName = options.toolbar[idx][0];
- var groupButtons = options.toolbar[idx][1];
-
- var $group = $('<div class="note-' + groupName + ' btn-group" />');
- for (var i = 0, btnLength = groupButtons.length; i < btnLength; i++) {
- var buttonInfo = tplButtonInfo[groupButtons[i]];
- // continue creating toolbar even if a button doesn't exist
- if (!$.isFunction(buttonInfo)) { continue; }
-
- var $button = $(buttonInfo(langInfo, options));
- $button.attr('data-name', groupButtons[i]); // set button's alias, becuase to get button element from $toolbar
- $group.append($button);
- }
- $toolbar.append($group);
- }
-
- var keyMap = options.keyMap[agent.isMac ? 'mac' : 'pc'];
- this.createPalette($toolbar, options); // ODOO: use (maybe) overrided method
- createTooltip($toolbar, keyMap, 'bottom');
- $toolbar.prependTo($editor);
-
- //07. create Dropzone
- $('<div class="note-dropzone"><div class="note-dropzone-message"></div></div>').prependTo($editor);
-
- //08. create Dialog
- var $dialogContainer = options.dialogsInBody ? $(document.body) : $editor;
- var $dialog = $(tplDialogs(langInfo, options)).prependTo($dialogContainer);
- $dialog.find('button.close, a.modal-close').click(function () {
- $(this).closest('.modal').modal('hide');
- });
-
- //09. Editor/Holder switch
- $editor.insertAfter($holder);
- $holder.hide();
- };
-
- this.hasNoteEditor = function ($holder) {
- return this.noteEditorFromHolder($holder).length > 0;
- };
-
- this.noteEditorFromHolder = function ($holder) {
- if ($holder.hasClass('note-air-editor')) {
- return $holder;
- } else if ($holder.next().hasClass('note-editor')) {
- return $holder.next();
- } else {
- return $();
- }
- };
-
- /**
- * create summernote layout
- *
- * @param {jQuery} $holder
- * @param {Object} options
- */
- this.createLayout = function ($holder, options) {
- if (options.airMode) {
- this.createLayoutByAirMode($holder, options);
- } else {
- this.createLayoutByFrame($holder, options);
- }
- };
-
- /**
- * returns layoutInfo from holder
- *
- * @param {jQuery} $holder - placeholder
- * @return {Object}
- */
- this.layoutInfoFromHolder = function ($holder) {
- var $editor = this.noteEditorFromHolder($holder);
- if (!$editor.length) {
- return;
- }
-
- // connect $holder to $editor
- $editor.data('holder', $holder);
-
- return dom.buildLayoutInfo($editor);
- };
-
- /**
- * removeLayout
- *
- * @param {jQuery} $holder - placeholder
- * @param {Object} layoutInfo
- * @param {Object} options
- *
- */
- this.removeLayout = function ($holder, layoutInfo, options) {
- if (options.airMode) {
- $holder.removeClass('note-air-editor note-editable')
- .removeAttr('contentEditable'); // ODOO: removed id 'id contentEditable'
-
- layoutInfo.popover().remove();
- layoutInfo.handle().remove();
- layoutInfo.dialog().remove();
- } else {
- $holder.html(layoutInfo.editable().html());
-
- if (options.dialogsInBody) {
- layoutInfo.dialog().remove();
- }
- layoutInfo.editor().remove();
- $holder.show();
- }
- };
-
- /**
- *
- * @return {Object}
- * @return {function(label, options=):string} return.button {@link #tplButton function to make text button}
- * @return {function(iconClass, options=):string} return.iconButton {@link #tplIconButton function to make icon button}
- * @return {function(className, title=, body=, footer=):string} return.dialog {@link #tplDialog function to make dialog}
- */
- this.getTemplate = function () {
- return {
- button: tplButton,
- iconButton: tplIconButton,
- dialog: tplDialog,
- dropdown: tplDropdown // ODOO: suggest upstream
- };
- };
-
- /**
- * add button information
- *
- * @param {String} name button name
- * @param {Function} buttonInfo function to make button, reference to {@link #tplButton},{@link #tplIconButton}
- */
- this.addButtonInfo = function (name, buttonInfo) {
- tplButtonInfo[name] = buttonInfo;
- };
-
- /**
- *
- * @param {String} name
- * @param {Function} dialogInfo function to make dialog, reference to {@link #tplDialog}
- */
- this.addDialogInfo = function (name, dialogInfo) {
- tplDialogInfo[name] = dialogInfo;
- };
- };
-
- return Renderer;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/app.js b/addons/web_editor/static/lib/summernote/src/js/app.js
deleted file mode 100644
index fba17afd588..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/app.js
+++ /dev/null
@@ -1,42 +0,0 @@
-require.config({
- baseUrl: 'src/js',
- paths: {
- jquery: '//code.jquery.com/jquery-1.11.3',
- bootstrap: '//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min',
- summernotevideo: '/../../plugin/summernote-ext-video',
- CodeMirror: '//cdnjs.cloudflare.com/ajax/libs/codemirror/3.20.0/codemirror',
- CodeMirrorXml: '//cdnjs.cloudflare.com/ajax/libs/codemirror/3.20.0/mode/xml/xml.min',
- CodeMirrorFormatting: '//cdnjs.cloudflare.com/ajax/libs/codemirror/2.36.0/formatting.min'
- },
- shim: {
- bootstrap: ['jquery'],
- CodeMirror: { exports: 'CodeMirror' },
- CodeMirrorXml: ['CodeMirror'],
- CodeMirrorFormatting: ['CodeMirror', 'CodeMirrorXml'],
- summernotevideo: ['summernote']
- },
- packages: [{
- name: 'summernote',
- location: './',
- main: 'summernote'
- }]
-});
-
-require([
- 'jquery', 'bootstrap', 'CodeMirrorFormatting',
- 'summernote', 'summernotevideo'
-], function ($) {
- // summernote
- $('.summernote').summernote({
- height: 300, // set editable area's height
- focus: true, // set focus editable area after summernote loaded
- tabsize: 2, // size of tab
- placeholder: 'Type your message here...', // set editable area's placeholder text
- codemirror: { // code mirror options
- mode: 'text/html',
- htmlMode: true,
- lineNumbers: true,
- theme: 'monokai'
- }
- });
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/agent.js b/addons/web_editor/static/lib/summernote/src/js/core/agent.js
deleted file mode 100644
index 21555a1191c..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/agent.js
+++ /dev/null
@@ -1,170 +0,0 @@
-define(['jquery'], function ($) {
- if (!Array.prototype.reduce) {
- /**
- * Array.prototype.reduce polyfill
- *
- * @param {Function} callback
- * @param {Value} [initialValue]
- * @return {Value}
- *
- * @see http://goo.gl/WNriQD
- */
- Array.prototype.reduce = function (callback) {
- var t = Object(this), len = t.length >>> 0, k = 0, value;
- if (arguments.length === 2) {
- value = arguments[1];
- } else {
- while (k < len && !(k in t)) {
- k++;
- }
- if (k >= len) {
- throw new TypeError('Reduce of empty array with no initial value');
- }
- value = t[k++];
- }
- for (; k < len; k++) {
- if (k in t) {
- value = callback(value, t[k], k, t);
- }
- }
- return value;
- };
- }
-
- if ('function' !== typeof Array.prototype.filter) {
- /**
- * Array.prototype.filter polyfill
- *
- * @param {Function} func
- * @return {Array}
- *
- * @see http://goo.gl/T1KFnq
- */
- Array.prototype.filter = function (func) {
- var t = Object(this), len = t.length >>> 0;
-
- var res = [];
- var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
- for (var i = 0; i < len; i++) {
- if (i in t) {
- var val = t[i];
- if (func.call(thisArg, val, i, t)) {
- res.push(val);
- }
- }
- }
-
- return res;
- };
- }
-
- if (!Array.prototype.map) {
- /**
- * Array.prototype.map polyfill
- *
- * @param {Function} callback
- * @return {Array}
- *
- * @see https://goo.gl/SMWaMK
- */
- Array.prototype.map = function (callback, thisArg) {
- var T, A, k;
- if (this === null) {
- throw new TypeError(' this is null or not defined');
- }
-
- var O = Object(this);
- var len = O.length >>> 0;
- if (typeof callback !== 'function') {
- throw new TypeError(callback + ' is not a function');
- }
-
- if (arguments.length > 1) {
- T = thisArg;
- }
-
- A = new Array(len);
- k = 0;
-
- while (k < len) {
- var kValue, mappedValue;
- if (k in O) {
- kValue = O[k];
- mappedValue = callback.call(T, kValue, k, O);
- A[k] = mappedValue;
- }
- k++;
- }
- return A;
- };
- }
-
- var isSupportAmd = typeof define === 'function' && define.amd;
-
- /**
- * returns whether font is installed or not.
- *
- * @param {String} fontName
- * @return {Boolean}
- */
- var isFontInstalled = function (fontName) {
- var testFontName = fontName === 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS';
- var $tester = $('<div>').css({
- position: 'absolute',
- left: '-9999px',
- top: '-9999px',
- fontSize: '200px'
- }).text('mmmmmmmmmwwwwwww').appendTo(document.body);
-
- var originalWidth = $tester.css('fontFamily', testFontName).width();
- var width = $tester.css('fontFamily', fontName + ',' + testFontName).width();
-
- $tester.remove();
-
- return originalWidth !== width;
- };
-
- var userAgent = navigator.userAgent;
- var isMSIE = /MSIE|Trident/i.test(userAgent);
- var browserVersion;
- if (isMSIE) {
- var matches = /MSIE (\d+[.]\d+)/.exec(userAgent);
- if (matches) {
- browserVersion = parseFloat(matches[1]);
- }
- matches = /Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(userAgent);
- if (matches) {
- browserVersion = parseFloat(matches[1]);
- }
- }
-
- /**
- * @class core.agent
- *
- * Object which check platform and agent
- *
- * @singleton
- * @alternateClassName agent
- */
- var agent = {
- /** @property {Boolean} [isMac=false] true if this agent is Mac */
- isMac: navigator.appVersion.indexOf('Mac') > -1,
- /** @property {Boolean} [isMSIE=false] true if this agent is a Internet Explorer */
- isMSIE: isMSIE,
- /** @property {Boolean} [isFF=false] true if this agent is a Firefox */
- isFF: /firefox/i.test(userAgent),
- isWebkit: /webkit/i.test(userAgent),
- /** @property {Boolean} [isSafari=false] true if this agent is a Safari */
- isSafari: /safari/i.test(userAgent),
- /** @property {Float} browserVersion current browser version */
- browserVersion: browserVersion,
- /** @property {String} jqueryVersion current jQuery version string */
- jqueryVersion: parseFloat($.fn.jquery),
- isSupportAmd: isSupportAmd,
- hasCodeMirror: isSupportAmd ? require.specified('CodeMirror') : !!window.CodeMirror,
- isFontInstalled: isFontInstalled,
- isW3CRangeSupport: !!document.createRange
- };
-
- return agent;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/async.js b/addons/web_editor/static/lib/summernote/src/js/core/async.js
deleted file mode 100644
index 7bc23586e86..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/async.js
+++ /dev/null
@@ -1,68 +0,0 @@
-define('summernote/core/async', function () {
- /**
- * @class core.async
- *
- * Async functions which returns `Promise`
- *
- * @singleton
- * @alternateClassName async
- */
- var async = (function () {
- /**
- * @method readFileAsDataURL
- *
- * read contents of file as representing URL
- *
- * @param {File} file
- * @return {Promise} - then: sDataUrl
- */
- var readFileAsDataURL = function (file) {
- return $.Deferred(function (deferred) {
- $.extend(new FileReader(), {
- onload: function (e) {
- var sDataURL = e.target.result;
- deferred.resolve(sDataURL);
- },
- onerror: function () {
- deferred.reject(this);
- }
- }).readAsDataURL(file);
- }).promise();
- };
-
- /**
- * @method createImage
- *
- * create `<image>` from url string
- *
- * @param {String} sUrl
- * @param {String} filename
- * @return {Promise} - then: $image
- */
- var createImage = function (sUrl, filename) {
- return $.Deferred(function (deferred) {
- var $img = $('<img>');
-
- $img.one('load', function () {
- $img.off('error abort');
- deferred.resolve($img);
- }).one('error abort', function () {
- $img.off('load').detach();
- deferred.reject($img);
- }).css({
- display: 'none'
- }).appendTo(document.body).attr({
- 'src': sUrl,
- 'data-filename': filename
- });
- }).promise();
- };
-
- return {
- readFileAsDataURL: readFileAsDataURL,
- createImage: createImage
- };
- })();
-
- return async;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/dom.js b/addons/web_editor/static/lib/summernote/src/js/core/dom.js
deleted file mode 100644
index 2250b50ac2a..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/dom.js
+++ /dev/null
@@ -1,1120 +0,0 @@
-define([
- 'summernote/core/func',
- 'summernote/core/list',
- 'summernote/core/agent'
-], function (func, list, agent) {
-
- var NBSP_CHAR = String.fromCharCode(160);
- var ZERO_WIDTH_NBSP_CHAR = '\ufeff';
-
- /**
- * @class core.dom
- *
- * Dom functions
- *
- * @singleton
- * @alternateClassName dom
- */
- var dom = (function () {
- /**
- * @method isEditable
- *
- * returns whether node is `note-editable` or not.
- *
- * @param {Node} node
- * @return {Boolean}
- */
- var isEditable = function (node) {
- return node && $(node).hasClass('note-editable');
- };
-
- /**
- * @method isControlSizing
- *
- * returns whether node is `note-control-sizing` or not.
- *
- * @param {Node} node
- * @return {Boolean}
- */
- var isControlSizing = function (node) {
- return node && $(node).hasClass('note-control-sizing');
- };
-
- /**
- * @method buildLayoutInfo
- *
- * build layoutInfo from $editor(.note-editor)
- *
- * @param {jQuery} $editor
- * @return {Object}
- * @return {Function} return.editor
- * @return {Node} return.dropzone
- * @return {Node} return.toolbar
- * @return {Node} return.editable
- * @return {Node} return.codable
- * @return {Node} return.popover
- * @return {Node} return.handle
- * @return {Node} return.dialog
- */
- var buildLayoutInfo = function ($editor) {
- var makeFinder;
-
- // air mode
- if ($editor.hasClass('note-air-editor')) {
- // ODOO: editor on [data-note-id] attribute
- // var id = list.last($editor.attr('id').split('-'));
- var id = list.last($editor.attr('data-note-id').split('-'));
- makeFinder = function (sIdPrefix) {
- return function () { return $(sIdPrefix + id); };
- };
-
- return {
- editor: function () { return $editor; },
- holder : function () { return $editor.data('holder'); },
- editable: function () { return $editor; },
- popover: makeFinder('#note-popover-'),
- handle: makeFinder('#note-handle-'),
- dialog: makeFinder('#note-dialog-')
- };
-
- // frame mode
- } else {
- makeFinder = function (className, $base) {
- $base = $base || $editor;
- return function () { return $base.find(className); };
- };
-
- var options = $editor.data('options');
- var $dialogHolder = (options && options.dialogsInBody) ? $(document.body) : null;
-
- return {
- editor: function () { return $editor; },
- holder : function () { return $editor.data('holder'); },
- dropzone: makeFinder('.note-dropzone'),
- toolbar: makeFinder('.note-toolbar'),
- editable: makeFinder('.note-editable'),
- codable: makeFinder('.note-codable'),
- statusbar: makeFinder('.note-statusbar'),
- popover: makeFinder('.note-popover'),
- handle: makeFinder('.note-handle'),
- dialog: makeFinder('.note-dialog', $dialogHolder)
- };
- }
- };
-
- /**
- * returns makeLayoutInfo from editor's descendant node.
- *
- * @private
- * @param {Node} descendant
- * @return {Object}
- */
- var makeLayoutInfo = function (descendant) {
- var $target = $(descendant).closest('.note-editor, .note-air-editor, .note-air-layout');
-
- if (!$target.length) {
- return null;
- }
-
- var $editor;
- if ($target.is('.note-editor, .note-air-editor')) {
- $editor = $target;
- } else {
- // ODOO: editor on [data-note-id] attribute
- // $editor = $('#note-editor-' + list.last($target.attr('id').split('-')));
- $editor = $('[data-note-id="' + list.last($target.attr('id')) + '"]');
- }
-
- return buildLayoutInfo($editor);
- };
-
- /**
- * @method makePredByNodeName
- *
- * returns predicate which judge whether nodeName is same
- *
- * @param {String} nodeName
- * @return {Function}
- */
- var makePredByNodeName = function (nodeName) {
- nodeName = nodeName.toUpperCase();
- return function (node) {
- return node && node.nodeName.toUpperCase() === nodeName;
- };
- };
-
- /**
- * @method isText
- *
- *
- *
- * @param {Node} node
- * @return {Boolean} true if node's type is text(3)
- */
- var isText = function (node) {
- return node && node.nodeType === 3;
- };
-
- /**
- * ex) br, col, embed, hr, img, input, ...
- * @see http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
- */
- var isVoid = function (node) {
- return node && /^BR|^IMG|^HR|^IFRAME|^BUTTON/.test(node.nodeName.toUpperCase());
- };
-
- var isPara = function (node) {
- if (isEditable(node)) {
- return false;
- }
-
- // Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
- return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName.toUpperCase());
- };
-
- var isLi = makePredByNodeName('LI');
-
- var isPurePara = function (node) {
- return isPara(node) && !isLi(node);
- };
-
- var isTable = makePredByNodeName('TABLE');
-
- var isInline = function (node) {
- return !isBodyContainer(node) &&
- !isList(node) &&
- !isHr(node) &&
- !isPara(node) &&
- !isTable(node) &&
- !isBlockquote(node);
- };
-
- var isList = function (node) {
- return node && /^UL|^OL/.test(node.nodeName.toUpperCase());
- };
-
- var isHr = makePredByNodeName('HR');
-
- var isCell = function (node) {
- return node && /^TD|^TH/.test(node.nodeName.toUpperCase());
- };
-
- var isBlockquote = makePredByNodeName('BLOCKQUOTE');
-
- var isBodyContainer = function (node) {
- return isCell(node) || isBlockquote(node) || isEditable(node);
- };
-
- var isAnchor = makePredByNodeName('A');
-
- var isParaInline = function (node) {
- return isInline(node) && !!ancestor(node, isPara);
- };
-
- var isBodyInline = function (node) {
- return isInline(node) && !ancestor(node, isPara);
- };
-
- var isBody = makePredByNodeName('BODY');
-
- /**
- * returns whether nodeB is closest sibling of nodeA
- *
- * @param {Node} nodeA
- * @param {Node} nodeB
- * @return {Boolean}
- */
- var isClosestSibling = function (nodeA, nodeB) {
- return nodeA.nextSibling === nodeB ||
- nodeA.previousSibling === nodeB;
- };
-
- /**
- * returns array of closest siblings with node
- *
- * @param {Node} node
- * @param {function} [pred] - predicate function
- * @return {Node[]}
- */
- var withClosestSiblings = function (node, pred) {
- pred = pred || func.ok;
-
- var siblings = [];
- if (node.previousSibling && pred(node.previousSibling)) {
- siblings.push(node.previousSibling);
- }
- siblings.push(node);
- if (node.nextSibling && pred(node.nextSibling)) {
- siblings.push(node.nextSibling);
- }
- return siblings;
- };
-
- /**
- * blank HTML for cursor position
- * - [workaround] old IE only works with &nbsp;
- * - [workaround] IE11 and other browser works with bogus br
- */
- var blankHTML = agent.isMSIE && agent.browserVersion < 11 ? '&nbsp;' : '<br>';
-
- /**
- * @method nodeLength
- *
- * returns #text's text size or element's childNodes size
- *
- * @param {Node} node
- */
- var nodeLength = function (node) {
- if (isText(node)) {
- return node.nodeValue.length;
- }
-
- return node.childNodes.length;
- };
-
- /**
- * returns whether node is empty or not.
- *
- * @param {Node} node
- * @return {Boolean}
- */
- var isEmpty = function (node) {
- var len = nodeLength(node);
-
- if (len === 0) {
- return true;
- } else if (!isText(node) && len === 1 && node.innerHTML === blankHTML) {
- // ex) <p><br></p>, <span><br></span>
- return true;
- } else if (list.all(node.childNodes, isText) && node.innerHTML === '') {
- // ex) <p></p>, <span></span>
- return true;
- }
-
- return false;
- };
-
- /**
- * padding blankHTML if node is empty (for cursor position)
- */
- var paddingBlankHTML = function (node) {
- if (!isVoid(node) && !nodeLength(node)) {
- node.innerHTML = blankHTML;
- }
- };
-
- /**
- * find nearest ancestor predicate hit
- *
- * @param {Node} node
- * @param {Function} pred - predicate function
- */
- var ancestor = function (node, pred) {
- while (node) {
- if (pred(node)) { return node; }
- if (isEditable(node)) { break; }
-
- node = node.parentNode;
- }
- return null;
- };
-
- /**
- * find nearest ancestor only single child blood line and predicate hit
- *
- * @param {Node} node
- * @param {Function} pred - predicate function
- */
- var singleChildAncestor = function (node, pred) {
- node = node.parentNode;
-
- while (node) {
- if (nodeLength(node) !== 1) { break; }
- if (pred(node)) { return node; }
- if (isEditable(node)) { break; }
-
- node = node.parentNode;
- }
- return null;
- };
-
- /**
- * returns new array of ancestor nodes (until predicate hit).
- *
- * @param {Node} node
- * @param {Function} [optional] pred - predicate function
- */
- var listAncestor = function (node, pred) {
- pred = pred || func.fail;
-
- var ancestors = [];
- ancestor(node, function (el) {
- if (!isEditable(el)) {
- ancestors.push(el);
- }
-
- return pred(el);
- });
- return ancestors;
- };
-
- /**
- * find farthest ancestor predicate hit
- */
- var lastAncestor = function (node, pred) {
- var ancestors = listAncestor(node);
- return list.last(ancestors.filter(pred));
- };
-
- /**
- * returns common ancestor node between two nodes.
- *
- * @param {Node} nodeA
- * @param {Node} nodeB
- */
- var commonAncestor = function (nodeA, nodeB) {
- var ancestors = listAncestor(nodeA);
- for (var n = nodeB; n; n = n.parentNode) {
- if ($.inArray(n, ancestors) > -1) { return n; }
- }
- return null; // difference document area
- };
-
- /**
- * listing all previous siblings (until predicate hit).
- *
- * @param {Node} node
- * @param {Function} [optional] pred - predicate function
- */
- var listPrev = function (node, pred) {
- pred = pred || func.fail;
-
- var nodes = [];
- while (node) {
- if (pred(node)) { break; }
- nodes.push(node);
- node = node.previousSibling;
- }
- return nodes;
- };
-
- /**
- * listing next siblings (until predicate hit).
- *
- * @param {Node} node
- * @param {Function} [pred] - predicate function
- */
- var listNext = function (node, pred) {
- pred = pred || func.fail;
-
- var nodes = [];
- while (node) {
- if (pred(node)) { break; }
- nodes.push(node);
- node = node.nextSibling;
- }
- return nodes;
- };
-
- /**
- * listing descendant nodes
- *
- * @param {Node} node
- * @param {Function} [pred] - predicate function
- */
- var listDescendant = function (node, pred) {
- var descendents = [];
- pred = pred || func.ok;
-
- // start DFS(depth first search) with node
- (function fnWalk(current) {
- if (node !== current && pred(current)) {
- descendents.push(current);
- }
- for (var idx = 0, len = current.childNodes.length; idx < len; idx++) {
- fnWalk(current.childNodes[idx]);
- }
- })(node);
-
- return descendents;
- };
-
- /**
- * wrap node with new tag.
- *
- * @param {Node} node
- * @param {Node} tagName of wrapper
- * @return {Node} - wrapper
- */
- var wrap = function (node, wrapperName) {
- var parent = node.parentNode;
- var wrapper = $('<' + wrapperName + '>')[0];
-
- parent.insertBefore(wrapper, node);
- wrapper.appendChild(node);
-
- return wrapper;
- };
-
- /**
- * insert node after preceding
- *
- * @param {Node} node
- * @param {Node} preceding - predicate function
- */
- var insertAfter = function (node, preceding) {
- var next = preceding.nextSibling, parent = preceding.parentNode;
- if (next) {
- parent.insertBefore(node, next);
- } else {
- parent.appendChild(node);
- }
- return node;
- };
-
- /**
- * append elements.
- *
- * @param {Node} node
- * @param {Collection} aChild
- */
- var appendChildNodes = function (node, aChild) {
- $.each(aChild, function (idx, child) {
- node.appendChild(child);
- });
- return node;
- };
-
- /**
- * returns whether boundaryPoint is left edge or not.
- *
- * @param {BoundaryPoint} point
- * @return {Boolean}
- */
- var isLeftEdgePoint = function (point) {
- return point.offset === 0;
- };
-
- /**
- * returns whether boundaryPoint is right edge or not.
- *
- * @param {BoundaryPoint} point
- * @return {Boolean}
- */
- var isRightEdgePoint = function (point) {
- return point.offset === nodeLength(point.node);
- };
-
- /**
- * returns whether boundaryPoint is edge or not.
- *
- * @param {BoundaryPoint} point
- * @return {Boolean}
- */
- var isEdgePoint = function (point) {
- return isLeftEdgePoint(point) || isRightEdgePoint(point);
- };
-
- /**
- * returns wheter node is left edge of ancestor or not.
- *
- * @param {Node} node
- * @param {Node} ancestor
- * @return {Boolean}
- */
- var isLeftEdgeOf = function (node, ancestor) {
- while (node && node !== ancestor) {
- if (position(node) !== 0) {
- return false;
- }
- node = node.parentNode;
- }
-
- return true;
- };
-
- /**
- * returns whether node is right edge of ancestor or not.
- *
- * @param {Node} node
- * @param {Node} ancestor
- * @return {Boolean}
- */
- var isRightEdgeOf = function (node, ancestor) {
- while (node && node !== ancestor) {
- if (position(node) !== nodeLength(node.parentNode) - 1) {
- return false;
- }
- node = node.parentNode;
- }
-
- return true;
- };
-
- /**
- * returns whether point is left edge of ancestor or not.
- * @param {BoundaryPoint} point
- * @param {Node} ancestor
- * @return {Boolean}
- */
- var isLeftEdgePointOf = function (point, ancestor) {
- return isLeftEdgePoint(point) && isLeftEdgeOf(point.node, ancestor);
- };
-
- /**
- * returns whether point is right edge of ancestor or not.
- * @param {BoundaryPoint} point
- * @param {Node} ancestor
- * @return {Boolean}
- */
- var isRightEdgePointOf = function (point, ancestor) {
- return isRightEdgePoint(point) && isRightEdgeOf(point.node, ancestor);
- };
-
- /**
- * returns offset from parent.
- *
- * @param {Node} node
- */
- var position = function (node) {
- var offset = 0;
- while ((node = node.previousSibling)) {
- offset += 1;
- }
- return offset;
- };
-
- var hasChildren = function (node) {
- return !!(node && node.childNodes && node.childNodes.length);
- };
-
- /**
- * returns previous boundaryPoint
- *
- * @param {BoundaryPoint} point
- * @param {Boolean} isSkipInnerOffset
- * @return {BoundaryPoint}
- */
- var prevPoint = function (point, isSkipInnerOffset) {
- var node, offset;
-
- if (point.offset === 0) {
- if (isEditable(point.node)) {
- return null;
- }
-
- node = point.node.parentNode;
- offset = position(point.node);
- } else if (hasChildren(point.node)) {
- node = point.node.childNodes[point.offset - 1];
- offset = nodeLength(node);
- } else {
- node = point.node;
- offset = isSkipInnerOffset ? 0 : point.offset - 1;
- }
-
- return {
- node: node,
- offset: offset
- };
- };
-
- /**
- * returns next boundaryPoint
- *
- * @param {BoundaryPoint} point
- * @param {Boolean} isSkipInnerOffset
- * @return {BoundaryPoint}
- */
- var nextPoint = function (point, isSkipInnerOffset) {
- var node, offset;
-
- if (nodeLength(point.node) === point.offset) {
- if (isEditable(point.node)) {
- return null;
- }
-
- node = point.node.parentNode;
- offset = position(point.node) + 1;
- } else if (hasChildren(point.node)) {
- node = point.node.childNodes[point.offset];
- offset = 0;
- } else {
- node = point.node;
- offset = isSkipInnerOffset ? nodeLength(point.node) : point.offset + 1;
- }
-
- return {
- node: node,
- offset: offset
- };
- };
-
- /**
- * returns whether pointA and pointB is same or not.
- *
- * @param {BoundaryPoint} pointA
- * @param {BoundaryPoint} pointB
- * @return {Boolean}
- */
- var isSamePoint = function (pointA, pointB) {
- return pointA.node === pointB.node && pointA.offset === pointB.offset;
- };
-
- /**
- * returns whether point is visible (can set cursor) or not.
- *
- * @param {BoundaryPoint} point
- * @return {Boolean}
- */
- var isVisiblePoint = function (point) {
- if (isText(point.node) || !hasChildren(point.node) || isEmpty(point.node)) {
- return true;
- }
-
- var leftNode = point.node.childNodes[point.offset - 1];
- var rightNode = point.node.childNodes[point.offset];
- if ((!leftNode || isVoid(leftNode)) && (!rightNode || isVoid(rightNode))) {
- return true;
- }
-
- return false;
- };
-
- /**
- * @method prevPointUtil
- *
- * @param {BoundaryPoint} point
- * @param {Function} pred
- * @return {BoundaryPoint}
- */
- var prevPointUntil = function (point, pred) {
- while (point) {
- if (pred(point)) {
- return point;
- }
-
- point = prevPoint(point);
- }
-
- return null;
- };
-
- /**
- * @method nextPointUntil
- *
- * @param {BoundaryPoint} point
- * @param {Function} pred
- * @return {BoundaryPoint}
- */
- var nextPointUntil = function (point, pred) {
- while (point) {
- if (pred(point)) {
- return point;
- }
-
- point = nextPoint(point);
- }
-
- return null;
- };
-
- /**
- * returns whether point has character or not.
- *
- * @param {Point} point
- * @return {Boolean}
- */
- var isCharPoint = function (point) {
- if (!isText(point.node)) {
- return false;
- }
-
- var ch = point.node.nodeValue.charAt(point.offset - 1);
- return ch && (ch !== ' ' && ch !== NBSP_CHAR);
- };
-
- /**
- * @method walkPoint
- *
- * @param {BoundaryPoint} startPoint
- * @param {BoundaryPoint} endPoint
- * @param {Function} handler
- * @param {Boolean} isSkipInnerOffset
- */
- var walkPoint = function (startPoint, endPoint, handler, isSkipInnerOffset) {
- var point = startPoint;
-
- while (point) {
- handler(point);
-
- if (isSamePoint(point, endPoint)) {
- break;
- }
-
- var isSkipOffset = isSkipInnerOffset &&
- startPoint.node !== point.node &&
- endPoint.node !== point.node;
- point = nextPoint(point, isSkipOffset);
- }
- };
-
- /**
- * @method makeOffsetPath
- *
- * return offsetPath(array of offset) from ancestor
- *
- * @param {Node} ancestor - ancestor node
- * @param {Node} node
- */
- var makeOffsetPath = function (ancestor, node) {
- var ancestors = listAncestor(node, func.eq(ancestor));
- return ancestors.map(position).reverse();
- };
-
- /**
- * @method fromOffsetPath
- *
- * return element from offsetPath(array of offset)
- *
- * @param {Node} ancestor - ancestor node
- * @param {array} offsets - offsetPath
- */
- var fromOffsetPath = function (ancestor, offsets) {
- var current = ancestor;
- for (var i = 0, len = offsets.length; i < len; i++) {
- if (current.childNodes.length <= offsets[i]) {
- current = current.childNodes[current.childNodes.length - 1];
- } else {
- current = current.childNodes[offsets[i]];
- }
- }
- return current;
- };
-
- /**
- * @method splitNode
- *
- * split element or #text
- *
- * @param {BoundaryPoint} point
- * @param {Object} [options]
- * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
- * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
- * @return {Node} right node of boundaryPoint
- */
- var splitNode = function (point, options) {
- var isSkipPaddingBlankHTML = options && options.isSkipPaddingBlankHTML;
- var isNotSplitEdgePoint = options && options.isNotSplitEdgePoint;
-
- // edge case
- if (isEdgePoint(point) && (isText(point.node) || isNotSplitEdgePoint)) {
- if (isLeftEdgePoint(point)) {
- return point.node;
- } else if (isRightEdgePoint(point)) {
- return point.node.nextSibling;
- }
- }
-
- // split #text
- if (isText(point.node)) {
- return point.node.splitText(point.offset);
- } else {
- var childNode = point.node.childNodes[point.offset];
- var clone = insertAfter(point.node.cloneNode(false), point.node);
- appendChildNodes(clone, listNext(childNode));
-
- if (!isSkipPaddingBlankHTML) {
- paddingBlankHTML(point.node);
- paddingBlankHTML(clone);
- }
-
- return clone;
- }
- };
-
- /**
- * @method splitTree
- *
- * split tree by point
- *
- * @param {Node} root - split root
- * @param {BoundaryPoint} point
- * @param {Object} [options]
- * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
- * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
- * @return {Node} right node of boundaryPoint
- */
- var splitTree = function (root, point, options) {
- // ex) [#text, <span>, <p>]
- var ancestors = listAncestor(point.node, func.eq(root));
-
- if (!ancestors.length) {
- return null;
- } else if (ancestors.length === 1) {
- return splitNode(point, options);
- }
-
- return ancestors.reduce(function (node, parent) {
- if (node === point.node) {
- node = splitNode(point, options);
- }
-
- return splitNode({
- node: parent,
- offset: node ? dom.position(node) : nodeLength(parent)
- }, options);
- });
- };
-
- /**
- * split point
- *
- * @param {Point} point
- * @param {Boolean} isInline
- * @return {Object}
- */
- var splitPoint = function (point, isInline) {
- // find splitRoot, container
- // - inline: splitRoot is a child of paragraph
- // - block: splitRoot is a child of bodyContainer
- var pred = isInline ? isPara : isBodyContainer;
- var ancestors = listAncestor(point.node, pred);
- var topAncestor = list.last(ancestors) || point.node;
-
- var splitRoot, container;
- if (pred(topAncestor)) {
- splitRoot = ancestors[ancestors.length - 2];
- container = topAncestor;
- } else {
- splitRoot = topAncestor;
- container = splitRoot.parentNode;
- }
-
- // if splitRoot is exists, split with splitTree
- var pivot = splitRoot && splitTree(splitRoot, point, {
- isSkipPaddingBlankHTML: isInline,
- isNotSplitEdgePoint: isInline
- });
-
- // if container is point.node, find pivot with point.offset
- if (!pivot && container === point.node) {
- pivot = point.node.childNodes[point.offset];
- }
-
- return {
- rightNode: pivot,
- container: container
- };
- };
-
- var create = function (nodeName) {
- return document.createElement(nodeName);
- };
-
- var createText = function (text) {
- return document.createTextNode(text);
- };
-
- /**
- * @method remove
- *
- * remove node, (isRemoveChild: remove child or not)
- *
- * @param {Node} node
- * @param {Boolean} isRemoveChild
- */
- var remove = function (node, isRemoveChild) {
- if (!node || !node.parentNode) { return; }
- if (node.removeNode) { return node.removeNode(isRemoveChild); }
-
- var parent = node.parentNode;
- if (!isRemoveChild) {
- var nodes = [];
- var i, len;
- for (i = 0, len = node.childNodes.length; i < len; i++) {
- nodes.push(node.childNodes[i]);
- }
-
- for (i = 0, len = nodes.length; i < len; i++) {
- parent.insertBefore(nodes[i], node);
- }
- }
-
- parent.removeChild(node);
- };
-
- /**
- * @method removeWhile
- *
- * @param {Node} node
- * @param {Function} pred
- */
- var removeWhile = function (node, pred) {
- while (node) {
- if (isEditable(node) || !pred(node)) {
- break;
- }
-
- var parent = node.parentNode;
- remove(node);
- node = parent;
- }
- };
-
- /**
- * @method replace
- *
- * replace node with provided nodeName
- *
- * @param {Node} node
- * @param {String} nodeName
- * @return {Node} - new node
- */
- var replace = function (node, nodeName) {
- if (node.nodeName.toUpperCase() === nodeName.toUpperCase()) {
- return node;
- }
-
- var newNode = create(nodeName);
-
- if (node.style.cssText) {
- newNode.style.cssText = node.style.cssText;
- }
-
- appendChildNodes(newNode, list.from(node.childNodes));
- insertAfter(newNode, node);
- remove(node);
-
- return newNode;
- };
-
- var isTextarea = makePredByNodeName('TEXTAREA');
-
- /**
- * @param {jQuery} $node
- * @param {Boolean} [stripLinebreaks] - default: false
- */
- var value = function ($node, stripLinebreaks) {
- var val = isTextarea($node[0]) ? $node.val() : $node.html();
- if (stripLinebreaks) {
- return val.replace(/[\n\r]/g, '');
- }
- return val;
- };
-
- /**
- * @method html
- *
- * get the HTML contents of node
- *
- * @param {jQuery} $node
- * @param {Boolean} [isNewlineOnBlock]
- */
- var html = function ($node, isNewlineOnBlock) {
- var markup = value($node);
-
- if (isNewlineOnBlock) {
- var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
- markup = markup.replace(regexTag, function (match, endSlash, name) {
- name = name.toUpperCase();
- var isEndOfInlineContainer = /^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(name) &&
- !!endSlash;
- var isBlockNode = /^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(name);
-
- return match + ((isEndOfInlineContainer || isBlockNode) ? '\n' : '');
- });
- markup = $.trim(markup);
- }
-
- return markup;
- };
-
- return {
- /** @property {String} NBSP_CHAR */
- NBSP_CHAR: NBSP_CHAR,
- /** @property {String} ZERO_WIDTH_NBSP_CHAR */
- ZERO_WIDTH_NBSP_CHAR: ZERO_WIDTH_NBSP_CHAR,
- /** @property {String} blank */
- blank: blankHTML,
- /** @property {String} emptyPara */
- emptyPara: '<p>' + blankHTML + '</p>',
- makePredByNodeName: makePredByNodeName,
- isEditable: isEditable,
- isControlSizing: isControlSizing,
- buildLayoutInfo: buildLayoutInfo,
- makeLayoutInfo: makeLayoutInfo,
- isText: isText,
- isVoid: isVoid,
- isPara: isPara,
- isPurePara: isPurePara,
- isInline: isInline,
- isBlock: func.not(isInline),
- isBodyInline: isBodyInline,
- isBody: isBody,
- isParaInline: isParaInline,
- isList: isList,
- isTable: isTable,
- isCell: isCell,
- isBlockquote: isBlockquote,
- isBodyContainer: isBodyContainer,
- isAnchor: isAnchor,
- isDiv: makePredByNodeName('DIV'),
- isLi: isLi,
- isBR: makePredByNodeName('BR'),
- isSpan: makePredByNodeName('SPAN'),
- isB: makePredByNodeName('B'),
- isU: makePredByNodeName('U'),
- isS: makePredByNodeName('S'),
- isI: makePredByNodeName('I'),
- isImg: makePredByNodeName('IMG'),
- isTextarea: isTextarea,
- isEmpty: isEmpty,
- isEmptyAnchor: func.and(isAnchor, isEmpty),
- isClosestSibling: isClosestSibling,
- withClosestSiblings: withClosestSiblings,
- nodeLength: nodeLength,
- isLeftEdgePoint: isLeftEdgePoint,
- isRightEdgePoint: isRightEdgePoint,
- isEdgePoint: isEdgePoint,
- isLeftEdgeOf: isLeftEdgeOf,
- isRightEdgeOf: isRightEdgeOf,
- isLeftEdgePointOf: isLeftEdgePointOf,
- isRightEdgePointOf: isRightEdgePointOf,
- prevPoint: prevPoint,
- nextPoint: nextPoint,
- isSamePoint: isSamePoint,
- isVisiblePoint: isVisiblePoint,
- prevPointUntil: prevPointUntil,
- nextPointUntil: nextPointUntil,
- isCharPoint: isCharPoint,
- walkPoint: walkPoint,
- ancestor: ancestor,
- singleChildAncestor: singleChildAncestor,
- listAncestor: listAncestor,
- lastAncestor: lastAncestor,
- listNext: listNext,
- listPrev: listPrev,
- listDescendant: listDescendant,
- commonAncestor: commonAncestor,
- wrap: wrap,
- insertAfter: insertAfter,
- appendChildNodes: appendChildNodes,
- position: position,
- hasChildren: hasChildren,
- makeOffsetPath: makeOffsetPath,
- fromOffsetPath: fromOffsetPath,
- splitTree: splitTree,
- splitPoint: splitPoint,
- create: create,
- createText: createText,
- remove: remove,
- removeWhile: removeWhile,
- replace: replace,
- html: html,
- value: value
- };
- })();
-
- return dom;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/func.js b/addons/web_editor/static/lib/summernote/src/js/core/func.js
deleted file mode 100644
index 11bfdf546e0..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/func.js
+++ /dev/null
@@ -1,130 +0,0 @@
-define('summernote/core/func', function () {
- /**
- * @class core.func
- *
- * func utils (for high-order func's arg)
- *
- * @singleton
- * @alternateClassName func
- */
- var func = (function () {
- var eq = function (itemA) {
- return function (itemB) {
- return itemA === itemB;
- };
- };
-
- var eq2 = function (itemA, itemB) {
- return itemA === itemB;
- };
-
- var peq2 = function (propName) {
- return function (itemA, itemB) {
- return itemA[propName] === itemB[propName];
- };
- };
-
- var ok = function () {
- return true;
- };
-
- var fail = function () {
- return false;
- };
-
- var not = function (f) {
- return function () {
- return !f.apply(f, arguments);
- };
- };
-
- var and = function (fA, fB) {
- return function (item) {
- return fA(item) && fB(item);
- };
- };
-
- var self = function (a) {
- return a;
- };
-
- var idCounter = 0;
-
- /**
- * generate a globally-unique id
- *
- * @param {String} [prefix]
- */
- var uniqueId = function (prefix) {
- var id = ++idCounter + '';
- return prefix ? prefix + id : id;
- };
-
- /**
- * returns bnd (bounds) from rect
- *
- * - IE Compatability Issue: http://goo.gl/sRLOAo
- * - Scroll Issue: http://goo.gl/sNjUc
- *
- * @param {Rect} rect
- * @return {Object} bounds
- * @return {Number} bounds.top
- * @return {Number} bounds.left
- * @return {Number} bounds.width
- * @return {Number} bounds.height
- */
- var rect2bnd = function (rect) {
- var $document = $(document);
- return {
- top: rect.top + $document.scrollTop(),
- left: rect.left + $document.scrollLeft(),
- width: rect.right - rect.left,
- height: rect.bottom - rect.top
- };
- };
-
- /**
- * returns a copy of the object where the keys have become the values and the values the keys.
- * @param {Object} obj
- * @return {Object}
- */
- var invertObject = function (obj) {
- var inverted = {};
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- inverted[obj[key]] = key;
- }
- }
- return inverted;
- };
-
- /**
- * @param {String} namespace
- * @param {String} [prefix]
- * @return {String}
- */
- var namespaceToCamel = function (namespace, prefix) {
- prefix = prefix || '';
- return prefix + namespace.split('.').map(function (name) {
- return name.substring(0, 1).toUpperCase() + name.substring(1);
- }).join('');
- };
-
- return {
- eq: eq,
- eq2: eq2,
- peq2: peq2,
- ok: ok,
- fail: fail,
- self: self,
- not: not,
- and: and,
- uniqueId: uniqueId,
- rect2bnd: rect2bnd,
- invertObject: invertObject,
- namespaceToCamel: namespaceToCamel
- };
- })();
-
- return func;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/key.js b/addons/web_editor/static/lib/summernote/src/js/core/key.js
deleted file mode 100644
index a17b2756328..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/key.js
+++ /dev/null
@@ -1,96 +0,0 @@
-define([
- 'summernote/core/list',
- 'summernote/core/func'
-], function (list, func) {
- /**
- * @class core.key
- *
- * Object for keycodes.
- *
- * @singleton
- * @alternateClassName key
- */
- var key = (function () {
- var keyMap = {
- 'BACKSPACE': 8,
- 'TAB': 9,
- 'ENTER': 13,
- 'SPACE': 32,
-
- // Arrow
- 'LEFT': 37,
- 'UP': 38,
- 'RIGHT': 39,
- 'DOWN': 40,
-
- // Number: 0-9
- 'NUM0': 48,
- 'NUM1': 49,
- 'NUM2': 50,
- 'NUM3': 51,
- 'NUM4': 52,
- 'NUM5': 53,
- 'NUM6': 54,
- 'NUM7': 55,
- 'NUM8': 56,
-
- // Alphabet: a-z
- 'B': 66,
- 'E': 69,
- 'I': 73,
- 'J': 74,
- 'K': 75,
- 'L': 76,
- 'R': 82,
- 'S': 83,
- 'U': 85,
- 'V': 86,
- 'Y': 89,
- 'Z': 90,
-
- 'SLASH': 191,
- 'LEFTBRACKET': 219,
- 'BACKSLASH': 220,
- 'RIGHTBRACKET': 221
- };
-
- return {
- /**
- * @method isEdit
- *
- * @param {Number} keyCode
- * @return {Boolean}
- */
- isEdit: function (keyCode) {
- return list.contains([
- keyMap.BACKSPACE,
- keyMap.TAB,
- keyMap.ENTER,
- keyMap.SPACe
- ], keyCode);
- },
- /**
- * @method isMove
- *
- * @param {Number} keyCode
- * @return {Boolean}
- */
- isMove: function (keyCode) {
- return list.contains([
- keyMap.LEFT,
- keyMap.UP,
- keyMap.RIGHT,
- keyMap.DOWN
- ], keyCode);
- },
- /**
- * @property {Object} nameFromCode
- * @property {String} nameFromCode.8 "BACKSPACE"
- */
- nameFromCode: func.invertObject(keyMap),
- code: keyMap
- };
- })();
-
- return key;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/list.js b/addons/web_editor/static/lib/summernote/src/js/core/list.js
deleted file mode 100644
index 2d6708838df..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/list.js
+++ /dev/null
@@ -1,191 +0,0 @@
-define(['summernote/core/func'], function (func) {
- /**
- * @class core.list
- *
- * list utils
- *
- * @singleton
- * @alternateClassName list
- */
- var list = (function () {
- /**
- * returns the first item of an array.
- *
- * @param {Array} array
- */
- var head = function (array) {
- return array[0];
- };
-
- /**
- * returns the last item of an array.
- *
- * @param {Array} array
- */
- var last = function (array) {
- return array[array.length - 1];
- };
-
- /**
- * returns everything but the last entry of the array.
- *
- * @param {Array} array
- */
- var initial = function (array) {
- return array.slice(0, array.length - 1);
- };
-
- /**
- * returns the rest of the items in an array.
- *
- * @param {Array} array
- */
- var tail = function (array) {
- return array.slice(1);
- };
-
- /**
- * returns item of array
- */
- var find = function (array, pred) {
- for (var idx = 0, len = array.length; idx < len; idx ++) {
- var item = array[idx];
- if (pred(item)) {
- return item;
- }
- }
- };
-
- /**
- * returns true if all of the values in the array pass the predicate truth test.
- */
- var all = function (array, pred) {
- for (var idx = 0, len = array.length; idx < len; idx ++) {
- if (!pred(array[idx])) {
- return false;
- }
- }
- return true;
- };
-
- /**
- * returns index of item
- */
- var indexOf = function (array, item) {
- return $.inArray(item, array);
- };
-
- /**
- * returns true if the value is present in the list.
- */
- var contains = function (array, item) {
- return indexOf(array, item) !== -1;
- };
-
- /**
- * get sum from a list
- *
- * @param {Array} array - array
- * @param {Function} fn - iterator
- */
- var sum = function (array, fn) {
- fn = fn || func.self;
- return array.reduce(function (memo, v) {
- return memo + fn(v);
- }, 0);
- };
-
- /**
- * returns a copy of the collection with array type.
- * @param {Collection} collection - collection eg) node.childNodes, ...
- */
- var from = function (collection) {
- var result = [], idx = -1, length = collection.length;
- while (++idx < length) {
- result[idx] = collection[idx];
- }
- return result;
- };
-
- /**
- * cluster elements by predicate function.
- *
- * @param {Array} array - array
- * @param {Function} fn - predicate function for cluster rule
- * @param {Array[]}
- */
- var clusterBy = function (array, fn) {
- if (!array.length) { return []; }
- var aTail = tail(array);
- return aTail.reduce(function (memo, v) {
- var aLast = last(memo);
- if (fn(last(aLast), v)) {
- aLast[aLast.length] = v;
- } else {
- memo[memo.length] = [v];
- }
- return memo;
- }, [[head(array)]]);
- };
-
- /**
- * returns a copy of the array with all falsy values removed
- *
- * @param {Array} array - array
- * @param {Function} fn - predicate function for cluster rule
- */
- var compact = function (array) {
- var aResult = [];
- for (var idx = 0, len = array.length; idx < len; idx ++) {
- if (array[idx]) { aResult.push(array[idx]); }
- }
- return aResult;
- };
-
- /**
- * produces a duplicate-free version of the array
- *
- * @param {Array} array
- */
- var unique = function (array) {
- var results = [];
-
- for (var idx = 0, len = array.length; idx < len; idx ++) {
- if (!contains(results, array[idx])) {
- results.push(array[idx]);
- }
- }
-
- return results;
- };
-
- /**
- * returns next item.
- * @param {Array} array
- */
- var next = function (array, item) {
- var idx = indexOf(array, item);
- if (idx === -1) { return null; }
-
- return array[idx + 1];
- };
-
- /**
- * returns prev item.
- * @param {Array} array
- */
- var prev = function (array, item) {
- var idx = indexOf(array, item);
- if (idx === -1) { return null; }
-
- return array[idx - 1];
- };
-
- return { head: head, last: last, initial: initial, tail: tail,
- prev: prev, next: next, find: find, contains: contains,
- all: all, sum: sum, from: from,
- clusterBy: clusterBy, compact: compact, unique: unique };
- })();
-
- return list;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/core/range.js b/addons/web_editor/static/lib/summernote/src/js/core/range.js
deleted file mode 100644
index 5c2dbe3b32c..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/core/range.js
+++ /dev/null
@@ -1,790 +0,0 @@
-define([
- 'summernote/core/agent',
- 'summernote/core/func',
- 'summernote/core/list',
- 'summernote/core/dom'
-], function (agent, func, list, dom) {
-
- var range = (function () {
-
- /**
- * return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
- *
- * @param {TextRange} textRange
- * @param {Boolean} isStart
- * @return {BoundaryPoint}
- *
- * @see http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
- */
- var textRangeToPoint = function (textRange, isStart) {
- var container = textRange.parentElement(), offset;
-
- var tester = document.body.createTextRange(), prevContainer;
- var childNodes = list.from(container.childNodes);
- for (offset = 0; offset < childNodes.length; offset++) {
- if (dom.isText(childNodes[offset])) {
- continue;
- }
- tester.moveToElementText(childNodes[offset]);
- if (tester.compareEndPoints('StartToStart', textRange) >= 0) {
- break;
- }
- prevContainer = childNodes[offset];
- }
-
- if (offset !== 0 && dom.isText(childNodes[offset - 1])) {
- var textRangeStart = document.body.createTextRange(), curTextNode = null;
- textRangeStart.moveToElementText(prevContainer || container);
- textRangeStart.collapse(!prevContainer);
- curTextNode = prevContainer ? prevContainer.nextSibling : container.firstChild;
-
- var pointTester = textRange.duplicate();
- pointTester.setEndPoint('StartToStart', textRangeStart);
- var textCount = pointTester.text.replace(/[\r\n]/g, '').length;
-
- while (textCount > curTextNode.nodeValue.length && curTextNode.nextSibling) {
- textCount -= curTextNode.nodeValue.length;
- curTextNode = curTextNode.nextSibling;
- }
-
- /* jshint ignore:start */
- var dummy = curTextNode.nodeValue; // enforce IE to re-reference curTextNode, hack
- /* jshint ignore:end */
-
- if (isStart && curTextNode.nextSibling && dom.isText(curTextNode.nextSibling) &&
- textCount === curTextNode.nodeValue.length) {
- textCount -= curTextNode.nodeValue.length;
- curTextNode = curTextNode.nextSibling;
- }
-
- container = curTextNode;
- offset = textCount;
- }
-
- return {
- cont: container,
- offset: offset
- };
- };
-
- /**
- * return TextRange from boundary point (inspired by google closure-library)
- * @param {BoundaryPoint} point
- * @return {TextRange}
- */
- var pointToTextRange = function (point) {
- var textRangeInfo = function (container, offset) {
- var node, isCollapseToStart;
-
- if (dom.isText(container)) {
- var prevTextNodes = dom.listPrev(container, func.not(dom.isText));
- var prevContainer = list.last(prevTextNodes).previousSibling;
- node = prevContainer || container.parentNode;
- offset += list.sum(list.tail(prevTextNodes), dom.nodeLength);
- isCollapseToStart = !prevContainer;
- } else {
- node = container.childNodes[offset] || container;
- if (dom.isText(node)) {
- return textRangeInfo(node, 0);
- }
-
- offset = 0;
- isCollapseToStart = false;
- }
-
- return {
- node: node,
- collapseToStart: isCollapseToStart,
- offset: offset
- };
- };
-
- var textRange = document.body.createTextRange();
- var info = textRangeInfo(point.node, point.offset);
-
- textRange.moveToElementText(info.node);
- textRange.collapse(info.collapseToStart);
- textRange.moveStart('character', info.offset);
- return textRange;
- };
-
- /**
- * Wrapped Range
- *
- * @constructor
- * @param {Node} sc - start container
- * @param {Number} so - start offset
- * @param {Node} ec - end container
- * @param {Number} eo - end offset
- */
- var WrappedRange = function (sc, so, ec, eo) {
- this.sc = sc;
- this.so = so;
- this.ec = ec;
- this.eo = eo;
-
- // nativeRange: get nativeRange from sc, so, ec, eo
- var nativeRange = function () {
- if (agent.isW3CRangeSupport) {
- var w3cRange = document.createRange();
- w3cRange.setStart(sc, so);
- w3cRange.setEnd(ec, eo);
-
- return w3cRange;
- } else {
- var textRange = pointToTextRange({
- node: sc,
- offset: so
- });
-
- textRange.setEndPoint('EndToEnd', pointToTextRange({
- node: ec,
- offset: eo
- }));
-
- return textRange;
- }
- };
-
- this.getPoints = function () {
- return {
- sc: sc,
- so: so,
- ec: ec,
- eo: eo
- };
- };
-
- this.getStartPoint = function () {
- return {
- node: sc,
- offset: so
- };
- };
-
- this.getEndPoint = function () {
- return {
- node: ec,
- offset: eo
- };
- };
-
- /**
- * select update visible range
- */
- this.select = function () {
- var nativeRng = nativeRange();
- if (agent.isW3CRangeSupport) {
- var selection = document.getSelection();
- if (selection.rangeCount > 0) {
- selection.removeAllRanges();
- }
- selection.addRange(nativeRng);
- } else {
- nativeRng.select();
- }
-
- return this;
- };
-
- /**
- * @return {WrappedRange}
- */
- this.normalize = function () {
-
- /**
- * @param {BoundaryPoint} point
- * @param {Boolean} isLeftToRight
- * @return {BoundaryPoint}
- */
- var getVisiblePoint = function (point, isLeftToRight) {
- if ((dom.isVisiblePoint(point) && !dom.isEdgePoint(point)) ||
- (dom.isVisiblePoint(point) && dom.isRightEdgePoint(point) && !isLeftToRight) ||
- (dom.isVisiblePoint(point) && dom.isLeftEdgePoint(point) && isLeftToRight) ||
- (dom.isVisiblePoint(point) && dom.isBlock(point.node) && dom.isEmpty(point.node))) {
- return point;
- }
-
- // point on block's edge
- var block = dom.ancestor(point.node, dom.isBlock);
- if (((dom.isLeftEdgePointOf(point, block) || dom.isVoid(dom.prevPoint(point).node)) && !isLeftToRight) ||
- ((dom.isRightEdgePointOf(point, block) || dom.isVoid(dom.nextPoint(point).node)) && isLeftToRight)) {
-
- // returns point already on visible point
- if (dom.isVisiblePoint(point)) {
- return point;
- }
- // reverse direction
- isLeftToRight = !isLeftToRight;
- }
-
- var nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint) :
- dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint);
- return nextPoint || point;
- };
-
- var endPoint = getVisiblePoint(this.getEndPoint(), false);
- var startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true);
-
- return new WrappedRange(
- startPoint.node,
- startPoint.offset,
- endPoint.node,
- endPoint.offset
- );
- };
-
- /**
- * returns matched nodes on range
- *
- * @param {Function} [pred] - predicate function
- * @param {Object} [options]
- * @param {Boolean} [options.includeAncestor]
- * @param {Boolean} [options.fullyContains]
- * @return {Node[]}
- */
- this.nodes = function (pred, options) {
- pred = pred || func.ok;
-
- var includeAncestor = options && options.includeAncestor;
- var fullyContains = options && options.fullyContains;
-
- // TODO compare points and sort
- var startPoint = this.getStartPoint();
- var endPoint = this.getEndPoint();
-
- var nodes = [];
- var leftEdgeNodes = [];
-
- dom.walkPoint(startPoint, endPoint, function (point) {
- if (dom.isEditable(point.node)) {
- return;
- }
-
- var node;
- if (fullyContains) {
- if (dom.isLeftEdgePoint(point)) {
- leftEdgeNodes.push(point.node);
- }
- if (dom.isRightEdgePoint(point) && list.contains(leftEdgeNodes, point.node)) {
- node = point.node;
- }
- } else if (includeAncestor) {
- node = dom.ancestor(point.node, pred);
- } else {
- node = point.node;
- }
-
- if (node && pred(node)) {
- nodes.push(node);
- }
- }, true);
-
- return list.unique(nodes);
- };
-
- /**
- * returns commonAncestor of range
- * @return {Element} - commonAncestor
- */
- this.commonAncestor = function () {
- return dom.commonAncestor(sc, ec);
- };
-
- /**
- * returns expanded range by pred
- *
- * @param {Function} pred - predicate function
- * @return {WrappedRange}
- */
- this.expand = function (pred) {
- var startAncestor = dom.ancestor(sc, pred);
- var endAncestor = dom.ancestor(ec, pred);
-
- if (!startAncestor && !endAncestor) {
- return new WrappedRange(sc, so, ec, eo);
- }
-
- var boundaryPoints = this.getPoints();
-
- if (startAncestor) {
- boundaryPoints.sc = startAncestor;
- boundaryPoints.so = 0;
- }
-
- if (endAncestor) {
- boundaryPoints.ec = endAncestor;
- boundaryPoints.eo = dom.nodeLength(endAncestor);
- }
-
- return new WrappedRange(
- boundaryPoints.sc,
- boundaryPoints.so,
- boundaryPoints.ec,
- boundaryPoints.eo
- );
- };
-
- /**
- * @param {Boolean} isCollapseToStart
- * @return {WrappedRange}
- */
- this.collapse = function (isCollapseToStart) {
- if (isCollapseToStart) {
- return new WrappedRange(sc, so, sc, so);
- } else {
- return new WrappedRange(ec, eo, ec, eo);
- }
- };
-
- /**
- * splitText on range
- */
- this.splitText = function () {
- var isSameContainer = sc === ec;
- var boundaryPoints = this.getPoints();
-
- if (dom.isText(ec) && !dom.isEdgePoint(this.getEndPoint())) {
- ec.splitText(eo);
- }
-
- if (dom.isText(sc) && !dom.isEdgePoint(this.getStartPoint())) {
- boundaryPoints.sc = sc.splitText(so);
- boundaryPoints.so = 0;
-
- if (isSameContainer) {
- boundaryPoints.ec = boundaryPoints.sc;
- boundaryPoints.eo = eo - so;
- }
- }
-
- return new WrappedRange(
- boundaryPoints.sc,
- boundaryPoints.so,
- boundaryPoints.ec,
- boundaryPoints.eo
- );
- };
-
- /**
- * delete contents on range
- * @return {WrappedRange}
- */
- if(_.isUndefined(this.deleteContents)) // ODOO: ability to override by prototype
- this.deleteContents = function () {
- if (this.isCollapsed()) {
- return this;
- }
-
- var rng = this.splitText();
- var nodes = rng.nodes(null, {
- fullyContains: true
- });
-
- // find new cursor point
- var point = dom.prevPointUntil(rng.getStartPoint(), function (point) {
- return !list.contains(nodes, point.node);
- });
-
- var emptyParents = [];
- $.each(nodes, function (idx, node) {
- // find empty parents
- var parent = node.parentNode;
- if (point.node !== parent && dom.nodeLength(parent) === 1) {
- emptyParents.push(parent);
- }
- dom.remove(node, false);
- });
-
- // remove empty parents
- $.each(emptyParents, function (idx, node) {
- dom.remove(node, false);
- });
-
- return new WrappedRange(
- point.node,
- point.offset,
- point.node,
- point.offset
- ).normalize();
- };
-
- /**
- * makeIsOn: return isOn(pred) function
- */
- var makeIsOn = function (pred) {
- return function () {
- var ancestor = dom.ancestor(sc, pred);
- return !!ancestor && (ancestor === dom.ancestor(ec, pred));
- };
- };
-
- // isOnEditable: judge whether range is on editable or not
- this.isOnEditable = makeIsOn(dom.isEditable);
- // isOnList: judge whether range is on list node or not
- this.isOnList = makeIsOn(dom.isList);
- // isOnAnchor: judge whether range is on anchor node or not
- this.isOnAnchor = makeIsOn(dom.isAnchor);
- // isOnAnchor: judge whether range is on cell node or not
- this.isOnCell = makeIsOn(dom.isCell);
-
- /**
- * @param {Function} pred
- * @return {Boolean}
- */
- this.isLeftEdgeOf = function (pred) {
- if (!dom.isLeftEdgePoint(this.getStartPoint())) {
- return false;
- }
-
- var node = dom.ancestor(this.sc, pred);
- return node && dom.isLeftEdgeOf(this.sc, node);
- };
-
- /**
- * returns whether range was collapsed or not
- */
- this.isCollapsed = function () {
- return sc === ec && so === eo;
- };
-
- /**
- * wrap inline nodes which children of body with paragraph
- *
- * @return {WrappedRange}
- */
- this.wrapBodyInlineWithPara = function () {
- if (dom.isBodyContainer(sc) && dom.isEmpty(sc)) {
- sc.innerHTML = dom.emptyPara;
- return new WrappedRange(sc.firstChild, 0, sc.firstChild, 0);
- }
-
- /**
- * [workaround] firefox often create range on not visible point. so normalize here.
- * - firefox: |<p>text</p>|
- * - chrome: <p>|text|</p>
- */
- var rng = this.normalize();
- if (dom.isParaInline(sc) || dom.isPara(sc)) {
- return rng;
- }
-
- // ODOO: insert a p tag when try to insert a br with insertNode method, if the editor is inside a p, li, h1... (start_modification
- // if apply the editor to a P, LI... or inside a P, LI...
- if (dom.isText(sc)) {
- var node = sc;
- while (node.parentNode !== document) {
- node = node.parentNode;
- if (/^(P|LI|H[1-7]|BUTTON|A|SPAN)/.test(node.nodeName.toUpperCase())) {
- return this.normalize();
- }
- }
- }
- // ODOO: end_modification)
-
- // find inline top ancestor
- var topAncestor;
- if (dom.isInline(rng.sc)) {
- var ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline));
- topAncestor = list.last(ancestors);
- if (!dom.isInline(topAncestor)) {
- topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so];
- }
- } else {
- topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0];
- }
-
- // siblings not in paragraph
- var inlineSiblings = dom.listPrev(topAncestor, dom.isParaInline).reverse();
- inlineSiblings = inlineSiblings.concat(dom.listNext(topAncestor.nextSibling, dom.isParaInline));
-
- // wrap with paragraph
- if (inlineSiblings.length) {
- var para = dom.wrap(list.head(inlineSiblings), 'p');
- dom.appendChildNodes(para, list.tail(inlineSiblings));
- }
-
- return this.normalize();
- };
-
- /**
- * insert node at current cursor
- *
- * @param {Node} node
- * @return {Node}
- */
- this.insertNode = function (node) {
- var rng = this.wrapBodyInlineWithPara().deleteContents();
- // ODOO: override to not split world for inserting inline
- // original: var info = dom.splitPoint(rng.getStartPoint(), dom.isInline(node));
- var info = dom.splitPoint(rng.getStartPoint(), !dom.isBodyContainer(dom.ancestor(rng.sc, function(node) { return dom.isBodyContainer(node) || dom.isPara(node) })));
-
- if (info.rightNode) {
- info.rightNode.parentNode.insertBefore(node, info.rightNode);
- } else {
- info.container.appendChild(node);
- }
-
- return node;
- };
-
- /**
- * insert html at current cursor
- */
- this.pasteHTML = function (markup) {
- var contentsContainer = $('<div></div>').html(markup)[0];
- var childNodes = list.from(contentsContainer.childNodes);
-
- var rng = this.wrapBodyInlineWithPara().deleteContents();
-
- return childNodes.reverse().map(function (childNode) {
- return rng.insertNode(childNode);
- }).reverse();
- };
-
- /**
- * returns text in range
- *
- * @return {String}
- */
- this.toString = function () {
- var nativeRng = nativeRange();
- return agent.isW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
- };
-
- /**
- * returns range for word before cursor
- *
- * @param {Boolean} [findAfter] - find after cursor, default: false
- * @return {WrappedRange}
- */
- this.getWordRange = function (findAfter) {
- var endPoint = this.getEndPoint();
-
- if (!dom.isCharPoint(endPoint)) {
- return this;
- }
-
- var startPoint = dom.prevPointUntil(endPoint, function (point) {
- return !dom.isCharPoint(point);
- });
-
- if (findAfter) {
- endPoint = dom.nextPointUntil(endPoint, function (point) {
- return !dom.isCharPoint(point);
- });
- }
-
- return new WrappedRange(
- startPoint.node,
- startPoint.offset,
- endPoint.node,
- endPoint.offset
- );
- };
-
- /**
- * create offsetPath bookmark
- *
- * @param {Node} editable
- */
- this.bookmark = function (editable) {
- return {
- s: {
- path: dom.makeOffsetPath(editable, sc),
- offset: so
- },
- e: {
- path: dom.makeOffsetPath(editable, ec),
- offset: eo
- }
- };
- };
-
- /**
- * create offsetPath bookmark base on paragraph
- *
- * @param {Node[]} paras
- */
- this.paraBookmark = function (paras) {
- return {
- s: {
- path: list.tail(dom.makeOffsetPath(list.head(paras), sc)),
- offset: so
- },
- e: {
- path: list.tail(dom.makeOffsetPath(list.last(paras), ec)),
- offset: eo
- }
- };
- };
-
- /**
- * getClientRects
- * @return {Rect[]}
- */
- this.getClientRects = function () {
- var nativeRng = nativeRange();
- return nativeRng.getClientRects();
- };
- };
-
- /**
- * @class core.range
- *
- * Data structure
- * * BoundaryPoint: a point of dom tree
- * * BoundaryPoints: two boundaryPoints corresponding to the start and the end of the Range
- *
- * See to http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Position
- *
- * @singleton
- * @alternateClassName range
- */
- return {
- WrappedRange: WrappedRange, // ODOO: give access to WrappedRange
- /**
- * @method
- *
- * create Range Object From arguments or Browser Selection
- *
- * @param {Node} sc - start container
- * @param {Number} so - start offset
- * @param {Node} ec - end container
- * @param {Number} eo - end offset
- * @return {WrappedRange}
- */
- create : function (sc, so, ec, eo) {
- if (!arguments.length) { // from Browser Selection
- if (agent.isW3CRangeSupport) {
- var selection = document.getSelection();
- if (!selection || selection.rangeCount === 0) {
- return null;
- } else if (dom.isBody(selection.anchorNode)) {
- // Firefox: returns entire body as range on initialization. We won't never need it.
- return null;
- }
-
- var nativeRng = selection.getRangeAt(0);
- sc = nativeRng.startContainer;
- so = nativeRng.startOffset;
- ec = nativeRng.endContainer;
- eo = nativeRng.endOffset;
- } else { // IE8: TextRange
- var textRange = document.selection.createRange();
- var textRangeEnd = textRange.duplicate();
- textRangeEnd.collapse(false);
- var textRangeStart = textRange;
- textRangeStart.collapse(true);
-
- var startPoint = textRangeToPoint(textRangeStart, true),
- endPoint = textRangeToPoint(textRangeEnd, false);
-
- // same visible point case: range was collapsed.
- if (dom.isText(startPoint.node) && dom.isLeftEdgePoint(startPoint) &&
- dom.isTextNode(endPoint.node) && dom.isRightEdgePoint(endPoint) &&
- endPoint.node.nextSibling === startPoint.node) {
- startPoint = endPoint;
- }
-
- sc = startPoint.cont;
- so = startPoint.offset;
- ec = endPoint.cont;
- eo = endPoint.offset;
- }
- } else if (arguments.length === 2) { //collapsed
- ec = sc;
- eo = so;
- }
- return new WrappedRange(sc, so, ec, eo);
- },
-
- /**
- * @method
- *
- * create WrappedRange from node
- *
- * @param {Node} node
- * @return {WrappedRange}
- */
- createFromNode: function (node) {
- var sc = node;
- var so = 0;
- var ec = node;
- var eo = dom.nodeLength(ec);
-
- // browsers can't target a picture or void node
- if (dom.isVoid(sc)) {
- so = dom.listPrev(sc).length - 1;
- sc = sc.parentNode;
- }
- if (dom.isBR(ec)) {
- eo = dom.listPrev(ec).length - 1;
- ec = ec.parentNode;
- } else if (dom.isVoid(ec)) {
- eo = dom.listPrev(ec).length;
- ec = ec.parentNode;
- }
-
- return this.create(sc, so, ec, eo);
- },
-
- /**
- * create WrappedRange from node after position
- *
- * @param {Node} node
- * @return {WrappedRange}
- */
- createFromNodeBefore: function (node) {
- return this.createFromNode(node).collapse(true);
- },
-
- /**
- * create WrappedRange from node after position
- *
- * @param {Node} node
- * @return {WrappedRange}
- */
- createFromNodeAfter: function (node) {
- return this.createFromNode(node).collapse();
- },
-
- /**
- * @method
- *
- * create WrappedRange from bookmark
- *
- * @param {Node} editable
- * @param {Object} bookmark
- * @return {WrappedRange}
- */
- createFromBookmark : function (editable, bookmark) {
- var sc = dom.fromOffsetPath(editable, bookmark.s.path);
- var so = bookmark.s.offset;
- var ec = dom.fromOffsetPath(editable, bookmark.e.path);
- var eo = bookmark.e.offset;
- return new WrappedRange(sc, so, ec, eo);
- },
-
- /**
- * @method
- *
- * create WrappedRange from paraBookmark
- *
- * @param {Object} bookmark
- * @param {Node[]} paras
- * @return {WrappedRange}
- */
- createFromParaBookmark: function (bookmark, paras) {
- var so = bookmark.s.offset;
- var eo = bookmark.e.offset;
- var sc = dom.fromOffsetPath(list.head(paras), bookmark.s.path);
- var ec = dom.fromOffsetPath(list.last(paras), bookmark.e.path);
-
- return new WrappedRange(sc, so, ec, eo);
- }
- };
- })();
-
- return range;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/defaults.js b/addons/web_editor/static/lib/summernote/src/js/defaults.js
deleted file mode 100644
index 9d8e0bab661..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/defaults.js
+++ /dev/null
@@ -1,416 +0,0 @@
-define('summernote/defaults', function () {
- /**
- * @class defaults
- *
- * @singleton
- */
- var defaults = {
- /** @property */
- version: '@VERSION',
-
- /**
- *
- * for event options, reference to EventHandler.attach
- *
- * @property {Object} options
- * @property {String/Number} [options.width=null] set editor width
- * @property {String/Number} [options.height=null] set editor height, ex) 300
- * @property {String/Number} options.minHeight set minimum height of editor
- * @property {String/Number} options.maxHeight
- * @property {String/Number} options.focus
- * @property {Number} options.tabsize
- * @property {Boolean} options.styleWithSpan
- * @property {Object} options.codemirror
- * @property {Object} [options.codemirror.mode='text/html']
- * @property {Object} [options.codemirror.htmlMode=true]
- * @property {Object} [options.codemirror.lineNumbers=true]
- * @property {String} [options.lang=en-US] language 'en-US', 'ko-KR', ...
- * @property {String} [options.direction=null] text direction, ex) 'rtl'
- * @property {Array} [options.toolbar]
- * @property {Boolean} [options.airMode=false]
- * @property {Array} [options.airPopover]
- * @property {Fucntion} [options.onInit] initialize
- * @property {Fucntion} [options.onsubmit]
- */
- options: {
- width: null, // set editor width
- height: null, // set editor height, ex) 300
-
- minHeight: null, // set minimum height of editor
- maxHeight: null, // set maximum height of editor
-
- focus: false, // set focus to editable area after initializing summernote
-
- tabsize: 4, // size of tab ex) 2 or 4
- styleWithSpan: true, // style with span (Chrome and FF only)
-
- disableLinkTarget: false, // hide link Target Checkbox
- disableDragAndDrop: false, // disable drag and drop event
- disableResizeEditor: false, // disable resizing editor
- disableResizeImage: false, // disable resizing image
-
- shortcuts: true, // enable keyboard shortcuts
-
- textareaAutoSync: true, // enable textarea auto sync
-
- placeholder: false, // enable placeholder text
- prettifyHtml: true, // enable prettifying html while toggling codeview
-
- iconPrefix: 'fa fa-', // prefix for css icon classes
-
- icons: {
- font: {
- bold: 'bold',
- italic: 'italic',
- underline: 'underline',
- clear: 'eraser',
- height: 'text-height',
- strikethrough: 'strikethrough',
- superscript: 'superscript',
- subscript: 'subscript'
- },
- image: {
- image: 'picture-o',
- floatLeft: 'align-left',
- floatRight: 'align-right',
- floatNone: 'align-justify',
- shapeRounded: 'square',
- shapeCircle: 'circle-o',
- shapeThumbnail: 'picture-o',
- shapeNone: 'times',
- remove: 'trash-o'
- },
- link: {
- link: 'link',
- unlink: 'unlink',
- edit: 'edit'
- },
- table: {
- table: 'table'
- },
- hr: {
- insert: 'minus'
- },
- style: {
- style: 'magic'
- },
- lists: {
- unordered: 'list-ul',
- ordered: 'list-ol'
- },
- options: {
- help: 'question',
- fullscreen: 'arrows-alt',
- codeview: 'code'
- },
- paragraph: {
- paragraph: 'align-left',
- outdent: 'outdent',
- indent: 'indent',
- left: 'align-left',
- center: 'align-center',
- right: 'align-right',
- justify: 'align-justify'
- },
- color: {
- recent: 'font'
- },
- history: {
- undo: 'undo',
- redo: 'repeat'
- },
- misc: {
- check: 'check'
- }
- },
-
- dialogsInBody: false, // false will add dialogs into editor
-
- codemirror: { // codemirror options
- mode: 'text/html',
- htmlMode: true,
- lineNumbers: true
- },
-
- // language
- lang: 'en-US', // language 'en-US', 'ko-KR', ...
- direction: null, // text direction, ex) 'rtl'
-
- // toolbar
- toolbar: [
- ['style', ['style']],
- ['font', ['bold', 'italic', 'underline', 'clear']],
- // ['font', ['bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'clear']],
- ['fontname', ['fontname']],
- ['fontsize', ['fontsize']],
- ['color', ['color']],
- ['para', ['ul', 'ol', 'paragraph']],
- ['height', ['height']],
- ['table', ['table']],
- ['insert', ['link', 'picture', 'hr']],
- ['view', ['fullscreen', 'codeview']],
- ['help', ['help']]
- ],
-
- plugin : { },
-
- // air mode: inline editor
- airMode: false,
- // airPopover: [
- // ['style', ['style']],
- // ['font', ['bold', 'italic', 'underline', 'clear']],
- // ['fontname', ['fontname']],
- // ['color', ['color']],
- // ['para', ['ul', 'ol', 'paragraph']],
- // ['height', ['height']],
- // ['table', ['table']],
- // ['insert', ['link', 'picture']],
- // ['help', ['help']]
- // ],
- airPopover: [
- ['color', ['color']],
- ['font', ['bold', 'underline', 'clear']],
- ['para', ['ul', 'paragraph']],
- ['table', ['table']],
- ['insert', ['link', 'picture']]
- ],
-
- // style tag
- styleTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
-
- // default fontName
- defaultFontName: 'Helvetica Neue',
-
- // fontName
- fontNames: [
- 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
- 'Helvetica Neue', 'Helvetica', 'Impact', 'Lucida Grande',
- 'Tahoma', 'Times New Roman', 'Verdana'
- ],
- fontNamesIgnoreCheck: [],
-
- fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
-
- // pallete colors(n x n)
- colors: [
- ['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
- ['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
- ['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
- ['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
- ['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
- ['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
- ['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
- ['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
- ],
-
- // lineHeight
- lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
-
- // insertTable max size
- insertTableMaxSize: {
- col: 10,
- row: 10
- },
-
- // image
- maximumImageFileSize: null, // size in bytes, null = no limit
-
- // callbacks
- oninit: null, // initialize
- onfocus: null, // editable has focus
- onblur: null, // editable out of focus
- onenter: null, // enter key pressed
- onkeyup: null, // keyup
- onkeydown: null, // keydown
- onImageUpload: null, // imageUpload
- onImageUploadError: null, // imageUploadError
- onMediaDelete: null, // media delete
- onToolbarClick: null,
- onsubmit: null,
-
- /**
- * manipulate link address when user create link
- * @param {String} sLinkUrl
- * @return {String}
- */
- onCreateLink: function (sLinkUrl) {
- if (sLinkUrl.indexOf('@') !== -1 && sLinkUrl.indexOf(':') === -1) {
- sLinkUrl = 'mailto:' + sLinkUrl;
- }
-
- return sLinkUrl;
- },
-
- keyMap: {
- pc: {
- 'ENTER': 'insertParagraph',
- 'CTRL+Z': 'undo',
- 'CTRL+Y': 'redo',
- 'TAB': 'tab',
- 'SHIFT+TAB': 'untab',
- 'CTRL+B': 'bold',
- 'CTRL+I': 'italic',
- 'CTRL+U': 'underline',
- 'CTRL+SHIFT+S': 'strikethrough',
- 'CTRL+BACKSLASH': 'removeFormat',
- 'CTRL+SHIFT+L': 'justifyLeft',
- 'CTRL+SHIFT+E': 'justifyCenter',
- 'CTRL+SHIFT+R': 'justifyRight',
- 'CTRL+SHIFT+J': 'justifyFull',
- 'CTRL+SHIFT+NUM7': 'insertUnorderedList',
- 'CTRL+SHIFT+NUM8': 'insertOrderedList',
- 'CTRL+LEFTBRACKET': 'outdent',
- 'CTRL+RIGHTBRACKET': 'indent',
- 'CTRL+NUM0': 'formatPara',
- 'CTRL+NUM1': 'formatH1',
- 'CTRL+NUM2': 'formatH2',
- 'CTRL+NUM3': 'formatH3',
- 'CTRL+NUM4': 'formatH4',
- 'CTRL+NUM5': 'formatH5',
- 'CTRL+NUM6': 'formatH6',
- 'CTRL+ENTER': 'insertHorizontalRule',
- 'CTRL+K': 'showLinkDialog'
- },
-
- mac: {
- 'ENTER': 'insertParagraph',
- 'CMD+Z': 'undo',
- 'CMD+SHIFT+Z': 'redo',
- 'TAB': 'tab',
- 'SHIFT+TAB': 'untab',
- 'CMD+B': 'bold',
- 'CMD+I': 'italic',
- 'CMD+U': 'underline',
- 'CMD+SHIFT+S': 'strikethrough',
- 'CMD+BACKSLASH': 'removeFormat',
- 'CMD+SHIFT+L': 'justifyLeft',
- 'CMD+SHIFT+E': 'justifyCenter',
- 'CMD+SHIFT+R': 'justifyRight',
- 'CMD+SHIFT+J': 'justifyFull',
- 'CMD+SHIFT+NUM7': 'insertUnorderedList',
- 'CMD+SHIFT+NUM8': 'insertOrderedList',
- 'CMD+LEFTBRACKET': 'outdent',
- 'CMD+RIGHTBRACKET': 'indent',
- 'CMD+NUM0': 'formatPara',
- 'CMD+NUM1': 'formatH1',
- 'CMD+NUM2': 'formatH2',
- 'CMD+NUM3': 'formatH3',
- 'CMD+NUM4': 'formatH4',
- 'CMD+NUM5': 'formatH5',
- 'CMD+NUM6': 'formatH6',
- 'CMD+ENTER': 'insertHorizontalRule',
- 'CMD+K': 'showLinkDialog'
- }
- }
- },
-
- // default language: en-US
- lang: {
- 'en-US': {
- font: {
- bold: 'Bold',
- italic: 'Italic',
- underline: 'Underline',
- clear: 'Remove Font Style',
- height: 'Line Height',
- name: 'Font Family',
- strikethrough: 'Strikethrough',
- subscript: 'Subscript',
- superscript: 'Superscript',
- size: 'Font Size'
- },
- image: {
- image: 'Picture',
- insert: 'Insert Image',
- resizeFull: 'Resize Full',
- resizeHalf: 'Resize Half',
- resizeQuarter: 'Resize Quarter',
- floatLeft: 'Float Left',
- floatRight: 'Float Right',
- floatNone: 'Float None',
- shapeRounded: 'Shape: Rounded',
- shapeCircle: 'Shape: Circle',
- shapeThumbnail: 'Shape: Thumbnail',
- shapeNone: 'Shape: None',
- dragImageHere: 'Drag image or text here',
- dropImage: 'Drop image or Text',
- selectFromFiles: 'Select from files',
- maximumFileSize: 'Maximum file size',
- maximumFileSizeError: 'Maximum file size exceeded.',
- url: 'Image URL',
- remove: 'Remove Image'
- },
- link: {
- link: 'Link',
- insert: 'Insert Link',
- unlink: 'Unlink',
- edit: 'Edit',
- textToDisplay: 'Text to display',
- url: 'To what URL should this link go?',
- openInNewWindow: 'Open in new window'
- },
- table: {
- table: 'Table'
- },
- hr: {
- insert: 'Insert Horizontal Rule'
- },
- style: {
- style: 'Style',
- normal: 'Paragraph',
- blockquote: 'Quote',
- pre: 'Code',
- h1: 'Header 1',
- h2: 'Header 2',
- h3: 'Header 3',
- h4: 'Header 4',
- h5: 'Header 5',
- h6: 'Header 6'
- },
- lists: {
- unordered: 'Unordered list',
- ordered: 'Ordered list'
- },
- options: {
- help: 'Help',
- fullscreen: 'Full Screen',
- codeview: 'Code View'
- },
- paragraph: {
- paragraph: 'Paragraph',
- outdent: 'Outdent',
- indent: 'Indent',
- left: 'Align left',
- center: 'Align center',
- right: 'Align right',
- justify: 'Justify full'
- },
- color: {
- recent: 'Recent Color',
- more: 'More Color',
- background: 'Background Color',
- foreground: 'Foreground Color',
- transparent: 'Transparent',
- setTransparent: 'Set transparent',
- reset: 'Reset',
- resetToDefault: 'Reset to default'
- },
- shortcut: {
- shortcuts: 'Keyboard shortcuts',
- close: 'Close',
- textFormatting: 'Text formatting',
- action: 'Action',
- paragraphFormatting: 'Paragraph formatting',
- documentStyle: 'Document Style',
- extraKeys: 'Extra keys'
- },
- history: {
- undo: 'Undo',
- redo: 'Redo'
- }
- }
- }
- };
-
- return defaults;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js b/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js
deleted file mode 100644
index 6a115edaa90..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/editing/Bullet.js
+++ /dev/null
@@ -1,235 +0,0 @@
-define([
- 'summernote/core/list',
- 'summernote/core/func',
- 'summernote/core/dom',
- 'summernote/core/range'
-], function (list, func, dom, range) {
-
- /**
- * @class editing.Bullet
- *
- * @alternateClassName Bullet
- */
- var Bullet = function () {
- /**
- * @method insertOrderedList
- *
- * toggle ordered list
- *
- * @type command
- */
- this.insertOrderedList = function () {
- this.toggleList('OL');
- };
-
- /**
- * @method insertUnorderedList
- *
- * toggle unordered list
- *
- * @type command
- */
- this.insertUnorderedList = function () {
- this.toggleList('UL');
- };
-
- /**
- * @method indent
- *
- * indent
- *
- * @type command
- */
- this.indent = function () {
- var self = this;
- var rng = range.create().wrapBodyInlineWithPara();
-
- var paras = rng.nodes(dom.isPara, { includeAncestor: true });
- var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
-
- $.each(clustereds, function (idx, paras) {
- var head = list.head(paras);
- if (dom.isLi(head)) {
- self.wrapList(paras, head.parentNode.nodeName);
- } else {
- $.each(paras, function (idx, para) {
- $(para).css('marginLeft', function (idx, val) {
- return (parseInt(val, 10) || 0) + 25;
- });
- });
- }
- });
-
- rng.select();
- };
-
- /**
- * @method outdent
- *
- * outdent
- *
- * @type command
- */
- this.outdent = function () {
- var self = this;
- var rng = range.create().wrapBodyInlineWithPara();
-
- var paras = rng.nodes(dom.isPara, { includeAncestor: true });
- var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
-
- $.each(clustereds, function (idx, paras) {
- var head = list.head(paras);
- if (dom.isLi(head)) {
- self.releaseList([paras]);
- } else {
- $.each(paras, function (idx, para) {
- $(para).css('marginLeft', function (idx, val) {
- val = (parseInt(val, 10) || 0);
- return val > 25 ? val - 25 : '';
- });
- });
- }
- });
-
- rng.select();
- };
-
- /**
- * @method toggleList
- *
- * toggle list
- *
- * @param {String} listName - OL or UL
- */
- this.toggleList = function (listName) {
- var self = this;
- var rng = range.create().wrapBodyInlineWithPara();
-
- var paras = rng.nodes(dom.isPara, { includeAncestor: true });
- var bookmark = rng.paraBookmark(paras);
- var clustereds = list.clusterBy(paras, func.peq2('parentNode'));
-
- // paragraph to list
- if (list.find(paras, dom.isPurePara)) {
- var wrappedParas = [];
- $.each(clustereds, function (idx, paras) {
- wrappedParas = wrappedParas.concat(self.wrapList(paras, listName));
- });
- paras = wrappedParas;
- // list to paragraph or change list style
- } else {
- var diffLists = rng.nodes(dom.isList, {
- includeAncestor: true
- }).filter(function (listNode) {
- return !$.nodeName(listNode, listName);
- });
-
- if (diffLists.length) {
- $.each(diffLists, function (idx, listNode) {
- dom.replace(listNode, listName);
- });
- } else {
- paras = this.releaseList(clustereds, true);
- }
- }
-
- range.createFromParaBookmark(bookmark, paras).select();
- };
-
- /**
- * @method wrapList
- *
- * @param {Node[]} paras
- * @param {String} listName
- * @return {Node[]}
- */
- this.wrapList = function (paras, listName) {
- var head = list.head(paras);
- var last = list.last(paras);
-
- var prevList = dom.isList(head.previousSibling) && head.previousSibling;
- var nextList = dom.isList(last.nextSibling) && last.nextSibling;
-
- var listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
-
- // P to LI
- paras = paras.map(function (para) {
- return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
- });
-
- // append to list(<ul>, <ol>)
- dom.appendChildNodes(listNode, paras);
-
- if (nextList) {
- dom.appendChildNodes(listNode, list.from(nextList.childNodes));
- dom.remove(nextList);
- }
-
- return paras;
- };
-
- /**
- * @method releaseList
- *
- * @param {Array[]} clustereds
- * @param {Boolean} isEscapseToBody
- * @return {Node[]}
- */
- this.releaseList = function (clustereds, isEscapseToBody) {
- var releasedParas = [];
-
- $.each(clustereds, function (idx, paras) {
- var head = list.head(paras);
- var last = list.last(paras);
-
- var headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) :
- head.parentNode;
- var lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
- node: last.parentNode,
- offset: dom.position(last) + 1
- }, {
- isSkipPaddingBlankHTML: true
- }) : null;
-
- var middleList = dom.splitTree(headList, {
- node: head.parentNode,
- offset: dom.position(head)
- }, {
- isSkipPaddingBlankHTML: true
- });
-
- paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi) :
- list.from(middleList.childNodes).filter(dom.isLi);
-
- // LI to P
- if (isEscapseToBody || !dom.isList(headList.parentNode)) {
- paras = paras.map(function (para) {
- return dom.replace(para, 'P');
- });
- }
-
- $.each(list.from(paras).reverse(), function (idx, para) {
- dom.insertAfter(para, headList);
- });
-
- // remove empty lists
- var rootLists = list.compact([headList, middleList, lastList]);
- $.each(rootLists, function (idx, rootList) {
- var listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
- $.each(listNodes.reverse(), function (idx, listNode) {
- if (!dom.nodeLength(listNode)) {
- dom.remove(listNode, true);
- }
- });
- });
-
- releasedParas = releasedParas.concat(paras);
- });
-
- return releasedParas;
- };
- };
-
- return Bullet;
-});
-
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/History.js b/addons/web_editor/static/lib/summernote/src/js/editing/History.js
deleted file mode 100644
index 2cc946488c2..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/editing/History.js
+++ /dev/null
@@ -1,118 +0,0 @@
-define(['summernote/core/range', 'summernote/core/dom'], function (range, dom) { // ODOO: suggest upstream
- /**
- * @class editing.History
- *
- * Editor History
- *
- */
- var History = function ($editable) {
- var stack = [], stackOffset = -1;
- var editable = $editable[0];
-
- var makeSnapshot = function () {
- var rng = range.create();
- var emptyBookmark = {s: {path: [], offset: 0}, e: {path: [], offset: 0}};
-
- return {
- contents: $editable.html(),
- bookmark: (rng && dom.ancestor(rng.sc, dom.isEditable) ? rng.bookmark(editable) : emptyBookmark)
- // ODOO: suggest upstream added " && dom.ancestor(rng.sc, dom.isEditable) "
- };
- };
-
- var applySnapshot = function (snapshot) {
- if (snapshot.contents !== null) {
- $editable.html(snapshot.contents);
- }
- if (snapshot.bookmark !== null) {
- range.createFromBookmark(editable, snapshot.bookmark).select();
- }
- };
-
- /**
- * undo
- */
- this.undo = function () {
- // Create snap shot if not yet recorded
- if ($editable.html() !== stack[stackOffset].contents) {
- this.recordUndo();
- }
-
- if (0 < stackOffset) {
- stackOffset--;
- applySnapshot(stack[stackOffset]);
- }
- };
-
- /* ODOO: to suggest upstream */
- this.hasUndo = function () {
- return 0 < stackOffset;
- };
-
- /**
- * redo
- */
- this.redo = function () {
- if (stack.length - 1 > stackOffset) {
- stackOffset++;
- applySnapshot(stack[stackOffset]);
- }
- };
-
- /* ODOO: to suggest upstream */
- this.hasRedo = function () {
- return stack.length - 1 > stackOffset;
- };
-
- var last; // ODOO: to suggest upstream (since we may have several editor)
- /**
- * recorded undo
- */
- this.recordUndo = function () {
- // ODOO: method totally rewritten
- // test event for firefox: remove stack of history because event doesn't exists
- var key = typeof event !== 'undefined' ? event : false;
- if (key && !event.metaKey && !event.ctrlKey && !event.altKey && event.type === "keydown") {
- key = event.type + "-";
- if (event.which === 8 || event.which === 46) key += 'delete';
- else if (event.which === 13) key += 'enter';
- else key += 'other';
- if (key === last) return;
- hasUndo = true;
- }
- last = key;
-
- // Wash out stack after stackOffset
- if (stack.length > stackOffset+1) {
- stack = stack.slice(0, stackOffset+1);
- }
-
- if (stack[stackOffset] && stack[stackOffset].contents === $editable.html()) {
- return;
- }
-
- stackOffset++;
-
- // Create new snapshot and push it to the end
- stack.push(makeSnapshot());
- };
-
- /* ODOO: to suggest upstream */
- this.splitNext = function () {
- last = false;
- };
-
- /* ODOO: to suggest upstream */
- this.reset = function () {
- last = false;
- stack = [];
- stackOffset = -1;
- this.recordUndo();
- };
-
- // Create first undo stack
- this.recordUndo();
- };
-
- return History;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Style.js b/addons/web_editor/static/lib/summernote/src/js/editing/Style.js
deleted file mode 100644
index bf749cd817b..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/editing/Style.js
+++ /dev/null
@@ -1,161 +0,0 @@
-define([
- 'summernote/core/agent',
- 'summernote/core/func',
- 'summernote/core/list',
- 'summernote/core/dom'
-], function (agent, func, list, dom) {
- /**
- * @class editing.Style
- *
- * Style
- *
- */
- var Style = function () {
- /**
- * @method jQueryCSS
- *
- * [workaround] for old jQuery
- * passing an array of style properties to .css()
- * will result in an object of property-value pairs.
- * (compability with version < 1.9)
- *
- * @private
- * @param {jQuery} $obj
- * @param {Array} propertyNames - An array of one or more CSS properties.
- * @return {Object}
- */
- var jQueryCSS = function ($obj, propertyNames) {
- if (agent.jqueryVersion < 1.9) {
- var result = {};
- $.each(propertyNames, function (idx, propertyName) {
- result[propertyName] = $obj.css(propertyName);
- });
- return result;
- }
- return $obj.css.call($obj, propertyNames);
- };
-
- /**
- * returns style object from node
- *
- * @param {jQuery} $node
- * @return {Object}
- */
- this.fromNode = function ($node) {
- var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
- var styleInfo = jQueryCSS($node, properties) || {};
- styleInfo['font-size'] = parseInt(styleInfo['font-size'], 10);
- return styleInfo;
- };
-
- /**
- * paragraph level style
- *
- * @param {WrappedRange} rng
- * @param {Object} styleInfo
- */
- this.stylePara = function (rng, styleInfo) {
- $.each(rng.nodes(dom.isPara, {
- includeAncestor: true
- }), function (idx, para) {
- $(para).css(styleInfo);
- });
- };
-
- /**
- * insert and returns styleNodes on range.
- *
- * @param {WrappedRange} rng
- * @param {Object} [options] - options for styleNodes
- * @param {String} [options.nodeName] - default: `SPAN`
- * @param {Boolean} [options.expandClosestSibling] - default: `false`
- * @param {Boolean} [options.onlyPartialContains] - default: `false`
- * @return {Node[]}
- */
- this.styleNodes = function (rng, options) {
- rng = rng.splitText();
-
- var nodeName = options && options.nodeName || 'SPAN';
- var expandClosestSibling = !!(options && options.expandClosestSibling);
- var onlyPartialContains = !!(options && options.onlyPartialContains);
-
- if (rng.isCollapsed()) {
- return [rng.insertNode(dom.create(nodeName))];
- }
-
- var pred = dom.makePredByNodeName(nodeName);
- var nodes = rng.nodes(dom.isText, {
- fullyContains: true
- }).map(function (text) {
- return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
- });
-
- if (expandClosestSibling) {
- if (onlyPartialContains) {
- var nodesInRange = rng.nodes();
- // compose with partial contains predication
- pred = func.and(pred, function (node) {
- return list.contains(nodesInRange, node);
- });
- }
-
- return nodes.map(function (node) {
- var siblings = dom.withClosestSiblings(node, pred);
- var head = list.head(siblings);
- var tails = list.tail(siblings);
- $.each(tails, function (idx, elem) {
- dom.appendChildNodes(head, elem.childNodes);
- dom.remove(elem);
- });
- return list.head(siblings);
- });
- } else {
- return nodes;
- }
- };
-
- /**
- * get current style on cursor
- *
- * @param {WrappedRange} rng
- * @return {Object} - object contains style properties.
- */
- this.current = function (rng) {
- var $cont = $(dom.isText(rng.sc) ? rng.sc.parentNode : rng.sc);
- var styleInfo = this.fromNode($cont);
-
- // document.queryCommandState for toggle state
- styleInfo['font-bold'] = document.queryCommandState('bold') ? 'bold' : 'normal';
- styleInfo['font-italic'] = document.queryCommandState('italic') ? 'italic' : 'normal';
- styleInfo['font-underline'] = document.queryCommandState('underline') ? 'underline' : 'normal';
- styleInfo['font-strikethrough'] = document.queryCommandState('strikeThrough') ? 'strikethrough' : 'normal';
- styleInfo['font-superscript'] = document.queryCommandState('superscript') ? 'superscript' : 'normal';
- styleInfo['font-subscript'] = document.queryCommandState('subscript') ? 'subscript' : 'normal';
-
- // list-style-type to list-style(unordered, ordered)
- if (!rng.isOnList()) {
- styleInfo['list-style'] = 'none';
- } else {
- var aOrderedType = ['circle', 'disc', 'disc-leading-zero', 'square'];
- var isUnordered = $.inArray(styleInfo['list-style-type'], aOrderedType) > -1;
- styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
- }
-
- var para = dom.ancestor(rng.sc, dom.isPara);
- if (para && para.style['line-height']) {
- styleInfo['line-height'] = para.style.lineHeight;
- } else {
- var lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
- styleInfo['line-height'] = lineHeight.toFixed(1);
- }
-
- styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
- styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
- styleInfo.range = rng;
-
- return styleInfo;
- };
- };
-
- return Style;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Table.js b/addons/web_editor/static/lib/summernote/src/js/editing/Table.js
deleted file mode 100644
index 634d5a79d7e..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/editing/Table.js
+++ /dev/null
@@ -1,52 +0,0 @@
-define([
- 'summernote/core/dom', 'summernote/core/range', 'summernote/core/list'
-], function (dom, range, list) {
- /**
- * @class editing.Table
- *
- * Table
- *
- */
- var Table = function () {
- /**
- * handle tab key
- *
- * @param {WrappedRange} rng
- * @param {Boolean} isShift
- */
- this.tab = function (rng, isShift) {
- var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
- var table = dom.ancestor(cell, dom.isTable);
- var cells = dom.listDescendant(table, dom.isCell);
-
- var nextCell = list[isShift ? 'prev' : 'next'](cells, cell);
- if (nextCell) {
- range.create(nextCell, 0).select();
- }
- };
-
- /**
- * create empty table element
- *
- * @param {Number} rowCount
- * @param {Number} colCount
- * @return {Node}
- */
- this.createTable = function (colCount, rowCount) {
- var tds = [], tdHTML;
- for (var idxCol = 0; idxCol < colCount; idxCol++) {
- tds.push('<td>' + dom.blank + '</td>');
- }
- tdHTML = tds.join('');
-
- var trs = [], trHTML;
- for (var idxRow = 0; idxRow < rowCount; idxRow++) {
- trs.push('<tr>' + tdHTML + '</tr>');
- }
- trHTML = trs.join('');
- return $('<table class="table table-bordered">' + trHTML + '</table>')[0];
- };
- };
- return Table;
-});
-
diff --git a/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js b/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js
deleted file mode 100644
index f95eba9e9f3..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/editing/Typing.js
+++ /dev/null
@@ -1,86 +0,0 @@
-define([
- 'summernote/core/dom',
- 'summernote/core/range',
- 'summernote/editing/Bullet'
-], function (dom, range, Bullet) {
-
- /**
- * @class editing.Typing
- *
- * Typing
- *
- */
- var Typing = function () {
-
- // a Bullet instance to toggle lists off
- var bullet = new Bullet();
-
- /**
- * insert tab
- *
- * @param {jQuery} $editable
- * @param {WrappedRange} rng
- * @param {Number} tabsize
- */
- this.insertTab = function ($editable, rng, tabsize) {
- var tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
- rng = rng.deleteContents();
- rng.insertNode(tab, true);
-
- rng = range.create(tab, tabsize);
- rng.select();
- };
-
- /**
- * insert paragraph
- */
- this.insertParagraph = function () {
- var rng = range.create();
-
- // deleteContents on range.
- rng = rng.deleteContents();
-
- // Wrap range if it needs to be wrapped by paragraph
- rng = rng.wrapBodyInlineWithPara();
-
- // finding paragraph
- var splitRoot = dom.ancestor(rng.sc, dom.isPara);
-
- var nextPara;
- // on paragraph: split paragraph
- if (splitRoot) {
- // if it is an empty line with li
- if (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
- // disable UL/OL and escape!
- bullet.toggleList(splitRoot.parentNode.nodeName);
- return;
- // if new line has content (not a line break)
- } else {
- nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
-
- var emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
- emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
-
- $.each(emptyAnchors, function (idx, anchor) {
- dom.remove(anchor);
- });
- }
- // no paragraph: insert empty paragraph
- } else {
- var next = rng.sc.childNodes[rng.so];
- nextPara = $(dom.emptyPara)[0];
- if (next) {
- rng.sc.insertBefore(nextPara, next);
- } else {
- rng.sc.appendChild(nextPara);
- }
- }
-
- range.create(nextPara, 0).normalize().select();
-
- };
-
- };
-
- return Typing;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/intro.js b/addons/web_editor/static/lib/summernote/src/js/intro.js
deleted file mode 100644
index 357132eb110..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/intro.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Super simple wysiwyg editor on Bootstrap v@VERSION
- * http://summernote.org/
- *
- * summernote.js
- * Copyright 2013-2015 Alan Hong. and other contributors
- * summernote may be freely distributed under the MIT license./
- *
- * Date: @DATE
- */
-(function (factory) {
- /* global define */
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals: jQuery
- factory(window.jQuery);
- }
-}(function ($) {
- 'use strict';
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
deleted file mode 100644
index c6d28494cd0..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Button.js
+++ /dev/null
@@ -1,169 +0,0 @@
-define([
- 'summernote/core/list',
- 'summernote/core/agent'
-], function (list, agent) {
- /**
- * @class module.Button
- *
- * Button
- */
- var Button = function () {
- /**
- * update button status
- *
- * @param {jQuery} $container
- * @param {Object} styleInfo
- */
- this.update = function ($container, styleInfo) {
- /**
- * handle dropdown's check mark (for fontname, fontsize, lineHeight).
- * @param {jQuery} $btn
- * @param {Number} value
- */
- var checkDropdownMenu = function ($btn, value) {
- $btn.find('.dropdown-menu .dropdown-item').each(function () {
- // always compare string to avoid creating another func.
- var isChecked = ($(this).data('value') + '') === (value + '');
- this.className = 'dropdown-item' + (isChecked ? ' checked' : '');
- });
- };
-
- /**
- * update button state(active or not).
- *
- * @private
- * @param {String} selector
- * @param {Function} pred
- */
- var btnState = function (selector, pred) {
- var $btn = $container.find(selector);
- $btn.toggleClass('active', pred());
- };
-
- if (styleInfo.image) {
- var $img = $(styleInfo.image);
-
- btnState('button[data-event="imageShape"][data-value="rounded"]', function () {
- return $img.hasClass('rounded');
- });
- btnState('button[data-event="imageShape"][data-value="rounded-circle"]', function () {
- return $img.hasClass('rounded-circle');
- });
- btnState('button[data-event="imageShape"][data-value="img-thumbnail"]', function () {
- return $img.hasClass('img-thumbnail');
- });
- btnState('button[data-event="imageShape"]:not([data-value])', function () {
- return !$img.is('.rounded, .rounded-circle, .img-thumbnail');
- });
-
- var imgFloat = $img.css('float');
- btnState('button[data-event="floatMe"][data-value="left"]', function () {
- return imgFloat === 'left';
- });
- btnState('button[data-event="floatMe"][data-value="right"]', function () {
- return imgFloat === 'right';
- });
- btnState('button[data-event="floatMe"][data-value="none"]', function () {
- return imgFloat !== 'left' && imgFloat !== 'right';
- });
-
- var style = $img.attr('style');
- btnState('button[data-event="resize"][data-value="1"]', function () {
- return !!/(^|\s)(max-)?width\s*:\s*100%/.test(style);
- });
- btnState('button[data-event="resize"][data-value="0.5"]', function () {
- return !!/(^|\s)(max-)?width\s*:\s*50%/.test(style);
- });
- btnState('button[data-event="resize"][data-value="0.25"]', function () {
- return !!/(^|\s)(max-)?width\s*:\s*25%/.test(style);
- });
- return;
- }
-
- // fontname
- var $fontname = $container.find('.note-fontname');
- if ($fontname.length) {
- var selectedFont = styleInfo['font-family'];
- if (!!selectedFont) {
-
- 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);
-
- }
- }
-
- // fontsize
- var $fontsize = $container.find('.note-fontsize');
- $fontsize.find('.note-current-fontsize').text(styleInfo['font-size']);
- checkDropdownMenu($fontsize, parseFloat(styleInfo['font-size']));
-
- // lineheight
- var $lineHeight = $container.find('.note-height');
- checkDropdownMenu($lineHeight, parseFloat(styleInfo['line-height']));
-
- btnState('button[data-event="bold"]', function () {
- return styleInfo['font-bold'] === 'bold';
- });
- btnState('button[data-event="italic"]', function () {
- return styleInfo['font-italic'] === 'italic';
- });
- btnState('button[data-event="underline"]', function () {
- return styleInfo['font-underline'] === 'underline';
- });
- btnState('button[data-event="strikethrough"]', function () {
- return styleInfo['font-strikethrough'] === 'strikethrough';
- });
- btnState('button[data-event="superscript"]', function () {
- return styleInfo['font-superscript'] === 'superscript';
- });
- btnState('button[data-event="subscript"]', function () {
- return styleInfo['font-subscript'] === 'subscript';
- });
- btnState('button[data-event="justifyLeft"]', function () {
- return styleInfo['text-align'] === 'left' || styleInfo['text-align'] === 'start';
- });
- btnState('button[data-event="justifyCenter"]', function () {
- return styleInfo['text-align'] === 'center';
- });
- btnState('button[data-event="justifyRight"]', function () {
- return styleInfo['text-align'] === 'right';
- });
- btnState('button[data-event="justifyFull"]', function () {
- return styleInfo['text-align'] === 'justify';
- });
- btnState('button[data-event="insertUnorderedList"]', function () {
- return styleInfo['list-style'] === 'unordered';
- });
- btnState('button[data-event="insertOrderedList"]', function () {
- return styleInfo['list-style'] === 'ordered';
- });
- };
-
- /**
- * update recent color
- *
- * @param {Node} button
- * @param {String} eventName
- * @param {Mixed} value
- */
- this.updateRecentColor = function (button, eventName, value) {
- var $color = $(button).closest('.note-color');
- var $recentColor = $color.find('.note-recent-color');
- var colorInfo = JSON.parse($recentColor.attr('data-value'));
- colorInfo[eventName] = value;
- $recentColor.attr('data-value', JSON.stringify(colorInfo));
- var sKey = eventName === 'backColor' ? 'background-color' : 'color';
- $recentColor.find('i').css(sKey, value);
- };
- };
-
- return Button;
-});
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
deleted file mode 100644
index 4cd9088da75..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Clipboard.js
+++ /dev/null
@@ -1,41 +0,0 @@
-define([
- 'summernote/core/list',
- 'summernote/core/dom',
- 'summernote/core/key',
- 'summernote/core/agent',
- 'summernote/core/range'
-], function (list, dom, key, agent, range) {
- // ODOO override: use 0.8.10 version of this, adapted for the old summernote
- // version odoo is using
- var Clipboard = function (handler) {
- /**
- * paste by clipboard event
- *
- * @param {Event} event
- */
- var pasteByEvent = function (event) {
- if (["INPUT", "TEXTAREA"].indexOf(event.target.tagName) !== -1) {
- // ODOO override: from old summernote version
- return;
- }
-
- 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);
- }
- };
-
- this.attach = function (layoutInfo) {
- layoutInfo.editable().on('paste', pasteByEvent);
- };
- };
-
- 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
deleted file mode 100644
index 46f3d96ab12..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Codeview.js
+++ /dev/null
@@ -1,137 +0,0 @@
-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/DragAndDrop.js b/addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js
deleted file mode 100644
index b8871599c6f..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/DragAndDrop.js
+++ /dev/null
@@ -1,98 +0,0 @@
-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/Editor.js b/addons/web_editor/static/lib/summernote/src/js/module/Editor.js
deleted file mode 100644
index 0b63f315cf9..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Editor.js
+++ /dev/null
@@ -1,865 +0,0 @@
-define([
- 'summernote/core/agent',
- 'summernote/core/func',
- 'summernote/core/list',
- 'summernote/core/dom',
- 'summernote/core/range',
- 'summernote/core/async',
- 'summernote/editing/Style',
- 'summernote/editing/Typing',
- 'summernote/editing/Table',
- 'summernote/editing/Bullet'
-], function (agent, func, list, dom, range, async,
- Style, Typing, Table, Bullet) {
-
- var KEY_BOGUS = 'bogus';
-
- /**
- * @class editing.Editor
- *
- * Editor
- *
- */
- var Editor = function (handler) {
-
- var self = this;
- var style = new Style();
- var table = new Table();
- var typing = new Typing();
- var bullet = new Bullet();
-
- this.style = style; // ODOO: allow access for override
- this.table = table; // ODOO: allow access for override
- this.typing = typing; // ODOO: allow access for override
- this.bullet = bullet; // ODOO: allow access for override
-
- /**
- * @method createRange
- *
- * create range
- *
- * @param {jQuery} $editable
- * @return {WrappedRange}
- */
- this.createRange = function ($editable) {
- this.focus($editable);
- return range.create();
- };
-
- /**
- * @method saveRange
- *
- * save current range
- *
- * @param {jQuery} $editable
- * @param {Boolean} [thenCollapse=false]
- */
- this.saveRange = function ($editable, thenCollapse) {
- // ODOO: scroll to top when click on input in editable m (start_modification
- // this.focus($editable);
- var r = range.create();
- if (!r || ($editable[0] !== r.sc && !$.contains($editable[0], r.sc))) {
- $editable.focus();
- }
- // ODOO: end_modication)
- $editable.data('range', range.create());
- if (thenCollapse) {
- range.create().collapse().select();
- }
- };
-
- /**
- * @method saveRange
- *
- * save current node list to $editable.data('childNodes')
- *
- * @param {jQuery} $editable
- */
- this.saveNode = function ($editable) {
- // copy child node reference
- var copy = [];
- for (var key = 0, len = $editable[0].childNodes.length; key < len; key++) {
- copy.push($editable[0].childNodes[key]);
- }
- $editable.data('childNodes', copy);
- };
-
- /**
- * @method restoreRange
- *
- * restore lately range
- *
- * @param {jQuery} $editable
- */
- this.restoreRange = function ($editable) {
- var rng = $editable.data('range');
- if (rng) {
- rng.select();
- this.focus($editable);
- }
- };
-
- /**
- * @method restoreNode
- *
- * restore lately node list
- *
- * @param {jQuery} $editable
- */
- this.restoreNode = function ($editable) {
- $editable.html('');
- var child = $editable.data('childNodes');
- for (var index = 0, len = child.length; index < len; index++) {
- $editable[0].appendChild(child[index]);
- }
- };
-
- /**
- * @method currentStyle
- *
- * current style
- *
- * @param {Node} target
- * @return {Object|Boolean} unfocus
- */
- this.currentStyle = function (target) {
- var rng = range.create();
- var styleInfo = rng && rng.isOnEditable() ? style.current(rng.normalize()) : {};
- if (dom.isImg(target)) {
- styleInfo.image = target;
- }
- return styleInfo;
- };
-
- /**
- * style from node
- *
- * @param {jQuery} $node
- * @return {Object}
- */
- this.styleFromNode = function ($node) {
- return style.fromNode($node);
- };
-
- var triggerOnBeforeChange = function ($editable) {
- var $holder = dom.makeLayoutInfo($editable).holder();
- handler.bindCustomEvent(
- $holder, $editable.data('callbacks'), 'before.command'
- )($editable.html(), $editable);
- };
-
- var triggerOnChange = function ($editable) {
- var $holder = dom.makeLayoutInfo($editable).holder();
- handler.bindCustomEvent(
- $holder, $editable.data('callbacks'), 'change'
- )($editable.html(), $editable);
- };
-
- /**
- * @method undo
- * undo
- * @param {jQuery} $editable
- */
- this.undo = function ($editable) {
- triggerOnBeforeChange($editable);
- $editable.data('NoteHistory').undo();
- triggerOnChange($editable);
- };
-
- /**
- * @method redo
- * redo
- * @param {jQuery} $editable
- */
- this.redo = function ($editable) {
- triggerOnBeforeChange($editable);
- $editable.data('NoteHistory').redo();
- triggerOnChange($editable);
- };
-
- /**
- * @method beforeCommand
- * before command
- * @param {jQuery} $editable
- */
- var beforeCommand = this.beforeCommand = function ($editable) {
- triggerOnBeforeChange($editable);
- // keep focus on editable before command execution
- self.focus($editable);
- };
-
- /**
- * @method afterCommand
- * after command
- * @param {jQuery} $editable
- * @param {Boolean} isPreventTrigger
- */
- var afterCommand = this.afterCommand = function ($editable, isPreventTrigger) {
- $editable.data('NoteHistory').recordUndo();
- if (!isPreventTrigger) {
- triggerOnChange($editable);
- }
- };
-
- /**
- * @method bold
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method italic
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method underline
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method strikethrough
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method formatBlock
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method superscript
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method subscript
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method justifyLeft
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method justifyCenter
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method justifyRight
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method justifyFull
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method formatBlock
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method removeFormat
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method backColor
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method foreColor
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method insertHorizontalRule
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /**
- * @method fontName
- *
- * change font name
- *
- * @param {jQuery} $editable
- * @param {Mixed} value
- */
-
- /* jshint ignore:start */
- // native commands(with execCommand), generate function for execCommand
- var commands = ['bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
- 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
- 'formatBlock', 'removeFormat',
- 'backColor', 'foreColor', 'fontName'];
-
- for (var idx = 0, len = commands.length; idx < len; idx ++) {
- this[commands[idx]] = (function (sCmd) {
- return function ($editable, value) {
- beforeCommand($editable);
-
- document.execCommand(sCmd, false, value);
-
- afterCommand($editable, true);
- };
- })(commands[idx]);
- }
- /* jshint ignore:end */
-
- /**
- * @method tab
- *
- * handle tab key
- *
- * @param {jQuery} $editable
- * @param {Object} options
- */
- this.tab = function ($editable, options) {
- var rng = this.createRange($editable);
- if (rng.isCollapsed() && rng.isOnCell()) {
- table.tab(rng);
- } else {
- beforeCommand($editable);
- typing.insertTab($editable, rng, options.tabsize);
- afterCommand($editable);
- }
- };
-
- /**
- * @method untab
- *
- * handle shift+tab key
- *
- */
- this.untab = function ($editable) {
- var rng = this.createRange($editable);
- if (rng.isCollapsed() && rng.isOnCell()) {
- table.tab(rng, true);
- }
- };
-
- /**
- * @method insertParagraph
- *
- * insert paragraph
- *
- * @param {Node} $editable
- */
- this.insertParagraph = function ($editable) {
- beforeCommand($editable);
- typing.insertParagraph($editable);
- afterCommand($editable);
- };
-
- /**
- * @method insertOrderedList
- *
- * @param {jQuery} $editable
- */
- this.insertOrderedList = function ($editable) {
- beforeCommand($editable);
- bullet.insertOrderedList($editable);
- afterCommand($editable);
- };
-
- /**
- * @param {jQuery} $editable
- */
- this.insertUnorderedList = function ($editable) {
- beforeCommand($editable);
- bullet.insertUnorderedList($editable);
- afterCommand($editable);
- };
-
- /**
- * @param {jQuery} $editable
- */
- this.indent = function ($editable) {
- beforeCommand($editable);
- bullet.indent($editable);
- afterCommand($editable);
- };
-
- /**
- * @param {jQuery} $editable
- */
- this.outdent = function ($editable) {
- beforeCommand($editable);
- bullet.outdent($editable);
- afterCommand($editable);
- };
-
- /**
- * insert image
- *
- * @param {jQuery} $editable
- * @param {String} sUrl
- */
- this.insertImage = function ($editable, sUrl, filename) {
- async.createImage(sUrl, filename).then(function ($image) {
- beforeCommand($editable);
- $image.css({
- display: '',
- width: Math.min($editable.width(), $image.width())
- });
- range.create().insertNode($image[0]);
- range.createFromNodeAfter($image[0]).select();
- afterCommand($editable);
- }).fail(function () {
- var $holder = dom.makeLayoutInfo($editable).holder();
- handler.bindCustomEvent(
- $holder, $editable.data('callbacks'), 'image.upload.error'
- )();
- });
- };
-
- /**
- * @method insertNode
- * insert node
- * @param {Node} $editable
- * @param {Node} node
- */
- this.insertNode = function ($editable, node) {
- beforeCommand($editable);
- range.create().insertNode(node);
- range.createFromNodeAfter(node).select();
- afterCommand($editable);
- };
-
- /**
- * insert text
- * @param {Node} $editable
- * @param {String} text
- */
- this.insertText = function ($editable, text) {
- beforeCommand($editable);
- var textNode = range.create().insertNode(dom.createText(text));
- range.create(textNode, dom.nodeLength(textNode)).select();
- afterCommand($editable);
- };
-
- /**
- * paste HTML
- * @param {Node} $editable
- * @param {String} markup
- */
- this.pasteHTML = function ($editable, markup) {
- beforeCommand($editable);
- var contents = range.create().pasteHTML(markup);
- range.createFromNodeAfter(list.last(contents)).select();
- afterCommand($editable);
- };
-
- /**
- * formatBlock
- *
- * @param {jQuery} $editable
- * @param {String} tagName
- */
- this.formatBlock = function ($editable, tagName) {
- beforeCommand($editable);
- // [workaround] for MSIE, IE need `<`
- tagName = agent.isMSIE ? '<' + tagName + '>' : tagName;
- document.execCommand('FormatBlock', false, tagName);
- afterCommand($editable);
- };
-
- this.formatPara = function ($editable) {
- beforeCommand($editable);
- this.formatBlock($editable, 'P');
- afterCommand($editable);
- };
-
- /* jshint ignore:start */
- for (var idx = 1; idx <= 6; idx ++) {
- this['formatH' + idx] = function (idx) {
- return function ($editable) {
- this.formatBlock($editable, 'H' + idx);
- };
- }(idx);
- };
- /* jshint ignore:end */
-
- /**
- * fontSize
- *
- * @param {jQuery} $editable
- * @param {String} value - px
- */
- this.fontSize = function ($editable, value) {
- var rng = range.create();
-
- if (rng.isCollapsed()) {
- var spans = style.styleNodes(rng);
- var firstSpan = list.head(spans);
-
- $(spans).css({
- 'font-size': value + 'px'
- });
-
- // [workaround] added styled bogus span for style
- // - also bogus character needed for cursor position
- if (firstSpan && !dom.nodeLength(firstSpan)) {
- firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
- range.createFromNodeAfter(firstSpan.firstChild).select();
- $editable.data(KEY_BOGUS, firstSpan);
- }
- } else {
- beforeCommand($editable);
- $(style.styleNodes(rng)).css({
- 'font-size': value + 'px'
- });
- afterCommand($editable);
- }
- };
-
- /**
- * insert horizontal rule
- * @param {jQuery} $editable
- */
- this.insertHorizontalRule = function ($editable) {
- beforeCommand($editable);
-
- var rng = range.create();
- var hrNode = rng.insertNode($('<HR/>')[0]);
- if (hrNode.nextSibling) {
- range.create(hrNode.nextSibling, 0).normalize().select();
- }
-
- afterCommand($editable);
- };
-
- /**
- * remove bogus node and character
- */
- this.removeBogus = function ($editable) {
- var bogusNode = $editable.data(KEY_BOGUS);
- if (!bogusNode) {
- return;
- }
-
- var textNode = list.find(list.from(bogusNode.childNodes), dom.isText);
-
- var bogusCharIdx = textNode.nodeValue.indexOf(dom.ZERO_WIDTH_NBSP_CHAR);
- if (bogusCharIdx !== -1) {
- textNode.deleteData(bogusCharIdx, 1);
- }
-
- if (dom.isEmpty(bogusNode)) {
- dom.remove(bogusNode);
- }
-
- $editable.removeData(KEY_BOGUS);
- };
-
- /**
- * lineHeight
- * @param {jQuery} $editable
- * @param {String} value
- */
- this.lineHeight = function ($editable, value) {
- beforeCommand($editable);
- style.stylePara(range.create(), {
- lineHeight: value
- });
- afterCommand($editable);
- };
-
- /**
- * unlink
- *
- * @type command
- *
- * @param {jQuery} $editable
- */
- this.unlink = function ($editable) {
- var rng = this.createRange($editable);
- if (rng.isOnAnchor()) {
- var anchor = dom.ancestor(rng.sc, dom.isAnchor);
- rng = range.createFromNode(anchor);
- rng.select();
-
- beforeCommand($editable);
- document.execCommand('unlink');
- afterCommand($editable);
- }
- };
-
- /**
- * create link (command)
- *
- * @param {jQuery} $editable
- * @param {Object} linkInfo
- * @param {Object} options
- */
- this.createLink = function ($editable, linkInfo, options) {
- var linkUrl = linkInfo.url;
- var linkText = linkInfo.text;
- var isNewWindow = linkInfo.isNewWindow;
- var rng = linkInfo.range || this.createRange($editable);
- var isTextChanged = rng.toString() !== linkText;
-
- options = options || dom.makeLayoutInfo($editable).editor().data('options');
-
- beforeCommand($editable);
-
- if (options.onCreateLink) {
- linkUrl = options.onCreateLink(linkUrl);
- }
-
- var anchors = [];
- // ODOO: adding this branch to modify existing anchor if it fully contains the range
- var ancestor_anchor = dom.ancestor(rng.sc, dom.isAnchor);
- if(ancestor_anchor && ancestor_anchor === dom.ancestor(rng.ec, dom.isAnchor)) {
- anchors.push($(ancestor_anchor).html(linkText).get(0));
- } else if (isTextChanged) {
- // Create a new link when text changed.
- var anchor = rng.insertNode($('<A>' + linkText + '</A>')[0]);
- anchors.push(anchor);
- } else {
- anchors = style.styleNodes(rng, {
- nodeName: 'A',
- expandClosestSibling: true,
- onlyPartialContains: true
- });
- }
-
- $.each(anchors, function (idx, anchor) {
- $(anchor).attr('href', linkUrl);
- $(anchor).attr('class', linkInfo.className || null); // ODOO: addition
- $(anchor).css(linkInfo.style || {}); // ODOO: addition
- if (isNewWindow) {
- $(anchor).attr('target', '_blank');
- } else {
- $(anchor).removeAttr('target');
- }
- });
-
- var startRange = range.createFromNodeBefore(list.head(anchors));
- var startPoint = startRange.getStartPoint();
- var endRange = range.createFromNodeAfter(list.last(anchors));
- var endPoint = endRange.getEndPoint();
-
- range.create(
- startPoint.node,
- startPoint.offset,
- endPoint.node,
- endPoint.offset
- ).select();
-
- afterCommand($editable);
- };
-
- /**
- * returns link info
- *
- * @return {Object}
- * @return {WrappedRange} return.range
- * @return {String} return.text
- * @return {Boolean} [return.isNewWindow=true]
- * @return {String} [return.url=""]
- */
- this.getLinkInfo = function ($editable) {
- this.focus($editable);
-
- var rng = range.create().expand(dom.isAnchor);
-
- // Get the first anchor on range(for edit).
- var $anchor = $(list.head(rng.nodes(dom.isAnchor)));
-
- return {
- range: rng,
- text: rng.toString(),
- isNewWindow: $anchor.length ? $anchor.attr('target') === '_blank' : false,
- url: $anchor.length ? $anchor.attr('href') : ''
- };
- };
-
- /**
- * setting color
- *
- * @param {Node} $editable
- * @param {Object} sObjColor color code
- * @param {String} sObjColor.foreColor foreground color
- * @param {String} sObjColor.backColor background color
- */
- this.color = function ($editable, sObjColor) {
- var oColor = JSON.parse(sObjColor);
- var foreColor = oColor.foreColor, backColor = oColor.backColor;
-
- beforeCommand($editable);
-
- if (foreColor) { document.execCommand('foreColor', false, foreColor); }
- if (backColor) { document.execCommand('backColor', false, backColor); }
-
- afterCommand($editable);
- };
-
- /**
- * insert Table
- *
- * @param {Node} $editable
- * @param {String} sDim dimension of table (ex : "5x5")
- */
- this.insertTable = function ($editable, sDim) {
- var dimension = sDim.split('x');
- beforeCommand($editable);
-
- var rng = range.create().deleteContents();
- rng.insertNode(table.createTable(dimension[0], dimension[1]));
- afterCommand($editable);
- };
-
- /**
- * float me
- *
- * @param {jQuery} $editable
- * @param {String} value
- * @param {jQuery} $target
- */
- this.floatMe = function ($editable, value, $target) {
- beforeCommand($editable);
- // bootstrap
- $target.removeClass('float-left float-right');
- if (value && value !== 'none') {
- $target.addClass('pull-' + value);
- }
-
- // fallback for non-bootstrap
- $target.css('float', value);
- afterCommand($editable);
- };
-
- /**
- * change image shape
- *
- * @param {jQuery} $editable
- * @param {String} value css class
- * @param {Node} $target
- */
- this.imageShape = function ($editable, value, $target) {
- beforeCommand($editable);
-
- $target.removeClass('rounded rounded-circle img-thumbnail');
-
- if (value) {
- $target.addClass(value);
- }
-
- afterCommand($editable);
- };
-
- /**
- * resize overlay element
- * @param {jQuery} $editable
- * @param {String} value
- * @param {jQuery} $target - target element
- */
- this.resize = function ($editable, value, $target) {
- beforeCommand($editable);
-
- $target.css({
- width: value * 100 + '%',
- height: ''
- });
-
- afterCommand($editable);
- };
-
- /**
- * @param {Position} pos
- * @param {jQuery} $target - target element
- * @param {Boolean} [bKeepRatio] - keep ratio
- */
- this.resizeTo = function (pos, $target, bKeepRatio) {
- var imageSize;
- if (bKeepRatio) {
- var newRatio = pos.y / pos.x;
- var ratio = $target.data('ratio');
- imageSize = {
- width: ratio > newRatio ? pos.x : pos.y / ratio,
- height: ratio > newRatio ? pos.x * ratio : pos.y
- };
- } else {
- imageSize = {
- width: pos.x,
- height: pos.y
- };
- }
-
- $target.css(imageSize);
- };
-
- /**
- * remove media object
- *
- * @param {jQuery} $editable
- * @param {String} value - dummy argument (for keep interface)
- * @param {jQuery} $target - target element
- */
- this.removeMedia = function ($editable, value, $target) {
- beforeCommand($editable);
- $target.detach();
-
- handler.bindCustomEvent(
- $(), $editable.data('callbacks'), 'media.delete'
- )($target, $editable);
-
- afterCommand($editable);
- };
-
- /**
- * set focus
- *
- * @param $editable
- */
- this.focus = function ($editable) {
- $editable.focus();
-
- // [workaround] for firefox bug http://goo.gl/lVfAaI
- if (agent.isFF) {
- var rng = range.create();
- if (!rng || rng.isOnEditable()) {
- return;
- }
-
- range.createFromNode($editable[0])
- .normalize()
- .collapse()
- .select();
- }
- };
-
- /**
- * returns whether contents is empty or not.
- *
- * @param {jQuery} $editable
- * @return {Boolean}
- */
- this.isEmpty = function ($editable) {
- return dom.isEmpty($editable[0]) || dom.emptyPara === $editable.html();
- };
- };
-
- return Editor;
-});
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
deleted file mode 100644
index fd837483b3b..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Fullscreen.js
+++ /dev/null
@@ -1,51 +0,0 @@
-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
deleted file mode 100644
index 3b62304d706..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Handle.js
+++ /dev/null
@@ -1,101 +0,0 @@
-define([
- 'summernote/core/dom'
-], function (dom) {
- /**
- * @class module.Handle
- *
- * Handle
- */
- 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
- * @param {Object} styleInfo
- * @param {Boolean} isAirMode
- */
- this.update = function ($handle, styleInfo, isAirMode) {
- var $selection = $handle.find('.note-control-selection');
- if (styleInfo.image) {
- var $image = $(styleInfo.image);
- var pos = isAirMode ? $image.offset() : $image.position();
-
- // include margin
- var imageSize = {
- w: $image.outerWidth(true),
- h: $image.outerHeight(true)
- };
-
- $selection.css({
- display: 'block',
- left: pos.left,
- top: pos.top,
- width: imageSize.w,
- height: imageSize.h
- }).data('target', styleInfo.image); // save current image element.
- var sizingText = imageSize.w + 'x' + imageSize.h;
- $selection.find('.note-control-selection-info').text(sizingText);
- } else {
- $selection.hide();
- }
- };
-
- /**
- * hide
- *
- * @param {jQuery} $handle
- */
- this.hide = function ($handle) {
- $handle.children().hide();
- };
- };
-
- return Handle;
-});
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
deleted file mode 100644
index e4ff7b775ed..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/HelpDialog.js
+++ /dev/null
@@ -1,35 +0,0 @@
-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
deleted file mode 100644
index 042921183cc..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/ImageDialog.js
+++ /dev/null
@@ -1,110 +0,0 @@
-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
deleted file mode 100644
index 045119ee68a..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/LinkDialog.js
+++ /dev/null
@@ -1,129 +0,0 @@
-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
deleted file mode 100644
index f669569f6ba..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Popover.js
+++ /dev/null
@@ -1,129 +0,0 @@
-define([
- 'summernote/core/func',
- 'summernote/core/list',
- '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: allow access for override
-
- /**
- * returns position from placeholder
- *
- * @private
- * @param {Node} placeholder
- * @param {Object} options
- * @param {Boolean} options.isAirMode
- * @return {Position}
- */
- 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 = isLeftTop ? 0 : $placeholder.outerHeight(true); // include margin
-
- // popover below placeholder.
- return {
- left: pos.left,
- top: pos.top + height
- };
- };
-
- /**
- * show popover
- *
- * @private
- * @param {jQuery} popover
- * @param {Position} pos
- */
- var showPopover = function ($popover, pos) {
- $popover.css({
- display: 'block',
- left: pos.left,
- top: pos.top
- });
- };
-
- var PX_POPOVER_ARROW_OFFSET_X = 20;
-
- /**
- * update current state
- * @param {jQuery} $popover - popover container
- * @param {Object} styleInfo - style object
- * @param {Boolean} isAirMode
- */
- this.update = function ($popover, styleInfo, isAirMode) {
- button.update($popover, styleInfo);
-
- var $linkPopover = $popover.find('.note-link-popover');
- 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);
- 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: isAirMode,
- isLeftTop: true
- }));
- } else {
- $imagePopover.hide();
- }
-
- var $airPopover = $popover.find('.note-air-popover');
- if (isAirMode && styleInfo.range && !styleInfo.range.isCollapsed()) {
- var rect = list.last(styleInfo.range.getClientRects());
- if (rect) {
- var bnd = func.rect2bnd(rect);
- showPopover($airPopover, {
- left: Math.max(bnd.left + bnd.width / 2 - PX_POPOVER_ARROW_OFFSET_X, 0),
- top: bnd.top + bnd.height
- });
- }
- } else {
- $airPopover.hide();
- }
- };
-
- /**
- * @param {Node} button
- * @param {String} eventName
- * @param {String} value
- */
- this.updateRecentColor = function (button, eventName, value) {
- button.updateRecentColor(button, eventName, value);
- };
-
- /**
- * hide all popovers
- * @param {jQuery} $popover - popover container
- */
- this.hide = function ($popover) {
- $popover.children().hide();
- };
- };
-
- return Popover;
-});
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
deleted file mode 100644
index b50fce458eb..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Statusbar.js
+++ /dev/null
@@ -1,44 +0,0 @@
-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
deleted file mode 100644
index 5e5e5721b63..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/module/Toolbar.js
+++ /dev/null
@@ -1,101 +0,0 @@
-define([
- 'summernote/core/list',
- 'summernote/core/dom',
- 'summernote/module/Button'
-], function (list, dom, Button) {
- /**
- * @class module.Toolbar
- *
- * Toolbar
- */
- var Toolbar = function () {
- var button = new Button();
-
- this.button = button; // ODOO: allow access for override
-
- this.update = function ($toolbar, styleInfo) {
- button.update($toolbar, styleInfo);
- };
-
- /**
- * @param {Node} button
- * @param {String} eventName
- * @param {String} value
- */
- this.updateRecentColor = function (buttonNode, eventName, value) {
- button.updateRecentColor(buttonNode, eventName, value);
- };
-
- /**
- * activate buttons exclude codeview
- * @param {jQuery} $toolbar
- */
- this.activate = function ($toolbar) {
- $toolbar.find('button')
- .not('button[data-event="codeview"]')
- .removeClass('disabled');
- };
-
- /**
- * deactivate buttons exclude codeview
- * @param {jQuery} $toolbar
- */
- this.deactivate = function ($toolbar) {
- $toolbar.find('button')
- .not('button[data-event="codeview"]')
- .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);
- };
- };
-
- return Toolbar;
-});
diff --git a/addons/web_editor/static/lib/summernote/src/js/outro.js b/addons/web_editor/static/lib/summernote/src/js/outro.js
deleted file mode 100644
index be4600a5cb1..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/outro.js
+++ /dev/null
@@ -1 +0,0 @@
-}));
diff --git a/addons/web_editor/static/lib/summernote/src/js/summernote.js b/addons/web_editor/static/lib/summernote/src/js/summernote.js
deleted file mode 100644
index 329ba7fa126..00000000000
--- a/addons/web_editor/static/lib/summernote/src/js/summernote.js
+++ /dev/null
@@ -1,328 +0,0 @@
-define([
- 'summernote/core/agent',
- 'summernote/core/list',
- 'summernote/core/dom',
- 'summernote/core/range',
- 'summernote/defaults',
- 'summernote/EventHandler',
- 'summernote/Renderer',
- 'summernote/core/key' // ODOO: change for override
-], function (agent, list, dom, range,
- defaults, EventHandler, Renderer, key) {
-
- // jQuery namespace for summernote
- /**
- * @class $.summernote
- *
- * summernote attribute
- *
- * @mixin defaults
- * @singleton
- *
- */
- $.summernote = $.summernote || {};
-
- // extends default settings
- // - $.summernote.version
- // - $.summernote.options
- // - $.summernote.lang
- $.extend($.summernote, defaults);
-
- var renderer = new Renderer();
- var eventHandler = new EventHandler();
-
- $.extend($.summernote, {
- /** @property {Renderer} */
- renderer: renderer,
- /** @property {EventHandler} */
- eventHandler: eventHandler,
- /**
- * @property {Object} core
- * @property {core.agent} core.agent
- * @property {core.dom} core.dom
- * @property {core.range} core.range
- */
- core: {
- agent: agent,
- list : list,
- dom: dom,
- range: range,
- key: key // ODOO: change for override
- },
- /**
- * @property {Object}
- * pluginEvents event list for plugins
- * event has name and callback function.
- *
- * ```
- * $.summernote.addPlugin({
- * events : {
- * 'hello' : function(layoutInfo, value, $target) {
- * console.log('event name is hello, value is ' + value );
- * }
- * }
- * })
- * ```
- *
- * * event name is data-event property.
- * * layoutInfo is a summernote layout information.
- * * value is data-value property.
- */
- pluginEvents: {},
-
- plugins : []
- });
-
- /**
- * @method addPlugin
- *
- * add Plugin in Summernote
- *
- * Summernote can make a own plugin.
- *
- * ### Define plugin
- * ```
- * // get template function
- * var tmpl = $.summernote.renderer.getTemplate();
- *
- * // add a button
- * $.summernote.addPlugin({
- * buttons : {
- * // "hello" is button's namespace.
- * "hello" : function(lang, options) {
- * // make icon button by template function
- * return tmpl.iconButton(options.iconPrefix + 'header', {
- * // callback function name when button clicked
- * event : 'hello',
- * // set data-value property
- * value : 'hello',
- * hide : true
- * });
- * }
- *
- * },
- *
- * events : {
- * "hello" : function(layoutInfo, value) {
- * // here is event code
- * }
- * }
- * });
- * ```
- * ### Use a plugin in toolbar
- *
- * ```
- * $("#editor").summernote({
- * ...
- * toolbar : [
- * // display hello plugin in toolbar
- * ['group', [ 'hello' ]]
- * ]
- * ...
- * });
- * ```
- *
- *
- * @param {Object} plugin
- * @param {Object} [plugin.buttons] define plugin button. for detail, see to Renderer.addButtonInfo
- * @param {Object} [plugin.dialogs] define plugin dialog. for detail, see to Renderer.addDialogInfo
- * @param {Object} [plugin.events] add event in $.summernote.pluginEvents
- * @param {Object} [plugin.langs] update $.summernote.lang
- * @param {Object} [plugin.options] update $.summernote.options
- */
- $.summernote.addPlugin = function (plugin) {
-
- // save plugin list
- $.summernote.plugins.push(plugin);
-
- if (plugin.buttons) {
- $.each(plugin.buttons, function (name, button) {
- renderer.addButtonInfo(name, button);
- });
- }
-
- if (plugin.dialogs) {
- $.each(plugin.dialogs, function (name, dialog) {
- renderer.addDialogInfo(name, dialog);
- });
- }
-
- if (plugin.events) {
- $.each(plugin.events, function (name, event) {
- $.summernote.pluginEvents[name] = event;
- });
- }
-
- if (plugin.langs) {
- $.each(plugin.langs, function (locale, lang) {
- if ($.summernote.lang[locale]) {
- $.extend($.summernote.lang[locale], lang);
- }
- });
- }
-
- if (plugin.options) {
- $.extend($.summernote.options, plugin.options);
- }
- };
-
- /*
- * extend $.fn
- */
- $.fn.extend({
- /**
- * @method
- * Initialize summernote
- * - create editor layout and attach Mouse and keyboard events.
- *
- * ```
- * $("#summernote").summernote( { options ..} );
- * ```
- *
- * @member $.fn
- * @param {Object|String} options reference to $.summernote.options
- * @return {this}
- */
- summernote: function () {
- // check first argument's type
- // - {String}: External API call {{module}}.{{method}}
- // - {Object}: init options
- var type = $.type(list.head(arguments));
- var isExternalAPICalled = type === 'string';
- var hasInitOptions = type === 'object';
-
- // extend default options with custom user options
- var options = hasInitOptions ? list.head(arguments) : {};
-
- options = $.extend({}, $.summernote.options, options);
- options.icons = $.extend({}, $.summernote.options.icons, options.icons);
-
- // Include langInfo in options for later use, e.g. for image drag-n-drop
- // Setup language info with en-US as default
- options.langInfo = $.extend(true, {}, $.summernote.lang['en-US'], $.summernote.lang[options.lang]);
-
- // override plugin options
- if (!isExternalAPICalled && hasInitOptions) {
- for (var i = 0, len = $.summernote.plugins.length; i < len; i++) {
- var plugin = $.summernote.plugins[i];
-
- if (options.plugin[plugin.name]) {
- $.summernote.plugins[i] = $.extend(true, plugin, options.plugin[plugin.name]);
- }
- }
- }
-
- this.each(function (idx, holder) {
- var $holder = $(holder);
-
- // if layout isn't created yet, createLayout and attach events
- if (!renderer.hasNoteEditor($holder)) {
- renderer.createLayout($holder, options);
-
- var layoutInfo = renderer.layoutInfoFromHolder($holder);
- $holder.data('layoutInfo', layoutInfo);
-
- eventHandler.attach(layoutInfo, options);
- eventHandler.attachCustomEvent(layoutInfo, options);
- }
- });
-
- var $first = this.first();
- if ($first.length) {
- var layoutInfo = renderer.layoutInfoFromHolder($first);
-
- // external API
- if (isExternalAPICalled) {
- var moduleAndMethod = list.head(list.from(arguments));
- var args = list.tail(list.from(arguments));
-
- // TODO now external API only works for editor
- var params = [moduleAndMethod, layoutInfo.editable()].concat(args);
- return eventHandler.invoke.apply(eventHandler, params);
- } else if (options.focus) {
- // focus on first editable element for initialize editor
- layoutInfo.editable().focus();
- }
- }
-
- return this;
- },
-
- /**
- * @method
- *
- * get the HTML contents of note or set the HTML contents of note.
- *
- * * get contents
- * ```
- * var content = $("#summernote").code();
- * ```
- * * set contents
- *
- * ```
- * $("#summernote").code(html);
- * ```
- *
- * @member $.fn
- * @param {String} [html] - HTML contents(optional, set)
- * @return {this|String} - context(set) or HTML contents of note(get).
- */
- code: function (html) {
- // get the HTML contents of note
- if (html === undefined) {
- var $holder = this.first();
- if (!$holder.length) {
- return;
- }
-
- var layoutInfo = renderer.layoutInfoFromHolder($holder);
- var $editable = layoutInfo && layoutInfo.editable();
-
- if ($editable && $editable.length) {
- var isCodeview = eventHandler.invoke('codeview.isActivated', layoutInfo);
- eventHandler.invoke('codeview.sync', layoutInfo);
- return isCodeview ? layoutInfo.codable().val() :
- layoutInfo.editable().html();
- }
- return dom.value($holder);
- }
-
- // set the HTML contents of note
- this.each(function (i, holder) {
- var layoutInfo = renderer.layoutInfoFromHolder($(holder));
- var $editable = layoutInfo && layoutInfo.editable();
- if ($editable) {
- $editable.html(html);
- }
- });
-
- return this;
- },
-
- /**
- * @method
- *
- * destroy Editor Layout and detach Key and Mouse Event
- *
- * @member $.fn
- * @return {this}
- */
- destroy: function () {
- this.each(function (idx, holder) {
- var $holder = $(holder);
-
- if (!renderer.hasNoteEditor($holder)) {
- return;
- }
-
- var info = renderer.layoutInfoFromHolder($holder);
- var options = info.editor().data('options');
-
- eventHandler.detach(info, options);
- renderer.removeLayout($holder, info, options);
- });
-
- return this;
- }
- });
-});
diff --git a/addons/web_editor/static/lib/summernote/src/less/summernote.less b/addons/web_editor/static/lib/summernote/src/less/summernote.less
deleted file mode 100644
index dc8da25122d..00000000000
--- a/addons/web_editor/static/lib/summernote/src/less/summernote.less
+++ /dev/null
@@ -1,467 +0,0 @@
-/* Theme Variables
- ------------------------------------------*/
-@border-color: #a9a9a9;
-@background-color: #f5f5f5;
-
-/* Frame Mode Layout
- ------------------------------------------*/
-.note-editor {
- border: 1px solid @border-color;
- position: relative;
- /* overflow: hidden; ODOO: removed for embedded editor */
-
- /* dropzone */
- @dropzone-color: lightskyblue;
- @dropzone-active-color: darken(@dropzone-color, 30);
- .note-dropzone {
- position: absolute;
- display: none;
- z-index: 100;
- color: @dropzone-color;
- background-color: white;
- opacity: 0.95;
- pointer-event: none;
-
- .note-dropzone-message {
- display: table-cell;
- vertical-align: middle;
- text-align: center;
- font-size: 28px;
- font-weight: bold;
- }
-
- &.hover {
- color: @dropzone-active-color;
- }
- }
-
- &.dragover .note-dropzone {
- display: table;
- }
-
- /* codeview mode */
- &.codeview {
- .note-editing-area {
- .note-editable {
- display: none;
- }
- .note-codable {
- display: block;
- }
- }
- }
-
- /* fullscreen mode */
- &.fullscreen {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- z-index: 1050; /* bs3 modal-backdrop: 1030, bs2: 1040 */
- .note-editable {
- background-color: white;
- }
- .note-resizebar {
- display: none;
- }
- }
-
- .note-editing-area {
- position: relative;
- overflow: hidden;
-
- /* editable */
- .note-editable {
- background-color: #fff;
- color: #000;
- padding: 10px;
- overflow: auto;
- outline: none;
-
- &[contenteditable=true]:empty:not(:focus):before {
- content:attr(data-placeholder);
- }
- &[contenteditable="false"] {
- background-color: #e5e5e5;
- }
- }
-
- /* codeable */
- .note-codable {
- display: none;
- width: 100%;
- padding: 10px;
- border: none;
- box-shadow: none;
- font-family: Menlo, Monaco, monospace, sans-serif;
- font-size: 14px;
- color: #ccc;
- background-color: #222;
- resize: none;
-
- /* override BS2 default style */
- box-sizing: border-box;
- border-radius: 0;
- margin-bottom: 0;
- }
- }
-
- /* statusbar */
- .note-statusbar {
- background-color: @background-color;
- .note-resizebar {
- padding-top: 1px;
- height: 8px;
- width: 100%;
- cursor: ns-resize;
- .note-icon-bar {
- width: 20px;
- margin: 1px auto;
- border-top: 1px solid @border-color;
- }
- }
- }
-}
-
-/* Air Mode Layout
-------------------------------------------*/
-.note-air-editor {
- outline: none;
-}
-
-/* Popover
-------------------------------------------*/
-.note-popover .popover {
- max-width: none;
- .popover-body {
- a {
- display: inline-block;
- max-width: 200px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap; /* for FF */
- vertical-align: middle; /* for FF */
- }
- }
- & .arrow {
- left: 20px;
- }
-}
-
-/* Popover and Toolbar (Button container)
-------------------------------------------*/
-.note-popover .popover .popover-body, .panel-heading.note-toolbar {
- margin: 0;
- padding: 0 0 5px 5px;
-
- &>.btn-group {
- margin-top: 5px;
- margin-left: 0;
- margin-right: 5px;
- }
-
- .btn-group {
- .note-table {
- min-width: 0;
- padding: 5px;
- .note-dimension-picker {
- font-size: 18px;
- .note-dimension-picker-mousecatcher {
- position: absolute !important;
- z-index: 3;
- width: 10em;
- height: 10em;
- cursor: pointer;
- }
- .note-dimension-picker-unhighlighted {
- position: relative !important;
- z-index: 1;
- width: 5em;
- height: 5em;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIj4+Pjp6ekKlAqjAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKhmnaJzPAAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat;
- }
- .note-dimension-picker-highlighted {
- position: absolute !important;
- z-index: 2;
- width: 1em;
- height: 1em;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIjd6vvD2f9LKLW+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKwNDEVT0AAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat;
- }
- }
- }
- }
-
- .note-style {
- h1, h2, h3, h4, h5, h6, blockquote {
- margin: 0;
- }
- }
-
- .note-color {
- .dropdown-toggle {
- width: 20px;
- padding-left: 5px;
- }
- .dropdown-menu {
- min-width: 340px;
- .btn-group {
- margin: 0;
- &:first-child {
- margin: 0 5px;
- }
-
- .note-palette-title {
- font-size: 12px;
- margin: 2px 7px;
- text-align: center;
- border-bottom: 1px solid #eee;
- }
-
- .note-color-reset {
- font-size: 11px;
- margin: 3px;
- padding: 0 3px;
- cursor: pointer;
- border-radius: 5px;
- }
-
- .note-color-row {
- height: 20px;
- }
-
- .note-color-reset:hover {
- background: #eee;
- }
- }
- }
- }
-
- .note-para {
- .dropdown-menu {
- min-width: 216px;
- padding: 5px;
- &>div:first-child {
- margin-right: 5px;
- }
- }
- }
-
- /* dropdown-menu for toolbar and popover */
- .dropdown-menu {
- min-width: 90px;
-
- /* dropdown-menu right position */
- /* http://forrst.com/posts/Bootstrap_right_positioned_dropdown-2KB */
- &.right {
- right: 0;
- left: auto;
- &::before { right: 9px; left: auto !important; }
- &::after { right: 10px; left: auto !important; }
- }
- /* dropdown-menu for selectbox */
- &.note-check {
- li a i {
- color: deepskyblue;
- visibility: hidden;
- }
- li a.checked i {
- visibility: visible;
- }
- }
-
- }
-
- .note-fontsize-10 {
- font-size: 10px;
- }
-
- /* color palette for toolbar and popover */
- .note-color-palette {
- line-height: 1;
- div {
- .note-color-btn {
- width: 20px;
- height: 20px;
- padding: 0;
- margin: 0;
- border: 1px solid #fff;
- }
- .note-color-btn:hover {
- border: 1px solid #000;
- }
- }
- }
-}
-
-/* Dialog
-------------------------------------------*/
-.note-dialog {
- &>div {
- display: none; /* BS2's hide pacth. */
- }
- .form-group { /* overwrite BS's form-horizontal minus margins */
- margin-left: 0;
- margin-right: 0;
- }
- .note-modal-form {
- margin: 0; /* overwrite BS2's form margin bottom */
- }
- .note-image-dialog {
- .note-dropzone {
- min-height: 100px;
- font-size: 30px;
- line-height: 4; /* vertical-align */
- color: lightgray;
- text-align: center;
- border: 4px dashed lightgray;
- margin-bottom: 10px;
- }
- }
- .note-help-dialog {
- @note-shortcut-hl: #dd0;
- font-size: 12px;
- color: #ccc;
- background-color: #222 !important;
- opacity: 0.9;
- .modal-content {
- background: transparent;
- border: 1px solid white;
- box-shadow: none;
- border-radius: 5px;
- }
-
- /* BS2's background pacth. */
- background: transparent;
- border: none;
-
- a {
- font-size: 12px;
- color: white;
- }
-
- .title {
- color: white;
- font-size: 14px;
- font-weight: bold;
- padding-bottom: 5px;
- margin-bottom: 10px;
- border-bottom: white 1px solid;
- }
-
- .modal-close {
- font-size: 14px;
- color: @note-shortcut-hl;
- cursor: pointer;
- }
-
- .text-center {
- margin: 10px 0 0;
- }
-
- .note-shortcut {
- padding-top: 8px;
- padding-bottom: 8px;
-
- &-row {
- margin-right: -5px;
- margin-left: -5px;
- }
-
- &-col {
- padding-right: 5px;
- padding-left: 5px;
- }
-
- &-title {
- font-size: 13px;
- font-weight: bold;
- color: @note-shortcut-hl;
- }
-
- &-key {
- font-family: "Courier New";
- color: @note-shortcut-hl;
- text-align: right;
- }
- }
- }
-}
-
-/* Handle
-------------------------------------------*/
-.note-handle {
- /* control selection */
- .note-control-selection {
- position: absolute;
- display: none;
- border: 1px solid black;
- &>div { position: absolute; }
-
- .note-control-selection-bg {
- width: 100%;
- height: 100%;
- background-color: black;
- opacity: 0.3;
- }
-
- .note-control-handle {
- width: 7px;
- height: 7px;
- border: 1px solid black;
- }
-
- .note-control-holder {
- .note-control-handle;
- }
-
- .note-control-sizing {
- .note-control-handle;
- background-color: white;
- }
-
- .note-control-nw {
- top: -5px;
- left: -5px;
- border-right: none;
- border-bottom: none;
- }
-
- .note-control-ne {
- top: -5px;
- right: -5px;
- border-bottom: none;
- border-left: none;
- }
-
- .note-control-sw {
- bottom: -5px;
- left: -5px;
- border-top: none;
- border-right: none;
- }
-
- .note-control-se {
- right: -5px;
- bottom: -5px;
- cursor: se-resize;
- }
-
- .note-control-se.note-control-holder {
- cursor: default;
- border-top: none;
- border-left: none;
- }
-
- .note-control-selection-info {
- right: 0;
- bottom: 0;
- padding: 5px;
- margin: 5px;
- color: white;
- background-color: black;
- font-size: 12px;
- border-radius: 5px;
- opacity: 0.7;
- }
- }
-}
diff --git a/addons/web_editor/static/lib/summernote/summernote.css b/addons/web_editor/static/lib/summernote/summernote.css
new file mode 100644
index 00000000000..85492bd24e5
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/summernote.css
@@ -0,0 +1 @@
+@font-face{font-family:"summernote";font-style:normal;font-weight:normal;src:url("./font/summernote.eot?e557617934c52ea068954af79ed7c221");src:url("./font/summernote.eot?#iefix") format("embedded-opentype"),url("./font/summernote.woff?e557617934c52ea068954af79ed7c221") format("woff"),url("./font/summernote.ttf?e557617934c52ea068954af79ed7c221") format("truetype")}[class^="note-icon-"]:before,[class*=" note-icon-"]:before{display:inline-block;font:normal normal normal 14px summernote;font-size:inherit;-webkit-font-smoothing:antialiased;text-decoration:inherit;text-rendering:auto;text-transform:none;vertical-align:middle;speak:none;-moz-osx-font-smoothing:grayscale}.note-icon-align-center:before,.note-icon-align-indent:before,.note-icon-align-justify:before,.note-icon-align-left:before,.note-icon-align-outdent:before,.note-icon-align-right:before,.note-icon-align:before,.note-icon-arrow-circle-down:before,.note-icon-arrow-circle-left:before,.note-icon-arrow-circle-right:before,.note-icon-arrow-circle-up:before,.note-icon-arrows-alt:before,.note-icon-arrows-h:before,.note-icon-arrows-v:before,.note-icon-bold:before,.note-icon-caret:before,.note-icon-chain-broken:before,.note-icon-circle:before,.note-icon-close:before,.note-icon-code:before,.note-icon-col-after:before,.note-icon-col-before:before,.note-icon-col-remove:before,.note-icon-eraser:before,.note-icon-font:before,.note-icon-frame:before,.note-icon-italic:before,.note-icon-link:before,.note-icon-magic:before,.note-icon-menu-check:before,.note-icon-minus:before,.note-icon-orderedlist:before,.note-icon-pencil:before,.note-icon-picture:before,.note-icon-question:before,.note-icon-redo:before,.note-icon-row-above:before,.note-icon-row-below:before,.note-icon-row-remove:before,.note-icon-special-character:before,.note-icon-square:before,.note-icon-strikethrough:before,.note-icon-subscript:before,.note-icon-summernote:before,.note-icon-superscript:before,.note-icon-table:before,.note-icon-text-height:before,.note-icon-trash:before,.note-icon-underline:before,.note-icon-undo:before,.note-icon-unorderedlist:before,.note-icon-video:before{display:inline-block;font-family:"summernote";font-style:normal;font-weight:normal;text-decoration:inherit}.note-icon-align-center:before{content:"\f101"}.note-icon-align-indent:before{content:"\f102"}.note-icon-align-justify:before{content:"\f103"}.note-icon-align-left:before{content:"\f104"}.note-icon-align-outdent:before{content:"\f105"}.note-icon-align-right:before{content:"\f106"}.note-icon-align:before{content:"\f107"}.note-icon-arrow-circle-down:before{content:"\f108"}.note-icon-arrow-circle-left:before{content:"\f109"}.note-icon-arrow-circle-right:before{content:"\f10a"}.note-icon-arrow-circle-up:before{content:"\f10b"}.note-icon-arrows-alt:before{content:"\f10c"}.note-icon-arrows-h:before{content:"\f10d"}.note-icon-arrows-v:before{content:"\f10e"}.note-icon-bold:before{content:"\f10f"}.note-icon-caret:before{content:"\f110"}.note-icon-chain-broken:before{content:"\f111"}.note-icon-circle:before{content:"\f112"}.note-icon-close:before{content:"\f113"}.note-icon-code:before{content:"\f114"}.note-icon-col-after:before{content:"\f115"}.note-icon-col-before:before{content:"\f116"}.note-icon-col-remove:before{content:"\f117"}.note-icon-eraser:before{content:"\f118"}.note-icon-font:before{content:"\f119"}.note-icon-frame:before{content:"\f11a"}.note-icon-italic:before{content:"\f11b"}.note-icon-link:before{content:"\f11c"}.note-icon-magic:before{content:"\f11d"}.note-icon-menu-check:before{content:"\f11e"}.note-icon-minus:before{content:"\f11f"}.note-icon-orderedlist:before{content:"\f120"}.note-icon-pencil:before{content:"\f121"}.note-icon-picture:before{content:"\f122"}.note-icon-question:before{content:"\f123"}.note-icon-redo:before{content:"\f124"}.note-icon-row-above:before{content:"\f125"}.note-icon-row-below:before{content:"\f126"}.note-icon-row-remove:before{content:"\f127"}.note-icon-special-character:before{content:"\f128"}.note-icon-square:before{content:"\f129"}.note-icon-strikethrough:before{content:"\f12a"}.note-icon-subscript:before{content:"\f12b"}.note-icon-summernote:before{content:"\f12c"}.note-icon-superscript:before{content:"\f12d"}.note-icon-table:before{content:"\f12e"}.note-icon-text-height:before{content:"\f12f"}.note-icon-trash:before{content:"\f130"}.note-icon-underline:before{content:"\f131"}.note-icon-undo:before{content:"\f132"}.note-icon-unorderedlist:before{content:"\f133"}.note-icon-video:before{content:"\f134"}.note-editor{position:relative}.note-editor .note-dropzone{position:absolute;z-index:100;display:none;color:#87cefa;background-color:white;opacity:.95}.note-editor .note-dropzone .note-dropzone-message{display:table-cell;font-size:28px;font-weight:bold;text-align:center;vertical-align:middle}.note-editor .note-dropzone.hover{color:#098ddf}.note-editor.dragover .note-dropzone{display:table}.note-editor .note-editing-area{position:relative}.note-editor .note-editing-area .note-editable{outline:0}.note-editor .note-editing-area .note-editable sup{vertical-align:super}.note-editor .note-editing-area .note-editable sub{vertical-align:sub}.note-editor .note-editing-area img.note-float-left{margin-right:10px}.note-editor .note-editing-area img.note-float-right{margin-left:10px}.note-editor.note-frame{border:1px solid #a9a9a9}.note-editor.note-frame.codeview .note-editing-area .note-editable{display:none}.note-editor.note-frame.codeview .note-editing-area .note-codable{display:block}.note-editor.note-frame .note-editing-area{overflow:hidden}.note-editor.note-frame .note-editing-area .note-editable{padding:10px;overflow:auto;color:#000;word-wrap:break-word;background-color:#fff}.note-editor.note-frame .note-editing-area .note-editable[contenteditable="false"]{background-color:#e5e5e5}.note-editor.note-frame .note-editing-area .note-codable{display:none;width:100%;padding:10px;margin-bottom:0;font-family:Menlo,Monaco,monospace,sans-serif;font-size:14px;color:#ccc;background-color:#222;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;box-shadow:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;resize:none}.note-editor.note-frame.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important}.note-editor.note-frame.fullscreen .note-editable{background-color:white}.note-editor.note-frame.fullscreen .note-resizebar{display:none}.note-editor.note-frame .note-statusbar{background-color:#f5f5f5;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.note-editor.note-frame .note-statusbar .note-resizebar{width:100%;height:8px;padding-top:1px;cursor:ns-resize}.note-editor.note-frame .note-statusbar .note-resizebar .note-icon-bar{width:20px;margin:1px auto;border-top:1px solid #a9a9a9}.note-editor.note-frame .note-placeholder{padding:10px}.note-popover.popover{display:none;max-width:none}.note-popover.popover .popover-content a{display:inline-block;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.note-popover.popover .arrow{left:20px!important}.note-toolbar{position:relative;z-index:500}.note-popover .popover-content,.card-header.note-toolbar{padding:0 0 5px 5px;margin:0}.note-popover .popover-content>.btn-group,.card-header.note-toolbar>.btn-group{margin-top:5px;margin-right:5px;margin-left:0}.note-popover .popover-content .btn-group .note-table,.card-header.note-toolbar .btn-group .note-table{min-width:0;padding:5px}.note-popover .popover-content .btn-group .note-table .note-dimension-picker,.card-header.note-toolbar .btn-group .note-table .note-dimension-picker{font-size:18px}.note-popover .popover-content .btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher,.card-header.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher{position:absolute!important;z-index:3;width:10em;height:10em;cursor:pointer}.note-popover .popover-content .btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted,.card-header.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted{position:relative!important;z-index:1;width:5em;height:5em;background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIj4+Pjp6ekKlAqjAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKhmnaJzPAAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat}.note-popover .popover-content .btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted,.card-header.note-toolbar .btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted{position:absolute!important;z-index:2;width:1em;height:1em;background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIjd6vvD2f9LKLW+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKwNDEVT0AAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC') repeat}.note-popover .popover-content .note-style .dropdown-style blockquote,.card-header.note-toolbar .note-style .dropdown-style blockquote,.note-popover .popover-content .note-style .dropdown-style pre,.card-header.note-toolbar .note-style .dropdown-style pre{padding:5px 10px;margin:0}.note-popover .popover-content .note-style .dropdown-style h1,.card-header.note-toolbar .note-style .dropdown-style h1,.note-popover .popover-content .note-style .dropdown-style h2,.card-header.note-toolbar .note-style .dropdown-style h2,.note-popover .popover-content .note-style .dropdown-style h3,.card-header.note-toolbar .note-style .dropdown-style h3,.note-popover .popover-content .note-style .dropdown-style h4,.card-header.note-toolbar .note-style .dropdown-style h4,.note-popover .popover-content .note-style .dropdown-style h5,.card-header.note-toolbar .note-style .dropdown-style h5,.note-popover .popover-content .note-style .dropdown-style h6,.card-header.note-toolbar .note-style .dropdown-style h6,.note-popover .popover-content .note-style .dropdown-style p,.card-header.note-toolbar .note-style .dropdown-style p{padding:0;margin:0}.note-popover .popover-content .note-color .dropdown-toggle,.card-header.note-toolbar .note-color .dropdown-toggle{width:20px;padding-left:5px}.note-popover .popover-content .note-color .dropdown-menu,.card-header.note-toolbar .note-color .dropdown-menu{min-width:337px}.note-popover .popover-content .note-color .dropdown-menu .note-palette,.card-header.note-toolbar .note-color .dropdown-menu .note-palette{display:inline-block;width:160px;margin:0}.note-popover .popover-content .note-color .dropdown-menu .note-palette:first-child,.card-header.note-toolbar .note-color .dropdown-menu .note-palette:first-child{margin:0 5px}.note-popover .popover-content .note-color .dropdown-menu .note-palette .note-palette-title,.card-header.note-toolbar .note-color .dropdown-menu .note-palette .note-palette-title{margin:2px 7px;font-size:12px;text-align:center;border-bottom:1px solid #eee}.note-popover .popover-content .note-color .dropdown-menu .note-palette .note-color-reset,.card-header.note-toolbar .note-color .dropdown-menu .note-palette .note-color-reset{width:100%;padding:0 3px;margin:3px;font-size:11px;cursor:pointer;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.note-popover .popover-content .note-color .dropdown-menu .note-palette .note-color-row,.card-header.note-toolbar .note-color .dropdown-menu .note-palette .note-color-row{height:20px}.note-popover .popover-content .note-color .dropdown-menu .note-palette .note-color-reset:hover,.card-header.note-toolbar .note-color .dropdown-menu .note-palette .note-color-reset:hover{background:#eee}.note-popover .popover-content .note-para .dropdown-menu,.card-header.note-toolbar .note-para .dropdown-menu{min-width:216px;padding:5px}.note-popover .popover-content .note-para .dropdown-menu>div:first-child,.card-header.note-toolbar .note-para .dropdown-menu>div:first-child{margin-right:5px}.note-popover .popover-content .dropdown-menu,.card-header.note-toolbar .dropdown-menu{min-width:90px}.note-popover .popover-content .dropdown-menu.right,.card-header.note-toolbar .dropdown-menu.right{right:0;left:auto}.note-popover .popover-content .dropdown-menu.right::before,.card-header.note-toolbar .dropdown-menu.right::before{right:9px;left:auto!important}.note-popover .popover-content .dropdown-menu.right::after,.card-header.note-toolbar .dropdown-menu.right::after{right:10px;left:auto!important}.note-popover .popover-content .dropdown-menu.note-check a i,.card-header.note-toolbar .dropdown-menu.note-check a i{color:deepskyblue;visibility:hidden}.note-popover .popover-content .dropdown-menu.note-check a.checked i,.card-header.note-toolbar .dropdown-menu.note-check a.checked i{visibility:visible}.note-popover .popover-content .note-fontsize-10,.card-header.note-toolbar .note-fontsize-10{font-size:10px}.note-popover .popover-content .note-color-palette,.card-header.note-toolbar .note-color-palette{line-height:1}.note-popover .popover-content .note-color-palette div .note-color-btn,.card-header.note-toolbar .note-color-palette div .note-color-btn{width:20px;height:20px;padding:0;margin:0;border:1px solid #fff}.note-popover .popover-content .note-color-palette div .note-color-btn:hover,.card-header.note-toolbar .note-color-palette div .note-color-btn:hover{border:1px solid #000}.note-dialog>div{display:none}.note-dialog .form-group{margin-right:0;margin-left:0}.note-dialog .note-modal-form{margin:0}.note-dialog .note-image-dialog .note-dropzone{min-height:100px;margin-bottom:10px;font-size:30px;line-height:4;color:lightgray;text-align:center;border:4px dashed lightgray}@-moz-document url-prefix(){.note-image-input{height:auto}}.note-placeholder{position:absolute;display:none;color:gray}.note-handle .note-control-selection{position:absolute;display:none;border:1px solid black}.note-handle .note-control-selection>div{position:absolute}.note-handle .note-control-selection .note-control-selection-bg{width:100%;height:100%;background-color:black;-webkit-opacity:.3;-khtml-opacity:.3;-moz-opacity:.3;opacity:.3;-ms-filter:alpha(opacity=30);filter:alpha(opacity=30)}.note-handle .note-control-selection .note-control-handle{width:7px;height:7px;border:1px solid black}.note-handle .note-control-selection .note-control-holder{width:7px;height:7px;border:1px solid black}.note-handle .note-control-selection .note-control-sizing{width:7px;height:7px;background-color:white;border:1px solid black}.note-handle .note-control-selection .note-control-nw{top:-5px;left:-5px;border-right:0;border-bottom:0}.note-handle .note-control-selection .note-control-ne{top:-5px;right:-5px;border-bottom:0;border-left:none}.note-handle .note-control-selection .note-control-sw{bottom:-5px;left:-5px;border-top:0;border-right:0}.note-handle .note-control-selection .note-control-se{right:-5px;bottom:-5px;cursor:se-resize}.note-handle .note-control-selection .note-control-se.note-control-holder{cursor:default;border-top:0;border-left:none}.note-handle .note-control-selection .note-control-selection-info{right:0;bottom:0;padding:5px;margin:5px;font-size:12px;color:white;background-color:black;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-opacity:.7;-khtml-opacity:.7;-moz-opacity:.7;opacity:.7;-ms-filter:alpha(opacity=70);filter:alpha(opacity=70)}.note-hint-popover{min-width:100px;padding:2px}.note-hint-popover .popover-content{max-height:150px;padding:3px;overflow:auto}.note-hint-popover .popover-content .note-hint-group .note-hint-item{display:block!important;padding:3px}.note-hint-popover .popover-content .note-hint-group .note-hint-item.active,.note-hint-popover .popover-content .note-hint-group .note-hint-item:hover{display:block;clear:both;font-weight:400;line-height:1.4;color:white;text-decoration:none;white-space:nowrap;cursor:pointer;background-color:#428bca;outline:0} \ No newline at end of file
diff --git a/addons/web_editor/static/lib/summernote/summernote.js b/addons/web_editor/static/lib/summernote/summernote.js
new file mode 100644
index 00000000000..2bcb61bb873
--- /dev/null
+++ b/addons/web_editor/static/lib/summernote/summernote.js
@@ -0,0 +1,7288 @@
+/**
+ * Super simple wysiwyg editor v0.8.9
+ * https://summernote.org
+ *
+ * Copyright 2013- Alan Hong. and other contributors
+ * summernote may be freely distributed under the MIT license.
+ *
+ * Date: 2017-12-25T06:39Z
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
+ typeof define === 'function' && define.amd ? define(['jquery'], factory) :
+ (factory(global.jQuery));
+}(this, (function ($$1) { 'use strict';
+
+$$1 = $$1 && $$1.hasOwnProperty('default') ? $$1['default'] : $$1;
+
+var Renderer = /** @class */ (function () {
+ function Renderer(markup, children, options, callback) {
+ this.markup = markup;
+ this.children = children;
+ this.options = options;
+ this.callback = callback;
+ }
+ Renderer.prototype.render = function ($parent) {
+ var $node = $$1(this.markup);
+ if (this.options && this.options.contents) {
+ $node.html(this.options.contents);
+ }
+ if (this.options && this.options.className) {
+ $node.addClass(this.options.className);
+ }
+ if (this.options && this.options.data) {
+ $$1.each(this.options.data, function (k, v) {
+ $node.attr('data-' + k, v);
+ });
+ }
+ if (this.options && this.options.click) {
+ $node.on('click', this.options.click);
+ }
+ if (this.children) {
+ var $container_1 = $node.find('.note-children-container');
+ this.children.forEach(function (child) {
+ child.render($container_1.length ? $container_1 : $node);
+ });
+ }
+ if (this.callback) {
+ this.callback($node, this.options);
+ }
+ if (this.options && this.options.callback) {
+ this.options.callback($node);
+ }
+ if ($parent) {
+ $parent.append($node);
+ }
+ return $node;
+ };
+ return Renderer;
+}());
+var renderer = {
+ create: function (markup, callback) {
+ return function () {
+ var options = typeof arguments[1] === 'object' ? arguments[1] : arguments[0];
+ var children = $$1.isArray(arguments[0]) ? arguments[0] : [];
+ if (options && options.children) {
+ children = options.children;
+ }
+ return new Renderer(markup, children, options, callback);
+ };
+ }
+};
+
+var editor = renderer.create('<div class="note-editor note-frame card"/>');
+var toolbar = renderer.create('<div class="note-toolbar-wrapper"><div class="note-toolbar card-header"></div></div>');
+var editingArea = renderer.create('<div class="note-editing-area"/>');
+var codable = renderer.create('<textarea class="note-codable"/>');
+var editable = renderer.create('<div class="note-editable card-block" contentEditable="true"/>');
+var statusbar = renderer.create([
+ '<div class="note-statusbar">',
+ ' <div class="note-resizebar">',
+ ' <div class="note-icon-bar"/>',
+ ' <div class="note-icon-bar"/>',
+ ' <div class="note-icon-bar"/>',
+ ' </div>',
+ '</div>'
+].join(''));
+var airEditor = renderer.create('<div class="note-editor"/>');
+var airEditable = renderer.create('<div class="note-editable" contentEditable="true"/>');
+var buttonGroup = renderer.create('<div class="note-btn-group btn-group">');
+var dropdown = renderer.create('<div class="dropdown-menu">', function ($node, options) {
+ var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
+ var value = (typeof item === 'string') ? item : (item.value || '');
+ var content = options.template ? options.template(item) : item;
+ var option = (typeof item === 'object') ? item.option : undefined;
+ var dataValue = 'data-value="' + value + '"';
+ var dataOption = (option !== undefined) ? ' data-option="' + option + '"' : '';
+ return '<a class="dropdown-item" href="#" ' + (dataValue + dataOption) + '>' + content + '</a>';
+ }).join('') : options.items;
+ $node.html(markup);
+});
+var dropdownButtonContents = function (contents) {
+ return contents;
+};
+var dropdownCheck = renderer.create('<div class="dropdown-menu note-check">', function ($node, options) {
+ var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
+ var value = (typeof item === 'string') ? item : (item.value || '');
+ var content = options.template ? options.template(item) : item;
+ return '<a class="dropdown-item" href="#" data-value="' + value + '">' + icon(options.checkClassName) + ' ' + content + '</a>';
+ }).join('') : options.items;
+ $node.html(markup);
+});
+var palette = renderer.create('<div class="note-color-palette"/>', function ($node, options) {
+ var contents = [];
+ for (var row = 0, rowSize = options.colors.length; row < rowSize; row++) {
+ var eventName = options.eventName;
+ var colors = options.colors[row];
+ var buttons = [];
+ for (var col = 0, colSize = colors.length; col < colSize; col++) {
+ var color = colors[col];
+ buttons.push([
+ '<button type="button" class="note-color-btn"',
+ 'style="background-color:', color, '" ',
+ 'data-event="', eventName, '" ',
+ 'data-value="', color, '" ',
+ 'title="', color, '" ',
+ 'data-toggle="button" tabindex="-1"></button>'
+ ].join(''));
+ }
+ contents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
+ }
+ $node.html(contents.join(''));
+ if (options.tooltip) {
+ $node.find('.note-color-btn').tooltip({
+ container: options.container,
+ trigger: 'hover',
+ placement: 'bottom'
+ });
+ }
+});
+var dialog = renderer.create('<div class="modal" aria-hidden="false" tabindex="-1"/>', function ($node, options) {
+ if (options.fade) {
+ $node.addClass('fade');
+ }
+ $node.html([
+ '<div class="modal-dialog">',
+ ' <div class="modal-content">',
+ (options.title
+ ? ' <div class="modal-header">' +
+ ' <h4 class="modal-title">' + options.title + '</h4>' +
+ ' <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
+ ' </div>' : ''),
+ ' <div class="modal-body">' + options.body + '</div>',
+ (options.footer
+ ? ' <div class="modal-footer">' + options.footer + '</div>' : ''),
+ ' </div>',
+ '</div>'
+ ].join(''));
+});
+var popover = renderer.create([
+ '<div class="note-popover popover in">',
+ ' <div class="arrow"/>',
+ ' <div class="popover-content note-children-container"/>',
+ '</div>'
+].join(''), function ($node, options) {
+ var direction = typeof options.direction !== 'undefined' ? options.direction : 'bottom';
+ $node.addClass(direction);
+ if (options.hideArrow) {
+ $node.find('.arrow').hide();
+ }
+});
+var checkbox = renderer.create('<label class="custom-control custom-checkbox"></label>', function ($node, options) {
+ if (options.id) {
+ $node.attr('for', options.id);
+ }
+ $node.html([
+ ' <input type="checkbox" class="custom-control-input"' + (options.id ? ' id="' + options.id + '"' : ''),
+ (options.checked ? ' checked' : '') + '/>',
+ ' <span class="custom-control-indicator"></span>',
+ ' <span class="custom-control-description">' + (options.text ? options.text : '') + '</span>',
+ '</label>'
+ ].join(''));
+});
+var icon = function (iconClassName, tagName) {
+ tagName = tagName || 'i';
+ return '<' + tagName + ' class="' + iconClassName + '"/>';
+};
+var ui = {
+ editor: editor,
+ toolbar: toolbar,
+ editingArea: editingArea,
+ codable: codable,
+ editable: editable,
+ statusbar: statusbar,
+ airEditor: airEditor,
+ airEditable: airEditable,
+ buttonGroup: buttonGroup,
+ dropdown: dropdown,
+ dropdownButtonContents: dropdownButtonContents,
+ dropdownCheck: dropdownCheck,
+ palette: palette,
+ dialog: dialog,
+ popover: popover,
+ icon: icon,
+ checkbox: checkbox,
+ options: {},
+ button: function ($node, options) {
+ return renderer.create('<button type="button" class="note-btn btn btn-light btn-sm" tabindex="-1">', function ($node, options) {
+ if (options && options.tooltip) {
+ $node.attr({
+ title: options.tooltip
+ }).tooltip({
+ container: options.container,
+ trigger: 'hover',
+ placement: 'bottom'
+ });
+ }
+ })($node, options);
+ },
+ toggleBtn: function ($btn, isEnable) {
+ $btn.toggleClass('disabled', !isEnable);
+ $btn.attr('disabled', !isEnable);
+ },
+ toggleBtnActive: function ($btn, isActive) {
+ $btn.toggleClass('active', isActive);
+ },
+ onDialogShown: function ($dialog, handler) {
+ $dialog.one('shown.bs.modal', handler);
+ },
+ onDialogHidden: function ($dialog, handler) {
+ $dialog.one('hidden.bs.modal', handler);
+ },
+ showDialog: function ($dialog) {
+ $dialog.modal('show');
+ },
+ hideDialog: function ($dialog) {
+ $dialog.modal('hide');
+ },
+ createLayout: function ($note, options) {
+ var $editor = (options.airMode ? ui.airEditor([
+ ui.editingArea([
+ ui.airEditable()
+ ])
+ ]) : ui.editor([
+ ui.toolbar(),
+ ui.editingArea([
+ ui.codable(),
+ ui.editable()
+ ]),
+ ui.statusbar()
+ ])).render();
+ $editor.insertAfter($note);
+ return {
+ note: $note,
+ editor: $editor,
+ toolbar: $editor.find('.note-toolbar'),
+ editingArea: $editor.find('.note-editing-area'),
+ editable: $editor.find('.note-editable'),
+ codable: $editor.find('.note-codable'),
+ statusbar: $editor.find('.note-statusbar')
+ };
+ },
+ removeLayout: function ($note, layoutInfo) {
+ $note.html(layoutInfo.editable.html());
+ layoutInfo.editor.remove();
+ $note.show();
+ }
+};
+
+/**
+ * @class core.func
+ *
+ * func utils (for high-order func's arg)
+ *
+ * @singleton
+ * @alternateClassName func
+ */
+function eq(itemA) {
+ return function (itemB) {
+ return itemA === itemB;
+ };
+}
+function eq2(itemA, itemB) {
+ return itemA === itemB;
+}
+function peq2(propName) {
+ return function (itemA, itemB) {
+ return itemA[propName] === itemB[propName];
+ };
+}
+function ok() {
+ return true;
+}
+function fail() {
+ return false;
+}
+function not(f) {
+ return function () {
+ return !f.apply(f, arguments);
+ };
+}
+function and(fA, fB) {
+ return function (item) {
+ return fA(item) && fB(item);
+ };
+}
+function self(a) {
+ return a;
+}
+function invoke(obj, method) {
+ return function () {
+ return obj[method].apply(obj, arguments);
+ };
+}
+var idCounter = 0;
+/**
+ * generate a globally-unique id
+ *
+ * @param {String} [prefix]
+ */
+function uniqueId(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
+}
+/**
+ * returns bnd (bounds) from rect
+ *
+ * - IE Compatibility Issue: http://goo.gl/sRLOAo
+ * - Scroll Issue: http://goo.gl/sNjUc
+ *
+ * @param {Rect} rect
+ * @return {Object} bounds
+ * @return {Number} bounds.top
+ * @return {Number} bounds.left
+ * @return {Number} bounds.width
+ * @return {Number} bounds.height
+ */
+function rect2bnd(rect) {
+ var $document = $(document);
+ return {
+ top: rect.top + $document.scrollTop(),
+ left: rect.left + $document.scrollLeft(),
+ width: rect.right - rect.left,
+ height: rect.bottom - rect.top
+ };
+}
+/**
+ * returns a copy of the object where the keys have become the values and the values the keys.
+ * @param {Object} obj
+ * @return {Object}
+ */
+function invertObject(obj) {
+ var inverted = {};
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ inverted[obj[key]] = key;
+ }
+ }
+ return inverted;
+}
+/**
+ * @param {String} namespace
+ * @param {String} [prefix]
+ * @return {String}
+ */
+function namespaceToCamel(namespace, prefix) {
+ prefix = prefix || '';
+ return prefix + namespace.split('.').map(function (name) {
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }).join('');
+}
+/**
+ * Returns a function, that, as long as it continues to be invoked, will not
+ * be triggered. The function will be called after it stops being called for
+ * N milliseconds. If `immediate` is passed, trigger the function on the
+ * leading edge, instead of the trailing.
+ * @param {Function} func
+ * @param {Number} wait
+ * @param {Boolean} immediate
+ * @return {Function}
+ */
+function debounce(func, wait, immediate) {
+ var _this = this;
+ var timeout;
+ return function () {
+ var context = _this;
+ var args = arguments;
+ var later = function () {
+ timeout = null;
+ if (!immediate) {
+ func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) {
+ func.apply(context, args);
+ }
+ };
+}
+var func = {
+ eq: eq,
+ eq2: eq2,
+ peq2: peq2,
+ ok: ok,
+ fail: fail,
+ self: self,
+ not: not,
+ and: and,
+ invoke: invoke,
+ uniqueId: uniqueId,
+ rect2bnd: rect2bnd,
+ invertObject: invertObject,
+ namespaceToCamel: namespaceToCamel,
+ debounce: debounce
+};
+
+/**
+ * returns the first item of an array.
+ *
+ * @param {Array} array
+ */
+function head(array) {
+ return array[0];
+}
+/**
+ * returns the last item of an array.
+ *
+ * @param {Array} array
+ */
+function last(array) {
+ return array[array.length - 1];
+}
+/**
+ * returns everything but the last entry of the array.
+ *
+ * @param {Array} array
+ */
+function initial(array) {
+ return array.slice(0, array.length - 1);
+}
+/**
+ * returns the rest of the items in an array.
+ *
+ * @param {Array} array
+ */
+function tail(array) {
+ return array.slice(1);
+}
+/**
+ * returns item of array
+ */
+function find(array, pred) {
+ for (var idx = 0, len = array.length; idx < len; idx++) {
+ var item = array[idx];
+ if (pred(item)) {
+ return item;
+ }
+ }
+}
+/**
+ * returns true if all of the values in the array pass the predicate truth test.
+ */
+function all(array, pred) {
+ for (var idx = 0, len = array.length; idx < len; idx++) {
+ if (!pred(array[idx])) {
+ return false;
+ }
+ }
+ return true;
+}
+/**
+ * returns index of item
+ */
+function indexOf(array, item) {
+ return $$1.inArray(item, array);
+}
+/**
+ * returns true if the value is present in the list.
+ */
+function contains(array, item) {
+ return indexOf(array, item) !== -1;
+}
+/**
+ * get sum from a list
+ *
+ * @param {Array} array - array
+ * @param {Function} fn - iterator
+ */
+function sum(array, fn) {
+ fn = fn || func.self;
+ return array.reduce(function (memo, v) {
+ return memo + fn(v);
+ }, 0);
+}
+/**
+ * returns a copy of the collection with array type.
+ * @param {Collection} collection - collection eg) node.childNodes, ...
+ */
+function from(collection) {
+ var result = [];
+ var length = collection.length;
+ var idx = -1;
+ while (++idx < length) {
+ result[idx] = collection[idx];
+ }
+ return result;
+}
+/**
+ * returns whether list is empty or not
+ */
+function isEmpty$1(array) {
+ return !array || !array.length;
+}
+/**
+ * cluster elements by predicate function.
+ *
+ * @param {Array} array - array
+ * @param {Function} fn - predicate function for cluster rule
+ * @param {Array[]}
+ */
+function clusterBy(array, fn) {
+ if (!array.length) {
+ return [];
+ }
+ var aTail = tail(array);
+ return aTail.reduce(function (memo, v) {
+ var aLast = last(memo);
+ if (fn(last(aLast), v)) {
+ aLast[aLast.length] = v;
+ }
+ else {
+ memo[memo.length] = [v];
+ }
+ return memo;
+ }, [[head(array)]]);
+}
+/**
+ * returns a copy of the array with all false values removed
+ *
+ * @param {Array} array - array
+ * @param {Function} fn - predicate function for cluster rule
+ */
+function compact(array) {
+ var aResult = [];
+ for (var idx = 0, len = array.length; idx < len; idx++) {
+ if (array[idx]) {
+ aResult.push(array[idx]);
+ }
+ }
+ return aResult;
+}
+/**
+ * produces a duplicate-free version of the array
+ *
+ * @param {Array} array
+ */
+function unique(array) {
+ var results = [];
+ for (var idx = 0, len = array.length; idx < len; idx++) {
+ if (!contains(results, array[idx])) {
+ results.push(array[idx]);
+ }
+ }
+ return results;
+}
+/**
+ * returns next item.
+ * @param {Array} array
+ */
+function next(array, item) {
+ var idx = indexOf(array, item);
+ if (idx === -1) {
+ return null;
+ }
+ return array[idx + 1];
+}
+/**
+ * returns prev item.
+ * @param {Array} array
+ */
+function prev(array, item) {
+ var idx = indexOf(array, item);
+ if (idx === -1) {
+ return null;
+ }
+ return array[idx - 1];
+}
+/**
+ * @class core.list
+ *
+ * list utils
+ *
+ * @singleton
+ * @alternateClassName list
+ */
+var lists = {
+ head: head,
+ last: last,
+ initial: initial,
+ tail: tail,
+ prev: prev,
+ next: next,
+ find: find,
+ contains: contains,
+ all: all,
+ sum: sum,
+ from: from,
+ isEmpty: isEmpty$1,
+ clusterBy: clusterBy,
+ compact: compact,
+ unique: unique
+};
+
+var isSupportAmd = typeof define === 'function' && define.amd; // eslint-disable-line
+/**
+ * returns whether font is installed or not.
+ *
+ * @param {String} fontName
+ * @return {Boolean}
+ */
+function isFontInstalled(fontName) {
+ var testFontName = fontName === 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS';
+ var $tester = $$1('<div>').css({
+ position: 'absolute',
+ left: '-9999px',
+ top: '-9999px',
+ fontSize: '200px'
+ }).text('mmmmmmmmmwwwwwww').appendTo(document.body);
+ var originalWidth = $tester.css('fontFamily', testFontName).width();
+ var width = $tester.css('fontFamily', fontName + ',' + testFontName).width();
+ $tester.remove();
+ return originalWidth !== width;
+}
+var userAgent = navigator.userAgent;
+var isMSIE = /MSIE|Trident/i.test(userAgent);
+var browserVersion;
+if (isMSIE) {
+ var matches = /MSIE (\d+[.]\d+)/.exec(userAgent);
+ if (matches) {
+ browserVersion = parseFloat(matches[1]);
+ }
+ matches = /Trident\/.*rv:([0-9]{1,}[.0-9]{0,})/.exec(userAgent);
+ if (matches) {
+ browserVersion = parseFloat(matches[1]);
+ }
+}
+var isEdge = /Edge\/\d+/.test(userAgent);
+var hasCodeMirror = !!window.CodeMirror;
+if (!hasCodeMirror && isSupportAmd) {
+ // Webpack
+ if (typeof __webpack_require__ === 'function') {
+ try {
+ // If CodeMirror can't be resolved, `require.resolve` will throw an
+ // exception and `hasCodeMirror` won't be set to `true`.
+ require.resolve('codemirror');
+ hasCodeMirror = true;
+ }
+ catch (e) {
+ // do nothing
+ }
+ }
+ else if (typeof require !== 'undefined') {
+ // Browserify
+ if (typeof require.resolve !== 'undefined') {
+ try {
+ // If CodeMirror can't be resolved, `require.resolve` will throw an
+ // exception and `hasCodeMirror` won't be set to `true`.
+ require.resolve('codemirror');
+ hasCodeMirror = true;
+ }
+ catch (e) {
+ // do nothing
+ }
+ // Almond/Require
+ }
+ else if (typeof require.specified !== 'undefined') {
+ hasCodeMirror = require.specified('codemirror');
+ }
+ }
+}
+var isSupportTouch = (('ontouchstart' in window) ||
+ (navigator.MaxTouchPoints > 0) ||
+ (navigator.msMaxTouchPoints > 0));
+// [workaround] IE doesn't have input events for contentEditable
+// - see: https://goo.gl/4bfIvA
+var inputEventName = (isMSIE || isEdge) ? 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted' : 'input';
+/**
+ * @class core.env
+ *
+ * Object which check platform and agent
+ *
+ * @singleton
+ * @alternateClassName env
+ */
+var env = {
+ isMac: navigator.appVersion.indexOf('Mac') > -1,
+ isMSIE: isMSIE,
+ isEdge: isEdge,
+ isFF: !isEdge && /firefox/i.test(userAgent),
+ isPhantom: /PhantomJS/i.test(userAgent),
+ isWebkit: !isEdge && /webkit/i.test(userAgent),
+ isChrome: !isEdge && /chrome/i.test(userAgent),
+ isSafari: !isEdge && /safari/i.test(userAgent),
+ browserVersion: browserVersion,
+ jqueryVersion: parseFloat($$1.fn.jquery),
+ isSupportAmd: isSupportAmd,
+ isSupportTouch: isSupportTouch,
+ hasCodeMirror: hasCodeMirror,
+ isFontInstalled: isFontInstalled,
+ isW3CRangeSupport: !!document.createRange,
+ inputEventName: inputEventName
+};
+
+var NBSP_CHAR = String.fromCharCode(160);
+var ZERO_WIDTH_NBSP_CHAR = '\ufeff';
+/**
+ * @method isEditable
+ *
+ * returns whether node is `note-editable` or not.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+function isEditable(node) {
+ return node && $$1(node).hasClass('note-editable');
+}
+/**
+ * @method isControlSizing
+ *
+ * returns whether node is `note-control-sizing` or not.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+function isControlSizing(node) {
+ return node && $$1(node).hasClass('note-control-sizing');
+}
+/**
+ * @method makePredByNodeName
+ *
+ * returns predicate which judge whether nodeName is same
+ *
+ * @param {String} nodeName
+ * @return {Function}
+ */
+function makePredByNodeName(nodeName) {
+ nodeName = nodeName.toUpperCase();
+ return function (node) {
+ return node && node.nodeName.toUpperCase() === nodeName;
+ };
+}
+/**
+ * @method isText
+ *
+ *
+ *
+ * @param {Node} node
+ * @return {Boolean} true if node's type is text(3)
+ */
+function isText(node) {
+ return node && node.nodeType === 3;
+}
+/**
+ * @method isElement
+ *
+ *
+ *
+ * @param {Node} node
+ * @return {Boolean} true if node's type is element(1)
+ */
+function isElement(node) {
+ return node && node.nodeType === 1;
+}
+/**
+ * ex) br, col, embed, hr, img, input, ...
+ * @see http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
+ */
+function isVoid(node) {
+ return node && /^BR|^IMG|^HR|^IFRAME|^BUTTON|^INPUT/.test(node.nodeName.toUpperCase());
+}
+function isPara(node) {
+ if (isEditable(node)) {
+ return false;
+ }
+ // Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
+ return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName.toUpperCase());
+}
+function isHeading(node) {
+ return node && /^H[1-7]/.test(node.nodeName.toUpperCase());
+}
+var isPre = makePredByNodeName('PRE');
+var isLi = makePredByNodeName('LI');
+function isPurePara(node) {
+ return isPara(node) && !isLi(node);
+}
+var isTable = makePredByNodeName('TABLE');
+var isData = makePredByNodeName('DATA');
+function isInline(node) {
+ return !isBodyContainer(node) &&
+ !isList(node) &&
+ !isHr(node) &&
+ !isPara(node) &&
+ !isTable(node) &&
+ !isBlockquote(node) &&
+ !isData(node);
+}
+function isList(node) {
+ return node && /^UL|^OL/.test(node.nodeName.toUpperCase());
+}
+var isHr = makePredByNodeName('HR');
+function isCell(node) {
+ return node && /^TD|^TH/.test(node.nodeName.toUpperCase());
+}
+var isBlockquote = makePredByNodeName('BLOCKQUOTE');
+function isBodyContainer(node) {
+ return isCell(node) || isBlockquote(node) || isEditable(node);
+}
+var isAnchor = makePredByNodeName('A');
+function isParaInline(node) {
+ return isInline(node) && !!ancestor(node, isPara);
+}
+function isBodyInline(node) {
+ return isInline(node) && !ancestor(node, isPara);
+}
+var isBody = makePredByNodeName('BODY');
+/**
+ * returns whether nodeB is closest sibling of nodeA
+ *
+ * @param {Node} nodeA
+ * @param {Node} nodeB
+ * @return {Boolean}
+ */
+function isClosestSibling(nodeA, nodeB) {
+ return nodeA.nextSibling === nodeB ||
+ nodeA.previousSibling === nodeB;
+}
+/**
+ * returns array of closest siblings with node
+ *
+ * @param {Node} node
+ * @param {function} [pred] - predicate function
+ * @return {Node[]}
+ */
+function withClosestSiblings(node, pred) {
+ pred = pred || func.ok;
+ var siblings = [];
+ if (node.previousSibling && pred(node.previousSibling)) {
+ siblings.push(node.previousSibling);
+ }
+ siblings.push(node);
+ if (node.nextSibling && pred(node.nextSibling)) {
+ siblings.push(node.nextSibling);
+ }
+ return siblings;
+}
+/**
+ * blank HTML for cursor position
+ * - [workaround] old IE only works with &nbsp;
+ * - [workaround] IE11 and other browser works with bogus br
+ */
+var blankHTML = env.isMSIE && env.browserVersion < 11 ? '&nbsp;' : '<br>';
+/**
+ * @method nodeLength
+ *
+ * returns #text's text size or element's childNodes size
+ *
+ * @param {Node} node
+ */
+function nodeLength(node) {
+ if (isText(node)) {
+ return node.nodeValue.length;
+ }
+ if (node) {
+ return node.childNodes.length;
+ }
+ return 0;
+}
+/**
+ * returns whether node is empty or not.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+function isEmpty(node) {
+ var len = nodeLength(node);
+ if (len === 0) {
+ return true;
+ }
+ else if (!isText(node) && len === 1 && node.innerHTML === blankHTML) {
+ // ex) <p><br></p>, <span><br></span>
+ return true;
+ }
+ else if (lists.all(node.childNodes, isText) && node.innerHTML === '') {
+ // ex) <p></p>, <span></span>
+ return true;
+ }
+ return false;
+}
+/**
+ * padding blankHTML if node is empty (for cursor position)
+ */
+function paddingBlankHTML(node) {
+ if (!isVoid(node) && !nodeLength(node)) {
+ node.innerHTML = blankHTML;
+ }
+}
+/**
+ * find nearest ancestor predicate hit
+ *
+ * @param {Node} node
+ * @param {Function} pred - predicate function
+ */
+function ancestor(node, pred) {
+ while (node) {
+ if (pred(node)) {
+ return node;
+ }
+ if (isEditable(node)) {
+ break;
+ }
+ node = node.parentNode;
+ }
+ return null;
+}
+/**
+ * find nearest ancestor only single child blood line and predicate hit
+ *
+ * @param {Node} node
+ * @param {Function} pred - predicate function
+ */
+function singleChildAncestor(node, pred) {
+ node = node.parentNode;
+ while (node) {
+ if (nodeLength(node) !== 1) {
+ break;
+ }
+ if (pred(node)) {
+ return node;
+ }
+ if (isEditable(node)) {
+ break;
+ }
+ node = node.parentNode;
+ }
+ return null;
+}
+/**
+ * returns new array of ancestor nodes (until predicate hit).
+ *
+ * @param {Node} node
+ * @param {Function} [optional] pred - predicate function
+ */
+function listAncestor(node, pred) {
+ pred = pred || func.fail;
+ var ancestors = [];
+ ancestor(node, function (el) {
+ if (!isEditable(el)) {
+ ancestors.push(el);
+ }
+ return pred(el);
+ });
+ return ancestors;
+}
+/**
+ * find farthest ancestor predicate hit
+ */
+function lastAncestor(node, pred) {
+ var ancestors = listAncestor(node);
+ return lists.last(ancestors.filter(pred));
+}
+/**
+ * returns common ancestor node between two nodes.
+ *
+ * @param {Node} nodeA
+ * @param {Node} nodeB
+ */
+function commonAncestor(nodeA, nodeB) {
+ var ancestors = listAncestor(nodeA);
+ for (var n = nodeB; n; n = n.parentNode) {
+ if ($$1.inArray(n, ancestors) > -1) {
+ return n;
+ }
+ }
+ return null; // difference document area
+}
+/**
+ * listing all previous siblings (until predicate hit).
+ *
+ * @param {Node} node
+ * @param {Function} [optional] pred - predicate function
+ */
+function listPrev(node, pred) {
+ pred = pred || func.fail;
+ var nodes = [];
+ while (node) {
+ if (pred(node)) {
+ break;
+ }
+ nodes.push(node);
+ node = node.previousSibling;
+ }
+ return nodes;
+}
+/**
+ * listing next siblings (until predicate hit).
+ *
+ * @param {Node} node
+ * @param {Function} [pred] - predicate function
+ */
+function listNext(node, pred) {
+ pred = pred || func.fail;
+ var nodes = [];
+ while (node) {
+ if (pred(node)) {
+ break;
+ }
+ nodes.push(node);
+ node = node.nextSibling;
+ }
+ return nodes;
+}
+/**
+ * listing descendant nodes
+ *
+ * @param {Node} node
+ * @param {Function} [pred] - predicate function
+ */
+function listDescendant(node, pred) {
+ var descendants = [];
+ pred = pred || func.ok;
+ // start DFS(depth first search) with node
+ (function fnWalk(current) {
+ if (node !== current && pred(current)) {
+ descendants.push(current);
+ }
+ for (var idx = 0, len = current.childNodes.length; idx < len; idx++) {
+ fnWalk(current.childNodes[idx]);
+ }
+ })(node);
+ return descendants;
+}
+/**
+ * wrap node with new tag.
+ *
+ * @param {Node} node
+ * @param {Node} tagName of wrapper
+ * @return {Node} - wrapper
+ */
+function wrap(node, wrapperName) {
+ var parent = node.parentNode;
+ var wrapper = $$1('<' + wrapperName + '>')[0];
+ parent.insertBefore(wrapper, node);
+ wrapper.appendChild(node);
+ return wrapper;
+}
+/**
+ * insert node after preceding
+ *
+ * @param {Node} node
+ * @param {Node} preceding - predicate function
+ */
+function insertAfter(node, preceding) {
+ var next = preceding.nextSibling;
+ var parent = preceding.parentNode;
+ if (next) {
+ parent.insertBefore(node, next);
+ }
+ else {
+ parent.appendChild(node);
+ }
+ return node;
+}
+/**
+ * append elements.
+ *
+ * @param {Node} node
+ * @param {Collection} aChild
+ */
+function appendChildNodes(node, aChild) {
+ $$1.each(aChild, function (idx, child) {
+ node.appendChild(child);
+ });
+ return node;
+}
+/**
+ * returns whether boundaryPoint is left edge or not.
+ *
+ * @param {BoundaryPoint} point
+ * @return {Boolean}
+ */
+function isLeftEdgePoint(point) {
+ return point.offset === 0;
+}
+/**
+ * returns whether boundaryPoint is right edge or not.
+ *
+ * @param {BoundaryPoint} point
+ * @return {Boolean}
+ */
+function isRightEdgePoint(point) {
+ return point.offset === nodeLength(point.node);
+}
+/**
+ * returns whether boundaryPoint is edge or not.
+ *
+ * @param {BoundaryPoint} point
+ * @return {Boolean}
+ */
+function isEdgePoint(point) {
+ return isLeftEdgePoint(point) || isRightEdgePoint(point);
+}
+/**
+ * returns whether node is left edge of ancestor or not.
+ *
+ * @param {Node} node
+ * @param {Node} ancestor
+ * @return {Boolean}
+ */
+function isLeftEdgeOf(node, ancestor) {
+ while (node && node !== ancestor) {
+ if (position(node) !== 0) {
+ return false;
+ }
+ node = node.parentNode;
+ }
+ return true;
+}
+/**
+ * returns whether node is right edge of ancestor or not.
+ *
+ * @param {Node} node
+ * @param {Node} ancestor
+ * @return {Boolean}
+ */
+function isRightEdgeOf(node, ancestor) {
+ if (!ancestor) {
+ return false;
+ }
+ while (node && node !== ancestor) {
+ if (position(node) !== nodeLength(node.parentNode) - 1) {
+ return false;
+ }
+ node = node.parentNode;
+ }
+ return true;
+}
+/**
+ * returns whether point is left edge of ancestor or not.
+ * @param {BoundaryPoint} point
+ * @param {Node} ancestor
+ * @return {Boolean}
+ */
+function isLeftEdgePointOf(point, ancestor) {
+ return isLeftEdgePoint(point) && isLeftEdgeOf(point.node, ancestor);
+}
+/**
+ * returns whether point is right edge of ancestor or not.
+ * @param {BoundaryPoint} point
+ * @param {Node} ancestor
+ * @return {Boolean}
+ */
+function isRightEdgePointOf(point, ancestor) {
+ return isRightEdgePoint(point) && isRightEdgeOf(point.node, ancestor);
+}
+/**
+ * returns offset from parent.
+ *
+ * @param {Node} node
+ */
+function position(node) {
+ var offset = 0;
+ while ((node = node.previousSibling)) {
+ offset += 1;
+ }
+ return offset;
+}
+function hasChildren(node) {
+ return !!(node && node.childNodes && node.childNodes.length);
+}
+/**
+ * returns previous boundaryPoint
+ *
+ * @param {BoundaryPoint} point
+ * @param {Boolean} isSkipInnerOffset
+ * @return {BoundaryPoint}
+ */
+function prevPoint(point, isSkipInnerOffset) {
+ var node;
+ var offset;
+ if (point.offset === 0) {
+ if (isEditable(point.node)) {
+ return null;
+ }
+ node = point.node.parentNode;
+ offset = position(point.node);
+ }
+ else if (hasChildren(point.node)) {
+ node = point.node.childNodes[point.offset - 1];
+ offset = nodeLength(node);
+ }
+ else {
+ node = point.node;
+ offset = isSkipInnerOffset ? 0 : point.offset - 1;
+ }
+ return {
+ node: node,
+ offset: offset
+ };
+}
+/**
+ * returns next boundaryPoint
+ *
+ * @param {BoundaryPoint} point
+ * @param {Boolean} isSkipInnerOffset
+ * @return {BoundaryPoint}
+ */
+function nextPoint(point, isSkipInnerOffset) {
+ var node, offset;
+ if (nodeLength(point.node) === point.offset) {
+ if (isEditable(point.node)) {
+ return null;
+ }
+ node = point.node.parentNode;
+ offset = position(point.node) + 1;
+ }
+ else if (hasChildren(point.node)) {
+ node = point.node.childNodes[point.offset];
+ offset = 0;
+ }
+ else {
+ node = point.node;
+ offset = isSkipInnerOffset ? nodeLength(point.node) : point.offset + 1;
+ }
+ return {
+ node: node,
+ offset: offset
+ };
+}
+/**
+ * returns whether pointA and pointB is same or not.
+ *
+ * @param {BoundaryPoint} pointA
+ * @param {BoundaryPoint} pointB
+ * @return {Boolean}
+ */
+function isSamePoint(pointA, pointB) {
+ return pointA.node === pointB.node && pointA.offset === pointB.offset;
+}
+/**
+ * returns whether point is visible (can set cursor) or not.
+ *
+ * @param {BoundaryPoint} point
+ * @return {Boolean}
+ */
+function isVisiblePoint(point) {
+ if (isText(point.node) || !hasChildren(point.node) || isEmpty(point.node)) {
+ return true;
+ }
+ var leftNode = point.node.childNodes[point.offset - 1];
+ var rightNode = point.node.childNodes[point.offset];
+ if ((!leftNode || isVoid(leftNode)) && (!rightNode || isVoid(rightNode))) {
+ return true;
+ }
+ return false;
+}
+/**
+ * @method prevPointUtil
+ *
+ * @param {BoundaryPoint} point
+ * @param {Function} pred
+ * @return {BoundaryPoint}
+ */
+function prevPointUntil(point, pred) {
+ while (point) {
+ if (pred(point)) {
+ return point;
+ }
+ point = prevPoint(point);
+ }
+ return null;
+}
+/**
+ * @method nextPointUntil
+ *
+ * @param {BoundaryPoint} point
+ * @param {Function} pred
+ * @return {BoundaryPoint}
+ */
+function nextPointUntil(point, pred) {
+ while (point) {
+ if (pred(point)) {
+ return point;
+ }
+ point = nextPoint(point);
+ }
+ return null;
+}
+/**
+ * returns whether point has character or not.
+ *
+ * @param {Point} point
+ * @return {Boolean}
+ */
+function isCharPoint(point) {
+ if (!isText(point.node)) {
+ return false;
+ }
+ var ch = point.node.nodeValue.charAt(point.offset - 1);
+ return ch && (ch !== ' ' && ch !== NBSP_CHAR);
+}
+/**
+ * @method walkPoint
+ *
+ * @param {BoundaryPoint} startPoint
+ * @param {BoundaryPoint} endPoint
+ * @param {Function} handler
+ * @param {Boolean} isSkipInnerOffset
+ */
+function walkPoint(startPoint, endPoint, handler, isSkipInnerOffset) {
+ var point = startPoint;
+ while (point) {
+ handler(point);
+ if (isSamePoint(point, endPoint)) {
+ break;
+ }
+ var isSkipOffset = isSkipInnerOffset &&
+ startPoint.node !== point.node &&
+ endPoint.node !== point.node;
+ point = nextPoint(point, isSkipOffset);
+ }
+}
+/**
+ * @method makeOffsetPath
+ *
+ * return offsetPath(array of offset) from ancestor
+ *
+ * @param {Node} ancestor - ancestor node
+ * @param {Node} node
+ */
+function makeOffsetPath(ancestor, node) {
+ var ancestors = listAncestor(node, func.eq(ancestor));
+ return ancestors.map(position).reverse();
+}
+/**
+ * @method fromOffsetPath
+ *
+ * return element from offsetPath(array of offset)
+ *
+ * @param {Node} ancestor - ancestor node
+ * @param {array} offsets - offsetPath
+ */
+function fromOffsetPath(ancestor, offsets) {
+ var current = ancestor;
+ for (var i = 0, len = offsets.length; i < len; i++) {
+ if (current.childNodes.length <= offsets[i]) {
+ current = current.childNodes[current.childNodes.length - 1];
+ }
+ else {
+ current = current.childNodes[offsets[i]];
+ }
+ }
+ return current;
+}
+/**
+ * @method splitNode
+ *
+ * split element or #text
+ *
+ * @param {BoundaryPoint} point
+ * @param {Object} [options]
+ * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
+ * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
+ * @return {Node} right node of boundaryPoint
+ */
+function splitNode(point, options) {
+ var isSkipPaddingBlankHTML = options && options.isSkipPaddingBlankHTML;
+ var isNotSplitEdgePoint = options && options.isNotSplitEdgePoint;
+ // edge case
+ if (isEdgePoint(point) && (isText(point.node) || isNotSplitEdgePoint)) {
+ if (isLeftEdgePoint(point)) {
+ return point.node;
+ }
+ else if (isRightEdgePoint(point)) {
+ return point.node.nextSibling;
+ }
+ }
+ // split #text
+ if (isText(point.node)) {
+ return point.node.splitText(point.offset);
+ }
+ else {
+ var childNode = point.node.childNodes[point.offset];
+ var clone = insertAfter(point.node.cloneNode(false), point.node);
+ appendChildNodes(clone, listNext(childNode));
+ if (!isSkipPaddingBlankHTML) {
+ paddingBlankHTML(point.node);
+ paddingBlankHTML(clone);
+ }
+ return clone;
+ }
+}
+/**
+ * @method splitTree
+ *
+ * split tree by point
+ *
+ * @param {Node} root - split root
+ * @param {BoundaryPoint} point
+ * @param {Object} [options]
+ * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
+ * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
+ * @return {Node} right node of boundaryPoint
+ */
+function splitTree(root, point, options) {
+ // ex) [#text, <span>, <p>]
+ var ancestors = listAncestor(point.node, func.eq(root));
+ if (!ancestors.length) {
+ return null;
+ }
+ else if (ancestors.length === 1) {
+ return splitNode(point, options);
+ }
+ return ancestors.reduce(function (node, parent) {
+ if (node === point.node) {
+ node = splitNode(point, options);
+ }
+ return splitNode({
+ node: parent,
+ offset: node ? position(node) : nodeLength(parent)
+ }, options);
+ });
+}
+/**
+ * split point
+ *
+ * @param {Point} point
+ * @param {Boolean} isInline
+ * @return {Object}
+ */
+function splitPoint(point, isInline) {
+ // find splitRoot, container
+ // - inline: splitRoot is a child of paragraph
+ // - block: splitRoot is a child of bodyContainer
+ var pred = isInline ? isPara : isBodyContainer;
+ var ancestors = listAncestor(point.node, pred);
+ var topAncestor = lists.last(ancestors) || point.node;
+ var splitRoot, container;
+ if (pred(topAncestor)) {
+ splitRoot = ancestors[ancestors.length - 2];
+ container = topAncestor;
+ }
+ else {
+ splitRoot = topAncestor;
+ container = splitRoot.parentNode;
+ }
+ // if splitRoot is exists, split with splitTree
+ var pivot = splitRoot && splitTree(splitRoot, point, {
+ isSkipPaddingBlankHTML: isInline,
+ isNotSplitEdgePoint: isInline
+ });
+ // if container is point.node, find pivot with point.offset
+ if (!pivot && container === point.node) {
+ pivot = point.node.childNodes[point.offset];
+ }
+ return {
+ rightNode: pivot,
+ container: container
+ };
+}
+function create(nodeName) {
+ return document.createElement(nodeName);
+}
+function createText(text) {
+ return document.createTextNode(text);
+}
+/**
+ * @method remove
+ *
+ * remove node, (isRemoveChild: remove child or not)
+ *
+ * @param {Node} node
+ * @param {Boolean} isRemoveChild
+ */
+function remove(node, isRemoveChild) {
+ if (!node || !node.parentNode) {
+ return;
+ }
+ if (node.removeNode) {
+ return node.removeNode(isRemoveChild);
+ }
+ var parent = node.parentNode;
+ if (!isRemoveChild) {
+ var nodes = [];
+ for (var i = 0, len = node.childNodes.length; i < len; i++) {
+ nodes.push(node.childNodes[i]);
+ }
+ for (var i = 0, len = nodes.length; i < len; i++) {
+ parent.insertBefore(nodes[i], node);
+ }
+ }
+ parent.removeChild(node);
+}
+/**
+ * @method removeWhile
+ *
+ * @param {Node} node
+ * @param {Function} pred
+ */
+function removeWhile(node, pred) {
+ while (node) {
+ if (isEditable(node) || !pred(node)) {
+ break;
+ }
+ var parent = node.parentNode;
+ remove(node);
+ node = parent;
+ }
+}
+/**
+ * @method replace
+ *
+ * replace node with provided nodeName
+ *
+ * @param {Node} node
+ * @param {String} nodeName
+ * @return {Node} - new node
+ */
+function replace(node, nodeName) {
+ if (node.nodeName.toUpperCase() === nodeName.toUpperCase()) {
+ return node;
+ }
+ var newNode = create(nodeName);
+ if (node.style.cssText) {
+ newNode.style.cssText = node.style.cssText;
+ }
+ appendChildNodes(newNode, lists.from(node.childNodes));
+ insertAfter(newNode, node);
+ remove(node);
+ return newNode;
+}
+var isTextarea = makePredByNodeName('TEXTAREA');
+/**
+ * @param {jQuery} $node
+ * @param {Boolean} [stripLinebreaks] - default: false
+ */
+function value($node, stripLinebreaks) {
+ var val = isTextarea($node[0]) ? $node.val() : $node.html();
+ if (stripLinebreaks) {
+ return val.replace(/[\n\r]/g, '');
+ }
+ return val;
+}
+/**
+ * @method html
+ *
+ * get the HTML contents of node
+ *
+ * @param {jQuery} $node
+ * @param {Boolean} [isNewlineOnBlock]
+ */
+function html($node, isNewlineOnBlock) {
+ var markup = value($node);
+ if (isNewlineOnBlock) {
+ var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
+ markup = markup.replace(regexTag, function (match, endSlash, name) {
+ name = name.toUpperCase();
+ var isEndOfInlineContainer = /^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(name) &&
+ !!endSlash;
+ var isBlockNode = /^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(name);
+ return match + ((isEndOfInlineContainer || isBlockNode) ? '\n' : '');
+ });
+ markup = $$1.trim(markup);
+ }
+ return markup;
+}
+function posFromPlaceholder(placeholder) {
+ var $placeholder = $$1(placeholder);
+ var pos = $placeholder.offset();
+ var height = $placeholder.outerHeight(true); // include margin
+ return {
+ left: pos.left,
+ top: pos.top + height
+ };
+}
+function attachEvents($node, events) {
+ Object.keys(events).forEach(function (key) {
+ $node.on(key, events[key]);
+ });
+}
+function detachEvents($node, events) {
+ Object.keys(events).forEach(function (key) {
+ $node.off(key, events[key]);
+ });
+}
+/**
+ * @method isCustomStyleTag
+ *
+ * assert if a node contains a "note-styletag" class,
+ * which implies that's a custom-made style tag node
+ *
+ * @param {Node} an HTML DOM node
+ */
+function isCustomStyleTag(node) {
+ return node && !isText(node) && lists.contains(node.classList, 'note-styletag');
+}
+var dom = {
+ /** @property {String} NBSP_CHAR */
+ NBSP_CHAR: NBSP_CHAR,
+ /** @property {String} ZERO_WIDTH_NBSP_CHAR */
+ ZERO_WIDTH_NBSP_CHAR: ZERO_WIDTH_NBSP_CHAR,
+ /** @property {String} blank */
+ blank: blankHTML,
+ /** @property {String} emptyPara */
+ emptyPara: "<p>" + blankHTML + "</p>",
+ makePredByNodeName: makePredByNodeName,
+ isEditable: isEditable,
+ isControlSizing: isControlSizing,
+ isText: isText,
+ isElement: isElement,
+ isVoid: isVoid,
+ isPara: isPara,
+ isPurePara: isPurePara,
+ isHeading: isHeading,
+ isInline: isInline,
+ isBlock: func.not(isInline),
+ isBodyInline: isBodyInline,
+ isBody: isBody,
+ isParaInline: isParaInline,
+ isPre: isPre,
+ isList: isList,
+ isTable: isTable,
+ isData: isData,
+ isCell: isCell,
+ isBlockquote: isBlockquote,
+ isBodyContainer: isBodyContainer,
+ isAnchor: isAnchor,
+ isDiv: makePredByNodeName('DIV'),
+ isLi: isLi,
+ isBR: makePredByNodeName('BR'),
+ isSpan: makePredByNodeName('SPAN'),
+ isB: makePredByNodeName('B'),
+ isU: makePredByNodeName('U'),
+ isS: makePredByNodeName('S'),
+ isI: makePredByNodeName('I'),
+ isImg: makePredByNodeName('IMG'),
+ isTextarea: isTextarea,
+ isEmpty: isEmpty,
+ isEmptyAnchor: func.and(isAnchor, isEmpty),
+ isClosestSibling: isClosestSibling,
+ withClosestSiblings: withClosestSiblings,
+ nodeLength: nodeLength,
+ isLeftEdgePoint: isLeftEdgePoint,
+ isRightEdgePoint: isRightEdgePoint,
+ isEdgePoint: isEdgePoint,
+ isLeftEdgeOf: isLeftEdgeOf,
+ isRightEdgeOf: isRightEdgeOf,
+ isLeftEdgePointOf: isLeftEdgePointOf,
+ isRightEdgePointOf: isRightEdgePointOf,
+ prevPoint: prevPoint,
+ nextPoint: nextPoint,
+ isSamePoint: isSamePoint,
+ isVisiblePoint: isVisiblePoint,
+ prevPointUntil: prevPointUntil,
+ nextPointUntil: nextPointUntil,
+ isCharPoint: isCharPoint,
+ walkPoint: walkPoint,
+ ancestor: ancestor,
+ singleChildAncestor: singleChildAncestor,
+ listAncestor: listAncestor,
+ lastAncestor: lastAncestor,
+ listNext: listNext,
+ listPrev: listPrev,
+ listDescendant: listDescendant,
+ commonAncestor: commonAncestor,
+ wrap: wrap,
+ insertAfter: insertAfter,
+ appendChildNodes: appendChildNodes,
+ position: position,
+ hasChildren: hasChildren,
+ makeOffsetPath: makeOffsetPath,
+ fromOffsetPath: fromOffsetPath,
+ splitTree: splitTree,
+ splitPoint: splitPoint,
+ create: create,
+ createText: createText,
+ remove: remove,
+ removeWhile: removeWhile,
+ replace: replace,
+ html: html,
+ value: value,
+ posFromPlaceholder: posFromPlaceholder,
+ attachEvents: attachEvents,
+ detachEvents: detachEvents,
+ isCustomStyleTag: isCustomStyleTag
+};
+
+$$1.summernote = $$1.summernote || {
+ lang: {}
+};
+$$1.extend($$1.summernote.lang, {
+ 'en-US': {
+ font: {
+ bold: 'Bold',
+ italic: 'Italic',
+ underline: 'Underline',
+ clear: 'Remove Font Style',
+ height: 'Line Height',
+ name: 'Font Family',
+ strikethrough: 'Strikethrough',
+ subscript: 'Subscript',
+ superscript: 'Superscript',
+ size: 'Font Size'
+ },
+ image: {
+ image: 'Picture',
+ insert: 'Insert Image',
+ resizeFull: 'Resize Full',
+ resizeHalf: 'Resize Half',
+ resizeQuarter: 'Resize Quarter',
+ floatLeft: 'Float Left',
+ floatRight: 'Float Right',
+ floatNone: 'Float None',
+ shapeRounded: 'Shape: Rounded',
+ shapeCircle: 'Shape: Circle',
+ shapeThumbnail: 'Shape: Thumbnail',
+ shapeNone: 'Shape: None',
+ dragImageHere: 'Drag image or text here',
+ dropImage: 'Drop image or Text',
+ selectFromFiles: 'Select from files',
+ maximumFileSize: 'Maximum file size',
+ maximumFileSizeError: 'Maximum file size exceeded.',
+ url: 'Image URL',
+ remove: 'Remove Image',
+ original: 'Original'
+ },
+ video: {
+ video: 'Video',
+ videoLink: 'Video Link',
+ insert: 'Insert Video',
+ url: 'Video URL',
+ providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)'
+ },
+ link: {
+ link: 'Link',
+ insert: 'Insert Link',
+ unlink: 'Unlink',
+ edit: 'Edit',
+ textToDisplay: 'Text to display',
+ url: 'To what URL should this link go?',
+ openInNewWindow: 'Open in new window'
+ },
+ table: {
+ table: 'Table',
+ addRowAbove: 'Add row above',
+ addRowBelow: 'Add row below',
+ addColLeft: 'Add column left',
+ addColRight: 'Add column right',
+ delRow: 'Delete row',
+ delCol: 'Delete column',
+ delTable: 'Delete table'
+ },
+ hr: {
+ insert: 'Insert Horizontal Rule'
+ },
+ style: {
+ style: 'Style',
+ p: 'Normal',
+ blockquote: 'Quote',
+ pre: 'Code',
+ h1: 'Header 1',
+ h2: 'Header 2',
+ h3: 'Header 3',
+ h4: 'Header 4',
+ h5: 'Header 5',
+ h6: 'Header 6'
+ },
+ lists: {
+ unordered: 'Unordered list',
+ ordered: 'Ordered list'
+ },
+ options: {
+ help: 'Help',
+ fullscreen: 'Full Screen',
+ codeview: 'Code View'
+ },
+ paragraph: {
+ paragraph: 'Paragraph',
+ outdent: 'Outdent',
+ indent: 'Indent',
+ left: 'Align left',
+ center: 'Align center',
+ right: 'Align right',
+ justify: 'Justify full'
+ },
+ color: {
+ recent: 'Recent Color',
+ more: 'More Color',
+ background: 'Background Color',
+ foreground: 'Foreground Color',
+ transparent: 'Transparent',
+ setTransparent: 'Set transparent',
+ reset: 'Reset',
+ resetToDefault: 'Reset to default'
+ },
+ shortcut: {
+ shortcuts: 'Keyboard shortcuts',
+ close: 'Close',
+ textFormatting: 'Text formatting',
+ action: 'Action',
+ paragraphFormatting: 'Paragraph formatting',
+ documentStyle: 'Document Style',
+ extraKeys: 'Extra keys'
+ },
+ help: {
+ 'insertParagraph': 'Insert Paragraph',
+ 'undo': 'Undoes the last command',
+ 'redo': 'Redoes the last command',
+ 'tab': 'Tab',
+ 'untab': 'Untab',
+ 'bold': 'Set a bold style',
+ 'italic': 'Set a italic style',
+ 'underline': 'Set a underline style',
+ 'strikethrough': 'Set a strikethrough style',
+ 'removeFormat': 'Clean a style',
+ 'justifyLeft': 'Set left align',
+ 'justifyCenter': 'Set center align',
+ 'justifyRight': 'Set right align',
+ 'justifyFull': 'Set full align',
+ 'insertUnorderedList': 'Toggle unordered list',
+ 'insertOrderedList': 'Toggle ordered list',
+ 'outdent': 'Outdent on current paragraph',
+ 'indent': 'Indent on current paragraph',
+ 'formatPara': 'Change current block\'s format as a paragraph(P tag)',
+ 'formatH1': 'Change current block\'s format as H1',
+ 'formatH2': 'Change current block\'s format as H2',
+ 'formatH3': 'Change current block\'s format as H3',
+ 'formatH4': 'Change current block\'s format as H4',
+ 'formatH5': 'Change current block\'s format as H5',
+ 'formatH6': 'Change current block\'s format as H6',
+ 'insertHorizontalRule': 'Insert horizontal rule',
+ 'linkDialog.show': 'Show Link Dialog'
+ },
+ history: {
+ undo: 'Undo',
+ redo: 'Redo'
+ },
+ specialChar: {
+ specialChar: 'SPECIAL CHARACTERS',
+ select: 'Select Special characters'
+ }
+ }
+});
+
+var KEY_MAP = {
+ 'BACKSPACE': 8,
+ 'TAB': 9,
+ 'ENTER': 13,
+ 'SPACE': 32,
+ 'DELETE': 46,
+ // Arrow
+ 'LEFT': 37,
+ 'UP': 38,
+ 'RIGHT': 39,
+ 'DOWN': 40,
+ // Number: 0-9
+ 'NUM0': 48,
+ 'NUM1': 49,
+ 'NUM2': 50,
+ 'NUM3': 51,
+ 'NUM4': 52,
+ 'NUM5': 53,
+ 'NUM6': 54,
+ 'NUM7': 55,
+ 'NUM8': 56,
+ // Alphabet: a-z
+ 'B': 66,
+ 'E': 69,
+ 'I': 73,
+ 'J': 74,
+ 'K': 75,
+ 'L': 76,
+ 'R': 82,
+ 'S': 83,
+ 'U': 85,
+ 'V': 86,
+ 'Y': 89,
+ 'Z': 90,
+ 'SLASH': 191,
+ 'LEFTBRACKET': 219,
+ 'BACKSLASH': 220,
+ 'RIGHTBRACKET': 221
+};
+/**
+ * @class core.key
+ *
+ * Object for keycodes.
+ *
+ * @singleton
+ * @alternateClassName key
+ */
+var key = {
+ /**
+ * @method isEdit
+ *
+ * @param {Number} keyCode
+ * @return {Boolean}
+ */
+ isEdit: function (keyCode) {
+ return lists.contains([
+ KEY_MAP.BACKSPACE,
+ KEY_MAP.TAB,
+ KEY_MAP.ENTER,
+ KEY_MAP.SPACE,
+ KEY_MAP.DELETE
+ ], keyCode);
+ },
+ /**
+ * @method isMove
+ *
+ * @param {Number} keyCode
+ * @return {Boolean}
+ */
+ isMove: function (keyCode) {
+ return lists.contains([
+ KEY_MAP.LEFT,
+ KEY_MAP.UP,
+ KEY_MAP.RIGHT,
+ KEY_MAP.DOWN
+ ], keyCode);
+ },
+ /**
+ * @property {Object} nameFromCode
+ * @property {String} nameFromCode.8 "BACKSPACE"
+ */
+ nameFromCode: func.invertObject(KEY_MAP),
+ code: KEY_MAP
+};
+
+/**
+ * return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
+ *
+ * @param {TextRange} textRange
+ * @param {Boolean} isStart
+ * @return {BoundaryPoint}
+ *
+ * @see http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
+ */
+function textRangeToPoint(textRange, isStart) {
+ var container = textRange.parentElement();
+ var offset;
+ var tester = document.body.createTextRange();
+ var prevContainer;
+ var childNodes = lists.from(container.childNodes);
+ for (offset = 0; offset < childNodes.length; offset++) {
+ if (dom.isText(childNodes[offset])) {
+ continue;
+ }
+ tester.moveToElementText(childNodes[offset]);
+ if (tester.compareEndPoints('StartToStart', textRange) >= 0) {
+ break;
+ }
+ prevContainer = childNodes[offset];
+ }
+ if (offset !== 0 && dom.isText(childNodes[offset - 1])) {
+ var textRangeStart = document.body.createTextRange();
+ var curTextNode = null;
+ textRangeStart.moveToElementText(prevContainer || container);
+ textRangeStart.collapse(!prevContainer);
+ curTextNode = prevContainer ? prevContainer.nextSibling : container.firstChild;
+ var pointTester = textRange.duplicate();
+ pointTester.setEndPoint('StartToStart', textRangeStart);
+ var textCount = pointTester.text.replace(/[\r\n]/g, '').length;
+ while (textCount > curTextNode.nodeValue.length && curTextNode.nextSibling) {
+ textCount -= curTextNode.nodeValue.length;
+ curTextNode = curTextNode.nextSibling;
+ }
+ // [workaround] enforce IE to re-reference curTextNode, hack
+ var dummy = curTextNode.nodeValue; // eslint-disable-line
+ if (isStart && curTextNode.nextSibling && dom.isText(curTextNode.nextSibling) &&
+ textCount === curTextNode.nodeValue.length) {
+ textCount -= curTextNode.nodeValue.length;
+ curTextNode = curTextNode.nextSibling;
+ }
+ container = curTextNode;
+ offset = textCount;
+ }
+ return {
+ cont: container,
+ offset: offset
+ };
+}
+/**
+ * return TextRange from boundary point (inspired by google closure-library)
+ * @param {BoundaryPoint} point
+ * @return {TextRange}
+ */
+function pointToTextRange(point) {
+ var textRangeInfo = function (container, offset) {
+ var node, isCollapseToStart;
+ if (dom.isText(container)) {
+ var prevTextNodes = dom.listPrev(container, func.not(dom.isText));
+ var prevContainer = lists.last(prevTextNodes).previousSibling;
+ node = prevContainer || container.parentNode;
+ offset += lists.sum(lists.tail(prevTextNodes), dom.nodeLength);
+ isCollapseToStart = !prevContainer;
+ }
+ else {
+ node = container.childNodes[offset] || container;
+ if (dom.isText(node)) {
+ return textRangeInfo(node, 0);
+ }
+ offset = 0;
+ isCollapseToStart = false;
+ }
+ return {
+ node: node,
+ collapseToStart: isCollapseToStart,
+ offset: offset
+ };
+ };
+ var textRange = document.body.createTextRange();
+ var info = textRangeInfo(point.node, point.offset);
+ textRange.moveToElementText(info.node);
+ textRange.collapse(info.collapseToStart);
+ textRange.moveStart('character', info.offset);
+ return textRange;
+}
+/**
+ * Wrapped Range
+ *
+ * @constructor
+ * @param {Node} sc - start container
+ * @param {Number} so - start offset
+ * @param {Node} ec - end container
+ * @param {Number} eo - end offset
+ */
+var WrappedRange = /** @class */ (function () {
+ function WrappedRange(sc, so, ec, eo) {
+ this.sc = sc;
+ this.so = so;
+ this.ec = ec;
+ this.eo = eo;
+ // isOnEditable: judge whether range is on editable or not
+ this.isOnEditable = this.makeIsOn(dom.isEditable);
+ // isOnList: judge whether range is on list node or not
+ this.isOnList = this.makeIsOn(dom.isList);
+ // isOnAnchor: judge whether range is on anchor node or not
+ this.isOnAnchor = this.makeIsOn(dom.isAnchor);
+ // isOnCell: judge whether range is on cell node or not
+ this.isOnCell = this.makeIsOn(dom.isCell);
+ // isOnData: judge whether range is on data node or not
+ this.isOnData = this.makeIsOn(dom.isData);
+ }
+ // nativeRange: get nativeRange from sc, so, ec, eo
+ WrappedRange.prototype.nativeRange = function () {
+ if (env.isW3CRangeSupport) {
+ var w3cRange = document.createRange();
+ w3cRange.setStart(this.sc, this.so);
+ w3cRange.setEnd(this.ec, this.eo);
+ return w3cRange;
+ }
+ else {
+ var textRange = pointToTextRange({
+ node: this.sc,
+ offset: this.so
+ });
+ textRange.setEndPoint('EndToEnd', pointToTextRange({
+ node: this.ec,
+ offset: this.eo
+ }));
+ return textRange;
+ }
+ };
+ WrappedRange.prototype.getPoints = function () {
+ return {
+ sc: this.sc,
+ so: this.so,
+ ec: this.ec,
+ eo: this.eo
+ };
+ };
+ WrappedRange.prototype.getStartPoint = function () {
+ return {
+ node: this.sc,
+ offset: this.so
+ };
+ };
+ WrappedRange.prototype.getEndPoint = function () {
+ return {
+ node: this.ec,
+ offset: this.eo
+ };
+ };
+ /**
+ * select update visible range
+ */
+ WrappedRange.prototype.select = function () {
+ var nativeRng = this.nativeRange();
+ if (env.isW3CRangeSupport) {
+ var selection = document.getSelection();
+ if (selection.rangeCount > 0) {
+ selection.removeAllRanges();
+ }
+ selection.addRange(nativeRng);
+ }
+ else {
+ nativeRng.select();
+ }
+ return this;
+ };
+ /**
+ * Moves the scrollbar to start container(sc) of current range
+ *
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.scrollIntoView = function (container) {
+ var height = $$1(container).height();
+ if (container.scrollTop + height < this.sc.offsetTop) {
+ container.scrollTop += Math.abs(container.scrollTop + height - this.sc.offsetTop);
+ }
+ return this;
+ };
+ /**
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.normalize = function () {
+ /**
+ * @param {BoundaryPoint} point
+ * @param {Boolean} isLeftToRight
+ * @return {BoundaryPoint}
+ */
+ var getVisiblePoint = function (point, isLeftToRight) {
+ if ((dom.isVisiblePoint(point) && !dom.isEdgePoint(point)) ||
+ (dom.isVisiblePoint(point) && dom.isRightEdgePoint(point) && !isLeftToRight) ||
+ (dom.isVisiblePoint(point) && dom.isLeftEdgePoint(point) && isLeftToRight) ||
+ (dom.isVisiblePoint(point) && dom.isBlock(point.node) && dom.isEmpty(point.node))) {
+ return point;
+ }
+ // point on block's edge
+ var block = dom.ancestor(point.node, dom.isBlock);
+ if (((dom.isLeftEdgePointOf(point, block) || dom.isVoid(dom.prevPoint(point).node)) && !isLeftToRight) ||
+ ((dom.isRightEdgePointOf(point, block) || dom.isVoid(dom.nextPoint(point).node)) && isLeftToRight)) {
+ // returns point already on visible point
+ if (dom.isVisiblePoint(point)) {
+ return point;
+ }
+ // reverse direction
+ isLeftToRight = !isLeftToRight;
+ }
+ var nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint)
+ : dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint);
+ return nextPoint || point;
+ };
+ var endPoint = getVisiblePoint(this.getEndPoint(), false);
+ var startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true);
+ return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
+ };
+ /**
+ * returns matched nodes on range
+ *
+ * @param {Function} [pred] - predicate function
+ * @param {Object} [options]
+ * @param {Boolean} [options.includeAncestor]
+ * @param {Boolean} [options.fullyContains]
+ * @return {Node[]}
+ */
+ WrappedRange.prototype.nodes = function (pred, options) {
+ pred = pred || func.ok;
+ var includeAncestor = options && options.includeAncestor;
+ var fullyContains = options && options.fullyContains;
+ // TODO compare points and sort
+ var startPoint = this.getStartPoint();
+ var endPoint = this.getEndPoint();
+ var nodes = [];
+ var leftEdgeNodes = [];
+ dom.walkPoint(startPoint, endPoint, function (point) {
+ if (dom.isEditable(point.node)) {
+ return;
+ }
+ var node;
+ if (fullyContains) {
+ if (dom.isLeftEdgePoint(point)) {
+ leftEdgeNodes.push(point.node);
+ }
+ if (dom.isRightEdgePoint(point) && lists.contains(leftEdgeNodes, point.node)) {
+ node = point.node;
+ }
+ }
+ else if (includeAncestor) {
+ node = dom.ancestor(point.node, pred);
+ }
+ else {
+ node = point.node;
+ }
+ if (node && pred(node)) {
+ nodes.push(node);
+ }
+ }, true);
+ return lists.unique(nodes);
+ };
+ /**
+ * returns commonAncestor of range
+ * @return {Element} - commonAncestor
+ */
+ WrappedRange.prototype.commonAncestor = function () {
+ return dom.commonAncestor(this.sc, this.ec);
+ };
+ /**
+ * returns expanded range by pred
+ *
+ * @param {Function} pred - predicate function
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.expand = function (pred) {
+ var startAncestor = dom.ancestor(this.sc, pred);
+ var endAncestor = dom.ancestor(this.ec, pred);
+ if (!startAncestor && !endAncestor) {
+ return new WrappedRange(this.sc, this.so, this.ec, this.eo);
+ }
+ var boundaryPoints = this.getPoints();
+ if (startAncestor) {
+ boundaryPoints.sc = startAncestor;
+ boundaryPoints.so = 0;
+ }
+ if (endAncestor) {
+ boundaryPoints.ec = endAncestor;
+ boundaryPoints.eo = dom.nodeLength(endAncestor);
+ }
+ return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
+ };
+ /**
+ * @param {Boolean} isCollapseToStart
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.collapse = function (isCollapseToStart) {
+ if (isCollapseToStart) {
+ return new WrappedRange(this.sc, this.so, this.sc, this.so);
+ }
+ else {
+ return new WrappedRange(this.ec, this.eo, this.ec, this.eo);
+ }
+ };
+ /**
+ * splitText on range
+ */
+ WrappedRange.prototype.splitText = function () {
+ var isSameContainer = this.sc === this.ec;
+ var boundaryPoints = this.getPoints();
+ if (dom.isText(this.ec) && !dom.isEdgePoint(this.getEndPoint())) {
+ this.ec.splitText(this.eo);
+ }
+ if (dom.isText(this.sc) && !dom.isEdgePoint(this.getStartPoint())) {
+ boundaryPoints.sc = this.sc.splitText(this.so);
+ boundaryPoints.so = 0;
+ if (isSameContainer) {
+ boundaryPoints.ec = boundaryPoints.sc;
+ boundaryPoints.eo = this.eo - this.so;
+ }
+ }
+ return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
+ };
+ /**
+ * delete contents on range
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.deleteContents = function () {
+ if (this.isCollapsed()) {
+ return this;
+ }
+ var rng = this.splitText();
+ var nodes = rng.nodes(null, {
+ fullyContains: true
+ });
+ // find new cursor point
+ var point = dom.prevPointUntil(rng.getStartPoint(), function (point) {
+ return !lists.contains(nodes, point.node);
+ });
+ var emptyParents = [];
+ $$1.each(nodes, function (idx, node) {
+ // find empty parents
+ var parent = node.parentNode;
+ if (point.node !== parent && dom.nodeLength(parent) === 1) {
+ emptyParents.push(parent);
+ }
+ dom.remove(node, false);
+ });
+ // remove empty parents
+ $$1.each(emptyParents, function (idx, node) {
+ dom.remove(node, false);
+ });
+ return new WrappedRange(point.node, point.offset, point.node, point.offset).normalize();
+ };
+ /**
+ * makeIsOn: return isOn(pred) function
+ */
+ WrappedRange.prototype.makeIsOn = function (pred) {
+ return function () {
+ var ancestor = dom.ancestor(this.sc, pred);
+ return !!ancestor && (ancestor === dom.ancestor(this.ec, pred));
+ };
+ };
+ /**
+ * @param {Function} pred
+ * @return {Boolean}
+ */
+ WrappedRange.prototype.isLeftEdgeOf = function (pred) {
+ if (!dom.isLeftEdgePoint(this.getStartPoint())) {
+ return false;
+ }
+ var node = dom.ancestor(this.sc, pred);
+ return node && dom.isLeftEdgeOf(this.sc, node);
+ };
+ /**
+ * returns whether range was collapsed or not
+ */
+ WrappedRange.prototype.isCollapsed = function () {
+ return this.sc === this.ec && this.so === this.eo;
+ };
+ /**
+ * wrap inline nodes which children of body with paragraph
+ *
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.wrapBodyInlineWithPara = function () {
+ if (dom.isBodyContainer(this.sc) && dom.isEmpty(this.sc)) {
+ this.sc.innerHTML = dom.emptyPara;
+ return new WrappedRange(this.sc.firstChild, 0, this.sc.firstChild, 0);
+ }
+ /**
+ * [workaround] firefox often create range on not visible point. so normalize here.
+ * - firefox: |<p>text</p>|
+ * - chrome: <p>|text|</p>
+ */
+ var rng = this.normalize();
+ if (dom.isParaInline(this.sc) || dom.isPara(this.sc)) {
+ return rng;
+ }
+ // find inline top ancestor
+ var topAncestor;
+ if (dom.isInline(rng.sc)) {
+ var ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline));
+ topAncestor = lists.last(ancestors);
+ if (!dom.isInline(topAncestor)) {
+ topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so];
+ }
+ }
+ else {
+ topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0];
+ }
+ // siblings not in paragraph
+ var inlineSiblings = dom.listPrev(topAncestor, dom.isParaInline).reverse();
+ inlineSiblings = inlineSiblings.concat(dom.listNext(topAncestor.nextSibling, dom.isParaInline));
+ // wrap with paragraph
+ if (inlineSiblings.length) {
+ var para = dom.wrap(lists.head(inlineSiblings), 'p');
+ dom.appendChildNodes(para, lists.tail(inlineSiblings));
+ }
+ return this.normalize();
+ };
+ /**
+ * insert node at current cursor
+ *
+ * @param {Node} node
+ * @return {Node}
+ */
+ WrappedRange.prototype.insertNode = function (node) {
+ var rng = this.wrapBodyInlineWithPara().deleteContents();
+ var info = dom.splitPoint(rng.getStartPoint(), dom.isInline(node));
+ if (info.rightNode) {
+ info.rightNode.parentNode.insertBefore(node, info.rightNode);
+ }
+ else {
+ info.container.appendChild(node);
+ }
+ return node;
+ };
+ /**
+ * insert html at current cursor
+ */
+ WrappedRange.prototype.pasteHTML = function (markup) {
+ var contentsContainer = $$1('<div></div>').html(markup)[0];
+ var childNodes = lists.from(contentsContainer.childNodes);
+ var rng = this.wrapBodyInlineWithPara().deleteContents();
+ return childNodes.reverse().map(function (childNode) {
+ return rng.insertNode(childNode);
+ }).reverse();
+ };
+ /**
+ * returns text in range
+ *
+ * @return {String}
+ */
+ WrappedRange.prototype.toString = function () {
+ var nativeRng = this.nativeRange();
+ return env.isW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
+ };
+ /**
+ * returns range for word before cursor
+ *
+ * @param {Boolean} [findAfter] - find after cursor, default: false
+ * @return {WrappedRange}
+ */
+ WrappedRange.prototype.getWordRange = function (findAfter) {
+ var endPoint = this.getEndPoint();
+ if (!dom.isCharPoint(endPoint)) {
+ return this;
+ }
+ var startPoint = dom.prevPointUntil(endPoint, function (point) {
+ return !dom.isCharPoint(point);
+ });
+ if (findAfter) {
+ endPoint = dom.nextPointUntil(endPoint, function (point) {
+ return !dom.isCharPoint(point);
+ });
+ }
+ return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
+ };
+ /**
+ * create offsetPath bookmark
+ *
+ * @param {Node} editable
+ */
+ WrappedRange.prototype.bookmark = function (editable) {
+ return {
+ s: {
+ path: dom.makeOffsetPath(editable, this.sc),
+ offset: this.so
+ },
+ e: {
+ path: dom.makeOffsetPath(editable, this.ec),
+ offset: this.eo
+ }
+ };
+ };
+ /**
+ * create offsetPath bookmark base on paragraph
+ *
+ * @param {Node[]} paras
+ */
+ WrappedRange.prototype.paraBookmark = function (paras) {
+ return {
+ s: {
+ path: lists.tail(dom.makeOffsetPath(lists.head(paras), this.sc)),
+ offset: this.so
+ },
+ e: {
+ path: lists.tail(dom.makeOffsetPath(lists.last(paras), this.ec)),
+ offset: this.eo
+ }
+ };
+ };
+ /**
+ * getClientRects
+ * @return {Rect[]}
+ */
+ WrappedRange.prototype.getClientRects = function () {
+ var nativeRng = this.nativeRange();
+ return nativeRng.getClientRects();
+ };
+ return WrappedRange;
+}());
+/**
+ * Data structure
+ * * BoundaryPoint: a point of dom tree
+ * * BoundaryPoints: two boundaryPoints corresponding to the start and the end of the Range
+ *
+ * See to http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Position
+ */
+var range = {
+ /**
+ * create Range Object From arguments or Browser Selection
+ *
+ * @param {Node} sc - start container
+ * @param {Number} so - start offset
+ * @param {Node} ec - end container
+ * @param {Number} eo - end offset
+ * @return {WrappedRange}
+ */
+ create: function (sc, so, ec, eo) {
+ if (arguments.length === 4) {
+ return new WrappedRange(sc, so, ec, eo);
+ }
+ else if (arguments.length === 2) {
+ ec = sc;
+ eo = so;
+ return new WrappedRange(sc, so, ec, eo);
+ }
+ else {
+ var wrappedRange = this.createFromSelection();
+ if (!wrappedRange && arguments.length === 1) {
+ wrappedRange = this.createFromNode(arguments[0]);
+ return wrappedRange.collapse(dom.emptyPara === arguments[0].innerHTML);
+ }
+ return wrappedRange;
+ }
+ },
+ createFromSelection: function () {
+ var sc, so, ec, eo;
+ if (env.isW3CRangeSupport) {
+ var selection = document.getSelection();
+ if (!selection || selection.rangeCount === 0) {
+ return null;
+ }
+ else if (dom.isBody(selection.anchorNode)) {
+ // Firefox: returns entire body as range on initialization.
+ // We won't never need it.
+ return null;
+ }
+ var nativeRng = selection.getRangeAt(0);
+ sc = nativeRng.startContainer;
+ so = nativeRng.startOffset;
+ ec = nativeRng.endContainer;
+ eo = nativeRng.endOffset;
+ }
+ else {
+ var textRange = document.selection.createRange();
+ var textRangeEnd = textRange.duplicate();
+ textRangeEnd.collapse(false);
+ var textRangeStart = textRange;
+ textRangeStart.collapse(true);
+ var startPoint = textRangeToPoint(textRangeStart, true);
+ var endPoint = textRangeToPoint(textRangeEnd, false);
+ // same visible point case: range was collapsed.
+ if (dom.isText(startPoint.node) && dom.isLeftEdgePoint(startPoint) &&
+ dom.isTextNode(endPoint.node) && dom.isRightEdgePoint(endPoint) &&
+ endPoint.node.nextSibling === startPoint.node) {
+ startPoint = endPoint;
+ }
+ sc = startPoint.cont;
+ so = startPoint.offset;
+ ec = endPoint.cont;
+ eo = endPoint.offset;
+ }
+ return new WrappedRange(sc, so, ec, eo);
+ },
+ /**
+ * @method
+ *
+ * create WrappedRange from node
+ *
+ * @param {Node} node
+ * @return {WrappedRange}
+ */
+ createFromNode: function (node) {
+ var sc = node;
+ var so = 0;
+ var ec = node;
+ var eo = dom.nodeLength(ec);
+ // browsers can't target a picture or void node
+ if (dom.isVoid(sc)) {
+ so = dom.listPrev(sc).length - 1;
+ sc = sc.parentNode;
+ }
+ if (dom.isBR(ec)) {
+ eo = dom.listPrev(ec).length - 1;
+ ec = ec.parentNode;
+ }
+ else if (dom.isVoid(ec)) {
+ eo = dom.listPrev(ec).length;
+ ec = ec.parentNode;
+ }
+ return this.create(sc, so, ec, eo);
+ },
+ /**
+ * create WrappedRange from node after position
+ *
+ * @param {Node} node
+ * @return {WrappedRange}
+ */
+ createFromNodeBefore: function (node) {
+ return this.createFromNode(node).collapse(true);
+ },
+ /**
+ * create WrappedRange from node after position
+ *
+ * @param {Node} node
+ * @return {WrappedRange}
+ */
+ createFromNodeAfter: function (node) {
+ return this.createFromNode(node).collapse();
+ },
+ /**
+ * @method
+ *
+ * create WrappedRange from bookmark
+ *
+ * @param {Node} editable
+ * @param {Object} bookmark
+ * @return {WrappedRange}
+ */
+ createFromBookmark: function (editable, bookmark) {
+ var sc = dom.fromOffsetPath(editable, bookmark.s.path);
+ var so = bookmark.s.offset;
+ var ec = dom.fromOffsetPath(editable, bookmark.e.path);
+ var eo = bookmark.e.offset;
+ return new WrappedRange(sc, so, ec, eo);
+ },
+ /**
+ * @method
+ *
+ * create WrappedRange from paraBookmark
+ *
+ * @param {Object} bookmark
+ * @param {Node[]} paras
+ * @return {WrappedRange}
+ */
+ createFromParaBookmark: function (bookmark, paras) {
+ var so = bookmark.s.offset;
+ var eo = bookmark.e.offset;
+ var sc = dom.fromOffsetPath(lists.head(paras), bookmark.s.path);
+ var ec = dom.fromOffsetPath(lists.last(paras), bookmark.e.path);
+ return new WrappedRange(sc, so, ec, eo);
+ }
+};
+
+/**
+ * @method readFileAsDataURL
+ *
+ * read contents of file as representing URL
+ *
+ * @param {File} file
+ * @return {Promise} - then: dataUrl
+ */
+function readFileAsDataURL(file) {
+ return $$1.Deferred(function (deferred) {
+ $$1.extend(new FileReader(), {
+ onload: function (e) {
+ var dataURL = e.target.result;
+ deferred.resolve(dataURL);
+ },
+ onerror: function (err) {
+ deferred.reject(err);
+ }
+ }).readAsDataURL(file);
+ }).promise();
+}
+/**
+ * @method createImage
+ *
+ * create `<image>` from url string
+ *
+ * @param {String} url
+ * @return {Promise} - then: $image
+ */
+function createImage(url) {
+ return $$1.Deferred(function (deferred) {
+ var $img = $$1('<img>');
+ $img.one('load', function () {
+ $img.off('error abort');
+ deferred.resolve($img);
+ }).one('error abort', function () {
+ $img.off('load').detach();
+ deferred.reject($img);
+ }).css({
+ display: 'none'
+ }).appendTo(document.body).attr('src', url);
+ }).promise();
+}
+
+var History = /** @class */ (function () {
+ function History($editable) {
+ this.stack = [];
+ this.stackOffset = -1;
+ this.$editable = $editable;
+ this.editable = $editable[0];
+ }
+ History.prototype.makeSnapshot = function () {
+ var rng = range.create(this.editable);
+ var emptyBookmark = { s: { path: [], offset: 0 }, e: { path: [], offset: 0 } };
+ return {
+ contents: this.$editable.html(),
+ bookmark: (rng ? rng.bookmark(this.editable) : emptyBookmark)
+ };
+ };
+ History.prototype.applySnapshot = function (snapshot) {
+ if (snapshot.contents !== null) {
+ this.$editable.html(snapshot.contents);
+ }
+ if (snapshot.bookmark !== null) {
+ range.createFromBookmark(this.editable, snapshot.bookmark).select();
+ }
+ };
+ /**
+ * @method rewind
+ * Rewinds the history stack back to the first snapshot taken.
+ * Leaves the stack intact, so that "Redo" can still be used.
+ */
+ History.prototype.rewind = function () {
+ // Create snap shot if not yet recorded
+ if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
+ this.recordUndo();
+ }
+ // Return to the first available snapshot.
+ this.stackOffset = 0;
+ // Apply that snapshot.
+ this.applySnapshot(this.stack[this.stackOffset]);
+ };
+ /**
+ * @method reset
+ * Resets the history stack completely; reverting to an empty editor.
+ */
+ History.prototype.reset = function () {
+ // Clear the stack.
+ this.stack = [];
+ // Restore stackOffset to its original value.
+ this.stackOffset = -1;
+ // Clear the editable area.
+ this.$editable.html('');
+ // Record our first snapshot (of nothing).
+ this.recordUndo();
+ };
+ /**
+ * undo
+ */
+ History.prototype.undo = function () {
+ // Create snap shot if not yet recorded
+ if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
+ this.recordUndo();
+ }
+ if (this.stackOffset > 0) {
+ this.stackOffset--;
+ this.applySnapshot(this.stack[this.stackOffset]);
+ }
+ };
+ /**
+ * redo
+ */
+ History.prototype.redo = function () {
+ if (this.stack.length - 1 > this.stackOffset) {
+ this.stackOffset++;
+ this.applySnapshot(this.stack[this.stackOffset]);
+ }
+ };
+ /**
+ * recorded undo
+ */
+ History.prototype.recordUndo = function () {
+ this.stackOffset++;
+ // Wash out stack after stackOffset
+ if (this.stack.length > this.stackOffset) {
+ this.stack = this.stack.slice(0, this.stackOffset);
+ }
+ // Create new snapshot and push it to the end
+ this.stack.push(this.makeSnapshot());
+ };
+ return History;
+}());
+
+var Style = /** @class */ (function () {
+ function Style() {
+ }
+ /**
+ * @method jQueryCSS
+ *
+ * [workaround] for old jQuery
+ * passing an array of style properties to .css()
+ * will result in an object of property-value pairs.
+ * (compability with version < 1.9)
+ *
+ * @private
+ * @param {jQuery} $obj
+ * @param {Array} propertyNames - An array of one or more CSS properties.
+ * @return {Object}
+ */
+ Style.prototype.jQueryCSS = function ($obj, propertyNames) {
+ if (env.jqueryVersion < 1.9) {
+ var result_1 = {};
+ $$1.each(propertyNames, function (idx, propertyName) {
+ result_1[propertyName] = $obj.css(propertyName);
+ });
+ return result_1;
+ }
+ return $obj.css(propertyNames);
+ };
+ /**
+ * returns style object from node
+ *
+ * @param {jQuery} $node
+ * @return {Object}
+ */
+ Style.prototype.fromNode = function ($node) {
+ var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
+ var styleInfo = this.jQueryCSS($node, properties) || {};
+ styleInfo['font-size'] = parseInt(styleInfo['font-size'], 10);
+ return styleInfo;
+ };
+ /**
+ * paragraph level style
+ *
+ * @param {WrappedRange} rng
+ * @param {Object} styleInfo
+ */
+ Style.prototype.stylePara = function (rng, styleInfo) {
+ $$1.each(rng.nodes(dom.isPara, {
+ includeAncestor: true
+ }), function (idx, para) {
+ $$1(para).css(styleInfo);
+ });
+ };
+ /**
+ * insert and returns styleNodes on range.
+ *
+ * @param {WrappedRange} rng
+ * @param {Object} [options] - options for styleNodes
+ * @param {String} [options.nodeName] - default: `SPAN`
+ * @param {Boolean} [options.expandClosestSibling] - default: `false`
+ * @param {Boolean} [options.onlyPartialContains] - default: `false`
+ * @return {Node[]}
+ */
+ Style.prototype.styleNodes = function (rng, options) {
+ rng = rng.splitText();
+ var nodeName = (options && options.nodeName) || 'SPAN';
+ var expandClosestSibling = !!(options && options.expandClosestSibling);
+ var onlyPartialContains = !!(options && options.onlyPartialContains);
+ if (rng.isCollapsed()) {
+ return [rng.insertNode(dom.create(nodeName))];
+ }
+ var pred = dom.makePredByNodeName(nodeName);
+ var nodes = rng.nodes(dom.isText, {
+ fullyContains: true
+ }).map(function (text) {
+ return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
+ });
+ if (expandClosestSibling) {
+ if (onlyPartialContains) {
+ var nodesInRange_1 = rng.nodes();
+ // compose with partial contains predication
+ pred = func.and(pred, function (node) {
+ return lists.contains(nodesInRange_1, node);
+ });
+ }
+ return nodes.map(function (node) {
+ var siblings = dom.withClosestSiblings(node, pred);
+ var head = lists.head(siblings);
+ var tails = lists.tail(siblings);
+ $$1.each(tails, function (idx, elem) {
+ dom.appendChildNodes(head, elem.childNodes);
+ dom.remove(elem);
+ });
+ return lists.head(siblings);
+ });
+ }
+ else {
+ return nodes;
+ }
+ };
+ /**
+ * get current style on cursor
+ *
+ * @param {WrappedRange} rng
+ * @return {Object} - object contains style properties.
+ */
+ Style.prototype.current = function (rng) {
+ var $cont = $$1(!dom.isElement(rng.sc) ? rng.sc.parentNode : rng.sc);
+ var styleInfo = this.fromNode($cont);
+ // document.queryCommandState for toggle state
+ // [workaround] prevent Firefox nsresult: "0x80004005 (NS_ERROR_FAILURE)"
+ try {
+ styleInfo = $$1.extend(styleInfo, {
+ 'font-bold': document.queryCommandState('bold') ? 'bold' : 'normal',
+ 'font-italic': document.queryCommandState('italic') ? 'italic' : 'normal',
+ 'font-underline': document.queryCommandState('underline') ? 'underline' : 'normal',
+ 'font-subscript': document.queryCommandState('subscript') ? 'subscript' : 'normal',
+ 'font-superscript': document.queryCommandState('superscript') ? 'superscript' : 'normal',
+ 'font-strikethrough': document.queryCommandState('strikethrough') ? 'strikethrough' : 'normal',
+ 'font-family': document.queryCommandValue('fontname') || styleInfo['font-family']
+ });
+ }
+ catch (e) { }
+ // list-style-type to list-style(unordered, ordered)
+ if (!rng.isOnList()) {
+ styleInfo['list-style'] = 'none';
+ }
+ else {
+ var orderedTypes = ['circle', 'disc', 'disc-leading-zero', 'square'];
+ var isUnordered = $$1.inArray(styleInfo['list-style-type'], orderedTypes) > -1;
+ styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
+ }
+ var para = dom.ancestor(rng.sc, dom.isPara);
+ if (para && para.style['line-height']) {
+ styleInfo['line-height'] = para.style.lineHeight;
+ }
+ else {
+ var lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
+ styleInfo['line-height'] = lineHeight.toFixed(1);
+ }
+ styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
+ styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
+ styleInfo.range = rng;
+ return styleInfo;
+ };
+ return Style;
+}());
+
+var Bullet = /** @class */ (function () {
+ function Bullet() {
+ }
+ /**
+ * toggle ordered list
+ */
+ Bullet.prototype.insertOrderedList = function (editable) {
+ this.toggleList('OL', editable);
+ };
+ /**
+ * toggle unordered list
+ */
+ Bullet.prototype.insertUnorderedList = function (editable) {
+ this.toggleList('UL', editable);
+ };
+ /**
+ * indent
+ */
+ Bullet.prototype.indent = function (editable) {
+ var _this = this;
+ var rng = range.create(editable).wrapBodyInlineWithPara();
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
+ $$1.each(clustereds, function (idx, paras) {
+ var head = lists.head(paras);
+ if (dom.isLi(head)) {
+ _this.wrapList(paras, head.parentNode.nodeName);
+ }
+ else {
+ $$1.each(paras, function (idx, para) {
+ $$1(para).css('marginLeft', function (idx, val) {
+ return (parseInt(val, 10) || 0) + 25;
+ });
+ });
+ }
+ });
+ rng.select();
+ };
+ /**
+ * outdent
+ */
+ Bullet.prototype.outdent = function (editable) {
+ var _this = this;
+ var rng = range.create(editable).wrapBodyInlineWithPara();
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
+ $$1.each(clustereds, function (idx, paras) {
+ var head = lists.head(paras);
+ if (dom.isLi(head)) {
+ _this.releaseList([paras]);
+ }
+ else {
+ $$1.each(paras, function (idx, para) {
+ $$1(para).css('marginLeft', function (idx, val) {
+ val = (parseInt(val, 10) || 0);
+ return val > 25 ? val - 25 : '';
+ });
+ });
+ }
+ });
+ rng.select();
+ };
+ /**
+ * toggle list
+ *
+ * @param {String} listName - OL or UL
+ */
+ Bullet.prototype.toggleList = function (listName, editable) {
+ var _this = this;
+ var rng = range.create(editable).wrapBodyInlineWithPara();
+ var paras = rng.nodes(dom.isPara, { includeAncestor: true });
+ var bookmark = rng.paraBookmark(paras);
+ var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
+ // paragraph to list
+ if (lists.find(paras, dom.isPurePara)) {
+ var wrappedParas_1 = [];
+ $$1.each(clustereds, function (idx, paras) {
+ wrappedParas_1 = wrappedParas_1.concat(_this.wrapList(paras, listName));
+ });
+ paras = wrappedParas_1;
+ // list to paragraph or change list style
+ }
+ else {
+ var diffLists = rng.nodes(dom.isList, {
+ includeAncestor: true
+ }).filter(function (listNode) {
+ return !$$1.nodeName(listNode, listName);
+ });
+ if (diffLists.length) {
+ $$1.each(diffLists, function (idx, listNode) {
+ dom.replace(listNode, listName);
+ });
+ }
+ else {
+ paras = this.releaseList(clustereds, true);
+ }
+ }
+ range.createFromParaBookmark(bookmark, paras).select();
+ };
+ /**
+ * @param {Node[]} paras
+ * @param {String} listName
+ * @return {Node[]}
+ */
+ Bullet.prototype.wrapList = function (paras, listName) {
+ var head = lists.head(paras);
+ var last = lists.last(paras);
+ var prevList = dom.isList(head.previousSibling) && head.previousSibling;
+ var nextList = dom.isList(last.nextSibling) && last.nextSibling;
+ var listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
+ // P to LI
+ paras = paras.map(function (para) {
+ return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
+ });
+ // append to list(<ul>, <ol>)
+ dom.appendChildNodes(listNode, paras);
+ if (nextList) {
+ dom.appendChildNodes(listNode, lists.from(nextList.childNodes));
+ dom.remove(nextList);
+ }
+ return paras;
+ };
+ /**
+ * @method releaseList
+ *
+ * @param {Array[]} clustereds
+ * @param {Boolean} isEscapseToBody
+ * @return {Node[]}
+ */
+ Bullet.prototype.releaseList = function (clustereds, isEscapseToBody) {
+ var releasedParas = [];
+ $$1.each(clustereds, function (idx, paras) {
+ var head = lists.head(paras);
+ var last = lists.last(paras);
+ var headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) : head.parentNode;
+ var lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
+ node: last.parentNode,
+ offset: dom.position(last) + 1
+ }, {
+ isSkipPaddingBlankHTML: true
+ }) : null;
+ var middleList = dom.splitTree(headList, {
+ node: head.parentNode,
+ offset: dom.position(head)
+ }, {
+ isSkipPaddingBlankHTML: true
+ });
+ paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi)
+ : lists.from(middleList.childNodes).filter(dom.isLi);
+ // LI to P
+ if (isEscapseToBody || !dom.isList(headList.parentNode)) {
+ paras = paras.map(function (para) {
+ return dom.replace(para, 'P');
+ });
+ }
+ $$1.each(lists.from(paras).reverse(), function (idx, para) {
+ dom.insertAfter(para, headList);
+ });
+ // remove empty lists
+ var rootLists = lists.compact([headList, middleList, lastList]);
+ $$1.each(rootLists, function (idx, rootList) {
+ var listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
+ $$1.each(listNodes.reverse(), function (idx, listNode) {
+ if (!dom.nodeLength(listNode)) {
+ dom.remove(listNode, true);
+ }
+ });
+ });
+ releasedParas = releasedParas.concat(paras);
+ });
+ return releasedParas;
+ };
+ return Bullet;
+}());
+
+/**
+ * @class editing.Typing
+ *
+ * Typing
+ *
+ */
+var Typing = /** @class */ (function () {
+ function Typing() {
+ // a Bullet instance to toggle lists off
+ this.bullet = new Bullet();
+ }
+ /**
+ * insert tab
+ *
+ * @param {WrappedRange} rng
+ * @param {Number} tabsize
+ */
+ Typing.prototype.insertTab = function (rng, tabsize) {
+ var tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
+ rng = rng.deleteContents();
+ rng.insertNode(tab, true);
+ rng = range.create(tab, tabsize);
+ rng.select();
+ };
+ /**
+ * insert paragraph
+ */
+ Typing.prototype.insertParagraph = function (editable) {
+ var rng = range.create(editable);
+ // deleteContents on range.
+ rng = rng.deleteContents();
+ // Wrap range if it needs to be wrapped by paragraph
+ rng = rng.wrapBodyInlineWithPara();
+ // finding paragraph
+ var splitRoot = dom.ancestor(rng.sc, dom.isPara);
+ var nextPara;
+ // on paragraph: split paragraph
+ if (splitRoot) {
+ // if it is an empty line with li
+ if (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
+ // toogle UL/OL and escape
+ this.bullet.toggleList(splitRoot.parentNode.nodeName);
+ return;
+ // if it is an empty line with para on blockquote
+ }
+ else if (dom.isEmpty(splitRoot) && dom.isPara(splitRoot) && dom.isBlockquote(splitRoot.parentNode)) {
+ // escape blockquote
+ dom.insertAfter(splitRoot, splitRoot.parentNode);
+ nextPara = splitRoot;
+ // if new line has content (not a line break)
+ }
+ else {
+ nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
+ var emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
+ emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
+ $$1.each(emptyAnchors, function (idx, anchor) {
+ dom.remove(anchor);
+ });
+ // replace empty heading, pre or custom-made styleTag with P tag
+ if ((dom.isHeading(nextPara) || dom.isPre(nextPara) || dom.isCustomStyleTag(nextPara)) && dom.isEmpty(nextPara)) {
+ nextPara = dom.replace(nextPara, 'p');
+ }
+ }
+ // no paragraph: insert empty paragraph
+ }
+ else {
+ var next = rng.sc.childNodes[rng.so];
+ nextPara = $$1(dom.emptyPara)[0];
+ if (next) {
+ rng.sc.insertBefore(nextPara, next);
+ }
+ else {
+ rng.sc.appendChild(nextPara);
+ }
+ }
+ range.create(nextPara, 0).normalize().select().scrollIntoView(editable);
+ };
+ return Typing;
+}());
+
+/**
+ * @class Create a virtual table to create what actions to do in change.
+ * @param {object} startPoint Cell selected to apply change.
+ * @param {enum} where Where change will be applied Row or Col. Use enum: TableResultAction.where
+ * @param {enum} action Action to be applied. Use enum: TableResultAction.requestAction
+ * @param {object} domTable Dom element of table to make changes.
+ */
+var TableResultAction = function (startPoint, where, action, domTable) {
+ var _startPoint = { 'colPos': 0, 'rowPos': 0 };
+ var _virtualTable = [];
+ var _actionCellList = [];
+ /// ///////////////////////////////////////////
+ // Private functions
+ /// ///////////////////////////////////////////
+ /**
+ * Set the startPoint of action.
+ */
+ function setStartPoint() {
+ if (!startPoint || !startPoint.tagName || (startPoint.tagName.toLowerCase() !== 'td' && startPoint.tagName.toLowerCase() !== 'th')) {
+ console.error('Impossible to identify start Cell point.', startPoint);
+ return;
+ }
+ _startPoint.colPos = startPoint.cellIndex;
+ if (!startPoint.parentElement || !startPoint.parentElement.tagName || startPoint.parentElement.tagName.toLowerCase() !== 'tr') {
+ console.error('Impossible to identify start Row point.', startPoint);
+ return;
+ }
+ _startPoint.rowPos = startPoint.parentElement.rowIndex;
+ }
+ /**
+ * Define virtual table position info object.
+ *
+ * @param {int} rowIndex Index position in line of virtual table.
+ * @param {int} cellIndex Index position in column of virtual table.
+ * @param {object} baseRow Row affected by this position.
+ * @param {object} baseCell Cell affected by this position.
+ * @param {bool} isSpan Inform if it is an span cell/row.
+ */
+ function setVirtualTablePosition(rowIndex, cellIndex, baseRow, baseCell, isRowSpan, isColSpan, isVirtualCell) {
+ var objPosition = {
+ 'baseRow': baseRow,
+ 'baseCell': baseCell,
+ 'isRowSpan': isRowSpan,
+ 'isColSpan': isColSpan,
+ 'isVirtual': isVirtualCell
+ };
+ if (!_virtualTable[rowIndex]) {
+ _virtualTable[rowIndex] = [];
+ }
+ _virtualTable[rowIndex][cellIndex] = objPosition;
+ }
+ /**
+ * Create action cell object.
+ *
+ * @param {object} virtualTableCellObj Object of specific position on virtual table.
+ * @param {enum} resultAction Action to be applied in that item.
+ */
+ function getActionCell(virtualTableCellObj, resultAction, virtualRowPosition, virtualColPosition) {
+ return {
+ 'baseCell': virtualTableCellObj.baseCell,
+ 'action': resultAction,
+ 'virtualTable': {
+ 'rowIndex': virtualRowPosition,
+ 'cellIndex': virtualColPosition
+ }
+ };
+ }
+ /**
+ * Recover free index of row to append Cell.
+ *
+ * @param {int} rowIndex Index of row to find free space.
+ * @param {int} cellIndex Index of cell to find free space in table.
+ */
+ function recoverCellIndex(rowIndex, cellIndex) {
+ if (!_virtualTable[rowIndex]) {
+ return cellIndex;
+ }
+ if (!_virtualTable[rowIndex][cellIndex]) {
+ return cellIndex;
+ }
+ var newCellIndex = cellIndex;
+ while (_virtualTable[rowIndex][newCellIndex]) {
+ newCellIndex++;
+ if (!_virtualTable[rowIndex][newCellIndex]) {
+ return newCellIndex;
+ }
+ }
+ }
+ /**
+ * Recover info about row and cell and add information to virtual table.
+ *
+ * @param {object} row Row to recover information.
+ * @param {object} cell Cell to recover information.
+ */
+ function addCellInfoToVirtual(row, cell) {
+ var cellIndex = recoverCellIndex(row.rowIndex, cell.cellIndex);
+ var cellHasColspan = (cell.colSpan > 1);
+ var cellHasRowspan = (cell.rowSpan > 1);
+ var isThisSelectedCell = (row.rowIndex === _startPoint.rowPos && cell.cellIndex === _startPoint.colPos);
+ setVirtualTablePosition(row.rowIndex, cellIndex, row, cell, cellHasRowspan, cellHasColspan, false);
+ // Add span rows to virtual Table.
+ var rowspanNumber = cell.attributes.rowSpan ? parseInt(cell.attributes.rowSpan.value, 10) : 0;
+ if (rowspanNumber > 1) {
+ for (var rp = 1; rp < rowspanNumber; rp++) {
+ var rowspanIndex = row.rowIndex + rp;
+ adjustStartPoint(rowspanIndex, cellIndex, cell, isThisSelectedCell);
+ setVirtualTablePosition(rowspanIndex, cellIndex, row, cell, true, cellHasColspan, true);
+ }
+ }
+ // Add span cols to virtual table.
+ var colspanNumber = cell.attributes.colSpan ? parseInt(cell.attributes.colSpan.value, 10) : 0;
+ if (colspanNumber > 1) {
+ for (var cp = 1; cp < colspanNumber; cp++) {
+ var cellspanIndex = recoverCellIndex(row.rowIndex, (cellIndex + cp));
+ adjustStartPoint(row.rowIndex, cellspanIndex, cell, isThisSelectedCell);
+ setVirtualTablePosition(row.rowIndex, cellspanIndex, row, cell, cellHasRowspan, true, true);
+ }
+ }
+ }
+ /**
+ * Process validation and adjust of start point if needed
+ *
+ * @param {int} rowIndex
+ * @param {int} cellIndex
+ * @param {object} cell
+ * @param {bool} isSelectedCell
+ */
+ function adjustStartPoint(rowIndex, cellIndex, cell, isSelectedCell) {
+ if (rowIndex === _startPoint.rowPos && _startPoint.colPos >= cell.cellIndex && cell.cellIndex <= cellIndex && !isSelectedCell) {
+ _startPoint.colPos++;
+ }
+ }
+ /**
+ * Create virtual table of cells with all cells, including span cells.
+ */
+ function createVirtualTable() {
+ var rows = domTable.rows;
+ for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ var cells = rows[rowIndex].cells;
+ for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) {
+ addCellInfoToVirtual(rows[rowIndex], cells[cellIndex]);
+ }
+ }
+ }
+ /**
+ * Get action to be applied on the cell.
+ *
+ * @param {object} cell virtual table cell to apply action
+ */
+ function getDeleteResultActionToCell(cell) {
+ switch (where) {
+ case TableResultAction.where.Column:
+ if (cell.isColSpan) {
+ return TableResultAction.resultAction.SubtractSpanCount;
+ }
+ break;
+ case TableResultAction.where.Row:
+ if (!cell.isVirtual && cell.isRowSpan) {
+ return TableResultAction.resultAction.AddCell;
+ }
+ else if (cell.isRowSpan) {
+ return TableResultAction.resultAction.SubtractSpanCount;
+ }
+ break;
+ }
+ return TableResultAction.resultAction.RemoveCell;
+ }
+ /**
+ * Get action to be applied on the cell.
+ *
+ * @param {object} cell virtual table cell to apply action
+ */
+ function getAddResultActionToCell(cell) {
+ switch (where) {
+ case TableResultAction.where.Column:
+ if (cell.isColSpan) {
+ return TableResultAction.resultAction.SumSpanCount;
+ }
+ else if (cell.isRowSpan && cell.isVirtual) {
+ return TableResultAction.resultAction.Ignore;
+ }
+ break;
+ case TableResultAction.where.Row:
+ if (cell.isRowSpan) {
+ return TableResultAction.resultAction.SumSpanCount;
+ }
+ else if (cell.isColSpan && cell.isVirtual) {
+ return TableResultAction.resultAction.Ignore;
+ }
+ break;
+ }
+ return TableResultAction.resultAction.AddCell;
+ }
+ function init() {
+ setStartPoint();
+ createVirtualTable();
+ }
+ /// ///////////////////////////////////////////
+ // Public functions
+ /// ///////////////////////////////////////////
+ /**
+ * Recover array os what to do in table.
+ */
+ this.getActionList = function () {
+ var fixedRow = (where === TableResultAction.where.Row) ? _startPoint.rowPos : -1;
+ var fixedCol = (where === TableResultAction.where.Column) ? _startPoint.colPos : -1;
+ var actualPosition = 0;
+ var canContinue = true;
+ while (canContinue) {
+ var rowPosition = (fixedRow >= 0) ? fixedRow : actualPosition;
+ var colPosition = (fixedCol >= 0) ? fixedCol : actualPosition;
+ var row = _virtualTable[rowPosition];
+ if (!row) {
+ canContinue = false;
+ return _actionCellList;
+ }
+ var cell = row[colPosition];
+ if (!cell) {
+ canContinue = false;
+ return _actionCellList;
+ }
+ // Define action to be applied in this cell
+ var resultAction = TableResultAction.resultAction.Ignore;
+ switch (action) {
+ case TableResultAction.requestAction.Add:
+ resultAction = getAddResultActionToCell(cell);
+ break;
+ case TableResultAction.requestAction.Delete:
+ resultAction = getDeleteResultActionToCell(cell);
+ break;
+ }
+ _actionCellList.push(getActionCell(cell, resultAction, rowPosition, colPosition));
+ actualPosition++;
+ }
+ return _actionCellList;
+ };
+ init();
+};
+/**
+*
+* Where action occours enum.
+*/
+TableResultAction.where = { 'Row': 0, 'Column': 1 };
+/**
+*
+* Requested action to apply enum.
+*/
+TableResultAction.requestAction = { 'Add': 0, 'Delete': 1 };
+/**
+*
+* Result action to be executed enum.
+*/
+TableResultAction.resultAction = { 'Ignore': 0, 'SubtractSpanCount': 1, 'RemoveCell': 2, 'AddCell': 3, 'SumSpanCount': 4 };
+/**
+ *
+ * @class editing.Table
+ *
+ * Table
+ *
+ */
+var Table = /** @class */ (function () {
+ function Table() {
+ }
+ /**
+ * handle tab key
+ *
+ * @param {WrappedRange} rng
+ * @param {Boolean} isShift
+ */
+ Table.prototype.tab = function (rng, isShift) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var table = dom.ancestor(cell, dom.isTable);
+ var cells = dom.listDescendant(table, dom.isCell);
+ var nextCell = lists[isShift ? 'prev' : 'next'](cells, cell);
+ if (nextCell) {
+ range.create(nextCell, 0).select();
+ }
+ };
+ /**
+ * Add a new row
+ *
+ * @param {WrappedRange} rng
+ * @param {String} position (top/bottom)
+ * @return {Node}
+ */
+ Table.prototype.addRow = function (rng, position) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var currentTr = $$1(cell).closest('tr');
+ var trAttributes = this.recoverAttributes(currentTr);
+ var html = $$1('<tr' + trAttributes + '></tr>');
+ var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Add, $$1(currentTr).closest('table')[0]);
+ var actions = vTable.getActionList();
+ for (var idCell = 0; idCell < actions.length; idCell++) {
+ var currentCell = actions[idCell];
+ var tdAttributes = this.recoverAttributes(currentCell.baseCell);
+ switch (currentCell.action) {
+ case TableResultAction.resultAction.AddCell:
+ html.append('<td' + tdAttributes + '>' + dom.blank + '</td>');
+ break;
+ case TableResultAction.resultAction.SumSpanCount:
+ if (position === 'top') {
+ var baseCellTr = currentCell.baseCell.parent;
+ var isTopFromRowSpan = (!baseCellTr ? 0 : currentCell.baseCell.closest('tr').rowIndex) <= currentTr[0].rowIndex;
+ if (isTopFromRowSpan) {
+ var newTd = $$1('<div></div>').append($$1('<td' + tdAttributes + '>' + dom.blank + '</td>').removeAttr('rowspan')).html();
+ html.append(newTd);
+ break;
+ }
+ }
+ var rowspanNumber = parseInt(currentCell.baseCell.rowSpan, 10);
+ rowspanNumber++;
+ currentCell.baseCell.setAttribute('rowSpan', rowspanNumber);
+ break;
+ }
+ }
+ if (position === 'top') {
+ currentTr.before(html);
+ }
+ else {
+ var cellHasRowspan = (cell.rowSpan > 1);
+ if (cellHasRowspan) {
+ var lastTrIndex = currentTr[0].rowIndex + (cell.rowSpan - 2);
+ $$1($$1(currentTr).parent().find('tr')[lastTrIndex]).after($$1(html));
+ return;
+ }
+ currentTr.after(html);
+ }
+ };
+ /**
+ * Add a new col
+ *
+ * @param {WrappedRange} rng
+ * @param {String} position (left/right)
+ * @return {Node}
+ */
+ Table.prototype.addCol = function (rng, position) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var row = $$1(cell).closest('tr');
+ var rowsGroup = $$1(row).siblings();
+ rowsGroup.push(row);
+ var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Add, $$1(row).closest('table')[0]);
+ var actions = vTable.getActionList();
+ for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
+ var currentCell = actions[actionIndex];
+ var tdAttributes = this.recoverAttributes(currentCell.baseCell);
+ switch (currentCell.action) {
+ case TableResultAction.resultAction.AddCell:
+ if (position === 'right') {
+ $$1(currentCell.baseCell).after('<td' + tdAttributes + '>' + dom.blank + '</td>');
+ }
+ else {
+ $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
+ }
+ break;
+ case TableResultAction.resultAction.SumSpanCount:
+ if (position === 'right') {
+ var colspanNumber = parseInt(currentCell.baseCell.colSpan, 10);
+ colspanNumber++;
+ currentCell.baseCell.setAttribute('colSpan', colspanNumber);
+ }
+ else {
+ $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
+ }
+ break;
+ }
+ }
+ };
+ /*
+ * Copy attributes from element.
+ *
+ * @param {object} Element to recover attributes.
+ * @return {string} Copied string elements.
+ */
+ Table.prototype.recoverAttributes = function (el) {
+ var resultStr = '';
+ if (!el) {
+ return resultStr;
+ }
+ var attrList = el.attributes || [];
+ for (var i = 0; i < attrList.length; i++) {
+ if (attrList[i].name.toLowerCase() === 'id') {
+ continue;
+ }
+ if (attrList[i].specified) {
+ resultStr += ' ' + attrList[i].name + '=\'' + attrList[i].value + '\'';
+ }
+ }
+ return resultStr;
+ };
+ /**
+ * Delete current row
+ *
+ * @param {WrappedRange} rng
+ * @return {Node}
+ */
+ Table.prototype.deleteRow = function (rng) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var row = $$1(cell).closest('tr');
+ var cellPos = row.children('td, th').index($$1(cell));
+ var rowPos = row[0].rowIndex;
+ var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
+ var actions = vTable.getActionList();
+ for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
+ if (!actions[actionIndex]) {
+ continue;
+ }
+ var baseCell = actions[actionIndex].baseCell;
+ var virtualPosition = actions[actionIndex].virtualTable;
+ var hasRowspan = (baseCell.rowSpan && baseCell.rowSpan > 1);
+ var rowspanNumber = (hasRowspan) ? parseInt(baseCell.rowSpan, 10) : 0;
+ switch (actions[actionIndex].action) {
+ case TableResultAction.resultAction.Ignore:
+ continue;
+ case TableResultAction.resultAction.AddCell:
+ var nextRow = row.next('tr')[0];
+ if (!nextRow) {
+ continue;
+ }
+ var cloneRow = row[0].cells[cellPos];
+ if (hasRowspan) {
+ if (rowspanNumber > 2) {
+ rowspanNumber--;
+ nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
+ nextRow.cells[cellPos].setAttribute('rowSpan', rowspanNumber);
+ nextRow.cells[cellPos].innerHTML = '';
+ }
+ else if (rowspanNumber === 2) {
+ nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
+ nextRow.cells[cellPos].removeAttribute('rowSpan');
+ nextRow.cells[cellPos].innerHTML = '';
+ }
+ }
+ continue;
+ case TableResultAction.resultAction.SubtractSpanCount:
+ if (hasRowspan) {
+ if (rowspanNumber > 2) {
+ rowspanNumber--;
+ baseCell.setAttribute('rowSpan', rowspanNumber);
+ if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
+ baseCell.innerHTML = '';
+ }
+ }
+ else if (rowspanNumber === 2) {
+ baseCell.removeAttribute('rowSpan');
+ if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
+ baseCell.innerHTML = '';
+ }
+ }
+ }
+ continue;
+ case TableResultAction.resultAction.RemoveCell:
+ // Do not need remove cell because row will be deleted.
+ continue;
+ }
+ }
+ row.remove();
+ };
+ /**
+ * Delete current col
+ *
+ * @param {WrappedRange} rng
+ * @return {Node}
+ */
+ Table.prototype.deleteCol = function (rng) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ var row = $$1(cell).closest('tr');
+ var cellPos = row.children('td, th').index($$1(cell));
+ var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
+ var actions = vTable.getActionList();
+ for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
+ if (!actions[actionIndex]) {
+ continue;
+ }
+ switch (actions[actionIndex].action) {
+ case TableResultAction.resultAction.Ignore:
+ continue;
+ case TableResultAction.resultAction.SubtractSpanCount:
+ var baseCell = actions[actionIndex].baseCell;
+ var hasColspan = (baseCell.colSpan && baseCell.colSpan > 1);
+ if (hasColspan) {
+ var colspanNumber = (baseCell.colSpan) ? parseInt(baseCell.colSpan, 10) : 0;
+ if (colspanNumber > 2) {
+ colspanNumber--;
+ baseCell.setAttribute('colSpan', colspanNumber);
+ if (baseCell.cellIndex === cellPos) {
+ baseCell.innerHTML = '';
+ }
+ }
+ else if (colspanNumber === 2) {
+ baseCell.removeAttribute('colSpan');
+ if (baseCell.cellIndex === cellPos) {
+ baseCell.innerHTML = '';
+ }
+ }
+ }
+ continue;
+ case TableResultAction.resultAction.RemoveCell:
+ dom.remove(actions[actionIndex].baseCell, true);
+ continue;
+ }
+ }
+ };
+ /**
+ * create empty table element
+ *
+ * @param {Number} rowCount
+ * @param {Number} colCount
+ * @return {Node}
+ */
+ Table.prototype.createTable = function (colCount, rowCount, options) {
+ var tds = [];
+ var tdHTML;
+ for (var idxCol = 0; idxCol < colCount; idxCol++) {
+ tds.push('<td>' + dom.blank + '</td>');
+ }
+ tdHTML = tds.join('');
+ var trs = [];
+ var trHTML;
+ for (var idxRow = 0; idxRow < rowCount; idxRow++) {
+ trs.push('<tr>' + tdHTML + '</tr>');
+ }
+ trHTML = trs.join('');
+ var $table = $$1('<table>' + trHTML + '</table>');
+ if (options && options.tableClassName) {
+ $table.addClass(options.tableClassName);
+ }
+ return $table[0];
+ };
+ /**
+ * Delete current table
+ *
+ * @param {WrappedRange} rng
+ * @return {Node}
+ */
+ Table.prototype.deleteTable = function (rng) {
+ var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
+ $$1(cell).closest('table').remove();
+ };
+ return Table;
+}());
+
+var KEY_BOGUS = 'bogus';
+/**
+ * @class Editor
+ */
+var Editor = /** @class */ (function () {
+ function Editor(context) {
+ var _this = this;
+ this.context = context;
+ this.$note = context.layoutInfo.note;
+ this.$editor = context.layoutInfo.editor;
+ this.$editable = context.layoutInfo.editable;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ this.editable = this.$editable[0];
+ this.lastRange = null;
+ this.style = new Style();
+ this.table = new Table();
+ this.typing = new Typing();
+ this.bullet = new Bullet();
+ this.history = new History(this.$editable);
+ this.context.memo('help.undo', this.lang.help.undo);
+ this.context.memo('help.redo', this.lang.help.redo);
+ this.context.memo('help.tab', this.lang.help.tab);
+ this.context.memo('help.untab', this.lang.help.untab);
+ this.context.memo('help.insertParagraph', this.lang.help.insertParagraph);
+ this.context.memo('help.insertOrderedList', this.lang.help.insertOrderedList);
+ this.context.memo('help.insertUnorderedList', this.lang.help.insertUnorderedList);
+ this.context.memo('help.indent', this.lang.help.indent);
+ this.context.memo('help.outdent', this.lang.help.outdent);
+ this.context.memo('help.formatPara', this.lang.help.formatPara);
+ this.context.memo('help.insertHorizontalRule', this.lang.help.insertHorizontalRule);
+ this.context.memo('help.fontName', this.lang.help.fontName);
+ // native commands(with execCommand), generate function for execCommand
+ var commands = [
+ 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
+ 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
+ 'formatBlock', 'removeFormat', 'backColor'
+ ];
+ for (var idx = 0, len = commands.length; idx < len; idx++) {
+ this[commands[idx]] = (function (sCmd) {
+ return function (value) {
+ _this.beforeCommand();
+ document.execCommand(sCmd, false, value);
+ _this.afterCommand(true);
+ };
+ })(commands[idx]);
+ this.context.memo('help.' + commands[idx], this.lang.help[commands[idx]]);
+ }
+ this.fontName = this.wrapCommand(function (value) {
+ return _this.fontStyling('font-family', "\'" + value + "\'");
+ });
+ this.fontSize = this.wrapCommand(function (value) {
+ return _this.fontStyling('font-size', value + 'px');
+ });
+ for (var idx = 1; idx <= 6; idx++) {
+ this['formatH' + idx] = (function (idx) {
+ return function () {
+ _this.formatBlock('H' + idx);
+ };
+ })(idx);
+ this.context.memo('help.formatH' + idx, this.lang.help['formatH' + idx]);
+ }
+
+ this.insertParagraph = this.wrapCommand(function () {
+ _this.typing.insertParagraph(_this.editable);
+ });
+ this.insertOrderedList = this.wrapCommand(function () {
+ _this.bullet.insertOrderedList(_this.editable);
+ });
+ this.insertUnorderedList = this.wrapCommand(function () {
+ _this.bullet.insertUnorderedList(_this.editable);
+ });
+ this.indent = this.wrapCommand(function () {
+ _this.bullet.indent(_this.editable);
+ });
+ this.outdent = this.wrapCommand(function () {
+ _this.bullet.outdent(_this.editable);
+ });
+ /**
+ * insertNode
+ * insert node
+ * @param {Node} node
+ */
+ this.insertNode = this.wrapCommand(function (node) {
+ if (_this.isLimited($$1(node).text().length)) {
+ return;
+ }
+ var rng = _this.createRange();
+ rng.insertNode(node);
+ range.createFromNodeAfter(node).select();
+ });
+ /**
+ * insert text
+ * @param {String} text
+ */
+ this.insertText = this.wrapCommand(function (text) {
+ if (_this.isLimited(text.length)) {
+ return;
+ }
+ var rng = _this.createRange();
+ var textNode = rng.insertNode(dom.createText(text));
+ range.create(textNode, dom.nodeLength(textNode)).select();
+ });
+ /**
+ * paste HTML
+ * @param {String} markup
+ */
+ this.pasteHTML = this.wrapCommand(function (markup) {
+ if (_this.isLimited(markup.length)) {
+ return;
+ }
+ var contents = _this.createRange().pasteHTML(markup);
+ range.createFromNodeAfter(lists.last(contents)).select();
+ });
+ /**
+ * formatBlock
+ *
+ * @param {String} tagName
+ */
+ this.formatBlock = this.wrapCommand(function (tagName, $target) {
+ var onApplyCustomStyle = _this.options.callbacks.onApplyCustomStyle;
+ if (onApplyCustomStyle) {
+ onApplyCustomStyle.call(_this, $target, _this.context, _this.onFormatBlock);
+ }
+ else {
+ _this.onFormatBlock(tagName, $target);
+ }
+ });
+ /**
+ * insert horizontal rule
+ */
+ this.insertHorizontalRule = this.wrapCommand(function () {
+ var hrNode = _this.createRange().insertNode(dom.create('HR'));
+ if (hrNode.nextSibling) {
+ range.create(hrNode.nextSibling, 0).normalize().select();
+ }
+ });
+ /**
+ * lineHeight
+ * @param {String} value
+ */
+ this.lineHeight = this.wrapCommand(function (value) {
+ _this.style.stylePara(_this.createRange(), {
+ lineHeight: value
+ });
+ });
+ /**
+ * create link (command)
+ *
+ * @param {Object} linkInfo
+ */
+ this.createLink = this.wrapCommand(function (linkInfo) {
+ var linkUrl = linkInfo.url;
+ var linkText = linkInfo.text;
+ var isNewWindow = linkInfo.isNewWindow;
+ var rng = linkInfo.range || _this.createRange();
+ var isTextChanged = rng.toString() !== linkText;
+ // handle spaced urls from input
+ if (typeof linkUrl === 'string') {
+ linkUrl = linkUrl.trim();
+ }
+ if (_this.options.onCreateLink) {
+ linkUrl = _this.options.onCreateLink(linkUrl);
+ }
+ else {
+ // if url doesn't match an URL schema, set http:// as default
+ linkUrl = /^[A-Za-z][A-Za-z0-9+-.]*\:[\/\/]?/.test(linkUrl)
+ ? linkUrl : 'http://' + linkUrl;
+ }
+ var anchors = [];
+ if (isTextChanged) {
+ rng = rng.deleteContents();
+ var anchor = rng.insertNode($$1('<A>' + linkText + '</A>')[0]);
+ anchors.push(anchor);
+ }
+ else {
+ anchors = _this.style.styleNodes(rng, {
+ nodeName: 'A',
+ expandClosestSibling: true,
+ onlyPartialContains: true
+ });
+ }
+ $$1.each(anchors, function (idx, anchor) {
+ $$1(anchor).attr('href', linkUrl);
+ if (isNewWindow) {
+ $$1(anchor).attr('target', '_blank');
+ }
+ else {
+ $$1(anchor).removeAttr('target');
+ }
+ });
+ var startRange = range.createFromNodeBefore(lists.head(anchors));
+ var startPoint = startRange.getStartPoint();
+ var endRange = range.createFromNodeAfter(lists.last(anchors));
+ var endPoint = endRange.getEndPoint();
+ range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset).select();
+ });
+ /**
+ * setting color
+ *
+ * @param {Object} sObjColor color code
+ * @param {String} sObjColor.foreColor foreground color
+ * @param {String} sObjColor.backColor background color
+ */
+ this.color = this.wrapCommand(function (colorInfo) {
+ var foreColor = colorInfo.foreColor;
+ var backColor = colorInfo.backColor;
+ if (foreColor) {
+ document.execCommand('foreColor', false, foreColor);
+ }
+ if (backColor) {
+ document.execCommand('backColor', false, backColor);
+ }
+ });
+ /**
+ * Set foreground color
+ *
+ * @param {String} colorCode foreground color code
+ */
+ this.foreColor = this.wrapCommand(function (colorInfo) {
+ document.execCommand('styleWithCSS', false, true);
+ document.execCommand('foreColor', false, colorInfo);
+ });
+ /**
+ * insert Table
+ *
+ * @param {String} dimension of table (ex : "5x5")
+ */
+ this.insertTable = this.wrapCommand(function (dim) {
+ var dimension = dim.split('x');
+ var rng = _this.createRange().deleteContents();
+ rng.insertNode(_this.table.createTable(dimension[0], dimension[1], _this.options));
+ });
+ /**
+ * remove media object and Figure Elements if media object is img with Figure.
+ */
+ this.removeMedia = this.wrapCommand(function () {
+ var $target = $$1(_this.restoreTarget()).parent();
+ if ($target.parent('figure').length) {
+ $target.parent('figure').remove();
+ }
+ else {
+ $target = $$1(_this.restoreTarget()).detach();
+ }
+ _this.context.triggerEvent('media.delete', $target, _this.$editable);
+ });
+ /**
+ * float me
+ *
+ * @param {String} value
+ */
+ this.floatMe = this.wrapCommand(function (value) {
+ var $target = $$1(_this.restoreTarget());
+ $target.toggleClass('note-float-left', value === 'left');
+ $target.toggleClass('note-float-right', value === 'right');
+ $target.css('float', value);
+ });
+ /**
+ * resize overlay element
+ * @param {String} value
+ */
+ this.resize = this.wrapCommand(function (value) {
+ var $target = $$1(_this.restoreTarget());
+ $target.css({
+ width: value * 100 + '%',
+ height: ''
+ });
+ });
+ }
+ Editor.prototype.initialize = function () {
+ var _this = this;
+ // bind custom events
+ this.$editable.on('keydown', function (event) {
+ if (event.keyCode === key.code.ENTER) {
+ _this.context.triggerEvent('enter', event);
+ }
+ _this.context.triggerEvent('keydown', event);
+ if (!event.isDefaultPrevented()) {
+ if (_this.options.shortcuts) {
+ _this.handleKeyMap(event);
+ }
+ else {
+ _this.preventDefaultEditableShortCuts(event);
+ }
+ }
+ if (_this.isLimited(1, event)) {
+ return false;
+ }
+ }).on('keyup', function (event) {
+ _this.context.triggerEvent('keyup', event);
+ }).on('focus', function (event) {
+ _this.context.triggerEvent('focus', event);
+ }).on('blur', function (event) {
+ _this.context.triggerEvent('blur', event);
+ }).on('mousedown', function (event) {
+ _this.context.triggerEvent('mousedown', event);
+ }).on('mouseup', function (event) {
+ _this.context.triggerEvent('mouseup', event);
+ }).on('scroll', function (event) {
+ _this.context.triggerEvent('scroll', event);
+ }).on('paste', function (event) {
+ _this.context.triggerEvent('paste', event);
+ });
+ // init content before set event
+ this.$editable.html(dom.html(this.$note) || dom.emptyPara);
+ this.$editable.on(env.inputEventName, func.debounce(function () {
+ _this.context.triggerEvent('change', _this.$editable.html());
+ }, 100));
+ this.$editor.on('focusin', function (event) {
+ _this.context.triggerEvent('focusin', event);
+ }).on('focusout', function (event) {
+ _this.context.triggerEvent('focusout', event);
+ });
+ if (!this.options.airMode) {
+ if (this.options.width) {
+ this.$editor.outerWidth(this.options.width);
+ }
+ if (this.options.height) {
+ this.$editable.outerHeight(this.options.height);
+ }
+ if (this.options.maxHeight) {
+ this.$editable.css('max-height', this.options.maxHeight);
+ }
+ if (this.options.minHeight) {
+ this.$editable.css('min-height', this.options.minHeight);
+ }
+ }
+ this.history.recordUndo();
+ };
+ Editor.prototype.destroy = function () {
+ this.$editable.off();
+ };
+ Editor.prototype.handleKeyMap = function (event) {
+ var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
+ var keys = [];
+ if (event.metaKey) {
+ keys.push('CMD');
+ }
+ if (event.ctrlKey && !event.altKey) {
+ keys.push('CTRL');
+ }
+ if (event.shiftKey) {
+ keys.push('SHIFT');
+ }
+ var keyName = key.nameFromCode[event.keyCode];
+ if (keyName) {
+ keys.push(keyName);
+ }
+ var eventName = keyMap[keys.join('+')];
+ if (eventName) {
+ if (this.context.invoke(eventName) !== false) {
+ event.preventDefault();
+ }
+ }
+ else if (key.isEdit(event.keyCode)) {
+ this.afterCommand();
+ }
+ };
+ Editor.prototype.preventDefaultEditableShortCuts = function (event) {
+ // B(Bold, 66) / I(Italic, 73) / U(Underline, 85)
+ if ((event.ctrlKey || event.metaKey) &&
+ lists.contains([66, 73, 85], event.keyCode)) {
+ event.preventDefault();
+ }
+ };
+ Editor.prototype.isLimited = function (pad, event) {
+ pad = pad || 0;
+ if (typeof event !== 'undefined') {
+ if (key.isMove(event.keyCode) ||
+ (event.ctrlKey || event.metaKey) ||
+ lists.contains([key.code.BACKSPACE, key.code.DELETE], event.keyCode)) {
+ return false;
+ }
+ }
+ if (this.options.maxTextLength > 0) {
+ if ((this.$editable.text().length + pad) >= this.options.maxTextLength) {
+ return true;
+ }
+ }
+ return false;
+ };
+ /**
+ * create range
+ * @return {WrappedRange}
+ */
+ Editor.prototype.createRange = function () {
+ this.focus();
+ return range.create(this.editable);
+ };
+ /**
+ * saveRange
+ *
+ * save current range
+ *
+ * @param {Boolean} [thenCollapse=false]
+ */
+ Editor.prototype.saveRange = function (thenCollapse) {
+ this.lastRange = this.createRange();
+ if (thenCollapse) {
+ this.lastRange.collapse().select();
+ }
+ };
+ /**
+ * restoreRange
+ *
+ * restore lately range
+ */
+ Editor.prototype.restoreRange = function () {
+ if (this.lastRange) {
+ this.lastRange.select();
+ this.focus();
+ }
+ };
+ Editor.prototype.saveTarget = function (node) {
+ this.$editable.data('target', node);
+ };
+ Editor.prototype.clearTarget = function () {
+ this.$editable.removeData('target');
+ };
+ Editor.prototype.restoreTarget = function () {
+ return this.$editable.data('target');
+ };
+ /**
+ * currentStyle
+ *
+ * current style
+ * @return {Object|Boolean} unfocus
+ */
+ Editor.prototype.currentStyle = function () {
+ var rng = range.create();
+ if (rng) {
+ rng = rng.normalize();
+ }
+ return rng ? this.style.current(rng) : this.style.fromNode(this.$editable);
+ };
+ /**
+ * style from node
+ *
+ * @param {jQuery} $node
+ * @return {Object}
+ */
+ Editor.prototype.styleFromNode = function ($node) {
+ return this.style.fromNode($node);
+ };
+ /**
+ * undo
+ */
+ Editor.prototype.undo = function () {
+ this.context.triggerEvent('before.command', this.$editable.html());
+ this.history.undo();
+ this.context.triggerEvent('change', this.$editable.html());
+ };
+ /**
+ * redo
+ */
+ Editor.prototype.redo = function () {
+ this.context.triggerEvent('before.command', this.$editable.html());
+ this.history.redo();
+ this.context.triggerEvent('change', this.$editable.html());
+ };
+ /**
+ * before command
+ */
+ Editor.prototype.beforeCommand = function () {
+ this.context.triggerEvent('before.command', this.$editable.html());
+ // keep focus on editable before command execution
+ this.focus();
+ };
+ /**
+ * after command
+ * @param {Boolean} isPreventTrigger
+ */
+ Editor.prototype.afterCommand = function (isPreventTrigger) {
+ this.history.recordUndo();
+ if (!isPreventTrigger) {
+ this.context.triggerEvent('change', this.$editable.html());
+ }
+ };
+ /**
+ * handle tab key
+ */
+ Editor.prototype.tab = function () {
+ var rng = this.createRange();
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.table.tab(rng);
+ }
+ else {
+ if (this.options.tabSize === 0) {
+ return false;
+ }
+ if (!this.isLimited(this.options.tabSize)) {
+ this.beforeCommand();
+ this.typing.insertTab(rng, this.options.tabSize);
+ this.afterCommand();
+ }
+ }
+ };
+ /**
+ * handle shift+tab key
+ */
+ Editor.prototype.untab = function () {
+ var rng = this.createRange();
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.table.tab(rng, true);
+ }
+ else {
+ if (this.options.tabSize === 0) {
+ return false;
+ }
+ }
+ };
+ /**
+ * run given function between beforeCommand and afterCommand
+ */
+ Editor.prototype.wrapCommand = function (fn) {
+ var _this = this;
+ return function () {
+ _this.beforeCommand();
+ fn.apply(_this, arguments);
+ _this.afterCommand();
+ };
+ };
+ /**
+ * insert image
+ *
+ * @param {String} src
+ * @param {String|Function} param
+ * @return {Promise}
+ */
+ Editor.prototype.insertImage = function (src, param) {
+ var _this = this;
+ return createImage(src, param).then(function ($image) {
+ _this.beforeCommand();
+ if (typeof param === 'function') {
+ param($image);
+ }
+ else {
+ if (typeof param === 'string') {
+ $image.attr('data-filename', param);
+ }
+ $image.css('width', Math.min(_this.$editable.width(), $image.width()));
+ }
+ $image.show();
+ range.create(_this.editable).insertNode($image[0]);
+ range.createFromNodeAfter($image[0]).select();
+ _this.afterCommand();
+ }).fail(function (e) {
+ _this.context.triggerEvent('image.upload.error', e);
+ });
+ };
+ /**
+ * insertImages
+ * @param {File[]} files
+ */
+ Editor.prototype.insertImages = function (files) {
+ var _this = this;
+ $$1.each(files, function (idx, file) {
+ var filename = file.name;
+ if (_this.options.maximumImageFileSize && _this.options.maximumImageFileSize < file.size) {
+ _this.context.triggerEvent('image.upload.error', _this.lang.image.maximumFileSizeError);
+ }
+ else {
+ readFileAsDataURL(file).then(function (dataURL) {
+ return _this.insertImage(dataURL, filename);
+ }).fail(function () {
+ _this.context.triggerEvent('image.upload.error');
+ });
+ }
+ });
+ };
+ /**
+ * insertImagesOrCallback
+ * @param {File[]} files
+ */
+ Editor.prototype.insertImagesOrCallback = function (files) {
+ var callbacks = this.options.callbacks;
+ // If onImageUpload this.options setted
+ if (callbacks.onImageUpload) {
+ this.context.triggerEvent('image.upload', files);
+ // else insert Image as dataURL
+ }
+ else {
+ this.insertImages(files);
+ }
+ };
+ /**
+ * return selected plain text
+ * @return {String} text
+ */
+ Editor.prototype.getSelectedText = function () {
+ var rng = this.createRange();
+ // if range on anchor, expand range with anchor
+ if (rng.isOnAnchor()) {
+ rng = range.createFromNode(dom.ancestor(rng.sc, dom.isAnchor));
+ }
+ return rng.toString();
+ };
+ Editor.prototype.onFormatBlock = function (tagName, $target) {
+ // [workaround] for MSIE, IE need `<`
+ tagName = env.isMSIE ? '<' + tagName + '>' : tagName;
+ document.execCommand('FormatBlock', false, tagName);
+ // support custom class
+ if ($target && $target.length) {
+ var className = $target[0].className || '';
+ if (className) {
+ var currentRange = this.createRange();
+ var $parent = $$1([currentRange.sc, currentRange.ec]).closest(tagName);
+ $parent.addClass(className);
+ }
+ }
+ };
+ Editor.prototype.formatPara = function () {
+ this.formatBlock('P');
+ };
+ Editor.prototype.fontStyling = function (target, value) {
+ var rng = this.createRange();
+ if (rng) {
+ var spans = this.style.styleNodes(rng);
+ $$1(spans).css(target, value);
+ // [workaround] added styled bogus span for style
+ // - also bogus character needed for cursor position
+ if (rng.isCollapsed()) {
+ var firstSpan = lists.head(spans);
+ if (firstSpan && !dom.nodeLength(firstSpan)) {
+ firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
+ range.createFromNodeAfter(firstSpan.firstChild).select();
+ this.$editable.data(KEY_BOGUS, firstSpan);
+ }
+ }
+ }
+ };
+ /**
+ * unlink
+ *
+ * @type command
+ */
+ Editor.prototype.unlink = function () {
+ var rng = this.createRange();
+ if (rng.isOnAnchor()) {
+ var anchor = dom.ancestor(rng.sc, dom.isAnchor);
+ rng = range.createFromNode(anchor);
+ rng.select();
+ this.beforeCommand();
+ document.execCommand('unlink');
+ this.afterCommand();
+ }
+ };
+ /**
+ * returns link info
+ *
+ * @return {Object}
+ * @return {WrappedRange} return.range
+ * @return {String} return.text
+ * @return {Boolean} [return.isNewWindow=true]
+ * @return {String} [return.url=""]
+ */
+ Editor.prototype.getLinkInfo = function () {
+ var rng = this.createRange().expand(dom.isAnchor);
+ // Get the first anchor on range(for edit).
+ var $anchor = $$1(lists.head(rng.nodes(dom.isAnchor)));
+ var linkInfo = {
+ range: rng,
+ text: rng.toString(),
+ url: $anchor.length ? $anchor.attr('href') : ''
+ };
+ // Define isNewWindow when anchor exists.
+ if ($anchor.length) {
+ linkInfo.isNewWindow = $anchor.attr('target') === '_blank';
+ }
+ return linkInfo;
+ };
+ Editor.prototype.addRow = function (position) {
+ var rng = this.createRange(this.$editable);
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.beforeCommand();
+ this.table.addRow(rng, position);
+ this.afterCommand();
+ }
+ };
+ Editor.prototype.addCol = function (position) {
+ var rng = this.createRange(this.$editable);
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.beforeCommand();
+ this.table.addCol(rng, position);
+ this.afterCommand();
+ }
+ };
+ Editor.prototype.deleteRow = function () {
+ var rng = this.createRange(this.$editable);
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.beforeCommand();
+ this.table.deleteRow(rng);
+ this.afterCommand();
+ }
+ };
+ Editor.prototype.deleteCol = function () {
+ var rng = this.createRange(this.$editable);
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.beforeCommand();
+ this.table.deleteCol(rng);
+ this.afterCommand();
+ }
+ };
+ Editor.prototype.deleteTable = function () {
+ var rng = this.createRange(this.$editable);
+ if (rng.isCollapsed() && rng.isOnCell()) {
+ this.beforeCommand();
+ this.table.deleteTable(rng);
+ this.afterCommand();
+ }
+ };
+ /**
+ * @param {Position} pos
+ * @param {jQuery} $target - target element
+ * @param {Boolean} [bKeepRatio] - keep ratio
+ */
+ Editor.prototype.resizeTo = function (pos, $target, bKeepRatio) {
+ var imageSize;
+ if (bKeepRatio) {
+ var newRatio = pos.y / pos.x;
+ var ratio = $target.data('ratio');
+ imageSize = {
+ width: ratio > newRatio ? pos.x : pos.y / ratio,
+ height: ratio > newRatio ? pos.x * ratio : pos.y
+ };
+ }
+ else {
+ imageSize = {
+ width: pos.x,
+ height: pos.y
+ };
+ }
+ $target.css(imageSize);
+ };
+ /**
+ * returns whether editable area has focus or not.
+ */
+ Editor.prototype.hasFocus = function () {
+ return this.$editable.is(':focus');
+ };
+ /**
+ * set focus
+ */
+ Editor.prototype.focus = function () {
+ // [workaround] Screen will move when page is scolled in IE.
+ // - do focus when not focused
+ if (!this.hasFocus()) {
+ this.$editable.focus();
+ }
+ };
+ /**
+ * returns whether contents is empty or not.
+ * @return {Boolean}
+ */
+ Editor.prototype.isEmpty = function () {
+ return dom.isEmpty(this.$editable[0]) || dom.emptyPara === this.$editable.html();
+ };
+ /**
+ * Removes all contents and restores the editable instance to an _emptyPara_.
+ */
+ Editor.prototype.empty = function () {
+ this.context.invoke('code', dom.emptyPara);
+ };
+ return Editor;
+}());
+
+var Clipboard = /** @class */ (function () {
+ function Clipboard(context) {
+ this.context = context;
+ this.$editable = context.layoutInfo.editable;
+ }
+ Clipboard.prototype.initialize = function () {
+ this.$editable.on('paste', this.pasteByEvent.bind(this));
+ };
+ /**
+ * paste by clipboard event
+ *
+ * @param {Event} event
+ */
+ Clipboard.prototype.pasteByEvent = function (event) {
+ var clipboardData = event.originalEvent.clipboardData;
+ if (clipboardData && clipboardData.items && clipboardData.items.length) {
+ var item = lists.head(clipboardData.items);
+ if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
+ this.context.invoke('editor.insertImagesOrCallback', [item.getAsFile()]);
+ }
+ this.context.invoke('editor.afterCommand');
+ }
+ };
+ return Clipboard;
+}());
+
+var Dropzone = /** @class */ (function () {
+ function Dropzone(context) {
+ this.context = context;
+ this.$eventListener = $$1(document);
+ this.$editor = context.layoutInfo.editor;
+ this.$editable = context.layoutInfo.editable;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ this.documentEventHandlers = {};
+ this.$dropzone = $$1([
+ '<div class="note-dropzone">',
+ ' <div class="note-dropzone-message"/>',
+ '</div>'
+ ].join('')).prependTo(this.$editor);
+ }
+ /**
+ * attach Drag and Drop Events
+ */
+ Dropzone.prototype.initialize = function () {
+ if (this.options.disableDragAndDrop) {
+ // prevent default drop event
+ this.documentEventHandlers.onDrop = function (e) {
+ e.preventDefault();
+ };
+ // do not consider outside of dropzone
+ this.$eventListener = this.$dropzone;
+ this.$eventListener.on('drop', this.documentEventHandlers.onDrop);
+ }
+ else {
+ this.attachDragAndDropEvent();
+ }
+ };
+ /**
+ * attach Drag and Drop Events
+ */
+ Dropzone.prototype.attachDragAndDropEvent = function () {
+ var _this = this;
+ var collection = $$1();
+ var $dropzoneMessage = this.$dropzone.find('.note-dropzone-message');
+ this.documentEventHandlers.onDragenter = function (e) {
+ var isCodeview = _this.context.invoke('codeview.isActivated');
+ var hasEditorSize = _this.$editor.width() > 0 && _this.$editor.height() > 0;
+ if (!isCodeview && !collection.length && hasEditorSize) {
+ _this.$editor.addClass('dragover');
+ _this.$dropzone.width(_this.$editor.width());
+ _this.$dropzone.height(_this.$editor.height());
+ $dropzoneMessage.text(_this.lang.image.dragImageHere);
+ }
+ collection = collection.add(e.target);
+ };
+ this.documentEventHandlers.onDragleave = function (e) {
+ collection = collection.not(e.target);
+ if (!collection.length) {
+ _this.$editor.removeClass('dragover');
+ }
+ };
+ this.documentEventHandlers.onDrop = function () {
+ collection = $$1();
+ _this.$editor.removeClass('dragover');
+ };
+ // 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
+ this.$eventListener.on('dragenter', this.documentEventHandlers.onDragenter)
+ .on('dragleave', this.documentEventHandlers.onDragleave)
+ .on('drop', this.documentEventHandlers.onDrop);
+ // change dropzone's message on hover.
+ this.$dropzone.on('dragenter', function () {
+ _this.$dropzone.addClass('hover');
+ $dropzoneMessage.text(_this.lang.image.dropImage);
+ }).on('dragleave', function () {
+ _this.$dropzone.removeClass('hover');
+ $dropzoneMessage.text(_this.lang.image.dragImageHere);
+ });
+ // attach dropImage
+ this.$dropzone.on('drop', function (event) {
+ var dataTransfer = event.originalEvent.dataTransfer;
+ // stop the browser from opening the dropped content
+ event.preventDefault();
+ if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
+ _this.$editable.focus();
+ _this.context.invoke('editor.insertImagesOrCallback', dataTransfer.files);
+ }
+ else {
+ $$1.each(dataTransfer.types, function (idx, type) {
+ var content = dataTransfer.getData(type);
+ if (type.toLowerCase().indexOf('text') > -1) {
+ _this.context.invoke('editor.pasteHTML', content);
+ }
+ else {
+ $$1(content).each(function (idx, item) {
+ _this.context.invoke('editor.insertNode', item);
+ });
+ }
+ });
+ }
+ }).on('dragover', false); // prevent default dragover event
+ };
+ Dropzone.prototype.destroy = function () {
+ var _this = this;
+ Object.keys(this.documentEventHandlers).forEach(function (key) {
+ _this.$eventListener.off(key.substr(2).toLowerCase(), _this.documentEventHandlers[key]);
+ });
+ this.documentEventHandlers = {};
+ };
+ return Dropzone;
+}());
+
+var CodeMirror;
+if (env.hasCodeMirror) {
+ if (env.isSupportAmd) {
+ require(['codemirror'], function (cm) {
+ CodeMirror = cm;
+ });
+ }
+ else {
+ CodeMirror = window.CodeMirror;
+ }
+}
+/**
+ * @class Codeview
+ */
+var CodeView = /** @class */ (function () {
+ function CodeView(context) {
+ this.context = context;
+ this.$editor = context.layoutInfo.editor;
+ this.$editable = context.layoutInfo.editable;
+ this.$codable = context.layoutInfo.codable;
+ this.options = context.options;
+ }
+ CodeView.prototype.sync = function () {
+ var isCodeview = this.isActivated();
+ if (isCodeview && env.hasCodeMirror) {
+ this.$codable.data('cmEditor').save();
+ }
+ };
+ /**
+ * @return {Boolean}
+ */
+ CodeView.prototype.isActivated = function () {
+ return this.$editor.hasClass('codeview');
+ };
+ /**
+ * toggle codeview
+ */
+ CodeView.prototype.toggle = function () {
+ if (this.isActivated()) {
+ this.deactivate();
+ }
+ else {
+ this.activate();
+ }
+ this.context.triggerEvent('codeview.toggled');
+ };
+ /**
+ * activate code view
+ */
+ CodeView.prototype.activate = function () {
+ var _this = this;
+ this.$codable.val(dom.html(this.$editable, this.options.prettifyHtml));
+ this.$codable.height(this.$editable.height());
+ this.context.invoke('toolbar.updateCodeview', true);
+ this.$editor.addClass('codeview');
+ this.$codable.focus();
+ // activate CodeMirror as codable
+ if (env.hasCodeMirror) {
+ var cmEditor_1 = CodeMirror.fromTextArea(this.$codable[0], this.options.codemirror);
+ // CodeMirror TernServer
+ if (this.options.codemirror.tern) {
+ var server_1 = new CodeMirror.TernServer(this.options.codemirror.tern);
+ cmEditor_1.ternServer = server_1;
+ cmEditor_1.on('cursorActivity', function (cm) {
+ server_1.updateArgHints(cm);
+ });
+ }
+ cmEditor_1.on('blur', function (event) {
+ _this.context.triggerEvent('blur.codeview', cmEditor_1.getValue(), event);
+ });
+ // CodeMirror hasn't Padding.
+ cmEditor_1.setSize(null, this.$editable.outerHeight());
+ this.$codable.data('cmEditor', cmEditor_1);
+ }
+ else {
+ this.$codable.on('blur', function (event) {
+ _this.context.triggerEvent('blur.codeview', _this.$codable.val(), event);
+ });
+ }
+ };
+ /**
+ * deactivate code view
+ */
+ CodeView.prototype.deactivate = function () {
+ // deactivate CodeMirror as codable
+ if (env.hasCodeMirror) {
+ var cmEditor = this.$codable.data('cmEditor');
+ this.$codable.val(cmEditor.getValue());
+ cmEditor.toTextArea();
+ }
+ var value = dom.value(this.$codable, this.options.prettifyHtml) || dom.emptyPara;
+ var isChange = this.$editable.html() !== value;
+ this.$editable.html(value);
+ this.$editable.height(this.options.height ? this.$codable.height() : 'auto');
+ this.$editor.removeClass('codeview');
+ if (isChange) {
+ this.context.triggerEvent('change', this.$editable.html(), this.$editable);
+ }
+ this.$editable.focus();
+ this.context.invoke('toolbar.updateCodeview', false);
+ };
+ CodeView.prototype.destroy = function () {
+ if (this.isActivated()) {
+ this.deactivate();
+ }
+ };
+ return CodeView;
+}());
+
+var EDITABLE_PADDING = 24;
+var Statusbar = /** @class */ (function () {
+ function Statusbar(context) {
+ this.$document = $$1(document);
+ this.$statusbar = context.layoutInfo.statusbar;
+ this.$editable = context.layoutInfo.editable;
+ this.options = context.options;
+ }
+ Statusbar.prototype.initialize = function () {
+ var _this = this;
+ if (this.options.airMode || this.options.disableResizeEditor) {
+ this.destroy();
+ return;
+ }
+ this.$statusbar.on('mousedown', function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+ var editableTop = _this.$editable.offset().top - _this.$document.scrollTop();
+ var onMouseMove = function (event) {
+ var height = event.clientY - (editableTop + EDITABLE_PADDING);
+ height = (_this.options.minheight > 0) ? Math.max(height, _this.options.minheight) : height;
+ height = (_this.options.maxHeight > 0) ? Math.min(height, _this.options.maxHeight) : height;
+ _this.$editable.height(height);
+ };
+ _this.$document.on('mousemove', onMouseMove).one('mouseup', function () {
+ _this.$document.off('mousemove', onMouseMove);
+ });
+ });
+ };
+ Statusbar.prototype.destroy = function () {
+ this.$statusbar.off();
+ };
+ return Statusbar;
+}());
+
+var Fullscreen = /** @class */ (function () {
+ function Fullscreen(context) {
+ var _this = this;
+ this.context = context;
+ this.$editor = context.layoutInfo.editor;
+ this.$toolbar = context.layoutInfo.toolbar;
+ this.$editable = context.layoutInfo.editable;
+ this.$codable = context.layoutInfo.codable;
+ this.$window = $$1(window);
+ this.$scrollbar = $$1('html, body');
+ this.onResize = function () {
+ _this.resizeTo({
+ h: _this.$window.height() - _this.$toolbar.outerHeight()
+ });
+ };
+ }
+ Fullscreen.prototype.resizeTo = function (size) {
+ this.$editable.css('height', size.h);
+ this.$codable.css('height', size.h);
+ if (this.$codable.data('cmeditor')) {
+ this.$codable.data('cmeditor').setsize(null, size.h);
+ }
+ };
+ /**
+ * toggle fullscreen
+ */
+ Fullscreen.prototype.toggle = function () {
+ this.$editor.toggleClass('fullscreen');
+ if (this.isFullscreen()) {
+ this.$editable.data('orgHeight', this.$editable.css('height'));
+ this.$window.on('resize', this.onResize).trigger('resize');
+ this.$scrollbar.css('overflow', 'hidden');
+ }
+ else {
+ this.$window.off('resize', this.onResize);
+ this.resizeTo({ h: this.$editable.data('orgHeight') });
+ this.$scrollbar.css('overflow', 'visible');
+ }
+ this.context.invoke('toolbar.updateFullscreen', this.isFullscreen());
+ };
+ Fullscreen.prototype.isFullscreen = function () {
+ return this.$editor.hasClass('fullscreen');
+ };
+ return Fullscreen;
+}());
+
+var Handle = /** @class */ (function () {
+ function Handle(context) {
+ var _this = this;
+ this.context = context;
+ this.$document = $$1(document);
+ this.$editingArea = context.layoutInfo.editingArea;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ this.events = {
+ 'summernote.mousedown': function (we, e) {
+ if (_this.update(e.target)) {
+ e.preventDefault();
+ }
+ },
+ 'summernote.keyup summernote.scroll summernote.change summernote.dialog.shown': function () {
+ _this.update();
+ },
+ 'summernote.disable': function () {
+ _this.hide();
+ },
+ 'summernote.codeview.toggled': function () {
+ _this.update();
+ }
+ };
+ }
+ Handle.prototype.initialize = function () {
+ var _this = this;
+ this.$handle = $$1([
+ '<div class="note-handle">',
+ '<div class="note-control-selection">',
+ '<div class="note-control-selection-bg"></div>',
+ '<div class="note-control-holder note-control-nw"></div>',
+ '<div class="note-control-holder note-control-ne"></div>',
+ '<div class="note-control-holder note-control-sw"></div>',
+ '<div class="',
+ (this.options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing'),
+ ' note-control-se"></div>',
+ (this.options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>'),
+ '</div>',
+ '</div>'
+ ].join('')).prependTo(this.$editingArea);
+ this.$handle.on('mousedown', function (event) {
+ if (dom.isControlSizing(event.target)) {
+ event.preventDefault();
+ event.stopPropagation();
+ var $target_1 = _this.$handle.find('.note-control-selection').data('target');
+ var posStart_1 = $target_1.offset();
+ var scrollTop_1 = _this.$document.scrollTop();
+ var onMouseMove_1 = function (event) {
+ _this.context.invoke('editor.resizeTo', {
+ x: event.clientX - posStart_1.left,
+ y: event.clientY - (posStart_1.top - scrollTop_1)
+ }, $target_1, !event.shiftKey);
+ _this.update($target_1[0]);
+ };
+ _this.$document
+ .on('mousemove', onMouseMove_1)
+ .one('mouseup', function (e) {
+ e.preventDefault();
+ _this.$document.off('mousemove', onMouseMove_1);
+ _this.context.invoke('editor.afterCommand');
+ });
+ if (!$target_1.data('ratio')) {
+ $target_1.data('ratio', $target_1.height() / $target_1.width());
+ }
+ }
+ });
+ // Listen for scrolling on the handle overlay.
+ this.$handle.on('wheel', function (e) {
+ e.preventDefault();
+ _this.update();
+ });
+ };
+ Handle.prototype.destroy = function () {
+ this.$handle.remove();
+ };
+ Handle.prototype.update = function (target) {
+ if (this.context.isDisabled()) {
+ return false;
+ }
+ var isImage = dom.isImg(target);
+ var $selection = this.$handle.find('.note-control-selection');
+ this.context.invoke('imagePopover.update', target);
+ if (isImage) {
+ var $image = $$1(target);
+ var position = $image.position();
+ var pos = {
+ left: position.left + parseInt($image.css('marginLeft'), 10),
+ top: position.top + parseInt($image.css('marginTop'), 10)
+ };
+ // exclude margin
+ var imageSize = {
+ w: $image.outerWidth(false),
+ h: $image.outerHeight(false)
+ };
+ $selection.css({
+ display: 'block',
+ left: pos.left,
+ top: pos.top,
+ width: imageSize.w,
+ height: imageSize.h
+ }).data('target', $image); // save current image element.
+ var origImageObj = new Image();
+ origImageObj.src = $image.attr('src');
+ var sizingText = imageSize.w + 'x' + imageSize.h + ' (' + this.lang.image.original + ': ' + origImageObj.width + 'x' + origImageObj.height + ')';
+ $selection.find('.note-control-selection-info').text(sizingText);
+ this.context.invoke('editor.saveTarget', target);
+ }
+ else {
+ this.hide();
+ }
+ return isImage;
+ };
+ /**
+ * hide
+ *
+ * @param {jQuery} $handle
+ */
+ Handle.prototype.hide = function () {
+ this.context.invoke('editor.clearTarget');
+ this.$handle.children().hide();
+ };
+ return Handle;
+}());
+
+var defaultScheme = 'http://';
+var linkPattern = /^([A-Za-z][A-Za-z0-9+-.]*\:[\/\/]?|mailto:[A-Z0-9._%+-]+@)?(www\.)?(.+)$/i;
+var AutoLink = /** @class */ (function () {
+ function AutoLink(context) {
+ var _this = this;
+ this.context = context;
+ this.events = {
+ 'summernote.keyup': function (we, e) {
+ if (!e.isDefaultPrevented()) {
+ _this.handleKeyup(e);
+ }
+ },
+ 'summernote.keydown': function (we, e) {
+ _this.handleKeydown(e);
+ }
+ };
+ }
+ AutoLink.prototype.initialize = function () {
+ this.lastWordRange = null;
+ };
+ AutoLink.prototype.destroy = function () {
+ this.lastWordRange = null;
+ };
+ AutoLink.prototype.replace = function () {
+ if (!this.lastWordRange) {
+ return;
+ }
+ var keyword = this.lastWordRange.toString();
+ var match = keyword.match(linkPattern);
+ if (match && (match[1] || match[2])) {
+ var link = match[1] ? keyword : defaultScheme + keyword;
+ var node = $$1('<a />').html(keyword).attr('href', link)[0];
+ this.lastWordRange.insertNode(node);
+ this.lastWordRange = null;
+ this.context.invoke('editor.focus');
+ }
+ };
+ AutoLink.prototype.handleKeydown = function (e) {
+ if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
+ var wordRange = this.context.invoke('editor.createRange').getWordRange();
+ this.lastWordRange = wordRange;
+ }
+ };
+ AutoLink.prototype.handleKeyup = function (e) {
+ if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
+ this.replace();
+ }
+ };
+ return AutoLink;
+}());
+
+/**
+ * textarea auto sync.
+ */
+var AutoSync = /** @class */ (function () {
+ function AutoSync(context) {
+ var _this = this;
+ this.$note = context.layoutInfo.note;
+ this.events = {
+ 'summernote.change': function () {
+ _this.$note.val(context.invoke('code'));
+ }
+ };
+ }
+ AutoSync.prototype.shouldInitialize = function () {
+ return dom.isTextarea(this.$note[0]);
+ };
+ return AutoSync;
+}());
+
+var Placeholder = /** @class */ (function () {
+ function Placeholder(context) {
+ var _this = this;
+ this.context = context;
+ this.$editingArea = context.layoutInfo.editingArea;
+ this.options = context.options;
+ this.events = {
+ 'summernote.init summernote.change': function () {
+ _this.update();
+ },
+ 'summernote.codeview.toggled': function () {
+ _this.update();
+ }
+ };
+ }
+ Placeholder.prototype.shouldInitialize = function () {
+ return !!this.options.placeholder;
+ };
+ Placeholder.prototype.initialize = function () {
+ var _this = this;
+ this.$placeholder = $$1('<div class="note-placeholder">');
+ this.$placeholder.on('click', function () {
+ _this.context.invoke('focus');
+ }).text(this.options.placeholder).prependTo(this.$editingArea);
+ this.update();
+ };
+ Placeholder.prototype.destroy = function () {
+ this.$placeholder.remove();
+ };
+ Placeholder.prototype.update = function () {
+ var isShow = !this.context.invoke('codeview.isActivated') && this.context.invoke('editor.isEmpty');
+ this.$placeholder.toggle(isShow);
+ };
+ return Placeholder;
+}());
+
+var Buttons = /** @class */ (function () {
+ function Buttons(context) {
+ this.ui = $$1.summernote.ui;
+ this.context = context;
+ this.$toolbar = context.layoutInfo.toolbar;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ this.invertedKeyMap = func.invertObject(this.options.keyMap[env.isMac ? 'mac' : 'pc']);
+ }
+ Buttons.prototype.representShortcut = function (editorMethod) {
+ var shortcut = this.invertedKeyMap[editorMethod];
+ if (!this.options.shortcuts || !shortcut) {
+ return '';
+ }
+ if (env.isMac) {
+ shortcut = shortcut.replace('CMD', '⌘').replace('SHIFT', '⇧');
+ }
+ shortcut = shortcut.replace('BACKSLASH', '\\')
+ .replace('SLASH', '/')
+ .replace('LEFTBRACKET', '[')
+ .replace('RIGHTBRACKET', ']');
+ return ' (' + shortcut + ')';
+ };
+ Buttons.prototype.button = function (o) {
+ if (!this.options.tooltip && o.tooltip) {
+ delete o.tooltip;
+ }
+ o.container = this.options.container;
+ return this.ui.button(o);
+ };
+ Buttons.prototype.initialize = function () {
+ this.addToolbarButtons();
+ this.addImagePopoverButtons();
+ this.addLinkPopoverButtons();
+ this.addTablePopoverButtons();
+ this.fontInstalledMap = {};
+ };
+ Buttons.prototype.destroy = function () {
+ delete this.fontInstalledMap;
+ };
+ Buttons.prototype.isFontInstalled = function (name) {
+ if (!this.fontInstalledMap.hasOwnProperty(name)) {
+ this.fontInstalledMap[name] = env.isFontInstalled(name) ||
+ lists.contains(this.options.fontNamesIgnoreCheck, name);
+ }
+ return this.fontInstalledMap[name];
+ };
+ Buttons.prototype.isFontDeservedToAdd = function (name) {
+ var genericFamilies = ['sans-serif', 'serif', 'monospace', 'cursive', 'fantasy'];
+ name = name.toLowerCase();
+ return ((name !== '') && this.isFontInstalled(name) && ($$1.inArray(name, genericFamilies) === -1));
+ };
+ Buttons.prototype.addToolbarButtons = function () {
+ var _this = this;
+ this.context.memo('button.style', function () {
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.magic), _this.options),
+ tooltip: _this.lang.style.style,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdown({
+ className: 'dropdown-style',
+ items: _this.options.styleTags,
+ template: function (item) {
+ if (typeof item === 'string') {
+ item = { tag: item, title: (_this.lang.style.hasOwnProperty(item) ? _this.lang.style[item] : item) };
+ }
+ var tag = item.tag;
+ var title = item.title;
+ var style = item.style ? ' style="' + item.style + '" ' : '';
+ var className = item.className ? ' class="' + item.className + '"' : '';
+ return '<' + tag + style + className + '>' + title + '</' + tag + '>';
+ },
+ click: _this.context.createInvokeHandler('editor.formatBlock')
+ })
+ ]).render();
+ });
+ var _loop_1 = function (styleIdx, styleLen) {
+ var item = this_1.options.styleTags[styleIdx];
+ this_1.context.memo('button.style.' + item, function () {
+ return _this.button({
+ className: 'note-btn-style-' + item,
+ contents: '<div data-value="' + item + '">' + item.toUpperCase() + '</div>',
+ tooltip: item.toUpperCase(),
+ click: _this.context.createInvokeHandler('editor.formatBlock')
+ }).render();
+ });
+ };
+ var this_1 = this;
+ for (var styleIdx = 0, styleLen = this.options.styleTags.length; styleIdx < styleLen; styleIdx++) {
+ _loop_1(styleIdx, styleLen);
+ }
+ this.context.memo('button.bold', function () {
+ return _this.button({
+ className: 'note-btn-bold',
+ contents: _this.ui.icon(_this.options.icons.bold),
+ tooltip: _this.lang.font.bold + _this.representShortcut('bold'),
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.bold')
+ }).render();
+ });
+ this.context.memo('button.italic', function () {
+ return _this.button({
+ className: 'note-btn-italic',
+ contents: _this.ui.icon(_this.options.icons.italic),
+ tooltip: _this.lang.font.italic + _this.representShortcut('italic'),
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.italic')
+ }).render();
+ });
+ this.context.memo('button.underline', function () {
+ return _this.button({
+ className: 'note-btn-underline',
+ contents: _this.ui.icon(_this.options.icons.underline),
+ tooltip: _this.lang.font.underline + _this.representShortcut('underline'),
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.underline')
+ }).render();
+ });
+ this.context.memo('button.clear', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.eraser),
+ tooltip: _this.lang.font.clear + _this.representShortcut('removeFormat'),
+ click: _this.context.createInvokeHandler('editor.removeFormat')
+ }).render();
+ });
+ this.context.memo('button.strikethrough', function () {
+ return _this.button({
+ className: 'note-btn-strikethrough',
+ contents: _this.ui.icon(_this.options.icons.strikethrough),
+ tooltip: _this.lang.font.strikethrough + _this.representShortcut('strikethrough'),
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.strikethrough')
+ }).render();
+ });
+ this.context.memo('button.superscript', function () {
+ return _this.button({
+ className: 'note-btn-superscript',
+ contents: _this.ui.icon(_this.options.icons.superscript),
+ tooltip: _this.lang.font.superscript,
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.superscript')
+ }).render();
+ });
+ this.context.memo('button.subscript', function () {
+ return _this.button({
+ className: 'note-btn-subscript',
+ contents: _this.ui.icon(_this.options.icons.subscript),
+ tooltip: _this.lang.font.subscript,
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.subscript')
+ }).render();
+ });
+ this.context.memo('button.fontname', function () {
+ var styleInfo = _this.context.invoke('editor.currentStyle');
+ // Add 'default' fonts into the fontnames array if not exist
+ $$1.each(styleInfo['font-family'].split(','), function (idx, fontname) {
+ fontname = fontname.trim().replace(/['"]+/g, '');
+ if (_this.isFontDeservedToAdd(fontname)) {
+ if ($$1.inArray(fontname, _this.options.fontNames) === -1) {
+ _this.options.fontNames.push(fontname);
+ }
+ }
+ });
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents('<span class="note-current-fontname"/>', _this.options),
+ tooltip: _this.lang.font.name,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdownCheck({
+ className: 'dropdown-fontname',
+ checkClassName: _this.options.icons.menuCheck,
+ items: _this.options.fontNames.filter(_this.isFontInstalled.bind(_this)),
+ template: function (item) {
+ return '<span style="font-family: \'' + item + '\'">' + item + '</span>';
+ },
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.fontName')
+ })
+ ]).render();
+ });
+ this.context.memo('button.fontsize', function () {
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents('<span class="note-current-fontsize"/>', _this.options),
+ tooltip: _this.lang.font.size,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdownCheck({
+ className: 'dropdown-fontsize',
+ checkClassName: _this.options.icons.menuCheck,
+ items: _this.options.fontSizes,
+ click: _this.context.createInvokeHandlerAndUpdateState('editor.fontSize')
+ })
+ ]).render();
+ });
+ this.context.memo('button.color', function () {
+ return _this.ui.buttonGroup({
+ className: 'note-color',
+ children: [
+ _this.button({
+ className: 'note-current-color-button',
+ contents: _this.ui.icon(_this.options.icons.font + ' note-recent-color'),
+ tooltip: _this.lang.color.recent,
+ click: function (e) {
+ var $button = $$1(e.currentTarget);
+ _this.context.invoke('editor.color', {
+ backColor: $button.attr('data-backColor'),
+ foreColor: $button.attr('data-foreColor')
+ });
+ },
+ callback: function ($button) {
+ var $recentColor = $button.find('.note-recent-color');
+ $recentColor.css('background-color', '#FFFF00');
+ $button.attr('data-backColor', '#FFFF00');
+ }
+ }),
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents('', _this.options),
+ tooltip: _this.lang.color.more,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdown({
+ items: [
+ '<div class="note-palette">',
+ ' <div class="note-palette-title">' + _this.lang.color.background + '</div>',
+ ' <div>',
+ ' <button type="button" class="note-color-reset btn btn-light" data-event="backColor" data-value="inherit">',
+ _this.lang.color.transparent,
+ ' </button>',
+ ' </div>',
+ ' <div class="note-holder" data-event="backColor"/>',
+ '</div>',
+ '<div class="note-palette">',
+ ' <div class="note-palette-title">' + _this.lang.color.foreground + '</div>',
+ ' <div>',
+ ' <button type="button" class="note-color-reset btn btn-light" data-event="removeFormat" data-value="foreColor">',
+ _this.lang.color.resetToDefault,
+ ' </button>',
+ ' </div>',
+ ' <div class="note-holder" data-event="foreColor"/>',
+ '</div>'
+ ].join(''),
+ callback: function ($dropdown) {
+ $dropdown.find('.note-holder').each(function (idx, item) {
+ var $holder = $$1(item);
+ $holder.append(_this.ui.palette({
+ colors: _this.options.colors,
+ eventName: $holder.data('event'),
+ container: _this.options.container,
+ tooltip: _this.options.tooltip
+ }).render());
+ });
+ },
+ click: function (event) {
+ var $button = $$1(event.target);
+ var eventName = $button.data('event');
+ var value = $button.data('value');
+ if (eventName && value) {
+ var key = eventName === 'backColor' ? 'background-color' : 'color';
+ var $color = $button.closest('.note-color').find('.note-recent-color');
+ var $currentButton = $button.closest('.note-color').find('.note-current-color-button');
+ $color.css(key, value);
+ $currentButton.attr('data-' + eventName, value);
+ _this.context.invoke('editor.' + eventName, value);
+ }
+ }
+ })
+ ]
+ }).render();
+ });
+ this.context.memo('button.ul', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.unorderedlist),
+ tooltip: _this.lang.lists.unordered + _this.representShortcut('insertUnorderedList'),
+ click: _this.context.createInvokeHandler('editor.insertUnorderedList')
+ }).render();
+ });
+ this.context.memo('button.ol', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.orderedlist),
+ tooltip: _this.lang.lists.ordered + _this.representShortcut('insertOrderedList'),
+ click: _this.context.createInvokeHandler('editor.insertOrderedList')
+ }).render();
+ });
+ var justifyLeft = this.button({
+ contents: this.ui.icon(this.options.icons.alignLeft),
+ tooltip: this.lang.paragraph.left + this.representShortcut('justifyLeft'),
+ click: this.context.createInvokeHandler('editor.justifyLeft')
+ });
+ var justifyCenter = this.button({
+ contents: this.ui.icon(this.options.icons.alignCenter),
+ tooltip: this.lang.paragraph.center + this.representShortcut('justifyCenter'),
+ click: this.context.createInvokeHandler('editor.justifyCenter')
+ });
+ var justifyRight = this.button({
+ contents: this.ui.icon(this.options.icons.alignRight),
+ tooltip: this.lang.paragraph.right + this.representShortcut('justifyRight'),
+ click: this.context.createInvokeHandler('editor.justifyRight')
+ });
+ var justifyFull = this.button({
+ contents: this.ui.icon(this.options.icons.alignJustify),
+ tooltip: this.lang.paragraph.justify + this.representShortcut('justifyFull'),
+ click: this.context.createInvokeHandler('editor.justifyFull')
+ });
+ var outdent = this.button({
+ contents: this.ui.icon(this.options.icons.outdent),
+ tooltip: this.lang.paragraph.outdent + this.representShortcut('outdent'),
+ click: this.context.createInvokeHandler('editor.outdent')
+ });
+ var indent = this.button({
+ contents: this.ui.icon(this.options.icons.indent),
+ tooltip: this.lang.paragraph.indent + this.representShortcut('indent'),
+ click: this.context.createInvokeHandler('editor.indent')
+ });
+ this.context.memo('button.justifyLeft', func.invoke(justifyLeft, 'render'));
+ this.context.memo('button.justifyCenter', func.invoke(justifyCenter, 'render'));
+ this.context.memo('button.justifyRight', func.invoke(justifyRight, 'render'));
+ this.context.memo('button.justifyFull', func.invoke(justifyFull, 'render'));
+ this.context.memo('button.outdent', func.invoke(outdent, 'render'));
+ this.context.memo('button.indent', func.invoke(indent, 'render'));
+ this.context.memo('button.paragraph', function () {
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.alignLeft), _this.options),
+ tooltip: _this.lang.paragraph.paragraph,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdown([
+ _this.ui.buttonGroup({
+ className: 'note-align',
+ children: [justifyLeft, justifyCenter, justifyRight, justifyFull]
+ }),
+ _this.ui.buttonGroup({
+ className: 'note-list',
+ children: [outdent, indent]
+ })
+ ])
+ ]).render();
+ });
+ this.context.memo('button.height', function () {
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.textHeight), _this.options),
+ tooltip: _this.lang.font.height,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdownCheck({
+ items: _this.options.lineHeights,
+ checkClassName: _this.options.icons.menuCheck,
+ className: 'dropdown-line-height',
+ click: _this.context.createInvokeHandler('editor.lineHeight')
+ })
+ ]).render();
+ });
+ this.context.memo('button.table', function () {
+ return _this.ui.buttonGroup([
+ _this.button({
+ className: 'dropdown-toggle',
+ contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.table), _this.options),
+ tooltip: _this.lang.table.table,
+ data: {
+ toggle: 'dropdown'
+ }
+ }),
+ _this.ui.dropdown({
+ className: 'note-table',
+ items: [
+ '<div class="note-dimension-picker">',
+ ' <div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"/>',
+ ' <div class="note-dimension-picker-highlighted"/>',
+ ' <div class="note-dimension-picker-unhighlighted"/>',
+ '</div>',
+ '<div class="note-dimension-display">1 x 1</div>'
+ ].join('')
+ })
+ ], {
+ callback: function ($node) {
+ var $catcher = $node.find('.note-dimension-picker-mousecatcher');
+ $catcher.css({
+ width: _this.options.insertTableMaxSize.col + 'em',
+ height: _this.options.insertTableMaxSize.row + 'em'
+ }).mousedown(_this.context.createInvokeHandler('editor.insertTable'))
+ .on('mousemove', _this.tableMoveHandler.bind(_this));
+ }
+ }).render();
+ });
+ this.context.memo('button.link', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.link),
+ tooltip: _this.lang.link.link + _this.representShortcut('linkDialog.show'),
+ click: _this.context.createInvokeHandler('linkDialog.show')
+ }).render();
+ });
+ this.context.memo('button.picture', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.picture),
+ tooltip: _this.lang.image.image,
+ click: _this.context.createInvokeHandler('imageDialog.show')
+ }).render();
+ });
+ this.context.memo('button.video', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.video),
+ tooltip: _this.lang.video.video,
+ click: _this.context.createInvokeHandler('videoDialog.show')
+ }).render();
+ });
+ this.context.memo('button.hr', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.minus),
+ tooltip: _this.lang.hr.insert + _this.representShortcut('insertHorizontalRule'),
+ click: _this.context.createInvokeHandler('editor.insertHorizontalRule')
+ }).render();
+ });
+ this.context.memo('button.fullscreen', function () {
+ return _this.button({
+ className: 'btn-fullscreen',
+ contents: _this.ui.icon(_this.options.icons.arrowsAlt),
+ tooltip: _this.options.fullscreen,
+ click: _this.context.createInvokeHandler('fullscreen.toggle')
+ }).render();
+ });
+ this.context.memo('button.codeview', function () {
+ return _this.button({
+ className: 'btn-codeview',
+ contents: _this.ui.icon(_this.options.icons.code),
+ tooltip: _this.options.codeview,
+ click: _this.context.createInvokeHandler('codeview.toggle')
+ }).render();
+ });
+ this.context.memo('button.redo', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.redo),
+ tooltip: _this.lang.history.redo + _this.representShortcut('redo'),
+ click: _this.context.createInvokeHandler('editor.redo')
+ }).render();
+ });
+ this.context.memo('button.undo', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.undo),
+ tooltip: _this.lang.history.undo + _this.representShortcut('undo'),
+ click: _this.context.createInvokeHandler('editor.undo')
+ }).render();
+ });
+ this.context.memo('button.help', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.question),
+ tooltip: _this.options.help,
+ click: _this.context.createInvokeHandler('helpDialog.show')
+ }).render();
+ });
+ };
+ /**
+ * image : [
+ * ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
+ * ['float', ['floatLeft', 'floatRight', 'floatNone' ]],
+ * ['remove', ['removeMedia']]
+ * ],
+ */
+ Buttons.prototype.addImagePopoverButtons = function () {
+ var _this = this;
+ // Image Size Buttons
+ this.context.memo('button.imageSize100', function () {
+ return _this.button({
+ contents: '<span class="note-fontsize-10">100%</span>',
+ tooltip: _this.lang.image.resizeFull,
+ click: _this.context.createInvokeHandler('editor.resize', '1')
+ }).render();
+ });
+ this.context.memo('button.imageSize50', function () {
+ return _this.button({
+ contents: '<span class="note-fontsize-10">50%</span>',
+ tooltip: _this.lang.image.resizeHalf,
+ click: _this.context.createInvokeHandler('editor.resize', '0.5')
+ }).render();
+ });
+ this.context.memo('button.imageSize25', function () {
+ return _this.button({
+ contents: '<span class="note-fontsize-10">25%</span>',
+ tooltip: _this.lang.image.resizeQuarter,
+ click: _this.context.createInvokeHandler('editor.resize', '0.25')
+ }).render();
+ });
+ // Float Buttons
+ this.context.memo('button.floatLeft', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.alignLeft),
+ tooltip: _this.lang.image.floatLeft,
+ click: _this.context.createInvokeHandler('editor.floatMe', 'left')
+ }).render();
+ });
+ this.context.memo('button.floatRight', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.alignRight),
+ tooltip: _this.lang.image.floatRight,
+ click: _this.context.createInvokeHandler('editor.floatMe', 'right')
+ }).render();
+ });
+ this.context.memo('button.floatNone', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.alignJustify),
+ tooltip: _this.lang.image.floatNone,
+ click: _this.context.createInvokeHandler('editor.floatMe', 'none')
+ }).render();
+ });
+ // Remove Buttons
+ this.context.memo('button.removeMedia', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.trash),
+ tooltip: _this.lang.image.remove,
+ click: _this.context.createInvokeHandler('editor.removeMedia')
+ }).render();
+ });
+ };
+ Buttons.prototype.addLinkPopoverButtons = function () {
+ var _this = this;
+ this.context.memo('button.linkDialogShow', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.link),
+ tooltip: _this.lang.link.edit,
+ click: _this.context.createInvokeHandler('linkDialog.show')
+ }).render();
+ });
+ this.context.memo('button.unlink', function () {
+ return _this.button({
+ contents: _this.ui.icon(_this.options.icons.unlink),
+ tooltip: _this.lang.link.unlink,
+ click: _this.context.createInvokeHandler('editor.unlink')
+ }).render();
+ });
+ };
+ /**
+ * table : [
+ * ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
+ * ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
+ * ],
+ */
+ Buttons.prototype.addTablePopoverButtons = function () {
+ var _this = this;
+ this.context.memo('button.addRowUp', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.rowAbove),
+ tooltip: _this.lang.table.addRowAbove,
+ click: _this.context.createInvokeHandler('editor.addRow', 'top')
+ }).render();
+ });
+ this.context.memo('button.addRowDown', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.rowBelow),
+ tooltip: _this.lang.table.addRowBelow,
+ click: _this.context.createInvokeHandler('editor.addRow', 'bottom')
+ }).render();
+ });
+ this.context.memo('button.addColLeft', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.colBefore),
+ tooltip: _this.lang.table.addColLeft,
+ click: _this.context.createInvokeHandler('editor.addCol', 'left')
+ }).render();
+ });
+ this.context.memo('button.addColRight', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.colAfter),
+ tooltip: _this.lang.table.addColRight,
+ click: _this.context.createInvokeHandler('editor.addCol', 'right')
+ }).render();
+ });
+ this.context.memo('button.deleteRow', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.rowRemove),
+ tooltip: _this.lang.table.delRow,
+ click: _this.context.createInvokeHandler('editor.deleteRow')
+ }).render();
+ });
+ this.context.memo('button.deleteCol', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.colRemove),
+ tooltip: _this.lang.table.delCol,
+ click: _this.context.createInvokeHandler('editor.deleteCol')
+ }).render();
+ });
+ this.context.memo('button.deleteTable', function () {
+ return _this.button({
+ className: 'btn-md',
+ contents: _this.ui.icon(_this.options.icons.trash),
+ tooltip: _this.lang.table.delTable,
+ click: _this.context.createInvokeHandler('editor.deleteTable')
+ }).render();
+ });
+ };
+ Buttons.prototype.build = function ($container, groups) {
+ for (var groupIdx = 0, groupLen = groups.length; groupIdx < groupLen; groupIdx++) {
+ var group = groups[groupIdx];
+ var groupName = $$1.isArray(group) ? group[0] : group;
+ var buttons = $$1.isArray(group) ? ((group.length === 1) ? [group[0]] : group[1]) : [group];
+ var $group = this.ui.buttonGroup({
+ className: 'note-' + groupName
+ }).render();
+ for (var idx = 0, len = buttons.length; idx < len; idx++) {
+ var btn = this.context.memo('button.' + buttons[idx]);
+ if (btn) {
+ $group.append(typeof btn === 'function' ? btn(this.context) : btn);
+ }
+ }
+ $group.appendTo($container);
+ }
+ };
+ /**
+ * @param {jQuery} [$container]
+ */
+ Buttons.prototype.updateCurrentStyle = function ($container) {
+ var _this = this;
+ var $cont = $container || this.$toolbar;
+ var styleInfo = this.context.invoke('editor.currentStyle');
+ this.updateBtnStates($cont, {
+ '.note-btn-bold': function () {
+ return styleInfo['font-bold'] === 'bold';
+ },
+ '.note-btn-italic': function () {
+ return styleInfo['font-italic'] === 'italic';
+ },
+ '.note-btn-underline': function () {
+ return styleInfo['font-underline'] === 'underline';
+ },
+ '.note-btn-subscript': function () {
+ return styleInfo['font-subscript'] === 'subscript';
+ },
+ '.note-btn-superscript': function () {
+ return styleInfo['font-superscript'] === 'superscript';
+ },
+ '.note-btn-strikethrough': function () {
+ return styleInfo['font-strikethrough'] === 'strikethrough';
+ }
+ });
+ if (styleInfo['font-family']) {
+ var fontNames = styleInfo['font-family'].split(',').map(function (name) {
+ return name.replace(/[\'\"]/g, '')
+ .replace(/\s+$/, '')
+ .replace(/^\s+/, '');
+ });
+ var fontName_1 = lists.find(fontNames, this.isFontInstalled.bind(this));
+ $cont.find('.dropdown-fontname a').each(function (idx, item) {
+ var $item = $$1(item);
+ // always compare string to avoid creating another func.
+ var isChecked = ($item.data('value') + '') === (fontName_1 + '');
+ $item.toggleClass('checked', isChecked);
+ });
+ $cont.find('.note-current-fontname').text(fontName_1).css('font-family', fontName_1);
+ }
+ if (styleInfo['font-size']) {
+ var fontSize_1 = styleInfo['font-size'];
+ $cont.find('.dropdown-fontsize a').each(function (idx, item) {
+ var $item = $$1(item);
+ // always compare with string to avoid creating another func.
+ var isChecked = ($item.data('value') + '') === (fontSize_1 + '');
+ $item.toggleClass('checked', isChecked);
+ });
+ $cont.find('.note-current-fontsize').text(fontSize_1);
+ }
+ if (styleInfo['line-height']) {
+ var lineHeight_1 = styleInfo['line-height'];
+ $cont.find('.dropdown-line-height li a').each(function (idx, item) {
+ // always compare with string to avoid creating another func.
+ var isChecked = ($$1(item).data('value') + '') === (lineHeight_1 + '');
+ _this.className = isChecked ? 'checked' : '';
+ });
+ }
+ };
+ Buttons.prototype.updateBtnStates = function ($container, infos) {
+ var _this = this;
+ $$1.each(infos, function (selector, pred) {
+ _this.ui.toggleBtnActive($container.find(selector), pred());
+ });
+ };
+ Buttons.prototype.tableMoveHandler = function (event) {
+ var PX_PER_EM = 18;
+ var $picker = $$1(event.target.parentNode); // target is mousecatcher
+ var $dimensionDisplay = $picker.next();
+ var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
+ var $highlighted = $picker.find('.note-dimension-picker-highlighted');
+ var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
+ var posOffset;
+ // HTML5 with jQuery - e.offsetX is undefined in Firefox
+ if (event.offsetX === undefined) {
+ var posCatcher = $$1(event.target).offset();
+ posOffset = {
+ x: event.pageX - posCatcher.left,
+ y: event.pageY - posCatcher.top
+ };
+ }
+ else {
+ posOffset = {
+ x: event.offsetX,
+ y: event.offsetY
+ };
+ }
+ var dim = {
+ c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
+ r: Math.ceil(posOffset.y / PX_PER_EM) || 1
+ };
+ $highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
+ $catcher.data('value', dim.c + 'x' + dim.r);
+ if (dim.c > 3 && dim.c < this.options.insertTableMaxSize.col) {
+ $unhighlighted.css({ width: dim.c + 1 + 'em' });
+ }
+ if (dim.r > 3 && dim.r < this.options.insertTableMaxSize.row) {
+ $unhighlighted.css({ height: dim.r + 1 + 'em' });
+ }
+ $dimensionDisplay.html(dim.c + ' x ' + dim.r);
+ };
+ return Buttons;
+}());
+
+var Toolbar = /** @class */ (function () {
+ function Toolbar(context) {
+ this.context = context;
+ this.$window = $$1(window);
+ this.$document = $$1(document);
+ this.ui = $$1.summernote.ui;
+ this.$note = context.layoutInfo.note;
+ this.$editor = context.layoutInfo.editor;
+ this.$toolbar = context.layoutInfo.toolbar;
+ this.options = context.options;
+ this.followScroll = this.followScroll.bind(this);
+ }
+ Toolbar.prototype.shouldInitialize = function () {
+ return !this.options.airMode;
+ };
+ Toolbar.prototype.initialize = function () {
+ var _this = this;
+ this.options.toolbar = this.options.toolbar || [];
+ if (!this.options.toolbar.length) {
+ this.$toolbar.hide();
+ }
+ else {
+ this.context.invoke('buttons.build', this.$toolbar, this.options.toolbar);
+ }
+ if (this.options.toolbarContainer) {
+ this.$toolbar.appendTo(this.options.toolbarContainer);
+ }
+ this.changeContainer(false);
+ this.$note.on('summernote.keyup summernote.mouseup summernote.change', function () {
+ _this.context.invoke('buttons.updateCurrentStyle');
+ });
+ this.context.invoke('buttons.updateCurrentStyle');
+ if (this.options.followingToolbar) {
+ this.$window.on('scroll resize', this.followScroll);
+ }
+ };
+ Toolbar.prototype.destroy = function () {
+ this.$toolbar.children().remove();
+ if (this.options.followingToolbar) {
+ this.$window.off('scroll resize', this.followScroll);
+ }
+ };
+ Toolbar.prototype.followScroll = function () {
+ if (this.$editor.hasClass('fullscreen')) {
+ return false;
+ }
+ var $toolbarWrapper = this.$toolbar.parent('.note-toolbar-wrapper');
+ var editorHeight = this.$editor.outerHeight();
+ var editorWidth = this.$editor.width();
+ var toolbarHeight = this.$toolbar.height();
+ $toolbarWrapper.css({
+ height: toolbarHeight
+ });
+ // check if the web app is currently using another static bar
+ var otherBarHeight = 0;
+ if (this.options.otherStaticBar) {
+ otherBarHeight = $$1(this.options.otherStaticBar).outerHeight();
+ }
+ var currentOffset = this.$document.scrollTop();
+ var editorOffsetTop = this.$editor.offset().top;
+ var editorOffsetBottom = editorOffsetTop + editorHeight;
+ var activateOffset = editorOffsetTop - otherBarHeight;
+ var deactivateOffsetBottom = editorOffsetBottom - otherBarHeight - toolbarHeight;
+ if ((currentOffset > activateOffset) && (currentOffset < deactivateOffsetBottom)) {
+ this.$toolbar.css({
+ position: 'fixed',
+ top: otherBarHeight,
+ width: editorWidth
+ });
+ }
+ else {
+ this.$toolbar.css({
+ position: 'relative',
+ top: 0,
+ width: '100%'
+ });
+ }
+ };
+ Toolbar.prototype.changeContainer = function (isFullscreen) {
+ if (isFullscreen) {
+ this.$toolbar.prependTo(this.$editor);
+ }
+ else {
+ if (this.options.toolbarContainer) {
+ this.$toolbar.appendTo(this.options.toolbarContainer);
+ }
+ }
+ };
+ Toolbar.prototype.updateFullscreen = function (isFullscreen) {
+ this.ui.toggleBtnActive(this.$toolbar.find('.btn-fullscreen'), isFullscreen);
+ this.changeContainer(isFullscreen);
+ };
+ Toolbar.prototype.updateCodeview = function (isCodeview) {
+ this.ui.toggleBtnActive(this.$toolbar.find('.btn-codeview'), isCodeview);
+ if (isCodeview) {
+ this.deactivate();
+ }
+ else {
+ this.activate();
+ }
+ };
+ Toolbar.prototype.activate = function (isIncludeCodeview) {
+ var $btn = this.$toolbar.find('button');
+ if (!isIncludeCodeview) {
+ $btn = $btn.not('.btn-codeview');
+ }
+ this.ui.toggleBtn($btn, true);
+ };
+ Toolbar.prototype.deactivate = function (isIncludeCodeview) {
+ var $btn = this.$toolbar.find('button');
+ if (!isIncludeCodeview) {
+ $btn = $btn.not('.btn-codeview');
+ }
+ this.ui.toggleBtn($btn, false);
+ };
+ return Toolbar;
+}());
+
+var LinkDialog = /** @class */ (function () {
+ function LinkDialog(context) {
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.$body = $$1(document.body);
+ this.$editor = context.layoutInfo.editor;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ context.memo('help.linkDialog.show', this.options.langInfo.help['linkDialog.show']);
+ }
+ LinkDialog.prototype.initialize = function () {
+ var $container = this.options.dialogsInBody ? this.$body : this.$editor;
+ var body = [
+ '<div class="form-group note-form-group">',
+ "<label class=\"note-form-label\">" + this.lang.link.textToDisplay + "</label>",
+ '<input class="note-link-text form-control note-form-control note-input" type="text" />',
+ '</div>',
+ '<div class="form-group note-form-group">',
+ "<label class=\"note-form-label\">" + this.lang.link.url + "</label>",
+ '<input class="note-link-url form-control note-form-control note-input" type="text" value="http://" />',
+ '</div>',
+ !this.options.disableLinkTarget
+ ? $$1('<div/>').append(this.ui.checkbox({
+ id: 'sn-checkbox-open-in-new-window',
+ text: this.lang.link.openInNewWindow,
+ checked: true
+ }).render()).html()
+ : ''
+ ].join('');
+ var buttonClass = 'btn btn-primary note-btn note-btn-primary note-link-btn';
+ var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.link.insert + "</button>";
+ this.$dialog = this.ui.dialog({
+ className: 'link-dialog',
+ title: this.lang.link.insert,
+ fade: this.options.dialogsFade,
+ body: body,
+ footer: footer
+ }).render().appendTo($container);
+ };
+ LinkDialog.prototype.destroy = function () {
+ this.ui.hideDialog(this.$dialog);
+ this.$dialog.remove();
+ };
+ LinkDialog.prototype.bindEnterKey = function ($input, $btn) {
+ $input.on('keypress', function (event) {
+ if (event.keyCode === key.code.ENTER) {
+ event.preventDefault();
+ $btn.trigger('click');
+ }
+ });
+ };
+ /**
+ * toggle update button
+ */
+ LinkDialog.prototype.toggleLinkBtn = function ($linkBtn, $linkText, $linkUrl) {
+ this.ui.toggleBtn($linkBtn, $linkText.val() && $linkUrl.val());
+ };
+ /**
+ * Show link dialog and set event handlers on dialog controls.
+ *
+ * @param {Object} linkInfo
+ * @return {Promise}
+ */
+ LinkDialog.prototype.showLinkDialog = function (linkInfo) {
+ var _this = this;
+ return $$1.Deferred(function (deferred) {
+ var $linkText = _this.$dialog.find('.note-link-text');
+ var $linkUrl = _this.$dialog.find('.note-link-url');
+ var $linkBtn = _this.$dialog.find('.note-link-btn');
+ var $openInNewWindow = _this.$dialog.find('input[type=checkbox]');
+ _this.ui.onDialogShown(_this.$dialog, function () {
+ _this.context.triggerEvent('dialog.shown');
+ // if no url was given, copy text to url
+ if (!linkInfo.url) {
+ linkInfo.url = linkInfo.text;
+ }
+ $linkText.val(linkInfo.text);
+ var handleLinkTextUpdate = function () {
+ _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
+ // if linktext was modified by keyup,
+ // stop cloning text from linkUrl
+ linkInfo.text = $linkText.val();
+ };
+ $linkText.on('input', handleLinkTextUpdate).on('paste', function () {
+ setTimeout(handleLinkTextUpdate, 0);
+ });
+ var handleLinkUrlUpdate = function () {
+ _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
+ // display same link on `Text to display` input
+ // when create a new link
+ if (!linkInfo.text) {
+ $linkText.val($linkUrl.val());
+ }
+ };
+ $linkUrl.on('input', handleLinkUrlUpdate).on('paste', function () {
+ setTimeout(handleLinkUrlUpdate, 0);
+ }).val(linkInfo.url);
+ if (!env.isSupportTouch) {
+ $linkUrl.trigger('focus');
+ }
+ _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
+ _this.bindEnterKey($linkUrl, $linkBtn);
+ _this.bindEnterKey($linkText, $linkBtn);
+ var isChecked = linkInfo.isNewWindow !== undefined
+ ? linkInfo.isNewWindow : _this.context.options.linkTargetBlank;
+ $openInNewWindow.prop('checked', isChecked);
+ $linkBtn.one('click', function (event) {
+ event.preventDefault();
+ deferred.resolve({
+ range: linkInfo.range,
+ url: $linkUrl.val(),
+ text: $linkText.val(),
+ isNewWindow: $openInNewWindow.is(':checked')
+ });
+ _this.ui.hideDialog(_this.$dialog);
+ });
+ });
+ _this.ui.onDialogHidden(_this.$dialog, function () {
+ // detach events
+ $linkText.off('input paste keypress');
+ $linkUrl.off('input paste keypress');
+ $linkBtn.off('click');
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+ _this.ui.showDialog(_this.$dialog);
+ }).promise();
+ };
+ /**
+ * @param {Object} layoutInfo
+ */
+ LinkDialog.prototype.show = function () {
+ var _this = this;
+ var linkInfo = this.context.invoke('editor.getLinkInfo');
+ this.context.invoke('editor.saveRange');
+ this.showLinkDialog(linkInfo).then(function (linkInfo) {
+ _this.context.invoke('editor.restoreRange');
+ _this.context.invoke('editor.createLink', linkInfo);
+ }).fail(function () {
+ _this.context.invoke('editor.restoreRange');
+ });
+ };
+ return LinkDialog;
+}());
+
+var LinkPopover = /** @class */ (function () {
+ function LinkPopover(context) {
+ var _this = this;
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.options = context.options;
+ this.events = {
+ 'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function () {
+ _this.update();
+ },
+ 'summernote.disable summernote.dialog.shown': function () {
+ _this.hide();
+ }
+ };
+ }
+ LinkPopover.prototype.shouldInitialize = function () {
+ return !lists.isEmpty(this.options.popover.link);
+ };
+ LinkPopover.prototype.initialize = function () {
+ this.$popover = this.ui.popover({
+ className: 'note-link-popover',
+ callback: function ($node) {
+ var $content = $node.find('.popover-content,.note-popover-content');
+ $content.prepend('<span><a target="_blank"></a>&nbsp;</span>');
+ }
+ }).render().appendTo(this.options.container);
+ var $content = this.$popover.find('.popover-content,.note-popover-content');
+ this.context.invoke('buttons.build', $content, this.options.popover.link);
+ };
+ LinkPopover.prototype.destroy = function () {
+ this.$popover.remove();
+ };
+ LinkPopover.prototype.update = function () {
+ // Prevent focusing on editable when invoke('code') is executed
+ if (!this.context.invoke('editor.hasFocus')) {
+ this.hide();
+ return;
+ }
+ var rng = this.context.invoke('editor.createRange');
+ if (rng.isCollapsed() && rng.isOnAnchor()) {
+ var anchor = dom.ancestor(rng.sc, dom.isAnchor);
+ var href = $$1(anchor).attr('href');
+ this.$popover.find('a').attr('href', href).html(href);
+ var pos = dom.posFromPlaceholder(anchor);
+ this.$popover.css({
+ display: 'block',
+ left: pos.left,
+ top: pos.top
+ });
+ }
+ else {
+ this.hide();
+ }
+ };
+ LinkPopover.prototype.hide = function () {
+ this.$popover.hide();
+ };
+ return LinkPopover;
+}());
+
+var ImageDialog = /** @class */ (function () {
+ function ImageDialog(context) {
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.$body = $$1(document.body);
+ this.$editor = context.layoutInfo.editor;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ }
+ ImageDialog.prototype.initialize = function () {
+ var $container = this.options.dialogsInBody ? this.$body : this.$editor;
+ var imageLimitation = '';
+ if (this.options.maximumImageFileSize) {
+ var unit = Math.floor(Math.log(this.options.maximumImageFileSize) / Math.log(1024));
+ var readableSize = (this.options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
+ ' ' + ' KMGTP'[unit] + 'B';
+ imageLimitation = "<small>" + (this.lang.image.maximumFileSize + ' : ' + readableSize) + "</small>";
+ }
+ var body = [
+ '<div class="form-group note-form-group note-group-select-from-files">',
+ '<label class="note-form-label">' + this.lang.image.selectFromFiles + '</label>',
+ '<input class="note-image-input note-form-control note-input" ',
+ ' type="file" name="files" accept="image/*" multiple="multiple" />',
+ imageLimitation,
+ '</div>',
+ '<div class="form-group note-group-image-url" style="overflow:auto;">',
+ '<label class="note-form-label">' + this.lang.image.url + '</label>',
+ '<input class="note-image-url form-control note-form-control note-input ',
+ ' col-md-12" type="text" />',
+ '</div>'
+ ].join('');
+ var buttonClass = 'btn btn-primary note-btn note-btn-primary note-image-btn';
+ var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.image.insert + "</button>";
+ this.$dialog = this.ui.dialog({
+ title: this.lang.image.insert,
+ fade: this.options.dialogsFade,
+ body: body,
+ footer: footer
+ }).render().appendTo($container);
+ };
+ ImageDialog.prototype.destroy = function () {
+ this.ui.hideDialog(this.$dialog);
+ this.$dialog.remove();
+ };
+ ImageDialog.prototype.bindEnterKey = function ($input, $btn) {
+ $input.on('keypress', function (event) {
+ if (event.keyCode === key.code.ENTER) {
+ event.preventDefault();
+ $btn.trigger('click');
+ }
+ });
+ };
+ ImageDialog.prototype.show = function () {
+ var _this = this;
+ this.context.invoke('editor.saveRange');
+ this.showImageDialog().then(function (data) {
+ // [workaround] hide dialog before restore range for IE range focus
+ _this.ui.hideDialog(_this.$dialog);
+ _this.context.invoke('editor.restoreRange');
+ if (typeof data === 'string') {
+ _this.context.invoke('editor.insertImage', data);
+ }
+ else {
+ _this.context.invoke('editor.insertImagesOrCallback', data);
+ }
+ }).fail(function () {
+ _this.context.invoke('editor.restoreRange');
+ });
+ };
+ /**
+ * show image dialog
+ *
+ * @param {jQuery} $dialog
+ * @return {Promise}
+ */
+ ImageDialog.prototype.showImageDialog = function () {
+ var _this = this;
+ return $$1.Deferred(function (deferred) {
+ var $imageInput = _this.$dialog.find('.note-image-input');
+ var $imageUrl = _this.$dialog.find('.note-image-url');
+ var $imageBtn = _this.$dialog.find('.note-image-btn');
+ _this.ui.onDialogShown(_this.$dialog, function () {
+ _this.context.triggerEvent('dialog.shown');
+ // Cloning imageInput to clear element.
+ $imageInput.replaceWith($imageInput.clone().on('change', function (event) {
+ deferred.resolve(event.target.files || event.target.value);
+ }).val(''));
+ $imageBtn.click(function (event) {
+ event.preventDefault();
+ deferred.resolve($imageUrl.val());
+ });
+ $imageUrl.on('keyup paste', function () {
+ var url = $imageUrl.val();
+ _this.ui.toggleBtn($imageBtn, url);
+ }).val('');
+ if (!env.isSupportTouch) {
+ $imageUrl.trigger('focus');
+ }
+ _this.bindEnterKey($imageUrl, $imageBtn);
+ });
+ _this.ui.onDialogHidden(_this.$dialog, function () {
+ $imageInput.off('change');
+ $imageUrl.off('keyup paste keypress');
+ $imageBtn.off('click');
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+ _this.ui.showDialog(_this.$dialog);
+ });
+ };
+ return ImageDialog;
+}());
+
+/**
+ * Image popover module
+ * mouse events that show/hide popover will be handled by Handle.js.
+ * Handle.js will receive the events and invoke 'imagePopover.update'.
+ */
+var ImagePopover = /** @class */ (function () {
+ function ImagePopover(context) {
+ var _this = this;
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.editable = context.layoutInfo.editable[0];
+ this.options = context.options;
+ this.events = {
+ 'summernote.disable': function () {
+ _this.hide();
+ }
+ };
+ }
+ ImagePopover.prototype.shouldInitialize = function () {
+ return !lists.isEmpty(this.options.popover.image);
+ };
+ ImagePopover.prototype.initialize = function () {
+ this.$popover = this.ui.popover({
+ className: 'note-image-popover'
+ }).render().appendTo(this.options.container);
+ var $content = this.$popover.find('.popover-content,.note-popover-content');
+ this.context.invoke('buttons.build', $content, this.options.popover.image);
+ };
+ ImagePopover.prototype.destroy = function () {
+ this.$popover.remove();
+ };
+ ImagePopover.prototype.update = function (target) {
+ if (dom.isImg(target)) {
+ var pos = dom.posFromPlaceholder(target);
+ var posEditor = dom.posFromPlaceholder(this.editable);
+ this.$popover.css({
+ display: 'block',
+ left: this.options.popatmouse ? event.pageX - 20 : pos.left,
+ top: this.options.popatmouse ? event.pageY : Math.min(pos.top, posEditor.top)
+ });
+ }
+ else {
+ this.hide();
+ }
+ };
+ ImagePopover.prototype.hide = function () {
+ this.$popover.hide();
+ };
+ return ImagePopover;
+}());
+
+var TablePopover = /** @class */ (function () {
+ function TablePopover(context) {
+ var _this = this;
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.options = context.options;
+ this.events = {
+ 'summernote.mousedown': function (we, e) {
+ _this.update(e.target);
+ },
+ 'summernote.keyup summernote.scroll summernote.change': function () {
+ _this.update();
+ },
+ 'summernote.disable': function () {
+ _this.hide();
+ }
+ };
+ }
+ TablePopover.prototype.shouldInitialize = function () {
+ return !lists.isEmpty(this.options.popover.table);
+ };
+ TablePopover.prototype.initialize = function () {
+ this.$popover = this.ui.popover({
+ className: 'note-table-popover'
+ }).render().appendTo(this.options.container);
+ var $content = this.$popover.find('.popover-content,.note-popover-content');
+ this.context.invoke('buttons.build', $content, this.options.popover.table);
+ // [workaround] Disable Firefox's default table editor
+ if (env.isFF) {
+ document.execCommand('enableInlineTableEditing', false, false);
+ }
+ };
+ TablePopover.prototype.destroy = function () {
+ this.$popover.remove();
+ };
+ TablePopover.prototype.update = function (target) {
+ if (this.context.isDisabled()) {
+ return false;
+ }
+ var isCell = dom.isCell(target);
+ if (isCell) {
+ var pos = dom.posFromPlaceholder(target);
+ this.$popover.css({
+ display: 'block',
+ left: pos.left,
+ top: pos.top
+ });
+ }
+ else {
+ this.hide();
+ }
+ return isCell;
+ };
+ TablePopover.prototype.hide = function () {
+ this.$popover.hide();
+ };
+ return TablePopover;
+}());
+
+var VideoDialog = /** @class */ (function () {
+ function VideoDialog(context) {
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.$body = $$1(document.body);
+ this.$editor = context.layoutInfo.editor;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ }
+ VideoDialog.prototype.initialize = function () {
+ var $container = this.options.dialogsInBody ? this.$body : this.$editor;
+ var body = [
+ '<div class="form-group note-form-group row-fluid">',
+ "<label class=\"note-form-label\">" + this.lang.video.url + " <small class=\"text-muted\">" + this.lang.video.providers + "</small></label>",
+ '<input class="note-video-url form-control note-form-control note-input" type="text" />',
+ '</div>'
+ ].join('');
+ var buttonClass = 'btn btn-primary note-btn note-btn-primary note-video-btn';
+ var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.video.insert + "</button>";
+ this.$dialog = this.ui.dialog({
+ title: this.lang.video.insert,
+ fade: this.options.dialogsFade,
+ body: body,
+ footer: footer
+ }).render().appendTo($container);
+ };
+ VideoDialog.prototype.destroy = function () {
+ this.ui.hideDialog(this.$dialog);
+ this.$dialog.remove();
+ };
+ VideoDialog.prototype.bindEnterKey = function ($input, $btn) {
+ $input.on('keypress', function (event) {
+ if (event.keyCode === key.code.ENTER) {
+ event.preventDefault();
+ $btn.trigger('click');
+ }
+ });
+ };
+ VideoDialog.prototype.createVideoNode = function (url) {
+ // video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm)
+ var ytRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
+ var ytMatch = url.match(ytRegExp);
+ var igRegExp = /(?:www\.|\/\/)instagram\.com\/p\/(.[a-zA-Z0-9_-]*)/;
+ var igMatch = url.match(igRegExp);
+ var vRegExp = /\/\/vine\.co\/v\/([a-zA-Z0-9]+)/;
+ var vMatch = url.match(vRegExp);
+ var vimRegExp = /\/\/(player\.)?vimeo\.com\/([a-z]*\/)*(\d+)[?]?.*/;
+ var vimMatch = url.match(vimRegExp);
+ var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
+ var dmMatch = url.match(dmRegExp);
+ var youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/;
+ var youkuMatch = url.match(youkuRegExp);
+ var qqRegExp = /\/\/v\.qq\.com.*?vid=(.+)/;
+ var qqMatch = url.match(qqRegExp);
+ var qqRegExp2 = /\/\/v\.qq\.com\/x?\/?(page|cover).*?\/([^\/]+)\.html\??.*/;
+ var qqMatch2 = url.match(qqRegExp2);
+ var mp4RegExp = /^.+.(mp4|m4v)$/;
+ var mp4Match = url.match(mp4RegExp);
+ var oggRegExp = /^.+.(ogg|ogv)$/;
+ var oggMatch = url.match(oggRegExp);
+ var webmRegExp = /^.+.(webm)$/;
+ var webmMatch = url.match(webmRegExp);
+ var $video;
+ if (ytMatch && ytMatch[1].length === 11) {
+ var youtubeId = ytMatch[1];
+ $video = $$1('<iframe>')
+ .attr('frameborder', 0)
+ .attr('src', '//www.youtube.com/embed/' + youtubeId)
+ .attr('width', '640').attr('height', '360');
+ }
+ else if (igMatch && igMatch[0].length) {
+ $video = $$1('<iframe>')
+ .attr('frameborder', 0)
+ .attr('src', 'https://instagram.com/p/' + igMatch[1] + '/embed/')
+ .attr('width', '612').attr('height', '710')
+ .attr('scrolling', 'no')
+ .attr('allowtransparency', 'true');
+ }
+ else if (vMatch && vMatch[0].length) {
+ $video = $$1('<iframe>')
+ .attr('frameborder', 0)
+ .attr('src', vMatch[0] + '/embed/simple')
+ .attr('width', '600').attr('height', '600')
+ .attr('class', 'vine-embed');
+ }
+ else if (vimMatch && vimMatch[3].length) {
+ $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
+ .attr('frameborder', 0)
+ .attr('src', '//player.vimeo.com/video/' + vimMatch[3])
+ .attr('width', '640').attr('height', '360');
+ }
+ else if (dmMatch && dmMatch[2].length) {
+ $video = $$1('<iframe>')
+ .attr('frameborder', 0)
+ .attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
+ .attr('width', '640').attr('height', '360');
+ }
+ else if (youkuMatch && youkuMatch[1].length) {
+ $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
+ .attr('frameborder', 0)
+ .attr('height', '498')
+ .attr('width', '510')
+ .attr('src', '//player.youku.com/embed/' + youkuMatch[1]);
+ }
+ else if ((qqMatch && qqMatch[1].length) || (qqMatch2 && qqMatch2[2].length)) {
+ var vid = ((qqMatch && qqMatch[1].length) ? qqMatch[1] : qqMatch2[2]);
+ $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
+ .attr('frameborder', 0)
+ .attr('height', '310')
+ .attr('width', '500')
+ .attr('src', 'http://v.qq.com/iframe/player.html?vid=' + vid + '&amp;auto=0');
+ }
+ else if (mp4Match || oggMatch || webmMatch) {
+ $video = $$1('<video controls>')
+ .attr('src', url)
+ .attr('width', '640').attr('height', '360');
+ }
+ else {
+ // this is not a known video link. Now what, Cat? Now what?
+ return false;
+ }
+ $video.addClass('note-video-clip');
+ return $video[0];
+ };
+ VideoDialog.prototype.show = function () {
+ var _this = this;
+ var text = this.context.invoke('editor.getSelectedText');
+ this.context.invoke('editor.saveRange');
+ this.showVideoDialog(text).then(function (url) {
+ // [workaround] hide dialog before restore range for IE range focus
+ _this.ui.hideDialog(_this.$dialog);
+ _this.context.invoke('editor.restoreRange');
+ // build node
+ var $node = _this.createVideoNode(url);
+ if ($node) {
+ // insert video node
+ _this.context.invoke('editor.insertNode', $node);
+ }
+ }).fail(function () {
+ _this.context.invoke('editor.restoreRange');
+ });
+ };
+ /**
+ * show image dialog
+ *
+ * @param {jQuery} $dialog
+ * @return {Promise}
+ */
+ VideoDialog.prototype.showVideoDialog = function (text) {
+ var _this = this;
+ return $$1.Deferred(function (deferred) {
+ var $videoUrl = _this.$dialog.find('.note-video-url');
+ var $videoBtn = _this.$dialog.find('.note-video-btn');
+ _this.ui.onDialogShown(_this.$dialog, function () {
+ _this.context.triggerEvent('dialog.shown');
+ $videoUrl.val(text).on('input', function () {
+ _this.ui.toggleBtn($videoBtn, $videoUrl.val());
+ });
+ if (!env.isSupportTouch) {
+ $videoUrl.trigger('focus');
+ }
+ $videoBtn.click(function (event) {
+ event.preventDefault();
+ deferred.resolve($videoUrl.val());
+ });
+ _this.bindEnterKey($videoUrl, $videoBtn);
+ });
+ _this.ui.onDialogHidden(_this.$dialog, function () {
+ $videoUrl.off('input');
+ $videoBtn.off('click');
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+ _this.ui.showDialog(_this.$dialog);
+ });
+ };
+ return VideoDialog;
+}());
+
+var HelpDialog = /** @class */ (function () {
+ function HelpDialog(context) {
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.$body = $$1(document.body);
+ this.$editor = context.layoutInfo.editor;
+ this.options = context.options;
+ this.lang = this.options.langInfo;
+ }
+ HelpDialog.prototype.initialize = function () {
+ var $container = this.options.dialogsInBody ? this.$body : this.$editor;
+ var body = [
+ '<p class="text-center">',
+ '<a href="http://summernote.org/" target="_blank">Summernote 0.8.9</a> · ',
+ '<a href="https://github.com/summernote/summernote" target="_blank">Project</a> · ',
+ '<a href="https://github.com/summernote/summernote/issues" target="_blank">Issues</a>',
+ '</p>'
+ ].join('');
+ this.$dialog = this.ui.dialog({
+ title: this.lang.options.help,
+ fade: this.options.dialogsFade,
+ body: this.createShortcutList(),
+ footer: body,
+ callback: function ($node) {
+ $node.find('.modal-body,.note-modal-body').css({
+ 'max-height': 300,
+ 'overflow': 'scroll'
+ });
+ }
+ }).render().appendTo($container);
+ };
+ HelpDialog.prototype.destroy = function () {
+ this.ui.hideDialog(this.$dialog);
+ this.$dialog.remove();
+ };
+ HelpDialog.prototype.createShortcutList = function () {
+ var _this = this;
+ var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
+ return Object.keys(keyMap).map(function (key) {
+ var command = keyMap[key];
+ var $row = $$1('<div><div class="help-list-item"/></div>');
+ $row.append($$1('<label><kbd>' + key + '</kdb></label>').css({
+ 'width': 180,
+ 'margin-right': 10
+ })).append($$1('<span/>').html(_this.context.memo('help.' + command) || command));
+ return $row.html();
+ }).join('');
+ };
+ /**
+ * show help dialog
+ *
+ * @return {Promise}
+ */
+ HelpDialog.prototype.showHelpDialog = function () {
+ var _this = this;
+ return $$1.Deferred(function (deferred) {
+ _this.ui.onDialogShown(_this.$dialog, function () {
+ _this.context.triggerEvent('dialog.shown');
+ deferred.resolve();
+ });
+ _this.ui.showDialog(_this.$dialog);
+ }).promise();
+ };
+ HelpDialog.prototype.show = function () {
+ var _this = this;
+ this.context.invoke('editor.saveRange');
+ this.showHelpDialog().then(function () {
+ _this.context.invoke('editor.restoreRange');
+ });
+ };
+ return HelpDialog;
+}());
+
+var AIR_MODE_POPOVER_X_OFFSET = 20;
+var AirPopover = /** @class */ (function () {
+ function AirPopover(context) {
+ var _this = this;
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.options = context.options;
+ this.events = {
+ 'summernote.keyup summernote.mouseup summernote.scroll': function () {
+ _this.update();
+ },
+ 'summernote.disable summernote.change summernote.dialog.shown': function () {
+ _this.hide();
+ },
+ 'summernote.focusout': function (we, e) {
+ // [workaround] Firefox doesn't support relatedTarget on focusout
+ // - Ignore hide action on focus out in FF.
+ if (env.isFF) {
+ return;
+ }
+ if (!e.relatedTarget || !dom.ancestor(e.relatedTarget, func.eq(_this.$popover[0]))) {
+ _this.hide();
+ }
+ }
+ };
+ }
+ AirPopover.prototype.shouldInitialize = function () {
+ return this.options.airMode && !lists.isEmpty(this.options.popover.air);
+ };
+ AirPopover.prototype.initialize = function () {
+ this.$popover = this.ui.popover({
+ className: 'note-air-popover'
+ }).render().appendTo(this.options.container);
+ var $content = this.$popover.find('.popover-content');
+ this.context.invoke('buttons.build', $content, this.options.popover.air);
+ };
+ AirPopover.prototype.destroy = function () {
+ this.$popover.remove();
+ };
+ AirPopover.prototype.update = function () {
+ var styleInfo = this.context.invoke('editor.currentStyle');
+ if (styleInfo.range && !styleInfo.range.isCollapsed()) {
+ var rect = lists.last(styleInfo.range.getClientRects());
+ if (rect) {
+ var bnd = func.rect2bnd(rect);
+ this.$popover.css({
+ display: 'block',
+ left: Math.max(bnd.left + bnd.width / 2, 0) - AIR_MODE_POPOVER_X_OFFSET,
+ top: bnd.top + bnd.height
+ });
+ this.context.invoke('buttons.updateCurrentStyle', this.$popover);
+ }
+ }
+ else {
+ this.hide();
+ }
+ };
+ AirPopover.prototype.hide = function () {
+ this.$popover.hide();
+ };
+ return AirPopover;
+}());
+
+var POPOVER_DIST = 5;
+var HintPopover = /** @class */ (function () {
+ function HintPopover(context) {
+ var _this = this;
+ this.context = context;
+ this.ui = $$1.summernote.ui;
+ this.$editable = context.layoutInfo.editable;
+ this.options = context.options;
+ this.hint = this.options.hint || [];
+ this.direction = this.options.hintDirection || 'bottom';
+ this.hints = $$1.isArray(this.hint) ? this.hint : [this.hint];
+ this.events = {
+ 'summernote.keyup': function (we, e) {
+ if (!e.isDefaultPrevented()) {
+ _this.handleKeyup(e);
+ }
+ },
+ 'summernote.keydown': function (we, e) {
+ _this.handleKeydown(e);
+ },
+ 'summernote.disable summernote.dialog.shown': function () {
+ _this.hide();
+ }
+ };
+ }
+ HintPopover.prototype.shouldInitialize = function () {
+ return this.hints.length > 0;
+ };
+ HintPopover.prototype.initialize = function () {
+ var _this = this;
+ this.lastWordRange = null;
+ this.$popover = this.ui.popover({
+ className: 'note-hint-popover',
+ hideArrow: true,
+ direction: ''
+ }).render().appendTo(this.options.container);
+ this.$popover.hide();
+ this.$content = this.$popover.find('.popover-content,.note-popover-content');
+ this.$content.on('click', '.note-hint-item', function (e) {
+ _this.$content.find('.active').removeClass('active');
+ $$1(e.currentTarget).addClass('active');
+ _this.replace();
+ });
+ };
+ HintPopover.prototype.destroy = function () {
+ this.$popover.remove();
+ };
+ HintPopover.prototype.selectItem = function ($item) {
+ this.$content.find('.active').removeClass('active');
+ $item.addClass('active');
+ this.$content[0].scrollTop = $item[0].offsetTop - (this.$content.innerHeight() / 2);
+ };
+ HintPopover.prototype.moveDown = function () {
+ var $current = this.$content.find('.note-hint-item.active');
+ var $next = $current.next();
+ if ($next.length) {
+ this.selectItem($next);
+ }
+ else {
+ var $nextGroup = $current.parent().next();
+ if (!$nextGroup.length) {
+ $nextGroup = this.$content.find('.note-hint-group').first();
+ }
+ this.selectItem($nextGroup.find('.note-hint-item').first());
+ }
+ };
+ HintPopover.prototype.moveUp = function () {
+ var $current = this.$content.find('.note-hint-item.active');
+ var $prev = $current.prev();
+ if ($prev.length) {
+ this.selectItem($prev);
+ }
+ else {
+ var $prevGroup = $current.parent().prev();
+ if (!$prevGroup.length) {
+ $prevGroup = this.$content.find('.note-hint-group').last();
+ }
+ this.selectItem($prevGroup.find('.note-hint-item').last());
+ }
+ };
+ HintPopover.prototype.replace = function () {
+ var $item = this.$content.find('.note-hint-item.active');
+ if ($item.length) {
+ var node = this.nodeFromItem($item);
+ // XXX: consider to move codes to editor for recording redo/undo.
+ this.lastWordRange.insertNode(node);
+ range.createFromNode(node).collapse().select();
+ this.lastWordRange = null;
+ this.hide();
+ this.context.triggerEvent('change', this.$editable.html(), this.$editable[0]);
+ this.context.invoke('editor.focus');
+ }
+ };
+ HintPopover.prototype.nodeFromItem = function ($item) {
+ var hint = this.hints[$item.data('index')];
+ var item = $item.data('item');
+ var node = hint.content ? hint.content(item) : item;
+ if (typeof node === 'string') {
+ node = dom.createText(node);
+ }
+ return node;
+ };
+ HintPopover.prototype.createItemTemplates = function (hintIdx, items) {
+ var hint = this.hints[hintIdx];
+ return items.map(function (item, idx) {
+ var $item = $$1('<div class="note-hint-item"/>');
+ $item.append(hint.template ? hint.template(item) : item + '');
+ $item.data({
+ 'index': hintIdx,
+ 'item': item
+ });
+ return $item;
+ });
+ };
+ HintPopover.prototype.handleKeydown = function (e) {
+ if (!this.$popover.is(':visible')) {
+ return;
+ }
+ if (e.keyCode === key.code.ENTER) {
+ e.preventDefault();
+ this.replace();
+ }
+ else if (e.keyCode === key.code.UP) {
+ e.preventDefault();
+ this.moveUp();
+ }
+ else if (e.keyCode === key.code.DOWN) {
+ e.preventDefault();
+ this.moveDown();
+ }
+ };
+ HintPopover.prototype.searchKeyword = function (index, keyword, callback) {
+ var hint = this.hints[index];
+ if (hint && hint.match.test(keyword) && hint.search) {
+ var matches = hint.match.exec(keyword);
+ hint.search(matches[1], callback);
+ }
+ else {
+ callback();
+ }
+ };
+ HintPopover.prototype.createGroup = function (idx, keyword) {
+ var _this = this;
+ var $group = $$1('<div class="note-hint-group note-hint-group-' + idx + '"/>');
+ this.searchKeyword(idx, keyword, function (items) {
+ items = items || [];
+ if (items.length) {
+ $group.html(_this.createItemTemplates(idx, items));
+ _this.show();
+ }
+ });
+ return $group;
+ };
+ HintPopover.prototype.handleKeyup = function (e) {
+ var _this = this;
+ if (!lists.contains([key.code.ENTER, key.code.UP, key.code.DOWN], e.keyCode)) {
+ var wordRange = this.context.invoke('editor.createRange').getWordRange();
+ var keyword_1 = wordRange.toString();
+ if (this.hints.length && keyword_1) {
+ this.$content.empty();
+ var bnd = func.rect2bnd(lists.last(wordRange.getClientRects()));
+ if (bnd) {
+ this.$popover.hide();
+ this.lastWordRange = wordRange;
+ this.hints.forEach(function (hint, idx) {
+ if (hint.match.test(keyword_1)) {
+ _this.createGroup(idx, keyword_1).appendTo(_this.$content);
+ }
+ });
+ // select first .note-hint-item
+ this.$content.find('.note-hint-item:first').addClass('active');
+ // set position for popover after group is created
+ if (this.direction === 'top') {
+ this.$popover.css({
+ left: bnd.left,
+ top: bnd.top - this.$popover.outerHeight() - POPOVER_DIST
+ });
+ }
+ else {
+ this.$popover.css({
+ left: bnd.left,
+ top: bnd.top + bnd.height + POPOVER_DIST
+ });
+ }
+ }
+ }
+ else {
+ this.hide();
+ }
+ }
+ };
+ HintPopover.prototype.show = function () {
+ this.$popover.show();
+ };
+ HintPopover.prototype.hide = function () {
+ this.$popover.hide();
+ };
+ return HintPopover;
+}());
+
+var Context = /** @class */ (function () {
+ /**
+ * @param {jQuery} $note
+ * @param {Object} options
+ */
+ function Context($note, options) {
+ this.ui = $$1.summernote.ui;
+ this.$note = $note;
+ this.memos = {};
+ this.modules = {};
+ this.layoutInfo = {};
+ this.options = options;
+ this.initialize();
+ }
+ /**
+ * create layout and initialize modules and other resources
+ */
+ Context.prototype.initialize = function () {
+ this.layoutInfo = this.ui.createLayout(this.$note, this.options);
+ this._initialize();
+ this.$note.hide();
+ return this;
+ };
+ /**
+ * destroy modules and other resources and remove layout
+ */
+ Context.prototype.destroy = function () {
+ this._destroy();
+ this.$note.removeData('summernote');
+ this.ui.removeLayout(this.$note, this.layoutInfo);
+ };
+ /**
+ * destory modules and other resources and initialize it again
+ */
+ Context.prototype.reset = function () {
+ var disabled = this.isDisabled();
+ this.code(dom.emptyPara);
+ this._destroy();
+ this._initialize();
+ if (disabled) {
+ this.disable();
+ }
+ };
+ Context.prototype._initialize = function () {
+ var _this = this;
+ // add optional buttons
+ var buttons = $$1.extend({}, this.options.buttons);
+ Object.keys(buttons).forEach(function (key) {
+ _this.memo('button.' + key, buttons[key]);
+ });
+ var modules = $$1.extend({}, this.options.modules, $$1.summernote.plugins || {});
+ // add and initialize modules
+ Object.keys(modules).forEach(function (key) {
+ _this.module(key, modules[key], true);
+ });
+ Object.keys(this.modules).forEach(function (key) {
+ _this.initializeModule(key);
+ });
+ };
+ Context.prototype._destroy = function () {
+ var _this = this;
+ // destroy modules with reversed order
+ Object.keys(this.modules).reverse().forEach(function (key) {
+ _this.removeModule(key);
+ });
+ Object.keys(this.memos).forEach(function (key) {
+ _this.removeMemo(key);
+ });
+ // trigger custom onDestroy callback
+ this.triggerEvent('destroy', this);
+ };
+ Context.prototype.code = function (html) {
+ var isActivated = this.invoke('codeview.isActivated');
+ if (html === undefined) {
+ this.invoke('codeview.sync');
+ return isActivated ? this.layoutInfo.codable.val() : this.layoutInfo.editable.html();
+ }
+ else {
+ if (isActivated) {
+ this.layoutInfo.codable.val(html);
+ }
+ else {
+ this.layoutInfo.editable.html(html);
+ }
+ this.$note.val(html);
+ this.triggerEvent('change', html);
+ }
+ };
+ Context.prototype.isDisabled = function () {
+ return this.layoutInfo.editable.attr('contenteditable') === 'false';
+ };
+ Context.prototype.enable = function () {
+ this.layoutInfo.editable.attr('contenteditable', true);
+ this.invoke('toolbar.activate', true);
+ this.triggerEvent('disable', false);
+ };
+ Context.prototype.disable = function () {
+ // close codeview if codeview is opend
+ if (this.invoke('codeview.isActivated')) {
+ this.invoke('codeview.deactivate');
+ }
+ this.layoutInfo.editable.attr('contenteditable', false);
+ this.invoke('toolbar.deactivate', true);
+ this.triggerEvent('disable', true);
+ };
+ Context.prototype.triggerEvent = function () {
+ var namespace = lists.head(arguments);
+ var args = lists.tail(lists.from(arguments));
+ var callback = this.options.callbacks[func.namespaceToCamel(namespace, 'on')];
+ if (callback) {
+ callback.apply(this.$note[0], args);
+ }
+ this.$note.trigger('summernote.' + namespace, args);
+ };
+ Context.prototype.initializeModule = function (key) {
+ var module = this.modules[key];
+ module.shouldInitialize = module.shouldInitialize || func.ok;
+ if (!module.shouldInitialize()) {
+ return;
+ }
+ // initialize module
+ if (module.initialize) {
+ module.initialize();
+ }
+ // attach events
+ if (module.events) {
+ dom.attachEvents(this.$note, module.events);
+ }
+ };
+ Context.prototype.module = function (key, ModuleClass, withoutIntialize) {
+ if (arguments.length === 1) {
+ return this.modules[key];
+ }
+ this.modules[key] = new ModuleClass(this);
+ if (!withoutIntialize) {
+ this.initializeModule(key);
+ }
+ };
+ Context.prototype.removeModule = function (key) {
+ var module = this.modules[key];
+ if (module.shouldInitialize()) {
+ if (module.events) {
+ dom.detachEvents(this.$note, module.events);
+ }
+ if (module.destroy) {
+ module.destroy();
+ }
+ }
+ delete this.modules[key];
+ };
+ Context.prototype.memo = function (key, obj) {
+ if (arguments.length === 1) {
+ return this.memos[key];
+ }
+ this.memos[key] = obj;
+ };
+ Context.prototype.removeMemo = function (key) {
+ if (this.memos[key] && this.memos[key].destroy) {
+ this.memos[key].destroy();
+ }
+ delete this.memos[key];
+ };
+ /**
+ * Some buttons need to change their visual style immediately once they get pressed
+ */
+ Context.prototype.createInvokeHandlerAndUpdateState = function (namespace, value) {
+ var _this = this;
+ return function (event) {
+ _this.createInvokeHandler(namespace, value)(event);
+ _this.invoke('buttons.updateCurrentStyle');
+ };
+ };
+ Context.prototype.createInvokeHandler = function (namespace, value) {
+ var _this = this;
+ return function (event) {
+ event.preventDefault();
+ var $target = $$1(event.target);
+ _this.invoke(namespace, value || $target.closest('[data-value]').data('value'), $target);
+ };
+ };
+ Context.prototype.invoke = function () {
+ var namespace = lists.head(arguments);
+ var args = lists.tail(lists.from(arguments));
+ var splits = namespace.split('.');
+ var hasSeparator = splits.length > 1;
+ var moduleName = hasSeparator && lists.head(splits);
+ var methodName = hasSeparator ? lists.last(splits) : lists.head(splits);
+ var module = this.modules[moduleName || 'editor'];
+ if (!moduleName && this[methodName]) {
+ return this[methodName].apply(this, args);
+ }
+ else if (module && module[methodName] && module.shouldInitialize()) {
+ return module[methodName].apply(module, args);
+ }
+ };
+ return Context;
+}());
+
+$$1.fn.extend({
+ /**
+ * Summernote API
+ *
+ * @param {Object|String}
+ * @return {this}
+ */
+ summernote: function () {
+ var type = $$1.type(lists.head(arguments));
+ var isExternalAPICalled = type === 'string';
+ var hasInitOptions = type === 'object';
+ var options = $$1.extend({}, $$1.summernote.options, hasInitOptions ? lists.head(arguments) : {});
+ // Update options
+ options.langInfo = $$1.extend(true, {}, $$1.summernote.lang['en-US'], $$1.summernote.lang[options.lang]);
+ options.icons = $$1.extend(true, {}, $$1.summernote.options.icons, options.icons);
+ options.tooltip = options.tooltip === 'auto' ? !env.isSupportTouch : options.tooltip;
+ this.each(function (idx, note) {
+ var $note = $$1(note);
+ if (!$note.data('summernote')) {
+ var context = new Context($note, options);
+ $note.data('summernote', context);
+ $note.data('summernote').triggerEvent('init', context.layoutInfo);
+ }
+ });
+ var $note = this.first();
+ if ($note.length) {
+ var context = $note.data('summernote');
+ if (isExternalAPICalled) {
+ return context.invoke.apply(context, lists.from(arguments));
+ }
+ else if (options.focus) {
+ context.invoke('editor.focus');
+ }
+ }
+ return this;
+ }
+});
+
+$$1.summernote = $$1.extend($$1.summernote, {
+ version: '0.8.9',
+ ui: ui,
+ dom: dom,
+ range: range,
+
+ plugins: {},
+
+ options: {
+ modules: {
+ 'editor': Editor,
+ 'clipboard': Clipboard,
+ 'dropzone': Dropzone,
+ 'codeview': CodeView,
+ 'statusbar': Statusbar,
+ 'fullscreen': Fullscreen,
+ 'handle': Handle,
+ // FIXME: HintPopover must be front of autolink
+ // - Script error about range when Enter key is pressed on hint popover
+ 'hintPopover': HintPopover,
+ 'autoLink': AutoLink,
+ 'autoSync': AutoSync,
+ 'placeholder': Placeholder,
+ 'buttons': Buttons,
+ 'toolbar': Toolbar,
+ 'linkDialog': LinkDialog,
+ 'linkPopover': LinkPopover,
+ 'imageDialog': ImageDialog,
+ 'imagePopover': ImagePopover,
+ 'tablePopover': TablePopover,
+ 'videoDialog': VideoDialog,
+ 'helpDialog': HelpDialog,
+ 'airPopover': AirPopover
+ },
+ buttons: {},
+ lang: 'en-US',
+ followingToolbar: true,
+ otherStaticBar: '',
+ // toolbar
+ toolbar: [
+ ['style', ['style']],
+ ['font', ['bold', 'underline', 'clear']],
+ ['fontname', ['fontname']],
+ ['color', ['color']],
+ ['para', ['ul', 'ol', 'paragraph']],
+ ['table', ['table']],
+ ['insert', ['link', 'picture', 'video']],
+ ['view', ['fullscreen', 'codeview', 'help']]
+ ],
+ // popover
+ popatmouse: true,
+ popover: {
+ image: [
+ ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
+ ['float', ['floatLeft', 'floatRight', 'floatNone']],
+ ['remove', ['removeMedia']]
+ ],
+ link: [
+ ['link', ['linkDialogShow', 'unlink']]
+ ],
+ table: [
+ ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
+ ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
+ ],
+ air: [
+ ['color', ['color']],
+ ['font', ['bold', 'underline', 'clear']],
+ ['para', ['ul', 'paragraph']],
+ ['table', ['table']],
+ ['insert', ['link', 'picture']]
+ ]
+ },
+ // air mode: inline editor
+ airMode: false,
+ width: null,
+ height: null,
+ linkTargetBlank: true,
+ focus: false,
+ tabSize: 4,
+ styleWithSpan: true,
+ shortcuts: true,
+ textareaAutoSync: true,
+ hintDirection: 'bottom',
+ tooltip: 'auto',
+ container: 'body',
+ maxTextLength: 0,
+ styleTags: [
+ 'p',
+ { title: 'Blockquote', tag: 'blockquote', className: 'blockquote', value: 'blockquote' },
+ 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
+ ],
+ fontNames: [
+ 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
+ 'Helvetica Neue', 'Helvetica', 'Impact', 'Lucida Grande',
+ 'Tahoma', 'Times New Roman', 'Verdana'
+ ],
+ fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
+ // pallete colors(n x n)
+ colors: [
+ ['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
+ ['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
+ ['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
+ ['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
+ ['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
+ ['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
+ ['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
+ ['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
+ ],
+ lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
+ tableClassName: 'table table-bordered',
+ insertTableMaxSize: {
+ col: 10,
+ row: 10
+ },
+ dialogsInBody: false,
+ dialogsFade: false,
+ maximumImageFileSize: null,
+ callbacks: {
+ onInit: null,
+ onFocus: null,
+ onBlur: null,
+ onBlurCodeview: null,
+ onEnter: null,
+ onKeyup: null,
+ onKeydown: null,
+ onImageUpload: null,
+ onImageUploadError: null
+ },
+ codemirror: {
+ mode: 'text/html',
+ htmlMode: true,
+ lineNumbers: true
+ },
+ keyMap: {
+ pc: {
+ 'ENTER': 'insertParagraph',
+ 'CTRL+Z': 'undo',
+ 'CTRL+Y': 'redo',
+ 'TAB': 'tab',
+ 'SHIFT+TAB': 'untab',
+ 'CTRL+B': 'bold',
+ 'CTRL+I': 'italic',
+ 'CTRL+U': 'underline',
+ 'CTRL+SHIFT+S': 'strikethrough',
+ 'CTRL+BACKSLASH': 'removeFormat',
+ 'CTRL+SHIFT+L': 'justifyLeft',
+ 'CTRL+SHIFT+E': 'justifyCenter',
+ 'CTRL+SHIFT+R': 'justifyRight',
+ 'CTRL+SHIFT+J': 'justifyFull',
+ 'CTRL+SHIFT+NUM7': 'insertUnorderedList',
+ 'CTRL+SHIFT+NUM8': 'insertOrderedList',
+ 'CTRL+LEFTBRACKET': 'outdent',
+ 'CTRL+RIGHTBRACKET': 'indent',
+ 'CTRL+NUM0': 'formatPara',
+ 'CTRL+NUM1': 'formatH1',
+ 'CTRL+NUM2': 'formatH2',
+ 'CTRL+NUM3': 'formatH3',
+ 'CTRL+NUM4': 'formatH4',
+ 'CTRL+NUM5': 'formatH5',
+ 'CTRL+NUM6': 'formatH6',
+ 'CTRL+ENTER': 'insertHorizontalRule',
+ 'CTRL+K': 'linkDialog.show'
+ },
+ mac: {
+ 'ENTER': 'insertParagraph',
+ 'CMD+Z': 'undo',
+ 'CMD+SHIFT+Z': 'redo',
+ 'TAB': 'tab',
+ 'SHIFT+TAB': 'untab',
+ 'CMD+B': 'bold',
+ 'CMD+I': 'italic',
+ 'CMD+U': 'underline',
+ 'CMD+SHIFT+S': 'strikethrough',
+ 'CMD+BACKSLASH': 'removeFormat',
+ 'CMD+SHIFT+L': 'justifyLeft',
+ 'CMD+SHIFT+E': 'justifyCenter',
+ 'CMD+SHIFT+R': 'justifyRight',
+ 'CMD+SHIFT+J': 'justifyFull',
+ 'CMD+SHIFT+NUM7': 'insertUnorderedList',
+ 'CMD+SHIFT+NUM8': 'insertOrderedList',
+ 'CMD+LEFTBRACKET': 'outdent',
+ 'CMD+RIGHTBRACKET': 'indent',
+ 'CMD+NUM0': 'formatPara',
+ 'CMD+NUM1': 'formatH1',
+ 'CMD+NUM2': 'formatH2',
+ 'CMD+NUM3': 'formatH3',
+ 'CMD+NUM4': 'formatH4',
+ 'CMD+NUM5': 'formatH5',
+ 'CMD+NUM6': 'formatH6',
+ 'CMD+ENTER': 'insertHorizontalRule',
+ 'CMD+K': 'linkDialog.show'
+ }
+ },
+ icons: {
+ 'align': 'note-icon-align',
+ 'alignCenter': 'note-icon-align-center',
+ 'alignJustify': 'note-icon-align-justify',
+ 'alignLeft': 'note-icon-align-left',
+ 'alignRight': 'note-icon-align-right',
+ 'rowBelow': 'note-icon-row-below',
+ 'colBefore': 'note-icon-col-before',
+ 'colAfter': 'note-icon-col-after',
+ 'rowAbove': 'note-icon-row-above',
+ 'rowRemove': 'note-icon-row-remove',
+ 'colRemove': 'note-icon-col-remove',
+ 'indent': 'note-icon-align-indent',
+ 'outdent': 'note-icon-align-outdent',
+ 'arrowsAlt': 'note-icon-arrows-alt',
+ 'bold': 'note-icon-bold',
+ 'caret': 'note-icon-caret',
+ 'circle': 'note-icon-circle',
+ 'close': 'note-icon-close',
+ 'code': 'note-icon-code',
+ 'eraser': 'note-icon-eraser',
+ 'font': 'note-icon-font',
+ 'frame': 'note-icon-frame',
+ 'italic': 'note-icon-italic',
+ 'link': 'note-icon-link',
+ 'unlink': 'note-icon-chain-broken',
+ 'magic': 'note-icon-magic',
+ 'menuCheck': 'note-icon-menu-check',
+ 'minus': 'note-icon-minus',
+ 'orderedlist': 'note-icon-orderedlist',
+ 'pencil': 'note-icon-pencil',
+ 'picture': 'note-icon-picture',
+ 'question': 'note-icon-question',
+ 'redo': 'note-icon-redo',
+ 'square': 'note-icon-square',
+ 'strikethrough': 'note-icon-strikethrough',
+ 'subscript': 'note-icon-subscript',
+ 'superscript': 'note-icon-superscript',
+ 'table': 'note-icon-table',
+ 'textHeight': 'note-icon-text-height',
+ 'trash': 'note-icon-trash',
+ 'underline': 'note-icon-underline',
+ 'undo': 'note-icon-undo',
+ 'unorderedlist': 'note-icon-unorderedlist',
+ 'video': 'note-icon-video'
+ }
+ }
+});
+
+})));
+//# sourceMappingURL=summernote-bs4.js.map