summernote-ext-databasic.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. (function(factory) {
  2. /* global define */
  3. if (typeof define === 'function' && define.amd) {
  4. // AMD. Register as an anonymous module.
  5. define(['jquery'], factory);
  6. } else if (typeof module === 'object' && module.exports) {
  7. // Node/CommonJS
  8. module.exports = factory(require('jquery'));
  9. } else {
  10. // Browser globals
  11. factory(window.jQuery);
  12. }
  13. }(function($) {
  14. // pull in some summernote core functions
  15. var ui = $.summernote.ui;
  16. var dom = $.summernote.dom;
  17. // define the popover plugin
  18. var DataBasicPlugin = function(context) {
  19. var self = this;
  20. var options = context.options;
  21. var lang = options.langInfo;
  22. self.icon = '<i class="fa fa-object-group"/>';
  23. // add context menu button for dialog
  24. context.memo('button.databasic', function() {
  25. return ui.button({
  26. contents: self.icon,
  27. tooltip: lang.databasic.insert,
  28. click: context.createInvokeHandler('databasic.showDialog'),
  29. }).render();
  30. });
  31. // add popover edit button
  32. context.memo('button.databasicDialog', function() {
  33. return ui.button({
  34. contents: self.icon,
  35. tooltip: lang.databasic.edit,
  36. click: context.createInvokeHandler('databasic.showDialog'),
  37. }).render();
  38. });
  39. // add popover size buttons
  40. context.memo('button.databasicSize100', function() {
  41. return ui.button({
  42. contents: '<span class="note-fontsize-10">100%</span>',
  43. tooltip: lang.image.resizeFull,
  44. click: context.createInvokeHandler('editor.resize', '1'),
  45. }).render();
  46. });
  47. context.memo('button.databasicSize50', function() {
  48. return ui.button({
  49. contents: '<span class="note-fontsize-10">50%</span>',
  50. tooltip: lang.image.resizeHalf,
  51. click: context.createInvokeHandler('editor.resize', '0.5'),
  52. }).render();
  53. });
  54. context.memo('button.databasicSize25', function() {
  55. return ui.button({
  56. contents: '<span class="note-fontsize-10">25%</span>',
  57. tooltip: lang.image.resizeQuarter,
  58. click: context.createInvokeHandler('editor.resize', '0.25'),
  59. }).render();
  60. });
  61. self.events = {
  62. 'summernote.init': function(we, e) {
  63. // update existing containers
  64. $('data.ext-databasic', e.editable).each(function() { self.setContent($(this)); });
  65. // TODO: make this an undo snapshot...
  66. },
  67. 'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function() {
  68. self.update();
  69. },
  70. 'summernote.dialog.shown': function() {
  71. self.hidePopover();
  72. },
  73. };
  74. self.initialize = function() {
  75. // create dialog markup
  76. var $container = options.dialogsInBody ? $(document.body) : context.layoutInfo.editor;
  77. var body = '<div class="form-group row-fluid">' +
  78. '<label>' + lang.databasic.testLabel + '</label>' +
  79. '<input class="ext-databasic-test form-control" type="text" />' +
  80. '</div>';
  81. var footer = '<button href="#" class="btn btn-primary ext-databasic-save">' + lang.databasic.insert + '</button>';
  82. self.$dialog = ui.dialog({
  83. title: lang.databasic.name,
  84. fade: options.dialogsFade,
  85. body: body,
  86. footer: footer,
  87. }).render().appendTo($container);
  88. // create popover
  89. self.$popover = ui.popover({
  90. className: 'ext-databasic-popover',
  91. }).render().appendTo('body');
  92. var $content = self.$popover.find('.popover-content');
  93. context.invoke('buttons.build', $content, options.popover.databasic);
  94. };
  95. self.destroy = function() {
  96. self.$popover.remove();
  97. self.$popover = null;
  98. self.$dialog.remove();
  99. self.$dialog = null;
  100. };
  101. self.update = function() {
  102. // Prevent focusing on editable when invoke('code') is executed
  103. if (!context.invoke('editor.hasFocus')) {
  104. self.hidePopover();
  105. return;
  106. }
  107. var rng = context.invoke('editor.createRange');
  108. var visible = false;
  109. if (rng.isOnData()) {
  110. var $data = $(rng.sc).closest('data.ext-databasic');
  111. if ($data.length) {
  112. var pos = dom.posFromPlaceholder($data[0]);
  113. self.$popover.css({
  114. display: 'block',
  115. left: pos.left,
  116. top: pos.top,
  117. });
  118. // save editor target to let size buttons resize the container
  119. context.invoke('editor.saveTarget', $data[0]);
  120. visible = true;
  121. }
  122. }
  123. // hide if not visible
  124. if (!visible) {
  125. self.hidePopover();
  126. }
  127. };
  128. self.hidePopover = function() {
  129. self.$popover.hide();
  130. };
  131. // define plugin dialog
  132. self.getInfo = function() {
  133. var rng = context.invoke('editor.createRange');
  134. if (rng.isOnData()) {
  135. var $data = $(rng.sc).closest('data.ext-databasic');
  136. if ($data.length) {
  137. // Get the first node on range(for edit).
  138. return {
  139. node: $data,
  140. test: $data.attr('data-test'),
  141. };
  142. }
  143. }
  144. return {};
  145. };
  146. self.setContent = function($node) {
  147. $node.html('<p contenteditable="false">' + self.icon + ' ' + lang.databasic.name + ': ' +
  148. $node.attr('data-test') + '</p>');
  149. };
  150. self.updateNode = function(info) {
  151. self.setContent(info.node
  152. .attr('data-test', info.test));
  153. };
  154. self.createNode = function(info) {
  155. var $node = $('<data class="ext-databasic"></data>');
  156. if ($node) {
  157. // save node to info structure
  158. info.node = $node;
  159. // insert node into editor dom
  160. context.invoke('editor.insertNode', $node[0]);
  161. }
  162. return $node;
  163. };
  164. self.showDialog = function() {
  165. var info = self.getInfo();
  166. var newNode = !info.node;
  167. context.invoke('editor.saveRange');
  168. self
  169. .openDialog(info)
  170. .then(function(dialogInfo) {
  171. // [workaround] hide dialog before restore range for IE range focus
  172. ui.hideDialog(self.$dialog);
  173. context.invoke('editor.restoreRange');
  174. // insert a new node
  175. if (newNode) {
  176. self.createNode(info);
  177. }
  178. // update info with dialog info
  179. $.extend(info, dialogInfo);
  180. self.updateNode(info);
  181. })
  182. .fail(function() {
  183. context.invoke('editor.restoreRange');
  184. });
  185. };
  186. self.openDialog = function(info) {
  187. return $.Deferred(function(deferred) {
  188. var $inpTest = self.$dialog.find('.ext-databasic-test');
  189. var $saveBtn = self.$dialog.find('.ext-databasic-save');
  190. var onKeyup = function(event) {
  191. if (event.keyCode === 13) {
  192. $saveBtn.trigger('click');
  193. }
  194. };
  195. ui.onDialogShown(self.$dialog, function() {
  196. context.triggerEvent('dialog.shown');
  197. $inpTest.val(info.test).on('input', function() {
  198. ui.toggleBtn($saveBtn, $inpTest.val());
  199. }).trigger('focus').on('keyup', onKeyup);
  200. $saveBtn
  201. .text(info.node ? lang.databasic.edit : lang.databasic.insert)
  202. .click(function(event) {
  203. event.preventDefault();
  204. deferred.resolve({ test: $inpTest.val() });
  205. });
  206. // init save button
  207. ui.toggleBtn($saveBtn, $inpTest.val());
  208. });
  209. ui.onDialogHidden(self.$dialog, function() {
  210. $inpTest.off('input keyup');
  211. $saveBtn.off('click');
  212. if (deferred.state() === 'pending') {
  213. deferred.reject();
  214. }
  215. });
  216. ui.showDialog(self.$dialog);
  217. });
  218. };
  219. };
  220. // Extends summernote
  221. $.extend(true, $.summernote, {
  222. plugins: {
  223. databasic: DataBasicPlugin,
  224. },
  225. options: {
  226. popover: {
  227. databasic: [
  228. ['databasic', ['databasicDialog', 'databasicSize100', 'databasicSize50', 'databasicSize25']],
  229. ],
  230. },
  231. },
  232. // add localization texts
  233. lang: {
  234. 'en-US': {
  235. databasic: {
  236. name: 'Basic Data Container',
  237. insert: 'insert basic data container',
  238. edit: 'edit basic data container',
  239. testLabel: 'test input',
  240. },
  241. },
  242. },
  243. });
  244. }));