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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }