wscode
Version:
🎉 An Editor Used on the Browser Side.
437 lines (314 loc) • 14.7 kB
JavaScript
import keyString from '@yelloxing/core.js/tools/keyString';
import isFunction from '@yelloxing/core.js/isFunction';
import xhtml from '../xhtml';
import { getInputMessage } from './tool';
// 绑定键盘和鼠标等交互事件处理
export default function () {
let mouseDown = false;
// 辅助计算选择光标位置
let calcCursor = (event) => {
let position = xhtml.position(this._el, event);
let topIndex = Math.round((position.y - 20.5) / 21);
if (topIndex < 0) topIndex = 0;
if (topIndex >= this._contentArray.length) topIndex = this._contentArray.length - 1;
return {
leftNum: this.$$bestLeftNum(position.x, topIndex),
lineNum: topIndex
};
};
// 获取光标之间的内容
let calcTwoCursor = () => {
// 假定cursor2是结束光标
let beginCursor = this.__cursor2, endCursor = this.__cursor1;
// 根据行号来校对
if (this.__cursor1.lineNum < this.__cursor2.lineNum) {
beginCursor = this.__cursor1; endCursor = this.__cursor2;
} else if (this.__cursor1.lineNum == this.__cursor2.lineNum) {
// 根据列号来校对
if (this.__cursor1.leftNum < this.__cursor2.leftNum) {
beginCursor = this.__cursor1; endCursor = this.__cursor2;
}
return this._contentArray[beginCursor.lineNum].substring(beginCursor.leftNum, endCursor.leftNum);
}
// 余下的一定是多行
let resultData = "";
resultData += this._contentArray[beginCursor.lineNum].substr(beginCursor.leftNum) + "\n";
for (let lineNum = beginCursor.lineNum + 1; lineNum < endCursor.lineNum; lineNum++) {
resultData += this._contentArray[lineNum] + "\n";
}
resultData += this._contentArray[endCursor.lineNum].substr(0, endCursor.leftNum);
return resultData;
};
// 鼠标按下的时候,记录开始光标位置并标记鼠标按下动作
xhtml.bind(this._el, 'mousedown', event => {
mouseDown = true;
this.__cursor2 = this.__cursor1 = calcCursor(event);
this.$$updateCanvasSize();
// 绘制选中效果
this.$$updateSelectView();
});
// 移动的时候不停的同步结束光标位置
xhtml.bind(this._el, 'mousemove', event => {
if (!mouseDown) return;
this.__cursor2 = calcCursor(event);
// 绘制选中效果
this.$$updateSelectView();
});
// 鼠标分开或移出的时候,标记鼠标放开
xhtml.bind(this._el, 'mouseup', () => mouseDown = false);
xhtml.bind(this._el, 'mouseout', () => mouseDown = false);
// 点击编辑界面
xhtml.bind(this._el, 'click', event => {
this.__helpInputDOM.innerHTML = '';
let position = xhtml.position(this._el, event);
let topIndex = Math.round((position.y - 20.5) / 21);
// 如果超过了内容区域
if (topIndex < 0 || topIndex >= this._contentArray.length) return;
this.__lineNum = topIndex;
this.__leftNum = this.$$bestLeftNum(position.x);
this.$$updateCursorPosition();
this.$$updateView();
});
let update = text => {
// 获取输入内容
text = text || this.__focusDOM.value;
text = this.$$filterText(text);
this.__focusDOM.value = "";
// 如果有选区,先删除选区
if (this.$$selectIsNotBlank()) this.$$deleteSelect();
// 如果输入的是回车,切割文本
if (/^\n$/.test(text)) {
if (this.__leftNum >= this._contentArray[this.__lineNum].length) {
this._contentArray.splice(this.__lineNum + 1, 0, "");
} else {
this._contentArray.splice(this.__lineNum + 1, 0, this._contentArray[this.__lineNum].substring(this.__leftNum));
this._contentArray[this.__lineNum] = this._contentArray[this.__lineNum].substring(0, this.__leftNum);
}
this.__lineNum += 1;
this.__leftNum = 0;
}
// 否则就是一堆文本(包括复制来的)
else {
let textArray = text.split(/\n/);
// 如果只有一行文本(分开是为了加速)
if (textArray.length <= 1) {
this._contentArray[this.__lineNum] = this._contentArray[this.__lineNum].substring(0, this.__leftNum) + text + this._contentArray[this.__lineNum].substring(this.__leftNum);
this.__leftNum += text.length;
}
// 如果是复制的多行文本
else {
// 需要切割的行两边文本
let leftText = this._contentArray[this.__lineNum].substring(0, this.__leftNum);
let rightText = this._contentArray[this.__lineNum].substring(this.__leftNum);
// 旧行文本拼接进来
textArray[0] = leftText + textArray[0];
textArray[textArray.length - 1] += rightText;
// 新内容记录下来
this._contentArray.splice(this.__lineNum, 1, ...textArray);
this.__lineNum += (textArray.length - 1);
this.__leftNum = textArray[textArray.length - 1].length - rightText.length;
}
}
// 着色并更新视图
this.__formatData = this.$$diff(this.$shader(this._contentArray.join('\n')));
this.$$updateCursorPosition();
this.$$updateView();
// 通知文本改动
this.__updated__();
};
// 中文输入开始
xhtml.bind(this.__focusDOM, 'compositionstart', () => {
this.__needUpdate = false;
this.__focusDOM.style.color = "rgba(0,0,0,0)";
this.__focusDOM.style.borderLeft = '1px solid ' + this._colorCursor;
});
// 中文输入结束
xhtml.bind(this.__focusDOM, 'compositionend', () => {
this.__needUpdate = true;
this.__focusDOM.style.color = this._colorCursor;
this.__focusDOM.style.borderLeft = "none";
update();
// 辅助输入
if (this.$input != null) this.__helpInputEvent = this.$input(this.__helpInputDOM, getInputMessage(this), this._contentArray) || {};
});
// 输入
xhtml.bind(this.__focusDOM, 'input', () => {
// 如果是中文输入开始,不应该更新
if (this.__needUpdate) {
update();
// 辅助输入
if (this.$input != null) this.__helpInputEvent = this.$input(this.__helpInputDOM, getInputMessage(this), this._contentArray) || {};
}
});
// 处理键盘控制
xhtml.bind(this._el, 'keydown', event => {
let keyStringCode = keyString(event);
// 辅助输入前置拦截
if (this.__helpInputDOM.innerHTML != '') {
let __helpInputEvent = this.__helpInputEvent[keyStringCode];
if (isFunction(__helpInputEvent)) {
// 如果返回true表示继续调用,否则此快捷键结束
if (!__helpInputEvent()) return;
} else {
this.__helpInputDOM.innerHTML = '';
}
}
// 只读模式需要拦截部分快捷键
if (this._readonly && ['ctrl+a', 'ctrl+c'].indexOf(keyStringCode) < 0) return;
// 进入常规快捷键
switch (keyStringCode) {
// 全选
case "ctrl+a": {
// 修改选区范围
this.__cursor1 = { leftNum: 0, lineNum: 0 };
this.__cursor2 = { lineNum: this._contentArray.length - 1, leftNum: this._contentArray[this._contentArray.length - 1].length };
// 绘制选中效果
this.$$updateSelectView();
break;
}
// 复制
case "ctrl+c": {
if (this.$$selectIsNotBlank()) {
xhtml.copy(calcTwoCursor());
this.__focusDOM.focus();
}
break;
}
// 剪切
case "ctrl+x": {
if (this.$$selectIsNotBlank()) {
xhtml.copy(calcTwoCursor());
this.__focusDOM.focus();
this.$$deleteSelect();
// 由于内容改变,需要重新调用着色
this.__formatData = this.$$diff(this.$shader(this._contentArray.join('\n')));
// 更新视图
this.$$updateCursorPosition();
this.$$updateView();
this.$$cancelSelect();
// 通知文本改动
this.__updated__();
}
break;
}
// 多空格输入或多行移位
case "tab": {
// tab用来控制输入多个空格,默认事件需要禁止
xhtml.stopPropagation(event);
xhtml.preventDefault(event);
// 计算空格
let blanks = "";
for (let i = 0; i < this._tabSpace; i++) blanks += " ";
// 如果有选区,特殊处理
if (this.$$selectIsNotBlank()) {
let beginLineNum = this.__cursor1.lineNum, endLineNum = this.__cursor2.lineNum;
if (beginLineNum > endLineNum) {
beginLineNum = this.__cursor2.lineNum;
endLineNum = this.__cursor1.lineNum;
}
// 在开头追究tab
for (let lineNum = beginLineNum; lineNum <= endLineNum; lineNum++) {
this._contentArray[lineNum] = blanks + this._contentArray[lineNum];
}
// 校对选择区域
this.__cursor1.leftNum += this._tabSpace;
this.__cursor2.leftNum += this._tabSpace;
// 校对光标
this.__leftNum += this._tabSpace;
this.__formatData = this.$$diff(this.$shader(this._contentArray.join('\n')));
this.$$updateCursorPosition();
this.$$updateView();
this.$$updateCanvasSize();
this.$$updateSelectView();
// 通知文本改动
this.__updated__();
} else {
update(blanks);
}
break;
}
// 光标向上
case "up": {
// 如果是第一行不需要任何处理
if (this.__lineNum <= 0) return;
// 向上一行
this.__lineNum -= 1;
this.__leftNum = this.$$bestLeftNum(this.$$textWidth(this._contentArray[this.__lineNum + 1].substr(0, this.__leftNum)) + 40);
this.$$updateCursorPosition();
this.$$updateView();
this.$$cancelSelect();
this._el.scrollTop -= 21;
break;
}
// 光标向下
case "down": {
if (this.__lineNum >= this._contentArray.length - 1) return;
// 向下一行
this.__lineNum += 1;
this.__leftNum = this.$$bestLeftNum(this.$$textWidth(this._contentArray[this.__lineNum - 1].substr(0, this.__leftNum)) + 40);
this.$$updateCursorPosition();
this.$$updateView();
this.$$cancelSelect();
this._el.scrollTop += 21;
break;
}
// 光标向左
case "left": {
if (this.__leftNum <= 0) {
if (this.__lineNum <= 0) return;
this.__lineNum -= 1;
this.__leftNum = this._contentArray[this.__lineNum].length;
} else {
this.__leftNum -= 1;
}
this.$$updateCursorPosition();
this.$$cancelSelect();
break;
}
// 光标向右
case "right": {
if (this.__leftNum >= this._contentArray[this.__lineNum].length) {
if (this.__lineNum >= this._contentArray.length - 1) return;
this.__lineNum += 1;
this.__leftNum = 0;
} else {
this.__leftNum += 1;
}
this.$$updateCursorPosition();
this.$$cancelSelect();
break;
}
// 删除
case "backspace": {
// 如果有选区
if (this.$$selectIsNotBlank()) {
// 删除选区
this.$$deleteSelect();
}
// 无选区的常规操作
else {
if (this.__leftNum <= 0) {
if (this.__lineNum <= 0) return;
this.__lineNum -= 1;
this.__leftNum = this._contentArray[this.__lineNum].length;
// 一行的开头应该删除本行(合并到前一行)
this._contentArray[this.__lineNum] += this._contentArray[this.__lineNum + 1];
this._contentArray.splice(this.__lineNum + 1, 1);
} else {
this.__leftNum -= 1;
this._contentArray[this.__lineNum] = this._contentArray[this.__lineNum].substring(0, this.__leftNum) + this._contentArray[this.__lineNum].substring(this.__leftNum + 1);
}
}
// 由于内容改变,需要重新调用着色
this.__formatData = this.$$diff(this.$shader(this._contentArray.join('\n')));
// 更新视图
this.$$updateCursorPosition();
this.$$updateView();
this.$$cancelSelect();
// 通知文本改动
this.__updated__();
break;
}
}
});
};