|
|
@@ -12,26 +12,202 @@ $(function() {
|
|
|
}
|
|
|
}, 500);
|
|
|
|
|
|
- // tabs handle
|
|
|
- $('textarea#form-body').on('keydown', function(event){
|
|
|
- var self = $(this)
|
|
|
- start = this.selectionStart,
|
|
|
- end = this.selectionEnd
|
|
|
- val = self.val();
|
|
|
-
|
|
|
- if (event.keyCode === 9) {
|
|
|
- // tab
|
|
|
+ var getCurrentLine = function(event) {
|
|
|
+ var $target = $(event.target);
|
|
|
+
|
|
|
+ var text = $target.val();
|
|
|
+ var pos = $target.selection('getPos');
|
|
|
+ if (text === null || pos.start !== pos.end) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var startPos = text.lastIndexOf("\n", pos.start - 1) + 1;
|
|
|
+ var endPos = text.indexOf("\n", pos.start);
|
|
|
+ if (endPos === -1) {
|
|
|
+ endPos = text.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ text: text.slice(startPos, endPos),
|
|
|
+ start: startPos,
|
|
|
+ end: endPos,
|
|
|
+ caret: pos.start,
|
|
|
+ endOfLine: !$.trim(text.slice(pos.start, endPos))
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ var getPrevLine = function(event) {
|
|
|
+ var $target = $(event.target);
|
|
|
+ var currentLine = getCurrentLine(event);
|
|
|
+ var text = $target.val().slice(0, currentLine.start);
|
|
|
+ var startPos = text.lastIndexOf("\n", currentLine.start - 2) + 1;
|
|
|
+ var endPos = currentLine.start;
|
|
|
+
|
|
|
+ return {
|
|
|
+ text: text.slice(startPos, endPos),
|
|
|
+ start: startPos,
|
|
|
+ end: endPos
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ var handleTabKey = function(event) {
|
|
|
+ event.preventDefault();
|
|
|
+
|
|
|
+ var $target = $(event.target);
|
|
|
+ var currentLine = getCurrentLine(event);
|
|
|
+ var text = $target.val();
|
|
|
+ var pos = $target.selection('getPos');
|
|
|
+
|
|
|
+ if (currentLine) {
|
|
|
+ $target.selection('setPos', {start: currentLine.start, end: (currentLine.end - 1)});
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event.shiftKey === true) {
|
|
|
+ if (currentLine && currentLine.text.charAt(0) === '|') {
|
|
|
+ // prev cell in table
|
|
|
+ var newPos = text.lastIndexOf('|', pos.start - 1);
|
|
|
+ if (newPos > 0) {
|
|
|
+ $target.selection('setPos', {start: newPos - 1, end: newPos - 1});
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // re indent
|
|
|
+ var reindentedText = $target.selection().replace(/^ {1,4}/gm, '');
|
|
|
+ var reindentedCount = $target.selection().length - reindentedText.length;
|
|
|
+ $target.selection('replace', {text: reindentedText, mode: 'before'});
|
|
|
+ if (currentLine) {
|
|
|
+ $target.selection('setPos', {start: pos.start - reindentedCount, end: pos.start - reindentedCount});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (currentLine && currentLine.text.charAt(0) === '|') {
|
|
|
+ // next cell in table
|
|
|
+ var newPos = text.indexOf('|', pos.start + 1);
|
|
|
+ if (newPos < 0 || newPos === text.lastIndexOf('|', currentLine.end - 1)) {
|
|
|
+ $target.selection('setPos', {start: currentLine.end, end: currentLine.end});
|
|
|
+ } else {
|
|
|
+ $target.selection('setPos', {start: newPos + 2, end: newPos + 2});
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // indent
|
|
|
+ $target.selection('replace', {
|
|
|
+ text: ' ' + $target.selection().split("\n").join("\n "),
|
|
|
+ mode: 'before'
|
|
|
+ });
|
|
|
+ if (currentLine) {
|
|
|
+ $target.selection('setPos', {start: pos.start + 4, end: pos.start + 4});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $target.trigger('input');
|
|
|
+ };
|
|
|
+
|
|
|
+ var handleEnterKey = function(event) {
|
|
|
+ if (event.metaKey || event.ctrlKey || event.shiftKey) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var currentLine = getCurrentLine(event);
|
|
|
+ if (!currentLine || currentLine.start === currentLine.caret) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var $target = $(event.target);
|
|
|
+ var match = currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) (?:\[(?:x| )\] )?)\s*\S/);
|
|
|
+ if (match) {
|
|
|
+ // smart indent with list
|
|
|
+ if (currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) (?:\[(?:x| )\] ))\s*$/)) {
|
|
|
+ // empty task list
|
|
|
+ $target.selection('setPos', {start: currentLine.start, end: (currentLine.end - 1)});
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ event.preventDefault();
|
|
|
+ var listMark = match[1].replace(/\[x\]/, '[ ]');
|
|
|
+ var listMarkMatch = listMark.match(/^(\s*)(\d+)\./);
|
|
|
+ if (listMarkMatch) {
|
|
|
+ var indent = listMarkMatch[1];
|
|
|
+ var num = parseInt(listMarkMatch[2]);
|
|
|
+ if (num !== 1) {
|
|
|
+ listMark = listMark.return(/\s*\d+/, indent + (num +1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $target.selection('insert', {text: "\n" + listMark, mode: 'before'});
|
|
|
+ } else if (currentLine.text.match(/^(\s*(?:-|\+|\*|\d+\.) )/)) {
|
|
|
+ // remove list
|
|
|
+ $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
|
|
|
+ } else if (currentLine.text.match(/^.*\|\s*$/)) {
|
|
|
+ // new row for table
|
|
|
+ if (currentLine.text.match(/^[\|\s]+$/)) {
|
|
|
+ $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!currentLine.endOfLine) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ event.preventDefault();
|
|
|
+ var row = [];
|
|
|
+ var cellbarMatch = currentLine.text.match(/\|/g);
|
|
|
+ for (var i = 0; i < cellbarMatch.length; i++) {
|
|
|
+ row.push('|');
|
|
|
+ }
|
|
|
+ var prevLine = getPrevLine(event);
|
|
|
+ if (!prevLine || (!currentLine.text.match(/---/) && !prevLine.text.match(/\|/g))) {
|
|
|
+ $target.selection('insert', {text: "\n" + row.join(' --- ') + "\n" + row.join(' '), mode: 'before'});
|
|
|
+ $target.selection('setPos', {start: currentLine.caret + 6 * row.length - 1, end: currentLine.caret + 6 * row.length - 1});
|
|
|
+ } else {
|
|
|
+ $target.selection('insert', {text: "\n" + row.join(' '), mode: 'before'});
|
|
|
+ $target.selection('setPos', {start: currentLine.caret + 3, end: currentLine.caret + 3});
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $target.trigger('input');
|
|
|
+ };
|
|
|
+
|
|
|
+ var handleEscapeKey = function(event) {
|
|
|
+ event.preventDefault();
|
|
|
+ var $target = $(event.target);
|
|
|
+ $target.blur();
|
|
|
+ };
|
|
|
+
|
|
|
+ var handleSpaceKey = function(event) {
|
|
|
+ if (event.shiftKey && event.altKey) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ var currentLine = getCurrentLine(event);
|
|
|
+ if (!currentLine) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var $target = $(event.target);
|
|
|
+ var match = currentLine.text.match(/^(\s*)(-|\+|\*|\d+\.) (?:\[(x| )\] )(.*)/);
|
|
|
+ if (match) {
|
|
|
event.preventDefault();
|
|
|
- self.val(
|
|
|
- val.substring(0, start)
|
|
|
- + ' '
|
|
|
- + val.substring(end, val.length)
|
|
|
- );
|
|
|
- this.selectionStart = start + 4;
|
|
|
- this.selectionEnd = start + 4;
|
|
|
- } else if (event.keyCode === 27) {
|
|
|
- // escape
|
|
|
- self.blur();
|
|
|
+ var checkMark = (match[3] == ' ') ? 'x' : ' ';
|
|
|
+ var replaceTo = match[1] + match[2] + ' [' + checkMark + '] ' + match[4];
|
|
|
+ $target.selection('setPos', {start: currentLine.start, end: currentLine.end});
|
|
|
+ $target.selection('replace', {text: replaceTo, mode: 'keep'});
|
|
|
+ $target.selection('setPos', {start: currentLine.caret, end: currentLine.caret});
|
|
|
+ $target.trigger('input');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // markdown helper inspired by 'esarea'.
|
|
|
+ // see: https://github.com/fukayatsu/esarea
|
|
|
+ $('textarea#form-body').on('keydown', function(event) {
|
|
|
+ switch (event.which || event.keyCode) {
|
|
|
+ case 9:
|
|
|
+ handleTabKey(event);
|
|
|
+ break;
|
|
|
+ case 13:
|
|
|
+ handleEnterKey(event);
|
|
|
+ break;
|
|
|
+ case 27:
|
|
|
+ handleEscapeKey(event);
|
|
|
+ break;
|
|
|
+ case 32:
|
|
|
+ handleSpaceKey(event);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
}
|
|
|
});
|
|
|
|