Add project files
This commit is contained in:
commit
ba5971a788
37 files changed
+11180
No files matched your search
@@ -0,0 +1,476 @@
|
||||
|
||||
/* ================================================================== editor
|
||||
* A plain <textarea> sits on top of a <pre> that paints the same text with
|
||||
* syntax colors and marks. Both have identical font metrics and size, so the
|
||||
* textarea provides the caret, selection, IME and native undo while the <pre>
|
||||
* provides the colors. One editor (and so one undo history) per tab. */
|
||||
|
||||
var CLS = ['', 't-com', 't-str', 't-set', 't-act', 't-ann', 't-kw', 't-tok', 't-def', 't-lab', 't-op', 't-tokdef'];
|
||||
var MKCLS = [[1, 'mk-err'], [2, 'mk-warn'], [4, 'mk-hl'], [8, 'mk-find'], [16, 'mk-cur']];
|
||||
var KW = { grammar: 1, lexer: 1, parser: 1, fragment: 1, options: 1, tokens: 1, channels: 1, 'import': 1, mode: 1, returns: 1,
|
||||
locals: 1, throws: 1, 'catch': 1, 'finally': 1, skip: 1, more: 1, pushMode: 1, popMode: 1, type: 1, channel: 1 };
|
||||
|
||||
function isIdStart(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '_'; }
|
||||
function isIdChar(c) { return isIdStart(c) || (c >= '0' && c <= '9'); }
|
||||
|
||||
// One class id per UTF-16 unit, for ANTLR grammar syntax.
|
||||
function g4Classes(t) {
|
||||
var n = t.length, cls = new Uint8Array(n), i = 0, prevKw = '';
|
||||
function paint(a, b, c) { for (var k = a; k < b; k++) cls[k] = c; }
|
||||
function quoted(from, close) {
|
||||
var j = from + 1;
|
||||
while (j < n && t.charAt(j) !== '\n') {
|
||||
if (t.charAt(j) === '\\') { j += 2; continue; }
|
||||
if (t.charAt(j) === close) { j++; break; }
|
||||
j++;
|
||||
}
|
||||
return Math.min(j, n);
|
||||
}
|
||||
while (i < n) {
|
||||
var c = t.charAt(i), j;
|
||||
if (c === '/' && t.charAt(i + 1) === '/') {
|
||||
j = t.indexOf('\n', i); if (j < 0) j = n;
|
||||
paint(i, j, 1); i = j;
|
||||
} else if (c === '/' && t.charAt(i + 1) === '*') {
|
||||
j = t.indexOf('*/', i + 2); j = j < 0 ? n : j + 2;
|
||||
paint(i, j, 1); i = j;
|
||||
} else if (c === "'") {
|
||||
j = quoted(i, "'"); paint(i, j, 2); i = j;
|
||||
} else if (c === '[') {
|
||||
j = quoted(i, ']'); paint(i, j, 3); i = j;
|
||||
} else if (c === '{') {
|
||||
var depth = 0; j = i;
|
||||
while (j < n) {
|
||||
var d = t.charAt(j);
|
||||
if (d === "'" || d === '"') { j = quoted(j, d); continue; }
|
||||
if (d === '{') depth++;
|
||||
else if (d === '}' && --depth === 0) { j++; break; }
|
||||
j++;
|
||||
}
|
||||
paint(i, j, 4); i = j;
|
||||
} else if (c === '@' || c === '#') {
|
||||
j = i + 1;
|
||||
while (j < n && (t.charAt(j) === ' ' || t.charAt(j) === '\t') && c === '#') j++;
|
||||
while (j < n && (isIdChar(t.charAt(j)) || t.charAt(j) === ':')) j++;
|
||||
paint(i, j, 5); i = j;
|
||||
} else if (isIdStart(c)) {
|
||||
j = i + 1;
|
||||
while (j < n && isIdChar(t.charAt(j))) j++;
|
||||
var word = t.slice(i, j);
|
||||
var k = j;
|
||||
while (k < n && (t.charAt(k) === ' ' || t.charAt(k) === '\t' || t.charAt(k) === '\n' || t.charAt(k) === '\r')) k++;
|
||||
var next = t.charAt(k), after = t.charAt(k + 1);
|
||||
var ls = t.lastIndexOf('\n', i - 1) + 1;
|
||||
var first = /^[ \t]*$/.test(t.slice(ls, i));
|
||||
var def = next === ':' && after !== ':' && (first || prevKw === 'fragment');
|
||||
var c0 = word.charAt(0);
|
||||
var up = c0 >= 'A' && c0 <= 'Z';
|
||||
var id;
|
||||
if (KW[word] === 1 && !def && next !== ':') id = 6;
|
||||
else if (def) id = up ? 11 : 8;
|
||||
else if ((next === '=' && after !== '=') || (next === '+' && after === '=')) id = 9;
|
||||
else id = up ? 7 : 0;
|
||||
paint(i, j, id);
|
||||
prevKw = word;
|
||||
i = j;
|
||||
} else {
|
||||
if ('|:;()*+?~.,<>=-'.indexOf(c) >= 0) cls[i] = 10;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
// code point index -> UTF-16 index
|
||||
function cpMap(text) {
|
||||
if (!/[\ud800-\udbff]/.test(text)) return function (i) { return i; };
|
||||
var map = [];
|
||||
for (var i = 0; i < text.length;) {
|
||||
map.push(i);
|
||||
var c = text.charCodeAt(i);
|
||||
i += (c >= 0xd800 && c <= 0xdbff && i + 1 < text.length) ? 2 : 1;
|
||||
}
|
||||
map.push(text.length);
|
||||
return function (k) { return k < map.length ? map[k] : text.length; };
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------- line helpers */
|
||||
function lineStarts(ed) {
|
||||
if (!ed.ls) {
|
||||
var t = ed.ta.value, a = [0];
|
||||
for (var i = t.indexOf('\n'); i >= 0; i = t.indexOf('\n', i + 1)) a.push(i + 1);
|
||||
ed.ls = a;
|
||||
}
|
||||
return ed.ls;
|
||||
}
|
||||
function lineOf(ed, idx) {
|
||||
var a = lineStarts(ed), lo = 0, hi = a.length - 1;
|
||||
while (lo < hi) {
|
||||
var mid = (lo + hi + 1) >> 1;
|
||||
if (a[mid] <= idx) lo = mid; else hi = mid - 1;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
// 1-based line + 0-based column -> unit index (column clamped to the line)
|
||||
function unitAt(ed, line, col) {
|
||||
var a = lineStarts(ed);
|
||||
if (line < 1) return 0;
|
||||
if (line > a.length) return ed.ta.value.length;
|
||||
var s = a[line - 1], e = line < a.length ? a[line] - 1 : ed.ta.value.length;
|
||||
return Math.min(s + col, e);
|
||||
}
|
||||
|
||||
var metricCache = {};
|
||||
function metrics(ed) {
|
||||
var key = S.prefs.fs;
|
||||
if (metricCache[key]) return metricCache[key];
|
||||
var probe = el('span', '', '0000000000000000000000000000000000000000');
|
||||
probe.style.position = 'absolute';
|
||||
probe.style.visibility = 'hidden';
|
||||
probe.style.whiteSpace = 'pre';
|
||||
probe.className = 'hl';
|
||||
probe.style.padding = '0';
|
||||
probe.style.minHeight = '0';
|
||||
ed.hl.parentNode.appendChild(probe);
|
||||
var w = probe.getBoundingClientRect().width / 40;
|
||||
probe.remove();
|
||||
var lh = parseFloat(getComputedStyle(ed.ta).lineHeight) || 20;
|
||||
metricCache[key] = { cw: w || 8, lh: lh };
|
||||
return metricCache[key];
|
||||
}
|
||||
|
||||
// Scrolls the editor so that the given unit index is visible.
|
||||
function revealUnit(ed, idx) {
|
||||
var m = metrics(ed), sc = ed.scroller;
|
||||
var line = lineOf(ed, idx);
|
||||
var ls = lineStarts(ed)[line];
|
||||
var col = 0, pre = ed.ta.value.slice(ls, idx);
|
||||
for (var i = 0; i < pre.length; i++) col = pre.charAt(i) === '\t' ? (Math.floor(col / 4) + 1) * 4 : col + 1;
|
||||
var y = 8 + line * m.lh;
|
||||
var gw = ed.gutter.offsetWidth;
|
||||
var x = gw + 10 + col * m.cw;
|
||||
if (y < sc.scrollTop + m.lh) sc.scrollTop = Math.max(0, y - m.lh * 2);
|
||||
else if (y + m.lh * 2 > sc.scrollTop + sc.clientHeight) sc.scrollTop = y + m.lh * 3 - sc.clientHeight;
|
||||
if (x < sc.scrollLeft + gw + 30) sc.scrollLeft = Math.max(0, x - gw - 60);
|
||||
else if (x + 40 > sc.scrollLeft + sc.clientWidth) sc.scrollLeft = x + 80 - sc.clientWidth;
|
||||
}
|
||||
|
||||
function caretInfo(ed) {
|
||||
var p = ed.ta.selectionEnd, line = lineOf(ed, p);
|
||||
return { line: line + 1, col: p - lineStarts(ed)[line] + 1 };
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- painting */
|
||||
function paintEditor(ed) {
|
||||
var t = ed.ta.value, n = t.length;
|
||||
var big = n > 800000;
|
||||
var cls = (ed.tab.kind === 'grammar' && !big) ? g4Classes(t) : null;
|
||||
var mk = new Uint8Array(n + 1);
|
||||
function mark(list, bit) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var r = list[i];
|
||||
for (var k = Math.max(0, r.s); k < Math.min(n, r.e); k++) mk[k] |= (r.bit || bit);
|
||||
}
|
||||
}
|
||||
if (!big) {
|
||||
mark(ed.diag, 1);
|
||||
if (ed.find) {
|
||||
for (var f = 0; f < ed.find.matches.length && f < 5000; f++) {
|
||||
var fm = ed.find.matches[f];
|
||||
for (var q = fm[0]; q < fm[1]; q++) mk[q] |= (f === ed.find.cur ? 16 : 8);
|
||||
}
|
||||
}
|
||||
var rng = ed.hover || ed.pin;
|
||||
if (rng) for (var h = Math.max(0, rng[0]); h < Math.min(n, rng[1]); h++) mk[h] |= 4;
|
||||
}
|
||||
|
||||
var frag = document.createDocumentFragment();
|
||||
var i = 0;
|
||||
while (i < n) {
|
||||
var key = (cls ? cls[i] : 0) | (mk[i] << 4), j = i + 1;
|
||||
while (j < n && ((cls ? cls[j] : 0) | (mk[j] << 4)) === key) j++;
|
||||
var txt = t.slice(i, j);
|
||||
if (key === 0) {
|
||||
frag.appendChild(document.createTextNode(txt));
|
||||
} else {
|
||||
var names = [];
|
||||
if (key & 15) names.push(CLS[key & 15]);
|
||||
for (var m = 0; m < MKCLS.length; m++) if ((key >> 4) & MKCLS[m][0]) names.push(MKCLS[m][1]);
|
||||
frag.appendChild(el('span', names.join(' '), txt));
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
frag.appendChild(document.createTextNode('\n')); // matches the textarea's trailing line
|
||||
clear(ed.hl);
|
||||
ed.hl.appendChild(frag);
|
||||
|
||||
var lines = lineStarts(ed).length;
|
||||
if (lines !== ed.lines) {
|
||||
var nums = [];
|
||||
for (var L = 1; L <= lines; L++) nums.push(L);
|
||||
ed.gutter.textContent = nums.join('\n') + '\n';
|
||||
ed.lines = lines;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- editing */
|
||||
function replaceRange(ed, a, b, text) {
|
||||
var ta = ed.ta;
|
||||
ta.focus();
|
||||
ta.setSelectionRange(a, b);
|
||||
var ok = false;
|
||||
// execCommand keeps the undo history but is very slow for big multi-line
|
||||
// strings in Chrome, so large edits take the direct route.
|
||||
if (text.length + (b - a) <= 20000) {
|
||||
try { ok = document.execCommand('insertText', false, text); } catch (e) { ok = false; }
|
||||
}
|
||||
if (!ok) {
|
||||
ta.setRangeText(text, a, b, 'end');
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
}
|
||||
|
||||
function lineSpan(t, a, b) {
|
||||
if (b > a && t.charAt(b - 1) === '\n') b--;
|
||||
var s = t.lastIndexOf('\n', a - 1) + 1;
|
||||
var e = t.indexOf('\n', b);
|
||||
if (e < 0) e = t.length;
|
||||
return [s, e];
|
||||
}
|
||||
|
||||
function indentSelection(ed, outdent) {
|
||||
var ta = ed.ta, t = ta.value, a = ta.selectionStart, b = ta.selectionEnd;
|
||||
var unit = ed.tab.kind === 'grammar' ? ' ' : '\t';
|
||||
if (a === b && !outdent) {
|
||||
if (unit === '\t') { replaceRange(ed, a, b, '\t'); return; }
|
||||
var col = a - (t.lastIndexOf('\n', a - 1) + 1);
|
||||
replaceRange(ed, a, b, unit.slice(0, 4 - (col % 4)));
|
||||
return;
|
||||
}
|
||||
var sp = lineSpan(t, a, b);
|
||||
var lines = t.slice(sp[0], sp[1]).split('\n').map(function (l) {
|
||||
if (!outdent) return l.length ? unit + l : l;
|
||||
if (l.charAt(0) === '\t') return l.slice(1);
|
||||
var m = /^ {1,4}/.exec(l);
|
||||
return m ? l.slice(m[0].length) : l;
|
||||
});
|
||||
var out = lines.join('\n');
|
||||
replaceRange(ed, sp[0], sp[1], out);
|
||||
ed.ta.setSelectionRange(sp[0], sp[0] + out.length);
|
||||
}
|
||||
|
||||
function toggleComment(ed) {
|
||||
if (ed.tab.kind !== 'grammar') return;
|
||||
var ta = ed.ta, t = ta.value, sp = lineSpan(t, ta.selectionStart, ta.selectionEnd);
|
||||
var lines = t.slice(sp[0], sp[1]).split('\n');
|
||||
var live = lines.filter(function (l) { return l.trim().length; });
|
||||
var all = live.length && live.every(function (l) { return /^\s*\/\//.test(l); });
|
||||
var indent = Math.min.apply(null, live.map(function (l) { return /^\s*/.exec(l)[0].length; }).concat([1e9]));
|
||||
var out = lines.map(function (l) {
|
||||
if (!l.trim().length) return l;
|
||||
return all ? l.replace(/^(\s*)\/\/ ?/, '$1') : l.slice(0, indent) + '// ' + l.slice(indent);
|
||||
}).join('\n');
|
||||
replaceRange(ed, sp[0], sp[1], out);
|
||||
ta.setSelectionRange(sp[0], sp[0] + out.length);
|
||||
}
|
||||
|
||||
function editorKeys(ed, ev) {
|
||||
var ta = ed.ta, k = ev.key, mod = ev.ctrlKey || ev.metaKey;
|
||||
if (k === 'Tab' && !mod && !ev.altKey) { ev.preventDefault(); indentSelection(ed, ev.shiftKey); return; }
|
||||
if (k === 'Enter' && !mod && !ev.altKey && !ev.shiftKey) {
|
||||
ev.preventDefault();
|
||||
var a = ta.selectionStart, t = ta.value, ls = t.lastIndexOf('\n', a - 1) + 1;
|
||||
var lead = /^[ \t]*/.exec(t.slice(ls, a))[0];
|
||||
var before = t.slice(ls, a).replace(/\s+$/, '');
|
||||
var extra = (ed.tab.kind === 'grammar' && /[:({]$/.test(before)) ? ' ' : '';
|
||||
replaceRange(ed, a, ta.selectionEnd, '\n' + lead + extra);
|
||||
return;
|
||||
}
|
||||
if (mod && k === '/') { ev.preventDefault(); toggleComment(ed); return; }
|
||||
if (mod && (k === 'f' || k === 'F' || k === 'h' || k === 'H')) { ev.preventDefault(); openFind(ed); return; }
|
||||
if (k === 'Escape' && ed.findBar && !ed.findBar.hidden) { ev.preventDefault(); closeFind(ed); }
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- find */
|
||||
function buildFind(ed) {
|
||||
var bar = el('div', 'find');
|
||||
bar.setAttribute('role', 'search');
|
||||
var r1 = el('div', 'row'), r2 = el('div', 'row');
|
||||
var q = el('input'); q.type = 'text'; q.placeholder = 'Find'; q.setAttribute('aria-label', 'Find');
|
||||
var cnt = el('span', 'cnt');
|
||||
var bPrev = el('button', 'btn sm', '\u2191'); bPrev.title = 'Previous (Shift+Enter)';
|
||||
var bNext = el('button', 'btn sm', '\u2193'); bNext.title = 'Next (Enter)';
|
||||
var bCase = el('button', 'btn sm', 'Aa'); bCase.title = 'Match case'; bCase.setAttribute('aria-pressed', 'false');
|
||||
var bX = el('button', 'btn sm', '\u00d7'); bX.title = 'Close (Esc)';
|
||||
var rep = el('input'); rep.type = 'text'; rep.placeholder = 'Replace'; rep.setAttribute('aria-label', 'Replace with');
|
||||
var bRep = el('button', 'btn sm', 'Replace');
|
||||
var bAll = el('button', 'btn sm', 'All');
|
||||
[bPrev, bNext, bCase, bX, bRep, bAll].forEach(function (b) { b.type = 'button'; });
|
||||
[q, bPrev, bNext, bCase, cnt, bX].forEach(function (n) { r1.appendChild(n); });
|
||||
[rep, bRep, bAll].forEach(function (n) { r2.appendChild(n); });
|
||||
bar.appendChild(r1); bar.appendChild(r2);
|
||||
ed.root.appendChild(bar);
|
||||
ed.findBar = bar;
|
||||
ed.fq = q; ed.fr = rep; ed.fcnt = cnt; ed.fcase = false;
|
||||
|
||||
q.oninput = function () { runFind(ed, true); };
|
||||
q.onkeydown = function (ev) {
|
||||
if (ev.key === 'Enter') { ev.preventDefault(); stepFind(ed, ev.shiftKey ? -1 : 1); }
|
||||
else if (ev.key === 'Escape') { ev.preventDefault(); closeFind(ed); }
|
||||
};
|
||||
rep.onkeydown = function (ev) {
|
||||
if (ev.key === 'Enter') { ev.preventDefault(); replaceOne(ed); }
|
||||
else if (ev.key === 'Escape') { ev.preventDefault(); closeFind(ed); }
|
||||
};
|
||||
bPrev.onclick = function () { stepFind(ed, -1); };
|
||||
bNext.onclick = function () { stepFind(ed, 1); };
|
||||
bCase.onclick = function () {
|
||||
ed.fcase = !ed.fcase;
|
||||
bCase.setAttribute('aria-pressed', ed.fcase ? 'true' : 'false');
|
||||
runFind(ed, true);
|
||||
};
|
||||
bX.onclick = function () { closeFind(ed); };
|
||||
bRep.onclick = function () { replaceOne(ed); };
|
||||
bAll.onclick = function () { replaceAll(ed); };
|
||||
}
|
||||
|
||||
function openFind(ed) {
|
||||
if (!ed.findBar) buildFind(ed);
|
||||
ed.findBar.hidden = false;
|
||||
var sel = ed.ta.value.slice(ed.ta.selectionStart, ed.ta.selectionEnd);
|
||||
if (sel && sel.indexOf('\n') < 0) ed.fq.value = sel;
|
||||
ed.fq.focus();
|
||||
ed.fq.select();
|
||||
runFind(ed, false);
|
||||
}
|
||||
|
||||
function closeFind(ed) {
|
||||
if (!ed.findBar) return;
|
||||
ed.findBar.hidden = true;
|
||||
ed.find = null;
|
||||
paintEditor(ed);
|
||||
ed.ta.focus();
|
||||
}
|
||||
|
||||
function findMatches(ed) {
|
||||
var q = ed.fq.value, out = [];
|
||||
if (!q) return out;
|
||||
var hay = ed.fcase ? ed.ta.value : ed.ta.value.toLowerCase();
|
||||
var needle = ed.fcase ? q : q.toLowerCase();
|
||||
for (var i = hay.indexOf(needle); i >= 0 && out.length < 5000; i = hay.indexOf(needle, i + Math.max(1, needle.length))) {
|
||||
out.push([i, i + needle.length]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function runFind(ed, jump) {
|
||||
var ms = findMatches(ed), cur = -1;
|
||||
if (ms.length) {
|
||||
var from = ed.ta.selectionStart;
|
||||
for (var i = 0; i < ms.length; i++) { if (ms[i][0] >= from) { cur = i; break; } }
|
||||
if (cur < 0) cur = 0;
|
||||
}
|
||||
ed.find = ms.length || ed.fq.value ? { matches: ms, cur: cur } : null;
|
||||
ed.fcnt.textContent = ed.fq.value ? (ms.length ? (cur + 1) + ' of ' + ms.length : 'No results') : '';
|
||||
paintEditor(ed);
|
||||
if (jump && cur >= 0) selectMatch(ed, ms[cur]);
|
||||
}
|
||||
|
||||
function selectMatch(ed, m) {
|
||||
ed.ta.setSelectionRange(m[0], m[1]);
|
||||
revealUnit(ed, m[0]);
|
||||
}
|
||||
|
||||
function stepFind(ed, dir) {
|
||||
if (!ed.find || !ed.find.matches.length) { runFind(ed, true); return; }
|
||||
var n = ed.find.matches.length;
|
||||
ed.find.cur = (ed.find.cur + dir + n) % n;
|
||||
ed.fcnt.textContent = (ed.find.cur + 1) + ' of ' + n;
|
||||
paintEditor(ed);
|
||||
selectMatch(ed, ed.find.matches[ed.find.cur]);
|
||||
}
|
||||
|
||||
function replaceOne(ed) {
|
||||
if (!ed.find || ed.find.cur < 0) { runFind(ed, true); return; }
|
||||
var m = ed.find.matches[ed.find.cur];
|
||||
replaceRange(ed, m[0], m[1], ed.fr.value);
|
||||
ed.fq.focus();
|
||||
runFind(ed, true);
|
||||
}
|
||||
|
||||
function replaceAll(ed) {
|
||||
var ms = findMatches(ed);
|
||||
if (!ms.length) return;
|
||||
var t = ed.ta.value, out = '', last = 0;
|
||||
ms.forEach(function (m) { out += t.slice(last, m[0]) + ed.fr.value; last = m[1]; });
|
||||
out += t.slice(last);
|
||||
replaceRange(ed, 0, t.length, out);
|
||||
ed.fq.focus();
|
||||
runFind(ed, false);
|
||||
toast('Replaced ' + plural(ms.length, 'match'));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ editor */
|
||||
function makeEditor(tab) {
|
||||
var root = el('div', 'ed');
|
||||
root.hidden = true;
|
||||
var banner = el('div', 'banner');
|
||||
banner.hidden = true;
|
||||
var scroller = el('div', 'scroller');
|
||||
var rows = el('div', 'rows');
|
||||
var gutter = el('pre', 'gutter');
|
||||
gutter.setAttribute('aria-hidden', 'true');
|
||||
var content = el('div', 'content');
|
||||
var hl = el('pre', 'hl');
|
||||
hl.setAttribute('aria-hidden', 'true');
|
||||
var ta = el('textarea', 'ta');
|
||||
ta.spellcheck = false;
|
||||
ta.setAttribute('wrap', 'off');
|
||||
ta.setAttribute('autocomplete', 'off');
|
||||
ta.setAttribute('autocorrect', 'off');
|
||||
ta.setAttribute('autocapitalize', 'off');
|
||||
ta.setAttribute('aria-label', 'Editor');
|
||||
content.appendChild(hl);
|
||||
content.appendChild(ta);
|
||||
rows.appendChild(gutter);
|
||||
rows.appendChild(content);
|
||||
scroller.appendChild(rows);
|
||||
root.appendChild(banner);
|
||||
root.appendChild(scroller);
|
||||
|
||||
var ed = { tab: tab, root: root, banner: banner, scroller: scroller, gutter: gutter, hl: hl, ta: ta,
|
||||
diag: [], hover: null, pin: null, find: null, ls: null, lines: 0, findBar: null, onInput: null, onCaret: null };
|
||||
|
||||
ta.addEventListener('input', function () {
|
||||
ed.ls = null;
|
||||
ed.diag = [];
|
||||
ed.pin = null;
|
||||
if (ed.find) runFind(ed, false); else paintEditor(ed);
|
||||
revealUnit(ed, ta.selectionEnd);
|
||||
if (ed.onInput) ed.onInput(ed);
|
||||
if (ed.onCaret) ed.onCaret(ed);
|
||||
});
|
||||
ta.addEventListener('keydown', function (ev) { editorKeys(ed, ev); });
|
||||
['keyup', 'mouseup', 'focus'].forEach(function (name) {
|
||||
ta.addEventListener(name, function (ev) {
|
||||
if (name === 'keyup' && /^(Arrow|Page|Home|End)/.test(ev.key)) revealUnit(ed, ta.selectionEnd);
|
||||
if (ed.onCaret) ed.onCaret(ed);
|
||||
});
|
||||
});
|
||||
return ed;
|
||||
}
|
||||
|
||||
function setEditorText(ed, text) {
|
||||
ed.ta.value = text;
|
||||
ed.ls = null;
|
||||
ed.diag = [];
|
||||
ed.pin = null;
|
||||
ed.find = null;
|
||||
if (ed.findBar) { ed.findBar.hidden = true; }
|
||||
paintEditor(ed);
|
||||
}
|
||||
|
||||
function setEditorRange(ed, kind, range) { // kind: 'hover' | 'pin'; range [s,e) in units or null
|
||||
ed[kind] = range;
|
||||
paintEditor(ed);
|
||||
}
|
||||
Reference in new issue
Block a user