bootstrap-wysiwyg.js 6.7 KB

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