You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

194 line
6.1 KiB

  1. /* http://github.com/mindmup/bootstrap-wysiwyg */
  2. /*global jQuery, $, FileReader*/
  3. /*jslint browser:true*/
  4. jQuery(function ($) {
  5. 'use strict';
  6. var readFileIntoDataUrl = function (fileInfo) {
  7. var loader = $.Deferred(),
  8. fReader = new FileReader();
  9. wn.upload.upload_file(fileInfo, {
  10. from_form: 1,
  11. doctype: cur_frm.doctype,
  12. docname: cur_frm.docname
  13. }, function(fileid, filename, r) {
  14. if(!r.exc) {
  15. if(fileid)
  16. cur_frm.attachments.update_attachment(fileid, filename);
  17. loader.resolve(filename);
  18. }
  19. });
  20. return loader.promise();
  21. };
  22. $.fn.cleanHtml = function () {
  23. var html = $(this).html();
  24. html = html && html.replace(/(<br>|\s|<div><br><\/div>|&nbsp;)*$/, '');
  25. // remove custom typography (use CSS!)
  26. html = html && html.replace(/(font-family|font-size|line-height):[^;]*;/g, '');
  27. html = html && html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); });
  28. html = html && html.replace(/\s*style\s*=\s*["']\s*["']/g, '');
  29. return html;
  30. };
  31. $.fn.wysiwyg = function (userOptions) {
  32. var editor = this,
  33. selectedRange,
  34. defaultOptions = {
  35. hotKeys: {
  36. 'ctrl+b meta+b': 'bold',
  37. 'ctrl+i meta+i': 'italic',
  38. 'ctrl+u meta+u': 'underline',
  39. 'ctrl+z meta+z': 'undo',
  40. 'ctrl+y meta+y meta+shift+z': 'redo',
  41. 'ctrl+l meta+l': 'justifyleft',
  42. 'ctrl+e meta+e': 'justifycenter',
  43. 'ctrl+j meta+j': 'justifyfull',
  44. 'shift+tab': 'outdent',
  45. 'tab': 'indent'
  46. },
  47. toolbarSelector: '[data-role=editor-toolbar]',
  48. commandRole: 'edit',
  49. activeToolbarClass: 'btn-info',
  50. selectionMarker: 'edit-focus-marker',
  51. selectionColor: 'darkgrey'
  52. },
  53. options,
  54. updateToolbar = function () {
  55. if (options.activeToolbarClass) {
  56. $(options.toolbarSelector).find('.btn[data-' + options.commandRole + ']').each(function () {
  57. var command = $(this).data(options.commandRole);
  58. if (document.queryCommandState(command)) {
  59. $(this).addClass(options.activeToolbarClass);
  60. } else {
  61. $(this).removeClass(options.activeToolbarClass);
  62. }
  63. });
  64. }
  65. },
  66. execCommand = function (commandWithArgs, valueArg) {
  67. var commandArr = commandWithArgs.split(' '),
  68. command = commandArr.shift(),
  69. args = commandArr.join(' ') + (valueArg || '');
  70. document.execCommand(command, 0, args);
  71. updateToolbar();
  72. },
  73. bindHotkeys = function (hotKeys) {
  74. $.each(hotKeys, function (hotkey, command) {
  75. editor.keydown(hotkey, function (e) {
  76. if (editor.attr('contenteditable') && editor.is(':visible')) {
  77. e.preventDefault();
  78. e.stopPropagation();
  79. execCommand(command);
  80. }
  81. }).keyup(hotkey, function (e) {
  82. if (editor.attr('contenteditable') && editor.is(':visible')) {
  83. e.preventDefault();
  84. e.stopPropagation();
  85. }
  86. });
  87. });
  88. },
  89. getCurrentRange = function () {
  90. var sel = window.getSelection();
  91. if (sel.getRangeAt && sel.rangeCount) {
  92. return sel.getRangeAt(0);
  93. }
  94. },
  95. saveSelection = function () {
  96. selectedRange = getCurrentRange();
  97. },
  98. restoreSelection = function () {
  99. var selection = window.getSelection();
  100. if (selectedRange) {
  101. selection.removeAllRanges();
  102. selection.addRange(selectedRange);
  103. }
  104. },
  105. insertFiles = function (files) {
  106. editor.focus();
  107. $.each(files, function (idx, fileInfo) {
  108. if (/^image\//.test(fileInfo.type)) {
  109. $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
  110. execCommand('insertimage', dataUrl);
  111. });
  112. }
  113. });
  114. },
  115. markSelection = function (input, color) {
  116. restoreSelection();
  117. document.execCommand('hiliteColor', 0, color || 'transparent');
  118. saveSelection();
  119. input.data(options.selectionMarker, color);
  120. },
  121. bindToolbar = function (toolbar, options) {
  122. toolbar.find('a[data-' + options.commandRole + ']').click(function () {
  123. restoreSelection();
  124. editor.focus();
  125. execCommand($(this).data(options.commandRole));
  126. saveSelection();
  127. });
  128. toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
  129. toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
  130. var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
  131. this.value = '';
  132. restoreSelection();
  133. if (newValue) {
  134. editor.focus();
  135. execCommand($(this).data(options.commandRole), newValue);
  136. }
  137. saveSelection();
  138. }).on('focus', function () {
  139. var input = $(this);
  140. if (!input.data(options.selectionMarker)) {
  141. markSelection(input, options.selectionColor);
  142. input.focus();
  143. }
  144. }).on('blur', function () {
  145. var input = $(this);
  146. if (input.data(options.selectionMarker)) {
  147. markSelection(input, false);
  148. }
  149. });
  150. toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
  151. restoreSelection();
  152. if (this.type === 'file' && this.files && this.files.length > 0) {
  153. insertFiles(this.files);
  154. }
  155. saveSelection();
  156. this.value = '';
  157. });
  158. },
  159. initFileDrops = function () {
  160. editor.on('dragenter dragover', false)
  161. .on('drop', function (e) {
  162. var dataTransfer = e.originalEvent.dataTransfer;
  163. e.stopPropagation();
  164. e.preventDefault();
  165. if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
  166. insertFiles(dataTransfer.files);
  167. }
  168. });
  169. };
  170. options = $.extend({}, defaultOptions, userOptions);
  171. bindHotkeys(options.hotKeys);
  172. initFileDrops();
  173. bindToolbar($(options.toolbarSelector), options);
  174. editor.attr('contenteditable', true)
  175. .on('mouseup keyup mouseout', function () {
  176. saveSelection();
  177. updateToolbar();
  178. });
  179. $(window).bind('touchend', function (e) {
  180. var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
  181. currentRange = getCurrentRange(),
  182. clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
  183. if (!clear || isInside) {
  184. saveSelection();
  185. updateToolbar();
  186. }
  187. });
  188. return this;
  189. };
  190. });