25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
8.0KB

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