Files
mixly3/common/modules/mixly-modules/common/editor-ace.js

364 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
goog.loadJs('common', () => {
goog.require('ace');
goog.require('ace.ExtLanguageTools');
goog.require('Mixly.XML');
goog.require('Mixly.Env');
goog.require('Mixly.Msg');
goog.require('Mixly.Debug');
goog.require('Mixly.HTMLTemplate');
goog.require('Mixly.EditorBase');
goog.provide('Mixly.EditorAce');
const {
XML,
Env,
Msg,
Debug,
HTMLTemplate,
EditorBase
} = Mixly;
class EditorAce extends EditorBase {
static {
this.CTRL_BTNS = ['resetFontSize', 'increaseFontSize', 'decreaseFontSize'];
this.CTRL_BTN_TEMPLATE = '<div m-id="{{d.mId}}" class="code-editor-btn setFontSize"></div>';
this.MODE_MAP = goog.getJSON(path.join(Env.templatePath, 'json/ace-mode-map.json'));
HTMLTemplate.add(
'html/editor/editor-code.html',
new HTMLTemplate(goog.get(path.join(Env.templatePath, 'html/editor/editor-code.html')))
);
}
#editor_ = null;
#destroyed_ = null;
#cursorLayer_ = null;
constructor() {
super();
const editorHTMLTemplate = HTMLTemplate.get('html/editor/editor-code.html');
this.setContent($(editorHTMLTemplate.render()));
this.id = editorHTMLTemplate.id;
}
init() {
super.init();
this.#editor_ = ace.edit(this.getContent()[0]);
this.resetFontSize();
this.#addCursorLayer_();
this.#addCursorEventsListener_();
this.#addDefaultCommand_();
}
getEditor() {
return this.#editor_;
}
dispose() {
super.dispose();
this.#editor_.destroy();
this.#destroyed_ = true;
}
setValue(data, scroll = true) {
if (this.#destroyed_) {
return;
}
this.#editor_.updateSelectionMarkers();
const { selection } = this.#editor_;
const initCursor = selection.getCursor();
if (this.getValue() !== data) {
this.#editor_.setValue(data);
}
if (scroll) {
this.scrollToBottom();
} else {
selection.moveCursorTo(initCursor.row, initCursor.column, true);
selection.clearSelection();
}
}
addValue(data, scroll = true) {
if (this.#destroyed_) {
return;
}
const { session } = this.#editor_;
const endCursor = this.getEndPos();
session.insert(endCursor, data);
if (scroll) {
this.scrollToBottom();
}
}
getValue() {
return this.#destroyed_ ? '' : this.#editor_.getValue();
}
getValueRange(startPos, endPos) {
if (this.#destroyed_ || !startPos || !endPos
|| typeof startPos !== 'object' || typeof endPos !== 'object') {
return "";
}
const session = this.#editor_.getSession();
return session.getTextRange(new ace.Range(
startPos.row,
startPos.column,
endPos.row,
endPos.column
));
}
getEndPos() {
if (this.#destroyed_) {
return { row: 0, column: 0 };
}
const session = this.#editor_.getSession();
const row = session.getLength() - 1;
const column = session.getLine(row).length;
return { row, column };
}
clear() {
this.setValue('', true);
}
scrollToBottom() {
if (this.#destroyed_) {
return;
}
const { selection, session } = this.#editor_;
this.#editor_.updateSelectionMarkers();
this.#editor_.gotoLine(session.getLength());
selection.moveCursorLineEnd();
}
scrollToTop() {
if (this.#destroyed_) {
return;
}
this.#editor_.gotoLine(0);
}
#addDefaultCommand_() {
const { commands } = this.#editor_;
commands.addCommands([{
name: "increaseFontSize",
bindKey: "Ctrl-=|Ctrl-+",
exec: (editor) => {
this.increaseFontSize();
}
}, {
name: "decreaseFontSize",
bindKey: "Ctrl+-|Ctrl-_",
exec: (editor) => {
this.decreaseFontSize();
}
}, {
name: "resetFontSize",
bindKey: "Ctrl+0|Ctrl-Numpad0",
exec: (editor) => {
this.resetFontSize();
}
}, {
name: "mixly-message",
bindKey: "backspace|delete|enter",
readOnly: true,
exec: (editor) => {
if (!editor.getReadOnly()) {
return false;
}
this.#cursorLayer_.show();
return false;
}
}]);
}
#addCursorLayer_() {
this.#cursorLayer_ = tippy(this.getContent().find('.ace_cursor')[0], {
content: Msg.Lang['editor.viewReadOnly'],
trigger: 'manual',
hideOnClick: true,
delay: 0,
duration: [ 0, 0 ],
placement: 'right',
offset: [ 0, 0 ],
popperOptions: {
strategy: 'fixed',
modifiers: [
{
name: 'flip',
options: {
fallbackPlacements: ['top-end', 'right']
}
}
]
}
});
}
#addCursorEventsListener_() {
const editor = this.getEditor();
$('#mixly-footer-cursor').hide();
editor.on('focus', () => {
const cursor = selection.getCursor();
$('#mixly-footer-row').html(cursor.row + 1);
$('#mixly-footer-column').html(cursor.column + 1);
$('#mixly-footer-cursor').show();
});
editor.on("blur", () => {
this.#cursorLayer_.hide();
$('#mixly-footer-cursor').hide();
});
const { selection } = editor.getSession();
const { session } = editor;
selection.on('changeCursor', () => {
const cursor = selection.getCursor();
$('#mixly-footer-row').html(cursor.row + 1);
$('#mixly-footer-column').html(cursor.column + 1);
});
selection.on("changeSelection", () => {
if (selection.isEmpty()) {
$('#mixly-footer-selected').parent().hide();
} else {
const range = selection.getRange();
const text = session.getTextRange(range);
$('#mixly-footer-selected').parent().css('display', 'inline-flex');
$('#mixly-footer-selected').html(text.length);
}
});
session.on("changeScrollTop", () => {
this.#cursorLayer_.hide();
});
}
addCtrlBtns() {
const $content = this.getContent();
for (let mId of EditorAce.CTRL_BTNS) {
$content.append(XML.render(EditorAce.CTRL_BTN_TEMPLATE, { mId }));
}
this.$ctrlBtns = $content.children('.code-editor-btn');
this.$ctrlBtns.off().click((event) => {
const mId = $(event.target).attr('m-id');
this[mId]();
});
}
showCtrlBtns() {
this.$ctrlBtns.css('display', 'block');
}
hideCtrlBtns() {
this.$ctrlBtns.css('display', 'none');
}
resetFontSize() {
this.#editor_.setFontSize(17);
}
increaseFontSize() {
const size = parseInt(this.#editor_.getFontSize(), 10) || 17;
this.#editor_.setFontSize(size + 1);
}
decreaseFontSize() {
const size = parseInt(this.#editor_.getFontSize(), 10) || 17;
this.#editor_.setFontSize(Math.max(size - 1 || 1));
}
undo() {
super.undo();
this.#editor_.undo();
}
redo() {
super.redo();
this.#editor_.redo();
}
setReadOnly(status) {
this.#editor_.setReadOnly(status);
}
setMode(type) {
this.#editor_.session.setMode(`ace/mode/${type}`);
}
setFileMode(extname) {
const mode = this.getFileMode(extname) ?? 'text';
this.setMode(mode);
}
getFileMode(extname) {
for (const [mode, extensions] of Object.entries(EditorAce.MODE_MAP)) {
if (extensions.includes(extname)) {
return mode;
}
}
return null;
}
cut() {
const { selection, session } = this.#editor_;
const cutLine = selection.isEmpty();
const range = cutLine ? selection.getLineRange() : selection.getRange();
this.#editor_._emit('cut', range);
if (!range.isEmpty()) {
const copyText = session.getTextRange(range);
navigator.clipboard.writeText(copyText)
.then(() => {
Debug.log('clipboard复制成功', copyText);
}).catch((error) => {
Debug.error('clipboard复制失败', error);
});
session.remove(range);
}
this.#editor_.clearSelection();
}
copy() {
const copyText = this.#editor_.getSelectedText();
this.#editor_.clearSelection();
if (!copyText) {
return;
}
navigator.clipboard.writeText(copyText)
.then(() => {
Debug.log('clipboard复制成功', copyText);
}).catch((error) => {
Debug.error('clipboard复制失败', error);
});
}
paste() {
navigator.clipboard.readText()
.then((message) => {
this.#editor_.execCommand('paste', message);
Debug.log('clipboard粘贴成功', message);
})
.catch((error) => {
Debug.error('clipboard粘贴失败', error);
});
}
commentLine() {
this.#editor_.execCommand('togglecomment');
}
blockComment() {
this.#editor_.execCommand('toggleBlockComment');
}
resize() {
this.#editor_.resize();
super.resize();
}
focus() {
this.#editor_.focus();
}
}
Mixly.EditorAce = EditorAce;
});