@taufik-nurrohman/text-editor.source 2.2.11 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/index.min.js CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * The MIT License (MIT)
4
4
  *
5
- * Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
5
+ * Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
6
6
  *
7
7
  * Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  * of this software and associated documentation files (the “Software”), to deal
@@ -23,4 +23,4 @@
23
23
  * SOFTWARE.
24
24
  *
25
25
  */
26
- !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(((e="undefined"!=typeof globalThis?globalThis:e||self).TE=e.TE||{},e.TE.Source={}))}(this,(function(e){"use strict";var r=function(e,r){return-1!==r.indexOf(e)},t=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},n=function(e){return e.length},i=window,o=function(e){return n=RegExp,(r=e)&&t(n)&&r instanceof n;var r,n},a=function(e,r){return o(e)?e:(e=e.replace(/\//g,"\\/"),RegExp(e,t(r)?r:"g"))};function u(e,r){return new Promise((function(t,n){var o=i[e].apply(i,r);return o?t(o):n(o)}))}var s={source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"},type:null}};["alert","confirm","prompt"].forEach((function(e){s.source[e]=function(){for(var r=arguments.length,t=Array(r),n=0;n<r;n++)t[n]=arguments[n];return u(e,t)}}));var c={};c.toggle=function(e,r,i,o){void 0===o&&(o=!1),r||""===r||(r=e);var a=this,u=a.$(),s=u.after,c=u.before,f=u.value,l=n(r),p=n(e);return i&&r===f.slice(-l)&&e===f.slice(0,p)||r===s.slice(0,l)&&e===c.slice(-p)?a.peel(e,r,i):(!1!==o&&("string"==typeof o?o=[o,o]:function(e){return Array.isArray(e)}(o)||(o=["",""]),t(o[1])||(o[1]=o[0]),a.trim(o[0],o[1])),a.wrap(e,r,i))};var f="Control-",l="Shift-";var p,d,v,h=(p=function(e){return e.record()},d=100,function(){var e=arguments,r=this;v&&clearTimeout(v),v=setTimeout((function(){return p.apply(r,e)}),d)});var y=s;e.canKeyDown=function(e,t){var n,i,o=t.state.source.tab||t.state.tab||"\t",a=t.state.source.pairs||{},u=Object.values(a),s=e.key,c=e.queue,f=e+"";if(c.Alt||c.Control)return!0;if(" "===f){var p=t.$(),d=p.after,v=p.before,h=p.value;return n=a[i=v.slice(-1)],!(!h&&n&&i&&n===d[0])||(t.wrap(" "," "),!1)}if("Enter"===f||l+"Enter"===f){var y=t.$(),w=y.after,b=y.before,m=y.value,$=b.split("\n").pop().match(/^(\s+)/),g=$&&$[1]||"";if(!m){if(w&&b&&(n=a[i=b.slice(-1)])&&n===w[0])return t.wrap("\n"+g+(i!==n?o:""),"\n"+g).record(),!1;if(g)return t.insert("\n"+g,-1).record(),!1}return!0}if("Backspace"===f){var E=t.$(),T=E.after,A=E.before,D=E.value;T.split("\n")[0];var K=A.split("\n").pop(),W=K.match(/^(\s+)/),k=W&&W[1]||"";return n=a[i=A.slice(-1)],"\\"===i?!0:D?!(T&&A&&n&&n===T[0]&&!A.endsWith("\\"+i))||(t.record().peel(i,n).record(),!1):(n=a[i=A.trim().slice(-1)])&&i&&(T.startsWith(" "+n)&&A.endsWith(i+" ")||T.startsWith("\n"+k+n)&&A.endsWith(i+"\n"+k))?(t.trim("","").record(),!1):K.endsWith(o)?(t.pull(o).record(),!1):!(T&&A&&!A.endsWith("\\"+i)&&n===T[0]&&i===A.slice(-1))||(t.peel(i,n).record(),!1)}var x=t.$(),j=x.after,q=x.before,C=x.start,S=x.value;if("\\"===(i=q.slice(-1)))return!0;if(n=r(j[0],u)?j[0]:a[i],!S&&j&&q&&n&&s===n)return t.select(C+1).record(),!1;for(i in a){if(n=a[i],s===i&&n)return t.wrap(i,n).record(),!1;if(s===n){if(S)return t.record().wrap(i,n).record(),!1;break}}return!0},e.canKeyDownDent=function(e,r){var t=r.state.source.tab||r.state.tab||"\t";e.key,e.queue;var n=e+"";return f+"]"===n?(r.push(t).record(),!1):f+"["!==n||(r.pull(t).record(),!1)},e.canKeyDownEnter=function(e,r){e.key;var t=e.queue;if(t.Control&&t.Enter){var i=r.$(),o=i.after,a=i.before,u=i.end,s=i.start,c=i.value,f=o.split("\n").shift(),l=a.split("\n").pop(),p=l.match(/^(\s+)/),d=p&&p[1]||"";if(a||o)return t.Shift?(r.select(s-n(l)).wrap(d,"\n").insert(c).record(),!1):(r.select(u+n(f)).wrap("\n"+d,"").insert(c).record(),!1)}return!0},e.canKeyDownHistory=function(e,r){var t=e+"";return f+"y"===t?(r.redo(),!1):f+"z"!==t||(r.undo(),!1)},e.canKeyDownMove=function(e,t){e.key;var i=e+"";if(!e.queue.Control)return!0;var o,u,s,c=t.$(),l=c.after,p=c.before,d=c.end,v=c.start,h=c.value,y=t.state.source.pairs||{},w=[];if(h){for(o in y)(u=y[o])&&w.push("(?:\\"+o+"(?:\\\\.|[^\\"+o+(u!==o?"\\"+u:"")+"])*\\"+u+")");if(w.push("\\w+"),w.push("\\s+"),w.push("[\\s\\S]"),f+"ArrowLeft"===i)return(s=p.match(a("("+w.join("|")+")$","")))?(t.insert("").select(v-n(s[0])).insert(h),t.record(),!1):(t.select(),!1);if(f+"ArrowRight"===i)return(s=l.match(a("^("+w.join("|")+")","")))?(t.insert("").select(d+n(s[0])-n(h)).insert(h),t.record(),!1):(t.select(),!1)}var b=l.split("\n").shift(),m=p.split("\n").pop(),$=m.match(/^(\s+)/);if($&&$[1],d+=n(b),v-=n(m),h=m+h+b,f+"ArrowUp"===i){if(!r("\n",p))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"$2",1),t.replace(/(^|\n)([^\n]*?)$/,"",-1);var g=t.$();return p=g.before,v=g.start,m=p.split("\n").pop(),t.select(v-=n(m)).wrap(h,"\n"),t.select(v,v+n(h)),t.record(),!1}if(f+"ArrowDown"===i){if(!r("\n",l))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"",1),t.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var E=t.$();return l=E.after,d=E.end,b=l.split("\n").shift(),t.select(d+=n(b)).wrap("\n",h),d+=1,t.select(d,d+n(h)),t.record(),!1}return!0},e.canKeyDownTab=function(e,r){var t=r.state.source.tab||r.state.tab||"\t",n=e+"";return"Tab"===n?(r.push(t).record(),!1):l+"Tab"!==n||(r.pull(t).record(),!1)},e.canKeyUp=function(e,r){return h(r),!0},e.state=y,e.that=c,Object.defineProperty(e,"__esModule",{value:!0})}));
26
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):((e="undefined"!=typeof globalThis?globalThis:e||self).TextEditor=e.TextEditor||{},e.TextEditor.Source=r())}(this,(function(){"use strict";var e,r,t,n=function(e,r){return-1!==r.indexOf(e)},i=function(e){return Array.isArray(e)},o=function(e){return"function"==typeof e},s=function(e,r){return e&&u(r)&&e instanceof r},c=function(e){return l(e)&&0==e%1},l=function(e){return"number"==typeof e},f=function(e,r){return void 0===r&&(r=!0),"object"==typeof e&&(!r||s(e,Object))},u=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},a=function(e){return e.length},p=function(e){return Object.values(e)},d=function e(){for(var r=arguments.length,t=Array(r),o=0;o<r;o++)t[o]=arguments[o];for(var s=t.shift(),c=0,l=a(t);c<l;++c)for(var p in t[c])if(u(s[p]))if(i(s[p])&&i(t[c][p])){s[p]=[].concat(s[p]);for(var d=0,h=a(t[c][p]);d<h;++d)n(t[c][p][d],s[p])||s[p].push(t[c][p][d])}else f(s[p])&&f(t[c][p])?s[p]=e({},s[p],t[c][p]):s[p]=t[c][p];else s[p]=t[c][p];return s},h=window,v=function(e){return e&&e.preventDefault()},w=function(e,r){return function(e){return s(e,RegExp)}(e)?e:RegExp(e,u(r)?r:"g")},b="Alt-",$="Control-",m="Shift-",k=(e=function(e){return e.record()},r=10,function(){var n=arguments,i=this;t&&clearTimeout(t),t=setTimeout((function(){return e.apply(i,n)}),r)});function y(e){var r,t,i=this,o=i.k(!1).pop(),s=i.k();if(k(i),!e.defaultPrevented&&!i.keys[s]){var l,f,u=(null==(r=i.state.source)?void 0:r.tab)||i.state.tab||"\t",d=(null==(t=i.state.source)?void 0:t.pairs)||{},h=p(d);c(u)&&(u=" ".repeat(u));var y=i.$(),g=y.after,x=y.before,E=y.end,A=y.start,B=y.value,T=g.split("\n").shift(),W=x.split("\n").pop(),j=/^\s+/.exec(W),D=j&&j[0]||"";if($+m+"Enter"===s)return x||g?(v(e),i.select(A-a(W)).wrap(D,"\n").insert(B).record(),!1):void 0;if($+"Enter"===s&&(x||g))return v(e),i.select(E+a(T)).wrap("\n"+D,"").insert(B).record(),!1;if(b!==s+"-"&&$!==s+"-"){if(" "===s)return l=d[f=x.slice(-1)],!B&&l&&f&&l===g[0]?(v(e),i.wrap(" "," ")):void 0;if("Backspace"===s||"Delete"===s){if(l=d[f=x.slice(-1)],"\\"===f)return;return B?g&&x&&l&&l===g[0]&&!x.endsWith("\\"+f)?(v(e),i.record().peel(f,l).record()):void 0:(l=d[f=x.trim().slice(-1)])&&f&&(g.startsWith(" "+l)&&x.endsWith(f+" ")||g.startsWith("\n"+D+l)&&x.endsWith(f+"\n"+D))?(v(e),i.trim("","").record()):"Delete"!==s&&W.endsWith(u)?(v(e),i.pull(u).record()):g&&x&&!x.endsWith("\\"+f)&&l===g[0]&&f===x.slice(-1)?(v(e),i.peel(f,l).record()):void 0}if("Enter"!==s&&m+"Enter"!==s){if("\\"!==(f=x.slice(-1))){if(l=n(g[0],h)?g[0]:d[f],!B&&g&&x&&l&&o===l)return v(e),i.select(A+1).record();for(f in d){if(l=d[f],o===f&&l)return v(e),i.wrap(f,l).record();if(o===l){if(B)return v(e),i.record().wrap(f,l).record();break}}var O,R,S,C=[];if(B){for(O in d)(R=d[O])&&C.push("(?:\\"+O+"(?:\\\\.|[^\\"+O+(R!==O?"\\"+R:"")+"])*\\"+R+")");if(C.push("\\w+"),C.push("\\s+"),C.push("[\\s\\S]"),$+"ArrowLeft"===s)return v(e),(S=w("("+C.join("|")+")$","").exec(x))?i.insert("").select(A-a(S[0])).insert(B).record():i.select();if($+"ArrowRight"===s)return v(e),(S=g.match(w("^("+C.join("|")+")","")))?i.insert("").select(E+a(S[0])-a(B)).insert(B).record():i.select()}if(E+=a(T),A-=a(W),B=W+B+T,$+"ArrowUp"===s){if(v(e),!n("\n",x))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"$2",1),i.replace(/(^|\n)([^\n]*?)$/,"",-1);var L=i.$();return x=L.before,A=L.start,W=x.split("\n").pop(),i.select(A-=a(W)).wrap(B,"\n"),i.select(A,A+a(B)),i.record()}if($+"ArrowDown"===s){if(v(e),!n("\n",g))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"",1),i.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var P=i.$();return g=P.after,E=P.end,T=g.split("\n").shift(),i.select(E+=a(T)).wrap("\n",B),E+=1,i.select(E,E+a(B)),i.record()}}}else if(!B){if(g&&x&&(l=d[f=x.slice(-1)])&&l===g[0])return v(e),i.wrap("\n"+D+(f!==l?u:""),"\n"+D).record();if(D)return v(e),i.insert("\n"+D,-1).record()}}else v(e)}}var g={attach:function(){var e=this;return e.state=d({source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"}}},e.state),e.alert=function(r,t){return h.alert&&h.alert(r),o(t)&&t.call(e,!0)},e.confirm=function(r,t){return o(t)&&t.call(e,h.confirm&&h.confirm(r))},e.insertBlock=function(r,t){var n=e.$(),i=n.after,o=n.before,s=n.end,c=n.start,l=i.split("\n").shift(),f=a(l),u=o.split("\n").pop(),p=a(u),d=/^\s+/.exec(u),h=d&&d[0]||"";return-1===t?e.select(c-p).insert("\n",1).push(h).insert(r,1,!1):1===t?e.select(s+f).insert("\n",-1).push(h).insert(r,1,!1):e.select(c-p,s+f).insert(r,t,!0).wrap(h,"")},e.peelBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.end,l=i.start,f=i.value,u=a(t),p=o.split("\n").shift(),d=a(p),h=s.split("\n").pop(),v=a(h),w=a(r);return n&&t===f.slice(-u)&&r===f.slice(0,w)||t===p.slice(-u)&&r===h.slice(0,w)?e.select(l-v+(n?0:w),c+d-(n?0:u)).peel(r,t,n):e.select(l,c)},e.prompt=function(r,t,n){return o(n)&&n.call(e,!!h.prompt&&h.prompt(r,t))},e.selectBlock=function(){var r=e.$(),t=r.after,n=r.before,i=r.end,o=r.start,s=t.split("\n").shift(),c=a(s),l=n.split("\n").pop(),f=a(l);return e.select(o-f,i+c)},e.toggle=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.value,l=a(t),f=a(r);return n&&t===c.slice(-l)&&r===c.slice(0,f)||t===o.slice(0,l)&&r===s.slice(-f)?e.peel(r,t,n):e.wrap(r,t,n)},e.toggleBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.value,l=a(t),f=o.split("\n").shift(),u=s.split("\n").pop(),p=a(r);return n&&t===c.slice(-l)&&r===c.slice(0,p)||t===f.slice(-l)&&r===u.slice(0,p)?e.peelBlock(r,t,n):e.wrapBlock(r,t,n)},e.wrapBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.end,l=i.start,f=o.split("\n").shift(),u=a(f),p=s.split("\n").pop(),d=a(p);return e.select(l-d,c+u).wrap(r,t,n)},e.on("key.down",y).record()},detach:function(){return this.off("key.down",y)}};return g}));
package/index.mjs CHANGED
@@ -1,127 +1,79 @@
1
1
  import {W} from '@taufik-nurrohman/document';
2
2
  import {debounce} from '@taufik-nurrohman/tick';
3
- import {esc, escChar, toPattern} from '@taufik-nurrohman/pattern';
3
+ import {fromStates} from '@taufik-nurrohman/from';
4
4
  import {hasValue} from '@taufik-nurrohman/has';
5
- import {isArray, isSet, isString} from '@taufik-nurrohman/is';
5
+ import {isArray, isFunction, isInteger, isSet, isString} from '@taufik-nurrohman/is';
6
+ import {offEventDefault} from '@taufik-nurrohman/event';
6
7
  import {toCount, toObjectValues} from '@taufik-nurrohman/to';
8
+ import {toPattern} from '@taufik-nurrohman/pattern';
7
9
 
8
- const pairs = {
9
- '`': '`',
10
- '(': ')',
11
- '{': '}',
12
- '[': ']',
13
- '"': '"',
14
- "'": "'",
15
- '<': '>'
16
- };
17
-
18
- function promisify(type, lot) {
19
- return new Promise((resolve, reject) => {
20
- let r = W[type].apply(W, lot);
21
- return r ? resolve(r) : reject(r);
22
- });
23
- }
24
-
25
- const defaults = {
26
- source: {
27
- pairs,
28
- type: null
29
- }
30
- };
31
-
32
- ['alert', 'confirm', 'prompt'].forEach(type => {
33
- defaults.source[type] = (...lot) => promisify(type, lot);
34
- });
10
+ const ALT_PREFIX = 'Alt-';
11
+ const CTRL_PREFIX = 'Control-';
12
+ const SHIFT_PREFIX = 'Shift-';
35
13
 
36
- export const that = {};
14
+ const bounce = debounce($ => $.record(), 10);
37
15
 
38
- that.toggle = function (open, close, wrap, tidy = false) {
39
- if (!close && "" !== close) {
40
- close = open;
16
+ function onKeyDown(e) {
17
+ let $ = this,
18
+ key = $.k(false).pop(), // Capture the last key
19
+ keys = $.k();
20
+ bounce($);
21
+ if (e.defaultPrevented || $.keys[keys]) {
22
+ return;
41
23
  }
42
- let t = this,
43
- {after, before, value} = t.$(),
44
- closeCount = toCount(close),
45
- openCount = toCount(open);
46
- if (
47
- (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
48
- (close === after.slice(0, closeCount) && open === before.slice(-openCount))
49
- ) {
50
- return t.peel(open, close, wrap);
24
+ let charAfter,
25
+ charBefore,
26
+ charIndent = $.state.source?.tab || $.state.tab || '\t',
27
+ charPairs = $.state.source?.pairs || {},
28
+ charPairsValues = toObjectValues(charPairs);
29
+ if (isInteger(charIndent)) {
30
+ charIndent = ' '.repeat(charIndent);
51
31
  }
52
- if (false !== tidy) {
53
- if (isString(tidy)) {
54
- tidy = [tidy, tidy];
55
- } else if (!isArray(tidy)) {
56
- tidy = ["", ""];
32
+ let {after, before, end, start, value} = $.$(),
33
+ lineAfter = after.split('\n').shift(),
34
+ lineBefore = before.split('\n').pop(),
35
+ lineMatch = /^\s+/.exec(lineBefore),
36
+ lineMatchIndent = lineMatch && lineMatch[0] || "";
37
+ if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
38
+ if (before || after) {
39
+ // Insert line above with `⎈⇧↵`
40
+ offEventDefault(e);
41
+ return $.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
57
42
  }
58
- if (!isSet(tidy[1])) {
59
- tidy[1] = tidy[0];
43
+ return;
44
+ }
45
+ if (CTRL_PREFIX + 'Enter' === keys) {
46
+ if (before || after) {
47
+ // Insert line below with `⎈↵`
48
+ offEventDefault(e);
49
+ return $.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
60
50
  }
61
- t.trim(tidy[0], tidy[1]);
62
51
  }
63
- return t.wrap(open, close, wrap);
64
- };
65
-
66
- const ALT_PREFIX = 'Alt-';
67
- const CTRL_PREFIX = 'Control-';
68
- const SHIFT_PREFIX = 'Shift-';
69
-
70
- export function canKeyDown(map, of) {
71
- let charAfter,
72
- charBefore,
73
- charIndent = of.state.source.tab || of.state.tab || '\t',
74
- charPairs = of.state.source.pairs || {},
75
- charPairsValues = toObjectValues(charPairs),
76
- {key, queue} = map,
77
- keyValue = map + "";
78
52
  // Do nothing
79
- if (queue.Alt || queue.Control) {
80
- return true;
53
+ if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
54
+ offEventDefault(e);
55
+ return;
81
56
  }
82
- if (' ' === keyValue) {
83
- let {after, before, value} = of.$();
57
+ if (' ' === keys) {
84
58
  charAfter = charPairs[charBefore = before.slice(-1)];
85
59
  if (!value && charAfter && charBefore && charAfter === after[0]) {
86
- of.wrap(' ', ' ');
87
- return false;
88
- }
89
- return true;
90
- }
91
- if ('Enter' === keyValue || SHIFT_PREFIX + 'Enter' === keyValue) {
92
- let {after, before, value} = of.$(),
93
- lineBefore = before.split('\n').pop(),
94
- lineMatch = lineBefore.match(/^(\s+)/),
95
- lineMatchIndent = lineMatch && lineMatch[1] || "";
96
- if (!value) {
97
- if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
98
- of.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
99
- return false;
100
- }
101
- if (lineMatchIndent) {
102
- of.insert('\n' + lineMatchIndent, -1).record();
103
- return false;
104
- }
60
+ offEventDefault(e);
61
+ return $.wrap(' ', ' ');
105
62
  }
106
- return true;
63
+ return;
107
64
  }
108
- if ('Backspace' === keyValue) {
109
- let {after, before, value} = of.$(),
110
- lineAfter = after.split('\n')[0],
111
- lineBefore = before.split('\n').pop(),
112
- lineMatch = lineBefore.match(/^(\s+)/),
113
- lineMatchIndent = lineMatch && lineMatch[1] || "";
65
+ if ('Backspace' === keys || 'Delete' === keys) {
114
66
  charAfter = charPairs[charBefore = before.slice(-1)];
115
67
  // Do nothing on escape
116
68
  if ('\\' === charBefore) {
117
- return true;
69
+ return;
118
70
  }
119
71
  if (value) {
120
72
  if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
121
- of.record().peel(charBefore, charAfter).record();
122
- return false;
73
+ offEventDefault(e);
74
+ return $.record().peel(charBefore, charAfter).record();
123
75
  }
124
- return true;
76
+ return;
125
77
  }
126
78
  charAfter = charPairs[charBefore = before.trim().slice(-1)];
127
79
  if (charAfter && charBefore) {
@@ -130,36 +82,48 @@ export function canKeyDown(map, of) {
130
82
  after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)
131
83
  ) {
132
84
  // Collapse bracket(s)
133
- of.trim("", "").record();
134
- return false;
85
+ offEventDefault(e);
86
+ return $.trim("", "").record();
135
87
  }
136
88
  }
137
89
  // Outdent
138
- if (lineBefore.endsWith(charIndent)) {
139
- of.pull(charIndent).record();
140
- return false;
90
+ if ('Delete' !== keys && lineBefore.endsWith(charIndent)) {
91
+ offEventDefault(e);
92
+ return $.pull(charIndent).record();
141
93
  }
142
94
  if (after && before && !before.endsWith('\\' + charBefore)) {
143
95
  if (charAfter === after[0] && charBefore === before.slice(-1)) {
144
96
  // Peel pair
145
- of.peel(charBefore, charAfter).record();
146
- return false;
97
+ offEventDefault(e);
98
+ return $.peel(charBefore, charAfter).record();
99
+ }
100
+ }
101
+ return;
102
+ }
103
+ if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
104
+ if (!value) {
105
+ if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
106
+ offEventDefault(e);
107
+ return $.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
108
+ }
109
+ if (lineMatchIndent) {
110
+ offEventDefault(e);
111
+ return $.insert('\n' + lineMatchIndent, -1).record();
147
112
  }
148
113
  }
149
- return true;
114
+ return;
150
115
  }
151
- let {after, before, start, value} = of.$();
152
116
  // Do nothing on escape
153
117
  if ('\\' === (charBefore = before.slice(-1))) {
154
- return true;
118
+ return;
155
119
  }
156
120
  charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
157
121
  // `|}`
158
122
  if (!value && after && before && charAfter && key === charAfter) {
159
123
  // Move to the next character
160
124
  // `}|`
161
- of.select(start + 1).record();
162
- return false;
125
+ offEventDefault(e);
126
+ return $.select(start + 1).record();
163
127
  }
164
128
  for (charBefore in charPairs) {
165
129
  charAfter = charPairs[charBefore];
@@ -167,168 +131,192 @@ export function canKeyDown(map, of) {
167
131
  if (key === charBefore && charAfter) {
168
132
  // Wrap pair or selection
169
133
  // `{|}` `{|aaa|}`
170
- of.wrap(charBefore, charAfter).record();
171
- return false;
134
+ offEventDefault(e);
135
+ return $.wrap(charBefore, charAfter).record();
172
136
  }
173
137
  // `|}`
174
138
  if (key === charAfter) {
175
139
  if (value) {
176
140
  // Wrap selection
177
141
  // `{|aaa|}`
178
- of.record().wrap(charBefore, charAfter).record();
179
- return false;
142
+ offEventDefault(e);
143
+ return $.record().wrap(charBefore, charAfter).record();
180
144
  }
181
145
  break;
182
146
  }
183
147
  }
184
- return true;
185
- }
186
-
187
- export function canKeyDownDent(map, of) {
188
- let charIndent = of.state.source.tab || of.state.tab || '\t',
189
- {key, queue} = map,
190
- keyValue = map + "";
191
- // Indent with `⎈]`
192
- if (CTRL_PREFIX + ']' === keyValue) {
193
- of.push(charIndent).record();
194
- return false;
195
- }
196
- // Outdent with `⎈[`
197
- if (CTRL_PREFIX + '[' === keyValue) {
198
- of.pull(charIndent).record();
199
- return false;
200
- }
201
- return true;
202
- }
203
-
204
- export function canKeyDownEnter(map, of) {
205
- let {key, queue} = map;
206
- if (queue.Control && queue.Enter) {
207
- let {after, before, end, start, value} = of.$(),
208
- lineAfter = after.split('\n').shift(),
209
- lineBefore = before.split('\n').pop(),
210
- lineMatch = lineBefore.match(/^(\s+)/),
211
- lineMatchIndent = lineMatch && lineMatch[1] || "";
212
- if (before || after) {
213
- if (queue.Shift) {
214
- // Insert line above with `⎈⇧↵`
215
- return of.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
216
- }
217
- // Insert line below with `⎈↵`
218
- return of.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
219
- }
220
- }
221
- return true;
222
- }
223
-
224
- export function canKeyDownHistory(map, of) {
225
- let keyValue = map + "";
226
- // Redo with `⎈y`
227
- if (CTRL_PREFIX + 'y' === keyValue) {
228
- return of.redo(), false;
229
- }
230
- // Undo with `⎈z`
231
- if (CTRL_PREFIX + 'z' === keyValue) {
232
- return of.undo(), false;
233
- }
234
- return true;
235
- }
236
-
237
- export function canKeyDownMove(map, of) {
238
- let {key, queue} = map,
239
- keyValue = map + "";
240
- if (!queue.Control) {
241
- return true;
242
- }
243
- let {after, before, end, start, value} = of.$(),
244
- charPair, charPairValue,
245
- charPairs = of.state.source.pairs || {},
246
- boundaries = [], m;
148
+ let charPair,
149
+ charPairValue, m,
150
+ tokens = [];
247
151
  if (value) {
248
152
  for (charPair in charPairs) {
249
153
  if (!(charPairValue = charPairs[charPair])) {
250
154
  continue;
251
155
  }
252
- boundaries.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
156
+ tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
253
157
  }
254
- boundaries.push('\\w+'); // Word(s)
255
- boundaries.push('\\s+'); // White-space(s)
256
- boundaries.push('[\\s\\S]'); // Last try!
257
- if (CTRL_PREFIX + 'ArrowLeft' === keyValue) {
258
- if (m = before.match(toPattern('(' + boundaries.join('|') + ')$', ""))) {
259
- of.insert("").select(start - toCount(m[0])).insert(value);
260
- return of.record(), false;
158
+ tokens.push('\\w+'); // Word(s)
159
+ tokens.push('\\s+'); // White-space(s)
160
+ tokens.push('[\\s\\S]'); // Last try!
161
+ if (CTRL_PREFIX + 'ArrowLeft' === keys) {
162
+ offEventDefault(e);
163
+ if (m = toPattern('(' + tokens.join('|') + ')$', "").exec(before)) {
164
+ return $.insert("").select(start - toCount(m[0])).insert(value).record();
261
165
  }
262
- return of.select(), false;
166
+ return $.select();
263
167
  }
264
- if (CTRL_PREFIX + 'ArrowRight' === keyValue) {
265
- if (m = after.match(toPattern('^(' + boundaries.join('|') + ')', ""))) {
266
- of.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value);
267
- return of.record(), false;
168
+ if (CTRL_PREFIX + 'ArrowRight' === keys) {
169
+ offEventDefault(e);
170
+ if (m = after.match(toPattern('^(' + tokens.join('|') + ')', ""))) {
171
+ return $.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
268
172
  }
269
- return of.select(), false;
173
+ return $.select();
270
174
  }
271
175
  }
272
- let lineAfter = after.split('\n').shift(),
273
- lineBefore = before.split('\n').pop(),
274
- lineMatch = lineBefore.match(/^(\s+)/),
275
- lineMatchIndent = lineMatch && lineMatch[1] || "";
276
176
  // Force to select the current line if there is no selection
277
177
  end += toCount(lineAfter);
278
178
  start -= toCount(lineBefore);
279
179
  value = lineBefore + value + lineAfter;
280
- if (CTRL_PREFIX + 'ArrowUp' === keyValue) {
180
+ if (CTRL_PREFIX + 'ArrowUp' === keys) {
181
+ offEventDefault(e);
281
182
  if (!hasValue('\n', before)) {
282
- return of.select(), false;
183
+ return $.select();
283
184
  }
284
- of.insert("");
285
- of.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
286
- of.replace(/(^|\n)([^\n]*?)$/, "", -1);
287
- let $ = of.$();
288
- before = $.before;
289
- start = $.start;
185
+ $.insert("");
186
+ $.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
187
+ $.replace(/(^|\n)([^\n]*?)$/, "", -1);
188
+ let s = $.$();
189
+ before = s.before;
190
+ start = s.start;
290
191
  lineBefore = before.split('\n').pop();
291
- of.select(start = start - toCount(lineBefore)).wrap(value, '\n');
292
- of.select(start, start + toCount(value));
293
- return of.record(), false;
192
+ $.select(start = start - toCount(lineBefore)).wrap(value, '\n');
193
+ $.select(start, start + toCount(value));
194
+ return $.record();
294
195
  }
295
- if (CTRL_PREFIX + 'ArrowDown' === keyValue) {
196
+ if (CTRL_PREFIX + 'ArrowDown' === keys) {
197
+ offEventDefault(e);
296
198
  if (!hasValue('\n', after)) {
297
- return of.select(), false;
199
+ return $.select();
298
200
  }
299
- of.insert("");
300
- of.replace(/^([^\n]*?)(\n|$)/, "", 1);
301
- of.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
302
- let $ = of.$();
303
- after = $.after;
304
- end = $.end;
201
+ $.insert("");
202
+ $.replace(/^([^\n]*?)(\n|$)/, "", 1);
203
+ $.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
204
+ let s = $.$();
205
+ after = s.after;
206
+ end = s.end;
305
207
  lineAfter = after.split('\n').shift();
306
- of.select(end = end + toCount(lineAfter)).wrap('\n', value);
208
+ $.select(end = end + toCount(lineAfter)).wrap('\n', value);
307
209
  end += 1;
308
- of.select(end, end + toCount(value));
309
- return of.record(), false;
210
+ $.select(end, end + toCount(value));
211
+ return $.record();
310
212
  }
311
- return true;
213
+ return;
312
214
  }
313
215
 
314
- export function canKeyDownTab(map, of) {
315
- let charIndent = of.state.source.tab || of.state.tab || '\t',
316
- keyValue = map + "";
317
- // Indent with `⇥`
318
- if ('Tab' === keyValue) {
319
- return of.push(charIndent).record(), false;
320
- }
321
- // Outdent with `⇧+⇥`
322
- if (SHIFT_PREFIX + 'Tab' === keyValue) {
323
- return of.pull(charIndent).record(), false;
324
- }
325
- return true;
216
+ function attach() {
217
+ let $ = this;
218
+ $.state = fromStates({
219
+ source: {
220
+ pairs: {
221
+ '`': '`',
222
+ '(': ')',
223
+ '{': '}',
224
+ '[': ']',
225
+ '"': '"',
226
+ "'": "'",
227
+ '<': '>'
228
+ }
229
+ }
230
+ }, $.state);
231
+ $.alert = (hint, then) => {
232
+ W.alert && W.alert(hint);
233
+ return isFunction(then) && then.call($, true);
234
+ };
235
+ $.confirm = (hint, then) => {
236
+ return isFunction(then) && then.call($, W.confirm && W.confirm(hint));
237
+ };
238
+ $.insertBlock = (value, mode) => {
239
+ let {after, before, end, start} = $.$(),
240
+ lineAfter = after.split('\n').shift(),
241
+ lineAfterCount = toCount(lineAfter),
242
+ lineBefore = before.split('\n').pop(),
243
+ lineBeforeCount = toCount(lineBefore),
244
+ lineMatch = /^\s+/.exec(lineBefore),
245
+ lineMatchIndent = lineMatch && lineMatch[0] || "";
246
+ if (-1 === mode) {
247
+ return $.select(start - lineBeforeCount).insert('\n', 1).push(lineMatchIndent).insert(value, 1, false);
248
+ }
249
+ if (1 === mode) {
250
+ return $.select(end + lineAfterCount).insert('\n', -1).push(lineMatchIndent).insert(value, 1, false);
251
+ }
252
+ return $.select(start - lineBeforeCount, end + lineAfterCount).insert(value, mode, true).wrap(lineMatchIndent, "");
253
+ };
254
+ $.peelBlock = (open, close, wrap) => {
255
+ let {after, before, end, start, value} = $.$(),
256
+ closeCount = toCount(close),
257
+ lineAfter = after.split('\n').shift(),
258
+ lineAfterCount = toCount(lineAfter),
259
+ lineBefore = before.split('\n').pop(),
260
+ lineBeforeCount = toCount(lineBefore),
261
+ openCount = toCount(open);
262
+ if (
263
+ (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
264
+ (close === lineAfter.slice(-closeCount) && open === lineBefore.slice(0, openCount))
265
+ ) {
266
+ return $.select(start - lineBeforeCount + (wrap ? 0 : openCount), end + lineAfterCount - (wrap ? 0 : closeCount)).peel(open, close, wrap);
267
+ }
268
+ return $.select(start, end);
269
+ };
270
+ $.prompt = (hint, value, then) => {
271
+ return isFunction(then) && then.call($, W.prompt ? W.prompt(hint, value) : false);
272
+ };
273
+ $.selectBlock = () => {
274
+ let {after, before, end, start} = $.$(),
275
+ lineAfter = after.split('\n').shift(),
276
+ lineAfterCount = toCount(lineAfter),
277
+ lineBefore = before.split('\n').pop(),
278
+ lineBeforeCount = toCount(lineBefore);
279
+ return $.select(start - lineBeforeCount, end + lineAfterCount);
280
+ };
281
+ $.toggle = (open, close, wrap) => {
282
+ let {after, before, value} = $.$(),
283
+ closeCount = toCount(close),
284
+ openCount = toCount(open);
285
+ if (
286
+ (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
287
+ (close === after.slice(0, closeCount) && open === before.slice(-openCount))
288
+ ) {
289
+ return $.peel(open, close, wrap);
290
+ }
291
+ return $.wrap(open, close, wrap);
292
+ };
293
+ $.toggleBlock = (open, close, wrap) => {
294
+ let {after, before, value} = $.$(),
295
+ closeCount = toCount(close),
296
+ lineAfter = after.split('\n').shift(),
297
+ lineBefore = before.split('\n').pop(),
298
+ openCount = toCount(open);
299
+ if (
300
+ (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
301
+ (close === lineAfter.slice(-closeCount) && open === lineBefore.slice(0, openCount))
302
+ ) {
303
+ return $.peelBlock(open, close, wrap);
304
+ }
305
+ return $.wrapBlock(open, close, wrap);
306
+ };
307
+ $.wrapBlock = (open, close, wrap) => {
308
+ let {after, before, end, start} = $.$(),
309
+ lineAfter = after.split('\n').shift(),
310
+ lineAfterCount = toCount(lineAfter),
311
+ lineBefore = before.split('\n').pop(),
312
+ lineBeforeCount = toCount(lineBefore);
313
+ return $.select(start - lineBeforeCount, end + lineAfterCount).wrap(open, close, wrap);
314
+ };
315
+ return $.on('key.down', onKeyDown).record();
326
316
  }
327
317
 
328
- let bounce = debounce(of => of.record(), 100);
329
-
330
- export function canKeyUp(map, of) {
331
- return bounce(of), true;
318
+ function detach() {
319
+ return this.off('key.down', onKeyDown);
332
320
  }
333
321
 
334
- export const state = defaults;
322
+ export default {attach, detach};
package/package.json CHANGED
@@ -3,11 +3,15 @@
3
3
  "browser": "index.min.js",
4
4
  "bugs": "https://github.com/taufik-nurrohman/text-editor.source/issues",
5
5
  "dependencies": {
6
+ "@taufik-nurrohman/document": "*",
7
+ "@taufik-nurrohman/event": "*",
8
+ "@taufik-nurrohman/from": "*",
6
9
  "@taufik-nurrohman/has": "*",
7
- "@taufik-nurrohman/key": "*",
10
+ "@taufik-nurrohman/is": "*",
8
11
  "@taufik-nurrohman/pattern": "*",
9
12
  "@taufik-nurrohman/text-editor": "*",
10
13
  "@taufik-nurrohman/text-editor.history": "*",
14
+ "@taufik-nurrohman/text-editor.key": "*",
11
15
  "@taufik-nurrohman/tick": "*",
12
16
  "@taufik-nurrohman/to": "*"
13
17
  },
@@ -47,7 +51,7 @@
47
51
  "url": "git+https://github.com/taufik-nurrohman/text-editor.source.git"
48
52
  },
49
53
  "scripts": {
50
- "pack": "pack --clean=false --from=.github/factory --js-format=umd --js-name=TE.Source --js-top='%(js.license)' --mjs=true --to=."
54
+ "pack": "pack --clean=false --from=.factory --js-format=umd --js-name=TextEditor.Source --js-top='%(js.license)' --mjs=true --to=."
51
55
  },
52
- "version": "2.2.11"
56
+ "version": "3.0.1"
53
57
  }