crowi-form.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. $(function() {
  2. // preview watch
  3. var originalContent = $('#form-body').val();
  4. var prevContent = "";
  5. var watchTimer = setInterval(function() {
  6. var content = $('#form-body').val();
  7. if (prevContent != content) {
  8. var renderer = new Crowi.renderer($('#form-body').val(), $('#preview-body'));
  9. renderer.render();
  10. prevContent = content;
  11. }
  12. }, 500);
  13. var getCurrentLine = function(event) {
  14. var $target = $(event.target);
  15. var text = $target.val();
  16. var pos = $target.selection('getPos');
  17. if (text === null || pos.start !== pos.end) {
  18. return null;
  19. }
  20. var startPos = text.lastIndexOf("\n", pos.start - 1) + 1;
  21. var endPos = text.indexOf("\n", pos.start);
  22. if (endPos === -1) {
  23. endPos = text.length;
  24. }
  25. return {
  26. text: text.slice(startPos, endPos),
  27. start: startPos,
  28. end: endPos,
  29. caret: pos.start,
  30. endOfLine: !$.trim(text.slice(pos.start, endPos))
  31. };
  32. };
  33. var getPrevLine = function(event) {
  34. var $target = $(event.target);
  35. var currentLine = getCurrentLine(event);
  36. var text = $target.val().slice(0, currentLine.start);
  37. var startPos = text.lastIndexOf("\n", currentLine.start - 2) + 1;
  38. var endPos = currentLine.start;
  39. return {
  40. text: text.slice(startPos, endPos),
  41. start: startPos,
  42. end: endPos
  43. };
  44. };
  45. var handleTabKey = function(event) {
  46. event.preventDefault();
  47. var $target = $(event.target);
  48. var currentLine = getCurrentLine(event);
  49. var text = $target.val();
  50. var pos = $target.selection('getPos');
  51. if (currentLine) {
  52. $target.selection('setPos', {start: currentLine.start, end: (currentLine.end - 1)});
  53. }
  54. if (event.shiftKey === true) {
  55. if (currentLine && currentLine.text.charAt(0) === '|') {
  56. // prev cell in table
  57. var newPos = text.lastIndexOf('|', pos.start - 1);
  58. if (newPos > 0) {
  59. $target.selection('setPos', {start: newPos - 1, end: newPos - 1});
  60. }
  61. } else {
  62. // re indent
  63. var reindentedText = $target.selection().replace(/^ {1,4}/gm, '');
  64. var reindentedCount = $target.selection().length - reindentedText.length;
  65. $target.selection('replace', {text: reindentedText, mode: 'before'});
  66. if (currentLine) {
  67. $target.selection('setPos', {start: pos.start - reindentedCount, end: pos.start - reindentedCount});
  68. }
  69. }
  70. } else {
  71. if (currentLine && currentLine.text.charAt(0) === '|') {
  72. // next cell in table
  73. var newPos = text.indexOf('|', pos.start + 1);
  74. if (newPos < 0 || newPos === text.lastIndexOf('|', currentLine.end - 1)) {
  75. $target.selection('setPos', {start: currentLine.end, end: currentLine.end});
  76. } else {
  77. $target.selection('setPos', {start: newPos + 2, end: newPos + 2});
  78. }
  79. } else {
  80. // indent
  81. $target.selection('replace', {
  82. text: ' ' + $target.selection().split("\n").join("\n "),
  83. mode: 'before'
  84. });
  85. if (currentLine) {
  86. $target.selection('setPos', {start: pos.start + 4, end: pos.start + 4});
  87. }
  88. }
  89. }
  90. $target.trigger('input');
  91. };
  92. var handleEnterKey = function(event) {
  93. if (event.metaKey || event.ctrlKey || event.shiftKey) {
  94. return;
  95. }
  96. var currentLine = getCurrentLine(event);
  97. if (!currentLine || currentLine.start === currentLine.caret) {
  98. return;
  99. }
  100. var $target = $(event.target);
  101. var match = currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) (?:\[(?:x| )\] )?)\s*\S/);
  102. if (match) {
  103. // smart indent with list
  104. if (currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) (?:\[(?:x| )\] ))\s*$/)) {
  105. // empty task list
  106. $target.selection('setPos', {start: currentLine.start, end: (currentLine.end - 1)});
  107. return;
  108. }
  109. event.preventDefault();
  110. var listMark = match[1].replace(/\[x\]/, '[ ]');
  111. var listMarkMatch = listMark.match(/^(\s*)(\d+)\./);
  112. if (listMarkMatch) {
  113. var indent = listMarkMatch[1];
  114. var num = parseInt(listMarkMatch[2]);
  115. if (num !== 1) {
  116. listMark = listMark.return(/\s*\d+/, indent + (num +1));
  117. }
  118. }
  119. $target.selection('insert', {text: "\n" + listMark, mode: 'before'});
  120. } else if (currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) )/)) {
  121. // remove list
  122. $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
  123. } else if (currentLine.text.match(/^.*\|\s*$/)) {
  124. // new row for table
  125. if (currentLine.text.match(/^[\|\s]+$/)) {
  126. $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
  127. return;
  128. }
  129. if (!currentLine.endOfLine) {
  130. return;
  131. }
  132. event.preventDefault();
  133. var row = [];
  134. var cellbarMatch = currentLine.text.match(/\|/g);
  135. for (var i = 0; i < cellbarMatch.length; i++) {
  136. row.push('|');
  137. }
  138. var prevLine = getPrevLine(event);
  139. if (!prevLine || (!currentLine.text.match(/---/) && !prevLine.text.match(/\|/g))) {
  140. $target.selection('insert', {text: "\n" + row.join(' --- ') + "\n" + row.join(' '), mode: 'before'});
  141. $target.selection('setPos', {start: currentLine.caret + 6 * row.length - 1, end: currentLine.caret + 6 * row.length - 1});
  142. } else {
  143. $target.selection('insert', {text: "\n" + row.join(' '), mode: 'before'});
  144. $target.selection('setPos', {start: currentLine.caret + 3, end: currentLine.caret + 3});
  145. }
  146. }
  147. $target.trigger('input');
  148. };
  149. var handleEscapeKey = function(event) {
  150. event.preventDefault();
  151. var $target = $(event.target);
  152. $target.blur();
  153. };
  154. var handleSpaceKey = function(event) {
  155. // keybind: alt + shift + space
  156. if (!(event.shiftKey && event.altKey)) {
  157. return;
  158. }
  159. var currentLine = getCurrentLine(event);
  160. if (!currentLine) {
  161. return;
  162. }
  163. var $target = $(event.target);
  164. var match = currentLine.text.match(/^(\s*)(-|\+|\*|\d+\.) (?:\[(x| )\] )(.*)/);
  165. if (match) {
  166. event.preventDefault();
  167. var checkMark = (match[3] == ' ') ? 'x' : ' ';
  168. var replaceTo = match[1] + match[2] + ' [' + checkMark + '] ' + match[4];
  169. $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
  170. $target.selection('replace', {text: replaceTo, mode: 'keep'});
  171. $target.selection('setPos', {start: currentLine.caret, end: currentLine.caret});
  172. $target.trigger('input');
  173. }
  174. };
  175. // markdown helper inspired by 'esarea'.
  176. // see: https://github.com/fukayatsu/esarea
  177. $('textarea#form-body').on('keydown', function(event) {
  178. switch (event.which || event.keyCode) {
  179. case 9:
  180. handleTabKey(event);
  181. break;
  182. case 13:
  183. handleEnterKey(event);
  184. break;
  185. case 27:
  186. handleEscapeKey(event);
  187. break;
  188. case 32:
  189. handleSpaceKey(event);
  190. break;
  191. default:
  192. }
  193. });
  194. var unbindInlineAttachment = function($form) {
  195. $form.unbind('.inlineattach');
  196. };
  197. var bindInlineAttachment = function($form, attachmentOption) {
  198. var $this = $form;
  199. var editor = createEditorInstance($form);
  200. var inlineattach = new inlineAttachment(attachmentOption, editor);
  201. $form.bind({
  202. 'paste.inlineattach': function(e) {
  203. inlineattach.onPaste(e.originalEvent);
  204. },
  205. 'drop.inlineattach': function(e) {
  206. e.stopPropagation();
  207. e.preventDefault();
  208. inlineattach.onDrop(e.originalEvent);
  209. },
  210. 'dragenter.inlineattach dragover.inlineattach': function(e) {
  211. e.stopPropagation();
  212. e.preventDefault();
  213. }
  214. });
  215. };
  216. var createEditorInstance = function($form) {
  217. var $this = $form;
  218. return {
  219. getValue: function() {
  220. return $this.val();
  221. },
  222. insertValue: function(val) {
  223. inlineAttachment.util.insertTextAtCursor($this[0], val);
  224. },
  225. setValue: function(val) {
  226. $this.val(val);
  227. }
  228. };
  229. };
  230. var $inputForm = $('form.uploadable textarea#form-body');
  231. if ($inputForm.length > 0) {
  232. var pageId = $('#content-main').data('page-id') || 0;
  233. var attachmentOption = {
  234. uploadUrl: '/_api/attachment/page/' + pageId,
  235. extraParams: {
  236. path: location.pathname
  237. },
  238. progressText: '(Uploading file...)',
  239. urlText: "\n![file]({filename})\n"
  240. };
  241. attachmentOption.onFileUploadResponse = function(res) {
  242. var result = JSON.parse(res.response);
  243. if (result.status && result.pageCreated) {
  244. var page = result.page,
  245. pageId = page._id;
  246. $('#content-main').data('page-id', page._id);
  247. $('#page-form [name="pageForm[currentRevision]"]').val(page.revision)
  248. unbindInlineAttachment($inputForm);
  249. attachmentOption.uploadUrl = '/_api/attachment/page/' + pageId,
  250. bindInlineAttachment($inputForm, attachmentOption);
  251. }
  252. return true;
  253. };
  254. bindInlineAttachment($inputForm, attachmentOption);
  255. $('textarea#form-body').on('dragenter dragover', function() {
  256. $(this).addClass('dragover');
  257. });
  258. $('textarea#form-body').on('drop dragleave dragend', function() {
  259. $(this).removeClass('dragover');
  260. });
  261. }
  262. });