console
var $ = function (s) {
return document.querySelector(s)
}
var $$ = function (s) {
return document.querySelectorAll(s)
}
var on = function (obj, type, listener, usercapture) {
usercapture = usercapture ? false : usercapture
obj.addEventListener(type, listener, usercapture)
}
var off = function (obj, type, listener) {
obj.removeEventListener(type, listener)
}
var factories = {};
var define = function(modsname,factory) {
factories[modsname] = factory;
}
var require = function(mods,callback) {
let result = mods.map(function(name){
return factories[name]();
});
callback.apply(null,result);
}
var matchColor = (function () {
let keywords = ['break', 'else', 'new', 'var',
'case', 'finally', 'return', 'void',
'catch', 'for', 'switch', 'while',
'continue', 'function', 'with',
'default', 'if', 'throw', 'delete', 'in',
'try', 'do', 'instranceof', 'typeof', 'let',
'const', 'debugger', 'class', 'typeof', 'break'];
let identifier = /^[a-zA-Z_$]+[a-zA-Z0-9_$]*/;
let string = /[`'"]+[^`"']*[`'"]+/;
let number = /^[0-9]+$/;
function match(str) {
if (keywords.indexOf(str) != -1) {
return 'keyword';
} else if (number.test(str) || (str == 'true' || str == 'false')) {
return 'bool';
} else if (identifier.test(str)) {
return 'identifier';
} else if (string.test(str)) {
return 'str';
} else {
return 'common';
}
}
return match;
})();
var split = (function () {
let inline = {
identifier: /^[a-zA-Z_$]+[a-zA-Z0-9_$]+/,
str: /^[`'"]+[^`"']*[`'"]+/,
char: /^[^\~\!@#\$\%\^\&\*\(\)\_\-\+\=\[\]\{\}\:\;\<\,\>\.\?\/\\\|\s]+/,
space: /^\s+/,
punct: /^[\~\!@#\%\^\&\*\(\)\-\+\=\[\]\{\}\:\;\<\,\>\.\?\/\\\|]/,
}
function split(str) {
if (!str || typeof str != 'string') {
return false;
}
let substr = str
let cap = null
let arr = []
let len = 0;
let matches = '';
let n = 0;
while (substr) {
if ((cap = inline.identifier.exec(substr))) {
matches = cap[0]
len = matches.length
arr.push(matches)
substr = substr.slice(len)
}
if ((cap = inline.str.exec(substr))) {
matches = cap[0]
len = matches.length
arr.push(matches)
substr = substr.slice(len)
}
if ((cap = inline.char.exec(substr))) {
matches = cap[0]
len = matches.length
arr.push(matches)
substr = substr.slice(len)
}
if ((cap = inline.space.exec(substr))) {
matches = cap[0]
len = matches.length
arr.push(matches)
substr = substr.slice(len)
}
if ((cap = inline.punct.exec(substr))) {
matches = cap[0]
len = matches.length
arr.push(matches)
substr = substr.slice(len)
}
n++;
if (n > 500) {
break;
}
}
return arr;
}
return split;
})();
var parseElement = (function () {
function parse(ary) {
let arr = ary;
let elems = [];
for (let i = 0; i < arr.length; i++) {
let str = `<span class="${matchColor(arr[i])}">${arr[i].replace(/\s/g, ' ')}</span>`;
elems.push(str)
}
let line = `<div class="line"><span>${elems.join("")}</span></div>`;
return line;
}
return parse;
})();
var parseHTML = function (str) {
let arr = split(str);
return parseElement(arr);
}
var measurer = (function () {
let ctx = document.createElement('canvas').getContext('2d');
let object = {
measure(str) {
ctx.font = this.font;
return ctx.measureText(str).width;
},
font: '14px Consolas',
setFont(font) {
this.font = font;
}
}
return object;
})();
var code = (function () {
let codes = ['let a = 10;']
let substrfront = '';
let substrend = codes[0] ? codes[0] : '';
return {
initCode(callback) {
if (callback && typeof callback == 'function') {
callback();
}
},
getLineData(n, callback) {
if (callback && typeof callback == 'function') {
callback();
}
return codes[n]
},
setFront(str, callback) {
substrfront = str;
if (callback && typeof callback == 'function') {
callback();
}
},
setEnd(str, callback) {
substrend = str;
if (callback && typeof callback == 'function') {
callback();
}
},
getFront(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return substrfront;
},
getEnd(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return substrend;
},
writeLine(n, data, callback) {
codes[n] = data;
if (callback && typeof callback == 'function') {
callback();
}
}
}
})();
var cursor = (function () {
let cursor = $('.cursor');
let interval = null;
let timeout = null;
let col = 0;
let row = 0;
return {
initCursor(callback) {
if (callback && typeof callback == 'function') {
callback();
}
},
getCursor(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return cursor
},
setPositionX(x, callback) {
cursor.style.left = x + 'px'
if (callback && typeof callback == 'function') {
callback();
}
},
setPositionY(y, callback) {
cursor.style.top = y + 'px'
if (callback && typeof callback == 'function') {
callback();
}
},
setPosition(x, y, callback) {
cursor.style.left = x + 'px';
cursor.style.top = y + 'px';
if (callback && typeof callback == 'function') {
callback();
}
},
cursorHide(callback) {
clearInterval(interval);
cursor.style.visibility = 'hidden';
if (callback && typeof callback == 'function') {
callback();
}
},
cursorShow(callback) {
cursor.style.visibility = 'visible'
if (callback && typeof callback == 'function') {
callback();
}
},
start(callback) {
interval = setInterval(function () {
let ishide = cursor.style.visibility == 'hidden'
if (ishide) {
cursor.style.visibility = 'visible'
} else {
cursor.style.visibility = 'hidden'
}
}, 550);
if (callback && typeof callback == 'function') {
callback();
}
},
debounce(callback) {
clearInterval(interval)
clearTimeout(timeout)
let that = this;
this.cursorShow();
timeout = setTimeout(function () {
that.start()
}, 200);
if (callback && typeof callback == 'function') {
callback();
}
},
getRow(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return row;
},
getCol(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return col;
},
addCol(callback) {
col++;
if (callback && typeof callback == 'function') {
callback();
}
},
subCol(callback) {
col--;
if (callback && typeof callback == 'function') {
callback();
}
},
addRow(callback) {
row++;
if (callback && typeof callback == 'function') {
callback();
}
},
subRow(callback) {
row--;
if (callback && typeof callback == 'function') {
callback();
}
},
setCol(c, callback) {
col = c;
if (callback && typeof callback == 'function') {
callback();
}
},
setRow(r, callback) {
row = r;
if (callback && typeof callback == 'function') {
callback();
}
}
}
})();
var inputarea = (function () {
let inputarea = $('.inputarea');
return {
initInputarea(callback) {
if (callback && typeof callback == 'function') {
callback();
}
},
getInputElem(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return inputarea
},
focus(callback) {
if (callback && typeof callback == 'function') {
callback();
}
inputarea.focus();
},
blur(callback) {
if (callback && typeof callback == 'function') {
callback();
}
inputarea.blur();
},
setPositionX(x, callback) {
inputarea.style.left = x + 'px';
if (callback && typeof callback == 'function') {
callback();
}
},
setPositionY(y, callback) {
inputarea.style.top = y + 'px'
if (callback && typeof callback == 'function') {
callback();
}
},
setPosition(x, y, callback) {
inputarea.style.left = x + 'px';
inputarea.style.top = y + 'px';
if (callback && typeof callback == 'function') {
callback();
}
},
writeLine(data, callback) {
inputarea.value = data;
if (callback && typeof callback == 'function') {
callback();
}
},
setSelectionRange(start, end) {
inputarea.setSelectionRange(start, end);
},
getValue(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return inputarea.value;
},
getSelectionStart(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return inputarea.selectionStart;
}
}
})();
var codearea = (function () {
let lines = $('.lines');
let root = $('#root');
return {
getLinesElem(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return lines;
},
getRootElem(callback) {
if (callback && typeof callback == 'function') {
callback();
}
return root;
},
initCodearea(callback) {
if (callback && typeof callback == 'function') {
callback();
}
},
writeLine(data, callback) {
lines.innerHTML = data;
},
}
})();
function Main() {
cursor.initCursor(function () {
cursor.cursorHide()
})
codearea.initCodearea(function () {
let lines = codearea.getLinesElem()
let root = codearea.getRootElem()
function eventInit() {
on(root, 'click', function () {
inputarea.focus()
});
}
eventInit();
function codeInit() {
let line = parseHTML(code.getLineData(0));
lines.innerHTML = line;
}
codeInit();
});
inputarea.initInputarea(function () {
let row = cursor.getRow();
let data = code.getLineData(row);
inputarea.writeLine(data)
function eventInit() {
let inputElem = inputarea.getInputElem();
let moveInline = function () {
let row = cursor.getRow();
let col = cursor.getCol();
let line = code.getLineData(row);
if (col > line.length) {
cursor.setCol(line.length);
}
if (col < 0) {
cursor.setCol(0);
}
row = cursor.getRow();
col = cursor.getCol();
line = code.getLineData(row);
let frontstr = line.slice(0, col);
let endstr = line.slice(col);
code.setEnd(endstr);
code.setFront(frontstr, function () {
let frontwidth = measurer.measure(code.getFront());
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
inputarea.setSelectionRange(col, col);
});
}
let backspace = function () {
let front = code.getFront();
let end = code.getEnd();
let row = cursor.getRow();
front = front.slice(0, front.length - 1);
code.setFront(front);
let data = front + end;
code.writeLine(row, data);
let line = parseHTML(code.getLineData(row));
codearea.writeLine(line)
inputarea.writeLine(data);
cursor.subCol(function () {
let frontwidth = measurer.measure(code.getFront());
let col = cursor.getCol();
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
inputarea.setSelectionRange(col, col);
});
}
let input = function () {
let value = inputarea.getValue();
let row = cursor.getRow();
let col = inputarea.getSelectionStart();
cursor.setCol(col, function () {
let col = cursor.getCol();
let front = value.slice(0, col);
code.setFront(front);
let frontwidth = measurer.measure(code.getFront());
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
});
code.writeLine(row, value);
codearea.writeLine(parseHTML(value));
}
let del = function () {
let front = code.getFront();
let end = code.getEnd();
let row = cursor.getRow();
end = end.slice(1);
code.setEnd(end);
let data = front + end;
code.writeLine(row, data);
let line = parseHTML(code.getLineData(row));
codearea.writeLine(line)
inputarea.writeLine(data);
let frontwidth = measurer.measure(code.getFront());
let col = cursor.getCol();
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
inputarea.setSelectionRange(col, col);
}
let end = function () {
let row = cursor.getRow();
let len = code.getLineData(row).length;
cursor.setCol(len, function () {
let col = cursor.getCol();
let line = code.getLineData(row);
code.setFront(line);
code.setEnd("");
let frontwidth = measurer.measure(code.getFront());
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
inputarea.setSelectionRange(col, col);
})
}
let home = function () {
let row = cursor.getRow();
cursor.setCol(0, function () {
let col = cursor.getCol();
let line = code.getLineData(row);
code.setFront("");
code.setEnd(line);
let frontwidth = measurer.measure(code.getFront());
cursor.setPositionX(frontwidth);
inputarea.setPositionX(frontwidth);
inputarea.setSelectionRange(col, col);
})
}
on(inputElem, 'input', function () {
cursor.debounce()
input();
})
on(inputElem, 'focus', function () {
cursor.cursorShow()
cursor.start(function () {
let col = cursor.getCol();
inputarea.setSelectionRange(col, col);
});
})
on(inputElem, 'blur', function () {
cursor.cursorHide()
});
on(inputElem, 'keydown', function (e) {
let keys = [
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
'Home', 'End', 'Backspace', 'Delete', 'Enter'
]
let key = e.key
if (keys.includes(key)) {
e.preventDefault()
}
});
on(inputElem, 'mousedown', function (e) {
e.preventDefault()
})
on(inputElem, 'keydown', function (e) {
let key = e.key;
if (key == 'ArrowLeft') {
cursor.debounce();
cursor.subCol(moveInline);
} else if (key == 'ArrowRight') {
cursor.debounce();
cursor.addCol(moveInline);
} else if (key == 'ArrowUp') {
cursor.debounce();
} else if (key == 'ArrowDown') {
cursor.debounce();
}
});
on(inputElem, 'keydown', function (e) {
let key = e.key;
if (key == 'Backspace') {
cursor.debounce();
backspace();
} else if (key == 'Delete') {
cursor.debounce();
del();
} else if (key == 'End') {
cursor.debounce();
end();
} else if (key == 'Home') {
cursor.debounce();
home();
}
});
}
eventInit();
});
}
Main()
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=">
<meta http-equiv="X-UA-Compatible" content="">
<title></title>
</head>
<body>
<div id="root">
<div class="lines"></div>
<textarea name="" class="inputarea" spellcheck="false" wrap="off" cols="" rows=""></textarea>
<div class="cursor"></div>
</div>
</body>
</html>
* {
margin: 0;
padding: 0;
}
#root {
font-size: 14px;
overflow: hidden;
position: absolute;
width: 100%;
height: 100%;
background: rgb(40, 44, 52);
line-height: 20px;
font-family: Consolas;
}
.inputarea {
position: absolute;
top: 0;
left: 0;
resize: none;
box-sizing: border-box;
height: 20px;
width: 1px;
opacity: 0;
}
.cursor {
position: absolute;
left: 0;
top: 0;
width: 0px;
height: 20px;
border-left: 2px solid red;
box-sizing: border-box;
}
.lines .line {
user-select: none;
white-space: nowrap;
height: 20px;
cursor: text;
}
.keyword {
color: #c678dd;
}
.identifier {
color: #e06c75;
}
.bool {
color: #d19a66;
}
.str {
color: #98c379;
}
.common {
color: #abb2bf;
}