summaryrefslogtreecommitdiff
path: root/src/main/resources/static/plugins/summernote/plugin
diff options
context:
space:
mode:
authorAlisaLinUwU <alisalinuwu@gmail.com>2025-01-26 10:42:28 +0500
committerAlisaLinUwU <alisalinuwu@gmail.com>2025-01-26 10:42:28 +0500
commit0225bdb772d1334cc1aa7ab0fc3678df0864df6b (patch)
tree85a8c8e4fcf1d935fcbad54886b73410c8cb2e26 /src/main/resources/static/plugins/summernote/plugin
Initializemain
Diffstat (limited to 'src/main/resources/static/plugins/summernote/plugin')
-rw-r--r--src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.css16
-rw-r--r--src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.js291
-rw-r--r--src/main/resources/static/plugins/summernote/plugin/hello/summernote-ext-hello.js82
-rw-r--r--src/main/resources/static/plugins/summernote/plugin/specialchars/summernote-ext-specialchars.js311
4 files changed, 700 insertions, 0 deletions
diff --git a/src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.css b/src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.css
new file mode 100644
index 0000000..6232dde
--- /dev/null
+++ b/src/main/resources/static/plugins/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/src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.js b/src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.js
new file mode 100644
index 0000000..749a867
--- /dev/null
+++ b/src/main/resources/static/plugins/summernote/plugin/databasic/summernote-ext-databasic.js
@@ -0,0 +1,291 @@
+(function(factory) {
+ 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"></i>';
+
+ // 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/src/main/resources/static/plugins/summernote/plugin/hello/summernote-ext-hello.js b/src/main/resources/static/plugins/summernote/plugin/hello/summernote-ext-hello.js
new file mode 100644
index 0000000..f95d302
--- /dev/null
+++ b/src/main/resources/static/plugins/summernote/plugin/hello/summernote-ext-hello.js
@@ -0,0 +1,82 @@
+(function(factory) {
+ 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) {
+ // eslint-disable-next-line
+ console.log('summernote initialized', we, e);
+ },
+ // This will be called when user releases a key on editable.
+ 'summernote.keyup': function(we, e) {
+ // eslint-disable-next-line
+ 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/src/main/resources/static/plugins/summernote/plugin/specialchars/summernote-ext-specialchars.js b/src/main/resources/static/plugins/summernote/plugin/specialchars/summernote-ext-specialchars.js
new file mode 100644
index 0000000..d80eb19
--- /dev/null
+++ b/src/main/resources/static/plugins/summernote/plugin/specialchars/summernote-ext-specialchars.js
@@ -0,0 +1,311 @@
+(function(factory) {
+ 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 = 12;
+ 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"></i>',
+ 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></table>');
+ $.each(specialCharDataSet, function(idx, text) {
+ var $td = $('<td></td>').addClass('note-specialchar-node');
+ var $tr = (idx % COLUMN_LENGTH === 0) ? $('<tr></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();
+
+ $(document).off('keydown', keyDownEventHandler);
+
+ if (deferred.state() === 'pending') {
+ deferred.reject();
+ }
+ });
+
+ ui.showDialog(self.$dialog);
+ });
+ };
+ },
+ });
+}));