`);
+ DropdownMenu.$container.append(this.#$contextMenuElem_);
+ this.#contextMenuId_ = contextMenuId;
+ this.#layer_ = layer;
+ }
+
+ show() {
+ this.#$contextMenuElem_.contextMenu();
+ }
+
+ hide() {
+ this.#$contextMenuElem_.contextMenu('hide');
+ }
+
+ dispose() {
+ super.dispose();
+ this.#layer_.destroy();
+ this.#layer_ = null;
+ this.#$contextMenuElem_.remove();
+ this.#$contextMenuElem_ = null;
+ }
+}
+
+Mixly.DropdownMenu = DropdownMenu;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-ace.js b/mixly/common/modules/mixly-modules/common/editor-ace.js
new file mode 100644
index 00000000..ee8c5133
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-ace.js
@@ -0,0 +1,409 @@
+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 = '
';
+ this.MODE_MAP = goog.readJsonSync(path.join(Env.templatePath, 'json/ace-mode-map.json'));
+
+ HTMLTemplate.add(
+ 'html/editor/editor-code.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-code.html')))
+ );
+
+ if (['zh-hans', 'zh-hant'].includes(Msg.nowLang)) {
+ ace.config.setMessages(
+ goog.readJsonSync(path.join(Env.templatePath, `json/ace.i18n.${Msg.nowLang}.json`))
+ );
+ }
+ }
+
+ #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();
+ }
+ }
+
+ replaceLine(lineNumber, newContent) {
+ const session = this.#editor_.getSession();
+ const totalLines = session.getLength();
+ let targetLine = lineNumber < 0 ? totalLines + lineNumber : lineNumber;
+ if (targetLine < 0) {
+ targetLine = 0;
+ } else if (targetLine >= totalLines) {
+ targetLine = totalLines - 1;
+ }
+ const cursorPos = this.#editor_.getCursorPosition();
+ const oldLine = session.getLine(targetLine);
+ const range = new ace.Range(targetLine, 0, targetLine, oldLine.length);
+ session.replace(range, newContent);
+ if (cursorPos.row > targetLine) {
+ this.#editor_.moveCursorTo(cursorPos.row, cursorPos.column);
+ } else if (cursorPos.row === targetLine) {
+ const newCol = Math.min(cursorPos.column, newContent.length);
+ this.#editor_.moveCursorTo(targetLine, newCol);
+ } else {
+ this.#editor_.moveCursorTo(cursorPos.row, cursorPos.column);
+ }
+ this.#editor_.renderer.scrollCursorIntoView(null, 0.5);
+ }
+
+ appendLine(text) {
+ const session = this.#editor_.getSession();
+ const totalLines = session.getLength();
+ const lastLineIndex = totalLines - 1;
+ const lastLineText = session.getLine(lastLineIndex);
+ if (lastLineText.trim()) {
+ session.insert({ row: totalLines, column: 0 }, `\n${text}`);
+ } else {
+ const range = new ace.Range(lastLineIndex, 0, lastLineIndex, lastLineText.length);
+ session.replace(range, text);
+ }
+ 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;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-base.js b/mixly/common/modules/mixly-modules/common/editor-base.js
new file mode 100644
index 00000000..1bbadaea
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-base.js
@@ -0,0 +1,50 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Events');
+goog.require('Mixly.PageBase');
+goog.provide('Mixly.EditorBase');
+
+const { Events, PageBase } = Mixly;
+
+class EditorBase extends PageBase {
+ #$btnsContent_ = null;
+
+ constructor() {
+ super();
+ }
+
+ getBtnsContent() {
+ return this.#$btnsContent_;
+ }
+
+ setBtnsContent($elem) {
+ this.#$btnsContent_ = $elem;
+ }
+
+ getValue() {
+ return '';
+ }
+
+ setValue(data, ext) {
+ this.removeDirty();
+ }
+
+ getCode() {
+ return this.getValue();
+ }
+
+ dispose() {
+ this.#$btnsContent_ && this.#$btnsContent_.remove();
+ super.dispose();
+ }
+
+ undo() {}
+
+ redo() {}
+
+ empty() {}
+}
+
+Mixly.EditorBase = EditorBase;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-blockly.js b/mixly/common/modules/mixly-modules/common/editor-blockly.js
new file mode 100644
index 00000000..6b0c286c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-blockly.js
@@ -0,0 +1,367 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Blockly');
+goog.require('WorkspaceSearch');
+goog.require('Backpack');
+goog.require('Minimap');
+goog.require('PositionedMinimap');
+goog.require('ContentHighlight');
+goog.require('ZoomToFitControl');
+goog.require('Blockly.FieldGridDropdown');
+goog.require('Blockly.FieldDependentDropdown');
+goog.require('Blockly.FieldSlider');
+goog.require('Blockly.FieldBitmap');
+goog.require('Blockly.FieldColourHsvSliders');
+goog.require('Blockly.FieldDate');
+goog.require('Blockly.FieldAngle');
+goog.require('Blockly.FieldColour');
+goog.require('Blockly.FieldMultilineInput');
+goog.require('Blockly.Screenshot');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.ToolboxSearcher');
+goog.require('Mixly.Debug');
+goog.require('Mixly.EditorBase');
+goog.provide('Mixly.EditorBlockly');
+
+const {
+ Config,
+ Env,
+ XML,
+ HTMLTemplate,
+ ToolboxSearcher,
+ Debug,
+ EditorBase
+} = Mixly;
+const { USER, BOARD } = Config;
+
+Blockly.ALIGN_LEFT = Blockly.inputs.Align.LEFT;
+Blockly.ALIGN_CENTRE = Blockly.inputs.Align.CENTRE;
+Blockly.ALIGN_RIGHT = Blockly.inputs.Align.RIGHT;
+
+class EditorBlockly extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-blockly.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-blockly.html')))
+ );
+
+ HTMLTemplate.add(
+ 'xml/default-categories.xml',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'xml/default-categories.xml')))
+ );
+
+ this.$blockly = $('
');
+ this.editor = null;
+ this.workspace = null;
+ this.initBlockly = () => {
+ const DEFAULT_CATEGORIES = HTMLTemplate.get('xml/default-categories.xml').render();
+ const media = path.join(Env.srcDirPath, 'common/media/blockly');
+ const renderer = ['geras', 'zelos', 'thrasos'].includes(USER.blockRenderer) ? USER.blockRenderer : 'geras';
+ this.editor = Blockly.inject(this.$blockly[0], {
+ media,
+ toolbox: DEFAULT_CATEGORIES,
+ renderer,
+ zoom: {
+ controls: true,
+ wheel: true,
+ scaleSpeed: 1.03
+ },
+ grid: USER.blocklyShowGrid ==='yes' ? {
+ spacing: 20,
+ length: 3,
+ colour: '#ccc',
+ snap: true
+ } : {}
+ });
+
+ const $blocklyScrollbarHandle = this.$blockly.find('.blocklyScrollbarHandle');
+ $blocklyScrollbarHandle.on('pointerdown', (event) => {
+ const { currentTarget } = event;
+ currentTarget.setPointerCapture(event.pointerId);
+ });
+ $blocklyScrollbarHandle.on('pointerup', (event) => {
+ const { currentTarget } = event;
+ currentTarget.releasePointerCapture(event.pointerId);
+ });
+
+ this.editor.registerToolboxCategoryCallback(
+ Blockly.Variables.CATEGORY_NAME,
+ (...args) => Blockly.Variables.flyoutCategory(...args)
+ );
+
+ this.editor.registerToolboxCategoryCallback(
+ Blockly.Procedures.CATEGORY_NAME,
+ (...args) => Blockly.Procedures.flyoutCategory(...args)
+ );
+
+ this.editor.setTheme(Blockly.Themes[
+ USER.theme === 'dark' ? 'Dark' : 'Classic'
+ ]);
+
+ this.addPlugins();
+ this.workspace = new Blockly.Workspace(new Blockly.Options({
+ toolbox: null
+ }));
+ }
+
+ this.addPlugins = () => {
+ const { editor } = this;
+ Blockly.ContextMenuItems.registerCommentOptions();
+ editor.configureContextMenu = (menuOptions, e) => {
+ const workspaceSearchOption = {
+ text: Blockly.Msg['WORKSPACE_SEARCH_OPEN'],
+ enabled: editor.getTopBlocks().length,
+ callback: () => {
+ this.workspaceSearch.open();
+ }
+ };
+ menuOptions.push(workspaceSearchOption);
+ const screenshotOption = {
+ text: Blockly.Msg['DOWNLOAD_SCREENSHOT'],
+ enabled: editor.getTopBlocks().length,
+ callback: function() {
+ Blockly.Screenshot.downloadScreenshot(editor);
+ },
+ };
+ menuOptions.push(screenshotOption);
+ }
+
+ this.toolboxSeacher = new ToolboxSearcher(editor);
+
+ this.workspaceSearch = new WorkspaceSearch(editor);
+ this.workspaceSearch.init();
+
+ this.zoomToFit = new ZoomToFitControl(editor);
+ this.zoomToFit.init();
+
+ this.backpack = new Backpack(editor, {
+ useFilledBackpackImage: true,
+ skipSerializerRegistration: false,
+ contextMenu: {
+ emptyBackpack: true,
+ removeFromBackpack: true,
+ copyToBackpack: true,
+ copyAllToBackpack: true,
+ pasteAllToBackpack: true,
+ disablePreconditionChecks: false
+ }
+ });
+ this.backpack.init();
+
+ /*if (USER.blocklyMultiselect === 'yes') {
+ this.multiselectPlugin = new Multiselect(editor);
+ this.multiselectPlugin.init({
+ useDoubleClick: false,
+ bumpNeighbours: false,
+ multiselectIcon: {
+ hideIcon: true
+ },
+ multiselectCopyPaste: {
+ crossTab: true,
+ menu: false
+ }
+ });
+ }
+
+ if (USER.blocklyShowMinimap === 'yes') {
+ this.minimap = new PositionedMinimap(editor);
+ this.minimap.init();
+ }*/
+
+ if (USER.blocklyContentHighlight === 'yes') {
+ this.contentHighlight = new ContentHighlight(editor);
+ this.contentHighlight.init();
+ }
+ }
+
+ this.getEditor = () => {
+ return this.editor;
+ }
+
+ this.getContent = () => {
+ return this.$blockly;
+ }
+
+ this.getXML = (workspace) => {
+ const $xml = $(Blockly.Xml.workspaceToDom(workspace));
+ return $xml[0].outerHTML;
+ }
+
+ this.getRawCode = (workspace, generator) => {
+ return generator?.workspaceToCode(workspace) || '';
+ }
+
+ this.getCode = (workspace, generator) => {
+ let code = generator?.workspaceToCode(workspace) || '';
+ code = code.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/g, function (s) {
+ try {
+ return decodeURIComponent(s.replace(/_/g, '%'));
+ } catch (error) {
+ Debug.error(error);
+ return s;
+ }
+ });
+ return code;
+ }
+
+ this.updateToolbox = () => {
+ this.editor.updateToolbox($('#toolbox')[0]);
+ }
+
+ this.reloadWorkspace = () => {
+ let workspaceState = Blockly.serialization.workspaces.save(this.editor);
+ let undoStack = [...this.editor.undoStack_];
+ let redoStack = [...this.editor.redoStack_];
+ Blockly.serialization.workspaces.load(workspaceState, this.editor, {
+ recordUndo: false
+ });
+ this.editor.undoStack_ = [...undoStack];
+ this.editor.redoStack_ = [...redoStack];
+ }
+
+ this.initBlockly();
+ }
+
+ #editor_ = null;
+ #workspaceState_ = null;
+ #undoStack_ = null;
+ #redoStack_ = null;
+
+ constructor() {
+ super();
+ this.setContent(
+ $(HTMLTemplate.get('html/editor/editor-blockly.html').render())
+ );
+ this.#editor_ = EditorBlockly.getEditor();
+ }
+
+ getEditor() {
+ return this.#editor_;
+ }
+
+ undo() {
+ super.undo();
+ this.#editor_.undo(0);
+ }
+
+ redo() {
+ super.redo();
+ this.#editor_.undo(1);
+ }
+
+ setVisible(status) {
+ this.#editor_.setVisible(status);
+ }
+
+ scrollCenter() {
+ this.#editor_.scrollCenter();
+ }
+
+ resize() {
+ // 重新调整编辑器尺寸
+ this.#editor_.hideChaff(false);
+ this.#editor_.hideComponents(true);
+ Blockly.common.svgResize(this.#editor_);
+ Blockly.bumpObjects.bumpTopObjectsIntoBounds(this.#editor_);
+ super.resize();
+ }
+
+ dispose() {
+ super.dispose();
+ if (this.isActive()) {
+ Blockly.Events.disable();
+ this.#editor_.clear();
+ Blockly.Events.enable();
+ EditorBlockly.getContent().detach();
+ }
+ this.#undoStack_ = null;
+ this.#redoStack_ = null;
+ this.#editor_ = null;
+ this.getContent().remove();
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.getContent().append(EditorBlockly.getContent());
+ setTimeout(() => {
+ Blockly.Events.disable();
+ if (this.#workspaceState_) {
+ Blockly.serialization.workspaces.load(this.#workspaceState_, this.#editor_, {
+ recordUndo: false
+ });
+ } else {
+ this.#editor_.clear();
+ }
+ if (this.#undoStack_) {
+ this.#editor_.undoStack_ = [...this.#undoStack_];
+ }
+ if (this.#redoStack_) {
+ this.#editor_.redoStack_ = [...this.#redoStack_];
+ }
+ Blockly.Events.enable();
+ }, 0);
+ }
+
+ onUnmounted() {
+ super.onUnmounted();
+ EditorBlockly.getContent().detach();
+ this.getContent().empty();
+ this.#workspaceState_ = Blockly.serialization.workspaces.save(this.#editor_);
+ this.#undoStack_ = [...this.#editor_.undoStack_];
+ this.#redoStack_ = [...this.#editor_.redoStack_];
+ Blockly.Events.disable();
+ this.#editor_.clearUndo();
+ Blockly.Events.enable();
+ }
+
+ setValue(data, ext) {}
+
+ #getTargetWorkspace_() {
+ let workspace = this.#editor_;
+ if (!this.isActive()) {
+ if (this.isDirty()) {
+ this.#workspaceState_ = Blockly.serialization.workspaces.save(this.#editor_);
+ }
+ Blockly.serialization.workspaces.load(this.#workspaceState_, EditorBlockly.workspace, {
+ recordUndo: false
+ });
+ workspace = EditorBlockly.workspace;
+ }
+ return workspace;
+ }
+
+ getXML() {
+ const workspace = this.#getTargetWorkspace_();
+ return EditorBlockly.getXML(workspace);
+ }
+
+ getRawCode() {
+ const workspace = this.#getTargetWorkspace_();
+ return EditorBlockly.getRawCode(workspace, Blockly.generator);
+ }
+
+ getCode() {
+ const workspace = this.#getTargetWorkspace_();
+ return EditorBlockly.getCode(workspace, Blockly.generator);
+ }
+
+ getMix() {
+ const workspace = this.#getTargetWorkspace_();
+ return {
+ block: EditorBlockly.getXML(workspace),
+ code: EditorBlockly.getCode(workspace, Blockly.generator)
+ };
+ }
+
+ getValue() {
+ return this.getCode();
+ }
+}
+
+Mixly.EditorBlockly = EditorBlockly;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-code.js b/mixly/common/modules/mixly-modules/common/editor-code.js
new file mode 100644
index 00000000..8386af1a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-code.js
@@ -0,0 +1,231 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Config');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Menu');
+goog.require('Mixly.ContextMenu');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.CodeFormatter');
+goog.require('Mixly.MonacoTheme');
+goog.require('Mixly.MonacoTreeSitter');
+goog.require('Mixly.EditorMonaco');
+goog.provide('Mixly.EditorCode');
+
+const {
+ Config,
+ XML,
+ Env,
+ Msg,
+ Debug,
+ Menu,
+ ContextMenu,
+ IdGenerator,
+ CodeFormatter,
+ MonacoTheme,
+ MonacoTreeSitter,
+ EditorMonaco
+} = Mixly;
+const { USER } = Config;
+
+
+class EditorCode extends EditorMonaco {
+ #contextMenu_ = null;
+ #monacoTreeSitter_ = null;
+
+ constructor() {
+ super();
+ }
+
+ init() {
+ super.init();
+ this.setLanguage('text');
+ this.setTabSize(4);
+ this.#addContextMenu_();
+ this.setTheme(USER.theme);
+ this.#monacoTreeSitter_ = new MonacoTreeSitter(this);
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#addChangeEventListenerExt_();
+ }
+
+ #addContextMenu_() {
+ this.#contextMenu_ = new ContextMenu(`div[page-id="${this.getId()}"]`, {
+ zIndex: 300
+ });
+ let menu = new Menu();
+ menu.add({
+ weight: 0,
+ id: 'cut',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.cut'], 'Ctrl+X'),
+ callback: (key, opt) => this.cut()
+ }
+ });
+ menu.add({
+ weight: 1,
+ id: 'copy',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.copy'], 'Ctrl+C'),
+ callback: (key, opt) => this.copy()
+ }
+ });
+ menu.add({
+ weight: 2,
+ id: 'paste',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.paste'], 'Ctrl+V'),
+ callback: (key, opt) => this.paste()
+ }
+ });
+ menu.add({
+ weight: 3,
+ id: 'sep1',
+ data: '---------'
+ });
+ menu.add({
+ weight: 4,
+ id: 'togglecomment',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.togglecomment'], 'Ctrl+/'),
+ callback: (key, opt) => this.commentLine()
+ }
+ });
+ menu.add({
+ weight: 5,
+ id: 'toggleBlockComment',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.toggleBlockComment'], 'Shift+Alt+A'),
+ callback: (key, opt) => this.blockComment()
+ }
+ });
+ this.#contextMenu_.register('code', menu);
+ this.#contextMenu_.bind('getMenu', () => 'code');
+ }
+
+ getContextMenu() {
+ return this.#contextMenu_;
+ }
+
+ setValue(data, ext) {
+ this.disableChangeEvent();
+ super.setValue(data);
+ const language = this.getLanguageByExt(ext);
+ if (MonacoTheme.supportThemes.includes(language)) {
+ this.setTheme(`${USER.theme}-${language}`);
+ } else {
+ this.setTheme(USER.theme);
+ }
+ this.setLanguage(language);
+ this.enableChangeEvent();
+ this.setCodeFormatter(language, ext).catch(Debug.error);
+ }
+
+ diffEdits(data, ext) {
+ this.disableChangeEvent();
+ super.diffEdits(data);
+ const language = this.getLanguageByExt(ext);
+ this.setLanguage(language);
+ if (MonacoTheme.supportThemes.includes(language)) {
+ this.setTheme(`${USER.theme}-${language}`);
+ } else {
+ this.setTheme(USER.theme);
+ }
+ this.#monacoTreeSitter_.setValue(language, USER.theme, data);
+ this.enableChangeEvent();
+ this.setCodeFormatter(language, ext).catch(Debug.error);
+ }
+
+ async setCodeFormatter(language, ext) {
+ const formatter = await CodeFormatter.activateFormatter(language);
+ const menu = this.#contextMenu_.getItem('code');
+ if (!formatter) {
+ menu.remove('sep-format');
+ menu.remove('format');
+ return;
+ }
+ menu.add({
+ weight: 6,
+ id: 'sep-format',
+ data: '---------'
+ });
+ menu.add({
+ weight: 6,
+ id: 'format',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.formatDocument'], ''),
+ callback: () => {
+ CodeFormatter.format(language, this.getValue())
+ .then((data) => {
+ super.setValue(data, ext);
+ })
+ .catch(Debug.error);
+ }
+ }
+ });
+ }
+
+ getTreeSitter() {
+ return this.#monacoTreeSitter_;
+ }
+
+ getLanguageByExt(ext) {
+ let language = 'plaintext';
+ switch(ext) {
+ case '.json':
+ language = 'json';
+ break;
+ case '.c':
+ case '.cpp':
+ case '.h':
+ case '.hpp':
+ case '.ino':
+ language = 'cpp';
+ break;
+ case '.js':
+ language = 'javascript';
+ break;
+ case '.py':
+ language = 'python';
+ break;
+ case '.lua':
+ language = 'lua';
+ break;
+ case '.md':
+ case '.mdx':
+ language = 'markdown';
+ break;
+ default:
+ language = 'plaintext';
+ }
+ return language;
+ }
+
+ #addChangeEventListenerExt_() {
+ this.offEvent('change');
+ this.bind('change', () => {
+ this.addDirty();
+ this.#monacoTreeSitter_.setValue(this.getLanguage(), USER.theme, this.getValue());
+ });
+ }
+
+ dispose() {
+ this.#contextMenu_.dispose();
+ this.#contextMenu_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.EditorCode = EditorCode;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-md.js b/mixly/common/modules/mixly-modules/common/editor-md.js
new file mode 100644
index 00000000..5c7b246e
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-md.js
@@ -0,0 +1,170 @@
+goog.loadJs('common', () => {
+
+goog.require('marked');
+goog.require('markedKatex');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Drag');
+goog.require('Mixly.DragV');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorBase');
+goog.require('Mixly.EditorCode');
+goog.provide('Mixly.EditorMd');
+
+const {
+ XML,
+ Env,
+ Drag,
+ DragV,
+ IdGenerator,
+ HTMLTemplate,
+ EditorBase,
+ EditorCode
+} = Mixly;
+
+
+class EditorMd extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-md.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-md.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/editor/editor-md-btns.html',
+ goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-md-btns.html'))
+ );
+
+ marked.use(markedKatex({ throwOnError: false }));
+ }
+
+ #prevCode_ = '';
+ #listener_ = null;
+ #drag_ = null;
+ #$preview_ = null;
+ #$btns_ = null;
+
+ constructor() {
+ super();
+ const $content = $(HTMLTemplate.get('html/editor/editor-md.html').render());
+ const $btnsContent = $(HTMLTemplate.get('html/editor/editor-md-btns.html'));
+ this.#$preview_ = $content.find('.markdown-body');
+ this.addPage($content.find('.editor-code'), 'code', new EditorCode());
+ this.#$btns_ = $btnsContent.find('button');
+ this.#drag_ = null;
+ this.setContent($content);
+ this.setBtnsContent($btnsContent);
+ }
+
+ init() {
+ super.init();
+ this.#addDragEventsListener_();
+ this.#addBtnEventsListener_();
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#addChangeEventListener_();
+ }
+
+ #addDragEventsListener_() {
+ this.#drag_ = new DragV(this.getContent()[0], {
+ min: '200px',
+ full: [true, true],
+ startSize: '0%',
+ startExitFullSize: '70%'
+ });
+ this.#drag_.bind('sizeChanged', () => this.resize());
+ this.#drag_.bind('onfull', (type) => {
+ this.#$btns_.removeClass('self-adaption-btn');
+ let $btn = null;
+ switch(type) {
+ case Drag.Extend.POSITIVE:
+ $btn = this.#$btns_.filter('[m-id="code"]');
+ break;
+ case Drag.Extend.NEGATIVE:
+ $btn = this.#$btns_.filter('[m-id="preview"]');
+ break;
+ }
+ $btn.addClass('self-adaption-btn');
+ });
+ this.#drag_.bind('exitfull', (type) => {
+ this.#$btns_.removeClass('self-adaption-btn');
+ const $btn = this.#$btns_.filter('[m-id="mixture"]');
+ $btn.addClass('self-adaption-btn');
+ if (type === Drag.Extend.POSITIVE) {
+ this.updatePreview();
+ }
+ });
+ }
+
+ #addBtnEventsListener_() {
+ this.#$btns_.on('click', (event) => {
+ const $btn = $(event.currentTarget);
+ const mId = $btn.attr('m-id');
+ if (!$btn.hasClass('self-adaption-btn')) {
+ this.#$btns_.removeClass('self-adaption-btn');
+ $btn.addClass('self-adaption-btn');
+ }
+ switch (mId) {
+ case 'code':
+ this.#drag_.full(Drag.Extend.POSITIVE);
+ break;
+ case 'mixture':
+ this.#drag_.exitfull(Drag.Extend.POSITIVE);
+ this.#drag_.exitfull(Drag.Extend.NEGATIVE);
+ break;
+ case 'preview':
+ this.#drag_.full(Drag.Extend.NEGATIVE);
+ break;
+ }
+ })
+ }
+
+ #addChangeEventListener_() {
+ const codePage = this.getPage('code');
+ codePage.offEvent('change');
+ codePage.bind('change', () => {
+ this.addDirty();
+ if (this.#drag_.shown === 'NEGATIVE') {
+ return;
+ }
+ this.updatePreview();
+ });
+ }
+
+ updatePreview() {
+ const code = this.getPage('code').getValue();
+ if (code === this.#prevCode_) {
+ return;
+ }
+ this.#prevCode_ = code;
+ const $dom = $(marked.parse(code));
+ const $as = $dom.find('a');
+ $as.attr('target', '_blank');
+ this.#$preview_.html($dom);
+ }
+
+ setValue(data, ext) {
+ const codePage = this.getPage('code');
+ codePage.disableChangeEvent();
+ codePage.setValue(data, ext);
+ this.updatePreview();
+ codePage.enableChangeEvent();
+ }
+
+ getValue() {
+ const codePage = this.getPage('code');
+ return codePage.getValue();
+ }
+
+ dispose() {
+ this.#drag_.dispose();
+ super.dispose();
+ }
+}
+
+Mixly.EditorMd = EditorMd;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-mix.js b/mixly/common/modules/mixly-modules/common/editor-mix.js
new file mode 100644
index 00000000..686c9f9b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-mix.js
@@ -0,0 +1,644 @@
+goog.loadJs('common', () => {
+
+goog.require('layui');
+goog.require('tippy');
+goog.require('Base64');
+goog.require('Blockly');
+goog.require('Mixly.Drag');
+goog.require('Mixly.DragV');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Menu');
+goog.require('Mixly.Boards');
+goog.require('Mixly.MJson');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorBlockly');
+goog.require('Mixly.EditorCode');
+goog.require('Mixly.EditorBase');
+goog.provide('Mixly.EditorMix');
+
+const { dropdown } = layui;
+const {
+ EditorBlockly,
+ EditorCode,
+ EditorBase,
+ Drag,
+ DragV,
+ XML,
+ Msg,
+ Config,
+ Env,
+ Debug,
+ Menu,
+ Boards,
+ MJson,
+ HTMLTemplate,
+ LayerExt
+} = Mixly;
+const { BOARD, SOFTWARE, USER } = Config;
+
+const { form } = layui;
+
+
+class EditorMix extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-mix.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-mix.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/editor/editor-mix-btns.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-mix-btns.html')))
+ );
+ }
+
+ #language_ = null;
+ #tabSize_ = null;
+ #$btns_ = null;
+ #temp_ = '';
+
+ constructor() {
+ super();
+ const $content = $(HTMLTemplate.get('html/editor/editor-mix.html').render());
+ const $btnsContent = $(HTMLTemplate.get('html/editor/editor-mix-btns.html').render({
+ block: Msg.Lang['editor.block'],
+ mix: Msg.Lang['editor.mix'],
+ code: Msg.Lang['editor.code']
+ }));
+ this.drag = null;
+ this.#$btns_ = $btnsContent.find('button');
+ this.setContent($content);
+ this.setBtnsContent($btnsContent);
+ this.addPage($content.find('.editor-blockly'), 'block', new EditorBlockly());
+ this.addPage($content.find('.editor-code'), 'code', new EditorCode());
+ }
+
+ init() {
+ super.init();
+ this.#addDragEventsListener_();
+ this.#addBtnEventsListener_();
+ this.#language_ = this.getDefaultLanguageExt();
+ this.#tabSize_ = this.getDefaultTabSize();
+ const codePage = this.getPage('code');
+ codePage.setLanguage(codePage.getLanguageByExt(this.#language_));
+ codePage.setTabSize(this.#tabSize_);
+ codePage.setReadOnly(true);
+ this.#py2BlockEditorInit_();
+ const contextMenu = codePage.getContextMenu();
+ let codeMenu = contextMenu.getItem('code');
+ codeMenu.add({
+ weight: 7,
+ id: 'sep2',
+ data: '---------'
+ });
+ codeMenu.add({
+ weight: 8,
+ id: 'block',
+ data: {
+ isHtmlName: false,
+ name: Msg.Lang['editor.contextMenu.exitCodeEditor'],
+ callback: (key, opt) => this.drag.exitfull(Drag.Extend.NEGATIVE)
+ }
+ });
+ let blockMenu = new Menu();
+ blockMenu.add({
+ weight: 0,
+ id: 'copy',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.copy'], 'Ctrl+C'),
+ callback: (key, opt) => codePage.copy()
+ }
+ });
+ blockMenu.add({
+ weight: 1,
+ id: 'sep1',
+ data: '---------'
+ });
+ blockMenu.add({
+ weight: 2,
+ id: 'code',
+ data: {
+ isHtmlName: false,
+ name: Msg.Lang['editor.contextMenu.enterCodeEditor'],
+ callback: (key, opt) => this.drag.full(Drag.Extend.NEGATIVE)
+ }
+ });
+ contextMenu.register('block', blockMenu);
+ contextMenu.offEvent('getMenu');
+ contextMenu.bind('getMenu', () => {
+ return this.getPageType();
+ });
+ }
+
+ #getCodeExtname_() {
+ let extname = '.c';
+ const language = BOARD.language.toLowerCase();
+ switch (language) {
+ case 'python':
+ case 'circuitpython':
+ case 'micropython':
+ extname = '.py';
+ break;
+ case 'lua':
+ extname = '.lua';
+ break;
+ case 'javascript':
+ extname = '.js';
+ break;
+ case 'c/c++':
+ default:
+ extname = '.c';
+ }
+ return extname;
+ }
+
+ #py2BlockEditorInit_() {
+ const codePage = this.getPage('code');
+ if (typeof Sk === 'object'
+ && typeof PythonToBlocks === 'function'
+ && typeof Py2blockEditor === 'function') {
+ const py2blockConverter = new PythonToBlocks();
+ this.py2BlockEditor = new Py2blockEditor(py2blockConverter, codePage.getEditor());
+ }
+ }
+
+ #addCodeChangeEventListener_() {
+ const codePage = this.getPage('code');
+ codePage.offEvent('change');
+ codePage.bind('change', () => {
+ this.addDirty();
+ codePage.getTreeSitter().setValue(codePage.getLanguage(), USER.theme, codePage.getValue());
+ });
+ }
+
+ #addDragEventsListener_() {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ this.drag = new DragV(this.getContent()[0], {
+ min: '200px',
+ full: [true, true],
+ startSize: '100%',
+ startExitFullSize: '70%'
+ });
+ this.drag.bind('sizeChanged', () => this.resize());
+ this.drag.bind('onfull', (type) => {
+ this.#$btns_.removeClass('self-adaption-btn');
+ let $btn = null;
+ switch(type) {
+ case Drag.Extend.POSITIVE:
+ $btn = this.#$btns_.filter('[m-id="block"]');
+ blockPage.scrollCenter();
+ break;
+ case Drag.Extend.NEGATIVE:
+ $btn = this.#$btns_.filter('[m-id="code"]');
+ codePage.setReadOnly(false);
+ codePage.focus();
+ if (this.py2BlockEditor && BOARD.pythonToBlockly) {
+ this.py2BlockEditor.fromCode = true;
+ }
+ break;
+ }
+ $btn.addClass('self-adaption-btn');
+ });
+ this.drag.bind('exitfull', (type) => {
+ this.#$btns_.removeClass('self-adaption-btn');
+ const $btn = this.#$btns_.filter('[m-id="mixture"]');
+ $btn.addClass('self-adaption-btn');
+ switch(type) {
+ case Drag.Extend.NEGATIVE:
+ codePage.setReadOnly(true);
+ if (this.py2BlockEditor
+ && BOARD.pythonToBlockly
+ && typeof this.py2BlockEditor.updateBlock === 'function') {
+ this.py2BlockEditor.updateBlock();
+ } else {
+ codePage.diffEdits(blockPage.getValue(), this.#language_);
+ }
+ break;
+ case Drag.Extend.POSITIVE:
+ this.#temp_ = blockPage.getValue();
+ codePage.diffEdits(this.#temp_, this.#language_);
+ break;
+ }
+ blockPage.resize();
+ blockPage.scrollCenter();
+ });
+ }
+
+ #addBtnEventsListener_() {
+ this.#$btns_.on('click', (event) => {
+ const $btn = $(event.currentTarget);
+ const mId = $btn.attr('m-id');
+ if (mId === 'deps') {
+ return;
+ }
+ if (!$btn.hasClass('self-adaption-btn')) {
+ this.#$btns_.removeClass('self-adaption-btn');
+ $btn.addClass('self-adaption-btn');
+ }
+ switch (mId) {
+ case 'block':
+ this.drag.full(Drag.Extend.POSITIVE);
+ break;
+ case 'mixture':
+ this.drag.exitfull(Drag.Extend.POSITIVE);
+ this.drag.exitfull(Drag.Extend.NEGATIVE);
+ break;
+ case 'code':
+ this.drag.full(Drag.Extend.NEGATIVE);
+ break;
+ }
+ })
+ }
+
+ getCurrentEditor() {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ if (this.getPageType() === 'code') {
+ return codePage;
+ } else {
+ return blockPage;
+ }
+ }
+
+ getDefaultLanguageExt() {
+ let ext = '.txt';
+ let type = (BOARD.language || '').toLowerCase();
+ switch(type) {
+ case 'python':
+ case 'micropython':
+ case 'circuitpython':
+ ext = '.py';
+ break;
+ case 'c/c++':
+ ext = '.cpp';
+ break;
+ case 'javascript':
+ ext = '.js';
+ break;
+ case 'markdown':
+ ext = '.md';
+ break;
+ case 'lua':
+ ext = '.lua';
+ break;
+ default:
+ ext = '.txt';
+ }
+ return ext;
+ }
+
+ getDefaultTabSize() {
+ let tabSize = 4;
+ let type = (BOARD.language || '').toLowerCase();
+ switch(type) {
+ case 'c/c++':
+ case 'markdown':
+ tabSize = 2;
+ break;
+ case 'python':
+ case 'micropython':
+ case 'circuitpython':
+ case 'javascript':
+ default:
+ tabSize = 4;
+ }
+ return tabSize;
+ }
+
+ getPageType() {
+ if (this.drag.shown !== Drag.Extend.NEGATIVE) {
+ return 'block';
+ } else {
+ return 'code';
+ }
+ }
+
+ undo() {
+ super.undo();
+ const editor = this.getCurrentEditor();
+ editor.undo();
+ }
+
+ redo() {
+ super.redo();
+ const editor = this.getCurrentEditor();
+ editor.redo();
+ }
+
+ dispose() {
+ const blockPage = this.getPage('block');
+ const blocklyWorkspace = blockPage.getEditor();
+ blocklyWorkspace.removeChangeListener(this.codeChangeListener);
+ this.drag.dispose();
+ super.dispose();
+ this.drag = null;
+ }
+
+ onMounted() {
+ super.onMounted();
+ const blockPage = this.getPage('block');
+ const blocklyWorkspace = blockPage.getEditor();
+ this.codeChangeListener = blocklyWorkspace.addChangeListener((event) => {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ if (
+ event.isUiEvent ||
+ event.type == Blockly.Events.FINISHED_LOADING ||
+ blocklyWorkspace.isDragging()
+ ) {
+ return;
+ }
+ this.addDirty();
+ if (this.drag.shown !== Drag.Extend.BOTH) {
+ return;
+ }
+ const code = blockPage.getCode();
+ if (this.#temp_ === code) {
+ return;
+ }
+ this.#temp_ = code;
+ codePage.diffEdits(code, this.#language_);
+ });
+ this.#addCodeChangeEventListener_();
+ }
+
+ onUnmounted() {
+ super.onUnmounted();
+ const blockPage = this.getPage('block');
+ const blocklyWorkspace = blockPage.getEditor();
+ blocklyWorkspace.removeChangeListener(this.codeChangeListener);
+ }
+
+ setValue(data, ext) {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ switch (ext) {
+ case '.mix':
+ case '.xml':
+ Blockly.Events.disable();
+ try {
+ data = XML.convert(data, true);
+ data = data.replace(/\\(u[0-9a-fA-F]{4})/g, function (s) {
+ return unescape(s.replace(/\\(u[0-9a-fA-F]{4})/g, '%$1'));
+ });
+ } catch (error) {
+ Debug.error(error);
+ }
+ this.parseMix($(data), false, false, (message) => {
+ if (message) {
+ switch (message) {
+ case 'USE_CODE':
+ Debug.log('已从code标签中读取代码');
+ break;
+ case 'USE_INCOMPLETE_BLOCKS':
+ Debug.log('一些块已被忽略');
+ break;
+ }
+ blockPage.scrollCenter();
+ Blockly.hideChaff();
+ }
+ });
+ Blockly.Events.enable();
+ break;
+ default:
+ this.drag.full(Drag.Extend.NEGATIVE);
+ this.getPage('code').setValue(data, ext);
+ break;
+ }
+ }
+
+ getValue() {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ const mix = blockPage.getMix();
+ const $xml = $(mix.block);
+ const config = Boards.getSelectedBoardConfig();
+ const boardName = Boards.getSelectedBoardName();
+ const board = BOARD?.boardType ?? 'default';
+ let xml = '';
+ let code = '';
+ $xml.removeAttr('xmlns')
+ .attr('version', SOFTWARE?.version ?? 'Mixly 2.0')
+ .attr('board', board + '@' + boardName);
+ if (this.drag.shown !== Drag.Extend.NEGATIVE) {
+ $xml.attr('shown', 'block');
+ code = mix.code;
+ } else {
+ $xml.attr('shown', 'code');
+ code = codePage.getValue();
+ }
+ xml = $xml[0].outerHTML;
+ if (config) {
+ xml += `
${MJson.stringify(config)} `;
+ }
+ xml += `
${Base64.encode(code)}`;
+ return xml;
+ }
+
+ getCode() {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ if (this.drag.shown !== Drag.Extend.NEGATIVE) {
+ return blockPage.getRawCode();
+ } else {
+ return codePage.getCode();
+ }
+ }
+
+ getMil() {
+ const $mix = $(this.getValue());
+ let $xml, $config, $code;
+ for (let i = 0; $mix[i]; i++) {
+ switch ($mix[i].nodeName) {
+ case 'XML':
+ $xml = $($mix[i]);
+ break;
+ case 'CONFIG':
+ $config = $($mix[i]);
+ break;
+ case 'CODE':
+ $code = $($mix[i]);
+ break;
+ }
+ }
+ if (!$xml) return '';
+ $config && $config.remove();
+ $code && $code.remove();
+ $xml.attr('type', 'lib');
+ $xml.find('block,shadow').removeAttr('id varid x y');
+ const $blocks = $xml.children('block');
+ let blockXmlList = [];
+ for (let i = 0; $blocks[i]; i++) {
+ const outerHTML = $blocks[i].outerHTML;
+ if (!blockXmlList.includes(outerHTML)) {
+ blockXmlList.push(outerHTML);
+ } else {
+ $blocks[i].remove();
+ }
+ }
+ return $xml[0].outerHTML;
+ }
+
+ parseMix(xml, useCode = false, useIncompleteBlocks = false, endFunc = (message) => {}) {
+ const blockPage = this.getPage('block');
+ const codePage = this.getPage('code');
+ const mixDom = xml;
+ let xmlDom, configDom, codeDom;
+ for (let i = 0; mixDom[i]; i++) {
+ switch (mixDom[i].nodeName) {
+ case 'XML':
+ xmlDom = $(mixDom[i]);
+ break;
+ case 'CONFIG':
+ configDom = $(mixDom[i]);
+ break;
+ case 'CODE':
+ codeDom = $(mixDom[i]);
+ break;
+ }
+ }
+ if (!xmlDom && !codeDom) {
+ layer.msg(Msg.Lang['editor.invalidData'], { time: 1000 });
+ return;
+ }
+ for (let i of ['version', 'id', 'type', 'varid', 'name', 'x', 'y', 'items']) {
+ const nowDom = xmlDom.find('*[' + i + ']');
+ if (nowDom.length) {
+ for (let j = 0; nowDom[j]; j++) {
+ let attr = $(nowDom[j]).attr(i);
+ try {
+ attr = attr.replaceAll('\\\"', '');
+ } catch (error) {
+ Debug.error(error);
+ }
+ $(nowDom[j]).attr(i, attr);
+ }
+ }
+ }
+ let config, configStr = configDom && configDom.html();
+ config = configStr? MJson.parse(configStr) : {};
+ let boardName = xmlDom.attr('board') ?? '';
+ blockPage.getEditor().clear();
+ Boards.setSelectedBoard(boardName, config);
+ let code = codeDom ? codeDom.html() : '';
+ if (Base64.isValid(code)) {
+ code = Base64.decode(code);
+ } else {
+ try {
+ code = util.unescape(code);
+ code = code.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/g, function (s) {
+ try {
+ return decodeURIComponent(s.replace(/_/g, '%'));
+ } catch (error) {
+ return s;
+ }
+ });
+ } catch (error) {
+ Debug.error(error);
+ }
+ }
+ if (useCode) {
+ if (!codeDom) {
+ layer.msg(Msg.Lang['editor.invalidData'], { time: 1000 });
+ return;
+ }
+ this.drag.full(Drag.Extend.NEGATIVE); // 完全显示代码编辑器
+ codePage.diffEdits(code, this.#language_);
+ endFunc('USE_CODE');
+ return;
+ }
+ const blockDom = mixDom.find('block');
+ const shadowDom = mixDom.find('shadow');
+ blockDom.removeAttr('id varid');
+ shadowDom.removeAttr('id varid');
+ let blocks = [];
+ let undefinedBlocks = [];
+ for (let i = 0; blockDom[i]; i++) {
+ const blockType = $(blockDom[i]).attr('type');
+ if (blockType && !blocks.includes(blockType))
+ blocks.push(blockType);
+ }
+ for (let i = 0; shadowDom[i]; i++) {
+ const shadowType = $(shadowDom[i]).attr('type');
+ if (shadowType && !blocks.includes(shadowType))
+ blocks.push(shadowType);
+ }
+ const blocklyGenerator = Blockly.generator;
+ for (let i of blocks) {
+ if (Blockly.Blocks[i] && blocklyGenerator.forBlock[i]) {
+ continue;
+ }
+ undefinedBlocks.push(i);
+ }
+ if (undefinedBlocks.length) {
+ this.showParseMixErrorDialog(mixDom, undefinedBlocks, endFunc);
+ return;
+ }
+ Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom[0], blockPage.getEditor());
+ blockPage.getEditor().scrollCenter();
+ Blockly.hideChaff();
+ if (!useIncompleteBlocks && codeDom && xmlDom.attr('shown') === 'code') {
+ this.drag.full(Drag.Extend.NEGATIVE); // 完全显示代码编辑器
+ codePage.diffEdits(code, this.#language_);
+ endFunc();
+ return;
+ }
+ this.drag.full(Drag.Extend.POSITIVE); // 完全显示块编辑器
+ if (useIncompleteBlocks)
+ endFunc('USE_INCOMPLETE_BLOCKS');
+ else
+ endFunc();
+ }
+
+ showParseMixErrorDialog(xml, undefinedBlocks, endFunc = () => {}) {
+ const { PARSE_MIX_ERROR_DIV } = XML.TEMPLATE_STR;
+ const renderStr = XML.render(PARSE_MIX_ERROR_DIV, {
+ text: undefinedBlocks.join('
'),
+ btn1Name: Msg.Lang['editor.cancel'],
+ btn2Name: Msg.Lang['editor.ignoreBlocks'],
+ btn3Name: Msg.Lang['editor.loadCode']
+ })
+ LayerExt.open({
+ title: Msg.Lang['editor.parseMixErrorInfo'],
+ id: 'parse-mix-error-layer',
+ area: ['50%', '250px'],
+ max: ['500px', '250px'],
+ min: ['350px', '100px'],
+ shade: LayerExt.SHADE_ALL,
+ content: renderStr,
+ borderRadius: '5px',
+ success: (layero, index) => {
+ $('#parse-mix-error-layer').css('overflow', 'hidden');
+ form.render(null, 'parse-mix-error-filter');
+ layero.find('button').click((event) => {
+ layer.close(index);
+ const mId = $(event.currentTarget).attr('m-id');
+ switch (mId) {
+ case '0':
+ break;
+ case '1':
+ for (let i of undefinedBlocks) {
+ xml.find('*[type='+i+']').remove();
+ }
+ this.parseMix(xml, false, true, endFunc);
+ break;
+ case '2':
+ this.parseMix(xml, true, false, endFunc);
+ break;
+ }
+ });
+ }
+ });
+ }
+}
+
+Mixly.EditorMix = EditorMix;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-monaco.js b/mixly/common/modules/mixly-modules/common/editor-monaco.js
new file mode 100644
index 00000000..0c3180f4
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-monaco.js
@@ -0,0 +1,396 @@
+goog.loadJs('common', () => {
+
+goog.require('Diff');
+goog.require('monaco');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Events');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorBase');
+goog.provide('Mixly.EditorMonaco');
+
+const {
+ XML,
+ Env,
+ Msg,
+ Debug,
+ Events,
+ HTMLTemplate,
+ EditorBase
+} = Mixly;
+
+
+class EditorMonaco extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-code.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-code.html')))
+ );
+
+ this.$monaco = $('
');
+ this.editor = null;
+ this.events = new Events(['change']);
+
+ this.addEventsListener = () => {
+ const { editor } = this;
+ $('#mixly-footer-cursor').hide();
+
+ editor.onDidBlurEditorText(() => {
+ $('#mixly-footer-cursor').hide();
+ });
+
+ editor.onDidFocusEditorText(() => {
+ const position = editor.getPosition();
+ $('#mixly-footer-row').html(position.lineNumber);
+ $('#mixly-footer-column').html(position.column);
+ const selection = editor.getSelection();
+ if (selection.isEmpty()) {
+ $('#mixly-footer-selected').parent().hide();
+ } else {
+ const text = editor.getModel().getValueInRange(selection);
+ $('#mixly-footer-selected').parent().css('display', 'inline-flex');
+ $('#mixly-footer-selected').html(text.length);
+ }
+ $('#mixly-footer-cursor').show();
+ });
+
+ editor.onDidChangeCursorPosition((e) => {
+ $('#mixly-footer-row').html(e.position.lineNumber);
+ $('#mixly-footer-column').html(e.position.column);
+ });
+
+ editor.onDidChangeCursorSelection((e) => {
+ if (e.selection.isEmpty()) {
+ $('#mixly-footer-selected').parent().hide();
+ } else {
+ const text = editor.getModel().getValueInRange(e.selection);
+ $('#mixly-footer-selected').parent().css('display', 'inline-flex');
+ $('#mixly-footer-selected').html(text.length);
+ }
+ });
+
+ editor.onDidChangeModelContent((...args) => {
+ this.events.run('change', ...args);
+ });
+ }
+
+ this.getEditor = () => {
+ return this.editor;
+ }
+
+ this.getContent = () => {
+ return this.$monaco;
+ }
+
+ this.initMonaco = () => {
+ this.editor = monaco.editor.create(this.$monaco[0], {
+ theme: 'vs-dark',
+ disableLayerHinting: true, // 等宽优化
+ emptySelectionClipboard: false, // 空选择剪切板
+ selectionClipboard: false, // 选择剪切板
+ codeLens: true, // 代码镜头
+ scrollBeyondLastLine: true, // 滚动完最后一行后再滚动一屏幕
+ colorDecorators: true, // 颜色装饰器
+ accessibilitySupport: 'off', // 辅助功能支持 "auto" | "off" | "on"
+ lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
+ lineNumbersMinChars: 5, // 行号最小字符 number
+ enableSplitViewResizing: false,
+ contextmenu: false,
+ fontSize: 17,
+ automaticLayout: false,
+ wordWrap: 'wordWrapColumn',
+ wordWrapColumn: 300,
+ scrollbar: {
+ vertical: 'visible',
+ horizontal: 'visible',
+ verticalScrollbarSize: 10,
+ horizontalScrollbarSize: 10
+ }
+ });
+ this.addEventsListener();
+ }
+
+ this.initMonaco();
+ }
+
+ #readOnly_ = false;
+ #changeListener_ = null;
+ #enableChangeEvent_ = true;
+ #editor_ = null;
+ #state_ = null;
+ #tabSize_ = null;
+ #language_ = null;
+ #mode_ = null;
+
+ constructor() {
+ super();
+ const editorHTMLTemplate = HTMLTemplate.get('html/editor/editor-code.html');
+ this.setContent($(editorHTMLTemplate.render()));
+ this.addEventsType(['change']);
+ }
+
+ init() {
+ super.init();
+ this.#editor_ = monaco.editor.createModel('');
+ this.#editor_.setEOL(monaco.editor.EndOfLineSequence.LF);
+ }
+
+ onMounted() {
+ super.onMounted();
+ const editor = EditorMonaco.getEditor();
+ editor.setModel(this.#editor_);
+ if (this.#state_) {
+ editor.restoreViewState(this.#state_);
+ }
+ this.setReadOnly(this.#readOnly_);
+ this.getContent().append(EditorMonaco.getContent());
+ if (!this.#readOnly_) {
+ this.focus();
+ }
+ this.#addChangeEventListener_();
+ }
+
+ disableChangeEvent() {
+ this.#enableChangeEvent_ = false;
+ }
+
+ enableChangeEvent() {
+ this.#enableChangeEvent_ = true;
+ }
+
+ #addChangeEventListener_() {
+ const $content = EditorMonaco.getContent();
+ this.#changeListener_ = EditorMonaco.events.bind('change', () => {
+ this.#enableChangeEvent_ && this.runEvent('change');
+ });
+ }
+
+ #removeChangeEventListener_() {
+ this.#changeListener_ && EditorMonaco.events.unbind(this.#changeListener_);
+ this.offEvent('change');
+ }
+
+ onUnmounted() {
+ super.onUnmounted();
+ const editor = EditorMonaco.getEditor();
+ const $content = EditorMonaco.getContent();
+ this.#state_ = editor.saveViewState();
+ $content.detach();
+ this.getContent().empty();
+ this.#removeChangeEventListener_();
+ }
+
+ dispose() {
+ this.#removeChangeEventListener_();
+ this.#editor_.dispose();
+ super.dispose();
+ this.#editor_ = null;
+ }
+
+ setTheme(mode) {
+ if (this.#mode_ === mode) {
+ return;
+ }
+ const editor = EditorMonaco.getEditor();
+ editor.updateOptions({
+ theme: `vs-${mode}`
+ });
+ this.#mode_ = mode;
+ }
+
+ getTheme() {
+ return this.#mode_;
+ }
+
+ setValue(data) {
+ if (this.getValue() === data) {
+ return;
+ }
+ this.#editor_.setValue(data);
+ }
+
+ addValue(data) {
+ const prevData = this.getValue();
+ this.setValue(prevData + data);
+ }
+
+ getValue() {
+ return this.#editor_.getValue();
+ }
+
+ diffEdits(data) {
+ const prevData = this.getValue();
+ if (prevData === data) {
+ return;
+ }
+ const edits = this.buildDiffEdits(prevData, data);
+ if (!edits.length) {
+ return;
+ }
+ this.#editor_.pushEditOperations([], edits, () => null);
+ }
+
+ buildDiffEdits(prevData, nextData) {
+ if (prevData === nextData) {
+ return [];
+ }
+ const diffs = Diff.diffChars(prevData, nextData);
+ const edits = [];
+ const state = {
+ index: 0,
+ pendingStart: null,
+ pendingText: ''
+ };
+ for (const d of diffs) {
+ if (d.added) {
+ if (state.pendingStart == null) {
+ state.pendingStart = state.index;
+ }
+ state.pendingText += d.value;
+ }
+ else if (d.removed) {
+ if (state.pendingStart == null) {
+ state.pendingStart = state.index;
+ }
+ state.index += d.value.length;
+ }
+ else {
+ this.flushPendingEdit(state, edits);
+ state.index += d.value.length;
+ }
+ }
+ this.flushPendingEdit(state, edits);
+ return edits;
+ }
+
+ flushPendingEdit(state, edits) {
+ const { pendingStart, index, pendingText } = state;
+ if (pendingStart == null) {
+ return;
+ }
+ const startPos = this.#editor_.getPositionAt(pendingStart);
+ const endPos = this.#editor_.getPositionAt(index);
+ edits.push({
+ range: new monaco.Range(
+ startPos.lineNumber,
+ startPos.column,
+ endPos.lineNumber,
+ endPos.column
+ ),
+ text: pendingText,
+ forceMoveMarkers: true
+ });
+ state.pendingStart = null;
+ state.pendingText = '';
+ }
+
+ clear() {
+ this.setValue('', true);
+ }
+
+ scrollToBottom() {
+ const editor = EditorMonaco.getEditor();
+ editor.setScrollTop(editor.getScrollHeight());
+ }
+
+ scrollToTop() {
+ const editor = EditorMonaco.getEditor();
+ editor.setScrollTop(0);
+ }
+
+ resize() {
+ super.resize();
+ const editor = EditorMonaco.getEditor();
+ editor.layout(null, true);
+ }
+
+ undo() {
+ this.#editor_.undo();
+ }
+
+ redo() {
+ this.#editor_.redo();
+ }
+
+ cut() {
+ const editor = EditorMonaco.getEditor();
+ let selection = editor.getSelection();
+ let selectedText = this.#editor_.getValueInRange(selection);
+ if (selection) {
+ editor.executeEdits('cut', [{ range: selection, text: '' }]);
+ navigator.clipboard.writeText(selectedText);
+ }
+ this.focus();
+ }
+
+ copy() {
+ const editor = EditorMonaco.getEditor();
+ editor.trigger('source', 'editor.action.clipboardCopyWithSyntaxHighlightingAction');
+ }
+
+ paste() {
+ const editor = EditorMonaco.getEditor();
+ navigator.clipboard.readText()
+ .then((clipboardText) => {
+ editor.trigger('source', 'type', { text: clipboardText });
+ this.focus();
+ })
+ .catch(Debug.error);
+ }
+
+ getEditor() {
+ return this.#editor_;
+ }
+
+ setReadOnly(readOnly) {
+ const editor = EditorMonaco.getEditor();
+ editor.updateOptions({ readOnly });
+ this.#readOnly_ = readOnly;
+ }
+
+ setLanguage(language) {
+ if (this.#language_ === language) {
+ return;
+ }
+ this.#language_ = language;
+ monaco.editor.setModelLanguage(this.#editor_, language);
+ }
+
+ getLanguage() {
+ return this.#language_;
+ }
+
+ setTabSize(tabSize) {
+ if (this.#tabSize_ === tabSize) {
+ return;
+ }
+ this.#tabSize_ = tabSize;
+ this.#editor_.updateOptions({ tabSize });
+ }
+
+ setFontSize(fontSize) {
+ const editor = EditorMonaco.getEditor();
+ editor.updateOptions({ fontSize });
+ }
+
+ focus() {
+ const editor = EditorMonaco.getEditor();
+ editor.focus();
+ }
+
+ commentLine() {
+ const editor = EditorMonaco.getEditor();
+ EditorMonaco.getEditor().trigger('source', 'editor.action.commentLine');
+ }
+
+ blockComment() {
+ const editor = EditorMonaco.getEditor();
+ editor.trigger('source', 'editor.action.blockComment');
+ }
+}
+
+Mixly.EditorMonaco = EditorMonaco;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-unknown.js b/mixly/common/modules/mixly-modules/common/editor-unknown.js
new file mode 100644
index 00000000..5f6c755c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-unknown.js
@@ -0,0 +1,35 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorBase');
+goog.provide('Mixly.EditorUnknown');
+
+const {
+ XML,
+ Env,
+ HTMLTemplate,
+ EditorBase
+} = Mixly;
+
+class EditorUnknown extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-unknown.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-unknown.html')))
+ );
+ }
+
+ constructor() {
+ super();
+ this.setContent(
+ $(HTMLTemplate.get('html/editor/editor-unknown.html').render())
+ );
+ }
+}
+
+Mixly.EditorUnknown = EditorUnknown;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editor-welcome.js b/mixly/common/modules/mixly-modules/common/editor-welcome.js
new file mode 100644
index 00000000..fcc061e1
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editor-welcome.js
@@ -0,0 +1,34 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorBase');
+goog.provide('Mixly.EditorWelcome');
+
+const {
+ XML,
+ Env,
+ HTMLTemplate,
+ EditorBase
+} = Mixly;
+
+class EditorWelcome extends EditorBase {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-welcome.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-welcome.html')))
+ );
+ }
+
+ constructor() {
+ super();
+ this.setContent(
+ $(HTMLTemplate.get('html/editor/editor-welcome.html').render())
+ );
+ }
+}
+
+Mixly.EditorWelcome = EditorWelcome;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/editors-manager.js b/mixly/common/modules/mixly-modules/common/editors-manager.js
new file mode 100644
index 00000000..b0dc0d21
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/editors-manager.js
@@ -0,0 +1,144 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Nav');
+goog.require('Mixly.EditorMix');
+goog.require('Mixly.EditorCode');
+goog.require('Mixly.EditorMd');
+goog.require('Mixly.EditorBlockly');
+goog.require('Mixly.EditorUnknown');
+goog.require('Mixly.EditorWelcome');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Debug');
+goog.require('Mixly.PagesManager');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Electron.FS');
+goog.require('Mixly.Web.FS');
+goog.provide('Mixly.EditorsManager');
+
+const {
+ XML,
+ Env,
+ Nav,
+ EditorMix,
+ EditorCode,
+ EditorMd,
+ EditorBlockly,
+ EditorUnknown,
+ EditorWelcome,
+ Registry,
+ Debug,
+ PagesManager,
+ HTMLTemplate,
+ Electron = {},
+ Web = {}
+} = Mixly;
+
+const { FS } = goog.isElectron? Electron : Web;
+
+class EditorsManager extends PagesManager {
+ static {
+ HTMLTemplate.add(
+ 'html/editor/editor-manager.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-manager.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/editor/editor-tab.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/editor/editor-tab.html')))
+ );
+
+ this.typesRegistry = new Registry();
+ this.typesRegistry.register(['.mix', '.mil'], EditorMix);
+ this.typesRegistry.register(['.txt', '.ino', '.json'], EditorCode);
+ this.typesRegistry.register('.md', EditorMd);
+ this.typesRegistry.register('#default', EditorUnknown);
+ this.typesRegistry.register('#welcome', EditorWelcome);
+ }
+
+ constructor(element) {
+ const managerHTMLTemplate = HTMLTemplate.get('html/editor/editor-manager.html');
+ const tabHTMLTemplate = HTMLTemplate.get('html/editor/editor-tab.html');
+ const $manager = $(managerHTMLTemplate.render());
+ const $tab = $(tabHTMLTemplate.render());
+ super({
+ parentElem: element,
+ managerContentElem: $manager[0],
+ bodyElem: $manager.find('.body')[0],
+ tabElem: $manager.find('.tabs')[0],
+ tabContentElem: $tab[0],
+ typesRegistry: EditorsManager.typesRegistry
+ });
+ this.#addEventsListenerExt_();
+ }
+
+ #addEventsListenerExt_() {
+ const editorTabs = this.getTabs();
+
+ // active Tab被改变时触发
+ editorTabs.bind('activeTabChange', (event) => {
+ const $btnsContainer = Nav.getMain().getEditorBtnsContainer();
+ $btnsContainer.children('div').detach();
+ const { tabEl } = event.detail;
+ const id = $(tabEl).attr('data-tab-id');
+ const editor = this.get(id);
+ const $btns = editor.getBtnsContent();
+ if ($btns && $btns.length) {
+ $btnsContainer.removeClass('empty');
+ $btnsContainer.children('a').css('display', 'none');
+ $btnsContainer.append($btns);
+ } else {
+ $btnsContainer.addClass('empty');
+ $btnsContainer.children('a').css('display', 'block');
+ }
+ Nav.getMain().resize();
+ });
+
+ // 添加新Tab时触发
+ editorTabs.bind('tabCreated', (event) => {
+ const { tabEl } = event.detail;
+ const id = $(tabEl).attr('data-tab-id');
+ const editor = this.get(id);
+ FS.isFile(id)
+ .then((isFile) => {
+ if (!isFile) {
+ return;
+ }
+ return FS.readFile(id);
+ })
+ .then((data) => {
+ data && editor.setValue(data, path.extname(id));
+ })
+ .catch(Debug.error);
+ const tabs = this.getTabs();
+ editor.bind('addDirty', () => {
+ tabs.updateTab(id, {
+ title: id + ' - 未保存'
+ });
+ });
+ editor.bind('removeDirty', () => {
+ tabs.updateTab(id, {
+ title: id
+ });
+ });
+ });
+
+ // 移除已有Tab时触发
+ editorTabs.bind('tabDestroyed', (event) => {
+ if (this.length()) {
+ return;
+ }
+ const $btnsContainer = Nav.getMain().getEditorBtnsContainer();
+ $btnsContainer.children('div').detach();
+ $btnsContainer.children('a').css('display', 'block');
+ $btnsContainer.addClass('empty');
+ Nav.getMain().resize();
+ });
+ }
+}
+
+Mixly.EditorsManager = EditorsManager;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/env.js b/mixly/common/modules/mixly-modules/common/env.js
new file mode 100644
index 00000000..683d6e5b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/env.js
@@ -0,0 +1,141 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.Url');
+goog.provide('Mixly.Env');
+
+const fs_plus = Mixly.require('fs-plus');
+const electron_remote = Mixly.require('@electron/remote');
+const timers = Mixly.require('node:timers');
+const { Env, Url } = Mixly;
+
+/**
+ * 检测是否启用node服务器
+ * @type {Boolean}
+ */
+Env.hasSocketServer = false;
+
+/**
+ * 检测是否启用node编译服务器
+ * @type {Boolean}
+ */
+Env.hasCompiler = false;
+
+/**
+ * 获取当前mixly的路径
+ * @type {String}
+ */
+Env.clientPath = null;
+
+/**
+ * 检测当前系统
+ * @type {String} win32、darwin、linux
+ */
+Env.currentPlatform = goog.platform();
+
+/**
+ * 对于win系统,获取免安装python3的路径,对于mac与linux,则此变量值为python3
+ * @type {String}
+ */
+Env.python3Path = null;
+
+/**
+ * 获取mixly.py的路径
+ * @type {String}
+ */
+Env.pyFilePath = null;
+
+let htmlPath = decodeURIComponent((new URL($('html')[0].baseURI)).pathname);
+let baseJsPath = decodeURIComponent((new URL(goog.basePath)).pathname);
+
+if (goog.isElectron && Env.currentPlatform === 'win32') {
+ htmlPath = htmlPath.substring(1);
+ baseJsPath = baseJsPath.substring(1);
+}
+
+/**
+ * 获取板卡index或主页面index的路径
+ * @type {String}
+ */
+Env.indexDirPath = path.join(htmlPath, '..');
+
+/**
+ * 资源文件夹所在路径
+ * @type {String}
+ */
+Env.srcDirPath = path.join(Env.indexDirPath, '..');
+
+/**
+ * 获取板卡index或主页面index的缩放比例
+ * @type {String}
+ */
+Env.winSize = null;
+
+/**
+ * 获取板卡index默认xml
+ * @type {String}
+ */
+Env.defaultXML = null;
+
+/**
+ * 获取第三方库所用css
+ * @type {Array}
+ */
+Env.thirdPartyCSS = [];
+
+/**
+ * 获取第三方库所用js
+ * @type {Array}
+ */
+Env.thirdPartyJS = [];
+
+/**
+ * 默认模板路径
+ * @type {String}
+ */
+Env.templatePath = path.join(baseJsPath, '../templates');
+
+/**
+ * 语言文件路径
+ * @type {String}
+ */
+Env.msgPath = path.join(baseJsPath, '../msg');
+
+/**
+ * 模板index所在路径
+ * @type {String}
+ */
+const urlConfig = Url.getConfig() ?? {};
+Env.boardIndexPath = path.join(Env.indexDirPath, '../', urlConfig.boardIndex ?? 'index.xml');
+
+/**
+ * 模板index所在目录路径
+ * @type {String}
+ */
+Env.boardDirPath = path.join(Env.boardIndexPath, '..');
+
+if (goog.isElectron) {
+ window.setInterval = timers.setInterval;
+ window.clearInterval = timers.clearInterval;
+ window.setTimeout = timers.setTimeout;
+ window.clearTimeout = timers.clearTimeout;
+ window.setImmediate = timers.setImmediate;
+ window.clearImmediate = timers.clearImmediate;
+ const { app } = electron_remote;
+ if (Env.currentPlatform === 'darwin') {
+ Env.clientPath = path.join(app.getPath('exe'), '../../../../');
+ } else {
+ Env.clientPath = path.join(app.getPath('exe'), '../');
+ }
+ Env.pyFilePath = path.join(Env.clientPath, 'mixpyBuild/mixly.py');
+ if (Env.currentPlatform === 'win32') {
+ Env.python3Path = path.join(Env.clientPath, 'mixpyBuild/win_python3/python3.exe');
+ } else {
+ Env.python3Path = '/usr/bin/python3';
+ if (fs_plus.isFileSync('/usr/local/bin/python3')) {
+ Env.python3Path = '/usr/local/bin/python3';
+ }
+ }
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/events.js b/mixly/common/modules/mixly-modules/common/events.js
new file mode 100644
index 00000000..1ec7d697
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/events.js
@@ -0,0 +1,98 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.MArray');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Debug');
+goog.provide('Mixly.Events');
+
+const {
+ IdGenerator,
+ MArray,
+ Registry,
+ Debug
+} = Mixly;
+
+class Events {
+ #events_ = new Registry();
+ #eventsType_ = null;
+
+ constructor(eventsType = []) {
+ this.#eventsType_ = eventsType;
+ }
+
+ addType(eventsType) {
+ this.#eventsType_ = MArray.unique([...this.#eventsType_, ...eventsType]);
+ }
+
+ exist(type) {
+ if (!this.#eventsType_.includes(type)) {
+ Debug.warn(`${type} event does not exist under the class`);
+ return false;
+ }
+ return true;
+ }
+
+ bind(type, func) {
+ if (!this.exist(type)) {
+ return this;
+ }
+ const id = IdGenerator.generate();
+ let typeEvent = this.#events_.getItem(type);
+ if (!typeEvent) {
+ typeEvent = new Registry();
+ this.#events_.register(type, typeEvent);
+ }
+ typeEvent.register(id, func);
+ return id;
+ }
+
+ unbind(id) {
+ for (let [_, value] of this.#events_.getAllItems()) {
+ let typeEvent = value;
+ if (!typeEvent.getItem(id)) {
+ continue;
+ }
+ typeEvent.unregister(id);
+ }
+ return this;
+ }
+
+ off(type) {
+ if (this.#events_.getItem(type)) {
+ this.#events_.unregister(type);
+ }
+ return this;
+ }
+
+ run(type, ...args) {
+ let outputs = [];
+ if (!this.exist(type)) {
+ return outputs;
+ }
+ const eventsFunc = this.#events_.getItem(type);
+ if (!eventsFunc) {
+ return outputs;
+ }
+ for (let [_, func] of eventsFunc.getAllItems()) {
+ outputs.push(func(...args));
+ }
+ return outputs;
+ }
+
+ reset() {
+ this.#events_.reset();
+ }
+
+ length(type) {
+ const typeEvent = this.#events_.getItem(type);
+ if (typeEvent) {
+ return typeEvent.length();
+ }
+ return 0;
+ }
+}
+
+Mixly.Events = Events;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/file-tree.js b/mixly/common/modules/mixly-modules/common/file-tree.js
new file mode 100644
index 00000000..4ea1ca68
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/file-tree.js
@@ -0,0 +1,998 @@
+goog.loadJs('common', () => {
+
+goog.require('XScrollbar');
+goog.require('path');
+goog.require('$.jstree');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Config');
+goog.require('Mixly.Events');
+goog.require('Mixly.Menu');
+goog.require('Mixly.ContextMenu');
+goog.require('Mixly.Registry');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Component');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.FileTree');
+
+const {
+ Env,
+ Msg,
+ Config,
+ Menu,
+ Events,
+ ContextMenu,
+ Registry,
+ IdGenerator,
+ Debug,
+ Component,
+ HTMLTemplate
+} = Mixly;
+
+const { USER } = Config;
+
+class FileTree extends Component {
+ static {
+ this.FILE_ICON_MAP = goog.readJsonSync(path.join(Env.templatePath, 'json/file-icons.json'));
+ this.FOLDER_ICON_MAP = goog.readJsonSync(path.join(Env.templatePath, 'json/folder-icons.json'));
+
+ HTMLTemplate.add(
+ 'html/file-tree.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/file-tree.html')))
+ );
+ }
+
+ #$openFolderContent_ = null;
+ #$folderContent_ = null;
+ #$rootFolder_ = null;
+ #$iconTriangle_ = null;
+ #$iconFolder_ = null;
+ #$name_ = null;
+ #$children_ = null;
+ #$progress_ = null;
+ #$mask_ = null;
+ #$fileTree_ = null;
+ #mprogress_ = null;
+ #rootFolderOpened_ = false;
+ #rootPath_ = '';
+ #rootName_ = '';
+ #rootTitle_ = '';
+ #fs_ = null;
+ #contextMenu_ = null;
+ #selected_ = null;
+ #jstree_ = null;
+ #scrollbar_ = null;
+
+ constructor(fs) {
+ super();
+ const $content = $(HTMLTemplate.get('html/file-tree.html').render());
+ this.setContent($content);
+ this.#$rootFolder_ = $content.find('.folder-title');
+ this.#$iconTriangle_ = this.#$rootFolder_.find('.triangle');
+ this.#$iconFolder_ = this.#$rootFolder_.find('.folder');
+ this.#$name_ = this.#$rootFolder_.find('.name');
+ this.#$children_ = $content.find('.children');
+ this.#$progress_ = $content.children('.progress');
+ this.#$mask_ = $content.children('.mask');
+ this.#fs_ = fs;
+ this.#rootPath_ = '';
+ this.#scrollbar_ = new XScrollbar(this.#$children_[0], {
+ onlyHorizontal: false,
+ thumbSize: '4px',
+ thumbRadius: '1px',
+ thumbBackground: USER.theme === 'dark'? '#b0b0b0' : '#5f5f5f'
+ });
+ this.#$fileTree_ = $(this.#scrollbar_.$content);
+ this.#$fileTree_.jstree({
+ core: {
+ strings: {
+ 'Loading ...': Msg.Lang['fileTree.loading'] + '...'
+ },
+ multiple: false,
+ animation: false,
+ worker: false,
+ dblclick_toggle: false,
+ check_callback: function(operation, node, parent, position, more) {
+ if(operation === 'copy_node' || operation === 'move_node') {
+ if(parent.id === '#') {
+ return false;
+ }
+ }
+ return true;
+ },
+ data: (node, cb) => {
+ if (!this.#rootPath_) {
+ cb([]);
+ return;
+ }
+ this.showProgress();
+ let folderPath = this.#rootPath_;
+ if(node.id !== '#') {
+ let $li = this.#jstree_.get_node(node, true);
+ let $i = $li.find('.jstree-anchor > .jstree-icon');
+ $i.addClass('layui-anim layui-anim-fadein layui-anim-fadeout layui-anim-loop');
+ folderPath = node.id;
+ }
+ this.#getChildren_(folderPath)
+ .then((data) => {
+ cb(data);
+ })
+ .catch(Debug.error)
+ .finally(() => this.hideProgress());
+ },
+ themes: {
+ dots: true,
+ name: USER.theme === 'light'? 'default' : 'default-dark',
+ responsive: false,
+ ellipsis: true
+ }
+ },
+ plugins: ['wholerow', 'unique']
+ });
+ this.#jstree_ = this.#$fileTree_.jstree(true);
+ this.addEventsType([
+ 'beforeSelectLeaf', 'afterSelectLeaf', 'afterOpenNode', 'afterCloseNode', 'afterRefreshNode',
+ 'afterCreateNode', 'afterDeleteNode', 'afterRenameNode'
+ ]);
+ this.#addEventsListener_();
+ this.#addContextMenu_();
+ this.nodeAliveRegistry = new Registry();
+ this.delayRefreshRegistry = new Registry();
+ this.watchRegistry = new Registry();
+ }
+
+ #addEventsListener_() {
+ this.#$fileTree_
+ .on('click.jstree', '.jstree-open>a', ({ target }) => {
+ setTimeout(() => {
+ $(target).parent().removeClass('jstree-leaf').addClass('jstree-opened');
+ this.#jstree_.close_node(target);
+ });
+ })
+ .on('click.jstree', '.jstree-closed>a', ({ target }) => {
+ setTimeout(() => {
+ $(target).parent().removeClass('jstree-leaf').addClass('jstree-closed');
+ this.#jstree_.open_node(target);
+ });
+ })
+ .on('open_node.jstree', (e, data) => {
+ const { id } = data.node;
+ let elem = document.getElementById(id);
+ let $i = $(elem).children('.jstree-anchor').children('.jstree-icon');
+ $i.addClass('opened');
+ })
+ .on('close_node.jstree', (e, data) => {
+ const { id } = data.node;
+ let elem = document.getElementById(id);
+ let $i = $(elem).children('.jstree-anchor').children('.jstree-icon');
+ $i.removeClass('opened');
+ })
+ .on('after_open.jstree', (e, data) => {
+ const { id } = data.node;
+ const eventId = this.nodeAliveRegistry.getItem(id);
+ if (eventId) {
+ clearTimeout(eventId);
+ this.nodeAliveRegistry.unregister(id);
+ } else {
+ this.watchFolder(id);
+ }
+ this.runEvent('afterOpenNode', data);
+ this.reselect();
+ })
+ .on('after_close.jstree', (e, data) => {
+ const { id } = data.node;
+ const eventId = setTimeout(() => {
+ this.unwatchFolder(id);
+ }, 60 * 1000);
+ if (!this.nodeAliveRegistry.getItem(id)) {
+ this.nodeAliveRegistry.register(id, eventId);
+ }
+ this.runEvent('afterCloseNode', data);
+ this.reselect();
+ })
+ .on('changed.jstree', (e, data) => {
+ const selected = data.instance.get_selected(true);
+ if (!selected.length) {
+ // this.#selected_ = null;
+ return;
+ }
+ if ((selected[0].icon || '').indexOf('foldericon') !== -1) {
+ this.reselect();
+ return;
+ }
+ if (selected[0].id === this.#selected_) {
+ return;
+ }
+ const result = this.runEvent('beforeSelectLeaf', selected);
+ if ((result.length && result[0]) || !result.length) {
+ this.#selected_ = selected[0].id;
+ this.runEvent('afterSelectLeaf', selected);
+ } else {
+ this.deselect(selected[0].id);
+ this.reselect();
+ }
+ })
+ .on('refresh.jstree', (e, data) => {
+ this.runEvent('afterRefreshNode', data.node);
+ })
+ .on('refresh_node.jstree', (e, data) => {
+ this.runEvent('afterRefreshNode', data.node);
+ });
+
+ this.#$rootFolder_.click(() => {
+ if (this.isRootFolderOpened()) {
+ this.closeRootFolder();
+ } else {
+ this.openRootFolder();
+ this.reselect();
+ }
+ });
+ }
+
+ #addContextMenu_() {
+ const selector = `div[page-id="${this.getId()}"] .jstree-node, div[page-id="${this.getId()}"] > button`;
+ this.#contextMenu_ = new ContextMenu(selector, {
+ zIndex: 300,
+ events: {
+ hide: ({ $trigger }) => {
+ $trigger.removeClass('active');
+ },
+ activated: ({ $trigger }) => {
+ $trigger.addClass('active');
+ }
+ }
+ });
+ this.#addFileContextMenuItems_();
+ this.#contextMenu_.bind('getMenu', () => 'menu');
+ }
+
+ #addFileContextMenuItems_() {
+ let menu = new Menu();
+ menu.add({
+ weight: 0,
+ id: 'new_folder',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['root', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['fileTree.newFolder'], ''),
+ callback: (_, { $trigger }) => {
+ const type = $trigger.attr('type');
+ if (type === 'root') {
+ this.openRootFolder();
+ this.createRootChildFolderNode();
+ } else {
+ const id = $trigger.attr('id');
+ this.createFolderNode(id);
+ }
+ }
+ }
+ });
+ menu.add({
+ weight: 1,
+ id: 'new_file',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['root', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['fileTree.newFile'], ''),
+ callback: (_, { $trigger }) => {
+ const type = $trigger.attr('type');
+ if (type === 'root') {
+ this.openRootFolder();
+ this.createRootChildFileNode();
+ } else {
+ const id = $trigger.attr('id');
+ this.createFileNode(id);
+ }
+ }
+ }
+ });
+ menu.add({
+ weight: 2,
+ id: 'sep1',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['root', 'folder'].includes(type);
+ },
+ data: '---------'
+ });
+ menu.add({
+ weight: 3,
+ id: 'cut',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['file', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.cut'], ''),
+ callback: (_, { $trigger }) => {
+ const id = $trigger.attr('id');
+ this.cutNode(id);
+ }
+ }
+ });
+ menu.add({
+ weight: 4,
+ id: 'copy',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['file', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.copy'], ''),
+ callback: (_, { $trigger }) => {
+ const id = $trigger.attr('id');
+ this.copyNode(id);
+ }
+ }
+ });
+ menu.add({
+ weight: 5,
+ id: 'paste',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['root', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.paste'], ''),
+ callback: (_, { $trigger }) => {
+ const type = $trigger.attr('type');
+ if (type === 'root') {
+ this.openRootFolder();
+ this.pasteNode('/');
+ } else {
+ const id = $trigger.attr('id');
+ this.pasteNode(id);
+ }
+ }
+ }
+ });
+ menu.add({
+ weight: 6,
+ id: 'sep2',
+ data: '---------'
+ });
+ menu.add({
+ weight: 7,
+ id: 'copy_path',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['fileTree.copyPath'], ''),
+ callback: (_, { $trigger }) => {
+ let outPath = null;
+ const type = $trigger.attr('type');
+ if (type === 'root') {
+ outPath = this.#rootPath_;
+ } else {
+ outPath = $trigger.attr('id');
+ }
+ navigator.clipboard.writeText(outPath)
+ .catch(Debug.error);
+ }
+ }
+ });
+ menu.add({
+ weight: 8,
+ id: 'rename',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['file', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['fileTree.rename'], ''),
+ callback: (_, { $trigger }) => {
+ const type = $trigger.attr('type');
+ const id = $trigger.attr('id');
+ if (type === 'folder') {
+ this.renameFolderNode(id);
+ } else {
+ this.renameFileNode(id);
+ }
+ }
+ }
+ });
+ menu.add({
+ weight: 9,
+ id: 'del',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ return ['file', 'folder'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['fileTree.delete'], ''),
+ callback: (_, { $trigger }) => {
+ const type = $trigger.attr('type');
+ const id = $trigger.attr('id');
+ if (type === 'folder') {
+ if (this.#selected_) {
+ const relative = path.relative(id, this.#selected_);
+ if (relative.indexOf('../') !== 0) {
+ this.deselect(this.#selected_);
+ }
+ }
+ this.deleteFolderNode(id);
+ } else {
+ if (this.#selected_ === id) {
+ this.deselect(id);
+ }
+ this.deleteFileNode(id);
+ }
+ }
+ }
+ });
+ this.#contextMenu_.register('menu', menu);
+ }
+
+ getContextMenu() {
+ return this.#contextMenu_;
+ }
+
+ openRootFolder() {
+ if (this.isRootFolderOpened()) {
+ return;
+ }
+ this.#$iconTriangle_.removeClass('codicon-chevron-right');
+ this.#$iconTriangle_.addClass('codicon-chevron-down');
+ this.#$iconFolder_.addClass('opened');
+ this.#$rootFolder_.addClass('opened');
+ this.#$children_.css('display', 'block');
+ this.#rootFolderOpened_ = true;
+ }
+
+ closeRootFolder() {
+ if (!this.isRootFolderOpened()) {
+ return;
+ }
+ this.#$iconTriangle_.removeClass('codicon-chevron-down');
+ this.#$iconTriangle_.addClass('codicon-chevron-right');
+ this.#$iconFolder_.removeClass('opened');
+ this.#$rootFolder_.removeClass('opened');
+ this.#$children_.css('display', 'none');
+ const selected = this.#selected_;
+ this.deselectAll();
+ this.#selected_ = selected;
+ this.#rootFolderOpened_ = false;
+ }
+
+ isRootFolderOpened() {
+ return this.#rootFolderOpened_;
+ }
+
+ setFolderPath(folderPath) {
+ let newFolderPath = path.join(folderPath);
+ if (newFolderPath === this.#rootPath_) {
+ this.#jstree_.refresh();
+ return;
+ }
+ if (this.#rootPath_) {
+ this.unwatchFolder(this.#rootPath_);
+ }
+ this.#rootPath_ = newFolderPath;
+ this.nodeAliveRegistry.reset();
+ this.#jstree_.refresh();
+ this.watchFolder(this.#rootPath_);
+ this.setRootFolderTitle(this.#rootPath_);
+ const rootNodeName = path.basename(folderPath).toUpperCase();
+ this.setRootFolderName(rootNodeName);
+ }
+
+ getFolderPath() {
+ return this.#rootPath_;
+ }
+
+ setRootFolderName(name) {
+ this.#rootName_ = name;
+ this.#$name_.text(name);
+ }
+
+ getRootFolderName() {
+ return this.#rootName_;
+ }
+
+ setRootFolderTitle(name) {
+ this.#rootTitle_ = name;
+ this.#$rootFolder_.attr('title', name);
+ }
+
+ getRootFolderTitle() {
+ return this.#rootTitle_;
+ }
+
+ refreshFolder(folderPath) {
+ // 延迟刷新节点,防止过于频繁的IO操作
+ let eventId = this.delayRefreshRegistry.getItem(folderPath);
+ if (eventId) {
+ clearTimeout(eventId);
+ this.delayRefreshRegistry.unregister(folderPath);
+ }
+ eventId = setTimeout(() => {
+ if (folderPath === this.#rootPath_) {
+ this.#jstree_.refresh();
+ return;
+ }
+ const node = this.#jstree_.get_node(folderPath);
+ const nodeIsOpened = node && !this.isClosed(folderPath);
+ if (nodeIsOpened) {
+ this.watchFolder(folderPath);
+ this.clearFolderTemp(folderPath);
+ this.#jstree_.refresh_node(folderPath);
+ } else {
+ this.unwatchFolder(folderPath);
+ }
+ }, 500);
+ this.delayRefreshRegistry.register(folderPath, eventId);
+ }
+
+ clearFolderTemp(folderPath) {
+ const node = this.#jstree_.get_node(folderPath);
+ if (!node) {
+ return;
+ }
+ node.state.loaded = false;
+ }
+
+ watchFolder(folderPath) {
+ if (this.isWatched(folderPath)) {
+ return;
+ }
+ this.watchRegistry.register(folderPath, 'folder');
+ }
+
+ unwatchFolder(folderPath) {
+ if (!this.isWatched(folderPath)) {
+ return;
+ }
+ this.clearFolderTemp(folderPath);
+ const keys = this.nodeAliveRegistry.keys();
+ for (let key of keys) {
+ if (key.indexOf(folderPath) === -1) {
+ continue;
+ }
+ const eventId = this.nodeAliveRegistry.getItem(key);
+ if (eventId) {
+ clearTimeout(eventId);
+ this.nodeAliveRegistry.unregister(key);
+ }
+ }
+ this.watchRegistry.unregister(folderPath);
+ }
+
+ watchFile(filePath) {}
+
+ unwatchFile(filePath) {}
+
+ isWatched(inPath) {
+ return !!this.watchRegistry.getItem(inPath);
+ }
+
+ isClosed(inPath) {
+ return this.#jstree_.is_closed(inPath);
+ }
+
+ select(inPath) {
+ let elem = document.getElementById(inPath);
+ if (!elem) {
+ this.#selected_ = null;
+ return;
+ }
+ this.#selected_ = inPath;
+ this.#jstree_.select_node(inPath, true, true);
+ $(elem).children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
+ }
+
+ reselect() {
+ if (!this.#selected_) {
+ return;
+ }
+ let elem = document.getElementById(this.#selected_);
+ if (!elem) {
+ return;
+ }
+ this.#jstree_.select_node(this.#selected_, true, true);
+ $(elem).children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
+ }
+
+ deselect(inPath) {
+ if (this.#selected_ === inPath) {
+ this.#selected_ = null;
+ }
+ let elem = document.getElementById(inPath);
+ if (!elem) {
+ return;
+ }
+ this.#jstree_.deselect_node(elem, true);
+ $(elem).children('.jstree-wholerow').removeClass('jstree-wholerow-clicked');
+ }
+
+ deselectAll() {
+ this.#selected_ = null;
+ this.#jstree_.deselect_all();
+ }
+
+ getSelectedNodeId() {
+ return this.#selected_;
+ }
+
+ getNode(inPath) {
+ return this.#jstree_.get_node(inPath);
+ }
+
+ getSelectedNodes() {
+ return this.#jstree_.get_selected(true);
+ }
+
+ async #getChildren_(inPath) {
+ let output = [];
+ const content = await this.readFolder(inPath);
+ for (let item of content) {
+ const { type, id, title, children } = item;
+ const text = item.text ?? path.basename(id);
+ let icon = item.icon ?? 'icon-doc';
+ if (!item.icon) {
+ if (type === 'folder') {
+ icon = this.#getFolderIcon_(text);
+ } else {
+ icon = this.#getFileIcon_(text);
+ }
+ }
+ output.push({
+ text,
+ id,
+ children,
+ li_attr: {
+ type,
+ name: text,
+ title: title ?? id
+ },
+ icon
+ });
+ }
+ return output;
+ }
+
+ async readFolder(inPath) {
+ return [];
+ }
+
+ #getFileIcon_(filename) {
+ const prefix = 'fileicon-';
+ if (FileTree.FILE_ICON_MAP[filename]) {
+ return prefix + FileTree.FILE_ICON_MAP[filename];
+ }
+ const extname = path.extname(filename).toLowerCase();
+ if (FileTree.FILE_ICON_MAP[extname]) {
+ return prefix + FileTree.FILE_ICON_MAP[extname];
+ }
+ return prefix + FileTree.FILE_ICON_MAP['default'];
+ }
+
+ #getFolderIcon_(foldername) {
+ const prefix = 'foldericon-';
+ if (FileTree.FOLDER_ICON_MAP[foldername]) {
+ return prefix + FileTree.FOLDER_ICON_MAP[foldername];
+ }
+ return prefix + FileTree.FOLDER_ICON_MAP['default'];
+ }
+
+ createRootChildNode(type) {
+ this.showProgress();
+ this.hideMask();
+ const node = this.#jstree_.get_node('#');
+ const children = false;
+ let icon = 'foldericon-default';
+ if (type === 'file') {
+ icon = 'fileicon-mix';
+ }
+ const folderPath = this.#rootPath_;
+ this.#jstree_.create_node(node, { children, icon }, 'first', (childNode) => {
+ this.#jstree_.edit(childNode, '', (newNode) => {
+ this.showMask();
+ const desPath = path.join(folderPath, newNode.text);
+ this.#jstree_.delete_node(newNode);
+ const oldNode = this.#jstree_.get_node(desPath);
+ if (oldNode || !newNode.text) {
+ this.hideProgress();
+ return;
+ }
+ let createPromise = null;
+ if (type === 'file') {
+ createPromise = this.#fs_.createFile(desPath);
+ } else {
+ createPromise = this.#fs_.createDirectory(desPath);
+ }
+ createPromise
+ .catch(Debug.error)
+ .finally(() => {
+ this.hideProgress();
+ this.runEvent('afterCreateNode', folderPath);
+ });
+ });
+ });
+ }
+
+ createRootChildFileNode() {
+ this.createRootChildNode('file');
+ }
+
+ createRootChildFolderNode() {
+ this.createRootChildNode('folder');
+ }
+
+ createNode(type, folderPath) {
+ this.showProgress();
+ this.hideMask();
+ const node = this.#jstree_.get_node(folderPath);
+ const children = false;
+ let icon = 'foldericon-default';
+ if (type === 'file') {
+ icon = 'fileicon-mix';
+ }
+ if (folderPath === '#') {
+ folderPath = this.#rootPath_;
+ }
+ this.#jstree_.open_node(node, () => {
+ this.#jstree_.create_node(node, { children, icon }, 'first', (childNode) => {
+ this.#jstree_.edit(childNode, '', (newNode) => {
+ this.showMask();
+ this.#jstree_.delete_node(newNode);
+ if (!newNode.text) {
+ this.hideProgress();
+ return;
+ }
+ const desPath = path.join(folderPath, newNode.text);
+ const parentNode = this.#jstree_.get_node(folderPath) ?? {};
+ for (let nodeId of parentNode.children ?? []) {
+ if (nodeId !== desPath) {
+ continue;
+ }
+ this.hideProgress();
+ return;
+ }
+ let createPromise = null;
+ if (type === 'file') {
+ createPromise = this.#fs_.createFile(desPath);
+ } else {
+ createPromise = this.#fs_.createDirectory(desPath);
+ }
+ createPromise
+ .catch(Debug.error)
+ .finally(() => {
+ this.hideProgress();
+ this.runEvent('afterCreateNode', folderPath);
+ });
+ });
+ });
+ });
+ }
+
+ createFileNode(folderPath) {
+ this.createNode('file', folderPath);
+ }
+
+ createFolderNode(folderPath) {
+ this.createNode('folder', folderPath);
+ }
+
+ renameNode(type, inPath) {
+ this.showProgress();
+ this.hideMask();
+ const node = this.#jstree_.get_node(inPath);
+ const oldNodeName = node.text;
+ this.#jstree_.edit(node, oldNodeName, (newNode) => {
+ this.showMask();
+ const folderPath = path.join(inPath, '..');
+ const desPath = path.join(folderPath, newNode.text);
+ this.#jstree_.close_node(newNode);
+ if (oldNodeName === newNode.text) {
+ this.hideProgress();
+ return;
+ }
+ this.#jstree_.rename_node(newNode, oldNodeName);
+ const parentNode = this.#jstree_.get_node(folderPath) ?? {};
+ for (let nodeId of parentNode.children ?? []) {
+ if (nodeId !== desPath) {
+ continue;
+ }
+ this.hideProgress();
+ return;
+ }
+ let renamePromise = null;
+ if (type === 'file') {
+ renamePromise = this.#fs_.renameFile(inPath, desPath);
+ } else {
+ renamePromise = this.#fs_.renameDirectory(inPath, desPath);
+ }
+ renamePromise
+ .catch(Debug.error)
+ .finally(() => {
+ this.hideProgress();
+ this.runEvent('afterRenameNode', path.join(inPath, '..'));
+ });
+ });
+ }
+
+ renameFileNode(filePath) {
+ this.renameNode('file', filePath);
+ }
+
+ renameFolderNode(folderPath) {
+ this.renameNode('folder', folderPath);
+ }
+
+ deleteNode(type, inPath) {
+ this.showProgress();
+ let deletePromise = null;
+ if (type === 'file') {
+ deletePromise = this.#fs_.deleteFile(inPath);
+ } else {
+ deletePromise = this.#fs_.deleteDirectory(inPath);
+ }
+ deletePromise
+ .catch(Debug.error)
+ .finally(() => {
+ this.hideProgress();
+ this.runEvent('afterDeleteNode', path.join(inPath, '..'));
+ });
+ }
+
+ deleteFileNode(filePath) {
+ this.deleteNode('file', filePath);
+ }
+
+ deleteFolderNode(folderPath) {
+ this.deleteNode('folder', folderPath);
+ }
+
+ copyNode(inPath) {
+ const node = this.#jstree_.get_node(inPath);
+ this.#jstree_.copy(node);
+ }
+
+ cutNode(inPath) {
+ const node = this.#jstree_.get_node(inPath);
+ this.#jstree_.cut(node);
+ }
+
+ pasteNode(folderPath) {
+ if (!this.#jstree_.can_paste()) {
+ return;
+ }
+ this.showProgress();
+ const oldNodes = this.#jstree_.get_buffer();
+ const oldNode = oldNodes.node[0];
+ const { mode } = oldNodes;
+ const { type } = oldNode.li_attr;
+ let pastePromise = null;
+ let startPath = oldNode.id;
+ let endPath = path.join(folderPath, oldNode.text);
+ if (mode === 'move_node') {
+ const relativePath = path.relative(startPath, endPath);
+ if (relativePath.indexOf('..') === -1) {
+ pastePromise = Promise.resolve();
+ } else {
+ if (type === 'file') {
+ pastePromise = this.#fs_.moveFile(startPath, endPath);
+ } else {
+ pastePromise = this.#fs_.createDirectory(endPath)
+ .then(() => {
+ return this.#fs_.moveDirectory(startPath, endPath);
+ });
+ }
+ }
+ } else if (mode === 'copy_node') {
+ if (type === 'file') {
+ pastePromise = this.#fs_.copyFile(startPath, endPath);
+ } else {
+ pastePromise = this.#fs_.createDirectory(endPath)
+ .then(() => {
+ return this.#fs_.copyDirectory(startPath, endPath);
+ });
+ }
+ }
+ pastePromise
+ .then(async () => {
+ if (mode === 'move_node') {
+ const temp = path.join(startPath, '..');
+ const relativePath = path.relative(temp, endPath);
+ if (relativePath.indexOf('..') === -1) {
+ this.clearFolderTemp(temp);
+ await this.loadNode(temp);
+ await this.openNode(temp);
+ this.clearFolderTemp(folderPath);
+ await this.loadNode(folderPath);
+ await this.openNode(folderPath);
+ } else {
+ this.clearFolderTemp(folderPath);
+ await this.loadNode(folderPath);
+ await this.openNode(folderPath);
+ this.clearFolderTemp(temp);
+ await this.loadNode(temp);
+ await this.openNode(temp);
+ }
+ } else {
+ this.clearFolderTemp(folderPath);
+ this.#jstree_.refresh_node(folderPath);
+ this.openNode(folderPath);
+ }
+ })
+ .catch(Debug.error)
+ .finally(() => {
+ this.hideProgress();
+ });
+ }
+
+ loadNode(inPath) {
+ return new Promise((resolve, reject) => {
+ if (inPath === '/') {
+ inPath = '#';
+ }
+ const node = this.#jstree_.get_node(inPath);
+ if (!node) {
+ resolve();
+ }
+ this.#jstree_.load_node(node, () => {
+ resolve();
+ });
+ });
+ }
+
+ openNode(folderPath) {
+ return new Promise((resolve, reject) => {
+ const node = this.#jstree_.get_node(folderPath);
+ if (!node) {
+ resolve();
+ }
+ this.#jstree_.open_node(node, () => {
+ resolve();
+ });
+ });
+ }
+
+ dispose() {
+ this.#jstree_.destroy();
+ this.#scrollbar_.destroy();
+ this.#contextMenu_.dispose();
+ super.dispose();
+ }
+
+ getFS() {
+ return this.#fs_;
+ }
+
+ setFS(fs) {
+ this.#fs_ = fs;
+ }
+
+ showProgress() {
+ this.#$progress_.css('display', 'block');
+ this.showMask();
+ }
+
+ hideProgress() {
+ this.#$progress_.css('display', 'none');
+ this.hideMask();
+ }
+
+ showMask() {
+ this.#$mask_.css('display', 'block');
+ }
+
+ hideMask() {
+ this.#$mask_.css('display', 'none');
+ }
+}
+
+Mixly.FileTree = FileTree;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/file.js b/mixly/common/modules/mixly-modules/common/file.js
new file mode 100644
index 00000000..ebfa1321
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/file.js
@@ -0,0 +1,43 @@
+goog.loadJs('common', () => {
+
+goog.provide('Mixly.File');
+
+class File {
+ static {
+ this.showOpenFilePicker = async () => {
+ return new Promise((resolve, reject) => {
+ reject();
+ });
+ }
+
+ this.showDirectoryPicker = async () => {
+ return new Promise((resolve, reject) => {
+ reject();
+ });
+ }
+
+ this.showSaveFilePicker = async () => {
+ return new Promise((resolve, reject) => {
+ reject();
+ });
+ }
+ }
+
+ constructor() {}
+
+ async readFile() {
+ return new Promise((resolve, reject) => {
+ reject();
+ });
+ }
+
+ async writeFile(data) {
+ return new Promise((resolve, reject) => {
+ reject();
+ });
+ }
+}
+
+Mixly.File = File;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/footerbar.js b/mixly/common/modules/mixly-modules/common/footerbar.js
new file mode 100644
index 00000000..68e29f22
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/footerbar.js
@@ -0,0 +1,94 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Config');
+goog.require('Mixly.Boards');
+goog.require('Mixly.FooterLayer');
+goog.require('Mixly.FooterLayerBoardConfig');
+goog.require('Mixly.FooterLayerMessage');
+goog.require('Mixly.Component');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Electron.FooterLayerExample');
+goog.require('Mixly.Web.FooterLayerExample');
+goog.provide('Mixly.FooterBar');
+
+const {
+ Env,
+ XML,
+ Msg,
+ Config,
+ Boards,
+ FooterLayer,
+ FooterLayerBoardConfig,
+ FooterLayerMessage,
+ Component,
+ HTMLTemplate,
+ Electron = {},
+ Web = {}
+} = Mixly;
+
+const { FooterLayerExample } = goog.isElectron? Electron : Web;
+
+const { BOARD } = Config;
+
+
+class FooterBar extends Component {
+ static {
+ HTMLTemplate.add(
+ 'html/footerbar.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/footerbar.html')))
+ );
+ }
+
+ #exampleLayer_ = null;
+ #messageLayer_ = null;
+ #boardConfigLayer_ = null;
+
+ constructor() {
+ super();
+ let content = $(HTMLTemplate.get('html/footerbar.html').render({
+ outputAceName: Msg.Lang['footerbar.output'],
+ row: Msg.Lang['footerbar.cursor.row'],
+ column: Msg.Lang['footerbar.cursor.column'],
+ unknown: Msg.Lang['footerbar.board.unknown'],
+ config: Msg.Lang['footerbar.config'],
+ selected: Msg.Lang['footerbar.cursor.selected'],
+ on: Msg.Lang['footerbar.board.on'],
+ message: Msg.Lang['footerbar.message'],
+ example: Msg.Lang['footerbar.examples']
+ }));
+ Boards.init();
+ this.setContent(content);
+ content.find('.code-lang').html(BOARD.language ?? Msg.Lang['footerbar.language.unknown']);
+ this.#exampleLayer_ = new FooterLayerExample(content.find('.example')[0]);
+ this.#messageLayer_ = new FooterLayerMessage(content.find('.message')[0]);
+ this.#boardConfigLayer_ = new FooterLayerBoardConfig(content.find('.board-config')[0], Boards.dict);
+ Boards.addLayer(this.#boardConfigLayer_);
+ }
+
+ resize() {
+ super.resize();
+ this.#exampleLayer_.resize();
+ this.#messageLayer_.resize();
+ this.#boardConfigLayer_.resize();
+ }
+
+ getExampleLayer() {
+ return this.#exampleLayer_;
+ }
+
+ getMessageLayer() {
+ return this.#messageLayer_;
+ }
+
+ getBoardConfigLayer() {
+ return this.#boardConfigLayer_;
+ }
+}
+
+Mixly.FooterBar = FooterBar;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/footerlayer-board-config.js b/mixly/common/modules/mixly-modules/common/footerlayer-board-config.js
new file mode 100644
index 00000000..fa69483d
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/footerlayer-board-config.js
@@ -0,0 +1,226 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('tippy');
+goog.require('Blockly');
+goog.require('layui');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.FooterLayer');
+goog.provide('Mixly.FooterLayerBoardConfig');
+
+const {
+ FooterLayer,
+ Env,
+ XML,
+ Msg,
+ HTMLTemplate
+} = Mixly;
+
+const { dropdown } = layui;
+
+class FooterLayerBoardConfig extends FooterLayer {
+ static {
+ // 弹层模板
+ this.menuHTMLTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer-board-config.html'))
+ );
+ }
+
+ /**
+ * @param domId { string } 绑定dom的id
+ * @param boardsInfo { obj } 板卡配置信息
+ {
+ "xxx板卡": BoardConfigItem,
+ ...
+ }
+ * @return { FooterLayerBoardConfig obj }
+ **/
+ #canvas_ = null;
+ constructor(element, boardsInfo) {
+ super(element, {
+ onHidden: (instance) => {
+ this.boardsInfo[this.boardName].writeSelectedOptions();
+ },
+ onMount: (instance) => {
+ if (this.renderBoardName === this.boardName) {
+ return;
+ }
+ this.renderMenu();
+ },
+ btns: [
+ {
+ class: 'reset',
+ title: Msg.Lang['footerbar.config.default'],
+ icon: 'layui-icon-refresh-1',
+ onclick: () => {
+ const selectedBoardName = this.boardName;
+ let { defaultOptions } = this.boardsInfo[selectedBoardName];
+ this.setSelectedOptions(defaultOptions);
+ }
+ }
+ ]
+ });
+ this.$content.addClass('footer-layer-board-config');
+ this.$footerContainer = $(element);
+ this.boardsInfo = boardsInfo;
+ // 当前用户所选择的板卡
+ this.boardName = null;
+ // 当前渲染的板卡配置所对应的板卡名
+ this.renderBoardName = null;
+ this.dropdownItems = {};
+ }
+
+ getSelectedParams() {
+ let paramList = [];
+ let { ignore, selectedOptions } = this.boardsInfo[this.boardName];
+ for (let i in selectedOptions) {
+ if (ignore.includes(i)) {
+ continue;
+ }
+ paramList.push(i + '=' + selectedOptions[i].key);
+ }
+ let { boardKey } = this;
+ const index = boardKey.indexOf('@');
+ if (index !== -1) {
+ boardKey = boardKey.substring(0, index);
+ }
+ return paramList.length ? (boardKey + ':' + paramList.join(',')) : boardKey;
+ }
+
+ getSelectedParamByName(name) {
+ let { selectedOptions } = this.boardsInfo[this.boardName];
+ for (let i in selectedOptions) {
+ if (i === name) {
+ return selectedOptions[i].key;
+ }
+ }
+ return '';
+ }
+
+ renderMenu() {
+ let { options, selectedOptions } = this.boardsInfo[this.boardName];
+ this.renderTemplate(options);
+ this.renderOptions(options);
+ this.setSelectedOptions(selectedOptions);
+ this.renderBoardName = this.boardName
+ }
+
+ setSelectedOptions(selectedOptions) {
+ // 每次打开板卡设置窗口时设置其默认选中项
+ const boardsInfo = this.boardsInfo[this.boardName];
+ let optionsType = Object.keys(boardsInfo.defaultOptions);
+ for (let i in selectedOptions) {
+ if (!boardsInfo.defaultOptions[i]) {
+ continue;
+ }
+ let id = boardsInfo.defaultOptions[i].key;
+ if (boardsInfo.optionIsLegal(i, selectedOptions[i])) {
+ id = selectedOptions[i].key;
+ }
+ this.$body.find(`[mid=${i}]`).val(id).trigger('change');
+ boardsInfo.setSelectedOption(i, selectedOptions[i]);
+ }
+ // 重新计算窗口的位置
+ this.setProps({});
+ }
+
+ renderOptions(options) {
+ const _this = this;
+ for (let item of options) {
+ this.createDropdownMenu(item.key, item.options);
+ this.createMessageLayer(item.key, item.messageId);
+ }
+ }
+
+ createMessageLayer(mId, messageId) {
+ if (!messageId) {
+ return;
+ }
+ if (!Blockly.Msg[messageId]) {
+ return;
+ }
+ let $container = this.$body.find(`[mid="${mId}-label"]`);
+ tippy($container[0], {
+ content: Blockly.Msg[messageId],
+ allowHTML: true,
+ interactive: true,
+ placement: 'left',
+ offset: [0, 16]
+ });
+ }
+
+ createDropdownMenu(mId, options) {
+ let $container = this.$body.find(`[mid="${mId}"]`);
+ if (!$container.length) {
+ return;
+ }
+ let menu = [];
+ let maxLength = 0;
+ for (let item of options) {
+ menu.push({
+ id: item.id,
+ text: item.title
+ });
+ let nowLength = this.getStrWidth(item.title);
+ if (maxLength < nowLength) {
+ maxLength = nowLength;
+ }
+ }
+ $container.select2({
+ data: menu,
+ minimumResultsForSearch: Infinity,
+ width: 'auto',
+ dropdownCssClass: 'mixly-scrollbar select2-board-config'
+ });
+ $container.next().css('min-width', (maxLength + 30) + 'px');
+ $container.on('select2:select', (e) => {
+ const boardName = this.boardName;
+ const data = e.params.data;
+ this.boardsInfo[boardName].setSelectedOption(mId, {
+ key: data.id,
+ label: data.text
+ });
+ this.setProps({});
+ });
+ }
+
+ renderTemplate(options) {
+ this.$body.find('select').select2('destroy');
+ const xmlStr = FooterLayerBoardConfig.menuHTMLTemplate.render({ options });
+ this.updateContent(xmlStr);
+ }
+
+ changeTo(boardName) {
+ if (this.boardsInfo[boardName]?.options.length) {
+ this.$footerContainer.css('display', 'inline-flex');
+ } else {
+ this.$footerContainer.css('display', 'none');
+ this.hide();
+ }
+ this.boardName = boardName;
+ this.boardKey = this.boardsInfo[boardName].key;
+ this.renderMenu(this.layer);
+ }
+
+ getStrWidth(str, font = '14px Arial') {
+ try {
+ this.#canvas_ = this.#canvas_ || document.createElement('canvas').getContext('2d');
+ this.#canvas_.font = font;
+ return this.#canvas_.measureText(str).width;
+ } catch (e) {
+ return 0;
+ }
+ }
+
+ resize() {
+ this.$body.find('select').select2('close');
+ }
+}
+
+Mixly.FooterLayerBoardConfig = FooterLayerBoardConfig;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/footerlayer-example.js b/mixly/common/modules/mixly-modules/common/footerlayer-example.js
new file mode 100644
index 00000000..aaf29b90
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/footerlayer-example.js
@@ -0,0 +1,86 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.Title');
+goog.require('Mixly.Env');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.FooterLayer');
+goog.provide('Mixly.FooterLayerExample');
+
+const {
+ Title,
+ Env,
+ Debug,
+ Workspace,
+ HTMLTemplate,
+ FooterLayer
+} = Mixly;
+
+const { dropdown, tree } = layui;
+
+class FooterLayerExample extends FooterLayer {
+ static {
+ // 弹层模板
+ this.menuHTMLTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer-example.html'))
+ );
+ }
+
+ constructor(element) {
+ super(element, {
+ onMount: (instance) => {
+ this.examplesTree.reload({ data: this.getRoot() });
+ }
+ });
+ this.$content.addClass('footer-layer-example');
+ this.updateContent(FooterLayerExample.menuHTMLTemplate.render());
+ this.$treeBody = this.$body.children('.example-tree-body');
+ this.DEPTH = 5;
+ this.render();
+ }
+
+ render() {
+ this.examplesTree = tree.render({
+ elem: this.$treeBody[0],
+ data: this.getRoot(),
+ accordion: true,
+ anim: false,
+ icon: [ 'icon-folder-empty', 'icon-folder-open-empty-1', 'icon-file-code' ],
+ getChildren: (obj) => {
+ return this.getChildren(obj.data.id);
+ },
+ click: (obj) => {
+ if (obj.data.children) {
+ return;
+ }
+ this.dataToWorkspace(obj.data.id);
+ },
+ statusChange: () => {
+ this.setProps({});
+ }
+ });
+ this.examplesTree.config.statusChange();
+ }
+
+ // 可覆盖
+ getRoot() {}
+
+ // 可覆盖
+ getChildren(inPath) {}
+
+ // 可覆盖
+ dataToWorkspace(inPath) {}
+
+ updateCode(ext, data) {
+ const editorMix = Workspace.getMain().getEditorsManager().getActive();
+ editorMix.setValue(data, ext);
+ Title.updateTitle(Title.title);
+ }
+}
+
+Mixly.FooterLayerExample = FooterLayerExample;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/footerlayer-message.js b/mixly/common/modules/mixly-modules/common/footerlayer-message.js
new file mode 100644
index 00000000..316e6385
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/footerlayer-message.js
@@ -0,0 +1,204 @@
+goog.loadJs('common', () => {
+
+goog.require('shortid');
+goog.require('path');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.FooterLayer');
+goog.provide('Mixly.FooterLayerMessage');
+
+const {
+ XML,
+ Msg,
+ Env,
+ HTMLTemplate,
+ FooterLayer
+} = Mixly;
+
+class FooterLayerMessage extends FooterLayer {
+ // 弹层模板
+ static {
+ this.menuHTMLTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer-message.html'))
+ );
+
+ this.menuItemHTMLTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer-message-item.html'))
+ );
+
+ this.menuItemWithIconHTMLTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer-message-item-with-icon.html'))
+ );
+
+ this.STYLES = ['primary', 'secondary', 'success', 'danger', 'warning'];
+ }
+
+ constructor(element) {
+ super(element, {
+ onMount: (instance) => {
+ this.$body.scrollTop(this.$container.parent().prop('scrollHeight'));
+ },
+ btns: [
+ {
+ class: 'clear',
+ title: Msg.Lang['footerbar.message.clear'],
+ icon: 'layui-icon-delete',
+ onclick: () => {
+ this.clear();
+ }
+ }
+ ]
+ });
+ this.DEFALUT_ITEM_CONFIG = {
+ type: 0,
+ style: 'primary',
+ src: '../common/media/mixly.png',
+ name: '',
+ onCreate: (obj) => {},
+ onDestroy: () => {},
+ btns: [],
+ checkbox: false
+ };
+ const { menuHTMLTemplate } = FooterLayerMessage;
+ this.updateContent(menuHTMLTemplate.render());
+ this.messageQuery = [];
+ this.$content.addClass('footer-layer-message');
+ this.$container = this.$content.find('.toast-container');
+ this.clear();
+ }
+
+ append(config) {
+ config.operate = 'append';
+ if (!this.$container.length) {
+ this.messageQuery.push(config);
+ if (this.messageQuery.length > 50) {
+ this.messageQuery.shift();
+ }
+ return;
+ }
+ this.add_(config);
+ }
+
+ add_(config) {
+ config = { ...this.DEFALUT_ITEM_CONFIG, ...config };
+ this.$body.css('width', '350px');
+ let $children = this.$container.children('div');
+ if (!$children.length) {
+ this.$container.html('');
+ } else if ($children.length >= 50) {
+ for (let i = 0; i <= $children.length - 50; i++) {
+ $($children[i]).remove();
+ }
+ }
+ if (!FooterLayerMessage.STYLES.includes(config.style)) {
+ config.style = FooterLayerMessage.STYLES[0];
+ }
+
+ let btnsFunc = [];
+ config.btns = config.btns ?? [];
+ for (let i in config.btns) {
+ if (!(config.btns[i] instanceof Object)) {
+ continue;
+ }
+ btnsFunc.push(config.btns[i].onclick);
+ delete config.btns[i].onclick;
+ config.btns[i].mId = i;
+ }
+
+ let checkbox = {
+ checked: '',
+ show: false,
+ title: ''
+ };
+ if (config.checkbox instanceof Object) {
+ checkbox.id = shortid.generate();
+ checkbox.show = true;
+ checkbox.title = config.checkbox.title;
+ checkbox.checked = config.checkbox.checked ? 'checked' : '';
+ }
+ let template = this.genItemTemplate({
+ type: config.type,
+ style: config.style,
+ src: config.src,
+ name: config.name,
+ message: config.message,
+ btns: config.btns,
+ checkbox
+ });
+ let $template = $(template);
+ this.$container.append($template);
+ this.createBtnsClickEvent_($template, config.checkbox, btnsFunc);
+ this.scrollToBottom();
+ this.setProps({});
+ if (typeof config.onCreate === 'function') {
+ config.onCreate($template);
+ }
+ if (typeof config.onDestroy === 'function') {
+ $template.find('.btn-close[data-bs-dismiss="toast"]').off().click(() => {
+ $template.remove();
+ if (!this.$container.children('div').length) {
+ this.clear();
+ }
+ config.onDestroy();
+ });
+ }
+ }
+
+ createBtnsClickEvent_(container, checkbox, btnsFunc) {
+ container.find('.btns').children('button').off().click((event) => {
+ let $target = $(event.target);
+ let mId = parseInt($target.attr('m-id'));
+ let checkboxValue = null;
+ if (checkbox) {
+ checkboxValue = container.find('input[class="form-check-input"]').prop('checked');
+ }
+ if (typeof btnsFunc[mId] === 'function') {
+ btnsFunc[mId](event, container, checkboxValue);
+ }
+ if (!this.$container.children('div').length) {
+ this.clear();
+ }
+ });
+ }
+
+ remove() {
+ if (!this.$container.children('div').length) {
+ return;
+ }
+ $container.eq(-1).remove();
+ }
+
+ clear() {
+ this.$container.html(Msg.Lang['footerbar.message.empty']);
+ this.$body.css('width', '100px');
+ this.setProps({});
+ }
+
+ genItemTemplate(items) {
+ const {
+ menuItemWithIconHTMLTemplate,
+ menuItemHTMLTemplate
+ } = FooterLayerMessage;
+ let htmlTemplate;
+ if (items.type === 1) {
+ htmlTemplate = menuItemWithIconHTMLTemplate;
+ } else {
+ htmlTemplate = menuItemHTMLTemplate;
+ }
+ return htmlTemplate.render(items);
+ }
+
+ scrollToTop() {
+ this.$body.scrollTop(0);
+ }
+
+ scrollToBottom() {
+ this.$body.scrollTop(this.$body.prop('scrollHeight'));
+ }
+}
+
+Mixly.FooterLayerMessage = FooterLayerMessage;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/footerlayer.js b/mixly/common/modules/mixly-modules/common/footerlayer.js
new file mode 100644
index 00000000..e6ddf26b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/footerlayer.js
@@ -0,0 +1,115 @@
+goog.loadJs('common', () => {
+
+goog.require('tippy');
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.provide('Mixly.FooterLayer');
+
+const { Env, XML, Msg } = Mixly;
+
+class FooterLayer {
+ static {
+ // 弹层模板和一些默认配置项
+ this.TEMPLATE = goog.readFileSync(path.join(Env.templatePath, 'html/footerlayer/footerlayer.html'));
+ this.DEFAULT_CONFIG_TIPPY = {
+ allowHTML: true,
+ trigger: 'manual',
+ interactive: true,
+ hideOnClick: false,
+ maxWidth: 'none',
+ offset: [ 0, 6 ],
+ delay: 0,
+ duration: [ 0, 0 ]
+ };
+ }
+
+ /**
+ * @param domId 被绑定元素的ID
+ * @param config 配置项
+ {
+ ...DEFAULT_CONFIG_TIPPY
+ }
+ **/
+ constructor(element, config) {
+ this.config = {
+ ...FooterLayer.DEFAULT_CONFIG_TIPPY,
+ ...(config ?? {})
+ };
+ this.btns = config.btns ?? [];
+ this.btnsClickEvent = {};
+ this.element = element;
+ this.layer = null;
+ this.#create_();
+ this.#addSharedMethod_();
+ this.setContent(XML.render(FooterLayer.TEMPLATE, {
+ content: '',
+ btns: this.btns,
+ close: Msg.Lang['footerlayer.close']
+ }));
+ this.$content = $(this.layer.popper).find('.tippy-content');
+ this.$body = this.$content.find('.footer-layer-body');
+ this.#addContainerClickEvent_();
+ this.#addBtnsClickEvent_();
+ }
+
+ #create_() {
+ this.layer = tippy(this.element, this.config);
+ }
+
+ updateContent(content) {
+ if (this.$body.length) {
+ this.$body.html(content);
+ } else {
+ this.setContent(content);
+ }
+ }
+
+ #addBtnsClickEvent_() {
+ for (let i of this.btns) {
+ if (!(i instanceof Object)) {
+ continue;
+ }
+ if (typeof i.onclick !== 'function' || !(i.class)) {
+ continue;
+ }
+ this.btnsClickEvent[i.class] = i.onclick;
+ delete i.onclick;
+ }
+ this.btnsClickEvent['close'] = this.hide;
+ for (let key in this.btnsClickEvent) {
+ $(this.layer.popper).find('.layui-layer-title').children(`.${key}`)
+ .off().click(() => {
+ this.btnsClickEvent[key]();
+ });
+ }
+ }
+
+ #addSharedMethod_() {
+ let sharedMethod = ['hide', 'show', 'destroy', 'setProps', 'setContent'];
+ for (let type of sharedMethod) {
+ this[type] = this.layer[type];
+ }
+ }
+
+ /**
+ * @method 为绑定的元素添加鼠标单击事件
+ * @return {void}
+ **/
+ #addContainerClickEvent_() {
+ $(this.element).off().click(() => {
+ if (this.layer.state.isShown) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ });
+ }
+
+ resize() {}
+}
+
+Mixly.FooterLayer = FooterLayer;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/fs-board-handler.js b/mixly/common/modules/mixly-modules/common/fs-board-handler.js
new file mode 100644
index 00000000..40bebec9
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/fs-board-handler.js
@@ -0,0 +1,144 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Registry');
+goog.provide('Mixly.FSBoardHandler');
+
+const { Registry } = Mixly;
+
+
+class FSBoardHandler {
+ static {
+ this.FsType = {
+ LITTLEFS: 'littlefs',
+ FATFS: 'fatfs',
+ SPIFFS: 'spiffs'
+ };
+
+ this.CommandType = {
+ DOWNLOAD: 'download',
+ UPLOAD: 'upload'
+ };
+ }
+
+ #config_ = {
+ type: FSBoardHandler.FsType.LITTLEFS,
+ offset: 0x610000,
+ size: 0x9F0000,
+ blockSize: 4096,
+ pageSize: 256,
+ usrFolder: '/'
+ };
+ #commandsRegistry_ = new Registry();
+
+ constructor() {
+ for (let key in FSBoardHandler.FsType) {
+ const fsRegistry = new Registry();
+ fsRegistry.register(FSBoardHandler.CommandType.DOWNLOAD, '');
+ fsRegistry.register(FSBoardHandler.CommandType.UPLOAD, '');
+ this.#commandsRegistry_.register(FSBoardHandler.FsType[key], fsRegistry);
+ }
+ }
+
+ getConfig() {
+ return this.#config_;
+ }
+
+ updateConfig(config) {
+ Object.assign(this.#config_, config);
+ }
+
+ setFSType(type) {
+ this.#config_.type = type;
+ }
+
+ getFSType() {
+ return this.#config_.type;
+ }
+
+ setFSSize() {
+ this.#config_.size = size;
+ }
+
+ getFSSize() {
+ return this.#config_.size;
+ }
+
+ setFSPageSize(size) {
+ this.#config_.pageSize = size;
+ }
+
+ getFSPageSize() {
+ return this.#config_.pageSize;
+ }
+
+ setFSBlockSize(size) {
+ this.#config_.blockSize = size;
+ }
+
+ getFSBlockSize() {
+ return this.#config_.blockSize;
+ }
+
+ setFSOffset(offset) {
+ this.#config_.offset = offset;
+ }
+
+ getFSOffset() {
+ return this.#config_.offset;
+ }
+
+ setUsrFolder(usrFolder) {
+ this.#config_.usrFolder = usrFolder;
+ }
+
+ getUsrFolder() {
+ return this.#config_.usrFolder;
+ }
+
+ setFSCommands(type, commands) {
+ const fsRegistry = this.getFSCommands(type);
+ if (!fsRegistry) {
+ return;
+ }
+ for (let key in commands) {
+ fsRegistry.hasKey(key) && fsRegistry.unregister(key);
+ fsRegistry.register(key, commands[key]);
+ }
+ }
+
+ getFSCommands(type) {
+ return this.#commandsRegistry_.getItem(type);
+ }
+
+ setLittleFSCommands(commands) {
+ this.setFSCommands(FSBoardHandler.FsType.LITTLEFS, commands);
+ }
+
+ getLittleFSCommands() {
+ return this.getFSCommands(FSBoardHandler.FsType.LITTLEFS);
+ }
+
+ setFatFSCommands(commands) {
+ this.setFSCommands(FSBoardHandler.FsType.FATFS, commands);
+ }
+
+ getFatFSCommands() {
+ return this.getFSCommands(FSBoardHandler.FsType.FATFS);
+ }
+
+ setSpifFSCommands(commands) {
+ this.setFSCommands(FSBoardHandler.FsType.SPIFFS, commands);
+ }
+
+ getSpifFSCommands() {
+ return this.getFSCommands(FSBoardHandler.FsType.SPIFFS);
+ }
+
+ onBeforeUpload() {}
+
+ onBeforeDownload() {}
+}
+
+Mixly.FSBoardHandler = FSBoardHandler;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/fs-board.js b/mixly/common/modules/mixly-modules/common/fs-board.js
new file mode 100644
index 00000000..bb195778
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/fs-board.js
@@ -0,0 +1,96 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Boards');
+goog.require('Mixly.FSBoardHandler');
+goog.provide('Mixly.FSBoard');
+
+const {
+ Serial,
+ FSBoardHandler,
+ Boards
+} = Mixly;
+
+class FSBoard {
+ #handler_ = null;
+
+ constructor() {}
+
+ #addParams_(usrFolder, fsType) {
+ this.#handler_.updateConfig({
+ port: Serial.getSelectedPortName()
+ });
+ this.#handler_.setUsrFolder(usrFolder);
+ this.#handler_.setFSType(fsType);
+ }
+
+ async download(usrFolder, fsType) {
+ this.#addParams_(usrFolder, fsType);
+ this.#handler_.onBeforeDownload();
+ }
+
+ async upload(usrFolder, fsType) {
+ this.#addParams_(usrFolder, fsType);
+ this.#handler_.onBeforeUpload();
+ }
+
+ getConfig() {
+ return this.#handler_.getConfig();
+ }
+
+ getFSType() {
+ return this.#handler_.getFSType();
+ }
+
+ getLittleFSCommands() {
+ return this.#handler_.getLittleFSCommands();
+ }
+
+ getFatFSCommands() {
+ return this.#handler_.getFatFSCommands();
+ }
+
+ getSpifFSCommands() {
+ return this.#handler_.getSpifFSCommands();
+ }
+
+ getFSCommands(type) {
+ return this.#handler_.getFSCommands(type);
+ }
+
+ getSelectedFSCommands() {
+ return this.getFSCommands(this.getFSType());
+ }
+
+ getSelectedFSDownloadCommand() {
+ const fsRegistry = this.getSelectedFSCommands();
+ if (!fsRegistry) {
+ return '';
+ }
+ return fsRegistry.getItem(FSBoardHandler.CommandType.DOWNLOAD);
+ }
+
+ getSelectedFSUploadCommand() {
+ const fsRegistry = this.getSelectedFSCommands();
+ if (!fsRegistry) {
+ return '';
+ }
+ return fsRegistry.getItem(FSBoardHandler.CommandType.UPLOAD);
+ }
+
+ setHandler(handler) {
+ this.#handler_ = handler;
+ }
+
+ getHandler() {
+ return this.#handler_;
+ }
+
+ dispose() {
+ this.#handler_ = null;
+ }
+}
+
+Mixly.FSBoard = FSBoard;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/fs.js b/mixly/common/modules/mixly-modules/common/fs.js
new file mode 100644
index 00000000..1108b8ec
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/fs.js
@@ -0,0 +1,47 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.FS');
+
+
+class FS {
+ constructor() {}
+
+ async rename(oldPath, newPath) {}
+
+ async createFile(filePath) {}
+
+ async readFile(filePath, encoding = 'utf8') {}
+
+ async writeFile(filePath, data) {}
+
+ async isFile(filePath) {}
+
+ async renameFile(oldFilePath, newFilePath) {}
+
+ async moveFile(oldFilePath, newFilePath) {}
+
+ async copyFile(oldFilePath, newFilePath) {}
+
+ async deleteFile(filePath) {}
+
+ async createDirectory(folderPath) {}
+
+ async readDirectory(folderPath) {}
+
+ async isDirectory(folderPath) {}
+
+ async isDirectoryEmpty(folderPath) {}
+
+ async renameDirectory(oldFolderPath, newFolderPath) {}
+
+ async moveDirectory(oldFolderPath, newFolderPath) {}
+
+ async copyDirectory(oldFolderPath, newFolderPath) {}
+
+ async deleteDirectory(folderPath) {}
+}
+
+Mixly.FS = FS;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/html-template.js b/mixly/common/modules/mixly-modules/common/html-template.js
new file mode 100644
index 00000000..9cac50f4
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/html-template.js
@@ -0,0 +1,74 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.XML');
+goog.require('Mixly.Registry');
+goog.require('Mixly.IdGenerator');
+goog.provide('Mixly.HTMLTemplate');
+
+const {
+ XML,
+ Registry,
+ IdGenerator,
+} = Mixly;
+
+class HTMLTemplate {
+ static {
+ this.regexStyle_ = /(?<=');
+ $style.attr('style-id', this.id);
+ $style.attr('type', 'text/css').html(this.#style_);
+ $('head').append($style);
+ }
+
+ getId() {
+ return this.id;
+ }
+}
+
+Mixly.HTMLTemplate = HTMLTemplate;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/id-generator.js b/mixly/common/modules/mixly-modules/common/id-generator.js
new file mode 100644
index 00000000..11488371
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/id-generator.js
@@ -0,0 +1,24 @@
+goog.loadJs('common', () => {
+
+goog.require('shortid');
+goog.require('Mixly');
+goog.provide('Mixly.IdGenerator');
+
+const { IdGenerator } = Mixly;
+
+IdGenerator.generate = function(input) {
+ let output = {};
+ if (input instanceof Array) {
+ for (let i of input) {
+ if (typeof i !== 'string') {
+ continue;
+ }
+ output[i] = shortid.generate();
+ }
+ return output;
+ } else {
+ return shortid.generate();
+ }
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/if-visiable.js b/mixly/common/modules/mixly-modules/common/if-visiable.js
new file mode 100644
index 00000000..b821f673
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/if-visiable.js
@@ -0,0 +1,44 @@
+goog.loadJs('common', () => {
+
+goog.require('ifvisible');
+goog.require('Mixly.Events');
+goog.provide('Mixly.IfVisible');
+
+const { Events, IfVisible } = Mixly;
+
+IfVisible.events = new Events(['blur', 'focus', 'idle', 'wakeup']);
+
+IfVisible.init = function () {
+ ifvisible.on('blur', () => this.runEvent('blur'));
+ ifvisible.on('focus', () => this.runEvent('focus'));
+ ifvisible.on('idle', () => this.runEvent('idle'));
+ ifvisible.on('wakeup', () => this.runEvent('wakeup'));
+}
+
+IfVisible.bind = function (type, func) {
+ return this.events.bind(type, func);
+}
+
+IfVisible.unbind = function (id) {
+ this.events.unbind(id);
+}
+
+IfVisible.addEventsType = function (eventsType) {
+ this.events.addType(eventsType);
+}
+
+IfVisible.runEvent = function (eventsType, ...args) {
+ return this.events.run(eventsType, ...args);
+}
+
+IfVisible.offEvent = function (eventsType) {
+ this.events.off(eventsType);
+}
+
+IfVisible.resetEvent = function () {
+ this.events.reset();
+}
+
+IfVisible.init();
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/js-funcs.js b/mixly/common/modules/mixly-modules/common/js-funcs.js
new file mode 100644
index 00000000..670ba95a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/js-funcs.js
@@ -0,0 +1,6 @@
+goog.provide('Mixly.JSFuncs');
+goog.require('Mixly.Config');
+
+Mixly.JSFuncs.getPlatform = function () {
+ return Mixly.Config.BOARD.boardType;
+};
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/layer-ext.js b/mixly/common/modules/mixly-modules/common/layer-ext.js
new file mode 100644
index 00000000..241ead08
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/layer-ext.js
@@ -0,0 +1,167 @@
+goog.loadJs('common', () => {
+
+goog.require('layui');
+goog.require('Mixly.Config');
+goog.require('Mixly.Msg');
+goog.provide('Mixly.LayerExt');
+
+const {
+ LayerExt,
+ Config,
+ Msg
+} = Mixly;
+
+const { BOARD, USER } = Config;
+
+const { layer } = layui;
+
+LayerExt.SHADE_ALL = [1, 'transparent'];
+LayerExt.SHADE_NAV = [1, 'transparent', '40px'];
+
+// 默认的弹层标题高度
+LayerExt.DEFAULT_TITLE_HEIGHT = 42;
+// 默认的弹层配置项
+LayerExt.DEFAULT_CONFIG = {
+ area: ['50%', '50%'],
+ max: ['850px', '543px'],
+ min: ['350px', '243px'],
+ title: '信息',
+ id: 'info',
+ content: '',
+ resize: true,
+ shade: LayerExt.SHADE_ALL,
+ success: null,
+ beforeEnd: null,
+ end: null,
+ cancel: null,
+ resizing: null,
+ offset: 'auto',
+ fixed: true,
+ borderRadius: '5px',
+ maxmin: false,
+ zIndex: 19891014
+};
+
+LayerExt.open = (toolConfig) => {
+ if (typeof toolConfig !== 'object')
+ toolConfig = LayerExt.DEFAULT_CONFIG;
+ else
+ toolConfig = {
+ ...LayerExt.DEFAULT_CONFIG,
+ ...toolConfig
+ };
+
+ const { title } = toolConfig;
+ let layerOffset = 42;
+ let layerTitle = null;
+ if (title) {
+ if (typeof title === 'object' && !isNaN(parseInt(title[1]))) {
+ layerOffset = parseInt(title[1]);
+ layerTitle = title[0];
+ } else {
+ layerTitle = title;
+ }
+ } else {
+ layerOffset = 0;
+ layerTitle = false;
+ }
+ return layer.open({
+ ...toolConfig,
+ type: 1,
+ title: layerTitle,
+ closeBtn: 1,
+ success: function (layero, index) {
+ const { borderRadius, id, max, min, success } = toolConfig;
+ layer.style(index, { borderRadius });
+ const pageBody = $('#' + id);
+ pageBody.addClass('mixly-scrollbar');
+ if (typeof max === 'object') {
+ layero.css({
+ 'maxWidth': max[0],
+ 'maxHeight': max[1]
+ });
+ pageBody.css('maxWidth', max[0]);
+ if (max[1].indexOf('%') !== -1)
+ pageBody.css('maxHeight', max[1]);
+ else
+ pageBody.css('maxHeight', (parseInt(max[1]) - layerOffset) + 'px');
+ }
+ if (typeof min === 'object') {
+ layero.css({
+ 'minHeight': min[1],
+ 'minWidth': min[0]
+ });
+ pageBody.css('minWidth', min[0]);
+ if (min[1].indexOf('%') !== -1)
+ pageBody.css('minHeight', min[1]);
+ else
+ pageBody.css('minHeight', (parseInt(min[1]) - layerOffset) + 'px');
+ }
+ const winHeight = $(window).height();
+ const winWidth = $(window).width();
+ layero.css({
+ 'left': (winWidth - layero.width()) / 2 + 'px',
+ 'top': (winHeight - layero.height()) / 2 + 'px'
+ });
+ const pageTitle = layero.find('.layui-layer-title');
+ pageTitle.css('borderRadius', borderRadius + ' ' + borderRadius + ' 0px 0px');
+ pageBody.css('borderRadius', '0px 0px ' + borderRadius + ' ' + borderRadius);
+ const $close = layero.find('.layui-layer-setwin');
+ if (layerOffset && layerOffset !== 42) {
+ pageTitle.css({
+ 'height': layerOffset + 'px',
+ 'line-height': layerOffset + 'px'
+ });
+ $close.css({
+ 'right': '10px',
+ 'top': '10px'
+ });
+ pageBody.css({
+ 'height': (layero.height() - layerOffset) + 'px'
+ });
+ }
+ if (typeof success === 'function')
+ success(layero, index);
+ },
+ beforeEnd: function (layero, index, that) {
+ const { beforeEnd } = toolConfig;
+ if (typeof beforeEnd === 'function') {
+ beforeEnd(layero, index, that);
+ }
+ },
+ end: function () {
+ const { end } = toolConfig;
+ if (typeof end === 'function') {
+ end();
+ }
+ },
+ cancel: function (index, layero) {
+ $('#layui-layer-shade' + index).remove();
+ const { cancel } = toolConfig;
+ if (typeof cancel === 'function') {
+ cancel(index, layero);
+ }
+ },
+ resizing: function (layero) {
+ const winHeight = $(window).height();
+ const winWidth = $(window).width();
+ const width = layero.width()/winWidth*100 + "%";
+ const height = layero.height()/winHeight*100 + "%";
+ layero.css({ width, height });
+ const { resizing } = toolConfig;
+ if (typeof resizing === 'function') {
+ const $content = layero.children('.layui-layer-content');
+ resizing({
+ layero: [ width, height ],
+ content: [ $content.width(), $content.height() ]
+ });
+ }
+ }
+ });
+}
+
+LayerExt.openProgressLayer = () => {
+
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/layer-firmware.js b/mixly/common/modules/mixly-modules/common/layer-firmware.js
new file mode 100644
index 00000000..a7905721
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/layer-firmware.js
@@ -0,0 +1,94 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Layer');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.LayerFirmware');
+
+const {
+ Env,
+ Msg,
+ Layer,
+ HTMLTemplate
+} = Mixly;
+
+
+class LayerFirmware extends Layer {
+ static {
+ HTMLTemplate.add(
+ 'html/dialog/firmware.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/dialog/firmware.html')))
+ );
+ }
+
+ #dialog_ = null;
+ #$dialogContent_ = null;
+ #$cancel_ = null;
+ #$burn_ = null;
+ #$select_ = null;
+ #map_ = {};
+
+ constructor(config = {}, shadowType = 'nav') {
+ const $dialogContent_ = $(HTMLTemplate.get('html/dialog/firmware.html').render({
+ cancel: Msg.Lang['nav.btn.cancel'],
+ burn: Msg.Lang['nav.btn.burn']
+ }));
+ config.content = $dialogContent_;
+ super(config, shadowType);
+ this.#$dialogContent_ = $dialogContent_;
+ this.#$cancel_ = $dialogContent_.find('.cancel');
+ this.#$burn_ = $dialogContent_.find('.burn');
+ this.#$select_ = $dialogContent_.find('.type');
+ this.#$select_.select2({
+ data: [],
+ minimumResultsForSearch: Infinity,
+ width: '380px',
+ dropdownCssClass: 'mixly-scrollbar'
+ });
+ this.addEventsType(['burn']);
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#$cancel_.click(() => {
+ this.hide();
+ });
+
+ this.#$burn_.click(() => {
+ this.hide();
+ this.runEvent('burn', this.#map_[this.#$select_.val()]);
+ });
+ }
+
+ setMenu(items) {
+ this.#$select_.empty();
+ for (let item of items) {
+ const newOption = new window.Option(item.text, item.id);
+ this.#$select_.append(newOption);
+ }
+ this.#$select_.trigger('change');
+ }
+
+ setMap(firmwareMap) {
+ this.#map_ = firmwareMap;
+ }
+
+ dispose() {
+ this.#$select_.select2('destroy');
+ this.#$select_.remove();
+ this.#$select_ = null;
+ this.#$cancel_.remove();
+ this.#$cancel_ = null;
+ this.#$burn_.remove();
+ this.#$burn_ = null;
+ this.#$dialogContent_.remove();
+ this.#$dialogContent_ = null;
+ this.#map_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.LayerFirmware = LayerFirmware;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/layer-new-file.js b/mixly/common/modules/mixly-modules/common/layer-new-file.js
new file mode 100644
index 00000000..6d85a396
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/layer-new-file.js
@@ -0,0 +1,76 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Layer');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.LayerNewFile');
+
+const {
+ Env,
+ Msg,
+ Layer,
+ HTMLTemplate
+} = Mixly;
+
+
+class LayerNewFile extends Layer {
+ static {
+ HTMLTemplate.add(
+ 'html/dialog/new-file.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/dialog/new-file.html')))
+ );
+ }
+
+ #dialog_ = null;
+ #$dialogContent_ = null;
+ #$cancel_ = null;
+ #$ok_ = null;
+
+ constructor(config = {}, shadowType = 'all') {
+ const $dialogContent_ = $(HTMLTemplate.get('html/dialog/new-file.html').render({
+ message: Msg.Lang['file.emptyInfo'],
+ cancel: Msg.Lang['nav.btn.cancel'],
+ ok: Msg.Lang['nav.btn.ok']
+ }));
+ config.title = Msg.Lang['nav.btn.file.new'];
+ config.cancelValue = Msg.Lang['nav.btn.cancel'];
+ config.cancel = () => {
+ this.hide();
+ return false;
+ };
+ config.cancelDisplay = false;
+ config.content = $dialogContent_;
+ super(config, shadowType);
+ this.#$dialogContent_ = $dialogContent_;
+ this.#$cancel_ = $dialogContent_.find('.cancel');
+ this.#$ok_ = $dialogContent_.find('.ok');
+ this.addEventsType(['empty']);
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#$cancel_.click(() => {
+ this.hide();
+ });
+
+ this.#$ok_.click(() => {
+ this.hide();
+ this.runEvent('empty');
+ });
+ }
+
+ dispose() {
+ this.#$cancel_.remove();
+ this.#$cancel_ = null;
+ this.#$ok_.remove();
+ this.#$ok_ = null;
+ this.#$dialogContent_.remove();
+ this.#$dialogContent_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.LayerNewFile = LayerNewFile;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/layer-progress.js b/mixly/common/modules/mixly-modules/common/layer-progress.js
new file mode 100644
index 00000000..6f74c078
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/layer-progress.js
@@ -0,0 +1,42 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Env');
+goog.require('Mixly.Layer');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.LayerProgress');
+
+const {
+ Env,
+ Layer,
+ HTMLTemplate
+} = Mixly;
+
+
+class LayerProgress extends Layer {
+ static {
+ HTMLTemplate.add(
+ 'html/dialog/progress.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/dialog/progress.html')))
+ );
+ }
+
+ #dialog_ = null;
+ #$dialogContent_ = null;
+
+ constructor(config = {}, shadowType = 'nav') {
+ const $dialogContent_ = $(HTMLTemplate.get('html/dialog/progress.html').render());
+ config.content = $dialogContent_;
+ super(config, shadowType);
+ this.#$dialogContent_ = $dialogContent_;
+ }
+
+ dispose() {
+ this.#$dialogContent_.remove();
+ this.#$dialogContent_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.LayerProgress = LayerProgress;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/layer.js b/mixly/common/modules/mixly-modules/common/layer.js
new file mode 100644
index 00000000..9bab639f
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/layer.js
@@ -0,0 +1,91 @@
+goog.loadJs('common', () => {
+
+goog.require('dialog');
+goog.require('Mixly.Env');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Component');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.Layer');
+
+const {
+ Env,
+ Registry,
+ Component,
+ HTMLTemplate
+} = Mixly;
+
+
+class Layer extends Component {
+ static {
+ this.templates = new Registry();
+
+ this.register = function (key, value) {
+ this.templates.register(key, value);
+ }
+
+ this.register('all', (new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/dialog/shadow-all.html'))
+ )).render());
+ this.register('nav', (new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/dialog/shadow-nav.html'))
+ )).render());
+ }
+
+ #dialog_ = null;
+
+ constructor(config = {}, shadowType = 'nav') {
+ super();
+ const shadow = Layer.templates.getItem(shadowType);
+ this.setContent($(shadow));
+ config.skin = config.skin ?? 'layui-anim layui-anim-scale';
+ this.#dialog_ = dialog({
+ padding: 0,
+ ...config
+ });
+ }
+
+ show() {
+ this.mountOn($(window.document.body));
+ this.#dialog_.show();
+ }
+
+ hide() {
+ this.getContent().detach();
+ this.#dialog_.close();
+ this.onUnmounted();
+ }
+
+ title(text) {
+ this.#dialog_.title(text);
+ }
+
+ width(value) {
+ this.#dialog_.width(value);
+ }
+
+ height(value) {
+ this.#dialog_.height(value);
+ }
+
+ reset() {
+ this.#dialog_.reset();
+ }
+
+ focus() {
+ this.#dialog_.focus();
+ }
+
+ blur() {
+ this.#dialog_.blur();
+ }
+
+ dispose() {
+ this.#dialog_.remove();
+ this.#dialog_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.Layer = Layer;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/loader.js b/mixly/common/modules/mixly-modules/common/loader.js
new file mode 100644
index 00000000..6163d903
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/loader.js
@@ -0,0 +1,148 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('d3');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.Boards');
+goog.require('Mixly.App');
+goog.require('Mixly.Msg');
+goog.require('Mixly.UserEvents');
+goog.require('Mixly.UserOPEvents');
+goog.require('Mixly.JSFuncs');
+goog.require('Mixly.Title');
+goog.require('Mixly.LocalStorage');
+goog.require('Mixly.Storage');
+goog.require('Mixly.Debug');
+goog.require('Mixly.API2');
+goog.require('Mixly.Electron.LibManager');
+goog.require('Mixly.Electron.File');
+goog.require('Mixly.WebCompiler.Loader');
+goog.require('Mixly.WebSocket.Loader');
+goog.provide('Mixly.Loader');
+
+const {
+ Config,
+ Boards,
+ Loader,
+ Env,
+ App,
+ Msg,
+ UserEvents,
+ UserOPEvents,
+ Title,
+ LocalStorage,
+ Storage,
+ Debug,
+ API2,
+ Electron = {},
+ Web = {},
+ WebCompiler = {},
+ WebSocket = {}
+} = Mixly;
+
+const { LibManager, File } = goog.isElectron? Electron : Web;
+const { USER } = Config;
+
+
+window.addEventListener('load', () => {
+ if (!goog.isElectron) {
+ if (Env.hasSocketServer) {
+ WebSocket.Loader.init();
+ } else if (Env.hasCompiler) {
+ WebCompiler.Loader.init();
+ }
+ }
+ const app = new App($('body')[0]);
+ Mixly.app = app;
+ const $xml = $(goog.readFileSync(Env.boardIndexPath));
+ let scrpitPaths = [];
+ let cssPaths = [];
+ let $categories = null;
+ for (let i = 0; i < $xml.length; i++) {
+ const $xmli = $($xml[i]);
+ let rePath = '';
+ switch ($xml[i].nodeName) {
+ case 'SCRIPT':
+ rePath = $xmli.attr('src');
+ rePath && scrpitPaths.push(path.join(Env.boardDirPath, rePath));
+ break;
+ case 'LINK':
+ rePath = $xmli.attr('href');
+ rePath && cssPaths.push(path.join(Env.boardDirPath, rePath));
+ break;
+ case 'XML':
+ $categories = $xmli;
+ break;
+ }
+ }
+ $categories && $('#toolbox').html($categories.html());
+ cssPaths.length && LazyLoad.css(cssPaths);
+ if (scrpitPaths.length) {
+ LazyLoad.js(scrpitPaths, () => {
+ Loader.start();
+ });
+ } else {
+ Loader.start();
+ }
+});
+
+Loader.start = () => {
+ if (window.frames.length !== parent.frames.length) {
+ window.userEvents = new UserEvents(Editor.blockEditor);
+ }
+ if (!goog.isElectron && window.location.host.indexOf('mixly.cn')) {
+ window.userOpEvents = new UserOPEvents();
+ }
+ if (goog.isElectron && typeof LibManager === 'object') {
+ LibManager.init(() => Loader.init());
+ } else {
+ Env.defaultXML = $('#toolbox').html();
+ Loader.init();
+ }
+}
+
+Loader.init = () => {
+ const selectedBoardName = Boards.getSelectedBoardName();
+ Boards.setSelectedBoard(selectedBoardName, {});
+ Msg.renderToolbox(true);
+ Mixly.app.getNav().resize();
+ const workspace = Mixly.app.getWorkspace();
+ const editor = workspace.getEditorsManager().getActive();
+ if (USER.cache !== 'no') {
+ Loader.restoreBlocks(editor);
+ }
+ Mixly.app.removeSkeleton();
+ window.addEventListener('unload', () => Loader.backupBlocks(editor), false);
+ API2.init();
+}
+
+Loader.restoreBlocks = (editor) => {
+ const filePath = LocalStorage.get(`${LocalStorage.PATH['USER']}/filePath`);
+ const mix = Storage.board('mix');
+ const openedPath = Storage.board('path');
+ if (filePath) {
+ LocalStorage.set(`${LocalStorage.PATH['USER']}/filePath`, '');
+ goog.isElectron && File.openFile(filePath);
+ } else if (mix) {
+ try {
+ editor.setValue(mix, '.mix');
+ if (openedPath && goog.isElectron) {
+ File.openedFilePath = openedPath;
+ File.workingPath = path.dirname(openedPath);
+ Title.updeteFilePath(File.openedFilePath);
+ }
+ } catch (error) {
+ Storage.board('mix', '');
+ Debug.error(error);
+ }
+ }
+}
+
+Loader.backupBlocks = (editor) => {
+ const mix = editor.getValue();
+ Storage.board('mix', mix);
+ Storage.board('path', Title.getFilePath());
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/local-storage.js b/mixly/common/modules/mixly-modules/common/local-storage.js
new file mode 100644
index 00000000..630c5c05
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/local-storage.js
@@ -0,0 +1,59 @@
+goog.loadJs('common', () => {
+
+goog.require('store');
+goog.require('Mixly.MArray');
+goog.provide('Mixly.LocalStorage');
+
+const { MArray, LocalStorage } = Mixly;
+
+LocalStorage.PATH = {
+ USER: 'mixly3.0/user',
+ BOARD: 'mixly3.0/boards/{{d.boardType}}/user',
+ THIRD_PARTY: 'mixly3.0/boards/{{d.boardType}}/third_party/{{d.thirdPartyName}}'
+};
+
+LocalStorage.set = function (path, value) {
+ let { first, last, firstKey, lastKey } = this.find(path);
+ if (!first) {
+ return;
+ }
+ last[lastKey] = value;
+ store.set(firstKey, first);
+}
+
+LocalStorage.get = function (path) {
+ let { first, last, lastKey } = this.find(path);
+ if (!first) {
+ return undefined;
+ }
+ return last[lastKey];
+}
+
+LocalStorage.getItems = function (path) {
+ let items = path.split('/');
+ MArray.remove(items, '');
+ return items;
+}
+
+LocalStorage.find = function (path) {
+ let items = this.getItems(path);
+ if (!items.length) {
+ return {};
+ }
+ let rootObj = {};
+ rootObj[items[0]] = store.get(items[0]);
+ let last = rootObj;
+ let value;
+ for (let i = 0; i < items.length - 1; i++) {
+ if (!(last[items[i]] instanceof Object)) {
+ last[items[i]] = {};
+ }
+ last = last[items[i]];
+ }
+ let first = rootObj[items[0]];
+ let firstKey = items[0];
+ let lastKey = items[items.length - 1];
+ return { first, last, firstKey, lastKey };
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/marray.js b/mixly/common/modules/mixly-modules/common/marray.js
new file mode 100644
index 00000000..3e4d972e
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/marray.js
@@ -0,0 +1,68 @@
+goog.loadJs('common', () => {
+goog.require('Mixly');
+goog.provide('Mixly.MArray');
+
+const { MArray } = Mixly;
+
+MArray.equals = (x, y) => {
+ // If both x and y are null or undefined and exactly the same
+ if (x === y) {
+ return true;
+ }
+ // If they are not strictly equal, they both need to be Objects
+ if (!(x instanceof Object ) || !(y instanceof Object)) {
+ return false;
+ }
+ //They must have the exact same prototype chain,the closest we can do is
+ //test the constructor.
+ if (x.constructor !== y.constructor) {
+ return false;
+ }
+ for (var p in x) {
+ //Inherited properties were tested using x.constructor === y.constructor
+ if (x.hasOwnProperty(p)) {
+ // Allows comparing x[ p ] and y[ p ] when set to undefined
+ if (!y.hasOwnProperty(p)) {
+ return false;
+ }
+ // If they have the same strict value or identity then they are equal
+ if (x[p] === y[p]) {
+ continue;
+ }
+ // Numbers, Strings, Functions, Booleans must be strictly equal
+ if ( typeof(x[p]) !== "object" ) {
+ return false;
+ }
+ // Objects and Arrays must be tested recursively
+ if (!MArray.equals(x[p], y[p])) {
+ return false;
+ }
+ }
+ }
+ for (p in y) {
+ // allows x[ p ] to be set to undefined
+ if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+MArray.remove = (arr, data) => {
+ let arrLen = arr.length;
+ for (let i = 0; i < arrLen; i++) {
+ if (arr[i] !== data) {
+ continue;
+ }
+ arr.splice(i, 1);
+ i--;
+ arrLen--;
+ }
+}
+
+//数组去除重复元素
+MArray.unique = (arr) => {
+ return Array.from(new Set(arr));
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/menu.js b/mixly/common/modules/mixly-modules/common/menu.js
new file mode 100644
index 00000000..b46356d5
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/menu.js
@@ -0,0 +1,135 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Env');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Events');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.HTMLTemplate');
+goog.provide('Mixly.Menu');
+
+const {
+ Env,
+ Debug,
+ Events,
+ IdGenerator,
+ HTMLTemplate
+} = Mixly;
+
+
+class Menu {
+ static {
+ HTMLTemplate.add(
+ 'html/menu-item.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/menu-item.html')))
+ );
+
+ this.getItem = (name, hotKey = '', icon = '') => {
+ return HTMLTemplate.get('html/menu-item.html').render({ name, hotKey, icon });
+ };
+ }
+
+ #menuItems_ = [];
+ #ids_ = {};
+ #isDynamic_ = false;
+ #events_ = new Events(['onRead']);
+ constructor(isDynamic) {
+ this.#ids_ = {};
+ this.#menuItems_ = [];
+ this.#isDynamic_ = isDynamic;
+ }
+
+ add(item) {
+ if (!item.id) {
+ if (item.type) {
+ item.id = item.type;
+ } else {
+ item.id = IdGenerator.generate();
+ }
+ }
+ if (!item.weight) {
+ item.weight = 0;
+ }
+ const { id, weight } = item;
+ if (this.#ids_[id]) {
+ delete this.#ids_[id];
+ }
+ let i = 0;
+ for (; i < this.#menuItems_.length; i++) {
+ if (this.#menuItems_[i].weight <= weight) {
+ continue;
+ }
+ break;
+ }
+ this.#menuItems_.splice(i, 0, item);
+ this.#ids_[id] = item;
+ return id;
+ }
+
+ remove(id) {
+ if (!this.#ids_[id]) {
+ return;
+ }
+ delete this.#ids_[id];
+ for (let i in this.#menuItems_) {
+ if (this.#menuItems_[i].id !== id) {
+ continue;
+ }
+ this.#menuItems_.splice(i, 1);
+ break;
+ }
+ }
+
+ empty() {
+ this.#ids_ = {};
+ this.#menuItems_ = [];
+ }
+
+ hasKey(id) {
+ return !!this.#ids_[id];
+ }
+
+ getItem(id) {
+ return this.#ids_[id] ?? null;
+ }
+
+ getAllItems() {
+ if (this.#isDynamic_) {
+ this.empty();
+ const results = this.runEvent('onRead');
+ if (results?.length) {
+ for (let item of results[0]) {
+ this.add(item);
+ }
+ }
+ }
+ return this.#menuItems_;
+ }
+
+ bind(type, func) {
+ return this.#events_.bind(type, func);
+ }
+
+ unbind(id) {
+ this.#events_.unbind(id);
+ }
+
+ addEventsType(eventsType) {
+ this.#events_.addType(eventsType);
+ }
+
+ runEvent(eventsType, ...args) {
+ return this.#events_.run(eventsType, ...args);
+ }
+
+ offEvent(eventsType) {
+ this.#events_.off(eventsType);
+ }
+
+ resetEvent() {
+ this.#events_.reset();
+ }
+}
+
+Mixly.Menu = Menu;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/mfile.js b/mixly/common/modules/mixly-modules/common/mfile.js
new file mode 100644
index 00000000..ce8e746f
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/mfile.js
@@ -0,0 +1,388 @@
+goog.loadJs('common', () => {
+
+goog.require('layui');
+goog.require('Blockly');
+goog.require('Base64');
+goog.require('Mixly.Config');
+goog.require('Mixly.MArray');
+goog.require('Mixly.Boards');
+goog.require('Mixly.XML');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Msg');
+goog.provide('Mixly.MFile');
+
+const { form, util } = layui;
+
+const {
+ Config,
+ MArray,
+ Boards,
+ XML,
+ LayerExt,
+ Msg,
+ MFile
+} = Mixly;
+
+const { BOARD, SOFTWARE } = Config;
+
+MFile.SAVE_FILTER_TYPE = {
+ mix: { name: Msg.Lang['file.type.mix'], extensions: ['mix'] },
+ py: { name: Msg.Lang['file.type.python'], extensions: ['py'] },
+ ino: { name: Msg.Lang['file.type.arduino'], extensions: ['ino'] },
+ hex: { name: Msg.Lang['file.type.hex'], extensions: ['hex'] },
+ bin: { name: Msg.Lang['file.type.bin'], extensions: ['bin'] },
+ mil: { name: Msg.Lang['file.type.mil'], extensions: ['mil'] }
+};
+
+MFile.saveFilters = [ MFile.SAVE_FILTER_TYPE.mix ];
+
+MFile.OPEN_FILTER_TYPE = ['mix','xml', 'py', 'ino', 'hex', 'bin'];
+
+MFile.openFilters = ['mix'];
+
+/**
+ * @function 更新保存文件时可用的后缀
+ * @param config { array }
+ * config = ["py", "ino", "hex", "bin", "png"]
+ * 注:mix后缀为默认添加,列表后缀名顺序即为保存时自上而下显示的顺序
+ * @param priority { string },配置需要优先显示的后缀名,没有此项可不填
+ * @return void
+ **/
+MFile.updateSaveFilters = (config, priority = null) => {
+ if (typeof config !== 'object')
+ config = [];
+ MFile.saveFilters = [ MFile.SAVE_FILTER_TYPE.mix ];
+ let saveFilterType = ['mix'];
+ for (let i of config)
+ if (MFile.SAVE_FILTER_TYPE[i] && !saveFilterType.includes(i)) {
+ saveFilterType.push(i);
+ if (i === priority) {
+ MFile.saveFilters.unshift(MFile.SAVE_FILTER_TYPE[i]);
+ } else {
+ MFile.saveFilters.push(MFile.SAVE_FILTER_TYPE[i]);
+ }
+ }
+}
+
+/**
+ * @function 更新打开文件时的可用后缀
+ * @param config { array }
+ * config = ["py", "ino", "hex", "bin"]
+ * @return void
+ **/
+MFile.updateOpenFilters = (config, priority) => {
+ if (typeof config !== 'object')
+ config = [];
+ MFile.openFilters = ['mix', 'xml'];
+ for (let i of config)
+ if (MFile.OPEN_FILTER_TYPE.includes(i) && !MFile.openFilters.includes(i))
+ MFile.openFilters.push(i);
+}
+
+MFile.init = () => {
+ const saveConfig = BOARD?.nav?.save ?? {};
+ let saveFilters = [], openFilters = [];
+ for (let i in saveConfig)
+ if (saveConfig[i]) {
+ saveFilters.push(i);
+ openFilters.push(i);
+ }
+ if (BOARD?.nav?.setting?.thirdPartyLibrary)
+ saveFilters.push('mil');
+ MFile.updateOpenFilters(openFilters);
+ MFile.updateSaveFilters(saveFilters);
+}
+
+MFile.init();
+
+MFile.getCode = (type) => {
+ const { mainEditor } = Editor;
+ const { codeEditor, blockEditor } = mainEditor;
+ const { editor, generator } = blockEditor;
+ if (mainEditor.selected === 'CODE')
+ return codeEditor.getValue();
+ else {
+ return generator.workspaceToCode(editor) || '';
+ }
+}
+
+MFile.getMix = () => {
+ const mixDom = $(Blockly.Xml.workspaceToDom(Editor.blockEditor)),
+ version = SOFTWARE?.version ?? 'Mixly 2.0',
+ boardName = Boards.getSelectedBoardName(),
+ board = BOARD?.boardType ?? 'default',
+ config = Boards.getSelectedBoardConfig();
+ mixDom.removeAttr('xmlns')
+ .attr('version', version)
+ .attr('board', board + '@' + boardName);
+ let xmlStr = mixDom[0].outerHTML;
+ let code = MFile.getCode();
+ if (config) {
+ try {
+ xmlStr += `
${JSON.stringify(config)} `;
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ if (BOARD.saveMixWithCode) {
+ code = Base64.encode(code);
+ xmlStr += `
${code}`;
+ }
+ return xmlStr;
+}
+
+MFile.getMil = () => {
+ const mixDom = $(MFile.getMix());
+ let xmlDom, configDom, codeDom;
+ for (let i = 0; mixDom[i]; i++) {
+ switch (mixDom[i].nodeName) {
+ case 'XML':
+ xmlDom = $(mixDom[i]);
+ break;
+ case 'CONFIG':
+ configDom = $(mixDom[i]);
+ break;
+ case 'CODE':
+ codeDom = $(mixDom[i]);
+ break;
+ }
+ }
+ if (!xmlDom) return '';
+ configDom && configDom.remove();
+ codeDom && codeDom.remove();
+ xmlDom.attr('type', 'lib');
+ xmlDom.find('block,shadow').removeAttr('id varid x y');
+ const blocksDom = xmlDom.children('block');
+ let blockXmlList = [];
+ for (let i = 0; blocksDom[i]; i++) {
+ const outerHTML = blocksDom[i].outerHTML;
+ if (!blockXmlList.includes(outerHTML))
+ blockXmlList.push(outerHTML);
+ else
+ blocksDom[i].remove();
+ }
+ return xmlDom[0].outerHTML;
+}
+
+MFile.parseMix = (xml, useCode = false, useIncompleteBlocks = false, endFunc = (message) => {}) => {
+ const mixDom = xml;
+ let xmlDom, configDom, codeDom;
+ for (let i = 0; mixDom[i]; i++) {
+ switch (mixDom[i].nodeName) {
+ case 'XML':
+ xmlDom = $(mixDom[i]);
+ break;
+ case 'CONFIG':
+ configDom = $(mixDom[i]);
+ break;
+ case 'CODE':
+ codeDom = $(mixDom[i]);
+ break;
+ }
+ }
+ if (!xmlDom && !codeDom) {
+ layer.msg(Msg.Lang['editor.invalidData'], { time: 1000 });
+ return;
+ }
+ for (let i of ['version', 'id', 'type', 'varid', 'name', 'x', 'y', 'items']) {
+ const nowDom = xmlDom.find('*[' + i + ']');
+ if (nowDom.length) {
+ for (let j = 0; nowDom[j]; j++) {
+ let attr = $(nowDom[j]).attr(i);
+ try {
+ attr = attr.replaceAll('\\\"', '');
+ } catch (error) {
+ console.log(error);
+ }
+ $(nowDom[j]).attr(i, attr);
+ }
+ }
+ }
+ let config, configStr = configDom && configDom.html();
+ try {
+ if (configStr)
+ config = JSON.parse(configStr);
+ } catch (error) {
+ console.log(error);
+ }
+ let boardName = xmlDom.attr('board') ?? '';
+ Boards.setSelectedBoard(boardName, config);
+ let code = codeDom ? codeDom.html() : '';
+ if (Base64.isValid(code)) {
+ code = Base64.decode(code);
+ } else {
+ try {
+ code = util.unescape(code);
+ code = code.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/g, function (s) {
+ try {
+ return decodeURIComponent(s.replace(/_/g, '%'));
+ } catch (error) {
+ return s;
+ }
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ if (useCode) {
+ if (!codeDom) {
+ layer.msg(Msg.Lang['editor.invalidData'], { time: 1000 });
+ return;
+ }
+ Editor.mainEditor.drag.full('NEGATIVE'); // 完全显示代码编辑器
+ Editor.codeEditor.setValue(code, -1);
+ Editor.blockEditor.clear();
+ endFunc('USE_CODE');
+ return;
+ }
+ const blockDom = mixDom.find('block');
+ const shadowDom = mixDom.find('shadow');
+ blockDom.removeAttr('id varid');
+ shadowDom.removeAttr('id varid');
+ let blocks = [];
+ let undefinedBlocks = [];
+ for (let i = 0; blockDom[i]; i++) {
+ const blockType = $(blockDom[i]).attr('type');
+ if (blockType && !blocks.includes(blockType))
+ blocks.push(blockType);
+ }
+ for (let i = 0; shadowDom[i]; i++) {
+ const shadowType = $(shadowDom[i]).attr('type');
+ if (shadowType && !blocks.includes(shadowType))
+ blocks.push(shadowType);
+ }
+ const blocklyGenerator = Editor.mainEditor.blockEditor.generator;
+ for (let i of blocks) {
+ if (Blockly.Blocks[i] && blocklyGenerator.forBlock[i]) {
+ continue;
+ }
+ undefinedBlocks.push(i);
+ }
+ if (undefinedBlocks.length) {
+ MFile.showParseMixErrorDialog(mixDom, undefinedBlocks, endFunc);
+ return;
+ }
+ Editor.blockEditor.clear();
+ Blockly.Xml.domToWorkspace(xmlDom[0], Editor.blockEditor);
+ Editor.blockEditor.scrollCenter();
+ Blockly.hideChaff();
+ if (!useIncompleteBlocks && codeDom) {
+ const workspaceCode = MFile.getCode();
+ if (workspaceCode !== code) {
+ Editor.mainEditor.drag.full('NEGATIVE'); // 完全显示代码编辑器
+ Editor.codeEditor.setValue(code, -1);
+ }
+ endFunc();
+ return;
+ }
+ Editor.mainEditor.drag.full('POSITIVE'); // 完全显示块编辑器
+ if (useIncompleteBlocks)
+ endFunc('USE_INCOMPLETE_BLOCKS');
+ else
+ endFunc();
+}
+
+MFile.removeUndefinedBlocks = (xml, undefinedBlocks) => {
+ for (let i of undefinedBlocks) {
+ xml.find('*[type='+i+']').remove();
+ }
+}
+
+MFile.showParseMixErrorDialog = (xml, undefinedBlocks, endFunc = () => {}) => {
+ const { PARSE_MIX_ERROR_DIV } = XML.TEMPLATE_STR;
+ const renderStr = XML.render(PARSE_MIX_ERROR_DIV, {
+ text: undefinedBlocks.join('
'),
+ btn1Name: Msg.Lang['editor.cancel'],
+ btn2Name: Msg.Lang['editor.ignoreBlocks'],
+ btn3Name: Msg.Lang['editor.loadCode']
+ })
+ LayerExt.open({
+ title: Msg.Lang['editor.parseMixErrorInfo'],
+ id: 'parse-mix-error-layer',
+ area: ['50%', '250px'],
+ max: ['500px', '250px'],
+ min: ['350px', '100px'],
+ shade: LayerExt.SHADE_ALL,
+ content: renderStr,
+ borderRadius: '5px',
+ success: (layero, index) => {
+ $('#parse-mix-error-layer').css('overflow', 'hidden');
+ form.render(null, 'parse-mix-error-filter');
+ layero.find('button').click((event) => {
+ layer.close(index);
+ const mId = $(event.currentTarget).attr('m-id');
+ switch (mId) {
+ case '0':
+ break;
+ case '1':
+ for (let i of undefinedBlocks) {
+ xml.find('*[type='+i+']').remove();
+ }
+ MFile.parseMix(xml, false, true, endFunc);
+ break;
+ case '2':
+ MFile.parseMix(xml, true, false, endFunc);
+ break;
+ }
+ });
+ }
+ });
+}
+
+MFile.openFile = (filters, readType = 'text', sucFunc = () => {}) => {
+ const loadFileDom = $('
');
+ loadFileDom.attr({
+ id: 'web-open-file',
+ type: 'file',
+ name: 'web-open-file',
+ accept: filters
+ });
+ loadFileDom.change(function(event) {
+ MFile.onclickOpenFile(this, readType, (data) => {
+ sucFunc(data);
+ });
+ });
+ loadFileDom.css('display', 'none');
+ $('#web-open-file').remove();
+ $('body').append(loadFileDom);
+ loadFileDom.click();
+}
+
+MFile.onclickOpenFile = (input, readType, endFunc) => {
+ const files = input.files;
+ //限制上传文件的 大小,此处为10M
+ if (files[0].size > 5 * 1024 * 1024) {
+ layer.msg('所选择文件大小必须在5MB内', { time: 1000 });
+ $('#web-open-file').remove();
+ endFunc(null);
+ return false;
+ }
+ const resultFile = input.files[0];
+ // 如果文件存在
+ if (resultFile) {
+ const filename = resultFile.name;
+ const reader = new FileReader();
+ switch (readType) {
+ case 'text':
+ reader.readAsText(resultFile);
+ break;
+ case 'bin':
+ reader.readAsBinaryString(resultFile);
+ break;
+ case 'url':
+ reader.readAsDataURL(resultFile);
+ break;
+ default:
+ reader.readAsArrayBuffer(resultFile);
+ }
+ reader.onload = function (e) {
+ const data = e.target.result;
+ $('#web-open-file').remove();
+ endFunc({ data, filename });
+ };
+ } else {
+ endFunc(null);
+ }
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/mixly.js b/mixly/common/modules/mixly-modules/common/mixly.js
new file mode 100644
index 00000000..bb6e0331
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/mixly.js
@@ -0,0 +1,12 @@
+goog.loadJs('common', () => {
+
+goog.provide('Mixly');
+
+Mixly.require = function(moduleName) {
+ if (!goog.isElectron) {
+ return;
+ }
+ return require(moduleName);
+}
+
+});
diff --git a/mixly/common/modules/mixly-modules/common/mixly2-api.js b/mixly/common/modules/mixly-modules/common/mixly2-api.js
new file mode 100644
index 00000000..6d5c61c7
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/mixly2-api.js
@@ -0,0 +1,50 @@
+goog.loadJs('common', () => {
+
+goog.require('Blockly');
+goog.require('Mixly');
+goog.provide('Mixly.API2');
+goog.provide('Mixly.Editor');
+
+const { API2, Editor } = Mixly;
+
+const HUES = {
+ ACTUATOR_HUE: 100,
+ BLYNK0_HUE: 0,
+ BLYNK1_HUE: 159,
+ COMMUNICATE_HUE: 140,
+ LOOPS_HUE: 120,
+ DISPLAY_HUE: 180,
+ ETHERNET_HUE: 0,
+ FACTORY_HUE: '#777777',
+ BASE_HUE: 20,
+ LISTS_HUE: 260,
+ LOGIC_HUE: 210,
+ MATH_HUE: 230,
+ PINS_HUE: 230,
+ PROCEDURES_HUE: 290,
+ SCOOP_HUE: 120,
+ SENSOR_HUE: 40,
+ SERIAL_HUE: 65,
+ STORAGE_HUE: 0,
+ TEXTS_HUE: 160,
+ TOOLS_HUE: '#555555',
+ VARIABLES_HUE: 330,
+ HANDBIT_HUE: 65
+};
+
+API2.init = () => {
+ const workspace = Mixly.app.getWorkspace();
+ const editorsManager = workspace.getEditorsManager();
+ const mixEditor = editorsManager.getActive();
+ const blockPage = mixEditor.getPage('block');
+ const codePage = mixEditor.getPage('code');
+ Blockly.mainWorkspace = blockPage.getEditor();
+ Editor.blockEditor = blockPage.getEditor();
+ Editor.codeEditor = codePage.getEditor();
+ Object.assign(Blockly.Msg, HUES);
+ Blockly.ALIGN_LEFT = Blockly.inputs.Align.LEFT;
+ Blockly.ALIGN_CENTRE = Blockly.inputs.Align.CENTRE;
+ Blockly.ALIGN_RIGHT = Blockly.inputs.Align.RIGHT;
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/mjson.js b/mixly/common/modules/mixly-modules/common/mjson.js
new file mode 100644
index 00000000..06545592
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/mjson.js
@@ -0,0 +1,68 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Debug');
+goog.provide('Mixly.MJson');
+
+const { Debug, MJson } = Mixly;
+
+MJson.operate = (jsonObj, optFunc) => {
+ // 循环所有键
+ for (var key in jsonObj) {
+ //如果对象类型为object类型且数组长度大于0 或者 是对象 ,继续递归解析
+ var element = jsonObj[key];
+ if (element.length > 0 && typeof (element) == "object" || typeof (element) == "object") {
+ let data = MJson.operate(element, optFunc);
+ for (let i in data) {
+ jsonObj[key][i] = data[i];
+ }
+ } else { //不是对象或数组、直接输出
+ if (typeof (element) === 'string') {
+ try {
+ jsonObj[key] = optFunc(jsonObj[key]);
+ } catch (error) {
+ Debug.error(error);
+ }
+ }
+ }
+ }
+ return jsonObj;
+}
+
+MJson.decode = (jsonObj) => {
+ // 深度拷贝对象,防止解码或编码时篡改原有对象
+ let newJsonObj = structuredClone(jsonObj);
+ return MJson.operate(newJsonObj, decodeURIComponent);
+}
+
+MJson.encode = (jsonObj) => {
+ // 深度拷贝对象,防止解码或编码时篡改原有对象
+ let newJsonObj = structuredClone(jsonObj);
+ return MJson.operate(newJsonObj, encodeURIComponent);;
+}
+
+MJson.parse = (jsonStr) => {
+ let jsonObj = null;
+ try {
+ jsonStr = jsonStr.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m);
+ jsonObj = JSON.parse(jsonStr);
+ } catch (error) {
+ Debug.error(error);
+ }
+ return jsonObj;
+}
+
+MJson.stringify = (jsonObj) => {
+ let jsonStr = '';
+ try {
+ jsonStr = JSON.stringify(jsonObj);
+ } catch (error) {
+ Debug.error(error);
+ }
+ return jsonStr;
+}
+
+MJson.get = (inPath) => {
+ return goog.readJsonSync(inPath);
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/monaco-theme.js b/mixly/common/modules/mixly-modules/common/monaco-theme.js
new file mode 100644
index 00000000..db5de20c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/monaco-theme.js
@@ -0,0 +1,119 @@
+goog.loadJs('common', () => {
+
+goog.require('monaco');
+goog.require('Mixly.Registry');
+goog.provide('Mixly.MonacoTheme');
+
+const { Registry } = Mixly;
+
+
+class MonacoTheme {
+ static {
+ this.cssClassNamePrefix = 'mts';
+ this.themesRegistry = new Registry();
+ this.supportThemes = [];
+ // this.supportThemes = ['cpp', 'python'];
+
+ this.getClassNameOfTerm = function (type, theme, term) {
+ return `${this.cssClassNamePrefix}-${type}-${theme}-${term}`;
+ }
+
+ /*this.themesRegistry.register('vs-dark-cpp', new MonacoTheme(
+ 'vs-dark-cpp',
+ 'cpp',
+ 'dark',
+ goog.readJsonSync('../common/templates/json/tree-sitter/themes/dark-cpp.json')
+ ));
+
+ this.themesRegistry.register('vs-light-cpp', new MonacoTheme(
+ 'vs-light-cpp',
+ 'cpp',
+ 'light',
+ goog.readJsonSync('../common/templates/json/tree-sitter/themes/light-cpp.json')
+ ));
+
+ this.themesRegistry.register('vs-dark-python', new MonacoTheme(
+ 'vs-dark-python',
+ 'python',
+ 'dark',
+ goog.readJsonSync('../common/templates/json/tree-sitter/themes/dark-python.json')
+ ));
+
+ this.themesRegistry.register('vs-light-python', new MonacoTheme(
+ 'vs-light-python',
+ 'python',
+ 'light',
+ goog.readJsonSync('../common/templates/json/tree-sitter/themes/light-python.json')
+ ));*/
+ }
+
+ #id_ = null;
+ #type_ = null;
+ #theme_ = null;
+ #$tag_ = null;
+ #config_ = null;
+
+ constructor(id, type, theme, config) {
+ this.#id_ = id;
+ this.#type_ = type;
+ this.#theme_ = theme;
+ this.load(config);
+ }
+
+ load(config) {
+ monaco.editor.defineTheme(this.#id_, config.base);
+ this.#config_ = config;
+
+ if (!this.#$tag_) {
+ let hasStyleNode = $('head').find(`style[style-id='${this.id_}']`).length;
+ if (hasStyleNode) {
+ return;
+ }
+ this.#$tag_ = $('');
+ this.#$tag_.attr('style-id', this.id);
+ this.#$tag_.attr('type', 'text/css')
+ $('head').append(this.#$tag_);
+ }
+
+ this.#$tag_.html(this.generateCss());
+ }
+
+ generateCss() {
+ return Object.keys(this.#config_.monacoTreeSitter)
+ .map(term =>
+ `span.${MonacoTheme.getClassNameOfTerm(this.#type_, this.#theme_, term)}{${this.generateStyleOfTerm(term)}}`
+ )
+ .join('');
+ }
+
+ generateStyleOfTerm(term) {
+ const style = this.#config_.monacoTreeSitter[term];
+ if (!style) {
+ return '';
+ }
+
+ if (typeof style === 'string') {
+ return `color:${style}`;
+ }
+
+ return `color:${style.color};${style.extraCssStyles || ''}`;
+ }
+
+ getColorOfTerm(term) {
+ const style = this.#config_.monacoTreeSitter[term];
+ if (!style) {
+ return undefined;
+ }
+ return typeof style === 'object' ? style.color : style;
+ }
+
+ dispose() {
+ this.#$tag_ && this.#$tag_.remove();
+ this.#$tag_ = null;
+ this.#config_ = null;
+ }
+}
+
+Mixly.MonacoTheme = MonacoTheme;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/monaco-tree-sitter.js b/mixly/common/modules/mixly-modules/common/monaco-tree-sitter.js
new file mode 100644
index 00000000..0aa5f7ae
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/monaco-tree-sitter.js
@@ -0,0 +1,127 @@
+goog.loadJs('common', () => {
+
+goog.require('_');
+goog.require('monaco');
+goog.require('Mixly.Registry');
+goog.require('Mixly.MonacoTheme');
+goog.provide('Mixly.MonacoTreeSitter');
+
+const { Registry, MonacoTheme } = Mixly;
+
+
+class MonacoTreeSitter {
+ static {
+ this.workerPath = '../common/modules/mixly-modules/workers/common/tree-sitter/index.js';
+ this.supportTreeSitters_ = new Registry();
+ this.activeTreeSitters_ = new Registry();
+
+ /*this.supportTreeSitters_.register('python', {
+ workerName: 'pythonTreeSitterService',
+ wasm: 'tree-sitter-python.wasm'
+ });
+
+ this.supportTreeSitters_.register('cpp', {
+ workerName: 'cppTreeSitterService',
+ wasm: 'tree-sitter-cpp.wasm'
+ });*/
+
+ this.activateTreeSitter = async function (type) {
+ if (!this.supportTreeSitters_.hasKey(type)) return null;
+
+ const info = this.supportTreeSitters_.getItem(type);
+ if (this.activeTreeSitters_.hasKey(type)) {
+ const ts = this.activeTreeSitters_.getItem(type);
+ if (ts.loading) await ts.loading;
+ return ts;
+ }
+
+ const treeSitter = workerpool.pool(this.workerPath, {
+ workerOpts: { name: info.workerName },
+ workerType: 'web'
+ });
+
+ const grammar = await goog.readJson(
+ `../common/templates/json/tree-sitter/grammars/${type}.json`
+ );
+
+ treeSitter.loading = treeSitter.exec(
+ 'init',
+ [info.wasm, grammar]
+ );
+
+ this.activeTreeSitters_.register(type, treeSitter);
+ await treeSitter.loading;
+ treeSitter.loading = null;
+ return treeSitter;
+ };
+
+ this.treeSitterPostion = function (pos) {
+ return {
+ row: pos.lineNumber - 1,
+ column: pos.column - 1
+ };
+ };
+ }
+
+ constructor(editor, opts) {
+ this.editor = editor;
+
+ this.seq = 0;
+ this.decorations = [];
+
+ this.refresh = _.debounce(
+ this.refresh.bind(this),
+ opts?.debounceUpdate ?? 15
+ );
+ }
+
+ dispose() {
+ this.pool.terminate(true);
+ this.decorations = [];
+ }
+
+ async updateWorker(type, theme, text) {
+ const treeSitter = await MonacoTreeSitter.activateTreeSitter(type);
+ if (!treeSitter) {
+ return;
+ }
+ const id = ++this.seq;
+ const dto = await treeSitter.exec('update', [text]);
+ if (id !== this.seq) return;
+ this.applyDecorations(type, theme, dto);
+ }
+
+ applyDecorations(type, theme, dto) {
+ const decos = [];
+
+ for (const [term, ranges] of Object.entries(dto)) {
+ const className = MonacoTheme.getClassNameOfTerm(type, theme, term);
+ for (const r of ranges) {
+ decos.push({
+ range: new monaco.Range(
+ r.startLineNumber,
+ r.startColumn,
+ r.endLineNumber,
+ r.endColumn
+ ),
+ options: {
+ inlineClassName: className
+ }
+ });
+ }
+ }
+ this.decorations = this.editor.getEditor().deltaDecorations(this.decorations, decos);
+ }
+
+ refresh(type, theme, newText) {
+ this.updateWorker(type, theme, newText);
+ }
+
+ setValue(type, theme, newText) {
+ this.refresh(type, theme, newText);
+ }
+}
+
+Mixly.MonacoTreeSitter = MonacoTreeSitter;
+
+});
diff --git a/mixly/common/modules/mixly-modules/common/msg.js b/mixly/common/modules/mixly-modules/common/msg.js
new file mode 100644
index 00000000..c8bbcb1a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/msg.js
@@ -0,0 +1,85 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.MJson');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Blockly');
+goog.require('Blockly.Lang.ZhHans');
+goog.require('Blockly.Lang.ZhHant');
+goog.require('Blockly.Lang.En');
+goog.provide('Mixly.Msg');
+
+const {
+ Msg,
+ MJson,
+ Config,
+ Env
+} = Mixly;
+
+const { USER } = Config;
+
+const {
+ ZhHans,
+ ZhHant,
+ En
+} = Blockly.Lang;
+
+
+Msg.LANG_TYPE = ['zh-hans', 'zh-hant', 'en'];
+Msg.LANG = {
+ 'zh-hans': MJson.get(path.join(Env.msgPath, 'mixly/zh-hans.json')),
+ 'zh-hant': MJson.get(path.join(Env.msgPath, 'mixly/zh-hant.json')),
+ 'en': MJson.get(path.join(Env.msgPath, 'mixly/en.json'))
+};
+Msg.BLOCKLY_LANG = {
+ 'zh-hans': ZhHans,
+ 'zh-hant': ZhHant,
+ 'en': En
+};
+Msg.BLOCKLY_LANG_DEFAULT = {
+ 'zh-hans': MJson.get(path.join(Env.msgPath, 'blockly/default/zh-hans.json')),
+ 'zh-hant': MJson.get(path.join(Env.msgPath, 'blockly/default/zh-hant.json')),
+ 'en': MJson.get(path.join(Env.msgPath, 'blockly/default/en.json'))
+};
+Msg.nowLang = USER.language ?? 'zh-hans';
+Msg.blocklyDefault = Blockly.Msg;
+
+Msg.getLang = (str) => {
+ return Msg.LANG[Msg.nowLang][str] ?? '';
+}
+
+Msg.changeTo = (lang) => {
+ lang = Msg.LANG_TYPE.includes(lang) ? lang : 'zh-hans';
+ Msg.nowLang = lang;
+ Msg.Lang = Msg.LANG[lang];
+ Blockly.Msg = Msg.BLOCKLY_LANG[lang];
+ Object.assign(Msg.blocklyDefault, Msg.BLOCKLY_LANG_DEFAULT[lang]);
+}
+
+Msg.renderToolbox = (addToolboxitemid = false) => {
+ let $categories = $('#toolbox').find('category');
+ for (let i = 0; i < $categories.length; i++) {
+ let { id } = $categories[i];
+ if (!Blockly.Msg.MSG[id]) {
+ continue;
+ }
+ let $category = $($categories[i]);
+ $category.attr('name', Blockly.Msg.MSG[id]);
+ if (addToolboxitemid) {
+ if ($category.attr('toolboxitemid')) {
+ continue;
+ }
+ $category.attr({
+ 'toolboxitemid': id,
+ 'name': Blockly.Msg.MSG[id]
+ });
+ } else {
+ $(`span[id="${id}.label"]`).html(Blockly.Msg.MSG[id]);
+ }
+ }
+}
+
+Msg.changeTo(Msg.nowLang);
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/mstring.js b/mixly/common/modules/mixly-modules/common/mstring.js
new file mode 100644
index 00000000..07051419
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/mstring.js
@@ -0,0 +1,76 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Debug');
+goog.provide('Mixly.MString');
+
+const { Debug, MString } = Mixly;
+
+/**
+ * @function 使用传入值替换字符串中{xxx}
+ * @param str {string} 传入字符串
+ * @param obj {object}
+ * obj = {
+ * xxx: value1,
+ * xxx: value2
+ * }
+ * 使用value替换{xxx}
+ * @return {string} 返回处理后的字符串
+ **/
+MString.tpl = (str, obj) => {
+ if (typeof str !== 'string' || !(obj instanceof Object)) {
+ return str;
+ }
+ for (let key in obj) {
+ let re = new RegExp("{[\s]*" + key + "[\s]*}", "gim");
+ str = str.replace(re, obj[key]);
+ }
+ return str;
+}
+
+MString.decode = (str) => {
+ try {
+ str = unescape(str.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
+ str = unescape(str.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
+ } catch (error) {
+ Debug.error(error);
+ }
+ return str;
+}
+
+MString.strToByte = (str) => {
+ var len, c;
+ len = str.length;
+ var bytes = [];
+ for (var i = 0; i < len; i++) {
+ c = str.charCodeAt(i);
+ if (c >= 0x010000 && c <= 0x10FFFF) {
+ bytes.push(((c >> 18) & 0x07) | 0xF0);
+ bytes.push(((c >> 12) & 0x3F) | 0x80);
+ bytes.push(((c >> 6) & 0x3F) | 0x80);
+ bytes.push((c & 0x3F) | 0x80);
+ } else if (c >= 0x000800 && c <= 0x00FFFF) {
+ bytes.push(((c >> 12) & 0x0F) | 0xE0);
+ bytes.push(((c >> 6) & 0x3F) | 0x80);
+ bytes.push((c & 0x3F) | 0x80);
+ } else if (c >= 0x000080 && c <= 0x0007FF) {
+ bytes.push(((c >> 6) & 0x1F) | 0xC0);
+ bytes.push((c & 0x3F) | 0x80);
+ } else {
+ bytes.push(c & 0xFF);
+ }
+ }
+ return new Int8Array(bytes);
+}
+
+MString.uint8ArrayToStr = (fileData) => {
+ var dataString = "";
+ for (var i = 0; i < fileData.length; i++) {
+ var convert = (fileData[i]).toString(16);
+ if (convert.length % 2 == 1)
+ convert = "0" + convert;
+ dataString = dataString + " " + convert.toUpperCase();
+ }
+ return dataString;
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/nav.js b/mixly/common/modules/mixly-modules/common/nav.js
new file mode 100644
index 00000000..0a0e3efe
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/nav.js
@@ -0,0 +1,464 @@
+goog.loadJs('common', () => {
+
+goog.require('layui');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Component');
+goog.require('Mixly.DropdownMenuGroup');
+goog.provide('Mixly.Nav');
+
+const {
+ Env,
+ XML,
+ Msg,
+ HTMLTemplate,
+ Component,
+ DropdownMenuGroup
+} = Mixly;
+
+const { element } = layui;
+
+
+class Nav extends Component {
+ static {
+ /**
+ * nav容器html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/nav.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/nav.html')))
+ );
+
+ /**
+ * nav按钮html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/nav-btn.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/nav-btn.html')))
+ );
+
+ /**
+ * nav子元素容器html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/nav-item-container.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/nav-item-container.html')))
+ );
+
+ /**
+ * nav子元素html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/nav-item.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/nav-item.html')))
+ );
+
+ /**
+ * 板卡选择器html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/board-selector-div.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/board-selector-div.html')))
+ );
+
+ /**
+ * 端口选择器html片段
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/port-selector-div.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/port-selector-div.html')))
+ );
+
+ /**
+ * 下拉菜单遮罩
+ * @type {String}
+ */
+ HTMLTemplate.add(
+ 'html/nav/shadow.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/nav/shadow.html')))
+ );
+
+ Nav.Scope = {
+ 'LEFT': -1,
+ 'CENTER': 0,
+ 'RIGHT': 1,
+ '-1': 'LEFT',
+ '0': 'CENTER',
+ '1': 'RIGHT'
+ };
+
+ this.navs = [];
+
+ this.getAll = () => {
+ return this.navs;
+ }
+
+ this.add = (nav) => {
+ this.remove(nav);
+ this.navs.push(nav);
+ }
+
+ this.remove = (nav) => {
+ for (let i in this.workspaces) {
+ if (this.navs[i].id !== nav.id) {
+ continue;
+ }
+ this.navs.slice(i, 1);
+ }
+ }
+
+ this.getMain = () => {
+ if (this.navs.length) {
+ return this.navs[0];
+ }
+ return null;
+ }
+ }
+
+ #$container_ = null;
+ #$leftBtnContainer_ = null;
+ #$leftBtnExtContainer_ = null;
+ #$rightBtnContainer_ = null;
+ #$dropdownContainer_ = null;
+ #$rightMenuContainer_ = null;
+ #$rightArea_ = null;
+ #$editorBtnsContainer_ = null;
+ #$boardSelect_ = null;
+ #$portSelect_ = null;
+ #$shadow_ = $(HTMLTemplate.get('html/nav/shadow.html').render());
+ #btns_ = [];
+ #rightDropdownMenuGroup_ = null;
+
+ constructor() {
+ super();
+ const navTemplate = HTMLTemplate.get('html/nav/nav.html');
+ this.setId(navTemplate.getId());
+ this.#$container_ = $(navTemplate.render({
+ more: Msg.Lang['nav.more']
+ }));
+ this.setContent(this.#$container_);
+ this.#$leftBtnContainer_ = this.#$container_.find('.left-btn-container');
+ this.#$leftBtnExtContainer_ = this.#$container_.find('.left-btn-ext-container');
+ this.#$rightBtnContainer_ = this.#$container_.find('.right-btn-container');
+ this.#$dropdownContainer_ = this.#$container_.find('.dropdown-container');
+ this.#$rightMenuContainer_ = this.#$container_.find('.right-menu-container');
+ this.#$rightArea_ = this.#$container_.find('.right-area');
+ this.#$editorBtnsContainer_ = this.#$container_.find('.editor-btn-container');
+ const boardSelectTemplate = HTMLTemplate.get('html/nav/board-selector-div.html');
+ this.#$boardSelect_ = $(boardSelectTemplate.render());
+ this.#$dropdownContainer_.append(this.#$boardSelect_);
+ this.#$boardSelect_.select2({
+ width: '150px',
+ minimumResultsForSearch: 10,
+ dropdownCssClass: `mixly-scrollbar mixly-${boardSelectTemplate.getId()}`,
+ dropdownAutoWidth: true,
+ placeholder: Msg.Lang['nav.selectBoard'],
+ language: Msg.nowLang
+ });
+ const portSelectTemplate = HTMLTemplate.get('html/nav/port-selector-div.html');
+ this.#$portSelect_ = $(portSelectTemplate.render());
+ this.#$dropdownContainer_.append(this.#$portSelect_);
+ this.#$portSelect_.select2({
+ width: '100px',
+ minimumResultsForSearch: Infinity,
+ dropdownCssClass: `mixly-scrollbar mixly-${portSelectTemplate.getId()}`,
+ dropdownAutoWidth: true,
+ placeholder: Msg.Lang['nav.selectPort']
+ });
+ const $merge = this.#$boardSelect_.add(this.#$portSelect_);
+ let count = 0;
+ $merge.on('select2:opening', (event) => {
+ count += 1;
+ $(document.body).append(this.#$shadow_);
+ });
+ $merge.on('select2:closing', () => {
+ count -= 1;
+ !count && this.#$shadow_.detach();
+ });
+ this.#$shadow_.click(() => $merge.select2('close'));
+ this.addEventsType(['changeBoard', 'changePort']);
+ this.#addEventsListener_();
+ this.#rightDropdownMenuGroup_ = new DropdownMenuGroup(this.#$rightMenuContainer_[0]);
+ Nav.add(this);
+ this.list = [];
+ }
+
+ onMounted() {
+ super.onMounted();
+ element.render('nav', 'nav-filter');
+ }
+
+ getEditorBtnsContainer() {
+ return this.#$editorBtnsContainer_;
+ }
+
+ getBoardSelector() {
+ return this.#$boardSelect_;
+ }
+
+ getPortSelector() {
+ return this.#$portSelect_;
+ }
+
+ getBoardName() {
+ return this.#$boardSelect_.find(':selected').text();
+ }
+
+ getBoardKey() {
+ return this.#$boardSelect_.val();
+ }
+
+ getPortName() {
+ return this.#$portSelect_.find(':selected').text();
+ }
+
+ getPortKey() {
+ return this.#$portSelect_.val();
+ }
+
+ /**
+ * @function 注册函数
+ * @param config 选项
+ * {
+ * icon: String,
+ * title: String,
+ * id: String | Array,
+ * displayText: String,
+ * preconditionFn: Function,
+ * callback: Function,
+ * scopeType: Nav.SCOPE_TYPE,
+ * weight: Number
+ * }
+ * @return {void}
+ **/
+ register(config) {
+ const { scopeType = Nav.ScopeType.LEFT } = config;
+ config = {
+ preconditionFn: () => true,
+ ...config
+ };
+ const {
+ id = '',
+ title = '',
+ icon = '',
+ displayText = ''
+ } = config;
+ switch (scopeType) {
+ case Nav.Scope.LEFT:
+ config.$moreBtn = $(HTMLTemplate.get('html/nav/nav-item.html').render({
+ mId: id,
+ icon,
+ text: displayText
+ }));
+ case Nav.Scope.CENTER:
+ config.$btn = $(HTMLTemplate.get('html/nav/nav-btn.html').render({
+ title,
+ mId: id,
+ icon,
+ text: displayText
+ }));
+ break;
+ }
+ this.#add_(config);
+ return config;
+ }
+
+ #add_(config) {
+ const {
+ scopeType = Nav.ScopeType.LEFT,
+ id = ''
+ } = config;
+ switch (scopeType) {
+ case Nav.Scope.LEFT:
+ if (id === 'home-btn') {
+ this.#btns_[config.id] = config;
+ } else {
+ this.#addLeftBtn_(config);
+ }
+ break;
+ case Nav.Scope.CENTER:
+ this.#addCenterBtn_(config);
+ break;
+ case Nav.Scope.RIGHT:
+ this.#addRightBtn_(config);
+ break;
+ }
+ }
+
+ /**
+ * @function 取消注册函数
+ * @param config 选项
+ * {
+ * id: String | Array,
+ * scopeType: Nav.SCOPE_TYPE
+ * }
+ * @return {void}
+ **/
+ unregister(config) {
+
+ }
+
+ #getElemWidth_(elem) {
+ const display = elem.css('display');
+ if (display !== 'none') {
+ return elem.outerWidth(true);
+ }
+ const visibility = elem.css('visibility');
+ const position = elem.css('position');
+ elem.css({
+ display: 'block',
+ visibility: 'hidden',
+ position: 'absolute'
+ });
+ const width = elem.outerWidth(true);
+ elem.css({ display, visibility, position });
+ return width;
+ }
+
+ #addEventsListener_() {
+ $(document).off('click', '.mixly-nav')
+ .on('click', '.mixly-nav', (event) => {
+ const mId = $(event.currentTarget).attr('m-id');
+ if (this.#btns_[mId]
+ && typeof this.#btns_[mId].callback === 'function') {
+ this.#btns_[mId].callback(this);
+ }
+ });
+
+ this.#$boardSelect_.on('select2:select', (event) => {
+ const { data } = event.params;
+ this.runEvent('changeBoard', data);
+ });
+
+ this.#$portSelect_.on('select2:select', (event) => {
+ const { data } = event.params;
+ this.runEvent('changePort', data);
+ $('#mixly-footer-port-div').css('display', 'inline-flex');
+ $('#mixly-footer-port').html(data.id);
+ });
+ }
+
+ #addLeftBtn_(config) {
+ const { id = '', weight = 0 } = config;
+ if (Object.keys(this.#btns_).includes(id)) {
+ this.#btns_[id].$btn.remove();
+ this.#btns_[id].$moreBtn.remove();
+ delete this.#btns_[id];
+ }
+ let $btn = null;
+ let $moreBtn = null;
+ const $btns = this.#$leftBtnContainer_.children('button');
+ for (let i = 0; $btns[i]; i++) {
+ const mId = $($btns[i]).attr('m-id');
+ if (!this.#btns_[mId]) {
+ continue;
+ }
+ if (weight < this.#btns_[mId].weight) {
+ $btn = this.#btns_[mId].$btn;
+ $moreBtn = this.#btns_[mId].$moreBtn;
+ break;
+ }
+ }
+ if ($btn) {
+ $btn.before(config.$btn);
+ $moreBtn.before(config.$moreBtn);
+ } else {
+ this.#$leftBtnContainer_.append(config.$btn);
+ this.#$leftBtnExtContainer_.append(config.$moreBtn);
+ }
+ config.width = this.#getElemWidth_(config.$btn);
+ this.#btns_[id] = config;
+ this.resize();
+ }
+
+ #addCenterBtn_(config) {
+ const { id = '', weight = 0 } = config;
+ let $btn = null;
+ const $btns = this.#$rightBtnContainer_.children('button');
+ for (let i = 0; $btns[i]; i++) {
+ const mId = $($btns[i]).attr('m-id');
+ if (!this.#btns_[mId]) {
+ continue;
+ }
+ if (weight < this.#btns_[mId].weight) {
+ $btn = this.#btns_[mId].$btn;
+ break;
+ }
+ }
+ if ($btn) {
+ $btn.before(config.$btn);
+ } else {
+ this.#$rightBtnContainer_.append(config.$btn);
+ }
+ config.width = this.#getElemWidth_(config.$btn);
+ this.#btns_[id] = config;
+ this.resize();
+ }
+
+ #addRightBtn_(config) {
+ const { preconditionFn } = config;
+ if (!preconditionFn()) {
+ return;
+ }
+ this.#rightDropdownMenuGroup_.add(config);
+ this.resize();
+ }
+
+ resize() {
+ super.resize();
+ this.#$boardSelect_.select2('close');
+ this.#$portSelect_.select2('close');
+ const navRightWidth = this.#getElemWidth_(this.#$rightArea_);
+ const navWidth = this.#getElemWidth_(this.#$container_);
+ const $btns = this.#$leftBtnContainer_.children('button');
+ let nowWidth = navRightWidth;
+ let showMoreBtnContainer = false;
+ for (let i = 0; $btns[i]; i++) {
+ const mId = $($btns[i]).attr('m-id');
+ if (mId === 'home-btn') {
+ continue;
+ }
+ const config = this.#btns_[mId];
+ let newWidth = nowWidth;
+ if (config) {
+ const { preconditionFn } = config;
+ if (!preconditionFn()) {
+ config.$btn.css('display', 'none');
+ config.$moreBtn.css('display', 'none');
+ continue;
+ }
+ newWidth += config.width;
+ } else {
+ newWidth += this.#getElemWidth_($($btns[i]));
+ continue;
+ }
+ if (navWidth < newWidth + this.#$editorBtnsContainer_.outerWidth(true) + 130) {
+ config.$btn.css('display', 'none');
+ config.$moreBtn.css('display', 'block');
+ showMoreBtnContainer = true;
+ } else {
+ config.$btn.css('display', 'block');
+ config.$moreBtn.css('display', 'none');
+ nowWidth = newWidth;
+ }
+ }
+ if (navWidth < nowWidth + this.#$editorBtnsContainer_.outerWidth(true) + 130) {
+ showMoreBtnContainer = false;
+ }
+ const parent = this.#$leftBtnExtContainer_.parent();
+ parent.css('display', showMoreBtnContainer? 'block' : 'none');
+ }
+}
+
+Mixly.Nav = Nav;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/page-base.js b/mixly/common/modules/mixly-modules/common/page-base.js
new file mode 100644
index 00000000..b3ae2a21
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/page-base.js
@@ -0,0 +1,159 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Component');
+goog.require('Mixly.Events');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Component');
+goog.provide('Mixly.PageBase');
+
+const {
+ Events,
+ Registry,
+ Component
+} = Mixly;
+
+class PageBase extends Component {
+ #pages_ = new Registry();
+ #$tab_ = null;
+ #$close_ = null;
+ #dirty_ = false;
+ #active_ = true;
+ #inited_ = false;
+
+ constructor() {
+ super();
+ this.addEventsType(['created', 'addDirty', 'removeDirty', 'active']);
+ }
+
+ init() {
+ this.#forward_('init');
+ this.#inited_ = true;
+ this.runEvent('created');
+ }
+
+ addPage($child, id, page) {
+ this.#pages_.register(id, page);
+ $child.append(page.getContent());
+ }
+
+ removePage(id) {
+ const page = this.getPage(id);
+ if (!page) {
+ return;
+ }
+ this.#pages_.unregister(id);
+ page.dispose();
+ }
+
+ getPage(id) {
+ return this.#pages_.getItem(id);
+ }
+
+ mountPage($child, id, page) {
+ if (!this.isMounted()) {
+ return;
+ }
+ this.addPage($child, id, page);
+ page.onMounted();
+ }
+
+ resize() {
+ super.resize();
+ this.#forward_('resize');
+ }
+
+ dispose() {
+ this.#forward_('dispose');
+ this.#pages_.reset();
+ this.#$close_ = null;
+ this.#$tab_ && this.#$tab_.remove();
+ this.#$tab_ = null;
+ super.dispose();
+ }
+
+ #forward_(type, ...args) {
+ this.#pages_.getAllItems().forEach((page) => {
+ if (typeof page[type] !== 'function') {
+ return;
+ }
+ page[type](...args);
+ });
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#forward_('onMounted');
+ this.setActive(true);
+ this.runEvent('active');
+ }
+
+ onUnmounted() {
+ super.onUnmounted();
+ this.#forward_('onUnmounted');
+ this.setActive(false);
+ }
+
+ setActive(status) {
+ this.#forward_('setActive', status);
+ this.#active_ = status;
+ }
+
+ setTab($tab) {
+ this.#$tab_ = $tab;
+ this.#$close_ = $tab.find('.chrome-tab-close');
+ }
+
+ hideCloseBtn() {
+ this.#$close_.css('display', 'none');
+ }
+
+ showCloseBtn() {
+ this.#$close_.css('display', 'block');
+ }
+
+ getTab() {
+ return this.#$tab_;
+ }
+
+ setMarkStatus(styleClass) {
+ this.#$close_.attr('class', `chrome-tab-close layui-badge-dot ${styleClass}`);
+ }
+
+ addDirty() {
+ this.#forward_('addDirty');
+ const $tab = this.getTab();
+ if (!$tab || this.isDirty()) {
+ return;
+ }
+ $tab.addClass('dirty');
+ this.runEvent('addDirty');
+ this.#dirty_ = true;
+ }
+
+ removeDirty() {
+ this.#forward_('removeDirty');
+ const $tab = this.getTab();
+ if (!$tab || !this.isDirty()) {
+ return;
+ }
+ this.runEvent('removeDirty');
+ $tab.removeClass('dirty');
+ this.#dirty_ = false;
+ }
+
+ isDirty() {
+ return this.#dirty_;
+ }
+
+ isInited() {
+ return this.#inited_;
+ }
+
+ isActive() {
+ return this.#active_;
+ }
+}
+
+Mixly.PageBase = PageBase;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/pages-manager.js b/mixly/common/modules/mixly-modules/common/pages-manager.js
new file mode 100644
index 00000000..aaecf27a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/pages-manager.js
@@ -0,0 +1,212 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.PagesTab');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Component');
+goog.provide('Mixly.PagesManager');
+
+const {
+ PagesTab,
+ Registry,
+ Component
+} = Mixly;
+
+class PagesManager extends Component {
+ #tabs_ = null;
+ #welcomePage_ = null;
+ #$editorContainer_ = null;
+ #$tabsContainer_ = null;
+ #$parentContainer_ = null;
+ #pagesRegistry_ = new Registry();
+ #page_ = 'welcome';
+ #activeId_ = null;
+ #typesRegistry_ = null;
+
+ /**
+ * config = {
+ * parentElem: element,
+ * managerContentElem: element,
+ * bodyElem: element,
+ * tabElem: element,
+ * tabContentElem: element,
+ * typesRegistry: Mixly.Registry
+ * }
+ **/
+ constructor(config) {
+ super();
+ this.#$parentContainer_ = $(config.parentElem);
+ const $content = $(config.managerContentElem);
+ this.#typesRegistry_ = config.typesRegistry;
+ this.#$tabsContainer_ = $(config.tabElem);
+ this.#$editorContainer_ = $(config.bodyElem);
+ this.#tabs_ = new PagesTab({
+ parentElem: config.tabElem,
+ contentElem: config.tabContentElem
+ });
+ $content.append(this.#$editorContainer_);
+ this.setContent($content);
+ this.mountOn(this.#$parentContainer_);
+ let PageType = this.#typesRegistry_.getItem('#welcome');
+ if (PageType) {
+ this.#welcomePage_ = new PageType();
+ this.#showWelcomePage_();
+ }
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ const pageTabs = this.getTabs();
+ // active Tab被改变时触发
+ pageTabs.bind('activeTabChange', (event) => {
+ const prevEditor = this.getActive();
+ const { tabEl } = event.detail;
+ const $tab = $(tabEl);
+ const id = $tab.attr('data-tab-id');
+ $tab.find('.chrome-tab-favicon').addClass('active');
+ const page = this.get(id);
+ this.#activeId_ = id;
+ if (prevEditor) {
+ const $prevTab = prevEditor.getTab();
+ $prevTab.find('.chrome-tab-favicon').removeClass('active');
+ prevEditor.unmount();
+ }
+ this.#$editorContainer_.empty();
+ this.#$editorContainer_.append(page.getContent());
+ page.onMounted();
+ page.resize();
+ });
+
+ // 添加新Tab时触发
+ pageTabs.bind('tabCreated', (event) => {
+ const { tabEl } = event.detail;
+ const id = $(tabEl).attr('data-tab-id');
+ const type = $(tabEl).attr('data-tab-type');
+ let PageType = this.#typesRegistry_.getItem(type);
+ if (!PageType) {
+ PageType = this.#typesRegistry_.getItem('#default');
+ }
+ let page = new PageType();
+ this.#pagesRegistry_.register(id, page);
+ page.setTab($(tabEl));
+ if (this.#welcomePage_
+ && this.#pagesRegistry_.length()
+ && this.#page_ === 'welcome') {
+ this.#hideWelcomePage_();
+ }
+ page.init();
+ });
+
+ // 移除已有Tab时触发
+ pageTabs.bind('tabDestroyed', (event) => {
+ const { tabEl } = event.detail;
+ const id = $(tabEl).attr('data-tab-id');
+ const page = this.get(id);
+ if (!page) {
+ return;
+ }
+ page.dispose();
+ this.#pagesRegistry_.unregister(id);
+ if (this.#welcomePage_
+ && !this.#pagesRegistry_.length()
+ && this.#page_ !== 'welcome') {
+ this.#showWelcomePage_();
+ }
+ });
+ }
+
+ #showWelcomePage_() {
+ this.unmount();
+ this.#welcomePage_.mountOn(this.#$parentContainer_);
+ this.#page_ = 'welcome';
+ }
+
+ #hideWelcomePage_() {
+ this.#welcomePage_.unmount();
+ this.mountOn(this.#$parentContainer_);
+ this.#page_ = 'editor';
+ }
+
+ resize() {
+ super.resize();
+ const page = this.getActive();
+ page && page.resize();
+ this.#tabs_.resize();
+ }
+
+ onMounted() {
+ super.onMounted();
+ const page = this.getActive();
+ page && page.onMounted();
+ this.#tabs_.onMounted();
+ }
+
+ onUnmounted() {
+ const page = this.getActive();
+ page && page.onUnmounted();
+ this.#tabs_.onUnmounted();
+ super.onUnmounted();
+ }
+
+ getActive() {
+ return this.get(this.#activeId_);
+ }
+
+ add(...args) {
+ if (args[0] && typeof args[0] === 'object') {
+ this.#tabs_.addTab(args[0]);
+ } else {
+ const [type, id, name, title, favicon] = args;
+ this.#tabs_.addTab({
+ type,
+ id,
+ name: name ?? id,
+ title: title ?? id,
+ favicon
+ });
+ }
+ }
+
+ remove(id) {
+ this.#tabs_.removeTab(id);
+ }
+
+ changeTo(id) {
+ if (!this.get(id)) {
+ return;
+ }
+ this.#tabs_.setCurrentTab(id);
+ }
+
+ get(id) {
+ return this.#pagesRegistry_.getItem(id);
+ }
+
+ keys() {
+ return this.#pagesRegistry_.keys();
+ }
+
+ length() {
+ return this.#pagesRegistry_.length();
+ }
+
+ getTabs() {
+ return this.#tabs_;
+ }
+
+ dispose() {
+ for (let id of this.keys()) {
+ this.remove(id);
+ }
+ this.#tabs_.dispose();
+ this.#welcomePage_ && this.#welcomePage_.dispose();
+ this.#tabs_ = null;
+ this.#welcomePage_ = null;
+ this.#$tabsContainer_ = null;
+ this.#$editorContainer_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.PagesManager = PagesManager;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/pages-tab.js b/mixly/common/modules/mixly-modules/common/pages-tab.js
new file mode 100644
index 00000000..c60af8f1
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/pages-tab.js
@@ -0,0 +1,164 @@
+goog.loadJs('common', () => {
+
+goog.require('_');
+goog.require('path');
+goog.require('ChromeTabs');
+goog.require('XScrollbar');
+goog.require('Sortable');
+goog.require('$.contextMenu');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.Config');
+goog.require('Mixly.Events');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Component');
+goog.provide('Mixly.PagesTab');
+
+const {
+ IdGenerator,
+ Config,
+ Events,
+ Registry,
+ Component
+} = Mixly;
+
+const { USER } = Config;
+
+class PagesTab extends Component {
+ #chromeTabs_ = null;
+ #scrollbar_ = null;
+ #tabsRegistry_ = null;
+ #sortable_ = null;
+
+ /**
+ * config = {
+ * parentElem: element,
+ * contentElem: element
+ * }
+ **/
+ constructor(config) {
+ super();
+ const $parentsContainer = $(config.parentElem);
+ const $content = $(config.contentElem);
+ this.$tabsContainer = $content.children('div');
+ this.#chromeTabs_ = new ChromeTabs();
+ this.#chromeTabs_.init(this.$tabsContainer[0]);
+ this.#scrollbar_ = new XScrollbar($content.find('.chrome-tabs-content')[0], {
+ onlyHorizontal: true,
+ thumbSize: 1.7,
+ thumbRadius: 0,
+ thumbBackground: USER.theme === 'dark'? '#b0b0b0' : '#5f5f5f'
+ });
+ this.#sortable_ = new Sortable(this.#chromeTabs_.tabContentEl, {
+ animation: 150,
+ ghostClass: 'blue-background-class',
+ direction: 'horizontal'
+ });
+ this.setContent($content);
+ this.mountOn($parentsContainer);
+ this.#addEventsListener_();
+ this.#tabsRegistry_ = new Registry();
+ this.addEventsType(['activeTabChange', 'tabCreated', 'tabDestroyed', 'tabCheckDestroy', 'tabBeforeDestroy']);
+ }
+
+ #addEventsListener_() {
+ const { $tabsContainer } = this;
+ const container = $tabsContainer[0];
+
+ this.#chromeTabs_.checkDestroy = (event) => {
+ const status = this.runEvent('tabCheckDestroy', event);
+ return _.sum(status) == status.length;
+ }
+
+ // active Tab被改变时触发
+ container.addEventListener('activeChange', (event) => {
+ this.runEvent('activeTabChange', event);
+ });
+
+ // 添加新Tab时触发
+ container.addEventListener('created', (event) => {
+ const { tabEl } = event.detail;
+ const tabId = $(tabEl).attr('data-tab-id');
+ this.#tabsRegistry_.register(tabId, tabEl);
+ this.runEvent('tabCreated', event);
+ setTimeout(() => {
+ this.#scrollbar_.update();
+ }, 500);
+ });
+
+ // 移除已有Tab之前触发
+ container.addEventListener('beforeDestroy', (event) => {
+ this.runEvent('tabBeforeDestroy', event);
+ });
+
+ // 移除已有Tab时触发
+ container.addEventListener('destroyed', (event) => {
+ const { tabEl } = event.detail;
+ const tabId = $(tabEl).attr('data-tab-id');
+ this.#tabsRegistry_.unregister(tabId);
+ this.runEvent('tabDestroyed', event);
+ setTimeout(() => {
+ this.#scrollbar_ && this.#scrollbar_.update();
+ }, 500);
+ });
+ }
+
+ addTab(tabProperties, others = {}) {
+ tabProperties = { ...tabProperties };
+ const { id } = tabProperties;
+ tabProperties.id = id ?? IdGenerator.generate();
+ let tab = this.#tabsRegistry_.getItem(tabProperties.id);
+ if (tab) {
+ this.updateTab(tabProperties.id, tabProperties);
+ this.setCurrentTab(tabProperties.id);
+ } else {
+ tab = this.#chromeTabs_.addTab(tabProperties, others);
+ }
+ let { left } = $(tab).position();
+ this.#scrollbar_.$container.scrollLeft = left;
+ this.#scrollbar_.update();
+ }
+
+ removeTab(id) {
+ const elem = this.#tabsRegistry_.getItem(id);
+ this.#chromeTabs_.removeTab(elem);
+ }
+
+ setCurrentTab(id) {
+ const elem = this.#tabsRegistry_.getItem(id);
+ this.#chromeTabs_.setCurrentTab(elem);
+ }
+
+ updateTab(id, tabProperties) {
+ const elem = this.#tabsRegistry_.getItem(id);
+ const newId = tabProperties.id || id;
+ this.#chromeTabs_.updateTab(elem, tabProperties);
+ if (id !== newId) {
+ this.#tabsRegistry_.unregister(id);
+ this.#tabsRegistry_.register(id, elem);
+ }
+ }
+
+ getScrollbar() {
+ return this.#scrollbar_;
+ }
+
+ getSortable() {
+ return this.#sortable_;
+ }
+
+ dispose() {
+ this.#chromeTabs_.dispose();
+ this.#chromeTabs_ = null;
+ this.#tabsRegistry_.reset();
+ this.#tabsRegistry_ = null;
+ this.#sortable_.destroy();
+ this.#sortable_ = null;
+ this.#scrollbar_.destroy();
+ this.#scrollbar_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.PagesTab = PagesTab;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/profile.js b/mixly/common/modules/mixly-modules/common/profile.js
new file mode 100644
index 00000000..d36ea646
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/profile.js
@@ -0,0 +1,57 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.Profile');
+
+const { Profile } = Mixly;
+
+Profile.parse = function (range) {
+ let pinList = [];
+ for (let i of range) {
+ const pinInfo = i.split('-');
+ switch (pinInfo.length) {
+ case 1:
+ const pinNumStr = pinInfo[0].toString();
+ if (!isNaN(pinNumStr)) {
+ const pinNum = parseInt(pinNumStr);
+ pinList.push(pinNum);
+ }
+ break;
+ case 2:
+ const pinNumStr0 = pinInfo[0].toString(),
+ pinNumStr1 = pinInfo[1].toString();
+ if (!isNaN(pinNumStr0) && !isNaN(pinNumStr1)) {
+ let pinNum0 = parseInt(pinNumStr0);
+ let pinNum1 = parseInt(pinNumStr1);
+ if (pinNum0 < 0 || pinNum1 < 0) break;
+ if (pinNum0 > pinNum1) {
+ [ pinNum0, pinNum1 ] = [ pinNum1, pinNum0 ];
+ }
+ for (let j = pinNum0; j <= pinNum1; j++) {
+ if (!pinList.includes(j)) {
+ pinList.push(j);
+ }
+ }
+ }
+ break;
+ }
+ }
+ return pinList;
+};
+
+Profile.generate = function (pinMap, add1 = '', add2 = '') {
+ const getPins = (list) => {
+ let pins = [];
+ for (let i of list) {
+ const pin = [ add1 + i, add2 + i ];
+ pins.push(pin);
+ }
+ return pins;
+ }
+ const pinList = this.parse(pinMap);
+ return getPins(pinList);
+};
+
+window.profile = Profile;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/registry.js b/mixly/common/modules/mixly-modules/common/registry.js
new file mode 100644
index 00000000..eaf00904
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/registry.js
@@ -0,0 +1,71 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Debug');
+goog.provide('Mixly.Registry');
+
+const { Debug } = Mixly;
+
+class Registry {
+ #registry_ = new Map();
+
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this.#registry_.clear();
+ }
+
+ validate(keys) {
+ if (!(keys instanceof Array)) {
+ keys = [keys];
+ }
+ return keys;
+ }
+
+ register(keys, value) {
+ keys = this.validate(keys);
+ for (let key of keys) {
+ if (this.#registry_.has(key)) {
+ Debug.warn(`${key}已存在,不可重复注册`);
+ continue;
+ }
+ this.#registry_.set(key, value);
+ }
+ }
+
+ unregister(keys) {
+ keys = this.validate(keys);
+ for (let key of keys) {
+ if (!this.#registry_.has(key)) {
+ Debug.warn(`${key}不存在,无需取消注册`);
+ continue;
+ }
+ this.#registry_.delete(key);
+ }
+ }
+
+ length() {
+ return this.#registry_.size;
+ }
+
+ hasKey(key) {
+ return this.#registry_.has(key);
+ }
+
+ keys() {
+ return [...this.#registry_.keys()];
+ }
+
+ getItem(key) {
+ return this.#registry_.get(key) ?? null;
+ }
+
+ getAllItems() {
+ return this.#registry_;
+ }
+}
+
+Mixly.Registry = Registry;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/regression.js b/mixly/common/modules/mixly-modules/common/regression.js
new file mode 100644
index 00000000..6cbc6bd6
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/regression.js
@@ -0,0 +1,84 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.Regression');
+
+class Regression {
+ constructor() {
+ this.x = [];
+ this.y = [];
+ this.n = 0;
+ this.beta = 1;
+ this.alpha = 0;
+ }
+ /**
+ * 适配
+ * @param {Array} x
+ * @param {Array} y
+ */
+ fit(x, y) {
+ this.x = x;
+ this.y = y;
+ this.n = x.length;
+ this.beta = this.getBeta();
+ this.alpha = this.getAlpha(this.beta);
+ }
+ /**
+ * 预测
+ * @param {Array} x 数据集
+ * @returns {Array} 预测结果数据集
+ */
+ predict(x) {
+ if (!Array.isArray(x)) x = [x];
+ const y = [];
+ for (const num of x) {
+ y.push(this.alpha + num * this.beta);
+ }
+ return y;
+ }
+ /**
+ * 获取beta
+ * @returns {Number} 斜率
+ */
+ getBeta() {
+ const beta = (this.sum(this.x, (v, k) => v * this.y[k]) * this.n
+ - this.sum(this.x) * this.sum(this.y)) /
+ (this.sum(this.x, (v) => v * v) * this.n
+ - Math.pow(this.sum(this.x), 2));
+ return beta;
+ }
+ /**
+ * 获取alpha
+ * @param {Number} beta 斜率
+ * @returns {Number} 偏移量
+ */
+ getAlpha(beta) {
+ return this.avg(this.y) - this.avg(this.x) * beta;
+ }
+ /**
+ * 求和(Σ)
+ * @param {Array} arr 数字集合
+ * @param {Function} fun 每个集合的操作方法
+ */
+ sum(arr, fun = (v, k) => v) {
+ let s = 0;
+ const operate = fun;
+ for (let i = 0; i < arr.length; i++) {
+ let num = arr[i];
+ s += operate(num, i);
+ }
+ return s;
+ }
+ /**
+ * 均值
+ * @param {Array} arr 数字集合
+ */
+ avg(arr) {
+ const s = this.sum(arr);
+ return s / arr.length;
+ }
+}
+
+Mixly.Regression = Regression;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/script-loader.js b/mixly/common/modules/mixly-modules/common/script-loader.js
new file mode 100644
index 00000000..363f841a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/script-loader.js
@@ -0,0 +1,65 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Env');
+goog.provide('Mixly.ScriptLoader');
+
+
+const { Env, ScriptLoader } = Mixly;
+
+/**
+ * 加载 script 文件
+ * @param src
+ */
+ScriptLoader.loadScript = function (src) {
+ let addSign = true;
+ let scripts = document.getElementsByTagName('script');
+ for (let i = 0; i < scripts.length; i++) {
+ if (scripts[i] && scripts[i].src && scripts[i].src.indexOf(src) !== -1) {
+ addSign = false;
+ }
+ }
+ if (addSign) {
+ let $script = document.createElement('script');
+ $script.setAttribute('type', 'text/javascript');
+ $script.setAttribute('src', src);
+ //$script.setAttribute('async', '');
+ document.getElementsByTagName('head').item(0).appendChild($script);
+ }
+}
+
+/**
+ * 删除 script 文件
+ * @param src
+ */
+ScriptLoader.removeScript = function (src) {
+ let scripts = document.getElementsByTagName('script');
+ if (src.indexOf('../') !== -1) {
+ src = src.substring(src.lastIndexOf('../') + 3, src.length);
+ }
+ for (let i = 0; i < scripts.length; i++) {
+ if (scripts[i] && scripts[i].src && scripts[i].src.indexOf(src) !== -1) {
+ scripts[i].parentNode.removeChild(scripts[i]);
+ }
+ }
+}
+
+ScriptLoader.loadLangJs = function (oldLang, newLang, doFunc) {
+ let scripts = document.querySelectorAll('script');
+ let newLangPathArr = [];
+ for (let i = 0; i < scripts.length; i++) {
+ if (scripts[i] && scripts[i].src && scripts[i].src.indexOf(oldLang + '.js') !== -1) {
+ let oldLangPath = scripts[i].src;
+ let newLangPath = oldLangPath.replace(oldLang + '.js', newLang + '.js');
+ scripts[i].parentNode.removeChild(scripts[i]);
+ newLangPathArr.push(newLangPath);
+ }
+ }
+ for (let i = 0; i < Env.thirdPartyJS.length; i++) {
+ Env.thirdPartyJS[i] = Env.thirdPartyJS[i].replace(oldLang + '.js', newLang + '.js');
+ }
+ LazyLoad.js(newLangPathArr, function () {
+ doFunc();
+ });
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/serial.js b/mixly/common/modules/mixly-modules/common/serial.js
new file mode 100644
index 00000000..9a7df1d2
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/serial.js
@@ -0,0 +1,398 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Config');
+goog.require('Mixly.Events');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Nav');
+goog.require('Mixly.Msg');
+goog.provide('Mixly.Serial');
+
+const {
+ Config,
+ Events,
+ Registry,
+ Nav,
+ Msg
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+
+class Serial {
+ static {
+ this.portsName = [];
+ this.portToNameRegistry = new Registry();
+ this.nameToPortRegistry = new Registry();
+ this.DEFAULT_CONFIG = {
+ ctrlCBtn: false,
+ ctrlDBtn: false,
+ baudRates: 9600,
+ yMax: 100,
+ yMin: 0,
+ pointNum: 100,
+ rts: true,
+ dtr: true
+ };
+ this.AVAILABEL_BAUDS = [
+ 300, 600, 750, 1200, 2400, 4800, 9600, 19200, 31250, 38400, 57600,
+ 74880, 115200, 230400, 250000, 460800, 500000, 921600, 1000000, 2000000,
+ ];
+
+ this.getSelectedPortName = function () {
+ return Nav.getMain().getPortSelector().val();
+ }
+
+ this.getCurrentPortsName = function () {
+ return this.portsName;
+ }
+
+ this.refreshPorts = function () {
+ let portsName = [];
+ for (let name of Serial.nameToPortRegistry.keys()) {
+ portsName.push({ name });
+ }
+ Serial.renderSelectBox(portsName);
+ }
+
+ this.getConfig = function () {
+ let config = SELECTED_BOARD?.serial ?? {};
+ return {
+ ...this.DEFAULT_CONFIG,
+ ...config
+ };
+ }
+
+ this.portIsLegal = function (port) {
+ return this.portsName.includes(port);
+ }
+
+ /**
+ * @function 重新渲染串口下拉框
+ * @param {array} 当前可用的所有串口
+ * @return {void}
+ **/
+ this.renderSelectBox = function (ports) {
+ const $portSelector = Nav.getMain().getPortSelector();
+ const selectedPort = $portSelector.val();
+ $portSelector.empty();
+ if (ports.length) {
+ let portsName = [];
+ ports.map(port => {
+ portsName.push(port.name);
+ });
+ portsName.sort();
+ this.portsName = portsName;
+ portsName.map(name => {
+ let newOption = new Option(name, name);
+ if (selectedPort === name) {
+ newOption.setAttribute('selected', true);
+ }
+ $portSelector.append(newOption);
+ });
+ } else {
+ this.portsName = [];
+ let newOption = new Option(Msg.Lang['statusbar.serial.noPort']);
+ newOption.setAttribute('disabled', true);
+ $portSelector.append(newOption);
+ }
+ $portSelector.select2('close');
+ $portSelector.trigger('change');
+ let footerStatus = ports.length ? 'inline-flex' : 'none';
+ $('#mixly-footer-port-div').css('display', footerStatus);
+ $('#mixly-footer-port').html($portSelector.val());
+ }
+ }
+
+ #buffer_ = [];
+ #bufferLength_ = 0;
+ #encoder_ = new TextEncoder('utf8');
+ #decoder_ = new TextDecoder('utf8');
+ #baud_ = 0;
+ #dtr_ = false;
+ #rts_ = false;
+ #vid_ = 0x0000;
+ #pid_ = 0x0000;
+ #isOpened_ = false;
+ #port_ = '';
+ #special_ = [];
+ #specialLength_ = 0;
+ #events_ = new Events(['onOpen', 'onClose', 'onError', 'onBuffer', 'onString', 'onByte', 'onChar']);
+ constructor(port) {
+ this.#port_ = port;
+ this.resetBuffer();
+ }
+
+ decodeBuffer(buffer) {
+ let output = '';
+ for (let i = 0; i < buffer.length; i++) {
+ output += this.decodeByte(buffer[i]);
+ }
+ return output;
+ }
+
+ /* UTF-8编码方式
+ * ------------------------------------------------------------
+ * |1字节 0xxxxxxx |
+ * |2字节 110xxxxx 10xxxxxx |
+ * |3字节 1110xxxx 10xxxxxx 10xxxxxx |
+ * |4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx|
+ * ------------------------------------------------------------
+ **/
+ decodeByte(byte) {
+ let output = '';
+ if (byte !== 0x5F && this.#special_.length && !this.#specialLength_) {
+ const str = this.#special_.join('');
+ try {
+ output += decodeURIComponent(str.replace(/_([0-9a-fA-F]{2})/gm, '%$1'));
+ } catch (_) {
+ output += str;
+ }
+ this.#special_ = [];
+ }
+ if ((byte & 0x80) === 0x00) {
+ // 1字节
+ this.#buffer_ = [];
+ this.#bufferLength_ = 0;
+ if (byte === 0x5F) {
+ // 如果当前字节是 "_"
+ this.#specialLength_ = 2;
+ this.#special_.push(String.fromCharCode(byte));
+ } else if (byte !== 0x0D) {
+ // 如果当前字节不是 "\r"
+ if (this.#specialLength_) {
+ this.#specialLength_--;
+ this.#special_.push(String.fromCharCode(byte));
+ } else {
+ output += String.fromCharCode(byte);
+ }
+ }
+ } else if ((byte & 0xC0) === 0x80) {
+ /*
+ * 2字节以上的中间字节,10xxxxxx
+ * 如果没有起始头,则丢弃这个字节
+ * 如果不是2字节及以上的起始头,则丢弃这个字节
+ **/
+ if (!this.#buffer_.length || this.#bufferLength_ < 2) {
+ return output;
+ }
+ this.#buffer_.push(byte);
+ if (this.#bufferLength_ === this.#buffer_.length) {
+ output += this.decode(new Uint8Array(this.#buffer_));
+ this.#buffer_ = [];
+ }
+ } else {
+ // 2字节以上的起始头
+ if (this.#buffer_.length) {
+ this.#buffer_ = [];
+ }
+ this.#bufferLength_ = this.#getBufferLength_(byte);
+ this.#buffer_.push(byte);
+ }
+ return output;
+ }
+
+ #getBufferLength_(byte) {
+ let len = 1;
+ if ((byte & 0xFC) === 0xFC) {
+ len = 6;
+ } else if ((byte & 0xF8) === 0xF8) {
+ len = 5;
+ } else if ((byte & 0xF0) === 0xF0) {
+ len = 4;
+ } else if ((byte & 0xE0) === 0xE0) {
+ len = 3;
+ } else if ((byte & 0xC0) === 0xC0) {
+ len = 2;
+ }
+ return len;
+ }
+
+ resetBuffer() {
+ this.#buffer_ = [];
+ this.#bufferLength_ = 0;
+ }
+
+ async open(baud) {
+ this.#isOpened_ = true;
+ }
+
+ async close() {
+ this.#isOpened_ = false;
+ this.#baud_ = 0;
+ }
+
+ async toggle() {
+ if (this.isOpened()) {
+ return this.close();
+ } else {
+ return this.open();
+ }
+ }
+
+ baudRateIsLegal(baud) {
+ return Serial.AVAILABEL_BAUDS.includes(baud);
+ }
+
+ async setBaudRate(baud) {
+ this.#baud_ = baud;
+ }
+
+ async setDTR(dtr) {
+ this.#dtr_ = dtr;
+ }
+
+ async setRTS(rts) {
+ this.#rts_ = rts;
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ this.#dtr_ = dtr;
+ this.#rts_ = rts;
+ }
+
+ async setVID(vid) {
+ this.#vid_ = vid;
+ }
+
+ async setPID(pid) {
+ this.#pid_ = pid;
+ }
+
+ async setVIDAndPID(vid, pid) {
+ this.#vid_ = vid;
+ this.#pid_ = pid;
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ getBaudRate() {
+ return this.#baud_ || Serial.getConfig().baudRates;
+ }
+
+ getRawBaudRate() {
+ return this.#baud_;
+ }
+
+ getDTR() {
+ return this.#dtr_;
+ }
+
+ getRTS() {
+ return this.#rts_;
+ }
+
+ getVID() {
+ return this.#vid_;
+ }
+
+ getPID() {
+ return this.#pid_;
+ }
+
+ async sendString(str) {}
+
+ async sendBuffer(buffer) {}
+
+ async interrupt() {
+ return this.sendBuffer([3]);
+ }
+
+ async reset() {
+ return this.sendBuffer([4]);
+ }
+
+ onBuffer(buffer) {
+ this.#events_.run('onBuffer', buffer);
+ }
+
+ onString(string) {
+ this.#events_.run('onString', string);
+ }
+
+ onByte(byte) {
+ this.#events_.run('onByte', byte);
+ }
+
+ onChar(char) {
+ this.#events_.run('onChar', char);
+ }
+
+ onOpen() {
+ this.#isOpened_ = true;
+ this.#events_.run('onOpen');
+ }
+
+ onClose(code) {
+ this.#isOpened_ = false;
+ this.#events_.run('onClose', code);
+ }
+
+ onError(error) {
+ this.#events_.run('onError', error);
+ }
+
+ isOpened() {
+ return this.#isOpened_;
+ }
+
+ encode(str) {
+ return this.#encoder_.encode(str);
+ }
+
+ decode(uint8Array) {
+ return this.#decoder_.decode(uint8Array);
+ }
+
+ async config(info) {
+ return Promise.all([
+ this.setBaudRate(info.baud),
+ this.setDTRAndRTS(info.dtr, info.rts)
+ ]);
+ }
+
+ async sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ async dispose() {
+ this.#events_.reset();
+ if (this.isOpened()) {
+ await this.close();
+ }
+ this.#events_ = null;
+ this.#encoder_ = null;
+ this.#decoder_ = null;
+ }
+
+ bind(type, func) {
+ return this.#events_.bind(type, func);
+ }
+
+ unbind(id) {
+ this.#events_.unbind(id);
+ }
+
+ addEventsType(eventsType) {
+ this.#events_.addType(eventsType);
+ }
+
+ runEvent(eventsType, ...args) {
+ return this.#events_.run(eventsType, ...args);
+ }
+
+ offEvent(eventsType) {
+ this.#events_.off(eventsType);
+ }
+
+ resetEvent() {
+ this.#events_.reset();
+ }
+}
+
+Mixly.Serial = Serial;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/sidebar-libs.js b/mixly/common/modules/mixly-modules/common/sidebar-libs.js
new file mode 100644
index 00000000..35f3e799
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/sidebar-libs.js
@@ -0,0 +1,57 @@
+goog.loadJs('common', () => {
+
+goog.require('XScrollbar');
+goog.require('Mixly.Env');
+goog.require('Mixly.Config');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Debug');
+goog.require('Mixly.PageBase');
+goog.provide('Mixly.SideBarLibs');
+
+const {
+ Env,
+ Config,
+ HTMLTemplate,
+ Debug,
+ PageBase
+} = Mixly;
+
+const { USER } = Config;
+
+
+class SideBarLibs extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/sidebar/sidebar-libs.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/sidebar-libs.html')))
+ );
+ }
+
+ #scrollbar_ = null;
+
+ constructor() {
+ super();
+ const $content = $(HTMLTemplate.get('html/sidebar/sidebar-libs.html').render());
+ this.#scrollbar_ = new XScrollbar($content[0], {
+ onlyHorizontal: false,
+ thumbSize: '4px',
+ thumbRadius: '1px',
+ thumbBackground: USER.theme === 'dark'? '#b0b0b0' : '#5f5f5f'
+ });
+ this.setContent($content);
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ }
+
+ dispose() {
+ this.#scrollbar_.destroy();
+ super.dispose();
+ }
+}
+
+Mixly.SideBarLibs = SideBarLibs;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/sidebar-local-storage.js b/mixly/common/modules/mixly-modules/common/sidebar-local-storage.js
new file mode 100644
index 00000000..5f9ab2b7
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/sidebar-local-storage.js
@@ -0,0 +1,126 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mprogress');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Menu');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.Electron.FileTree');
+goog.require('Mixly.Web.FileTree');
+goog.require('Mixly.Electron.FS');
+goog.require('Mixly.Web.FS');
+goog.provide('Mixly.SideBarLocalStorage');
+
+const {
+ IdGenerator,
+ XML,
+ Env,
+ HTMLTemplate,
+ Debug,
+ Menu,
+ PageBase,
+ Electron = {},
+ Web = {}
+} = Mixly;
+
+const {
+ FileTree,
+ FS
+} = goog.isElectron? Electron : Web;
+
+class SideBarLocalStorage extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/sidebar/sidebar-local-storage-open-folder.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/sidebar-local-storage-open-folder.html')))
+ );
+ }
+
+ #$openFolderContent_ = null;
+ #fileTree_ = null;
+ #contextMenu_ = null;
+ #folderPath_ = null;
+
+ constructor() {
+ super();
+ const $openFolderContent = $(HTMLTemplate.get('html/sidebar/sidebar-local-storage-open-folder.html').render());
+ this.#$openFolderContent_ = $openFolderContent;
+ this.setContent($openFolderContent);
+ this.#fileTree_ = new FileTree();
+ this.#addEventsListener_();
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ }
+
+ #addEventsListener_() {
+ this.#$openFolderContent_.find('button').click(() => {
+ this.showDirectoryPicker();
+ });
+
+ const contextMenu = this.#fileTree_.getContextMenu();
+ const menu = contextMenu.getItem('menu');
+
+ menu.add({
+ weight: 10,
+ type: 'open_new_folder',
+ preconditionFn: ($trigger) => {
+ let type = $trigger.attr('type');
+ return ['root'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem('打开新文件夹', ''),
+ callback: () => {
+ this.showDirectoryPicker();
+ }
+ }
+ });
+ }
+
+ showDirectoryPicker() {
+ FS.showDirectoryPicker()
+ .then((folderPath) => {
+ if (!folderPath) {
+ return;
+ }
+ this.setFolderPath(folderPath);
+ this.setContent(this.#fileTree_.getContent());
+ this.#$openFolderContent_.remove();
+ })
+ .catch(Debug.error);
+ }
+
+ getFileTree() {
+ return this.#fileTree_;
+ }
+
+ resize() {
+ super.resize();
+ this.#fileTree_ && this.#fileTree_.resize();
+ }
+
+ dispose() {
+ this.#fileTree_.dispose();
+ }
+
+ setFolderPath(folderPath) {
+ if (goog.isElectron) {
+ this.#fileTree_.setFolderPath(folderPath);
+ } else {
+ this.#fileTree_.setFolderPath('/');
+ }
+ this.#folderPath_ = this.#fileTree_.getFolderPath();
+ this.#fileTree_.openRootFolder();
+ }
+}
+
+Mixly.SideBarLocalStorage = SideBarLocalStorage;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/sidebars-manager.js b/mixly/common/modules/mixly-modules/common/sidebars-manager.js
new file mode 100644
index 00000000..3253cdf9
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/sidebars-manager.js
@@ -0,0 +1,144 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Events');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.SideBarLocalStorage');
+goog.require('Mixly.SideBarLibs');
+goog.require('Mixly.PagesManager');
+goog.require('Mixly.IdGenerator');
+goog.provide('Mixly.SideBarsManager');
+goog.provide('Mixly.LeftSideBarsManager');
+goog.provide('Mixly.RightSideBarsManager');
+
+const {
+ Env,
+ Msg,
+ Registry,
+ Events,
+ HTMLTemplate,
+ SideBarLocalStorage,
+ SideBarLibs,
+ PagesManager,
+ IdGenerator,
+ Boards
+} = Mixly;
+
+class SideBarsManager extends PagesManager {
+ static {
+ HTMLTemplate.add(
+ 'html/sidebar/left-sidebars-manager.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/left-sidebars-manager.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/sidebar/left-sidebars-tab.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/left-sidebars-tab.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/sidebar/right-sidebars-manager.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/right-sidebars-manager.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/sidebar/right-sidebars-tab.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/sidebar/right-sidebars-tab.html')))
+ );
+
+ this.typesRegistry = new Registry();
+ this.managersRegistry = new Registry();
+ this.typesRegistry.register(['#default', 'local_storage'], SideBarLocalStorage);
+ this.typesRegistry.register(['libs'], SideBarLibs);
+ this.Align = {
+ LEFT: 0,
+ RIGHT: 1,
+ 0: 'LEFT',
+ 1: 'RIGHT'
+ };
+
+ this.getMain = function() {
+ if (!this.managersRegistry.length()) {
+ return null;
+ }
+ const key = this.managersRegistry.keys()[0];
+ return this.managersRegistry.getItem(key);
+ }
+
+ this.add = function(manager) {
+ this.managersRegistry.register(manager.id, manager);
+ }
+
+ this.remove = function(manager) {
+ this.managersRegistry.unregister(manager.id);
+ }
+ }
+
+ #shown_ = false;
+
+ constructor(element, align = SideBarsManager.Align.LEFT) {
+ let managerHTMLTemplate = '', tabHTMLTemplate = '';
+ if (align === SideBarsManager.Align.RIGHT) {
+ managerHTMLTemplate = HTMLTemplate.get('html/sidebar/right-sidebars-manager.html');
+ tabHTMLTemplate = HTMLTemplate.get('html/sidebar/right-sidebars-tab.html');
+ } else {
+ managerHTMLTemplate = HTMLTemplate.get('html/sidebar/left-sidebars-manager.html');
+ tabHTMLTemplate = HTMLTemplate.get('html/sidebar/left-sidebars-tab.html');
+ }
+ const $manager = $(managerHTMLTemplate.render());
+ const $tab = $(tabHTMLTemplate.render());
+ super({
+ parentElem: element,
+ managerContentElem: $manager[0],
+ bodyElem: $manager.find('.body')[0],
+ tabElem: $manager.find('.tabs')[0],
+ tabContentElem: $tab[0],
+ typesRegistry: SideBarsManager.typesRegistry
+ });
+ this.id = IdGenerator.generate();
+ this.addEventsType(['show', 'hide']);
+ SideBarsManager.add(this);
+ }
+
+ show() {
+ this.runEvent('show');
+ }
+
+ hide() {
+ this.runEvent('hide');
+ }
+
+ toggle() {
+ this.isShown() ? this.hide() : this.show();
+ }
+
+ isShown() {
+ return this.#shown_;
+ }
+
+ dispose() {
+ SideBarsManager.remove(this);
+ super.dispose();
+ }
+}
+
+class LeftSideBarsManager extends SideBarsManager {
+ constructor(element) {
+ super(element, SideBarsManager.Align.LEFT);
+ }
+}
+
+class RightSideBarsManager extends SideBarsManager {
+ constructor(element) {
+ super(element, SideBarsManager.Align.RIGHT);
+ }
+}
+
+Mixly.SideBarsManager = SideBarsManager;
+Mixly.LeftSideBarsManager = LeftSideBarsManager;
+Mixly.RightSideBarsManager = RightSideBarsManager;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/socket.js b/mixly/common/modules/mixly-modules/common/socket.js
new file mode 100644
index 00000000..2d45deab
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/socket.js
@@ -0,0 +1,91 @@
+goog.loadJs('common', () => {
+
+goog.require('io');
+goog.require('Mixly');
+goog.provide('Mixly.Socket');
+
+
+class Socket {
+ #socket_ = null;
+ constructor(path, option) {
+ this.#socket_ = io(path, option);
+ }
+
+ #detectStatus_(status, callback) {
+ window.setTimeout(() => {
+ if (status.finished) {
+ return;
+ }
+ if (this.isConnected()) {
+ this.#detectStatus_(status, callback);
+ } else {
+ callback({
+ error: 'socket is not connected'
+ });
+ status.finished = true;
+ }
+ }, 1000);
+ }
+
+ emit(eventName, ...args) {
+ const callback = args.pop();
+ if (this.isConnected()) {
+ let emitStatus = {
+ finished: false
+ };
+ let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
+ if (emitStatus.finished) {
+ return;
+ }
+ emitStatus.finished = true;
+ callback(...callbackArgs);
+ });
+ this.#detectStatus_(emitStatus, callback);
+ return status;
+ } else {
+ callback({
+ error: 'socket is not connected'
+ });
+ return false;
+ }
+ }
+
+ async emitAsync(eventName, ...args) {
+ return new Promise((resolve, reject) => {
+ if (this.isConnected()) {
+ const callback = (...callbackArgs) => {
+ if (callbackArgs[0].error) {
+ reject(callbackArgs[0].error);
+ return;
+ }
+ resolve(...callbackArgs);
+ }
+ let emitStatus = {
+ finished: false
+ };
+ let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
+ if (emitStatus.finished) {
+ return;
+ }
+ emitStatus.finished = true;
+ callback(...callbackArgs);
+ });
+ this.#detectStatus_(emitStatus, callback);
+ } else {
+ reject('socket is not connected');
+ }
+ })
+ }
+
+ getSocket() {
+ return this.#socket_;
+ }
+
+ isConnected() {
+ return this.#socket_?.connected;
+ }
+}
+
+Mixly.Socket = Socket;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-ampy.js b/mixly/common/modules/mixly-modules/common/statusbar-ampy.js
new file mode 100644
index 00000000..75aadbfc
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-ampy.js
@@ -0,0 +1,596 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.DragV');
+goog.require('Mixly.StatusBar');
+goog.require('Mixly.Serial');
+goog.require('Mixly.Menu');
+goog.require('Mixly.AmpyFileTree');
+goog.provide('Mixly.StatusBarAmpy');
+
+const {
+ PageBase,
+ Env,
+ Msg,
+ Debug,
+ HTMLTemplate,
+ DragV,
+ StatusBar,
+ Serial,
+ Menu,
+ AmpyFileTree
+} = Mixly;
+
+const { layer } = layui;
+
+
+class StatusBarAmpy extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-ampy.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-ampy.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-ampy-open-fs.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-ampy-open-fs.html')))
+ );
+
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-ampy-editor-empty.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-ampy-editor-empty.html')))
+ );
+ }
+
+ #$fileTree_ = null;
+ #$editor_ = null;
+ #$openFS_ = null;
+ #$editorEmpty_ = null;
+ #editor_ = null;
+ #fileTree_ = null;
+ #drag_ = null;
+ #fileTreeShown_ = false;
+ #editorShown_ = false;
+ #changed_ = false;
+
+ constructor() {
+ super();
+ const $content = $(HTMLTemplate.get('html/statusbar/statusbar-ampy.html').render());
+ this.setContent($content);
+ this.#fileTree_ = new AmpyFileTree();
+ this.#$fileTree_ = $content.children('.file-tree');
+ this.#$openFS_ = $(HTMLTemplate.get('html/statusbar/statusbar-ampy-open-fs.html').render({
+ loadBoardFS: Msg.Lang['statusbar.ampy.loadBoardFS']
+ }));
+ this.#$fileTree_.append(this.#$openFS_);
+ this.#editor_ = new StatusBar();
+ this.#$editor_ = $content.children('.editor');
+ this.#$editorEmpty_ = $(HTMLTemplate.get('html/statusbar/statusbar-ampy-editor-empty.html').render());
+ this.#$editor_.append(this.#$editorEmpty_);
+ }
+
+ #addEventsListener_() {
+ this.#drag_ = new DragV(this.getContent()[0], {
+ min: '150px',
+ startSize: '15%',
+ full: [false, false]
+ });
+
+ this.#drag_.bind('sizeChanged', () => {
+ this.resize();
+ });
+
+ this.#$openFS_.children('button').click(() => {
+ this.openFS();
+ });
+
+ this.#fileTree_.bind('beforeSelectLeaf', (selected) => {
+ const filePath = selected[0].id;
+ const mode = this.#editor_.getFileMode(path.extname(filePath));
+ if (!mode) {
+ layer.msg(Msg.Lang['statusbar.ampy.cannotEdit'], { time: 1000 });
+ return false;
+ }
+ this.#editor_.setMode(mode);
+ return true;
+ });
+
+ this.#fileTree_.bind('afterSelectLeaf', async (selected) => {
+ const filePath = selected[0].id;
+ this.#fileTree_.showProgress();
+ const fs = this.#fileTree_.getFS();
+ const [error, result] = await fs.readFile(filePath);
+ if (error) {
+ this.hideEditor();
+ this.#fileTree_.deselectAll();
+ } else {
+ this.showEditor();
+ this.#editor_.setValue(result);
+ this.#editor_.scrollToTop();
+ this.#editor_.focus();
+ this.setStatus(false);
+ }
+ this.#fileTree_.hideProgress();
+ });
+
+ this.#fileTree_.bind('afterCreateNode', (folderPath) => {
+ this.#fileTree_.refreshFolder(folderPath);
+ });
+
+ this.#fileTree_.bind('afterDeleteNode', (folderPath) => {
+ this.#fileTree_.refreshFolder(folderPath);
+ });
+
+ this.#fileTree_.bind('afterRenameNode', (folderPath) => {
+ this.#fileTree_.refreshFolder(folderPath);
+ });
+
+ this.#fileTree_.bind('afterRefreshNode', (refreshedNode) => {
+ const selectedNodeId = this.#fileTree_.getSelectedNodeId();
+ if (!selectedNodeId) {
+ this.hideEditor();
+ }
+ });
+
+ const fileTreeContextMenu = this.#fileTree_.getContextMenu();
+ const fileTreeMenu = fileTreeContextMenu.getItem('menu');
+
+ fileTreeMenu.add({
+ weight: 14,
+ id: 'sep5',
+ data: '---------'
+ });
+
+ fileTreeMenu.add({
+ weight: 15,
+ id: 'upload-folder',
+ preconditionFn: ($trigger) => {
+ let type = $trigger.attr('type');
+ if (type === 'file') {
+ return false;
+ }
+ return true;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFolder'], ''),
+ callback: async (_, { $trigger }) => {
+ this.#fileTree_.showProgress();
+ try {
+ const fp = await window.showDirectoryPicker();
+ if (!fp) {
+ this.#fileTree_.hideProgress();
+ return;
+ }
+ const type = $trigger.attr('type');
+ let folderPath = '/';
+ if (type !== 'root') {
+ folderPath = $trigger.attr('id');
+ }
+ const fs = this.#fileTree_.getFS();
+ try {
+ for await (const [key, value] of fp.entries()) {
+ if (value.kind !== 'file') {
+ continue;
+ }
+ const file = await value.getFile();
+ const arrayBuffer = await file.arrayBuffer();
+ const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer);
+ if (error) {
+ Debug.error(error);
+ }
+ }
+ } catch (error) {
+ Debug.error(error);
+ }
+ this.#fileTree_.refreshFolder(folderPath);
+ } catch (_) { }
+ this.#fileTree_.hideProgress();
+ }
+ }
+ });
+
+ fileTreeMenu.add({
+ weight: 16,
+ id: 'upload-file',
+ preconditionFn: ($trigger) => {
+ let type = $trigger.attr('type');
+ if (type === 'file') {
+ return false;
+ }
+ return true;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFile'], ''),
+ callback: async (_, { $trigger }) => {
+ this.#fileTree_.showProgress();
+ try {
+ const [ fp ] = await window.showOpenFilePicker({
+ multiple: false
+ });
+ if (!fp) {
+ this.#fileTree_.hideProgress();
+ return;
+ }
+ const type = $trigger.attr('type');
+ let folderPath = '/';
+ if (type !== 'root') {
+ folderPath = $trigger.attr('id');
+ }
+ const file = await fp.getFile();
+ const arrayBuffer = await file.arrayBuffer();
+ const fs = this.#fileTree_.getFS();
+ const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer);
+ if (error) {
+ Debug.error(error);
+ } else {
+ this.#fileTree_.refreshFolder(folderPath);
+ }
+ } catch (_) { }
+ this.#fileTree_.hideProgress();
+ }
+ }
+ });
+
+ fileTreeMenu.add({
+ weight: 17,
+ id: 'download',
+ preconditionFn: ($trigger) => {
+ const type = $trigger.attr('type');
+ if (type === 'file') {
+ return true;
+ }
+ return false;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy.download'], ''),
+ callback: async (_, { $trigger }) => {
+ this.#fileTree_.showProgress();
+ try {
+ const filePath = $trigger.attr('id');
+ const fp = await window.showSaveFilePicker({
+ suggestedName: path.basename(filePath)
+ });
+ if (!fp) {
+ this.#fileTree_.hideProgress();
+ return;
+ }
+ const fs = this.#fileTree_.getFS();
+ const [error, result] = await fs.readFile(filePath, 'buffer');
+ if (error) {
+ Debug.error(error);
+ } else {
+ const writer = await fp.createWritable();
+ await writer.write(result);
+ await writer.close();
+ }
+ } catch (_) { }
+ this.#fileTree_.hideProgress();
+ }
+ }
+ });
+
+ fileTreeMenu.add({
+ weight: 18,
+ id: 'sep6',
+ preconditionFn: ($trigger) => {
+ const selectedNodeId = this.#fileTree_.getSelectedNodeId();
+ let type = $trigger.attr('type');
+ let id = $trigger.attr('id');
+ if (type === 'file' && selectedNodeId !== id) {
+ return false;
+ }
+ return true;
+ },
+ data: '---------'
+ });
+
+ fileTreeMenu.add({
+ weight: 19,
+ id: 'refresh',
+ preconditionFn: ($trigger) => {
+ const selectedNodeId = this.#fileTree_.getSelectedNodeId();
+ let type = $trigger.attr('type');
+ let id = $trigger.attr('id');
+ if (type === 'file' && selectedNodeId !== id) {
+ return false;
+ }
+ return true;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy.refresh'], ''),
+ callback: (_, { $trigger }) => {
+ let type = $trigger.attr('type');
+ if (type === 'root') {
+ this.#fileTree_.openRootFolder();
+ this.#fileTree_.refreshFolder('/');
+ } else if (type === 'folder') {
+ let id = $trigger.attr('id');
+ this.#fileTree_.openNode(id);
+ this.#fileTree_.refreshFolder(id);
+ } else {
+ const nodes = this.#fileTree_.getSelectedNodes();
+ this.#fileTree_.runEvent('afterSelectLeaf', nodes);
+ }
+ }
+ }
+ });
+
+ fileTreeMenu.add({
+ weight: 20,
+ id: 'sep6',
+ preconditionFn: ($trigger) => {
+ let type = $trigger.attr('type');
+ return ['root'].includes(type);
+ },
+ data: '---------'
+ });
+
+ fileTreeMenu.add({
+ weight: 21,
+ id: 'exit',
+ preconditionFn: ($trigger) => {
+ let type = $trigger.attr('type');
+ return ['root'].includes(type);
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy.exit'], ''),
+ callback: () => {
+ this.closeFS();
+ }
+ }
+ });
+
+ const editorContextMenu = this.#editor_.getContextMenu();
+ const editorMenu = editorContextMenu.getItem('code');
+
+ editorMenu.empty();
+
+ editorMenu.add({
+ weight: 0,
+ id: 'cut',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.cut'], 'Ctrl+X'),
+ callback: () => this.#editor_.cut()
+ }
+ });
+ editorMenu.add({
+ weight: 1,
+ id: 'copy',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.copy'], 'Ctrl+C'),
+ callback: () => this.#editor_.copy()
+ }
+ });
+ editorMenu.add({
+ weight: 2,
+ id: 'paste',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.paste'], 'Ctrl+V'),
+ callback: () => this.#editor_.paste()
+ }
+ });
+ editorMenu.add({
+ weight: 3,
+ id: 'sep1',
+ data: '---------'
+ });
+ editorMenu.add({
+ weight: 4,
+ id: 'togglecomment',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.togglecomment'], 'Ctrl+/'),
+ callback: () => this.#editor_.commentLine()
+ }
+ });
+ /*editorMenu.add({
+ weight: 5,
+ id: 'toggleBlockComment',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.toggleBlockComment'], 'Shift+Alt+A'),
+ callback: (key, opt) => this.#editor_.blockComment()
+ }
+ });*/
+
+ editorMenu.add({
+ weight: 6,
+ id: 'sep2',
+ preconditionFn: () => {
+ return this.#changed_;
+ },
+ data: '---------'
+ });
+
+ editorMenu.add({
+ weight: 7,
+ id: 'save',
+ preconditionFn: () => {
+ return this.#changed_;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['file.save'], 'Ctrl+S'),
+ callback: async () => {
+ await this.put();
+ }
+ }
+ });
+
+ const { commands } = this.#editor_.getEditor();
+ commands.addCommand({
+ name: "save",
+ bindKey: "Ctrl-S",
+ exec: async () => {
+ if (!this.#changed_) {
+ return;
+ }
+ await this.put();
+ }
+ });
+ }
+
+ async put() {
+ this.#fileTree_.showProgress();
+ const id = this.#fileTree_.getSelectedNodeId();
+ const fs = this.#fileTree_.getFS();
+ const [error, stdout] = await fs.writeFile(id, this.#editor_.getValue());
+ this.#fileTree_.hideProgress();
+ if (!error) {
+ this.setStatus(false);
+ }
+ }
+
+ showFileTree() {
+ if (this.#fileTreeShown_) {
+ return;
+ }
+ this.#$openFS_.detach();
+ this.#$fileTree_.empty();
+ this.#$fileTree_.append(this.#fileTree_.getContent());
+ this.#fileTreeShown_ = true;
+ }
+
+ hideFileTree() {
+ if (!this.#fileTreeShown_) {
+ return;
+ }
+ this.#fileTree_.getContent().detach();
+ this.#$fileTree_.empty();
+ this.#$fileTree_.append(this.#$openFS_);
+ this.#fileTreeShown_ = false;
+ }
+
+ showEditor() {
+ if (this.#editorShown_) {
+ return;
+ }
+ this.#$editorEmpty_.detach();
+ this.#$editor_.empty();
+ this.#$editor_.append(this.#editor_.getContent());
+ this.#editorShown_ = true;
+ }
+
+ hideEditor() {
+ if (!this.#editorShown_) {
+ return;
+ }
+ this.#editor_.getContent().detach();
+ this.#$editor_.empty();
+ this.#$editor_.append(this.#$editorEmpty_);
+ this.#editorShown_ = false;
+ this.setStatus(false);
+ }
+
+ getDrag() {
+ return this.#drag_;
+ }
+
+ init() {
+ super.init();
+ this.addDirty();
+ this.setMarkStatus('negative');
+ this.#editor_.init();
+ this.#addEventsListener_();
+ const editor = this.#editor_.getEditor();
+ editor.setReadOnly(false);
+ editor.renderer.setShowGutter(true);
+ editor.setOptions({
+ enableBasicAutocompletion: true,
+ enableSnippets: true,
+ enableLiveAutocompletion: true,
+ newLineMode: 'unix'
+ });
+ editor.on('change', () => {
+ this.setStatus(true);
+ });
+ }
+
+ openFS() {
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ return;
+ }
+ const fs = this.#fileTree_.getFS();
+ fs.setPortName(port);
+ this.#fileTree_.setFolderPath('/');
+ this.#fileTree_.setRootFolderName(`${port} - /`);
+ this.#fileTree_.openRootFolder();
+ this.showFileTree();
+ }
+
+ closeFS() {
+ this.#fileTree_.deselectAll();
+ this.hideFileTree();
+ this.hideEditor();
+ this.setStatus(false);
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#editor_.onMounted();
+ this.#fileTree_.onMounted();
+ // this.#fileTree_.refreshFolder('/');
+ }
+
+ onUnmounted() {
+ // this.hideEditor();
+ // this.#fileTree_.deselectAll();
+ super.onUnmounted();
+ this.#editor_.onUnmounted();
+ this.#fileTree_.onUnmounted();
+ }
+
+ resize() {
+ super.resize();
+ this.#editor_.resize();
+ this.#fileTree_.resize();
+ }
+
+ setStatus(isChanged) {
+ if (this.#changed_ === isChanged) {
+ return;
+ }
+ this.#changed_ = isChanged;
+ if (isChanged) {
+ this.setMarkStatus('positive');
+ } else {
+ this.setMarkStatus('negative');
+ }
+ }
+
+ dispose() {
+ this.#$fileTree_ = null;
+ this.#$editor_ = null;
+ this.#$openFS_ = null;
+ this.#$editorEmpty_ = null;
+ this.#editor_.dispose();
+ this.#editor_ = null;
+ this.#fileTree_.dispose();
+ this.#fileTree_ = null;
+ this.#drag_.dispose();
+ this.#drag_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.StatusBarAmpy = StatusBarAmpy;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-fs.js b/mixly/common/modules/mixly-modules/common/statusbar-fs.js
new file mode 100644
index 00000000..558ddeae
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-fs.js
@@ -0,0 +1,331 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mprogress');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Component');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Serial');
+goog.require('Mixly.FSBoardHandler');
+goog.require('Mixly.Electron.FS');
+goog.require('Mixly.Electron.FSBoard');
+goog.provide('Mixly.StatusBarFS');
+
+const {
+ Env,
+ Msg,
+ PageBase,
+ HTMLTemplate,
+ Debug,
+ Component,
+ Registry,
+ Serial,
+ FSBoardHandler,
+ Electron = {}
+} = Mixly;
+
+const { FS, FSBoard } = Electron;
+
+const { layer } = layui;
+
+const os = Mixly.require('os');
+
+
+class Panel extends Component {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-fs-panel.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-fs-panel.html')))
+ );
+ }
+
+ #$folderInput_ = null;
+ #$closeBtn_ = null;
+ #$selectFolderBtn_ = null;
+ #$downloadBtn_ = null;
+ #$uploadBtn_ = null;
+ #$fsSelect_ = null;
+ #$progress_ = null;
+ #folderPath_ = '';
+ #fs_ = 'default';
+ #opened_ = false;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-fs-panel.html');
+ const $content = $(template.render({
+ mapFolder: Msg.Lang['statusbar.fs.mapFolder'],
+ comment: Msg.Lang['statusbar.fs.comment'],
+ commentInfo: Msg.Lang['statusbar.fs.commentInfo'],
+ filesystem: Msg.Lang['statusbar.fs.filesystem'],
+ path: Msg.Lang['statusbar.fs.path'],
+ selectFolder: Msg.Lang['statusbar.fs.selectFolder'],
+ download: Msg.Lang['statusbar.fs.download'],
+ upload: Msg.Lang['statusbar.fs.upload']
+ }));
+ this.setContent($content);
+ this.#$folderInput_ = $content.find('.folder-input');
+ this.#$closeBtn_ = $content.find('.close-btn');
+ this.#$selectFolderBtn_ = $content.find('.folder-btn');
+ this.#$downloadBtn_ = $content.find('.download-btn');
+ this.#$uploadBtn_ = $content.find('.upload-btn');
+ this.#$fsSelect_ = $content.find('.fs-type');
+ this.#$progress_ = $content.find('.progress');
+ this.addEventsType(['download', 'upload']);
+ this.#addEventsListener_();
+ this.#$fsSelect_.select2({
+ width: '100%',
+ minimumResultsForSearch: Infinity,
+ dropdownCssClass: 'mixly-scrollbar'
+ });
+ }
+
+ #addEventsListener_() {
+ this.#$fsSelect_.on('select2:select', (event) => {
+ const { data } = event.params;
+ this.#fs_ = data.id;
+ });
+
+ this.#$closeBtn_.click(() => {
+ this.dispose();
+ });
+
+ this.#$selectFolderBtn_.click(() => {
+ FS.showDirectoryPicker()
+ .then((folderPath) => {
+ if (!folderPath) {
+ return;
+ }
+ this.#folderPath_ = path.join(folderPath);
+ this.#$folderInput_.val(this.#folderPath_);
+ })
+ .catch(Debug.error);
+ });
+
+ this.#$downloadBtn_.click(() => {
+ this.#checkFolder_()
+ .then((status) => {
+ if (!status) {
+ return;
+ }
+ this.runEvent('download', {
+ folderPath: this.#folderPath_,
+ fsType: this.#fs_
+ });
+ })
+ .catch(Debug.error);
+ });
+
+ this.#$uploadBtn_.click(() => {
+ this.#checkFolder_()
+ .then((status) => {
+ if (!status) {
+ return;
+ }
+ this.runEvent('upload', {
+ folderPath: this.#folderPath_,
+ fsType: this.#fs_
+ });
+ })
+ .catch(Debug.error);
+ });
+ }
+
+ #checkFolder_() {
+ return new Promise((resolve, reject) => {
+ if (!this.#folderPath_) {
+ layer.msg(Msg.Lang['statusbar.fs.localFolderNotExist'], { time: 1000 });
+ resolve(false);
+ return;
+ }
+ FS.isDirectory(this.#folderPath_)
+ .then((status) => {
+ if (status) {
+ resolve(true);
+ } else {
+ layer.msg(Msg.Lang['statusbar.fs.localFolderNotExist'], { time: 1000 });
+ resolve(false);
+ }
+ })
+ .catch(reject);
+ });
+ }
+
+ setFSMenu(menu) {
+ this.#$fsSelect_.empty();
+ this.#fs_ = menu[0].id;
+ for (let i in menu) {
+ this.#$fsSelect_.append(new Option(menu[i].text, menu[i].id));
+ }
+ }
+
+ setStatus(isOpened) {
+ if (this.#opened_ === isOpened) {
+ return;
+ }
+ this.#opened_ = isOpened;
+ if (isOpened) {
+ this.#$progress_.css('display', 'block');
+ } else {
+ this.#$progress_.css('display', 'none');
+ }
+ }
+
+ dispose() {
+ this.#$fsSelect_.select2('destroy');
+ this.#$folderInput_ = null;
+ this.#$closeBtn_ = null;
+ this.#$selectFolderBtn_ = null;
+ this.#$downloadBtn_ = null;
+ this.#$uploadBtn_ = null;
+ this.#$fsSelect_ = null;
+ this.#$progress_ = null;
+ super.dispose();
+ }
+}
+
+
+class StatusBarFS extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-fs.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-fs.html')))
+ );
+ }
+
+ #$btn_ = null;
+ #$mask_ = null;
+ #fsBoard_ = null;
+ #registry_ = new Registry();
+ #opened_ = false;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-fs.html');
+ const $content = $(template.render({
+ new: Msg.Lang['statusbar.fs.newMapFolder']
+ }));
+ this.setContent($content);
+ this.#$btn_ = $content.find('.manage-btn');
+ this.#$mask_ = $content.children('.mask');
+ this.#fsBoard_ = new FSBoard();
+ this.#fsBoard_.setHandler(new FSBoardHandler());
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#$btn_.click(() => {
+ this.addPanel();
+ });
+ }
+
+ init() {
+ this.addDirty();
+ this.setMarkStatus('negative');
+ }
+
+ setStatus(isOpened) {
+ if (this.#opened_ === isOpened) {
+ return;
+ }
+ this.#opened_ = isOpened;
+ if (isOpened) {
+ this.#$mask_.css('display', 'block');
+ this.setMarkStatus('positive');
+ } else {
+ this.#$mask_.css('display', 'none');
+ this.setMarkStatus('negative');
+ }
+ }
+
+ addPanel() {
+ const panel = new Panel();
+ panel.setFSMenu(this.#fsBoard_.getHandler().getFSMenu());
+ this.#$btn_.parent().before(panel.getContent());
+ this.#registry_.register(panel.getId(), panel);
+ panel.bind('download', (config) => {
+ this.#ensureSerial_()
+ .then((status) => {
+ if (!status) {
+ return;
+ }
+ this.setStatus(true);
+ panel.setStatus(true);
+ return this.#fsBoard_.download(config.folderPath, config.fsType);
+ })
+ .catch(Debug.error)
+ .finally(() => {
+ this.setStatus(false);
+ panel.setStatus(false);
+ });
+ });
+
+ panel.bind('upload', (config) => {
+ this.#ensureSerial_()
+ .then((status) => {
+ if (!status) {
+ return;
+ }
+ this.setStatus(true);
+ panel.setStatus(true);
+ return this.#fsBoard_.upload(config.folderPath, config.fsType);
+ })
+ .catch(Debug.error)
+ .finally(() => {
+ this.setStatus(false);
+ panel.setStatus(false);
+ });
+ });
+
+ panel.bind('destroyed', () => {
+ this.#registry_.unregister(panel.getId());
+ });
+ }
+
+ #ensureSerial_() {
+ return new Promise((resolve, reject) => {
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], { time: 1000 });
+ resolve(false);
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ let closePromise = Promise.resolve();
+ if (statusBarSerial) {
+ closePromise = statusBarSerial.close();
+ }
+ closePromise.then(() => {
+ resolve(true);
+ }).catch(reject);
+ });
+ }
+
+ dispose() {
+ for (let id of this.#registry_.keys()) {
+ this.#registry_.getItem(id).dispose();
+ }
+ this.#$btn_ = null;
+ this.#$mask_ = null;
+ this.#fsBoard_.dispose();
+ this.#fsBoard_ = null;
+ this.#registry_.reset();
+ this.#registry_ = null;
+ super.dispose();
+ }
+
+ setHandler(handler) {
+ this.#fsBoard_.setHandler(handler);
+ }
+}
+
+Mixly.StatusBarFS = StatusBarFS;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-libs-code.js b/mixly/common/modules/mixly-modules/common/statusbar-libs-code.js
new file mode 100644
index 00000000..71ba1fc5
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-libs-code.js
@@ -0,0 +1,39 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.PageBase');
+goog.provide('Mixly.StatusBarLibsCode');
+
+const {
+ Env,
+ HTMLTemplate,
+ PageBase
+} = Mixly;
+
+
+class StatusBarLibsCode extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-libs-mix.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-libs-mix.html')))
+ );
+ }
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-libs-mix.html');
+ const $content = $(template.render());
+ this.setContent($content);
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ }
+}
+
+Mixly.StatusBarLibsCode = StatusBarLibsCode;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-libs-mix.js b/mixly/common/modules/mixly-modules/common/statusbar-libs-mix.js
new file mode 100644
index 00000000..3fb6f726
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-libs-mix.js
@@ -0,0 +1,41 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.Env');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.PageBase');
+goog.provide('Mixly.StatusBarLibsMix');
+
+const {
+ Env,
+ HTMLTemplate,
+ PageBase
+} = Mixly;
+
+
+class StatusBarLibsMix extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-libs-mix.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-libs-mix.html')))
+ );
+ }
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-libs-mix.html');
+ const $content = $(template.render());
+ this.setContent($content);
+ this.id = template.getId();
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ }
+}
+
+Mixly.StatusBarLibsMix = StatusBarLibsMix;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-libs.js b/mixly/common/modules/mixly-modules/common/statusbar-libs.js
new file mode 100644
index 00000000..9d887730
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-libs.js
@@ -0,0 +1,89 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('dayjs');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.StatusBar');
+goog.require('Mixly.SideBarsManager');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.StatusBarLibsMix');
+goog.require('Mixly.StatusBarLibsCode');
+goog.provide('Mixly.StatusBarLibs');
+
+const {
+ Env,
+ Msg,
+ Debug,
+ StatusBar,
+ SideBarsManager,
+ RightSideBarsManager,
+ HTMLTemplate,
+ PageBase,
+ StatusBarLibsMix,
+ StatusBarLibsCode,
+} = Mixly;
+
+
+class StatusBarLibs extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'statusbar/statusbar-libs.html',
+ new HTMLTemplate(`
`)
+ );
+ SideBarsManager.typesRegistry.register(['libs_mix'], StatusBarLibsMix);
+ SideBarsManager.typesRegistry.register(['libs_code'], StatusBarLibsCode);
+ }
+
+ #manager_ = null;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('statusbar/statusbar-libs.html');
+ const $content = $(template.render());
+ this.setContent($content);
+ this.id = template.getId();
+ this.#manager_ = new RightSideBarsManager($content[0]);
+ this.#manager_.add('libs_mix', 'libs_mix', 'Mixly库');
+ this.#manager_.add('libs_code', 'libs_code', '代码库');
+ this.#manager_.changeTo('libs_mix');
+ }
+
+ init() {
+ this.addDirty();
+ const $tab = this.getTab();
+ this.setMarkStatus('negative');
+ }
+
+ getManager() {
+ return this.#manager_;
+ }
+
+ resize() {
+ super.resize();
+ this.#manager_.resize();
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#manager_.onMounted();
+ }
+
+ onUnmounted() {
+ this.#manager_.onUnmounted();
+ super.onUnmounted();
+ }
+
+ dispose() {
+ this.#manager_.dispose();
+ this.#manager_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.StatusBarLibs = StatusBarLibs;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-output.js b/mixly/common/modules/mixly-modules/common/statusbar-output.js
new file mode 100644
index 00000000..f3d97a57
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-output.js
@@ -0,0 +1,21 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.StatusBar');
+goog.provide('Mixly.StatusBarOutput');
+
+const { StatusBar } = Mixly;
+
+class StatusBarOutput extends StatusBar {
+ constructor() {
+ super();
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ }
+}
+
+Mixly.StatusBarOutput = StatusBarOutput;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-serial-chart.js b/mixly/common/modules/mixly-modules/common/statusbar-serial-chart.js
new file mode 100644
index 00000000..f2751370
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-serial-chart.js
@@ -0,0 +1,267 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('$.ui');
+goog.require('$.flot');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.Regression');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Config');
+goog.provide('Mixly.StatusBarSerialChart');
+
+const {
+ Env,
+ Msg,
+ PageBase,
+ Regression,
+ HTMLTemplate,
+ Config
+} = Mixly;
+
+const { USER, SELECTED_BOARD } = Config;
+
+
+class StatusBarSerialChart extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-serial-chart.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-serial-chart.html')))
+ );
+ }
+
+ #data_ = [];
+ #totalPoints_ = SELECTED_BOARD?.serial?.pointNum ?? 100;
+ #yMax_ = SELECTED_BOARD?.serial?.yMax ?? 100;
+ #yMin_ = SELECTED_BOARD?.serial?.yMin ?? 0;
+ #needUpdate_ = false;
+ #regression_ = new Regression();
+ #plot_ = null;
+ #opened_ = false;
+ #started_ = false;
+ #$pointNum_ = null;
+ #$yMax_ = null;
+ #$yMin_ = null;
+ #isOpened_ = false;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-serial-chart.html');
+ const $template = $(template.render({
+ pointsNum: Msg.Lang['statusbar.serial.chart.pointsNum'],
+ yMin: Msg.Lang['statusbar.serial.chart.yMin'],
+ yMax: Msg.Lang['statusbar.serial.chart.yMax']
+ }));
+ this.setContent($template);
+ this.#$pointNum_ = $template.find('select');
+ this.#$pointNum_.select2({
+ width: '100%',
+ minimumResultsForSearch: Infinity,
+ dropdownCssClass: 'mixly-scrollbar'
+ });
+ this.#$pointNum_.val(this.#totalPoints_).trigger('change');
+ this.#$yMax_ = $template.find('.y-max');
+ this.#$yMax_.val(this.#yMax_);
+ this.#$yMin_ = $template.find('.y-min');
+ this.#$yMin_.val(this.#yMin_);
+ const axisFontColor = USER.theme === 'light' ? '#000' : '#c2c3c2';
+ this.#plot_ = $.plot($template.find('.figure'), this.getValue(), {
+ series: {
+ shadowSize: 1
+ },
+ colors: ['#777'],
+ yaxis: {
+ min: this.#yMin_,
+ max: this.#yMax_,
+ show: true,
+ font: {
+ fill: axisFontColor
+ }
+ },
+ xaxis: {
+ show: true,
+ font: {
+ fill: axisFontColor
+ },
+ mode: 'time',
+ timezone: 'browser',
+ twelveHourClock: true,
+ timeBase: 'milliseconds',
+ minTickSize: [1, 'second'],
+ min: Date.now(),
+ max: Date.now() + 1000 * 10,
+ }
+ });
+
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#$pointNum_.on('select2:select', (event) => {
+ const { data } = event.params;
+ this.#totalPoints_ = data.id;
+ this.setValue([]);
+ });
+
+ this.#$yMax_.change(() => {
+ const yMax = parseInt(this.#$yMax_.val());
+ if (isNaN(yMax) || yMax <= this.#yMin_) {
+ this.#$yMax_.val(this.#yMax_);
+ } else {
+ this.#yMax_ = yMax;
+ let { yaxis } = this.#plot_.getAxes();
+ yaxis.options.max = yMax;
+ }
+ });
+
+ this.#$yMin_.change(() => {
+ const yMin = parseInt(this.#$yMin_.val());
+ if (isNaN(yMin) || yMin >= this.#yMax_) {
+ this.#$yMin_.val(this.#yMin_);
+ } else {
+ this.#yMin_ = yMin;
+ let { yaxis } = this.#plot_.getAxes();
+ yaxis.options.min = yMin;
+ }
+ });
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ this.resize();
+ }
+
+ resize() {
+ this.#plot_.getSurface().clearCache();
+ super.resize();
+ this.#plot_.resize();
+ this.#plot_.setupGrid(false);
+ this.#plot_.draw();
+ }
+
+ stop() {
+ this.setValue([]);
+ this.#opened_ = false;
+ this.#started_ = false;
+ }
+
+ start() {
+ this.#opened_ = true;
+ if (this.#started_) {
+ return;
+ }
+ this.#started_ = true;
+ this.update();
+ }
+
+ update() {
+ if (!this.#started_) {
+ return;
+ }
+ if (!this.isActive()) {
+ this.#started_ = false;
+ return;
+ }
+ if (!this.#needUpdate_) {
+ setTimeout(() => this.update(), 50);
+ return;
+ }
+ this.#plot_.setData(this.getValue());
+ this.#plot_.getSurface().clearCache();
+ this.#plot_.setupGrid(false);
+ this.#plot_.draw();
+ this.setRange(this.#plot_);
+ this.#needUpdate_ = false;
+ window.requestAnimationFrame(() => this.update());
+ }
+
+ getValue() {
+ return [{
+ data: this.#data_,
+ lines: {
+ show: true,
+ fill: false,
+ fillColor: '#007acc'
+ }
+ }];
+ }
+
+ addValue(data) {
+ if (!this.#started_) {
+ return;
+ }
+ if (this.#data_.length
+ && data[0] === this.#data_[this.#data_.length - 1][0]) {
+ return;
+ }
+ while (this.#data_.length > this.#totalPoints_) {
+ this.#data_.shift();
+ }
+ this.#data_.push([Date.now(), data]);
+ this.#needUpdate_ = true;
+ }
+
+ setValue(data) {
+ if (!this.#started_) {
+ return;
+ }
+ this.#data_ = data;
+ this.#needUpdate_ = true;
+ }
+
+ setRange() {
+ let { xaxis } = this.#plot_.getAxes();
+ let { data = [] } = this.#plot_.getData()[0] ?? {};
+ if (!data.length) {
+ return;
+ }
+ if (data.length >= this.#totalPoints_) {
+ xaxis.options.min = data[0][0];
+ xaxis.options.max = data[this.#totalPoints_ - 1][0];
+ return;
+ }
+ let x = [], y = [];
+ for (let i in data) {
+ x.push((i - 0) + 1);
+ y.push(data[i][0] - data[0][0]);
+ }
+ this.#regression_.fit(x, y);
+ let xMax = this.#regression_.predict([this.#totalPoints_])[0] + data[0][0];
+ let xMin = data[0][0];
+ xaxis.options.min = xMin;
+ xaxis.options.max = xMax;
+ }
+
+ setStatus(isOpened) {
+ this.#isOpened_ = isOpened;
+ }
+
+ isOpened() {
+ return this.#isOpened_;
+ }
+
+ onMounted() {
+ super.onMounted();
+ if (this.#opened_) {
+ this.start();
+ }
+ }
+
+ onUnmounted() {
+ this.setValue([]);
+ super.onUnmounted();
+ }
+
+ dispose() {
+ this.#plot_.shutdown();
+ this.#$pointNum_.select2('destroy');
+ super.dispose();
+ }
+}
+
+Mixly.StatusBarSerialChart = StatusBarSerialChart;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-serial-output.js b/mixly/common/modules/mixly-modules/common/statusbar-serial-output.js
new file mode 100644
index 00000000..ab95ea4e
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-serial-output.js
@@ -0,0 +1,155 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.StatusBar');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.HTMLTemplate')
+goog.provide('Mixly.StatusBarSerialOutput');
+
+const {
+ PageBase,
+ StatusBar,
+ Env,
+ Msg,
+ HTMLTemplate
+} = Mixly;
+
+
+class StatusBarSerialOutput extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-serial-output.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-serial-output.html')))
+ );
+ }
+
+ #$empty_ = null;
+ #$scroll_ = null;
+ #$timestamp_ = null;
+ #$hex_ = null;
+ #scroll_ = true;
+ #timestamp_ = false;
+ #hex_ = false;
+ #active_ = false;
+ #isOpened_ = false;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-serial-output.html');
+ const $content = $(template.render({
+ empty: Msg.Lang['statusbar.serial.output.empty'],
+ scroll: Msg.Lang['statusbar.serial.output.scroll'],
+ timestamp: Msg.Lang['statusbar.serial.output.timestamp']
+ }));
+ this.setContent($content);
+ this.#$empty_ = $content.find('.empty');
+ this.#$scroll_ = $content.find('.scroll');
+ this.#$timestamp_ = $content.find('.timestamp');
+ this.#$hex_ = $content.find('.hex');
+ this.addPage($content.find('.body'), 'editor', new StatusBar());
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#$empty_.click(() => {
+ this.empty({ startRow: 0 });
+ });
+
+ this.#$scroll_.change((event) => {
+ let scroll = false;
+ if (this.#$scroll_.prop('checked')) {
+ scroll = true;
+ }
+ this.#scroll_ = scroll;
+ });
+
+ this.#$timestamp_.change((event) => {
+ let timestamp = false;
+ if (this.#$timestamp_.prop('checked')) {
+ timestamp = true;
+ }
+ this.#timestamp_ = timestamp;
+ });
+
+ this.#$hex_.change((event) => {
+ let hex = false;
+ if (this.#$hex_.prop('checked')) {
+ hex = true;
+ }
+ this.#hex_ = hex;
+ });
+ }
+
+ init() {
+ super.init();
+ this.hideCloseBtn();
+ this.#$scroll_.prop('checked', this.#scroll_);
+ this.#$timestamp_.prop('checked', this.#timestamp_);
+ this.#$hex_.prop('checked', this.#hex_);
+ }
+
+ empty() {
+ const editor = this.getEditor();
+ const endRow = editor.session.getLength();
+ if (this.isOpened()) {
+ editor.session.removeFullLines(1, endRow);
+ this.addValue('\n');
+ } else {
+ editor.session.removeFullLines(0, endRow);
+ }
+ }
+
+ setStatus(isOpened) {
+ this.#isOpened_ = isOpened;
+ }
+
+ isOpened() {
+ return this.#isOpened_;
+ }
+
+ getValue() {
+ return this.getPage('editor').getValue();
+ }
+
+ setValue(data, scroll) {
+ this.getPage('editor').setValue(data, scroll);
+ }
+
+ addValue(data, scroll) {
+ this.getPage('editor').addValue(data, scroll);
+ }
+
+ getPageAce() {
+ return this.getPage('editor');
+ }
+
+ getEditor() {
+ return this.getPage('editor').getEditor();
+ }
+
+ getEditorPageId() {
+ return this.getPage('editor').getId();
+ }
+
+ getContextMenu() {
+ return this.getPage('editor').getContextMenu();
+ }
+
+ scrollChecked() {
+ return this.#scroll_;
+ }
+
+ timestampChecked() {
+ return this.#timestamp_;
+ }
+
+ hexChecked() {
+ return this.#hex_;
+ }
+}
+
+Mixly.StatusBarSerialOutput = StatusBarSerialOutput;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar-serial.js b/mixly/common/modules/mixly-modules/common/statusbar-serial.js
new file mode 100644
index 00000000..49f24a3f
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar-serial.js
@@ -0,0 +1,657 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('dayjs');
+goog.require('$.select2');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Config');
+goog.require('Mixly.StatusBar');
+goog.require('Mixly.SideBarsManager');
+goog.require('Mixly.RightSideBarsManager');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.PageBase');
+goog.require('Mixly.Menu');
+goog.require('Mixly.StatusBarSerialOutput');
+goog.require('Mixly.StatusBarSerialChart');
+goog.require('Mixly.Electron.Serial');
+goog.require('Mixly.Web.Serial');
+goog.require('Mixly.WebSocket.Serial');
+goog.provide('Mixly.StatusBarSerial');
+
+const {
+ Env,
+ Msg,
+ Debug,
+ Config,
+ StatusBar,
+ SideBarsManager,
+ RightSideBarsManager,
+ HTMLTemplate,
+ PageBase,
+ Menu,
+ StatusBarSerialOutput,
+ StatusBarSerialChart,
+ Electron = {},
+ Web = {},
+ WebSocket = {}
+} = Mixly;
+
+let currentObj = null;
+
+if (goog.isElectron) {
+ currentObj = Electron;
+} else {
+ if (Env.hasSocketServer) {
+ currentObj = WebSocket;
+ } else {
+ currentObj = Web;
+ }
+}
+
+const { Serial } = currentObj;
+
+const { SELECTED_BOARD } = Config;
+
+
+class StatusBarSerial extends PageBase {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbar-serial.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbar-serial.html')))
+ );
+ SideBarsManager.typesRegistry.register(['serial_output'], StatusBarSerialOutput);
+ SideBarsManager.typesRegistry.register(['serial_chart'], StatusBarSerialChart);
+
+ this.getMenu = function () {
+ let ports = [];
+ let menu = { list: ports };
+ Serial.getCurrentPortsName().map((name) => {
+ ports.push(name);
+ });
+ if (!ports.length) {
+ menu.empty = Msg.Lang['statusbar.serial.noPort'];
+ }
+ return menu;
+ }
+ }
+
+ #$sendInput_ = null;
+ #$settingMenu_ = null;
+ #$scroll_ = null;
+ #$timestamp_ = null;
+ #$dtr_ = null;
+ #$rts_ = null;
+ #$hex_ = null;
+ #opened_ = false;
+ #valueTemp_ = '';
+ #receiveTemp_ = '';
+ #manager_ = null;
+ #output_ = null;
+ #chart_ = null;
+ #serial_ = null;
+ #port_ = '';
+ #config_ = {
+ baud: 115200,
+ dtr: true,
+ rts: false,
+ sendWith: '\r\n',
+ hex: false
+ };
+ #addTimestamp_ = false;
+ #maxLine_ = 200;
+ #lastUpdate_ = 0;
+ #refreshFrequency_ = 50;
+ #timer_ = null;
+ #reading_ = false;
+
+ constructor() {
+ super();
+ const template = HTMLTemplate.get('html/statusbar/statusbar-serial.html');
+ const $content = $(template.render({
+ sendInfo: Msg.Lang['statusbar.serial.sendInfo']
+ }));
+ this.setContent($content);
+ this.#$settingMenu_ = $content.find('.setting-menu');
+ this.#$settingMenu_.select2({
+ minimumResultsForSearch: Infinity,
+ dropdownAutoWidth: true,
+ dropdownCssClass: 'mixly-scrollbar'
+ });
+ this.id = template.getId();
+ this.#$sendInput_ = $content.find('.send > .box > input');
+ this.#$scroll_ = $content.find('.scroll');
+ this.#$timestamp_ = $content.find('.timestamp');
+ this.#$dtr_ = $content.find('.dtr');
+ this.#$rts_ = $content.find('.rts');
+ this.#$hex_ = $content.find('.hex');
+ this.#manager_ = new RightSideBarsManager($content.find('.content')[0]);
+ this.#manager_.add('serial_output', 'serial_output', Msg.Lang['statusbar.serial.output'], '');
+ this.#manager_.add('serial_chart', 'serial_chart', Msg.Lang['statusbar.serial.chart'], '');
+ this.#manager_.changeTo('serial_output');
+ this.#output_ = this.#manager_.get('serial_output');
+ this.#chart_ = this.#manager_.get('serial_chart');
+ this.#addCommandsForOutput_();
+ const config = Serial.getConfig();
+ this.#config_.dtr = config.dtr;
+ this.#config_.rts = config.rts;
+ this.#config_.baud = config.baudRates;
+ this.#config_.pointNum = config.pointNum;
+ this.#config_.reset = config.ctrlDBtn;
+ this.#config_.interrupt = config.ctrlCBtn;
+ this.#config_.yMax = config.yMax;
+ this.#config_.yMin = config.yMin;
+ }
+
+ #addCommandsForOutput_() {
+ const { commands } = this.#output_.getEditor();
+ commands.addCommands([{
+ name: 'copy',
+ bindKey: 'Ctrl-C',
+ readOnly: true,
+ exec: (editor) => {
+ const copyText = editor.getSelectedText();
+ if (!copyText) {
+ this.interrupt();
+ return true;
+ }
+ return false;
+ }
+ }, {
+ name: 'reset',
+ bindKey: 'Ctrl-D',
+ readOnly: true,
+ exec: (editor) => {
+ this.reset();
+ return true;
+ }
+ }]);
+ }
+
+ #addContextMenuItemsForOutput_() {
+ let menu = this.#output_.getContextMenu().getItem('code');
+ menu.add({
+ weight: 1,
+ type: 'sep1',
+ preconditionFn: () => {
+ return this.portExit();
+ },
+ data: '---------'
+ });
+ if (this.#config_.interrupt) {
+ menu.add({
+ weight: 2,
+ type: 'interrupt',
+ preconditionFn: () => {
+ return this.portExit();
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.serial.interrupt'], 'Ctrl+C'),
+ callback: (key, opt) => this.interrupt().catch(Debug.error)
+ }
+ });
+ }
+ if (this.#config_.reset) {
+ menu.add({
+ weight: 3,
+ type: 'reset',
+ preconditionFn: () => {
+ return this.portExit();
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.serial.reset'], 'Ctrl+D'),
+ callback: (key, opt) => this.reset().catch(Debug.error)
+ }
+ });
+ }
+ menu.add({
+ weight: 4,
+ type: 'toggle',
+ preconditionFn: () => {
+ return this.portExit();
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.serial.toggle'], ''),
+ callback: (key, opt) => this.toggle().catch(Debug.error)
+ }
+ });
+ }
+
+ #addEventsListener_() {
+ this.getTab().dblclick(() => {
+ if (!this.portExit()) {
+ return;
+ }
+ this.toggle().catch(Debug.error);
+ });
+
+ this.#serial_.bind('onOpen', () => {
+ this.setStatus(true);
+ const portName = this.getPortName();
+ this.setValue(
+ `==${Msg.Lang['statusbar.serial.port']} ${portName} ${Msg.Lang['statusbar.serial.open']}==\n`
+ );
+ this.#$sendInput_.attr('disabled', false);
+ if (this.#output_.timestampChecked()) {
+ this.#addTimestamp_ = true;
+ }
+ this.#chart_.start();
+ });
+
+ this.#serial_.bind('onClose', () => {
+ this.stopRead();
+ this.#timer_ && clearTimeout(this.#timer_);
+ this.#timer_ = null;
+ if (this.isDisposed() || !this.isOpened()) {
+ return;
+ }
+ this.setStatus(false);
+ const portName = this.getPortName();
+ const output = `${this.getValue() + this.#valueTemp_}\n`
+ + `==${Msg.Lang['statusbar.serial.port']} ${portName} ${Msg.Lang['statusbar.serial.close']}==`;
+ this.setValue(output);
+ this.#valueTemp_ = '';
+ this.#receiveTemp_ = '';
+ this.#$sendInput_.val('');
+ this.#$sendInput_.attr('disabled', true);
+ this.#chart_.stop();
+ });
+
+ this.#serial_.bind('onError', (error) => {
+ this.stopRead();
+ this.#timer_ && clearTimeout(this.#timer_);
+ this.#timer_ = null;
+ if (this.isDisposed()) {
+ return;
+ }
+ if (!this.isOpened()) {
+ this.setValue(`${String(error)}\n`);
+ return;
+ }
+ this.setValue(`${this.getValue() + this.#valueTemp_}\n${String(error)}\n`);
+ this.#valueTemp_ = '';
+ });
+
+ this.#serial_.bind('onChar', (char) => {
+ if (!this.#reading_) {
+ this.#receiveTemp_ += char;
+ }
+ if (!this.#reading_ || this.#output_.hexChecked()) {
+ return;
+ }
+ if (this.#output_.timestampChecked()) {
+ if (this.#addTimestamp_) {
+ const timestamp = dayjs().format('HH:mm:ss.SSS');
+ this.addValue(`${timestamp} -> ${char}`);
+ } else {
+ this.addValue(char);
+ }
+ if (char === '\n') {
+ this.#addTimestamp_ = true;
+ } else {
+ this.#addTimestamp_ = false;
+ }
+ } else {
+ this.addValue(char);
+ }
+ if (this.#timer_) {
+ return;
+ }
+ this.#timer_ = setTimeout(this.#timedRefresh_.bind(this), this.#refreshFrequency_);
+ });
+
+ this.#serial_.bind('onString', (str) => {
+ if (!this.#chart_.isActive() || !this.isActive() || !this.#reading_) {
+ return;
+ }
+ const num = parseFloat(str);
+ if (isNaN(num)) {
+ return;
+ }
+ this.#chart_.addValue(num);
+ });
+
+ this.#serial_.bind('onByte', (byte) => {
+ if (!this.#reading_ || !this.#output_.hexChecked()) {
+ return;
+ }
+ let str = byte.toString(16).toUpperCase();
+ if (str.length < 2) {
+ str = '0' + str;
+ }
+ str = '0x' + str + (byte === 0x0A ? '\n' : ' ');
+ if (this.#output_.timestampChecked()) {
+ if (this.#addTimestamp_) {
+ const timestamp = dayjs().format('HH:mm:ss.SSS');
+ this.addValue(`${timestamp} -> ${str}`);
+ } else {
+ this.addValue(str);
+ }
+ if (byte === 0x0A) {
+ this.#addTimestamp_ = true;
+ } else {
+ this.#addTimestamp_ = false;
+ }
+ } else {
+ this.addValue(str);
+ }
+ if (this.#timer_) {
+ return;
+ }
+ this.#timer_ = setTimeout(this.#timedRefresh_.bind(this), this.#refreshFrequency_);
+ });
+
+ this.#$settingMenu_.on('select2:select', (event) => {
+ const { id } = event.currentTarget.dataset;
+ const { data } = event.params;
+ if (id === 'baud') {
+ this.setBaudRate(data.id - 0).catch(Debug.error);
+ } else if (id === 'send-with') {
+ if (data.id === 'no') {
+ this.#config_.sendWith = '';
+ } else {
+ this.#config_.sendWith = data.id.replace('\\r', '\r').replace('\\n', '\n');
+ }
+ }
+ });
+
+ this.#$sendInput_.keydown((event) => {
+ if (event.keyCode !== 13) {
+ return;
+ }
+ const { sendWith } = this.#config_;
+ let data = this.#$sendInput_.val();
+ if (this.#config_.hex) {
+ let hexstr = data.split(' ');
+ let hexs = [];
+ for (let str of hexstr) {
+ let hex = parseInt(str, 16);
+ if (isNaN(hex)) {
+ continue;
+ }
+ hexs.push(hex);
+ }
+ for (let char of sendWith) {
+ hexs.push(char.charCodeAt(0));
+ }
+ this.#serial_.sendBuffer(hexs).catch(Debug.error);
+ } else {
+ data += sendWith;
+ this.#serial_.sendString(data).catch(Debug.error);
+ }
+ this.#$sendInput_.val('');
+ });
+
+ this.#$dtr_.change((event) => {
+ let dtr = false;
+ if (this.#$dtr_.prop('checked')) {
+ dtr = true;
+ }
+ if (this.isOpened()) {
+ this.#serial_.setDTR(dtr)
+ .then(() => {
+ this.#config_.dtr = dtr;
+ })
+ .catch(Debug.error);
+ } else {
+ this.#config_.dtr = dtr;
+ }
+ });
+
+ this.#$rts_.change((event) => {
+ let rts = false;
+ if (this.#$rts_.prop('checked')) {
+ rts = true;
+ }
+ if (this.isOpened()) {
+ this.#serial_.setRTS(rts)
+ .then(() => {
+ this.#config_.rts = rts;
+ })
+ .catch(Debug.error);
+ } else {
+ this.#config_.rts = rts;
+ }
+ });
+
+ this.#$hex_.change((event) => {
+ let hex = false;
+ if (this.#$hex_.prop('checked')) {
+ hex = true;
+ }
+ this.#config_.hex = hex;
+ });
+ }
+
+ #timedRefresh_() {
+ this.#timer_ = null;
+ if (!this.#valueTemp_ || !this.isOpened()) {
+ return;
+ }
+ if (Date.now() - this.#lastUpdate_ < this.#refreshFrequency_) {
+ this.#timer_ = setTimeout(this.#timedRefresh_.bind(this), this.#refreshFrequency_);
+ return;
+ }
+ this.addValue('');
+ }
+
+ init() {
+ super.init();
+ this.addDirty();
+ this.setMarkStatus('negative');
+ const $tab = this.getTab();
+ this.#port_ = $tab.attr('data-tab-id');
+ this.#serial_ = new Serial(this.getPortName());
+ this.#serial_.config(this.#config_).catch(Debug.error);
+ this.#addEventsListener_();
+ this.#addContextMenuItemsForOutput_();
+ this.setValue(this.#valueTemp_);
+ this.#valueTemp_ = '';
+ this.#$settingMenu_.filter('[data-id="baud"]').val(this.#config_.baud).trigger('change');
+ this.#$dtr_.prop('checked', this.#config_.dtr);
+ this.#$rts_.prop('checked', this.#config_.rts);
+ }
+
+ async open() {
+ await this.#serial_.open(this.#config_.baud);
+ if (SELECTED_BOARD?.serial?.ctrlCBtn) {
+ await this.#serial_.sleep(1000);
+ await this.#serial_.interrupt();
+ const startTime = Number(new Date());
+ let endTime = startTime;
+ while (endTime - startTime < 2000) {
+ await this.#serial_.sleep(50);
+ if (this.#receiveTemp_.indexOf('>>>') !== -1) {
+ break;
+ }
+ endTime = Number(new Date());
+ }
+ this.#valueTemp_ = '';
+ this.empty();
+ this.startRead();
+ this.#receiveTemp_ = '';
+ await this.#serial_.reset();
+ } else {
+ this.startRead();
+ }
+ }
+
+ async close() {
+ await this.#serial_.close();
+ }
+
+ async toggle() {
+ if (this.isOpened()) {
+ await this.#serial_.close();
+ } else {
+ await this.#serial_.open();
+ await this.#serial_.sleep(200);
+ try {
+ await this.#serial_.setDTRAndRTS(this.#config_.dtr, this.#config_.rts);
+ } catch (error) {
+ Debug.error(error);
+ }
+ this.startRead();
+ }
+ }
+
+ async interrupt() {
+ await this.#serial_.open(this.#config_.baud);
+ this.startRead();
+ await this.#serial_.interrupt();
+ }
+
+ async reset() {
+ await this.#serial_.open(this.#config_.baud);
+ this.startRead();
+ await this.#serial_.interrupt();
+ await this.#serial_.reset();
+ }
+
+ setStatus(isOpened) {
+ if (this.isOpened() === isOpened) {
+ return;
+ }
+ this.#opened_ = isOpened;
+ if (isOpened) {
+ this.setMarkStatus('positive');
+ } else {
+ this.setMarkStatus('negative');
+ }
+ this.#output_.setStatus(isOpened);
+ this.#chart_.setStatus(isOpened);
+ }
+
+ async setBaudRate(baud) {
+ if (!this.isOpened()) {
+ this.#config_.baud = baud;
+ return;
+ }
+ if (this.#serial_.baudRateIsLegal(baud)) {
+ try {
+ await this.#serial_.setBaudRate(baud);
+ this.#config_.baud = baud;
+ } catch (error) {
+ Debug.error(error);
+ }
+ }
+ this.#$settingMenu_.filter('[data-id="baud"]').val(this.#config_.baud).trigger('change');
+ this.startRead();
+ }
+
+ isOpened() {
+ return this.#opened_;
+ }
+
+ portExit() {
+ const portsName = Serial.getCurrentPortsName();
+ return portsName.includes(this.getPortName());
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ getSerial() {
+ return this.#serial_;
+ }
+
+ startRead() {
+ this.#reading_ = true;
+ }
+
+ stopRead() {
+ this.#reading_ = false;
+ }
+
+ dispose() {
+ this.#$settingMenu_.select2('destroy');
+ this.#$settingMenu_ = null;
+ this.#serial_.close()
+ .catch(Debug.error)
+ .finally(() => {
+ this.#$sendInput_ = null;
+ this.#$settingMenu_ = null;
+ this.#$scroll_ = null;
+ this.#$timestamp_ = null;
+ this.#$dtr_ = null;
+ this.#$rts_ = null;
+ this.#$hex_ = null;
+ this.#output_ = null;
+ this.#chart_ = null;
+ this.#manager_.dispose();
+ this.#manager_ = null;
+ this.#serial_.dispose();
+ this.#serial_ = null;
+ super.dispose();
+ });
+ }
+
+ getValue() {
+ if (!this.isInited()) {
+ return this.#valueTemp_;
+ } else {
+ return this.#output_.getValue();
+ }
+ }
+
+ empty() {
+ this.#output_.empty();
+ }
+
+ setValue(data) {
+ if (!this.isInited()) {
+ this.#valueTemp_ = data;
+ return;
+ }
+ this.#output_.setValue(data, this.#output_.scrollChecked());
+ }
+
+ addValue(data) {
+ if (!this.isInited()) {
+ this.#valueTemp_ += data;
+ return;
+ }
+ this.#valueTemp_ += data;
+ if (Date.now() - this.#lastUpdate_ < this.#refreshFrequency_ || !this.isOpened()) {
+ return;
+ }
+ this.#output_.addValue(this.#valueTemp_, this.#output_.scrollChecked());
+ this.#valueTemp_ = '';
+ const editor = this.#output_.getEditor();
+ const row = editor.session.getLength();
+ if (row > this.#maxLine_) {
+ const initCursor = editor.selection.getCursor();
+ const removedLine = row - this.#maxLine_;
+ editor.session.removeFullLines(1, removedLine);
+ }
+ this.#lastUpdate_ = Date.now();
+ }
+
+ getManager() {
+ return this.#manager_;
+ }
+
+ resize() {
+ super.resize();
+ this.getManager().resize();
+ }
+
+ onMounted() {
+ super.onMounted();
+ this.#manager_.onMounted();
+ }
+
+ onUnmounted() {
+ this.#manager_.onUnmounted();
+ super.onUnmounted();
+ }
+}
+
+Mixly.StatusBarSerial = StatusBarSerial;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbar.js b/mixly/common/modules/mixly-modules/common/statusbar.js
new file mode 100644
index 00000000..19ac0099
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbar.js
@@ -0,0 +1,89 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Config');
+goog.require('Mixly.EditorAce');
+goog.require('Mixly.ContextMenu');
+goog.require('Mixly.Menu');
+goog.require('Mixly.Msg');
+goog.require('Mixly.IdGenerator');
+goog.provide('Mixly.StatusBar');
+
+const {
+ XML,
+ Env,
+ Config,
+ EditorAce,
+ ContextMenu,
+ Menu,
+ Msg,
+ IdGenerator
+} = Mixly;
+const { USER } = Config;
+
+
+class StatusBar extends EditorAce {
+ #contextMenu_ = null;
+ constructor() {
+ super();
+ this.#addContextMenu_();
+ }
+
+ init() {
+ super.init();
+ this.#toStatusBar_();
+ }
+
+ #addContextMenu_() {
+ this.#contextMenu_ = new ContextMenu(`div[page-id="${this.getId()}"]`, {
+ zIndex: 300
+ });
+ let menu = new Menu();
+ menu.add({
+ weight: 0,
+ id: 'copy',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['editor.contextMenu.copy'], 'Ctrl+C'),
+ callback: (key, opt) => this.copy()
+ }
+ });
+ this.#contextMenu_.register('code', menu);
+ this.#contextMenu_.bind('getMenu', () => 'code');
+ }
+
+ #toStatusBar_() {
+ const editor = this.getEditor();
+ if (USER.theme === 'dark') {
+ editor.setOption('theme', 'ace/theme/tomorrow_night');
+ } else {
+ editor.setOption('theme', 'ace/theme/xcode');
+ }
+ editor.getSession().setMode('ace/mode/python');
+ editor.setReadOnly(true);
+ // editor.setScrollSpeed(0.3);
+ editor.setShowPrintMargin(false);
+ editor.renderer.setShowGutter(false);
+ editor.setOptions({
+ enableBasicAutocompletion: false,
+ enableSnippets: false,
+ enableLiveAutocompletion: false
+ });
+ editor.setHighlightActiveLine(false);
+ }
+
+ getContextMenu() {
+ return this.#contextMenu_;
+ }
+
+ dispose() {
+ this.#contextMenu_.dispose();
+ this.#contextMenu_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.StatusBar = StatusBar;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/statusbars-manager.js b/mixly/common/modules/mixly-modules/common/statusbars-manager.js
new file mode 100644
index 00000000..85d94fc1
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/statusbars-manager.js
@@ -0,0 +1,289 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Events');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.StatusBarOutput');
+goog.require('Mixly.StatusBarSerial');
+goog.require('Mixly.StatusBarFS');
+goog.require('Mixly.StatusBarLibs');
+goog.require('Mixly.StatusBarAmpy')
+goog.require('Mixly.PagesManager');
+goog.require('Mixly.DropdownMenu');
+goog.require('Mixly.Menu');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.Serial');
+goog.require('Mixly.Config');
+goog.require('Mixly.Debug');
+goog.provide('Mixly.StatusBarsManager');
+
+const {
+ XML,
+ Env,
+ Msg,
+ Registry,
+ Events,
+ HTMLTemplate,
+ StatusBarOutput,
+ StatusBarSerial,
+ StatusBarFS,
+ StatusBarLibs,
+ StatusBarAmpy,
+ PagesManager,
+ DropdownMenu,
+ Menu,
+ IdGenerator,
+ Serial,
+ Config,
+ Debug
+} = Mixly;
+
+const { layer } = layui;
+const { BOARD } = Config;
+
+
+class StatusBarsManager extends PagesManager {
+ static {
+ HTMLTemplate.add(
+ 'html/statusbar/statusbars-manager.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbars-manager.html')))
+ );
+ HTMLTemplate.add(
+ 'html/statusbar/statusbars-tab.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/statusbar/statusbars-tab.html')))
+ );
+ this.typesRegistry = new Registry();
+ this.managersRegistry = new Registry();
+ this.typesRegistry.register(['#default', 'terminal'], StatusBarOutput);
+ this.typesRegistry.register(['serial'], StatusBarSerial);
+ this.typesRegistry.register(['board-fs'], StatusBarFS);
+ this.typesRegistry.register(['libs'], StatusBarLibs);
+ this.typesRegistry.register(['ampy'], StatusBarAmpy);
+
+ this.getMain = function() {
+ if (!this.managersRegistry.length()) {
+ return null;
+ }
+ const key = this.managersRegistry.keys()[0];
+ return this.managersRegistry.getItem(key);
+ }
+
+ this.add = function(manager) {
+ this.managersRegistry.register(manager.id, manager);
+ }
+
+ this.remove = function(manager) {
+ this.managersRegistry.unregister(manager.id);
+ }
+ }
+
+ #shown_ = false;
+ #dropdownMenu_ = null;
+ #$dropdownBtn_ = null;
+
+ constructor(element) {
+ const managerHTMLTemplate = HTMLTemplate.get('html/statusbar/statusbars-manager.html');
+ const tabHTMLTemplate = HTMLTemplate.get('html/statusbar/statusbars-tab.html');
+ const $manager = $(managerHTMLTemplate.render());
+ const $tab = $(tabHTMLTemplate.render());
+ super({
+ parentElem: element,
+ managerContentElem: $manager[0],
+ bodyElem: $manager.find('.body')[0],
+ tabElem: $manager.find('.tabs')[0],
+ tabContentElem: $tab[0],
+ typesRegistry: StatusBarsManager.typesRegistry
+ });
+ this.tabId = tabHTMLTemplate.id;
+ this.id = IdGenerator.generate();
+ this.addEventsType(['show', 'hide', 'onSelectMenu', 'getMenu']);
+ this.#$dropdownBtn_ = $tab.find('.statusbar-dropdown-menu > button');
+ $tab.find('.statusbar-close > button').click(() => this.hide());
+ this.#addDropdownMenu_();
+ this.#addEventsListener_();
+ StatusBarsManager.add(this);
+ }
+
+ getStatusBarById(id) {
+ return this.get(id);
+ }
+
+ removeStatusBarById(id) {
+ this.remove(id);
+ }
+
+ show() {
+ this.runEvent('show');
+ }
+
+ hide() {
+ this.runEvent('hide');
+ }
+
+ toggle() {
+ this.isShown() ? this.hide() : this.show();
+ }
+
+ isShown() {
+ return this.#shown_;
+ }
+
+ openSelectedPort() {
+ const port = Serial.getSelectedPortName();
+ if (port) {
+ this.show();
+ this.#onSelectMenu_(port);
+ } else {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ }
+ }
+
+ #addDropdownMenu_() {
+ let menu = new Menu();
+ let serialChildMenu = new Menu(true);
+ menu.add({
+ weight: 0,
+ id: 'serial-default',
+ preconditionFn: () => {
+ return !!Serial.getCurrentPortsName().length;
+ },
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.openSelectedPort'], ''),
+ callback: (key, opt) => {
+ this.openSelectedPort();
+ }
+ }
+ });
+ menu.add({
+ weight: 1,
+ id: 'serial',
+ children: serialChildMenu,
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.openPort'], '')
+ }
+ });
+
+ /*menu.add({
+ weight: 2,
+ id: 'lib',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem('第三方库管理', ''),
+ callback: (key, opt) => {
+ this.add('libs', 'libs', '第三方库管理');
+ this.changeTo('libs');
+ }
+ }
+ });*/
+
+ if (['micropython', 'circuitpython'].includes(BOARD.language.toLowerCase())
+ && !['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ menu.add({
+ weight: 2,
+ id: 'sep1',
+ data: '---------'
+ });
+ menu.add({
+ weight: 3,
+ id: 'ampy',
+ data: {
+ isHtmlName: true,
+ name: Menu.getItem(Msg.Lang['statusbar.ampy'], ''),
+ callback: (key, opt) => {
+ this.add({
+ id: 'ampy',
+ type: 'ampy',
+ name: Msg.Lang['statusbar.ampy'],
+ title: Msg.Lang['statusbar.ampy']
+ });
+ this.changeTo('ampy');
+ }
+ }
+ });
+ }
+ serialChildMenu.bind('onRead', () => {
+ const options = this.#getMenu_() ?? {};
+ options.list = options.list ?? [];
+ options.empty = options.empty ?? Msg.Lang['statusbar.serial.noPort'];
+ const result = [];
+ if (!options.list.length) {
+ result.push({
+ weight: 1,
+ id: 'empty',
+ data: {
+ isHtmlName: true,
+ name: options.empty,
+ disabled: true
+ }
+ });
+ }
+ for (let i in options.list) {
+ result.push({
+ weight: 1,
+ id: `serial${i}`,
+ data: {
+ isHtmlName: true,
+ name: options.list[i],
+ callback: (key, opt) => this.#onSelectMenu_(options.list[i])
+ }
+ });
+ }
+ return result;
+ });
+ this.#dropdownMenu_ = new DropdownMenu(this.#$dropdownBtn_[0]);
+ this.#dropdownMenu_.register('menu', menu);
+ this.#dropdownMenu_.bind('getMenu', () => 'menu');
+ }
+
+ #onSelectMenu_(port) {
+ this.runEvent('onSelectMenu', port);
+ }
+
+ #getMenu_() {
+ let menus = this.runEvent('getMenu');
+ if (menus && menus.length) {
+ return menus[0];
+ }
+ return { list: [], empty: Msg.Lang['statusbar.dropdownMenu.noOptions'] };
+ }
+
+ #addEventsListener_() {
+ this.bind('getMenu', () => {
+ return StatusBarSerial.getMenu();
+ });
+
+ this.bind('onSelectMenu', (port) => {
+ this.add('serial', port);
+ this.changeTo(port);
+ const statusBarSerial = this.getStatusBarById(port);
+ if (statusBarSerial.isInited() && !statusBarSerial.isOpened()) {
+ statusBarSerial.open().catch(Debug.error);
+ }
+ });
+ }
+
+ getDropdownMenu() {
+ return this.#dropdownMenu_;
+ }
+
+ dispose() {
+ StatusBarsManager.remove(this);
+ this.#dropdownMenu_.dispose();
+ this.#$dropdownBtn_.remove();
+ this.#$dropdownBtn_ = null;
+ super.dispose();
+ }
+}
+
+Mixly.StatusBarsManager = StatusBarsManager;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/storage.js b/mixly/common/modules/mixly-modules/common/storage.js
new file mode 100644
index 00000000..40c2f060
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/storage.js
@@ -0,0 +1,54 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.LocalStorage');
+goog.require('Mixly.Config');
+goog.provide('Mixly.Storage');
+
+const {
+ LocalStorage,
+ Config,
+ Storage
+} = Mixly;
+
+const { laytpl } = layui;
+
+const { BOARD } = Config;
+
+Storage.user = function (key, value) {
+ let storagePath = path.join(LocalStorage.PATH['USER'], key);
+ if (arguments.length > 1) {
+ LocalStorage.set(storagePath, value);
+ } else {
+ value = LocalStorage.get(storagePath);
+ }
+ return value;
+}
+
+Storage.board = function (key, value) {
+ let storagePath = path.join(laytpl(LocalStorage.PATH['BOARD']).render({
+ boardType: BOARD.boardType
+ }), key);
+ if (arguments.length > 1) {
+ LocalStorage.set(storagePath, value);
+ } else {
+ value = LocalStorage.get(storagePath);
+ }
+ return value;
+}
+
+Storage.thirdParty = function (name, key, value) {
+ let storagePath = path.join(laytpl(LocalStorage.PATH['THIRD_PARTY']).render({
+ boardType: BOARD.boardType,
+ thirdPartyName: name ?? 'default'
+ }), key);
+ if (arguments.length > 1) {
+ LocalStorage.set(storagePath, value);
+ } else {
+ value = LocalStorage.get(storagePath);
+ }
+ return value;
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/title.js b/mixly/common/modules/mixly-modules/common/title.js
new file mode 100644
index 00000000..13a8ba06
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/title.js
@@ -0,0 +1,70 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.Config');
+goog.require('Mixly.Debug');
+goog.provide('Mixly.Title');
+
+let {
+ Config,
+ Debug,
+ Title
+} = Mixly;
+let { BOARD, SOFTWARE } = Config;
+
+if (SOFTWARE?.version && BOARD?.boardType) {
+ document.title = SOFTWARE.version + " For " + BOARD.boardType;
+}
+Title.title = document.title;
+Title.NOWTITLE = Title.title;
+
+Title.updeteVersionNumber = (newVersionNumber) => {
+ try {
+ Title.NOWTITLE = document.title.replace(/Mixly[\s]?[\d.]+/g, "Mixly " + newVersionNumber);
+ document.title = Title.NOWTITLE;
+ } catch (error) {
+ Debug.error(error);
+ }
+}
+
+Title.getVersionNumber = () => {
+ try {
+ Title.NOWTITLE = document.title.match(/Mixly[\s]?[\d.]+/g);
+ return Title.NOWTITLE;
+ } catch (error) {
+ Debug.error(error);
+ return '';
+ }
+}
+
+Title.updeteFilePath = function (newPath) {
+ try {
+ var pathArr = Title.NOWTITLE.match(/\([^\n\r]+\)/g);
+ if (pathArr) {
+ Title.NOWTITLE = document.title.replace(/\([^\n\r]+\)/g, "(" + newPath + ")");
+ document.title = Title.NOWTITLE;
+ } else {
+ Title.NOWTITLE = document.title + " (" + newPath + ")";
+ document.title = Title.NOWTITLE;
+ }
+ } catch (error) {
+ Debug.error(error);
+ }
+}
+
+Title.getFilePath = () => {
+ try {
+ let filePathArr = document.title.match(/(?<=\()[^\n\r]+(?=\))/g);
+ if (filePathArr && filePathArr.length > 0) {
+ return filePathArr[0];
+ }
+ } catch (error) {
+ Debug.error(error);
+ }
+ return null;
+}
+
+Title.updateTitle = (newTitle) => {
+ Title.NOWTITLE = newTitle;
+ document.title = newTitle;
+}
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/toolbox-searcher.js b/mixly/common/modules/mixly-modules/common/toolbox-searcher.js
new file mode 100644
index 00000000..3d521794
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/toolbox-searcher.js
@@ -0,0 +1,208 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Blockly');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Debug');
+goog.provide('Mixly.ToolboxSearcher');
+
+const {
+ Env,
+ XML,
+ Msg,
+ HTMLTemplate,
+ Debug
+} = Mixly;
+
+class ToolboxSearcher {
+ static {
+ this.searchHtmlTemplate = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/search-div.html'))
+ );
+ }
+
+ constructor(mainWorkspace) {
+ this.mainWorkspace = mainWorkspace;
+ this.searchWorkspace = new Blockly.Workspace(new Blockly.Options({
+ toolbox: null
+ }));
+ this.mainToolbox = this.mainWorkspace.getToolbox();
+ this.$search = $(ToolboxSearcher.searchHtmlTemplate.render({
+ search: Msg.Lang['toolboxSearcher.search']
+ }));
+ this.$i = this.$search.find('i');
+ this.$input = this.$search.find('input');
+ this.prevText = '';
+ $(this.mainToolbox.HtmlDiv).append(this.$search);
+ this.addEventsListener();
+ }
+
+ addEventsListener() {
+ this.$input.change(() => this.startSearch());
+ this.$input.bind('input propertychange', (event) => {
+ const searchCategory = this.mainToolbox.getToolboxItemById('catSearch');
+ const keyText = event.target.value;
+ if (!keyText.replaceAll(' ', '')) {
+ searchCategory.hide();
+ this.$i.addClass('disabled');
+ return;
+ } else {
+ searchCategory.show();
+ }
+ this.scrollTop();
+ if (keyText === this.prevText) {
+ this.$i.addClass('disabled');
+ } else {
+ this.$i.removeClass('disabled');
+ }
+ });
+ }
+
+ scrollTop() {
+ const { HtmlDiv } = this.mainToolbox;
+ $(HtmlDiv).scrollTop(HtmlDiv.scrollHeight);
+ }
+
+ checkBlock(blockxml, keys) {
+ this.searchWorkspace.clear();
+ try {
+ Blockly.Xml.domToBlock(blockxml, this.searchWorkspace);
+ } catch (error) {
+ Debug.error(error);
+ return false;
+ }
+ const blocks = this.searchWorkspace.getAllBlocks(true);
+ let select = false;
+ for (let block of blocks) {
+ const { inputList } = block;
+ for (let input of inputList) {
+ const { fieldRow } = input;
+ for (let field of fieldRow) {
+ const fieldText = field.getText().toLowerCase();
+ let times = 0;
+ for (let key = 0; key < keys.length; key++) {
+ if (fieldText.indexOf(keys[key]) === -1) {
+ continue;
+ }
+ times++;
+ }
+ if (keys.length === times) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ searchBlocks(keys) {
+ return new Promise((resolve, reject) => {
+ const searchCategory = this.mainToolbox.getToolboxItemById('catSearch');
+ let outputXML = [];
+ let selectedBlocksLen = 0;
+ const categories = this.mainToolbox.getToolboxItems();
+ for (let j = 0; categories[j]; j++) {
+ const category = categories[j];
+ if (category.id_ === 'catSearch') continue;
+ if (typeof category.getContents !== 'function') continue;
+ const blocksList = category.getContents();
+ let addLabel = true;
+ for (let blockDef of blocksList) {
+ const { type, kind, blockxml } = blockDef;
+ if (kind !== 'BLOCK') {
+ continue;
+ }
+ if (!this.checkBlock(blockxml, keys)) {
+ continue;
+ }
+ if (addLabel) {
+ const categoryPath = this.getCategoryPath(category);
+ outputXML.push({
+ kind: 'LABEL',
+ text: categoryPath
+ });
+ addLabel = false;
+ }
+ outputXML.push(blockDef);
+ selectedBlocksLen++;
+ if (selectedBlocksLen > 30) {
+ break;
+ }
+ }
+ if (selectedBlocksLen > 30) {
+ outputXML.unshift({
+ kind: 'LABEL',
+ text: Msg.Lang['toolboxSearcher.tooManyResultsInfo']
+ });
+ break;
+ }
+ }
+ this.searchWorkspace.clear();
+ searchCategory.updateFlyoutContents(
+ outputXML.length ?
+ outputXML : [{
+ kind: 'LABEL',
+ text: Msg.Lang['toolboxSearcher.empty']
+ }]
+ );
+ this.mainToolbox.refreshSelection();
+ this.mainToolbox.setSelectedItem(searchCategory);
+ const { selectedItem_ } = this.mainToolbox;
+ if (selectedItem_ && selectedItem_.isCollapsible()) {
+ selectedItem_.setExpanded(true);
+ }
+ this.scrollTop();
+ resolve();
+ });
+ }
+
+ getCategoryPath(category) {
+ let categoryPath = '';
+ for (; category; category = category.getParent()) {
+ categoryPath = category.toolboxItemDef_.name + (categoryPath && (' > ' + categoryPath));
+ }
+ return categoryPath;
+ }
+
+ startSearch() {
+ if (this.$i.hasClass('disabled')) {
+ return
+ };
+ let text = this.$input.val();
+ this.prevText = text;
+ try {
+ if (!text.replaceAll(' ', '')) {
+ return;
+ }
+ } catch(error) {
+ Debug.error(error);
+ }
+ this.$input.attr('disabled', true);
+ this.$i.removeClass('codicon-search-fuzzy');
+ this.$i.addClass('codicon-loading layui-anim-rotate layui-anim-loop');
+ setTimeout(() => {
+ let keys = text.toLowerCase().split(' ');
+ this.searchBlocks(keys)
+ .catch(Debug.error)
+ .finally(() => {
+ this.$i.removeClass('codicon-loading layui-anim-rotate layui-anim-loop');
+ this.$i.addClass('codicon-search-fuzzy disabled');
+ this.$input.removeAttr('disabled');
+ });
+ }, 100);
+ }
+
+ restart() {
+ this.prevText = '';
+ const searchCategory = this.mainToolbox.getToolboxItemById('catSearch');
+ this.$input.val('');
+ searchCategory.hide();
+ }
+}
+
+Mixly.ToolboxSearcher = ToolboxSearcher;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/url.js b/mixly/common/modules/mixly-modules/common/url.js
new file mode 100644
index 00000000..7429006b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/url.js
@@ -0,0 +1,213 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.Url');
+
+const { Url } = Mixly;
+
+/**
+* @function 输入url,返回json
+* @param url {String} 输入的url字符串
+* @return object
+*/
+Url.urlToJson = (url) => {
+ // 递归字符串生成json对象
+ function strToObj(obj, str, value) {
+ if(str.indexOf('.') !== -1) {
+ let key = str.substring(0, str.indexOf('.'));
+ str = str.substring(str.indexOf('.') + 1, str.length);
+ if (obj[key] === undefined) {
+ obj[key] = {};
+ }
+ obj[key] = strToObj(obj[key], str, value);
+ return obj;
+ } else {
+ const decodeValue = decodeURIComponent(value);
+ const decodeKey = decodeURIComponent(str);
+ if (isNaN(decodeValue)) {
+ const decodeValueLower = decodeValue.toLowerCase();
+ switch (decodeValueLower) {
+ case 'true':
+ obj[decodeKey] = true;
+ break;
+ case 'false':
+ obj[decodeKey] = false;
+ break;
+ case 'undefined':
+ obj[decodeKey] = undefined;
+ break;
+ case 'null':
+ obj[decodeKey] = null;
+ break;
+ default:
+ obj[decodeKey] = decodeValue;
+ }
+ } else {
+ obj[decodeKey] = decodeValue-0;
+ }
+ return obj;
+ }
+ }
+ var hash;
+ var myJson = {};
+ var hashes = url.slice(url.indexOf('?') + 1).split('&');
+ for (var i = 0; i < hashes.length; i++) {
+ hash = hashes[i].split('=');
+ try {
+ var hash0 = hash[0].replaceAll('@', '=');
+ hash0 = hash0.replaceAll('$', '&');
+ var hash1 = hash[1].replaceAll('@', '=');
+ hash1 = hash1.replaceAll('$', '&');
+ myJson = strToObj(myJson, hash0, hash1);
+ } catch (e) {
+ myJson = strToObj(myJson, hash[0], hash[1]);
+ }
+ }
+ return myJson;
+}
+
+/**
+ * @function JSON对象转Url字符串
+ * @param param {object | array | string | number | boolean} 传入的JSON对象或者是转换过程中某个键的对应值
+ * @param key {null | string} 转换过程中传入的JSON对象中的某个键
+ * @return {string} 转换后的Url字符串
+ **/
+Url.jsonToUrl = (param, key = null) => {
+ var paramStr = '';
+ if (param instanceof String || param instanceof Number || param instanceof Boolean) {
+ try {
+ var newKey = key.toString().replaceAll('=', '@');
+ newKey = newKey.replaceAll('&', '$');
+ var newParam = param.toString().replaceAll('=', '@')
+ newParam = newParam.replaceAll('&', '$');
+ paramStr += '&' + newKey + '=' + encodeURIComponent(newParam);
+ } catch (e) {
+ //console.log(e);
+ paramStr += '&' + key + '=' + encodeURIComponent(param);
+ }
+ } else {
+ $.each(param, function (i) {
+ var k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i);
+ paramStr += '&' + Url.jsonToUrl(this, k);
+ });
+ }
+ return paramStr.substr(1);
+};
+
+/**
+* @function 获取主页面传递的配置信息
+* @return {object}
+*/
+Url.getConfig = () => {
+ var href = '';
+ try {
+ href = window.location.href.replaceAll('#', '');
+ } catch (e) {
+ //console.log(e);
+ href = window.location.href;
+ }
+ href = href.substring(href.indexOf('?') + 1, href.length);
+ var boardConfig = Url.urlToJson(href);
+ return boardConfig;
+}
+
+/**
+ * @function 更改传入Url字符串中某个参数的值,如果没有则添加该参数
+ * @param url {string} Url字符串
+ * @param arg {string} 参数名
+ * @param argVal {string} 参数名对应值
+ * @return {string} 修改后的Url字符串
+ **/
+Url.changeURLArg = (url, arg, argVal) => {
+ var pattern = arg + '=([^&]*)';
+ var replaceText = arg + '=' + argVal;
+ if (url.match(pattern)) {
+ var tmp = '/(' + arg + '=)([^&]*)/gi';
+ tmp = url.replace(eval(tmp), replaceText);
+ return tmp;
+ } else {
+ if (url.match('[\?]')) {
+ return url + '&' + replaceText;
+ } else {
+ return url + '?' + replaceText;
+ }
+ }
+}
+
+/**
+ * @function 求解某个字符串的CRC32值
+ * @param str {string} 传入的字符串
+ * @param radix {number} 返回文本的进制,默认十进制
+ * @return {string}
+ **/
+Url.CRC32 = (str, radix = 10) => {
+ const Utf8Encode = function (string) {
+ string = string.replace(/\r\n/g, '\n');
+ let text = '';
+ for (let n = 0; n < string.length; n++) {
+ const c = string.charCodeAt(n);
+ if (c < 128) {
+ text += String.fromCharCode(c);
+ } else if ((c > 127) && (c < 2048)) {
+ text += String.fromCharCode((c >> 6) | 192);
+ text += String.fromCharCode((c & 63) | 128);
+ } else {
+ text += String.fromCharCode((c >> 12) | 224);
+ text += String.fromCharCode(((c >> 6) & 63) | 128);
+ text += String.fromCharCode((c & 63) | 128);
+ }
+ }
+ return text;
+ }
+
+ const makeCRCTable = function () {
+ let c;
+ const crcTable = [];
+ for (let n = 0; n < 256; n++) {
+ c = n;
+ for (let k = 0; k < 8; k++) {
+ c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
+ }
+ crcTable[n] = c;
+ }
+ return crcTable;
+ }
+
+ const crcTable = makeCRCTable();
+ const strUTF8 = Utf8Encode(str);
+ let crc = 0 ^ (-1);
+ for (let i = 0; i < strUTF8.length; i++) {
+ crc = (crc >>> 8) ^ crcTable[(crc ^ strUTF8.charCodeAt(i)) & 0xFF];
+ }
+ crc = (crc ^ (-1)) >>> 0;
+ return crc.toString(radix);
+};
+
+/**
+ * @function 获取当前网页的IP地址
+ * @return {string | null}
+ **/
+Url.getIPAddress = () => {
+ const url = window.location.host;
+ const IPList = url.match(/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/g);
+ if (IPList && IPList.length > 0) {
+ return IPList[0];
+ }
+ return null;
+}
+
+/**
+ * @function 在新窗口打开某个网址
+ * @param href {string} 传入的网址
+ * @return {void}
+ **/
+Url.open = (href) => {
+ if (goog.isElectron) {
+ const { shell } = Mixly.require('electron');
+ shell.openExternal(href);
+ } else {
+ window.open(href, '_blank');
+ }
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/user-events.js b/mixly/common/modules/mixly-modules/common/user-events.js
new file mode 100644
index 00000000..7fe0de74
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/user-events.js
@@ -0,0 +1,134 @@
+goog.loadJs('common', () => {
+
+goog.require('Blockly');
+goog.require('Mixly.MFile');
+goog.require('Mixly.Config');
+goog.require('Mixly.Boards');
+goog.require('Mixly.LocalStorage');
+goog.provide('Mixly.UserEvents');
+
+const {
+ MFile,
+ Config,
+ Boards,
+ LocalStorage
+} = Mixly;
+
+const { USER, SOFTWARE } = Config;
+
+class UserEvents {
+ #DEFAULT_DATA = {
+ uid: () => {
+ return LocalStorage.get('Authorization/user_id') ?? '';
+ },
+ mid: () => {
+ return LocalStorage.get('module_id') ?? '';
+ },
+ type: () => {
+ return Boards.getSelectedBoardName();
+ },
+ code: () => {
+ return MFile.getCode();
+ },
+ blocks: () => {
+ return MFile.getMil();
+ },
+ time: () => {
+ return (new Date()).toLocaleString();
+ },
+ fileName: () => {
+ return LocalStorage.get('file_name') ?? '';
+ }
+ };
+
+ #actionFlow_ = [];
+ #prevCode_ = '';
+
+ constructor(workspace) {
+ this.workspace = workspace;
+ this.#addBlocklyEventListener_();
+ }
+
+ #addBlocklyEventListener_() {
+ this.blocklyEventListener = this.workspace.addChangeListener((event) => {
+ if (![
+ Blockly.Events.BLOCK_MOVE,
+ Blockly.Events.BLOCK_DELETE,
+ Blockly.Events.BLOCK_CREATE
+ ].includes(event.type)) {
+ return;
+ }
+ const currentCode = MFile.getCode();
+ if (Blockly.Events.BLOCK_MOVE === event.type && this.#prevCode_ === currentCode) {
+ return;
+ }
+ this.#prevCode_ = currentCode;
+ const recordLine = {};
+ recordLine.blockId = event.blockId;
+ recordLine.currentCode = currentCode;
+ recordLine.currentBlocks = MFile.getMil();
+ recordLine.time = (new Date()).toLocaleString();
+ let actionType = 1;
+ switch (event.type) {
+ case Blockly.Events.BLOCK_MOVE:
+ let block = this.workspace.getBlockById(event.blockId);
+ if (!block) {
+ return;
+ }
+ recordLine.blockType = block.type;
+ actionType = 3;
+ break;
+ case Blockly.Events.BLOCK_DELETE:
+ recordLine.blockType = event.oldJson.type;
+ actionType = 2;
+ break;
+ case Blockly.Events.BLOCK_CREATE:
+ recordLine.blockType = event.json.type;
+ actionType = 1;
+ break;
+ }
+ recordLine.actionType = actionType;
+ this.addFlowItem(recordLine);
+ });
+ }
+
+ addRecord(message) {
+ if (this.flowIsEmpty()) {
+ return;
+ }
+ let data = {};
+ for (let key in this.#DEFAULT_DATA) {
+ data[key] = this.#DEFAULT_DATA[key]();
+ }
+ for (let i in message) {
+ data[i] = message[i];
+ }
+ data.actionFlow = this.getFlowItems();
+ $.post(SOFTWARE?.behaviorRecord?.url, data, () => {
+ this.resetFlow();
+ })
+ .fail(() => {
+ this.resetFlow();
+ });
+ }
+
+ addFlowItem(data) {
+ this.#actionFlow_.push(data);
+ }
+
+ getFlowItems() {
+ return this.#actionFlow_;
+ }
+
+ resetFlow() {
+ this.#actionFlow_ = [];
+ }
+
+ flowIsEmpty() {
+ return this.#actionFlow_.length === 0;
+ }
+}
+
+Mixly.UserEvents = UserEvents;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/user-op-events.js b/mixly/common/modules/mixly-modules/common/user-op-events.js
new file mode 100644
index 00000000..90299300
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/user-op-events.js
@@ -0,0 +1,84 @@
+goog.loadJs('common', () => {
+
+goog.require('Mixly.MFile');
+goog.require('Mixly.Config');
+goog.require('Mixly.Boards');
+goog.provide('Mixly.UserOPEvents');
+
+const {
+ MFile,
+ Boards,
+ Config,
+ Editor
+} = Mixly;
+const { USER } = Config;
+
+class UserOPEvents {
+ #DEFAULT_DATA = {
+ code: () => {
+ return MFile.getCode();
+ },
+ board_name: () => {
+ return Boards.getSelectedBoardName();
+ },
+ time: () => {
+ return (new Date()).toLocaleString();
+ },
+ blocks: () => {
+ return MFile.getMil();
+ },
+ output: () => {
+ const { mainStatusBarTab } = Mixly;
+ const statusBarTerminal = mainStatusBarTab.getStatusBarById('output');
+ return statusBarTerminal.getValue();
+ },
+ id: () => {
+ return USER.visitorId.str32CRC32b;
+ }
+ };
+ #actionArrayRecord = [];
+ constructor() {
+ this.addTimer();
+ }
+
+ sendAll() {
+ let sendPromise = [];
+ let len = this.#actionArrayRecord.length;
+ for (;this.#actionArrayRecord.length;) {
+ let data = this.#actionArrayRecord.shift();
+ sendPromise.push(this.send(data));
+ }
+ Promise.all(sendPromise)
+ .finally(() => {
+ this.addTimer();
+ });
+ }
+
+ send(data) {
+ for (let key in this.#DEFAULT_DATA) {
+ data[key] = this.#DEFAULT_DATA[key]();
+ }
+ return new Promise((resolve, reject) => {
+ $.post('https://cc.mixly.cn/api/behaviorrecord', data, function() {
+ resolve();
+ })
+ .fail(function() {
+ resolve();
+ });
+ });
+ }
+
+ addTimer() {
+ setTimeout(() => this.sendAll(), 10000);
+ }
+
+ addRecord(data) {
+ if (Editor.mainEditor.selected === 'BLOCK') {
+ this.#actionArrayRecord.push(data);
+ }
+ }
+}
+
+Mixly.UserOPEvents = UserOPEvents;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/workspace.js b/mixly/common/modules/mixly-modules/common/workspace.js
new file mode 100644
index 00000000..ececd9fe
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/workspace.js
@@ -0,0 +1,223 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('Mixly.XML');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Drag');
+goog.require('Mixly.DragH');
+goog.require('Mixly.DragV');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.EditorsManager');
+goog.require('Mixly.StatusBarsManager');
+goog.require('Mixly.LeftSideBarsManager');
+goog.require('Mixly.RightSideBarsManager');
+goog.require('Mixly.Component');
+goog.provide('Mixly.Workspace');
+
+const {
+ XML,
+ Env,
+ Msg,
+ Drag,
+ DragH,
+ DragV,
+ HTMLTemplate,
+ EditorsManager,
+ StatusBarsManager,
+ LeftSideBarsManager,
+ RightSideBarsManager,
+ Component
+} = Mixly;
+
+
+class Workspace extends Component {
+ static {
+ HTMLTemplate.add(
+ 'html/workspace.html',
+ new HTMLTemplate(goog.readFileSync(path.join(Env.templatePath, 'html/workspace.html')))
+ );
+
+ this.workspaces = [];
+
+ this.getAll = () => {
+ return this.workspaces;
+ }
+
+ this.add = (workspace) => {
+ this.remove(workspace);
+ this.workspaces.push(workspace);
+ }
+
+ this.remove = (workspace) => {
+ for (let i in this.workspaces) {
+ if (this.workspaces[i].id !== workspace.id) {
+ continue;
+ }
+ this.workspaces.slice(i, 1);
+ }
+ }
+
+ this.getMain = () => {
+ if (this.workspaces.length) {
+ return this.workspaces[0];
+ }
+ return null;
+ }
+ }
+
+ #statusBarsManager_ = null;
+ #editorsManager_ = null;
+ #leftSideBarsManager_ = null;
+ #rightSideBarsManager_ = null;
+ #$dragVLeft_ = null;
+ #$dragVRight_ = null;
+ #$dragH_ = null;
+
+ constructor(element) {
+ super();
+ const $content = $(HTMLTemplate.get('html/workspace.html').render());
+ this.setContent($content);
+ this.mountOn($(element));
+ this.#$dragVLeft_ = $content.find('.drag-v-left');
+ this.#$dragVRight_ = $content.find('.drag-v-right');
+ this.#$dragH_ = $content.find('.drag-h');
+ this.#statusBarsManager_ = new StatusBarsManager($content.find('.statusbars')[0]);
+ this.#statusBarsManager_.add({
+ type: 'terminal',
+ id: 'output',
+ name: Msg.Lang['statusbar.output'],
+ title: Msg.Lang['statusbar.output']
+ });
+ this.#statusBarsManager_.changeTo('output');
+ this.#editorsManager_ = new EditorsManager($content.find('.editors')[0]);
+ this.#leftSideBarsManager_ = new LeftSideBarsManager($content.find('.left-sidebars')[0]);
+ // this.#leftSideBarsManager_.add('local_storage', 'local_storage', '本地');
+ // this.#leftSideBarsManager_.changeTo('local_storage');
+ this.#rightSideBarsManager_ = new RightSideBarsManager($content.find('.right-sidebars')[0]);
+ this.dragH = null;
+ this.dragVLeft = null;
+ this.dragVRight = null;
+ Workspace.add(this);
+ // this.#addEventsListenerForFileTree_();
+ this.#addDragEventsListener_();
+ // this.#addEventsListenerForEditorManager_();
+ this.#addFuncForStatusbarTabs_();
+ }
+
+ #addEventsListenerForFileTree_() {
+ const leftSideBarLocalStorage = this.getLeftSideBarsManager().get('local_storage');
+ const fileTree = leftSideBarLocalStorage.getFileTree();
+ fileTree.bind('afterSelectLeaf', (selected) => {
+ const tabs = this.#editorsManager_.getTabs();
+ tabs.addTab({
+ name: selected[0].text,
+ title: selected[0].id,
+ id: selected[0].id,
+ type: path.extname(selected[0].id),
+ favicon: selected[0].icon,
+ attr: {
+ 'data-link-file': 'true'
+ }
+ });
+ });
+ }
+
+ #addEventsListenerForEditorManager_() {
+ const tabs = this.#editorsManager_.getTabs();
+ tabs.bind('activeTabChange', (event) => {
+ const leftSideBarLocalStorage = this.getLeftSideBarsManager().get('local_storage');
+ const fileTree = leftSideBarLocalStorage.getFileTree();
+ const { tabEl } = event.detail;
+ const tabId = $(tabEl).attr('data-tab-id');
+ fileTree.deselectAll();
+ fileTree.select(tabId);
+ });
+ tabs.bind('tabDestroyed', (event) => {
+ const leftSideBarLocalStorage = this.getLeftSideBarsManager().get('local_storage');
+ const fileTree = leftSideBarLocalStorage.getFileTree();
+ const { tabEl } = event.detail;
+ const tabId = $(tabEl).attr('data-tab-id');
+ fileTree.deselect(tabId);
+ });
+ }
+
+ #addFuncForStatusbarTabs_() {
+ this.#statusBarsManager_.bind('show', () => this.dragH.show(Drag.Extend.NEGATIVE));
+ this.#statusBarsManager_.bind('hide', () => this.dragH.hide(Drag.Extend.NEGATIVE));
+ }
+
+ #addDragEventsListener_() {
+ // 编辑器(上)+状态栏(下)
+ this.dragH = new DragH(this.#$dragH_[0], {
+ min: '50px',
+ startSize: '100%',
+ startExitFullSize: '70%'
+ });
+
+ this.dragH.bind('sizeChanged', () => {
+ this.resize();
+ });
+
+ /*// 侧边栏(左)+[编辑器(上)+状态栏(下)]
+ this.dragVLeft = new DragV(this.#$dragVLeft_[0], {
+ min: '100px',
+ full: [true, false],
+ startSize: '0%',
+ startExitFullSize: '15%'
+ });
+
+ this.dragVLeft.bind('sizeChanged', () => {
+ this.resize();
+ });
+
+ // 侧边栏(右)+[编辑器(上)+状态栏(下)]
+ this.dragVRight = new DragV(this.#$dragVRight_[0], {
+ min: '100px',
+ full: [false, true],
+ startSize: '100%',
+ startExitFullSize: '85%'
+ });
+
+ this.dragVRight.bind('sizeChanged', () => {
+ this.resize();
+ });*/
+ }
+
+ getEditorsManager() {
+ return this.#editorsManager_;
+ }
+
+ getLeftSideBarsManager() {
+ return this.#leftSideBarsManager_;
+ }
+
+ getRightSideBarsManager() {
+ return this.#rightSideBarsManager_;
+ }
+
+ getStatusBarsManager() {
+ return this.#statusBarsManager_;
+ }
+
+ resize() {
+ super.resize();
+ this.getEditorsManager().resize();
+ this.getLeftSideBarsManager().resize();
+ this.getRightSideBarsManager().resize();
+ this.getStatusBarsManager().resize();
+ }
+
+ dispose() {
+ Workspace.remove(this);
+ this.getEditorsManager().dispose();
+ this.getLeftSideBarsManager().dispose();
+ this.getRightSideBarsManager().dispose();
+ this.getStatusBarsManager().dispose();
+ super.dispose();
+ }
+}
+
+Mixly.Workspace = Workspace;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/common/xml.js b/mixly/common/modules/mixly-modules/common/xml.js
new file mode 100644
index 00000000..c43063ed
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/common/xml.js
@@ -0,0 +1,158 @@
+goog.loadJs('common', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.Env');
+goog.require('Mixly.Config');
+goog.require('Mixly.Msg');
+goog.provide('Mixly.XML');
+
+const {
+ Env,
+ Config,
+ Msg,
+ XML
+} = Mixly;
+const { BOARD } = Config;
+const { laytpl } = layui;
+
+
+XML.TEMPLATE_CONFIG = [
+ {
+ type: 'LOADER_DIV',
+ path: '/html/loader-div.html',
+ config: {
+ btnName: Msg.Lang['nav.btn.stop']
+ },
+ appendToBody: true
+ }, {
+ type: 'SELECTOR_DIV',
+ path: '/html/selector-div.html',
+ config: {
+ btn1Name: Msg.Lang['nav.btn.stop'],
+ btn2Name: Msg.Lang['nav.btn.ok']
+ },
+ appendToBody: true
+ }, {
+ type: 'PARSE_MIX_ERROR_DIV',
+ path: '/html/parse-mix-error-div.html',
+ config: {},
+ appendToBody: false
+ }, {
+ type: 'READ_BITMAP_DIV',
+ path: '/html/read-bitmap-div.html',
+ config: {},
+ appendToBody: false
+ }, {
+ type: 'PROGRESS_BAR_DIV',
+ path: '/html/progress-bar-div.html',
+ config: {},
+ appendToBody: false
+ }, {
+ type: 'LIB_MANAGER_DIV',
+ path: '/html/lib-manager-div.html',
+ config: {},
+ appendToBody: false
+ }
+];
+
+XML.TEMPLATE_ENV = {
+ LOADER_DIV: true,
+ SELECTOR_DIV: true,
+ PARSE_MIX_ERROR_DIV: true,
+ READ_BITMAP_DIV: true,
+ PROGRESS_BAR_DIV: goog.isElectron && BOARD?.nav?.setting?.thirdPartyLibrary,
+ LIB_MANAGER_DIV: goog.isElectron && BOARD?.nav?.setting?.thirdPartyLibrary
+};
+
+XML.TEMPLATE_STR = {};
+
+XML.TEMPLATE_STR_RENDER = {};
+
+XML.TEMPLATE_DOM = {};
+
+XML.CATEGORIES_STR = {};
+
+XML.getDom = (xmlStr, config = {}) => {
+ return $(laytpl(xmlStr).render(config));
+}
+
+XML.render = (xmlStr, config = {}) => {
+ const newConfig = {};
+ for (let i in config) {
+ if (typeof config[i] === 'function') {
+ newConfig[i] = config[i]();
+ } else {
+ newConfig[i] = config[i];
+ }
+ }
+ return laytpl(xmlStr).render(newConfig);
+}
+
+XML.convert = function (str, trimEscaped) {
+ var xml = "";
+ var hasComleteAngleBracket = true;
+ var lenStr = str.length;
+ for (var i = 0; i < lenStr; i++) {
+ if (str[i] === "<") {
+ hasComleteAngleBracket = false;
+ } else if (str[i] === ">") {
+ hasComleteAngleBracket = true;
+ }
+
+ if (trimEscaped
+ && !hasComleteAngleBracket
+ && i + 1 < lenStr
+ && str[i] === "\\"
+ && str[i + 1] === '"') {
+ i += 1;
+ }
+
+ if (!trimEscaped
+ && !hasComleteAngleBracket
+ && i > 0
+ && str[i - 1] !== "\\"
+ && str[i] === '"') {
+ xml += "\\";
+ }
+ xml += str[i];
+ }
+ return xml;
+}
+
+for (let i of XML.TEMPLATE_CONFIG) {
+ const { type, config, appendToBody } = i;
+ if (XML.TEMPLATE_ENV[type]) {
+ const xmlStr = goog.readFileSync(path.join(Env.templatePath, i.path));
+ if (xmlStr) {
+ XML.TEMPLATE_STR[type] = xmlStr;
+ XML.TEMPLATE_STR_RENDER[type] = XML.render(xmlStr, config);
+ XML.TEMPLATE_DOM[type] = XML.getDom(xmlStr, config);
+ if (appendToBody)
+ $('body').append(XML.TEMPLATE_DOM[type]);
+ }
+ }
+}
+
+if (layui._typeof(BOARD.board) === 'object') {
+ for (let i in BOARD.board) {
+ const boardConfig = BOARD.board[i];
+ if (layui._typeof(boardConfig) === 'object'
+ && layui._typeof(boardConfig.xmlPath) === 'string') {
+ const categoriesStr = goog.readFileSync(path.join(Env.boardDirPath, boardConfig.xmlPath));
+ if (categoriesStr)
+ XML.CATEGORIES_STR[i] = categoriesStr;
+ }
+ }
+}
+
+window.addEventListener('load', () => {
+ for (let i of XML.TEMPLATE_CONFIG) {
+ const { type, appendToBody } = i;
+ if (XML.TEMPLATE_ENV[type] && XML.TEMPLATE_DOM[type] && appendToBody) {
+ $('body').append(XML.TEMPLATE_DOM[type]);
+ }
+ }
+});
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/deps.json b/mixly/common/modules/mixly-modules/deps.json
new file mode 100644
index 00000000..f81b39e5
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/deps.json
@@ -0,0 +1,1968 @@
+[
+ {
+ "path": "/common/ampy-file-tree.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.FileTree",
+ "Mixly.Electron.AmpyFS",
+ "Mixly.Web.AmpyFS",
+ "Mixly.WebSocket.AmpyFS"
+ ],
+ "provide": [
+ "Mixly.AmpyFileTree"
+ ]
+ },
+ {
+ "path": "/common/ampy.js",
+ "require": [],
+ "provide": [
+ "Mixly.Ampy"
+ ]
+ },
+ {
+ "path": "/common/app.js",
+ "require": [
+ "path",
+ "hotkeys",
+ "Mixly.Url",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Drag",
+ "Mixly.Nav",
+ "Mixly.Menu",
+ "Mixly.Workspace",
+ "Mixly.FooterBar",
+ "Mixly.HTMLTemplate",
+ "Mixly.LayerExt",
+ "Mixly.Debug",
+ "Mixly.Component",
+ "Mixly.EditorMix",
+ "Mixly.Electron.Loader",
+ "Mixly.Electron.FS",
+ "Mixly.Electron.File",
+ "Mixly.Electron.LibManager",
+ "Mixly.Electron.Serial",
+ "Mixly.Electron.ArduShell",
+ "Mixly.Electron.BU",
+ "Mixly.Electron.PythonShell",
+ "Mixly.Web.BU",
+ "Mixly.Web.FS",
+ "Mixly.Web.File",
+ "Mixly.Web.Serial",
+ "Mixly.WebCompiler.ArduShell",
+ "Mixly.WebSocket.File",
+ "Mixly.WebSocket.Serial",
+ "Mixly.WebSocket.ArduShell",
+ "Mixly.WebSocket.BU"
+ ],
+ "provide": [
+ "Mixly.App"
+ ]
+ },
+ {
+ "path": "/common/arduino-cli.js",
+ "require": [
+ "path"
+ ],
+ "provide": [
+ "Mixly.ArduinoCLI"
+ ]
+ },
+ {
+ "path": "/common/arduino-shell.js",
+ "require": [],
+ "provide": [
+ "Mixly.ArduShell"
+ ]
+ },
+ {
+ "path": "/common/board-config-item.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.Storage"
+ ],
+ "provide": [
+ "Mixly.BoardConfigItem"
+ ]
+ },
+ {
+ "path": "/common/boards.js",
+ "require": [
+ "tippy",
+ "layui",
+ "path",
+ "Mixly.LayerExt",
+ "Mixly.Config",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.ToolboxSearcher",
+ "Mixly.MString",
+ "Mixly.Msg",
+ "Mixly.BoardConfigItem",
+ "Mixly.Profile",
+ "Mixly.EditorBlockly",
+ "Mixly.Debug",
+ "Mixly.Nav"
+ ],
+ "provide": [
+ "Mixly.Boards"
+ ]
+ },
+ {
+ "path": "/common/code-formatter.js",
+ "require": [
+ "workerpool"
+ ],
+ "provide": [
+ "Mixly.Registry",
+ "Mixly.CodeFormatter"
+ ]
+ },
+ {
+ "path": "/common/command.js",
+ "require": [
+ "Mixly.Config",
+ "Mixly.MJson",
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.Command"
+ ]
+ },
+ {
+ "path": "/common/component.js",
+ "require": [
+ "Mixly.Events",
+ "Mixly.IdGenerator"
+ ],
+ "provide": [
+ "Mixly.Component"
+ ]
+ },
+ {
+ "path": "/common/config.js",
+ "require": [
+ "path",
+ "FingerprintJS",
+ "Mixly.Url",
+ "Mixly.Env",
+ "Mixly.LocalStorage"
+ ],
+ "provide": [
+ "Mixly.Config"
+ ]
+ },
+ {
+ "path": "/common/context-menu.js",
+ "require": [
+ "$.contextMenu",
+ "Mixly.Menu",
+ "Mixly.Events",
+ "Mixly.Registry"
+ ],
+ "provide": [
+ "Mixly.ContextMenu"
+ ]
+ },
+ {
+ "path": "/common/css-loader.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.CssLoader"
+ ]
+ },
+ {
+ "path": "/common/debug.js",
+ "require": [
+ "Mixly.Config"
+ ],
+ "provide": [
+ "Mixly.Debug"
+ ]
+ },
+ {
+ "path": "/common/drag.js",
+ "require": [
+ "Mixly.Config",
+ "Mixly.Events"
+ ],
+ "provide": [
+ "Mixly.Drag",
+ "Mixly.DragH",
+ "Mixly.DragV"
+ ]
+ },
+ {
+ "path": "/common/dropdown-menu-group.js",
+ "require": [
+ "path",
+ "tippy",
+ "Mixly.Env",
+ "Mixly.Menu",
+ "Mixly.Registry",
+ "Mixly.HTMLTemplate",
+ "Mixly.IdGenerator",
+ "Mixly.ContextMenu",
+ "Mixly.DropdownMenu"
+ ],
+ "provide": [
+ "Mixly.DropdownMenuGroup"
+ ]
+ },
+ {
+ "path": "/common/dropdown-menu.js",
+ "require": [
+ "tippy",
+ "Mixly.IdGenerator",
+ "Mixly.ContextMenu"
+ ],
+ "provide": [
+ "Mixly.DropdownMenu"
+ ]
+ },
+ {
+ "path": "/common/editor-ace.js",
+ "require": [
+ "ace",
+ "ace.ExtLanguageTools",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorAce"
+ ]
+ },
+ {
+ "path": "/common/editor-base.js",
+ "require": [
+ "Mixly.Events",
+ "Mixly.PageBase"
+ ],
+ "provide": [
+ "Mixly.EditorBase"
+ ]
+ },
+ {
+ "path": "/common/editor-blockly.js",
+ "require": [
+ "path",
+ "Blockly",
+ "WorkspaceSearch",
+ "Backpack",
+ "Minimap",
+ "PositionedMinimap",
+ "ContentHighlight",
+ "ZoomToFitControl",
+ "Blockly.FieldGridDropdown",
+ "Blockly.FieldDependentDropdown",
+ "Blockly.FieldSlider",
+ "Blockly.FieldBitmap",
+ "Blockly.FieldColourHsvSliders",
+ "Blockly.FieldDate",
+ "Blockly.FieldAngle",
+ "Blockly.FieldColour",
+ "Blockly.FieldMultilineInput",
+ "Blockly.Screenshot",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.HTMLTemplate",
+ "Mixly.ToolboxSearcher",
+ "Mixly.Debug",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorBlockly"
+ ]
+ },
+ {
+ "path": "/common/editor-code.js",
+ "require": [
+ "Mixly.Config",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.Menu",
+ "Mixly.ContextMenu",
+ "Mixly.IdGenerator",
+ "Mixly.CodeFormatter",
+ "Mixly.MonacoTheme",
+ "Mixly.MonacoTreeSitter",
+ "Mixly.EditorMonaco"
+ ],
+ "provide": [
+ "Mixly.EditorCode"
+ ]
+ },
+ {
+ "path": "/common/editor-md.js",
+ "require": [
+ "marked",
+ "markedKatex",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Drag",
+ "Mixly.DragV",
+ "Mixly.IdGenerator",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBase",
+ "Mixly.EditorCode"
+ ],
+ "provide": [
+ "Mixly.EditorMd"
+ ]
+ },
+ {
+ "path": "/common/editor-mix.js",
+ "require": [
+ "layui",
+ "tippy",
+ "Base64",
+ "Blockly",
+ "Mixly.Drag",
+ "Mixly.DragV",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Debug",
+ "Mixly.Menu",
+ "Mixly.Boards",
+ "Mixly.MJson",
+ "Mixly.LayerExt",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBlockly",
+ "Mixly.EditorCode",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorMix"
+ ]
+ },
+ {
+ "path": "/common/editor-monaco.js",
+ "require": [
+ "Diff",
+ "monaco",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.Events",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorMonaco"
+ ]
+ },
+ {
+ "path": "/common/editor-unknown.js",
+ "require": [
+ "path",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorUnknown"
+ ]
+ },
+ {
+ "path": "/common/editor-welcome.js",
+ "require": [
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorBase"
+ ],
+ "provide": [
+ "Mixly.EditorWelcome"
+ ]
+ },
+ {
+ "path": "/common/editors-manager.js",
+ "require": [
+ "path",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Nav",
+ "Mixly.EditorMix",
+ "Mixly.EditorCode",
+ "Mixly.EditorMd",
+ "Mixly.EditorBlockly",
+ "Mixly.EditorUnknown",
+ "Mixly.EditorWelcome",
+ "Mixly.Registry",
+ "Mixly.Debug",
+ "Mixly.PagesManager",
+ "Mixly.HTMLTemplate",
+ "Mixly.Electron.FS",
+ "Mixly.Web.FS"
+ ],
+ "provide": [
+ "Mixly.EditorsManager"
+ ]
+ },
+ {
+ "path": "/common/env.js",
+ "require": [
+ "path",
+ "Mixly.Url"
+ ],
+ "provide": [
+ "Mixly.Env"
+ ]
+ },
+ {
+ "path": "/common/events.js",
+ "require": [
+ "Mixly.IdGenerator",
+ "Mixly.MArray",
+ "Mixly.Registry",
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.Events"
+ ]
+ },
+ {
+ "path": "/common/file-tree.js",
+ "require": [
+ "XScrollbar",
+ "path",
+ "$.jstree",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Config",
+ "Mixly.Events",
+ "Mixly.Menu",
+ "Mixly.ContextMenu",
+ "Mixly.Registry",
+ "Mixly.IdGenerator",
+ "Mixly.Debug",
+ "Mixly.Component",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.FileTree"
+ ]
+ },
+ {
+ "path": "/common/file.js",
+ "require": [],
+ "provide": [
+ "Mixly.File"
+ ]
+ },
+ {
+ "path": "/common/footerbar.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.Config",
+ "Mixly.Boards",
+ "Mixly.FooterLayer",
+ "Mixly.FooterLayerBoardConfig",
+ "Mixly.FooterLayerMessage",
+ "Mixly.Component",
+ "Mixly.HTMLTemplate",
+ "Mixly.Electron.FooterLayerExample",
+ "Mixly.Web.FooterLayerExample"
+ ],
+ "provide": [
+ "Mixly.FooterBar"
+ ]
+ },
+ {
+ "path": "/common/footerlayer-board-config.js",
+ "require": [
+ "path",
+ "tippy",
+ "Blockly",
+ "layui",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.HTMLTemplate",
+ "Mixly.FooterLayer"
+ ],
+ "provide": [
+ "Mixly.FooterLayerBoardConfig"
+ ]
+ },
+ {
+ "path": "/common/footerlayer-example.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.Title",
+ "Mixly.Env",
+ "Mixly.Debug",
+ "Mixly.Workspace",
+ "Mixly.HTMLTemplate",
+ "Mixly.FooterLayer"
+ ],
+ "provide": [
+ "Mixly.FooterLayerExample"
+ ]
+ },
+ {
+ "path": "/common/footerlayer-message.js",
+ "require": [
+ "shortid",
+ "path",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.FooterLayer"
+ ],
+ "provide": [
+ "Mixly.FooterLayerMessage"
+ ]
+ },
+ {
+ "path": "/common/footerlayer.js",
+ "require": [
+ "tippy",
+ "path",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.Msg"
+ ],
+ "provide": [
+ "Mixly.FooterLayer"
+ ]
+ },
+ {
+ "path": "/common/fs-board-handler.js",
+ "require": [
+ "Mixly.Registry"
+ ],
+ "provide": [
+ "Mixly.FSBoardHandler"
+ ]
+ },
+ {
+ "path": "/common/fs-board.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Boards",
+ "Mixly.FSBoardHandler"
+ ],
+ "provide": [
+ "Mixly.FSBoard"
+ ]
+ },
+ {
+ "path": "/common/fs.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.FS"
+ ]
+ },
+ {
+ "path": "/common/html-template.js",
+ "require": [
+ "Mixly.XML",
+ "Mixly.Registry",
+ "Mixly.IdGenerator"
+ ],
+ "provide": [
+ "Mixly.HTMLTemplate"
+ ]
+ },
+ {
+ "path": "/common/id-generator.js",
+ "require": [
+ "shortid",
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.IdGenerator"
+ ]
+ },
+ {
+ "path": "/common/if-visiable.js",
+ "require": [
+ "ifvisible",
+ "Mixly.Events"
+ ],
+ "provide": [
+ "Mixly.IfVisible"
+ ]
+ },
+ {
+ "path": "/common/js-funcs.js",
+ "require": [
+ "Mixly.Config"
+ ],
+ "provide": [
+ "Mixly.JSFuncs"
+ ]
+ },
+ {
+ "path": "/common/layer-ext.js",
+ "require": [
+ "layui",
+ "Mixly.Config",
+ "Mixly.Msg"
+ ],
+ "provide": [
+ "Mixly.LayerExt"
+ ]
+ },
+ {
+ "path": "/common/layer-firmware.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Layer",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.LayerFirmware"
+ ]
+ },
+ {
+ "path": "/common/layer-new-file.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Layer",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.LayerNewFile"
+ ]
+ },
+ {
+ "path": "/common/layer-progress.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.Layer",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.LayerProgress"
+ ]
+ },
+ {
+ "path": "/common/layer.js",
+ "require": [
+ "dialog",
+ "Mixly.Env",
+ "Mixly.Registry",
+ "Mixly.Component",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.Layer"
+ ]
+ },
+ {
+ "path": "/common/loader.js",
+ "require": [
+ "path",
+ "d3",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Boards",
+ "Mixly.App",
+ "Mixly.Msg",
+ "Mixly.UserEvents",
+ "Mixly.UserOPEvents",
+ "Mixly.JSFuncs",
+ "Mixly.Title",
+ "Mixly.LocalStorage",
+ "Mixly.Storage",
+ "Mixly.Debug",
+ "Mixly.API2",
+ "Mixly.Electron.LibManager",
+ "Mixly.Electron.File",
+ "Mixly.WebCompiler.Loader",
+ "Mixly.WebSocket.Loader"
+ ],
+ "provide": [
+ "Mixly.Loader"
+ ]
+ },
+ {
+ "path": "/common/local-storage.js",
+ "require": [
+ "store",
+ "Mixly.MArray"
+ ],
+ "provide": [
+ "Mixly.LocalStorage"
+ ]
+ },
+ {
+ "path": "/common/marray.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.MArray"
+ ]
+ },
+ {
+ "path": "/common/menu.js",
+ "require": [
+ "Mixly.Env",
+ "Mixly.Debug",
+ "Mixly.Events",
+ "Mixly.IdGenerator",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.Menu"
+ ]
+ },
+ {
+ "path": "/common/mfile.js",
+ "require": [
+ "layui",
+ "Blockly",
+ "Base64",
+ "Mixly.Config",
+ "Mixly.MArray",
+ "Mixly.Boards",
+ "Mixly.XML",
+ "Mixly.LayerExt",
+ "Mixly.Msg"
+ ],
+ "provide": [
+ "Mixly.MFile"
+ ]
+ },
+ {
+ "path": "/common/mixly.js",
+ "require": [],
+ "provide": [
+ "Mixly"
+ ]
+ },
+ {
+ "path": "/common/mixly2-api.js",
+ "require": [
+ "Blockly",
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.API2",
+ "Mixly.Editor"
+ ]
+ },
+ {
+ "path": "/common/mjson.js",
+ "require": [
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.MJson"
+ ]
+ },
+ {
+ "path": "/common/monaco-theme.js",
+ "require": [
+ "monaco",
+ "Mixly.Registry"
+ ],
+ "provide": [
+ "Mixly.MonacoTheme"
+ ]
+ },
+ {
+ "path": "/common/monaco-tree-sitter.js",
+ "require": [
+ "_",
+ "monaco",
+ "Mixly.Registry",
+ "Mixly.MonacoTheme"
+ ],
+ "provide": [
+ "Mixly.MonacoTreeSitter"
+ ]
+ },
+ {
+ "path": "/common/msg.js",
+ "require": [
+ "path",
+ "Mixly.MJson",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Blockly",
+ "Blockly.Lang.ZhHans",
+ "Blockly.Lang.ZhHant",
+ "Blockly.Lang.En"
+ ],
+ "provide": [
+ "Mixly.Msg"
+ ]
+ },
+ {
+ "path": "/common/mstring.js",
+ "require": [
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.MString"
+ ]
+ },
+ {
+ "path": "/common/nav.js",
+ "require": [
+ "layui",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.HTMLTemplate",
+ "Mixly.Component",
+ "Mixly.DropdownMenuGroup"
+ ],
+ "provide": [
+ "Mixly.Nav"
+ ]
+ },
+ {
+ "path": "/common/page-base.js",
+ "require": [
+ "Mixly.Component",
+ "Mixly.Events",
+ "Mixly.Registry"
+ ],
+ "provide": [
+ "Mixly.PageBase"
+ ]
+ },
+ {
+ "path": "/common/pages-manager.js",
+ "require": [
+ "Mixly.PagesTab",
+ "Mixly.Registry",
+ "Mixly.Component"
+ ],
+ "provide": [
+ "Mixly.PagesManager"
+ ]
+ },
+ {
+ "path": "/common/pages-tab.js",
+ "require": [
+ "_",
+ "path",
+ "ChromeTabs",
+ "XScrollbar",
+ "Sortable",
+ "$.contextMenu",
+ "Mixly.IdGenerator",
+ "Mixly.Config",
+ "Mixly.Events",
+ "Mixly.Registry",
+ "Mixly.Component"
+ ],
+ "provide": [
+ "Mixly.PagesTab"
+ ]
+ },
+ {
+ "path": "/common/profile.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.Profile"
+ ]
+ },
+ {
+ "path": "/common/registry.js",
+ "require": [
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.Registry"
+ ]
+ },
+ {
+ "path": "/common/regression.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.Regression"
+ ]
+ },
+ {
+ "path": "/common/script-loader.js",
+ "require": [
+ "Mixly.Env"
+ ],
+ "provide": [
+ "Mixly.ScriptLoader"
+ ]
+ },
+ {
+ "path": "/common/serial.js",
+ "require": [
+ "Mixly.Config",
+ "Mixly.Events",
+ "Mixly.Registry",
+ "Mixly.Nav",
+ "Mixly.Msg"
+ ],
+ "provide": [
+ "Mixly.Serial"
+ ]
+ },
+ {
+ "path": "/common/sidebar-libs.js",
+ "require": [
+ "XScrollbar",
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.HTMLTemplate",
+ "Mixly.Debug",
+ "Mixly.PageBase"
+ ],
+ "provide": [
+ "Mixly.SideBarLibs"
+ ]
+ },
+ {
+ "path": "/common/sidebar-local-storage.js",
+ "require": [
+ "path",
+ "Mprogress",
+ "Mixly.IdGenerator",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.Debug",
+ "Mixly.Menu",
+ "Mixly.PageBase",
+ "Mixly.Electron.FileTree",
+ "Mixly.Web.FileTree",
+ "Mixly.Electron.FS",
+ "Mixly.Web.FS"
+ ],
+ "provide": [
+ "Mixly.SideBarLocalStorage"
+ ]
+ },
+ {
+ "path": "/common/sidebars-manager.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Registry",
+ "Mixly.Events",
+ "Mixly.HTMLTemplate",
+ "Mixly.SideBarLocalStorage",
+ "Mixly.SideBarLibs",
+ "Mixly.PagesManager",
+ "Mixly.IdGenerator"
+ ],
+ "provide": [
+ "Mixly.SideBarsManager",
+ "Mixly.LeftSideBarsManager",
+ "Mixly.RightSideBarsManager"
+ ]
+ },
+ {
+ "path": "/common/socket.js",
+ "require": [
+ "io",
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.Socket"
+ ]
+ },
+ {
+ "path": "/common/statusbar-ampy.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.PageBase",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.HTMLTemplate",
+ "Mixly.DragV",
+ "Mixly.StatusBar",
+ "Mixly.Serial",
+ "Mixly.Menu",
+ "Mixly.AmpyFileTree"
+ ],
+ "provide": [
+ "Mixly.StatusBarAmpy"
+ ]
+ },
+ {
+ "path": "/common/statusbar-fs.js",
+ "require": [
+ "path",
+ "layui",
+ "Mprogress",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.PageBase",
+ "Mixly.HTMLTemplate",
+ "Mixly.Debug",
+ "Mixly.Component",
+ "Mixly.Registry",
+ "Mixly.Serial",
+ "Mixly.FSBoardHandler",
+ "Mixly.Electron.FS",
+ "Mixly.Electron.FSBoard"
+ ],
+ "provide": [
+ "Mixly.StatusBarFS"
+ ]
+ },
+ {
+ "path": "/common/statusbar-libs-code.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.PageBase"
+ ],
+ "provide": [
+ "Mixly.StatusBarLibsCode"
+ ]
+ },
+ {
+ "path": "/common/statusbar-libs-mix.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.Env",
+ "Mixly.HTMLTemplate",
+ "Mixly.PageBase"
+ ],
+ "provide": [
+ "Mixly.StatusBarLibsMix"
+ ]
+ },
+ {
+ "path": "/common/statusbar-libs.js",
+ "require": [
+ "path",
+ "dayjs",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.StatusBar",
+ "Mixly.SideBarsManager",
+ "Mixly.HTMLTemplate",
+ "Mixly.PageBase",
+ "Mixly.StatusBarLibsMix",
+ "Mixly.StatusBarLibsCode"
+ ],
+ "provide": [
+ "Mixly.StatusBarLibs"
+ ]
+ },
+ {
+ "path": "/common/statusbar-output.js",
+ "require": [
+ "Mixly.StatusBar"
+ ],
+ "provide": [
+ "Mixly.StatusBarOutput"
+ ]
+ },
+ {
+ "path": "/common/statusbar-serial-chart.js",
+ "require": [
+ "path",
+ "$.ui",
+ "$.flot",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.PageBase",
+ "Mixly.Regression",
+ "Mixly.HTMLTemplate",
+ "Mixly.Config"
+ ],
+ "provide": [
+ "Mixly.StatusBarSerialChart"
+ ]
+ },
+ {
+ "path": "/common/statusbar-serial-output.js",
+ "require": [
+ "path",
+ "Mixly.PageBase",
+ "Mixly.StatusBar",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.HTMLTemplate"
+ ],
+ "provide": [
+ "Mixly.StatusBarSerialOutput"
+ ]
+ },
+ {
+ "path": "/common/statusbar-serial.js",
+ "require": [
+ "path",
+ "dayjs",
+ "$.select2",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.Config",
+ "Mixly.StatusBar",
+ "Mixly.SideBarsManager",
+ "Mixly.RightSideBarsManager",
+ "Mixly.HTMLTemplate",
+ "Mixly.PageBase",
+ "Mixly.Menu",
+ "Mixly.StatusBarSerialOutput",
+ "Mixly.StatusBarSerialChart",
+ "Mixly.Electron.Serial",
+ "Mixly.Web.Serial",
+ "Mixly.WebSocket.Serial"
+ ],
+ "provide": [
+ "Mixly.StatusBarSerial"
+ ]
+ },
+ {
+ "path": "/common/statusbar.js",
+ "require": [
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.EditorAce",
+ "Mixly.ContextMenu",
+ "Mixly.Menu",
+ "Mixly.Msg",
+ "Mixly.IdGenerator"
+ ],
+ "provide": [
+ "Mixly.StatusBar"
+ ]
+ },
+ {
+ "path": "/common/statusbars-manager.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Registry",
+ "Mixly.Events",
+ "Mixly.HTMLTemplate",
+ "Mixly.StatusBarOutput",
+ "Mixly.StatusBarSerial",
+ "Mixly.StatusBarFS",
+ "Mixly.StatusBarLibs",
+ "Mixly.StatusBarAmpy",
+ "Mixly.PagesManager",
+ "Mixly.DropdownMenu",
+ "Mixly.Menu",
+ "Mixly.IdGenerator",
+ "Mixly.Serial",
+ "Mixly.Config",
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.StatusBarsManager"
+ ]
+ },
+ {
+ "path": "/common/storage.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.LocalStorage",
+ "Mixly.Config"
+ ],
+ "provide": [
+ "Mixly.Storage"
+ ]
+ },
+ {
+ "path": "/common/title.js",
+ "require": [
+ "Mixly.Config",
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.Title"
+ ]
+ },
+ {
+ "path": "/common/toolbox-searcher.js",
+ "require": [
+ "path",
+ "Blockly",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.HTMLTemplate",
+ "Mixly.Debug"
+ ],
+ "provide": [
+ "Mixly.ToolboxSearcher"
+ ]
+ },
+ {
+ "path": "/common/url.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.Url"
+ ]
+ },
+ {
+ "path": "/common/user-events.js",
+ "require": [
+ "Blockly",
+ "Mixly.MFile",
+ "Mixly.Config",
+ "Mixly.Boards",
+ "Mixly.LocalStorage"
+ ],
+ "provide": [
+ "Mixly.UserEvents"
+ ]
+ },
+ {
+ "path": "/common/user-op-events.js",
+ "require": [
+ "Mixly.MFile",
+ "Mixly.Config",
+ "Mixly.Boards"
+ ],
+ "provide": [
+ "Mixly.UserOPEvents"
+ ]
+ },
+ {
+ "path": "/common/workspace.js",
+ "require": [
+ "path",
+ "Mixly.XML",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Drag",
+ "Mixly.DragH",
+ "Mixly.DragV",
+ "Mixly.HTMLTemplate",
+ "Mixly.EditorsManager",
+ "Mixly.StatusBarsManager",
+ "Mixly.LeftSideBarsManager",
+ "Mixly.RightSideBarsManager",
+ "Mixly.Component"
+ ],
+ "provide": [
+ "Mixly.Workspace"
+ ]
+ },
+ {
+ "path": "/common/xml.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.Msg"
+ ],
+ "provide": [
+ "Mixly.XML"
+ ]
+ },
+ {
+ "path": "/electron/ampy-fs.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.FS",
+ "Mixly.Debug",
+ "Mixly.MJson",
+ "Mixly.Electron.Ampy"
+ ],
+ "provide": [
+ "Mixly.Electron.AmpyFS"
+ ]
+ },
+ {
+ "path": "/electron/ampy.js",
+ "require": [
+ "path",
+ "Mustache",
+ "Mixly.Ampy",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Serial",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.Ampy"
+ ]
+ },
+ {
+ "path": "/electron/arduino-shell.js",
+ "require": [
+ "path",
+ "layui",
+ "Blockly",
+ "Mixly.Env",
+ "Mixly.LayerExt",
+ "Mixly.Config",
+ "Mixly.Title",
+ "Mixly.Boards",
+ "Mixly.MFile",
+ "Mixly.MArray",
+ "Mixly.Msg",
+ "Mixly.MString",
+ "Mixly.Workspace",
+ "Mixly.Serial",
+ "Mixly.LayerProgress",
+ "Mixly.Debug",
+ "Mixly.Electron.Shell"
+ ],
+ "provide": [
+ "Mixly.Electron.ArduShell"
+ ]
+ },
+ {
+ "path": "/electron/burn-upload.js",
+ "require": [
+ "layui",
+ "Mixly.Config",
+ "Mixly.LayerExt",
+ "Mixly.Env",
+ "Mixly.Boards",
+ "Mixly.MString",
+ "Mixly.Msg",
+ "Mixly.Workspace",
+ "Mixly.Debug",
+ "Mixly.HTMLTemplate",
+ "Mixly.LayerFirmware",
+ "Mixly.LayerProgress",
+ "Mixly.Electron.Serial"
+ ],
+ "provide": [
+ "Mixly.Electron.BU"
+ ]
+ },
+ {
+ "path": "/electron/cloud-download.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.MJson",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.CloudDownload"
+ ]
+ },
+ {
+ "path": "/electron/electron.js",
+ "require": [
+ "path",
+ "Mixly.Url"
+ ],
+ "provide": [
+ "Mixly.Electron"
+ ]
+ },
+ {
+ "path": "/electron/events.js",
+ "require": [
+ "Mixly.Command",
+ "Mixly.Config",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.Events"
+ ]
+ },
+ {
+ "path": "/electron/file-tree.js",
+ "require": [
+ "path",
+ "Mixly.FileTree",
+ "Mixly.Events",
+ "Mixly.Registry",
+ "Mixly.Debug",
+ "Mixly.Electron.FS"
+ ],
+ "provide": [
+ "Mixly.Electron.FileTree"
+ ]
+ },
+ {
+ "path": "/electron/file.js",
+ "require": [
+ "path",
+ "Blockly",
+ "Mixly.Env",
+ "Mixly.LayerNewFile",
+ "Mixly.Config",
+ "Mixly.Title",
+ "Mixly.MFile",
+ "Mixly.XML",
+ "Mixly.Msg",
+ "Mixly.Workspace",
+ "Mixly.Electron.ArduShell",
+ "Mixly.Electron.BU"
+ ],
+ "provide": [
+ "Mixly.Electron.File"
+ ]
+ },
+ {
+ "path": "/electron/footerlayer-example.js",
+ "require": [
+ "path",
+ "layui",
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.MFile",
+ "Mixly.Title",
+ "Mixly.XML",
+ "Mixly.FooterLayerExample",
+ "Mixly.Electron.File"
+ ],
+ "provide": [
+ "Mixly.Electron.FooterLayerExample"
+ ]
+ },
+ {
+ "path": "/electron/fs-board.js",
+ "require": [
+ "layui",
+ "path",
+ "Mustache",
+ "Mixly.Env",
+ "Mixly.FSBoard",
+ "Mixly.LayerExt",
+ "Mixly.Debug",
+ "Mixly.Msg",
+ "Mixly.Electron.Shell"
+ ],
+ "provide": [
+ "Mixly.Electron.FSBoard"
+ ]
+ },
+ {
+ "path": "/electron/fs.js",
+ "require": [
+ "path",
+ "Mixly.Msg",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.FS"
+ ]
+ },
+ {
+ "path": "/electron/lib-manager.js",
+ "require": [
+ "layui",
+ "Mixly.Env",
+ "Mixly.XML",
+ "Mixly.ScriptLoader",
+ "Mixly.CssLoader",
+ "Mixly.Boards",
+ "Mixly.LayerExt",
+ "Mixly.Config",
+ "Mixly.Msg",
+ "Mixly.Electron.CloudDownload"
+ ],
+ "provide": [
+ "Mixly.Electron.LibManager"
+ ]
+ },
+ {
+ "path": "/electron/loader.js",
+ "require": [
+ "path",
+ "Mixly.Url",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Electron.Serial",
+ "Mixly.Electron.PythonShell",
+ "Mixly.Electron.Events"
+ ],
+ "provide": [
+ "Mixly.Electron.Loader"
+ ]
+ },
+ {
+ "path": "/electron/python-shell.js",
+ "require": [
+ "dayjs.duration",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.Workspace",
+ "Mixly.MString",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.PythonShell"
+ ]
+ },
+ {
+ "path": "/electron/serial.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.MArray",
+ "Mixly.Config",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.Serial"
+ ]
+ },
+ {
+ "path": "/electron/shell.js",
+ "require": [
+ "dayjs.duration",
+ "Mixly.Env",
+ "Mixly.MString",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.Shell"
+ ]
+ },
+ {
+ "path": "/electron/wiki-generator.js",
+ "require": [
+ "path",
+ "Blockly",
+ "Mustache",
+ "Mixly.Env",
+ "Mixly.IdGenerator",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.WikiGenerator"
+ ]
+ },
+ {
+ "path": "/electron/wiki-manager.js",
+ "require": [
+ "path",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Electron"
+ ],
+ "provide": [
+ "Mixly.Electron.WikiManager"
+ ]
+ },
+ {
+ "path": "/web/ampy-fs.js",
+ "require": [
+ "path",
+ "Mixly.FS",
+ "Mixly.Debug",
+ "Mixly.Web.Serial",
+ "Mixly.Web.Ampy"
+ ],
+ "provide": [
+ "Mixly.Web.AmpyFS"
+ ]
+ },
+ {
+ "path": "/web/ampy.js",
+ "require": [
+ "path",
+ "Mustache",
+ "Mixly.Env",
+ "Mixly.Events",
+ "Mixly.Msg",
+ "Mixly.Ampy",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.Ampy"
+ ]
+ },
+ {
+ "path": "/web/bluetooth.js",
+ "require": [
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.Bluetooth"
+ ]
+ },
+ {
+ "path": "/web/burn-upload.js",
+ "require": [
+ "path",
+ "BoardId",
+ "FSWrapper",
+ "DAPWrapper",
+ "PartialFlashing",
+ "esptooljs",
+ "CryptoJS",
+ "JSZip",
+ "Mixly.Env",
+ "Mixly.LayerExt",
+ "Mixly.Config",
+ "Mixly.MFile",
+ "Mixly.Boards",
+ "Mixly.Msg",
+ "Mixly.Workspace",
+ "Mixly.Debug",
+ "Mixly.HTMLTemplate",
+ "Mixly.MString",
+ "Mixly.LayerFirmware",
+ "Mixly.LayerProgress",
+ "Mixly.Web.Serial",
+ "Mixly.Web.Ampy",
+ "Mixly.Web.KFlash",
+ "Mixly.Web.SerialTransport"
+ ],
+ "provide": [
+ "Mixly.Web.BU"
+ ]
+ },
+ {
+ "path": "/web/file-tree.js",
+ "require": [
+ "path",
+ "Mixly.FileTree",
+ "Mixly.Web.FS"
+ ],
+ "provide": [
+ "Mixly.Web.FileTree"
+ ]
+ },
+ {
+ "path": "/web/file.js",
+ "require": [
+ "path",
+ "Blockly",
+ "Mixly.MFile",
+ "Mixly.Title",
+ "Mixly.LayerNewFile",
+ "Mixly.Msg",
+ "Mixly.Workspace"
+ ],
+ "provide": [
+ "Mixly.Web.File"
+ ]
+ },
+ {
+ "path": "/web/footerlayer-example.js",
+ "require": [
+ "path",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.MJson",
+ "Mixly.FooterLayerExample",
+ "Mixly.Boards"
+ ],
+ "provide": [
+ "Mixly.Web.FooterLayerExample"
+ ]
+ },
+ {
+ "path": "/web/fs.js",
+ "require": [
+ "workerpool",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.FS"
+ ]
+ },
+ {
+ "path": "/web/hid.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Config",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.HID"
+ ]
+ },
+ {
+ "path": "/web/kflash.js",
+ "require": [
+ "crc",
+ "pako",
+ "struct",
+ "Mixly.Debug",
+ "Mixly.Events",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.KFlash"
+ ]
+ },
+ {
+ "path": "/web/serial-transport.js",
+ "require": [
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.SerialTransport"
+ ]
+ },
+ {
+ "path": "/web/serial.js",
+ "require": [
+ "path",
+ "Mixly.Config",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Registry",
+ "Mixly.Serial",
+ "Mixly.LayerExt",
+ "Mixly.HTMLTemplate",
+ "Mixly.Web.SerialPort",
+ "Mixly.Web.USB",
+ "Mixly.Web.USBMini",
+ "Mixly.Web.HID"
+ ],
+ "provide": [
+ "Mixly.Web.Serial"
+ ]
+ },
+ {
+ "path": "/web/serialport.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Debug",
+ "Mixly.Config",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.SerialPort"
+ ]
+ },
+ {
+ "path": "/web/usb-mini.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Registry",
+ "Mixly.Config",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.USBMini"
+ ]
+ },
+ {
+ "path": "/web/usb.js",
+ "require": [
+ "DAPjs",
+ "Mixly.Serial",
+ "Mixly.Registry",
+ "Mixly.Config",
+ "Mixly.Web"
+ ],
+ "provide": [
+ "Mixly.Web.USB"
+ ]
+ },
+ {
+ "path": "/web/web.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.Web"
+ ]
+ },
+ {
+ "path": "/web-compiler/arduino-shell.js",
+ "require": [
+ "layui",
+ "AVRUploader",
+ "esptooljs",
+ "CryptoJS",
+ "dayjs.duration",
+ "Mixly.Boards",
+ "Mixly.Debug",
+ "Mixly.LayerExt",
+ "Mixly.Msg",
+ "Mixly.Workspace",
+ "Mixly.LayerProgress",
+ "Mixly.Web.Serial",
+ "Mixly.Web.SerialTransport",
+ "Mixly.WebCompiler"
+ ],
+ "provide": [
+ "Mixly.WebCompiler.ArduShell"
+ ]
+ },
+ {
+ "path": "/web-compiler/loader.js",
+ "require": [
+ "Mixly.Debug",
+ "Mixly.Config",
+ "Mixly.StatusBarsManager",
+ "Mixly.Socket",
+ "Mixly.WebCompiler.ArduShell"
+ ],
+ "provide": [
+ "Mixly.WebCompiler.Loader"
+ ]
+ },
+ {
+ "path": "/web-compiler/web-compiler.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.WebCompiler"
+ ]
+ },
+ {
+ "path": "/web-socket/ampy-fs.js",
+ "require": [
+ "path",
+ "Mixly.Env",
+ "Mixly.FS",
+ "Mixly.Debug",
+ "Mixly.MJson",
+ "Mixly.WebSocket.Ampy"
+ ],
+ "provide": [
+ "Mixly.WebSocket.AmpyFS"
+ ]
+ },
+ {
+ "path": "/web-socket/ampy.js",
+ "require": [
+ "path",
+ "Mustache",
+ "Mixly.Ampy",
+ "Mixly.Env",
+ "Mixly.Serial",
+ "Mixly.WebSocket"
+ ],
+ "provide": [
+ "Mixly.WebSocket.Ampy"
+ ]
+ },
+ {
+ "path": "/web-socket/arduino-shell.js",
+ "require": [
+ "layui",
+ "dayjs.duration",
+ "Mixly.Boards",
+ "Mixly.Debug",
+ "Mixly.LayerExt",
+ "Mixly.Msg",
+ "Mixly.Workspace",
+ "Mixly.LayerProgress",
+ "Mixly.WebSocket.Serial"
+ ],
+ "provide": [
+ "Mixly.WebSocket.ArduShell"
+ ]
+ },
+ {
+ "path": "/web-socket/burn-upload.js",
+ "require": [
+ "path",
+ "layui",
+ "dayjs.duration",
+ "Mixly.Debug",
+ "Mixly.LayerExt",
+ "Mixly.Msg",
+ "Mixly.Env",
+ "Mixly.Config",
+ "Mixly.Workspace",
+ "Mixly.MString",
+ "Mixly.LayerProgress",
+ "Mixly.WebSocket.Serial"
+ ],
+ "provide": [
+ "Mixly.WebSocket.BU"
+ ]
+ },
+ {
+ "path": "/web-socket/file.js",
+ "require": [
+ "Mixly.Web.File"
+ ],
+ "provide": [
+ "Mixly.WebSocket.File"
+ ]
+ },
+ {
+ "path": "/web-socket/loader.js",
+ "require": [
+ "Mixly.Debug",
+ "Mixly.Config",
+ "Mixly.StatusBarsManager",
+ "Mixly.Socket",
+ "Mixly.WebSocket.Serial",
+ "Mixly.WebSocket.ArduShell",
+ "Mixly.WebSocket.BU",
+ "Mixly.WebSocket.Ampy"
+ ],
+ "provide": [
+ "Mixly.WebSocket.Loader"
+ ]
+ },
+ {
+ "path": "/web-socket/serial.js",
+ "require": [
+ "Mixly.Serial",
+ "Mixly.Env",
+ "Mixly.Msg",
+ "Mixly.Debug",
+ "Mixly.Registry",
+ "Mixly.WebSocket"
+ ],
+ "provide": [
+ "Mixly.WebSocket.Serial"
+ ]
+ },
+ {
+ "path": "/web-socket/web-socket.js",
+ "require": [
+ "Mixly"
+ ],
+ "provide": [
+ "Mixly.WebSocket"
+ ]
+ }
+]
diff --git a/mixly/common/modules/mixly-modules/electron/ampy-fs.js b/mixly/common/modules/mixly-modules/electron/ampy-fs.js
new file mode 100644
index 00000000..9c9897b9
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/ampy-fs.js
@@ -0,0 +1,241 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.FS');
+goog.require('Mixly.Debug');
+goog.require('Mixly.MJson');
+goog.require('Mixly.Electron.Ampy');
+goog.provide('Mixly.Electron.AmpyFS');
+
+const {
+ Env,
+ FS,
+ Debug,
+ MJson,
+ Electron
+} = Mixly;
+const { Ampy } = Electron;
+const fs_extra = Mixly.require('fs-extra');
+
+
+class AmpyFS extends FS {
+ #ampy_ = null;
+ #port_ = '';
+ #baud_ = 115200;
+ #decoder_ = new TextDecoder('utf8');
+
+ constructor() {
+ super();
+ this.#ampy_ = new Ampy();
+ }
+
+ async rename(oldPath, newPath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.rename(this.#port_, this.#baud_, oldPath, newPath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createFile(filePath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.mkfile(this.#port_, this.#baud_, filePath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readFile(filePath, encoding = 'utf8') {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.get(this.#port_, this.#baud_, filePath);
+ stdout = output.stdout;
+ if (encoding = 'utf8') {
+ stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout));
+ } else {
+ stdout = this.#ampy_.unhexlify(stdout);
+ }
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async writeFile(filePath, data) {
+ let stdout = '', error = null;
+ try {
+ const startFilePath = path.join(Env.clientPath, 'temp/temp');
+ if (data.constructor.name === 'ArrayBuffer') {
+ data = Buffer.from(data);
+ }
+ await fs_extra.outputFile(startFilePath, data);
+ const output = await this.#ampy_.put(this.#port_, this.#baud_, startFilePath, filePath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isFile(filePath) {
+ /*const [error, stdout] = await this.readDirectory(filePath);
+ if (error) {
+ return true;
+ }
+ return false;*/
+ let error = null;
+ if (path.extname(filePath)) {
+ return [error, true];
+ } else {
+ return [error, false];
+ }
+ }
+
+ async renameFile(oldFilePath, newFilePath) {
+ return this.rename(oldFilePath, newFilePath);
+ }
+
+ async moveFile(oldFilePath, newFilePath) {
+ return this.rename(oldFilePath, newFilePath);
+ }
+
+ async copyFile(oldFilePath, newFilePath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.cpfile(this.#port_, this.#baud_, oldFilePath, newFilePath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async deleteFile(filePath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.rm(this.#port_, this.#baud_, filePath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createDirectory(folderPath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.mkdir(this.#port_, this.#baud_, folderPath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readDirectory(folderPath) {
+ let stdout = [], error = null;
+ try {
+ const output = await this.#ampy_.ls(this.#port_, this.#baud_, folderPath);
+ const dirs = Array.from(new Set(output.stdout.split('\r\n')));
+ for (let i in dirs) {
+ if (!dirs[i]) {
+ continue;
+ }
+ stdout.push(MJson.parse(dirs[i].replaceAll('\'', '"')));
+ }
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isDirectory(folderPath) {
+ /*const [error, stdout] = await this.readDirectory(folderPath);
+ if (error) {
+ return false;
+ }
+ return true;*/
+ let error = null;
+ if (path.extname(folderPath)) {
+ return [error, false];
+ } else {
+ return [error, true];
+ }
+ }
+
+ async isDirectoryEmpty(folderPath) {
+ /*const [error, stdout] = await this.readDirectory(folderPath);
+ let isEmpty = false;
+ if (error || !stdout.length) {
+ isEmpty = true;
+ }*/
+ return [null, false];
+ }
+
+ async renameDirectory(oldFolderPath, newFolderPath) {
+ return this.rename(oldFolderPath, newFolderPath);
+ }
+
+ async moveDirectory(oldFolderPath, newFolderPath) {
+ return this.rename(oldFolderPath, newFolderPath);
+ }
+
+ async copyDirectory(oldFolderPath, newFolderPath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.cpdir(this.#port_, this.#baud_, oldFolderPath, newFolderPath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async deleteDirectory(folderPath) {
+ let stdout = '', error = null;
+ try {
+ const output = await this.#ampy_.rmdir(this.#port_, this.#baud_, folderPath);
+ stdout = output.stdout;
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ setPortName(port) {
+ this.#port_ = port;
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ setBaudRate(baud) {
+ this.#baud_ = baud;
+ }
+
+ getBaudRate() {
+ return this.#baud_;
+ }
+}
+
+Electron.AmpyFS = AmpyFS;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/ampy.js b/mixly/common/modules/mixly-modules/electron/ampy.js
new file mode 100644
index 00000000..75cad19c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/ampy.js
@@ -0,0 +1,131 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mustache');
+goog.require('Mixly.Ampy');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Serial');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.Ampy');
+
+const {
+ Ampy,
+ Env,
+ Msg,
+ Serial,
+ Electron
+} = Mixly;
+
+const util = Mixly.require('node:util');
+const child_process = Mixly.require('node:child_process');
+
+class AmpyExt extends Ampy {
+ static {
+ this.TEMPLATE = {
+ ls: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 ls "{{&folderPath}}"',
+ get: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 get "{{&filePath}}"',
+ mkdir: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 mkdir "{{&folderPath}}"',
+ mkfile: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 mkfile "{{&filePath}}"',
+ isdir: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 isdir "{{&folderPath}}"',
+ isfile: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 isfile "{{&filePath}}"',
+ put: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 put "{{&startPath}}" "{{&endPath}}"',
+ rm: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 rm "{{&filePath}}"',
+ rmdir: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 rmdir "{{&folderPath}}"',
+ rename: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 rename "{{&oldPath}}" "{{&newPath}}"',
+ cpdir: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 cpdir "{{&startPath}}" "{{&endPath}}"',
+ cpfile: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 cpfile "{{&startPath}}" "{{&endPath}}"',
+ run: '{{&y}} -p {{&port}} -b {{&baud}} -i 0 run "{{&filePath}}"'
+ }
+
+ this.AMPY_PATH = path.join(Env.srcDirPath, './tools/python/ampy_main.py');
+
+ this.AMPY_TEMPLATE = Mustache.render('"{{&python3}}" "{{&y}}"', {
+ python3: Env.python3Path,
+ ampy: this.AMPY_PATH
+ });
+ }
+
+ #exec_ = util.promisify(child_process.exec);
+
+ constructor() {
+ super();
+ }
+
+ async ls(port, baud, folderPath) {
+ return this.exec(port, this.render('ls', { port, baud, folderPath }));
+ }
+
+ async get(port, baud, filePath, encoding = 'utf8') {
+ return this.exec(port, this.render('get', { port, baud, filePath, encoding }));
+ }
+
+ async mkdir(port, baud, folderPath) {
+ return this.exec(port, this.render('mkdir', { port, baud, folderPath }));
+ }
+
+ async mkfile(port, baud, filePath) {
+ return this.exec(port, this.render('mkfile', { port, baud, filePath }));
+ }
+
+ async isdir(port, baud, folderPath) {
+ return this.exec(port, this.render('isdir', { port, baud, folderPath }));
+ }
+
+ async isfile(port, baud, filePath) {
+ return this.exec(port, this.render('isfile', { port, baud, filePath }));
+ }
+
+ async put(port, baud, startPath, endPath) {
+ return this.exec(port, this.render('put', { port, baud, startPath, endPath }));
+ }
+
+ async rm(port, baud, filePath) {
+ return this.exec(port, this.render('rm', { port, baud, filePath }));
+ }
+
+ async rmdir(port, baud, folderPath) {
+ return this.exec(port, this.render('rmdir', { port, baud, folderPath }));
+ }
+
+ async rename(port, baud, oldPath, newPath) {
+ return this.exec(port, this.render('rename', { port, baud, oldPath, newPath }));
+ }
+
+ async cpdir(port, baud, startPath, endPath) {
+ return this.exec(port, this.render('cpdir', { port, baud, startPath, endPath }));
+ }
+
+ async cpfile(port, baud, startPath, endPath) {
+ return this.exec(port, this.render('cpfile', { port, baud, startPath, endPath }));
+ }
+
+ async run(port, baud, filePath) {
+ return this.exec(port, this.render('run', { port, baud, filePath }));
+ }
+
+ render(templateName, args) {
+ return Mustache.render(AmpyExt.TEMPLATE[templateName], {
+ ...args,
+ ampy: AmpyExt.AMPY_TEMPLATE
+ });
+ }
+
+ async exec(port, command) {
+ const portsName = Serial.getCurrentPortsName();
+ if (!portsName.includes(port)) {
+ throw new Error(Msg.Lang['statusbar.serial.noPort']);
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+ return this.#exec_(command);
+ }
+}
+
+Electron.Ampy = AmpyExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/arduino-shell.js b/mixly/common/modules/mixly-modules/electron/arduino-shell.js
new file mode 100644
index 00000000..0beb7c9c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/arduino-shell.js
@@ -0,0 +1,598 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Blockly');
+goog.require('Mixly.Env');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Config');
+goog.require('Mixly.Title');
+goog.require('Mixly.Boards');
+goog.require('Mixly.MFile');
+goog.require('Mixly.MArray');
+goog.require('Mixly.Msg');
+goog.require('Mixly.MString');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.Serial');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Electron.Shell');
+goog.provide('Mixly.Electron.ArduShell');
+
+const {
+ Env,
+ Electron,
+ LayerExt,
+ Config,
+ Title,
+ Boards,
+ MFile,
+ MArray,
+ Msg,
+ MString,
+ Workspace,
+ Serial,
+ LayerProgress,
+ Debug
+} = Mixly;
+
+const { BOARD, SOFTWARE, USER } = Config;
+
+const fs = Mixly.require('node:fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const child_process = Mixly.require('node:child_process');
+const iconv_lite = Mixly.require('iconv-lite');
+
+const {
+ ArduShell,
+ Shell
+} = Electron;
+
+ArduShell.DEFAULT_CONFIG = goog.readJsonSync(path.join(Env.templatePath, 'json/arduino-cli-config.json'));
+ArduShell.ERROR_ENCODING = Env.currentPlatform == 'win32' ? 'cp936' : 'utf-8';
+ArduShell.binFilePath = '';
+ArduShell.shellPath = null;
+ArduShell.shell = null;
+ArduShell.compiling = false;
+ArduShell.uploading = false;
+ArduShell.killing = false;
+ArduShell.progressLayer = new LayerProgress({
+ width: 200,
+ cancelValue: Msg.Lang['nav.btn.stop'],
+ cancel: () => {
+ if (ArduShell.killing) {
+ return false;
+ }
+ ArduShell.progressLayer.title(`${Msg.Lang['shell.aborting']}...`);
+ ArduShell.killing = true;
+ ArduShell.cancel();
+ return false;
+ },
+ cancelDisplay: false
+});
+
+
+ArduShell.updateShellPath = () => {
+ let shellPath = path.join(Env.clientPath, 'arduino-cli');
+ if (Env.currentPlatform === 'win32')
+ shellPath = path.join(shellPath, 'arduino-cli.exe');
+ else
+ shellPath = path.join(shellPath, 'arduino-cli');
+ if (!fs_plus.isFileSync(shellPath)) {
+ const { defaultPath = {} } = SOFTWARE;
+ if (typeof defaultPath[Env.currentPlatform] === 'object') {
+ let defaultShellPath = defaultPath[Env.currentPlatform].arduinoCli ?? '';
+ defaultShellPath = path.join(Env.clientPath, defaultShellPath);
+ if (fs_plus.isFileSync(defaultShellPath))
+ shellPath = defaultShellPath;
+ else
+ shellPath = null;
+ }
+ }
+ ArduShell.shellPath = shellPath;
+}
+
+ArduShell.updateConfig = (config) => {
+ if (!ArduShell.shellPath) return;
+ const configPath = path.join(ArduShell.shellPath, '../arduino-cli.json');
+ let nowConfig = fs_extra.readJsonSync(configPath, { throws: false }) ?? { ...ArduShell.DEFAULT_CONFIG };
+ if (typeof config === 'object') {
+ if (MArray.equals(nowConfig.directories, config.directories))
+ return;
+ nowConfig = {
+ ...nowConfig,
+ ...config
+ };
+ fs_extra.outputJson(configPath, nowConfig, {
+ spaces: ' '
+ })
+ .then(() => {
+ console.log('arduino-cli.json已更新');
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ }
+}
+
+ArduShell.init = () => {
+ ArduShell.updateShellPath();
+ if (!ArduShell.shellPath) return;
+ ArduShell.updateConfig({
+ directories: {
+ data: path.join(ArduShell.shellPath, '../Arduino15'),
+ downloads: path.join(ArduShell.shellPath, '../staging'),
+ user: path.join(ArduShell.shellPath, '../Arduino')
+ }
+ });
+}
+
+ArduShell.init();
+
+ArduShell.burn = () => {
+ Mixly.Electron.BU.initBurn();
+}
+
+/**
+* @function 编译
+* @description 开始一个编译过程
+* @return void
+*/
+ArduShell.initCompile = () => {
+ ArduShell.compile(() => {});
+}
+
+/**
+* @function 编译
+* @description 开始一个编译过程
+* @return void
+*/
+ArduShell.compile = (doFunc = () => {}) => {
+ if (!ArduShell.shellPath) {
+ ArduShell.shellPath = '';
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue('');
+ mainStatusBarTabs.changeTo("output");
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ ArduShell.compiling = true;
+ ArduShell.uploading = false;
+ ArduShell.killing = false;
+ const boardType = Boards.getSelectedBoardCommandParam();
+ mainStatusBarTabs.show();
+ ArduShell.progressLayer.title(`${Msg.Lang['shell.compiling']}...`);
+ ArduShell.progressLayer.show();
+ statusBarTerminal.setValue(Msg.Lang['shell.compiling'] + "...\n");
+ let myLibPath = path.join(Env.boardDirPath, "/libraries/myLib/");
+ if (fs_plus.isDirectorySync(myLibPath))
+ myLibPath += '\",\"';
+ else
+ myLibPath = '';
+ const thirdPartyPath = path.join(Env.boardDirPath, 'libraries/ThirdParty');
+ if (fs_plus.isDirectorySync(thirdPartyPath)) {
+ const libList = fs.readdirSync(thirdPartyPath);
+ for (let libName of libList) {
+ const libPath = path.join(thirdPartyPath, libName, 'libraries');
+ if (!fs_plus.isDirectorySync(libPath)) continue;
+ myLibPath += libPath + ',';
+ }
+ }
+ const configPath = path.join(ArduShell.shellPath, '../arduino-cli.json'),
+ defaultLibPath = path.join(ArduShell.shellPath, '../libraries'),
+ buildPath = path.join(Env.clientPath, './mixlyBuild'),
+ buildCachePath = path.join(Env.clientPath, './mixlyBuildCache'),
+ codePath = path.join(Env.clientPath, './testArduino/testArduino.ino');
+ const cmdStr = '\"'
+ + ArduShell.shellPath
+ + '\" compile -b '
+ + boardType
+ + ' --config-file \"'
+ + configPath
+ + '\" --build-cache-path \"' + buildCachePath + '\" --verbose --libraries \"'
+ + myLibPath
+ + defaultLibPath
+ + '\" --build-path \"'
+ + buildPath
+ + '\" \"'
+ + codePath
+ + '\" --no-color';
+ ArduShell.runCmd('compile', cmdStr, code, doFunc);
+}
+
+/**
+* @function 初始化上传
+* @description 关闭已打开的串口,获取当前所连接的设备数,然后开始上传程序
+* @return void
+*/
+ArduShell.initUpload = () => {
+ const { mainStatusBarTabs } = Mixly;
+ ArduShell.compiling = false;
+ ArduShell.uploading = true;
+ ArduShell.killing = false;
+ const boardType = Boards.getSelectedBoardCommandParam();
+ const uploadType = Boards.getSelectedBoardConfigParam('upload_method');
+ let port = Serial.getSelectedPortName();
+ switch (uploadType) {
+ case 'STLinkMethod':
+ case 'jlinkMethod':
+ case 'usb':
+ port = 'None';
+ break;
+ }
+ if (port) {
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ if (statusBarSerial) {
+ statusBarSerial.close().finally(() => {
+ ArduShell.upload(boardType, port);
+ });
+ } else {
+ ArduShell.upload(boardType, port);
+ }
+ } else {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ }
+}
+
+/**
+* @function 上传程序
+* @description 通过所选择串口号开始一个上传过程
+* @return void
+*/
+ArduShell.upload = (boardType, port) => {
+ if (!ArduShell.shellPath) {
+ ArduShell.shellPath = '';
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue('');
+ mainStatusBarTabs.changeTo('output');
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ ArduShell.progressLayer.title(`${Msg.Lang['shell.uploading']}...`);
+ mainStatusBarTabs.show();
+ ArduShell.progressLayer.title(`${Msg.Lang['shell.uploading']}...`);
+ ArduShell.progressLayer.show();
+ statusBarTerminal.setValue(Msg.Lang['shell.uploading'] + "...\n");
+ const configPath = path.join(ArduShell.shellPath, '../arduino-cli.json'),
+ defaultLibPath = path.join(ArduShell.shellPath, '../libraries'),
+ buildPath = path.join(Env.clientPath, './mixlyBuild'),
+ buildCachePath = path.join(Env.clientPath, './mixlyBuildCache'),
+ codePath = path.join(Env.clientPath, './testArduino/testArduino.ino');
+ let cmdStr = '';
+ if (ArduShell.binFilePath !== '') {
+ cmdStr = '\"'
+ + ArduShell.shellPath
+ + '\" -b '
+ + boardType
+ + ' upload -p '
+ + port
+ + ' --config-file \"'
+ + configPath
+ + '\" --verbose '
+ + '-i \"' + ArduShell.binFilePath + '\" --no-color';
+ ArduShell.binFilePath = '';
+ } else {
+ let myLibPath = path.join(Env.boardDirPath, "/libraries/myLib/");
+ if (fs_plus.isDirectorySync(myLibPath)) {
+ myLibPath += '\",\"';
+ } else {
+ myLibPath = '';
+ }
+ const thirdPartyPath = path.join(Env.boardDirPath, 'libraries/ThirdParty');
+ if (fs_plus.isDirectorySync(thirdPartyPath)) {
+ const libList = fs.readdirSync(thirdPartyPath);
+ for (let libName of libList) {
+ const libPath = path.join(thirdPartyPath, libName, 'libraries');
+ if (!fs_plus.isDirectorySync(libPath)) continue;
+ myLibPath += libPath + ',';
+ }
+ }
+ cmdStr = '\"'
+ + ArduShell.shellPath
+ + '\" compile -b '
+ + boardType
+ + ' --upload -p '
+ + port
+ + ' --config-file \"'
+ + configPath
+ + '\" --build-cache-path \"' + buildCachePath + '\" --verbose --libraries \"'
+ + myLibPath
+ + defaultLibPath
+ + '\" --build-path \"'
+ + buildPath
+ + '\" \"'
+ + codePath
+ + '\" --no-color';
+ }
+ ArduShell.runCmd('upload', cmdStr, code, () => {
+ if (!Serial.portIsLegal(port)) {
+ return;
+ }
+ mainStatusBarTabs.add('serial', port);
+ mainStatusBarTabs.changeTo(port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.open()
+ .then(() => {
+ const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
+ if (!baudRates?.length) {
+ statusBarSerial.setBaudRate(9600);
+ } else {
+ statusBarSerial.setBaudRate(baudRates[0] - 0);
+ }
+ });
+
+ });
+}
+
+/**
+* @function 取消编译或上传
+* @description 取消正在执行的编译或上传过程
+* @return void
+*/
+ArduShell.cancel = function () {
+ ArduShell.shell && ArduShell.shell.kill();
+ ArduShell.shell = null;
+}
+
+/**
+* @function 检测文件扩展名
+* @description 检测文件扩展名是否在扩展名列表内
+* @param fileName {String} 文件名
+* @param extensionList {Array} 扩展名列表
+* @return Boolean
+*/
+ArduShell.checkFileNameExtension = (fileName, extensionList) => {
+ if (!fileName) return false;
+ let fileNameToLowerCase = fileName;
+ let fileNameLen = fileNameToLowerCase.length;
+ let fileType = fileNameToLowerCase.substring(fileNameToLowerCase.lastIndexOf("."), fileNameLen);
+ if (extensionList.includes(fileType)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+* @function 检测文件扩展名
+* @description 检测文件扩展名是否为.c/.cpp或.h/.hpp
+* @param fileName {String} 文件名
+* @return Boolean
+*/
+ArduShell.isCppOrHpp = (fileName) => {
+ return ArduShell.checkFileNameExtension(fileName, [".c", ".cpp", ".h", ".hpp"])
+}
+
+/**
+* @function 检测文件扩展名
+* @description 检测文件扩展名是否为.mix/.xml或.ino
+* @param fileName {String} 文件名
+* @return Boolean
+*/
+ArduShell.isMixOrIno = (fileName) => {
+ return ArduShell.checkFileNameExtension(fileName, [".mix", ".xml", ".ino"]);
+}
+
+/**
+* @function 删除给定文件夹下文件
+* @description 删除给定文件夹下.c/.cpp和.h/.hpp文件
+* @param dir {String} 文件夹路径
+* @return void
+*/
+ArduShell.clearDirCppAndHppFiles = (dir) => {
+ if (fs_plus.isDirectorySync(dir)) {
+ let libDir = fs.readdirSync(dir);
+ for (let i = 0; i < libDir.length; i++) {
+ if (ArduShell.isCppOrHpp(libDir[i])) {
+ const nowPath = path.join(dir, libDir[i]);
+ fs.unlinkSync(nowPath);
+ }
+ }
+ }
+}
+
+/**
+* @function 拷贝文件
+* @description 拷贝给定文件夹下.c/.cpp和.h/.hpp文件到目标目录
+* @param oldDir {String} 起始文件夹路径
+* @param newDir {String} 目标文件夹路径
+* @return void
+*/
+ArduShell.copyHppAndCppFiles = (oldDir, newDir) => {
+ if (fs_plus.isDirectorySync(oldDir) && fs_plus.isDirectorySync(newDir)) {
+ let oldLibDir = fs.readdirSync(oldDir);
+ for (let i = 0; i < oldLibDir.length; i++) {
+ if (ArduShell.isCppOrHpp(oldLibDir[i])) {
+ const oldPath = path.join(oldDir, oldLibDir[i]);
+ const newPath = path.join(newDir, oldLibDir[i]);
+ try {
+ fs.copyFileSync(oldPath, newPath);
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ }
+ }
+}
+
+/**
+* @function 写库文件
+* @description 将库文件数据写入本地
+* @param inPath {string} 需要写入库文件的目录
+* @return void
+*/
+ArduShell.writeLibFiles = (inPath) => {
+ return new Promise((resolve, reject) => {
+ const promiseList = [];
+ for (let name in Blockly.Arduino.libs_) {
+ const data = Blockly.Arduino.libs_[name];
+ const codePath = path.join(inPath, name + '.h');
+ promiseList.push(
+ new Promise((childResolve, childReject) => {
+ fs_extra.outputFile(codePath, data)
+ .finally(() => {
+ childResolve();
+ });
+ }
+ ));
+ }
+ if (!promiseList.length) {
+ resolve();
+ return;
+ }
+ Promise.all(promiseList)
+ .finally(() => {
+ resolve();
+ });
+ });
+}
+
+/**
+* @function 运行一个cmd命令
+* @description 输入编译或上传的cmd命令
+* @param cmd {String} 输入的cmd命令
+* @return void
+*/
+ArduShell.runCmd = (type, cmd, code, sucFunc) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const testArduinoDirPath = path.join(Env.clientPath, 'testArduino');
+ const codePath = path.join(testArduinoDirPath, 'testArduino.ino');
+ const nowFilePath = Title.getFilePath();
+ ArduShell.clearDirCppAndHppFiles(testArduinoDirPath);
+ if (USER.compileCAndH === 'yes' && fs_plus.isFileSync(nowFilePath) && ArduShell.isMixOrIno(nowFilePath)) {
+ const nowDirPath = path.dirname(nowFilePath);
+ ArduShell.copyHppAndCppFiles(nowDirPath, testArduinoDirPath);
+ }
+ ArduShell.writeLibFiles(testArduinoDirPath)
+ .then(() => {
+ return fs_extra.outputFile(codePath, code);
+ })
+ .then(() => {
+ ArduShell.shell = new Shell(cmd);
+ return ArduShell.shell.exec(cmd);
+ })
+ .then((info) => {
+ ArduShell.progressLayer.hide();
+ let message = '';
+ if (info.code) {
+ message = (type === 'compile' ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
+ statusBarTerminal.addValue("\n==" + message + "==\n");
+ } else {
+ message = (type === 'compile' ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
+ statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${info.time})==\n`);
+ sucFunc();
+ }
+ layer.msg(message, { time: 1000 });
+ })
+ .catch((error) => {
+ ArduShell.progressLayer.hide();
+ Debug.error(error);
+ })
+ .finally(() => {
+ statusBarTerminal.scrollToBottom();
+ });
+}
+
+ArduShell.saveBinOrHex = function (writePath) {
+ ArduShell.writeFile(Env.clientPath + "/mixlyBuild", writePath);
+}
+
+ArduShell.writeFile = function (readPath, writePath) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ ArduShell.compile(function () {
+ window.setTimeout(function () {
+ ArduShell.progressLayer.title(`${Msg.Lang['shell.saving']}...`);
+ ArduShell.progressLayer.show();
+ window.setTimeout(function () {
+ try {
+ readPath = readPath.replace(/\\/g, "/");
+ writePath = writePath.replace(/\\/g, "/");
+ } catch (error) {
+ Debug.error(error);
+ }
+ try {
+ let writeDirPath = writePath.substring(0, writePath.lastIndexOf("."));
+ let writeFileName = writePath.substring(writePath.lastIndexOf("/") + 1, writePath.lastIndexOf("."));
+ let writeFileType = writePath.substring(writePath.lastIndexOf(".") + 1);
+ if (!fs.existsSync(writeDirPath)) {
+ fs.mkdirSync(writeDirPath);
+ }
+ if (fs.existsSync(writePath)) {
+ fs.unlinkSync(writePath);
+ }
+ let readBinFilePath = readPath + "/testArduino.ino." + writeFileType;
+ let binFileData = fs.readFileSync(readBinFilePath);
+ fs.writeFileSync(writePath, binFileData);
+ let binFileType = [
+ ".eep",
+ ".hex",
+ ".with_bootloader.bin",
+ ".with_bootloader.hex",
+ ".bin",
+ ".elf",
+ ".map",
+ ".partitions.bin",
+ ".bootloader.bin"
+ ]
+ for (let i = 0; i < binFileType.length; i++) {
+ let readFilePath = readPath + "/testArduino.ino" + binFileType[i];
+ let writeFilePath = writeDirPath + "/" + writeFileName + binFileType[i];
+ if (fs.existsSync(readFilePath)) {
+ let binData = fs.readFileSync(readFilePath);
+ fs.writeFileSync(writeFilePath, binData);
+ }
+ }
+ layer.msg(Msg.Lang['shell.saveSucc'], {
+ time: 1000
+ });
+ } catch (error) {
+ Debug.error(error);
+ statusBarTerminal.addValue(e + "\n");
+ }
+ ArduShell.progressLayer.hide();
+ }, 500);
+ }, 1000);
+ });
+}
+
+ArduShell.showUploadBox = function (filePath) {
+ const dirPath = path.dirname(filePath);
+ const fileName = path.basename(filePath, path.extname(filePath));
+ if (fs_plus.isDirectorySync(path.join(dirPath, fileName))) {
+ filePath = path.join(dirPath, fileName, path.basename(filePath));
+ }
+ const layerNum = layer.msg(Msg.Lang['shell.uploadWithFileInfo'], {
+ time: -1,
+ btn: [Msg.Lang['nav.btn.stop'], Msg.Lang['nav.btn.upload']],
+ shade: LayerExt.SHADE_ALL,
+ btnAlign: 'c',
+ yes: function () {
+ layer.close(layerNum);
+ ArduShell.binFilePath = '';
+ },
+ btn2: function () {
+ layer.close(layerNum);
+ ArduShell.uploadWithBinOrHex(filePath);
+ },
+ end: function () {
+ ArduShell.binFilePath = '';
+ }
+ });
+}
+
+ArduShell.uploadWithBinOrHex = function (filePath) {
+ layer.closeAll();
+ ArduShell.binFilePath = filePath;
+ ArduShell.initUpload();
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/burn-upload.js b/mixly/common/modules/mixly-modules/electron/burn-upload.js
new file mode 100644
index 00000000..12f7dfec
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/burn-upload.js
@@ -0,0 +1,701 @@
+goog.loadJs('electron', () => {
+
+goog.require('layui');
+goog.require('Mixly.Config');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Env');
+goog.require('Mixly.Boards');
+goog.require('Mixly.MString');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.Debug');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.LayerFirmware');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.Electron.Serial');
+goog.provide('Mixly.Electron.BU');
+
+const {
+ Electron,
+ Config,
+ LayerExt,
+ Env,
+ Boards,
+ MString,
+ Msg,
+ Workspace,
+ Serial,
+ Debug,
+ HTMLTemplate,
+ LayerFirmware,
+ LayerProgress
+} = Mixly;
+
+const { BU } = Electron;
+const { BOARD, SELECTED_BOARD, USER } = Config;
+
+var downloadShell = null;
+
+const { form } = layui;
+
+const fs = Mixly.require('node:fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const path = Mixly.require('node:path');
+const child_process = Mixly.require('node:child_process');
+const iconv_lite = Mixly.require('iconv-lite');
+const os = Mixly.require('node:os');
+
+BU.uploading = false;
+BU.burning = false;
+BU.shell = null;
+BU.firmwareLayer = new LayerFirmware({
+ width: 400,
+ title: Msg.Lang['nav.btn.burn'],
+ cancelValue: false,
+ cancel: false,
+ cancelDisplay: false
+});
+BU.firmwareLayer.bind('burn', (info) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue('');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ BU.burning = true;
+ BU.uploading = false;
+ const port = Serial.getSelectedPortName();
+ BU.burnWithPort(port, info);
+});
+BU.killing = false;
+BU.progressLayer = new LayerProgress({
+ width: 200,
+ cancelValue: Msg.Lang['nav.btn.stop'],
+ cancel: () => {
+ if (BU.killing) {
+ return false;
+ }
+ BU.progressLayer.title(`${Msg.Lang['shell.aborting']}...`);
+ BU.killing = true;
+ BU.cancel();
+ return false;
+ },
+ cancelDisplay: false
+});
+
+/**
+ * @function 根据传入的stdout判断磁盘数量并选择对应操作
+ * @param type {string} 值为'burn' | 'upload'
+ * @param stdout {string} 磁盘名称字符串,形如'G:K:F:'
+ * @param startPath {string} 需要拷贝的文件路径
+ * @return {void}
+ **/
+BU.checkNumOfDisks = function (type, stdout, startPath) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ let wmicResult = stdout;
+ wmicResult = wmicResult.replace(/\s+/g, "");
+ wmicResult = wmicResult.replace("DeviceID", "");
+ // wmicResult = 'G:K:F:';
+ let result = wmicResult.split(':');
+ let pathAdd = (Env.currentPlatform === "win32") ? ':' : '';
+ if (stdout.indexOf(":") != stdout.lastIndexOf(":")) {
+ let form = layui.form;
+ let devicesName = $('#mixly-selector-type');
+ let oldDevice = $('#mixly-selector-type option:selected').val();
+ devicesName.empty();
+ for (let i = 0; i < result.length; i++) {
+ if (result[i]) {
+ if (oldDevice == result[i] + pathAdd) {
+ devicesName.append('
' + result[i] + pathAdd + ' ');
+ } else {
+ devicesName.append('
' + result[i] + pathAdd + ' ');
+ }
+ }
+ }
+ form.render();
+ let initBtnClicked = false;
+ const layerNum = layer.open({
+ type: 1,
+ id: "serial-select",
+ title: Msg.Lang['shell.tooManyDevicesInfo'],
+ area: ['350px', '150px'],
+ content: $('#mixly-selector-div'),
+ shade: LayerExt.SHADE_ALL,
+ resize: false,
+ closeBtn: 0,
+ success: function (layero) {
+ $('#serial-select').css('height', '195px');
+ $(".layui-layer-page").css("z-index","198910151");
+ $("#mixly-selector-btn1").off("click").click(() => {
+ layer.close(layerNum);
+ BU.cancel();
+ });
+ $("#mixly-selector-btn2").off("click").click(() => {
+ layer.close(layerNum);
+ initBtnClicked = true;
+ });
+ },
+ end: function () {
+ $('#mixly-selector-div').css('display', 'none');
+ $("#layui-layer-shade" + layerNum).remove();
+ if (initBtnClicked) {
+ BU.initWithDropdownBox(type, startPath);
+ }
+ $("#mixly-selector-btn1").off("click");
+ $("#mixly-selector-btn2").off("click");
+ }
+ });
+ } else {
+ const layerNum = layer.open({
+ type: 1,
+ title: (type === 'burn'? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading']) + '...',
+ content: $('#mixly-loader-div'),
+ shade: LayerExt.SHADE_ALL,
+ resize: false,
+ closeBtn: 0,
+ success: function (layero, index) {
+ if (type === 'burn') {
+ BU.copyFiles(type, index, startPath, result[0] + pathAdd + '/');
+ } else {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ fs_extra.outputFile(startPath, code)
+ .then(() => {
+ BU.copyFiles(type, index, startPath, result[0] + pathAdd + '/');
+ })
+ .catch((error) => {
+ layer.close(index);
+ BU.burning = false;
+ BU.uploading = false;
+ statusBarTerminal.setValue(error + '\n');
+ });
+ }
+ $("#mixly-loader-btn").off("click").click(() => {
+ layer.close(index);
+ BU.cancel();
+ });
+ },
+ end: function () {
+ $('#mixly-selector-div').css('display', 'none');
+ $("#layui-layer-shade" + layerNum).remove();
+ $("#mixly-loader-btn").off("click");
+ }
+ });
+ }
+}
+
+/**
+ * @function 将文件或文件夹下所有文件拷贝到指定文件夹
+ * @param type {string} 值为'burn' | 'upload'
+ * @param startPath {string} 需要拷贝的文件或文件夹的路径
+ * @param desPath {string} 文件的目的路径
+ **/
+BU.copyFiles = async (type, startPath, desPath) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const { burn, upload } = SELECTED_BOARD;
+ if (type === 'upload' && upload.copyLib) {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ let startLibPath = path.dirname(upload.filePath);
+ let pyFileArr = BU.copyLib(startPath, code);
+ startPath = path.dirname(startPath);
+ }
+ // 如果需要拷贝的是文件,则在目的路径后要加上文件名
+ if (fs_plus.isFileSync(startPath)) {
+ desPath = path.join(desPath, path.basename(startPath));
+ }
+ try {
+ await fs_extra.copy(startPath, desPath);
+ BU.progressLayer.hide();
+ const message = (type === 'burn'? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']);
+ layer.msg(message, {
+ time: 1000
+ });
+ statusBarTerminal.setValue(`==${message}==`);
+ if (type === 'upload' && Serial.uploadPorts.length === 1) {
+ if (USER.autoOpenPort === 'no') {
+ return;
+ }
+ mainStatusBarTabs.add('serial', port);
+ mainStatusBarTabs.changeTo(port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.open();
+ }
+ } catch (error) {
+ Debug.error(error);
+ BU.progressLayer.hide();
+ statusBarTerminal.setValue(error + '\n');
+ console.log(error);
+ }
+ BU.burning = false;
+ BU.uploading = false;
+}
+
+/**
+* @function 判断当前环境,以开始一个上传过程
+* @param type {string} 值为'burn' | 'upload'
+ * @param startPath {string} 需要拷贝的文件或文件夹的路径
+* @return {void}
+*/
+BU.initWithDropdownBox = function (type, startPath) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const message = (type === 'burn'? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading']);
+ BU.progressLayer.title(`${message}...`);
+ const desPath = $('#mixly-selector-type option:selected').val();
+ if (type === 'burn') {
+ BU.copyFiles(type, startPath, desPath)
+ .catch((error) => {
+ BU.progressLayer.hide();
+ BU.burning = false;
+ BU.uploading = false;
+ statusBarTerminal.setValue(error + '\n');
+ });
+ } else {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ fs_extra.outputFile(startPath, code)
+ .then(() => {
+ return BU.copyFiles(type, startPath, desPath);
+ })
+ .catch((error) => {
+ BU.progressLayer.hide();
+ BU.burning = false;
+ BU.uploading = false;
+ statusBarTerminal.setValue(error + '\n');
+ });
+ }
+}
+
+/**
+* @function 根据传入的盘符名称获取对应的磁盘名称
+* @param type {string} 值为'burn' | 'upload'
+* @param volumeName {string} 所要查找盘符的名称
+* @param startPath {string} 需要拷贝文件的路径
+* @return {void}
+*/
+BU.getDisksWithVolumesName = function (type, volumeName, startPath) {
+ let dirPath = path.dirname(startPath);
+ fs_extra.ensureDirSync(dirPath);
+ if (Env.currentPlatform === "win32") {
+ child_process.exec('wmic logicaldisk where "' + volumeName + '" get DeviceID', function (err, stdout, stderr) {
+ if (err || stderr) {
+ $('#mixly-loader-div').css('display', 'none');
+ console.log("root path open failed" + err + stderr);
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ BU.burning = false;
+ BU.uploading = false;
+ return;
+ }
+ BU.checkNumOfDisks(type, stdout, startPath);
+ });
+ } else {
+ let diskPath = '/Volumes/';
+ let addChar = ' ';
+ if (Env.currentPlatform === "linux") {
+ diskPath = '/media/';
+ addChar = '';
+ }
+ let stdout = '';
+ let result = null;
+ result = volumeName.split('/');
+ let deviceNum = 0;
+ for (var i = 0; i < result.length; i++) {
+ if (result[i] === '') continue;
+ for (var j = 0; ; j++) {
+ if (fs_plus.isDirectorySync(diskPath + result[i] + (j == 0 ? '' : (addChar + j)))) {
+ stdout += diskPath + result[i] + (j == 0 ? '' : (addChar + j)) + ':';
+ deviceNum++;
+ } else if (fs_plus.isDirectorySync(diskPath + os.userInfo().username + '/' + result[i] + (j == 0 ? '' : (addChar + j)))) {
+ stdout += diskPath + os.userInfo().username + '/' + result[i] + (j == 0 ? '' : (addChar + j)) + ':';
+ deviceNum++;
+ } else {
+ break;
+ }
+ }
+ }
+ if (deviceNum === 0) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ BU.burning = false;
+ BU.uploading = false;
+ return;
+ }
+ BU.checkNumOfDisks(type, stdout, startPath);
+ }
+}
+
+/**
+* @function 取消烧录或上传
+* @return {void}
+*/
+BU.cancel = function () {
+ if (BU.shell) {
+ BU.shell.stdout.end();
+ BU.shell.stdin.end();
+ if (Env.currentPlatform === 'win32') {
+ child_process.exec('taskkill /pid ' + BU.shell.pid + ' /f /t');
+ } else {
+ BU.shell.kill("SIGTERM");
+ }
+ BU.shell = null;
+ } else {
+ if (BU.uploading) {
+ BU.uploading = false;
+ layer.msg(Msg.Lang['shell.uploadCanceled'], {
+ time: 1000
+ });
+ } else if (BU.burning) {
+ BU.burning = false;
+ layer.msg(Msg.Lang['shell.burnCanceled'], {
+ time: 1000
+ });
+ }
+ }
+ BU.killing = false;
+}
+
+/**
+* @function 开始一个烧录过程
+* @return {void}
+*/
+BU.initBurn = function () {
+ if (BU.burning) return;
+ const { burn } = SELECTED_BOARD;
+ BU.burning = true;
+ BU.uploading = false;
+ if (burn.type === 'volume') {
+ BU.getDisksWithVolumesName('burn', burn.volume, burn.filePath);
+ } else {
+ const port = Serial.getSelectedPortName();
+ if (burn.special && burn.special instanceof Array) {
+ BU.burning = false;
+ BU.burnWithSpecialBin();
+ } else {
+ BU.burnWithPort(port, burn.command);
+ }
+ }
+}
+
+/**
+* @function 开始一个上传过程
+* @return {void}
+*/
+BU.initUpload = function () {
+ if (BU.uploading) return;
+ const { upload } = SELECTED_BOARD;
+ BU.burning = false;
+ BU.uploading = true;
+ if (upload.type === "volume") {
+ BU.getDisksWithVolumesName('upload', upload.volume, upload.filePath);
+ } else {
+ const port = Serial.getSelectedPortName();
+ BU.uploadWithPort(port, upload.command);
+ }
+}
+
+/**
+ * @function 递归代码找出import项并拷贝对应库文件到filePath所在目录
+ * @param filePath {string} 主代码文件所在路径
+ * @param code {string} 主代码数据
+ * @return {array} 库列表
+ **/
+BU.copyLib = function (filePath, code) {
+ const dirPath = path.dirname(filePath);
+ const fileName = path.basename(filePath);
+ fs_extra.ensureDirSync(dirPath);
+ try {
+ const libFiles = fs.readdirSync(dirPath);
+ for (let value of libFiles) {
+ if (value !== fileName) {
+ fs.unlinkSync(path.join(dirPath, value));
+ }
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ var pyFileArr = [];
+ pyFileArr = BU.searchLibs(dirPath, code, pyFileArr);
+ return pyFileArr;
+}
+
+/**
+ * @function 获取当前代码数据中所使用的库并检测此文件是否在库目录下存在,
+ * 若存在则拷贝到主文件所在目录
+ * @param dirPath {string} 主代码文件所在目录的路径
+ * @param code {string} 主代码数据
+ * @param libArr {array} 当前已查找出的库列表
+ * @return {array} 库列表
+ **/
+BU.searchLibs = function (dirPath, code, libArr) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const { upload } = SELECTED_BOARD;
+ let arrayObj = new Array();
+ code.trim().split("\n").forEach(function (v, i) {
+ arrayObj.push(v);
+ });
+ let moduleName = "";
+ let pyFileArr = [];
+ for (let i = 0; i < arrayObj.length; i++) {
+ let fromLoc = arrayObj[i].indexOf("from");
+ let importLoc = arrayObj[i].indexOf("import");
+ const str = arrayObj[i].substring(0, (fromLoc === -1)? importLoc : fromLoc);
+ str.split('').forEach((ch) => {
+ if (ch !== ' ' && ch !== '\t') {
+ fromLoc = -1;
+ importLoc = -1;
+ return;
+ }
+ });
+ if (fromLoc !== -1) {
+ moduleName = arrayObj[i].substring(fromLoc + 4, arrayObj[i].indexOf("import"));
+ } else if (importLoc !== -1) {
+ let endPos = arrayObj[i].indexOf("as");
+ if (endPos === -1) {
+ endPos = arrayObj[i].length;
+ }
+ moduleName = arrayObj[i].substring(importLoc + 6, endPos);
+ } else {
+ continue;
+ }
+ moduleName = moduleName.replaceAll(' ', '');
+ moduleName = moduleName.replaceAll('\r', '');
+ let moduleArr = moduleName.split(",");
+ for (let j = 0; j < moduleArr.length; j++) {
+ if (!libArr.includes(moduleArr[j] + '.py') && !libArr.includes(moduleArr[j] + '.mpy')) {
+ try {
+ let oldLibPath = null;
+ if (!(upload.libPath && upload.libPath.length))
+ return;
+ for (let nowDirPath of upload.libPath) {
+ const nowMpyFilePath = path.join(nowDirPath, moduleArr[j] + '.mpy');
+ const nowPyFilePath = path.join(nowDirPath, moduleArr[j] + '.py');
+ if (fs_plus.isFileSync(nowMpyFilePath)) {
+ oldLibPath = nowMpyFilePath;
+ break;
+ } else if (fs_plus.isFileSync(nowPyFilePath)) {
+ oldLibPath = nowPyFilePath;
+ }
+ }
+ if (oldLibPath) {
+ const extname = path.extname(oldLibPath);
+ const newLibPath = path.join(dirPath, moduleArr[j] + extname);
+ statusBarTerminal.addValue(Msg.Lang['shell.copyLib'] + ' ' + moduleArr[j] + '\n');
+ fs.copyFileSync(oldLibPath, newLibPath);
+ libArr.push(moduleArr[j] + extname);
+ if (extname === '.py') {
+ pyFileArr.push(moduleArr[j] + extname);
+ code = fs.readFileSync(oldLibPath, 'utf8');
+ libArr = BU.searchLibs(dirPath, code, libArr);
+ }
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ }
+ }
+ return libArr;
+}
+
+/**
+* @function 通过cmd烧录
+* @param port {string} 所选择的串口
+* @param command {string} 需要执行的指令
+* @return {void}
+*/
+BU.burnByCmd = function (port, command) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue(Msg.Lang['shell.burning'] + '...\n');
+ command = MString.tpl(command, {
+ baudrate: Boards.getSelectedBoardConfigParam('BurnSpeed') ?? '460800'
+ });
+ BU.runCmd('burn', port, command);
+}
+
+/**
+* @function 通过cmd上传
+* @param port {string} 所选择的串口
+* @param command {string} 需要执行的指令
+* @return {void}
+*/
+BU.uploadByCmd = async function (port, command) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue(Msg.Lang['shell.uploading'] + '...\n');
+ const { upload } = SELECTED_BOARD;
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ if (upload.copyLib) {
+ BU.copyLib(upload.filePath, code);
+ } else {
+ BU.copyLib(upload.filePath, '');
+ }
+ fs_extra.outputFile(upload.filePath, code)
+ .then(() => {
+ BU.runCmd('upload', port, command);
+ })
+ .catch((error) => {
+ BU.progressLayer.hide();
+ statusBarTerminal.setValue(error.toString() + '\n');
+ Debug.error(error);
+ BU.uploading = false;
+ });
+}
+
+/**
+* @function 运行cmd
+* @param type {string} 值为 'burn' | 'upload'
+* @param port {string} 所选择的串口
+* @param command {string} 需要执行的指令
+* @param sucFunc {function} 指令成功执行后所要执行的操作
+* @return {void}
+*/
+BU.runCmd = function (type, port, command, sucFunc) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ let nowCommand = MString.tpl(command, { com: port });
+
+ BU.shell = child_process.exec(nowCommand, { encoding: 'binary' }, function (error, stdout, stderr) {
+ BU.progressLayer.hide();
+ BU.burning = false;
+ BU.uploading = false;
+ BU.shell = null;
+ const text = statusBarTerminal.getValue();
+ if (text.lastIndexOf('\n') !== text.length - 1) {
+ statusBarTerminal.addValue('\n');
+ }
+ if (error) {
+ if (Env.currentPlatform === 'win32') {
+ error = iconv_lite.decode(Buffer.from(error.message, 'binary'), 'cp936');
+ } else {
+ let lines = error.message.split('\n');
+ for (let i in lines) {
+ if (lines[i].indexOf('Command failed') !== -1) {
+ continue;
+ }
+ lines[i] = iconv_lite.decode(Buffer.from(lines[i], 'binary'), 'utf-8');
+ }
+ error = lines.join('\n');
+ }
+ error = MString.decode(error);
+ statusBarTerminal.addValue(error);
+ statusBarTerminal.addValue('==' + (type === 'burn' ? Msg.Lang['shell.burnFailed'] : Msg.Lang['shell.uploadFailed']) + '==\n');
+ } else {
+ layer.msg((type === 'burn' ? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']), {
+ time: 1000
+ });
+ statusBarTerminal.addValue('==' + (type === 'burn' ? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']) + '==\n');
+ if (type === 'upload') {
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.add('serial', port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.setValue('');
+ mainStatusBarTabs.changeTo(port);
+ statusBarSerial.open().catch(Debug.error);
+ }
+ }
+ });
+
+ BU.shell.stdout.on('data', function (data) {
+ if (BU.uploading || BU.burning) {
+ data = iconv_lite.decode(Buffer.from(data, 'binary'), 'utf-8');
+ data = MString.decode(data);
+ statusBarTerminal.addValue(data);
+ }
+ });
+}
+
+/**
+ * @function 特殊固件的烧录
+ * @return {void}
+ **/
+BU.burnWithSpecialBin = () => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const firmwares = SELECTED_BOARD.burn.special;
+ let menu = [];
+ let commandMap = {};
+ for (let firmware of firmwares) {
+ if (!firmware?.name && !firmware?.command) continue;
+ menu.push({
+ id: firmware.name,
+ text: firmware.name
+ });
+ commandMap[firmware.name] = firmware.command;
+ }
+ BU.firmwareLayer.setMap(commandMap);
+ BU.firmwareLayer.setMenu(menu);
+ BU.firmwareLayer.show();
+}
+
+/**
+ * @function 通过串口执行命令行烧录或上传操作
+ * @param type {string} 值为 'burn' | 'upload'
+ * @param port {string} 所选择的串口
+ * @param command {string} 需要执行的指令
+ * @return {void}
+ **/
+BU.operateWithPort = (type, port, command) => {
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ BU.burning = false;
+ BU.uploading = false;
+ return;
+ }
+ const title = (type === 'burn' ? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading']) + '...';
+ BU.progressLayer.title(title);
+ BU.progressLayer.show();
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ let serialClosePromise = null;
+ if (statusBarSerial) {
+ serialClosePromise = statusBarSerial.close();
+ } else {
+ serialClosePromise = Promise.resolve();
+ }
+ serialClosePromise.finally(() => {
+ if (type === 'burn') {
+ BU.burnByCmd(port, command);
+ } else {
+ BU.uploadByCmd(port, command);
+ }
+ });
+}
+
+/**
+ * @function 通过串口执行命令行烧录操作
+ * @param port {string} 所选择的串口
+ * @param command {string} 需要执行的指令
+ * @return {void}
+ **/
+BU.burnWithPort = (port, command) => {
+ BU.operateWithPort('burn', port, command);
+}
+
+/**
+ * @function 通过串口执行命令行上传操作
+ * @param port {string} 所选择的串口
+ * @param command {string} 需要执行的指令
+ * @return {void}
+ **/
+BU.uploadWithPort = (port, command) => {
+ BU.operateWithPort('upload', port, command);
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/cloud-download.js b/mixly/common/modules/mixly-modules/electron/cloud-download.js
new file mode 100644
index 00000000..1cc4fc79
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/cloud-download.js
@@ -0,0 +1,115 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.MJson');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.CloudDownload');
+
+const {
+ Env,
+ MJson,
+ Electron
+} = Mixly;
+
+const { CloudDownload } = Electron;
+
+const fs = Mixly.require('fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const node_downloader_helper = Mixly.require('node-downloader-helper');
+
+CloudDownload.getJson = (url, downloadDir, endFunc) => {
+ if (url) {
+ CloudDownload.download(url, downloadDir)
+ .then((message) => {
+ if (message[0]) {
+ throw message[0];
+ } else {
+ let jsonObj = null;
+ if (fs_plus.isFileSync(message[1])) {
+ let data = fs.readFileSync(message[1], 'utf-8');
+ jsonObj = MJson.parse(data);
+ }
+ if (jsonObj) {
+ return jsonObj;
+ } else {
+ throw('解析失败');
+ }
+ }
+
+ })
+ .then((configObj) => {
+ endFunc([null, configObj]);
+ })
+ .catch((error) => {
+ endFunc([error, null]);
+ });
+ } else {
+ endFunc(['url读取出错!', null]);
+ }
+}
+
+CloudDownload.download = (url, downloadDir, options = {}) => {
+ return new Promise((resolve, reject) => {
+ const DEFAULT_OPTIONS = {
+ progress: null,
+ end: null,
+ error: null
+ }
+ if (typeof options !== 'object')
+ options = DEFAULT_OPTIONS;
+ else
+ options = { ...DEFAULT_OPTIONS, ...options };
+ try {
+ fs_extra.ensureDirSync(downloadDir);
+ const fileName = path.basename(url);
+ const filePath = path.join(downloadDir, './' + fileName);
+ if (fs_plus.isFileSync(filePath))
+ fs_extra.removeSync(filePath);
+ } catch (error) {
+ resolve([error, url]);
+ return;
+ }
+ const { DownloaderHelper } = node_downloader_helper;
+ const dl = new DownloaderHelper(url, downloadDir, {
+ override: true,
+ timeout: 15000,
+ retry: false
+ });
+ dl.on('progress', (stats) => {
+ if (typeof options.progress === 'function') {
+ options.progress(stats);
+ }
+ });
+ dl.on('end', (downloadInfo) => {
+ if (typeof options.end === 'function') {
+ options.end(downloadInfo);
+ }
+ resolve([null, downloadInfo.filePath]);
+ });
+ dl.on('error', (error) => {
+ console.log('Download Failed', error);
+ if (typeof options.error === 'function') {
+ options.error(error);
+ }
+ resolve([error, url]);
+ });
+ dl.on('timeout', () => {
+ console.log('Download Timeout');
+ if (typeof options.timeout === 'function') {
+ options.timeout('Download Timeout');
+ }
+ resolve(['Download Timeout', url]);
+ });
+ dl.start().catch((error) => {
+ console.log('Download Failed', error);
+ if (typeof options.error === 'function') {
+ options.error(error);
+ }
+ resolve([error, url]);
+ });
+ });
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/electron.js b/mixly/common/modules/mixly-modules/electron/electron.js
new file mode 100644
index 00000000..b780be9a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/electron.js
@@ -0,0 +1,50 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Url');
+goog.provide('Mixly.Electron');
+
+const {
+ Url,
+ Env,
+ Electron
+} = Mixly;
+
+const electron_remote = Mixly.require('@electron/remote');
+
+const {
+ Menu,
+ BrowserWindow
+} = electron_remote;
+
+Electron.newBrowserWindow = (indexPath, config = {}) => {
+ Menu.setApplicationMenu(null);
+ const win = new BrowserWindow({
+ ...{
+ show: false,
+ minHeight: 400,
+ minWidth: 700,
+ width: 0,
+ height: 0,
+ icon: path.join(Env.indexDirPath, '../files/mixly.ico'),
+ webPreferences: {
+ nodeIntegration: true,
+ enableRemoteModule: true,
+ contextIsolation: false,
+ spellcheck: false
+ }
+ },
+ ...(config.window ?? {})
+ });
+
+ win.loadFile(indexPath);
+
+ win.once('ready-to-show', () => {
+ win.maximize();
+ win.show();
+ });
+
+ return win;
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/events.js b/mixly/common/modules/mixly-modules/electron/events.js
new file mode 100644
index 00000000..873d8751
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/events.js
@@ -0,0 +1,23 @@
+goog.loadJs('electron', () => {
+
+goog.require('Mixly.Command');
+goog.require('Mixly.Config');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.Events');
+
+const { Command, Config, Electron } = Mixly;
+const { Events } = Electron;
+const { SOFTWARE } = Config;
+
+const electron = Mixly.require('electron');
+const ipcRenderer = electron.ipcRenderer;
+
+ipcRenderer.on('command', (event, message) => {
+ if (SOFTWARE.debug) {
+ console.log('receive -> ', message);
+ }
+ const commandObj = Command.parse(message);
+ Command.run(commandObj);
+});
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/file-tree.js b/mixly/common/modules/mixly-modules/electron/file-tree.js
new file mode 100644
index 00000000..f309586b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/file-tree.js
@@ -0,0 +1,172 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.FileTree');
+goog.require('Mixly.Events');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Electron.FS');
+goog.provide('Mixly.Electron.FileTree');
+
+const {
+ FileTree,
+ Events,
+ Registry,
+ Debug,
+ Electron
+} = Mixly;
+const { FS } = Electron;
+
+const chokidar = Mixly.require('chokidar');
+
+class FileTreeExt extends FileTree {
+ static {
+ /*this.worker = new Worker('../common/modules/mixly-modules/workers/nodejs/node-file-watcher.js', {
+ name: 'nodeFileWatcher'
+ });
+ this.watcherEventsRegistry = new Registry();
+ this.worker.addEventListener('message', (event) => {
+ const { data } = event;
+ const events = this.watcherEventsRegistry.getItem(data.watcher);
+ if (!events) {
+ return;
+ }
+ events.run('change', data);
+ });
+ this.worker.addEventListener('error', (event) => {
+ Debug.error(event);
+ });*/
+
+ this.addEventListener = function(folderPath, func) {
+ FileTreeExt.watch(folderPath);
+ let events = this.watcherEventsRegistry.getItem(folderPath);
+ if (!events) {
+ events = new Events(['change']);
+ this.watcherEventsRegistry.register(folderPath, events);
+ }
+ return events.bind('change', func);
+ }
+
+ this.removeEventListener = function(folderPath, eventId) {
+ let events = this.watcherEventsRegistry.getItem(folderPath);
+ if (!events) {
+ return;
+ }
+ if (!events.length('change')) {
+ this.watcherEventsRegistry.unregister(folderPath);
+ this.unwatch(folderPath);
+ }
+ }
+
+ this.watch = function(folderPath) {
+ FileTreeExt.worker.postMessage({
+ func: 'watch',
+ args: [folderPath]
+ });
+ }
+
+ this.unwatch = function(folderPath) {
+ FileTreeExt.worker.postMessage({
+ func: 'unwatch',
+ args: [folderPath]
+ });
+ }
+ }
+
+ constructor() {
+ super(FS);
+ this.watcher = null;
+ this.watcherEventsListenerIdRegistry = new Registry();
+ }
+
+ async readFolder(inPath) {
+ let output = [];
+ const fs = this.getFS();
+ const status = await fs.isDirectory(inPath);
+ if (!status) {
+ return output;
+ }
+ const children = await fs.readDirectory(inPath);
+ for (let data of children) {
+ const dataPath = path.join(inPath, data);
+ if (await fs.isDirectory(dataPath)) {
+ const isDirEmtpy = await fs.isDirectoryEmpty(dataPath);
+ output.push({
+ type: 'folder',
+ id: dataPath,
+ children: !isDirEmtpy
+ });
+ if (isDirEmtpy) {
+ this.watchEmptyFolder(dataPath);
+ }
+ } else {
+ output.push({
+ type: 'file',
+ id: dataPath,
+ children: false
+ });
+ }
+ }
+ return output;
+ }
+
+ watchFolder(folderPath) {
+ super.watchFolder(folderPath);
+ let id = this.watcherEventsListenerIdRegistry.getItem(folderPath);
+ if (id) {
+ return;
+ }
+ id = FileTreeExt.addEventListener(folderPath, (data) => {
+ if (data.event === 'unlinkDir') {
+ this.unwatchFolder(path.join(data.path));
+ }
+ const watcherPath = path.join(data.watcher);
+ if (this.isWatched(watcherPath)) {
+ this.refreshFolder(watcherPath);
+ }
+ });
+ this.watcherEventsListenerIdRegistry.register(folderPath, id);
+ }
+
+ watchEmptyFolder(folderPath) {
+ super.watchFolder(folderPath);
+ let id = this.watcherEventsListenerIdRegistry.getItem(folderPath);
+ if (id) {
+ return;
+ }
+ id = FileTreeExt.addEventListener(folderPath, (data) => {
+ const watcherPath = path.join(data.watcher);
+ if (this.isWatched(watcherPath)) {
+ this.refreshFolder(watcherPath);
+ }
+ if (this.isClosed(watcherPath)) {
+ this.unwatchFolder(watcherPath);
+ }
+ });
+ this.watcherEventsListenerIdRegistry.register(folderPath, id);
+ }
+
+ unwatchFolder(folderPath) {
+ const keys = this.watchRegistry.keys();
+ for (let key of keys) {
+ if (key.indexOf(folderPath) === -1) {
+ continue;
+ }
+ const type = this.watchRegistry.getItem(key);
+ if (type === 'file') {
+ this.unwatchFile(key);
+ }
+ const id = this.watcherEventsListenerIdRegistry.getItem(key);
+ if (!id) {
+ continue;
+ }
+ FileTreeExt.removeEventListener(key, id);
+ this.watcherEventsListenerIdRegistry.unregister(key);
+ }
+ super.unwatchFolder(folderPath);
+ }
+}
+
+Electron.FileTree = FileTreeExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/file.js b/mixly/common/modules/mixly-modules/electron/file.js
new file mode 100644
index 00000000..ecbd4102
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/file.js
@@ -0,0 +1,285 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Blockly');
+goog.require('Mixly.Env');
+goog.require('Mixly.LayerNewFile');
+goog.require('Mixly.Config');
+goog.require('Mixly.Title');
+goog.require('Mixly.MFile');
+goog.require('Mixly.XML');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.Electron.ArduShell');
+goog.require('Mixly.Electron.BU');
+goog.provide('Mixly.Electron.File');
+
+const {
+ Env,
+ LayerNewFile,
+ Config,
+ Title,
+ MFile,
+ XML,
+ Msg,
+ Workspace,
+ Electron
+} = Mixly;
+
+const { BOARD } = Config;
+
+const { ArduShell, BU, File } = Electron;
+
+const fs = Mixly.require('fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const fs_promise = Mixly.require('node:fs/promises');
+const electron_remote = Mixly.require('@electron/remote');
+const { dialog, app } = electron_remote;
+
+const { MSG } = Blockly.Msg;
+
+File.DEFAULT_PATH = path.join(app.getAppPath(), 'src/sample');
+File.workingPath = File.DEFAULT_PATH;
+File.openedFilePath = null;
+
+File.userPath = {
+ img: null,
+ mix: null,
+ code: null,
+ hex: null
+};
+
+File.newFileLayer = new LayerNewFile();
+File.newFileLayer.bind('empty', () => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const blockEditor = editor.getPage('block').getEditor();
+ const codeEditor = editor.getPage('code').getEditor();
+ const generator = Blockly.generator;
+ blockEditor.clear();
+ blockEditor.scrollCenter();
+ Blockly.hideChaff();
+ codeEditor.setValue(generator.workspaceToCode(blockEditor) || '', -1);
+ File.openedFilePath = null;
+ Title.updateTitle(Title.title);
+});
+
+File.showSaveDialog = (title, filters, endFunc) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ dialog.showSaveDialog(currentWindow, {
+ title,
+ defaultPath: File.workingPath,
+ filters,
+ // nameFieldLabel: Msg.Lang['替换文件'],
+ showsTagField: true,
+ properties: ['showHiddenFiles'],
+ message: title
+ }).then(result => {
+ let res = result.filePath;
+ if (res)
+ endFunc(res);
+ }).catch(error => {
+ console.log(error);
+ });
+}
+
+File.showOpenDialog = (title, filters, endFunc) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ dialog.showOpenDialog(currentWindow, {
+ title,
+ defaultPath: File.workingPath,
+ filters,
+ properties: ['openFile', 'showHiddenFiles'],
+ message: title
+ })
+ .then(result => {
+ let res = result.filePaths[0];
+ if (res)
+ endFunc(res);
+ })
+ .catch(error => {
+ console.log(error);
+ });
+}
+
+File.save = (endFunc = () => {}) => {
+ if (File.openedFilePath) {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const extname = path.extname(File.openedFilePath);
+ let data = '';
+ switch (extname) {
+ case '.mix':
+ data = editor.getValue();
+ break;
+ case '.py':
+ case '.ino':
+ data = editor.getCode();
+ break;
+ default:
+ layer.msg(Msg.Lang['file.type.error'], { time: 1000 });
+ return;
+ }
+ fs_extra.outputFile(File.openedFilePath, data)
+ .then(() => {
+ Title.updeteFilePath(File.openedFilePath);
+ layer.msg(Msg.Lang['file.saveSucc'], { time: 1000 });
+ })
+ .catch((error) => {
+ File.openedFilePath = null;
+ console.log(error);
+ layer.msg(Msg.Lang['file.saveFailed'], { time: 1000 });
+ })
+ .finally(() => {
+ endFunc();
+ })
+ } else {
+ File.saveAs(endFunc);
+ }
+}
+
+File.saveAs = (endFunc = () => {}) => {
+ File.showSaveDialog(Msg.Lang['file.saveAs'], MFile.saveFilters, (filePath) => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const extname = path.extname(filePath);
+ if (['.mix', '.py', '.ino'].includes(extname)) {
+ File.openedFilePath = filePath;
+ File.workingPath = path.dirname(filePath);
+ File.save(endFunc);
+ } else {
+ switch (extname) {
+ case '.bin':
+ case '.hex':
+ if (BOARD?.nav?.compile) {
+ ArduShell.saveBinOrHex(filePath);
+ } else {
+ const hexStr = MFile.getHex();
+ fs_extra.outputFile(filePath, hexStr)
+ .then(() => {
+ layer.msg(Msg.Lang['file.saveSucc'], { time: 1000 });
+ })
+ .catch((error) => {
+ console.log(error);
+ layer.msg(Msg.Lang['file.saveFailed'], { time: 1000 });
+ })
+ .finally(() => {
+ endFunc();
+ });
+ }
+ break;
+ case '.mil':
+ const milStr = editor.getMil();
+ const $mil = $(milStr);
+ $mil.attr('name', path.basename(filePath, '.mil'));
+ fs_extra.outputFile(filePath, $mil[0].outerHTML)
+ .then(() => {
+ layer.msg('file.saveSucc', { time: 1000 });
+ })
+ .catch((error) => {
+ console.log(error);
+ layer.msg(Msg.Lang['file.saveFailed'], { time: 1000 });
+ })
+ .finally(() => {
+ endFunc();
+ });
+ break;
+ default:
+ layer.msg(Msg.Lang['file.type.error'], { time: 1000 });
+ endFunc();
+ }
+ }
+ });
+}
+
+File.exportLib = (endFunc = () => {}) => {
+ File.showSaveDialog(Msg.Lang['file.exportAs'], [ MFile.SAVE_FILTER_TYPE.mil ], (filePath) => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const milStr = editor.getMil();
+ const $mil = $(milStr);
+ $mil.attr('name', path.basename(filePath, '.mil'));
+ fs_extra.outputFile(filePath, $mil[0].outerHTML)
+ .then(() => {
+ layer.msg('file.saveSucc', { time: 1000 });
+ })
+ .catch((error) => {
+ console.log(error);
+ layer.msg(Msg.Lang['file.saveFailed'], { time: 1000 });
+ })
+ .finally(() => {
+ endFunc();
+ });
+ });
+}
+
+File.new = () => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const blockEditor = editor.getPage('block').getEditor();
+ const codeEditor = editor.getPage('code').getEditor();
+ const generator = Blockly.generator;
+ const blocksList = blockEditor.getAllBlocks();
+ if (editor.getPageType() === 'code') {
+ const code = codeEditor.getValue(),
+ workspaceToCode = generator.workspaceToCode(blockEditor) || '';
+ if (!blocksList.length && workspaceToCode === code) {
+ layer.msg(Msg.Lang['editor.codeEditorEmpty'], { time: 1000 });
+ File.openedFilePath = null;
+ Title.updateTitle(Title.title);
+ return;
+ }
+ } else {
+ if (!blocksList.length) {
+ layer.msg(Msg.Lang['editor.blockEditorEmpty'], { time: 1000 });
+ File.openedFilePath = null;
+ Title.updateTitle(Title.title);
+ return;
+ }
+ }
+ File.newFileLayer.show();
+}
+
+File.open = () => {
+ File.showOpenDialog(Msg.Lang['file.open'], [
+ { name: Msg.Lang['file.type.mix'], extensions: MFile.openFilters }
+ ], (filePath) => {
+ File.openFile(filePath);
+ });
+}
+
+File.openFile = (filePath) => {
+ const extname = path.extname(filePath);
+ let data;
+ if (!fs_plus.isFileSync(filePath)) {
+ console.log(filePath + '不存在');
+ return;
+ }
+ try {
+ data = fs.readFileSync(filePath, 'utf-8');
+ } catch (error) {
+ console.log(error);
+ return;
+ }
+ if (['.bin', '.hex'].includes(extname)) {
+ if (BOARD?.nav?.compile) {
+ ArduShell.showUploadBox(filePath);
+ } else {
+ MFile.loadHex(data);
+ }
+ } else if (['.mix', '.xml', '.ino', '.py'].includes(extname)) {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ editor.setValue(data, extname);
+ File.openedFilePath = filePath;
+ File.workingPath = path.dirname(filePath);
+ Title.updeteFilePath(File.openedFilePath);
+ } else {
+ layer.msg(Msg.Lang['file.type.error'], { time: 1000 });
+ }
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/footerlayer-example.js b/mixly/common/modules/mixly-modules/electron/footerlayer-example.js
new file mode 100644
index 00000000..76206cee
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/footerlayer-example.js
@@ -0,0 +1,110 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('Mixly.Env');
+goog.require('Mixly.Config');
+goog.require('Mixly.MFile');
+goog.require('Mixly.Title');
+goog.require('Mixly.XML');
+goog.require('Mixly.FooterLayerExample');
+goog.require('Mixly.Electron.File');
+goog.provide('Mixly.Electron.FooterLayerExample');
+
+const {
+ Env,
+ Config,
+ MFile,
+ Title,
+ XML,
+ FooterLayerExample,
+ Electron
+} = Mixly;
+
+const { dropdown, tree } = layui;
+
+const { File } = Electron;
+
+const { BOARD } = Config;
+
+const fs = Mixly.require('fs');
+const fs_plus = Mixly.require('fs-plus');
+const electron_remote = Mixly.require('@electron/remote');
+const { app } = electron_remote;
+
+class FooterLayerExampleExt extends FooterLayerExample {
+ constructor(element) {
+ super(element);
+ }
+
+ getRoot() {
+ let exampleList = [];
+ let samplePath = path.join(Env.boardDirPath, 'examples');
+ const sampleList = this.getExamplesByPath(samplePath, '.mix');
+ if (sampleList.length) {
+ exampleList.push({
+ id: samplePath,
+ title: BOARD.boardType,
+ children: []
+ });
+ }
+ const thirdPartyPath = path.join(Env.boardDirPath, 'libraries/ThirdParty');
+ if (fs_plus.isDirectorySync(thirdPartyPath)) {
+ const libList = fs.readdirSync(thirdPartyPath);
+ for (let lib of libList) {
+ const libPath = path.join(thirdPartyPath, lib);
+ if (fs_plus.isFileSync(libPath))
+ continue;
+ const examplesPath = path.join(libPath, 'examples');
+ if (fs_plus.isFileSync(examplesPath))
+ continue;
+ const thirdPartyList = this.getExamplesByPath(examplesPath, '.mix');
+ if (thirdPartyList.length) {
+ exampleList.push({
+ id: examplesPath,
+ title: lib,
+ children: []
+ });
+ }
+ }
+ }
+ return exampleList;
+ }
+
+ getChildren(inPath) {
+ return this.getExamplesByPath(inPath, '.mix');
+ }
+
+ dataToWorkspace(inPath) {
+ if (!fs_plus.isFileSync(inPath)) {
+ return;
+ }
+ const data = fs.readFileSync(inPath, 'utf8');
+ const extname = path.extname(inPath);
+ this.updateCode(extname, data);
+ File.openedFilePath = null;
+ }
+
+ getExamplesByPath(inPath, fileExtname) {
+ let exampleList = [];
+ if (fs_plus.isDirectorySync(inPath)) {
+ const dataList = fs.readdirSync(inPath);
+ for (let data of dataList) {
+ const dataPath = path.join(inPath, data);
+ if (fs_plus.isDirectorySync(dataPath)) {
+ exampleList.push({ title: data, id: dataPath, children: [] });
+ } else {
+ const extname = path.extname(data);
+ if (extname === fileExtname) {
+ exampleList.push({ title: data, id: dataPath });
+ }
+ }
+ }
+ }
+ return exampleList;
+ }
+}
+
+Electron.FooterLayerExample = FooterLayerExampleExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/fs-board.js b/mixly/common/modules/mixly-modules/electron/fs-board.js
new file mode 100644
index 00000000..220091ae
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/fs-board.js
@@ -0,0 +1,153 @@
+goog.loadJs('electron', () => {
+
+goog.require('layui');
+goog.require('path');
+goog.require('Mustache');
+goog.require('Mixly.Env');
+goog.require('Mixly.FSBoard');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Electron.Shell');
+goog.provide('Mixly.Electron.FSBoard');
+
+const {
+ Env,
+ FSBoard,
+ LayerExt,
+ Debug,
+ Msg,
+ Electron = {}
+} = Mixly;
+
+const { Shell } = Electron;
+
+const fs_extra = Mixly.require('fs-extra');
+
+const { layer } = layui;
+
+
+class FSBoardExt extends FSBoard {
+ #shell_ = null;
+ constructor() {
+ super();
+ this.#shell_ = new Shell();
+ }
+
+ download(usrFolder, fsType) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ await super.download(usrFolder, fsType);
+ } catch (error) {
+ Debug.error(error);
+ resolve();
+ return;
+ }
+ const layerNum = layer.open({
+ type: 1,
+ title: `${Msg.Lang['shell.downloading']}...`,
+ content: $('#mixly-loader-div'),
+ shade: LayerExt.SHADE_NAV,
+ resize: false,
+ closeBtn: 0,
+ success: () => {
+ $("#mixly-loader-btn").off("click").click(() => {
+ $("#mixly-loader-btn").css('display', 'none');
+ layer.title(Msg.Lang['shell.aborting'] + '...', layerNum);
+ this.cancel();
+ });
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue('');
+ mainStatusBarTabs.changeTo('output');
+ const commandTemplate = this.getSelectedFSDownloadCommand();
+ const command = this.#renderTemplate_(commandTemplate);
+ this.#shell_.exec(command)
+ .then((info) => {
+ if (info.code) {
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.downloadFailed']}==\n`);
+ } else {
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.downloadSucc']}(${Msg.Lang['shell.timeCost']} ${info.time})==\n`);
+ }
+ })
+ .catch(Debug.error)
+ .finally(() => {
+ layer.close(layerNum);
+ resolve();
+ });
+ },
+ end: function () {
+ $("#mixly-loader-btn").off("click");
+ $("#mixly-loader-btn").css('display', 'inline-block');
+ }
+ });
+ });
+ }
+
+ upload(usrFolder, fsType) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ await super.upload(usrFolder, fsType);
+ } catch (error) {
+ Debug.error(error);
+ resolve();
+ return;
+ }
+ const layerNum = layer.open({
+ type: 1,
+ title: Msg.Lang['shell.uploading'] + '...',
+ content: $('#mixly-loader-div'),
+ shade: LayerExt.SHADE_NAV,
+ resize: false,
+ closeBtn: 0,
+ success: () => {
+ $("#mixly-loader-btn").off("click").click(() => {
+ $("#mixly-loader-btn").css('display', 'none');
+ layer.title('上传终止中...', layerNum);
+ this.cancel();
+ });
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue('');
+ mainStatusBarTabs.changeTo('output');
+ const commandTemplate = this.getSelectedFSUploadCommand();
+ const command = this.#renderTemplate_(commandTemplate);
+ this.#shell_.exec(command)
+ .then((info) => {
+ if (info.code) {
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
+ } else {
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadSucc']}(${Msg.Lang['shell.timeCost']} ${info.time})==\n`);
+ }
+ })
+ .catch(Debug.error)
+ .finally(() => {
+ layer.close(layerNum);
+ resolve();
+ });
+ },
+ end: function () {
+ $("#mixly-loader-btn").off("click");
+ $("#mixly-loader-btn").css('display', 'inline-block');
+ }
+ });
+ });
+ }
+
+ #renderTemplate_(template) {
+ const config = this.getConfig();
+ return Mustache.render(template, {
+ ...config,
+ python3: `"${Env.python3Path}"`,
+ esptool: `"${Env.python3Path}" "${path.join(Env.srcDirPath, 'tools/python/esptool_main.py')}"`
+ });
+ }
+
+ cancel() {
+ this.#shell_ && this.#shell_.kill();
+ }
+}
+
+Electron.FSBoard = FSBoardExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/fs.js b/mixly/common/modules/mixly-modules/electron/fs.js
new file mode 100644
index 00000000..b865344b
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/fs.js
@@ -0,0 +1,158 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.FS');
+
+const { Msg, Electron } = Mixly;
+const { FS } = Electron;
+
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const fs_promise = Mixly.require('node:fs/promises');
+const electron_remote = Mixly.require('@electron/remote');
+const { dialog, app } = electron_remote;
+
+FS.showOpenFilePicker = () => {
+ return new Promise((resolve, reject) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ dialog.showOpenDialog(currentWindow, {
+ title: Msg.Lang['file.openFile'],
+ defaultPath: File.workingPath,
+ filters,
+ properties: ['openFile', 'showHiddenFiles'],
+ message: Msg.Lang['file.openFile']
+ })
+ .then(result => {
+ const filePath = result.filePaths[0];
+ if (filePath) {
+ resolve(new File(filePath));
+ } else {
+ reject('file not found');
+ }
+ })
+ .catch(error => {
+ reject(error);
+ });
+ });
+}
+
+FS.showDirectoryPicker = () => {
+ return new Promise((resolve, reject) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ dialog.showOpenDialog(currentWindow, {
+ title: Msg.Lang['file.openFolder'],
+ // defaultPath: File.workingPath,
+ // filters,
+ properties: ['openDirectory', 'createDirectory'],
+ message: Msg.Lang['file.openFolder']
+ })
+ .then(result => {
+ const folderPath = result.filePaths[0];
+ if (folderPath) {
+ resolve(folderPath);
+ } else {
+ resolve(null);
+ }
+ })
+ .catch(reject);
+ });
+}
+
+FS.showSaveFilePicker = (fileName, ext) => {
+ return new Promise((resolve, reject) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ dialog.showSaveDialog(currentWindow, {
+ filters: [{
+ name: Msg.Lang['file.type.mix'],
+ extensions: [ext.substring(ext.lastIndexOf('.') + 1)]
+ }],
+ defaultPath: fileName,
+ showsTagField: true,
+ properties: ['showHiddenFiles']
+ }).then(result => {
+ let filePath = result.filePath;
+ if (filePath) {
+ resolve(filePath);
+ } else {
+ resolve(null);
+ }
+ }).catch(reject);
+ });
+}
+
+FS.createFile = (filePath) => {
+ return fs_extra.ensureFile(filePath);
+}
+
+FS.readFile = (filePath) => {
+ return fs_promise.readFile(filePath, { encoding: 'utf8' });
+}
+
+FS.writeFile = (filePath, data) => {
+ return fs_promise.writeFile(filePath, data, { encoding: 'utf8' });
+}
+
+FS.isFile = (filePath) => {
+ return new Promise((resolve, reject) => {
+ resolve(fs_plus.isFileSync(filePath));
+ });
+}
+
+FS.renameFile = (oldFilePath, newFilePath) => {
+ return fs_promise.rename(oldFilePath, newFilePath);
+}
+
+FS.moveFile = (oldFilePath, newFilePath) => {
+ return fs_extra.move(oldFilePath, newFilePath, { overwrite: true });
+}
+
+FS.copyFile = (oldFilePath, newFilePath) => {
+ return fs_extra.copy(oldFilePath, newFilePath);
+}
+
+FS.deleteFile = (filePath) => {
+ return fs_extra.remove(filePath);
+}
+
+FS.createDirectory = (folderPath) => {
+ return fs_extra.ensureDir(folderPath);
+}
+
+FS.readDirectory = (folderPath) => {
+ return fs_promise.readdir(folderPath);
+}
+
+FS.isDirectory = (folderPath) => {
+ return new Promise((resolve, reject) => {
+ fs_plus.isDirectory(folderPath, (status) => {
+ resolve(status);
+ });
+ });
+}
+
+FS.isDirectoryEmpty = async (folderPath) => {
+ return !(await FS.readDirectory(folderPath)).length;
+}
+
+FS.renameDirectory = (oldFolderPath, newFolderPath) => {
+ return fs_promise.rename(oldFolderPath, newFolderPath);
+}
+
+FS.moveDirectory = (oldFolderPath, newFolderPath) => {
+ return fs_extra.move(oldFolderPath, newFolderPath, { overwrite: true });
+}
+
+FS.copyDirectory = (oldFolderPath, newFolderPath) => {
+ return fs_extra.copy(oldFolderPath, newFolderPath);
+}
+
+FS.deleteDirectory = (folderPath) => {
+ return fs_extra.remove(folderPath);
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/lib-manager.js b/mixly/common/modules/mixly-modules/electron/lib-manager.js
new file mode 100644
index 00000000..5b910d90
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/lib-manager.js
@@ -0,0 +1,1186 @@
+goog.loadJs('electron', () => {
+
+goog.require('layui');
+goog.require('Mixly.Env');
+goog.require('Mixly.XML');
+goog.require('Mixly.ScriptLoader');
+goog.require('Mixly.CssLoader');
+goog.require('Mixly.Boards');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Config');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Electron.CloudDownload');
+goog.provide('Mixly.Electron.LibManager');
+
+const {
+ Env,
+ Electron,
+ XML,
+ ScriptLoader,
+ CssLoader,
+ Boards,
+ LayerExt,
+ Config,
+ Msg
+} = Mixly;
+
+const fs = Mixly.require('fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const path = Mixly.require('path');
+const electron_remote = Mixly.require('@electron/remote');
+const compressing = Mixly.require('compressing');
+
+const { BOARD, USER } = Config;
+const { CloudDownload, LibManager } = Electron;
+const { table, element } = layui;
+const { dialog, shell } = electron_remote;
+
+LibManager.URL = {
+ mixly: BOARD?.lib?.mixly?.url[0],
+ arduino: BOARD?.lib?.arduino?.url[0],
+ python: BOARD?.lib?.python?.url[0]
+}
+
+LibManager.LOCAL_IMPORT_FILTERS = {
+ 'MIXLY': [
+ { name: 'Mixly Lib File', extensions: ['xml', 'mil', 'zip'] }
+ ],
+ 'ARDUINO': [
+ { name: 'ZIP File', extensions: ['zip'] }
+ ],
+ 'PYTHON': [
+ { name: 'Python File', extensions: ['py'] }
+ ]
+};
+
+LibManager.getDefaultXML = () => {
+ let $toolbox = $('#toolbox');
+ return $toolbox.html();
+}
+
+LibManager.getLibs = (libsDir) => {
+ let thirdPartyXML = [];
+ let loadJs = [];
+ let loadCss = [];
+ if (!fs_plus.isDirectorySync(libsDir))
+ return {
+ xml: thirdPartyXML,
+ js: loadJs,
+ css: loadCss
+ };
+ const libList = fs.readdirSync(libsDir);
+ for (let libName of libList) {
+ const nowPath = path.join(libsDir, './' + libName);
+ if (fs_plus.isDirectorySync(nowPath)) {
+ const dataList = fs.readdirSync(nowPath);
+ for (let dataName of dataList) {
+ const extname = path.extname(dataName);
+ if (extname !== '.xml') continue;
+ let text = fs.readFileSync(path.join(nowPath, './' + dataName), 'utf8');
+ const $xml = XML.getDom(text, {
+ libPath: nowPath
+ });
+ for (let i = 0; $xml[i]; i++) {
+ if (['SCRIPT', 'LINK'].includes($xml[i].nodeName)) {
+ let loader, src;
+ if ($xml[i].nodeName == 'SCRIPT') {
+ loader = loadJs;
+ src = $($xml[i]).attr('src');
+ } else {
+ loader = loadCss;
+ src = $($xml[i]).attr('href');
+ }
+ const srcThirdDir = path.join(nowPath, src);
+ if (fs_plus.isFileSync(srcThirdDir)) {
+ loader.push(srcThirdDir);
+ }
+ } else if ($xml[i].nodeName == 'CATEGORY') {
+ thirdPartyXML.push($xml[i].outerHTML);
+ }
+ }
+ }
+ } else {
+ const extname = path.extname(nowPath);
+ const fileName = path.basename(nowPath, '.mil');
+ if (extname !== '.mil') continue;
+ const text = fs.readFileSync(nowPath, 'utf8');
+ const $mil = $('
');
+ $mil.append($(text).html());
+ thirdPartyXML.push($mil[0].outerHTML);
+ }
+ }
+ return {
+ xml: thirdPartyXML,
+ js: loadJs,
+ css: loadCss
+ };
+}
+
+LibManager.convertLibs = (libsDir) => {
+ if (!fs_plus.isDirectorySync(libsDir)) return;
+ const libList = fs.readdirSync(libsDir);
+ for (let libName of libList) {
+ const libDir = path.join(libsDir, './' + libName);
+ if (!fs_plus.isDirectorySync(libDir)) continue;
+ const dataList = fs.readdirSync(libDir);
+ const blocksDir = path.join(libDir, './block');
+ // 将1.x版本xml转化为2.0可用
+ for (let data of dataList) {
+ const extname = path.extname(data);
+ if (extname !== '.xml') continue;
+ const xmlPath = path.join(libDir, './' + data);
+ let xmlData = fs.readFileSync(xmlPath, 'utf8');
+ try {
+ xmlData = xmlData.replace(/\.\.\/\.\.\/blocks\/company\//g, "libraries/ThirdParty/" + libName + "/block/");
+ xmlData = xmlData.replace(/\.\.\/\.\.\/generators\/arduino\/company\//g, "libraries/ThirdParty/" + libName + "/generator/");
+ fs.writeFileSync(xmlPath, xmlData);
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ }
+}
+
+LibManager.loadLibsAndUpdateJsCssList = (doFunc = () => {}) => {
+ const {
+ js,
+ css,
+ xml
+ } = LibManager.getLibs(path.join(Env.boardDirPath, './libraries/ThirdParty/'));
+ Env.thirdPartyXML = xml;
+ let loadPromise = [];
+ if (js.length) {
+ loadPromise.push(new Promise((resolve, reject) => {
+ LazyLoad.js(js, function () {
+ resolve();
+ });
+ }));
+ }
+ if (css.length) {
+ loadPromise.push(new Promise((resolve, reject) => {
+ LazyLoad.css(css, function () {
+ resolve();
+ });
+ }));
+ }
+ if (loadPromise.length)
+ Promise.all(loadPromise)
+ .catch((error) => {
+ console.log(error);
+ })
+ .finally(() => {
+ doFunc();
+ })
+ else
+ doFunc();
+ Env.thirdPartyCSS = [...Env.thirdPartyCSS, ...(css ?? [])];
+ Env.thirdPartyJS = [...Env.thirdPartyJS, ...(js ?? [])];
+}
+
+LibManager.init = (doFunc = () => {}) => {
+ Env.defaultXML = LibManager.getDefaultXML();
+ Env.thirdPartyXML = [];
+ LibManager.reloadThirdPartyLibs(doFunc);
+}
+
+LibManager.reloadThirdPartyLibs = (doFunc = () => {}) => {
+ for (let i = 0; i < Env.thirdPartyJS.length; i++) {
+ ScriptLoader.removeScript(Env.thirdPartyJS[i]);
+ }
+ for (let i = 0; i < Env.thirdPartyCSS.length; i++) {
+ CssLoader.removeCss(Env.thirdPartyCSS[i]);
+ }
+ Env.thirdPartyJS = [];
+ Env.thirdPartyCSS = [];
+ $('#toolbox').html(Env.defaultXML);
+ LibManager.loadLibsAndUpdateJsCssList(function() {
+ const board = Boards.getSelectedBoardName();
+ Boards.updateCategories(board, true);
+ doFunc();
+ });
+}
+
+LibManager.menuAddEvent = (layero) => {
+ const thirdPartyPath = path.join(Env.boardDirPath, './libraries/ThirdParty');
+ // 左侧菜单状态, 右侧菜单状态×2
+ let menuStatus = [0, 0, 0];
+ element.tab({
+ headerElem: '#libs-menu-options>li',
+ bodyElem: '#libs-menu-body>.menu-body'
+ });
+ element.render('nav', 'libs-menu-filter');
+ // 左侧菜单点击事件
+ element.on('tab(libs-menu-filter)', function (data) {
+ const { index } = data;
+ if (index === menuStatus[0]) return;
+ if (index) {
+ LibManager.onclickManageLibs();
+ } else {
+ LibManager.onclickImportLibs();
+ }
+ menuStatus[0] = index;
+ });
+ // 右侧菜单点击事件
+ element.on('tab(import-lib-page-filter)', function (data) {
+ const { index } = data;
+ if (index === menuStatus[1]) return;
+ if (data.index) {
+
+ } else {
+
+ }
+ menuStatus[1] = index;
+ });
+ // 右侧菜单点击事件
+ element.on('tab(del-lib-page-filter)', function (data) {
+ const { index } = data;
+ if (index === menuStatus[2]) return;
+ if (data.index) {
+
+ } else {
+
+ }
+ menuStatus[2] = index;
+ });
+ // 按钮点击事件
+ layero.find('button').off().click(function () {
+ const $this = $(this);
+ const mId = $this.attr('m-id');
+ let importType, delType;
+ if (menuStatus[1]) {
+ if (BOARD?.nav?.compile) {
+ importType = 'ARDUINO';
+ } else {
+ importType = 'PYTHON';
+ }
+ } else {
+ importType = 'MIXLY';
+ }
+ if (menuStatus[2]) {
+ if (BOARD?.nav?.compile) {
+ delType = 'ARDUINO';
+ } else {
+ delType = 'PYTHON';
+ }
+ } else {
+ delType = 'MIXLY';
+ }
+ switch (mId) {
+ case 'cloud-import':
+ const cloudTableId = menuStatus[1] ? 'cloud-code-lib-table' : 'cloud-mixly-lib-table';
+ const cloudCheckStatus = table.checkStatus(cloudTableId);
+ var selected = cloudCheckStatus.data;
+ var libList = [];
+ for (let i = 0; i < selected.length; i++) {
+ libList.push({
+ desPath: thirdPartyPath,
+ ...selected[i]
+ });
+ }
+ if (libList.length > 0) {
+ LibManager.showCloudImportProgress(libList);
+ } else {
+ layer.msg(Msg.Lang['libManager.selectAtLeastOneLibInfo'], { time: 1000 });
+ }
+ break;
+ case 'local-import':
+ LibManager.showLocalImportDialog(importType);
+ break;
+ case 'del':
+ const delTableId = menuStatus[2] ? 'del-code-lib-table' : 'del-mixly-lib-table';
+ const delCheckStatus = table.checkStatus(delTableId);
+ var selected = delCheckStatus.data;
+ var libPathList = [];
+ for (let i = 0; i < selected.length; i++) {
+ libPathList.push(selected[i].path);
+ }
+ if (libPathList.length > 0) {
+ LibManager.showDelLibsProgress((index) => {
+ LibManager.delLibs(delType, libPathList, index);
+ });
+ } else {
+ layer.msg(Msg.Lang['libManager.selectAtLeastOneLibInfo'], { time: 1000 });
+ }
+ break;
+ case 'open-folder':
+ const arduinoLibPath = path.join(Env.boardDirPath, 'libraries/myLib');
+ const pyLibPath = path.join(Env.boardDirPath, 'build/lib');
+ let needOpenedPath;
+ switch (delType) {
+ case 'ARDUINO':
+ needOpenedPath = arduinoLibPath;
+ break;
+ case 'PYTHON':
+ needOpenedPath = pyLibPath;
+ break;
+ default:
+ needOpenedPath = thirdPartyPath;
+ }
+ fs_extra.ensureDir(needOpenedPath)
+ .then(() => {
+ return shell.openPath(needOpenedPath);
+ })
+ .catch(console.log);
+ }
+ });
+}
+
+LibManager.showManageDialog = () => {
+ const htmlStr = XML.render(XML.TEMPLATE_STR.LIB_MANAGER_DIV, {
+ importBoard: Msg.Lang['libManager.import'],
+ delBoard: Msg.Lang['libManager.manage'],
+ mixlyLib: 'Mixly',
+ codeLib: BOARD?.nav?.compile? 'Arduino' : 'Python',
+ cloudImport: Msg.Lang['libManager.import.cloud'],
+ localImport: Msg.Lang['libManager.import.local'],
+ openFolder: Msg.Lang['file.openFolder'],
+ del: Msg.Lang['libManager.delete']
+ });
+ LayerExt.open({
+ title: [Msg.Lang['libManager.layerTitle'], '35px'],
+ id: 'lib-manage-layer',
+ content: htmlStr,
+ shade: LayerExt.SHADE_ALL,
+ area: ['60%', '60%'],
+ min: ['400px', '200px'],
+ max: ['800px', '400px'],
+ success: (layero) => {
+ LibManager.onclickImportLibs();
+ LibManager.menuAddEvent(layero);
+ }
+ });
+}
+
+LibManager.compareLibConfig = (cloudConfig) => {
+ const { version, url } = cloudConfig;
+ cloudConfig.downloadIndex = true;
+ const libDir = path.join(Env.boardDirPath, 'libraries/ThirdParty', path.basename(url, '.zip'));
+ if (fs_plus.isDirectorySync(libDir)) {
+ const localConfigPath = path.join(libDir, './config.json');
+ const localConfig = fs_extra.readJsonSync(localConfigPath, { throws: false });
+ if (localConfig !== null) {
+ if (localConfig.version === version)
+ cloudConfig.downloadIndex = false;
+ }
+ if (cloudConfig.downloadIndex)
+ cloudConfig.status = Msg.Lang['libManager.pendingUpgrade'];
+ else
+ cloudConfig.status = Msg.Lang['libManager.installed'];
+ } else {
+ cloudConfig.status = Msg.Lang['libManager.notInstalled'];
+ }
+ return cloudConfig;
+}
+
+LibManager.onclickImportLibs = () => {
+ // 显示mixly云端库
+ let mixlyTableConfig = {
+ id: 'cloud-mixly-lib-table',
+ elem: '#import-mixly-lib-page',
+ data: [],
+ defaultToolbar: [],
+ title: Msg.Lang['libManager.lib.cloud'],
+ cols: [[
+ { type: 'checkbox', unresize: false, align: "center" },
+ { field: 'status', title: Msg.Lang['libManager.lib.status'], sort: true, unresize: false, align: "center", minWidth: 100 },
+ { field: 'name', title: Msg.Lang['libManager.lib.name'], sort: true, unresize: false, align: "center", minWidth: 200 },
+ { field: 'version', title: Msg.Lang['libManager.lib.version'], unresize: false, align: "center", minWidth: 80 },
+ { field: 'desc', title: Msg.Lang['libManager.lib.desc'], unresize: false, align: "center", minWidth: 250 }
+ ]],
+ limit: 1000,
+ skin: 'line',
+ even: false,
+ size: 'sm'
+ };
+ // 显示code云端库
+ let codeTableConfig = {
+ id: 'cloud-code-lib-table',
+ elem: '#import-code-lib-page',
+ data: [],
+ defaultToolbar: [],
+ title: Msg.Lang['libManager.lib.cloud'],
+ cols: [[
+ { type: 'checkbox', unresize: false, align: "center" },
+ { field: 'name', title: Msg.Lang['libManager.lib.name'], sort: true, unresize: false, align: "center", minWidth: 200 },
+ { field: 'version', title: Msg.Lang['libManager.lib.version'], unresize: false, align: "center", minWidth: 80 },
+ { field: 'desc', title: Msg.Lang['libManager.lib.desc'], unresize: false, align: "center", minWidth: 250 }
+ ]],
+ limit: 1000,
+ skin: 'line',
+ even: false,
+ size: 'sm'
+ };
+ table.render({
+ ...mixlyTableConfig,
+ text: {
+ none: Msg.Lang['libManager.json.downloading'] + '...'
+ }
+ });
+ table.render({
+ ...codeTableConfig,
+ text: {
+ none: Msg.Lang['libManager.json.downloading'] + '...'
+ }
+ });
+
+ table.on('row(import-mixly-lib-page-filter)', function(obj) {
+ let $checkbox = obj.tr.first().find('.layui-form-checkbox');
+ obj.setRowChecked({
+ checked: !$checkbox.hasClass('layui-form-checked')
+ });
+ });
+
+ const thirdPartyPath = path.join(Env.boardDirPath, './libraries/ThirdParty');
+ if (LibManager.URL.mixly) {
+ CloudDownload.getJson(LibManager.URL.mixly, thirdPartyPath, (message) => {
+ if (message[0]) {
+ console.log(message[0]);
+ table.render({
+ ...mixlyTableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ return;
+ }
+ let libsConfig = [];
+ for (let i of message[1]) {
+ libsConfig.push(LibManager.compareLibConfig({ ...i }));
+ }
+ mixlyTableConfig.data = libsConfig;
+ table.render(mixlyTableConfig);
+ });
+ } else {
+ table.render({
+ ...mixlyTableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ }
+ let codeLibPath, codeLibUrl;
+ if (BOARD?.nav?.compile) {
+ codeLibPath = path.join(Env.boardDirPath, './libraries/myLib');
+ codeLibUrl = LibManager.URL.arduino;
+ } else {
+ codeLibPath = path.join(Env.boardDirPath, './build/lib');
+ codeLibUrl = LibManager.URL.python;
+ }
+ if (codeLibUrl) {
+ CloudDownload.getJson(codeLibUrl, codeLibPath, (message) => {
+ if (message[0]) {
+ console.log(message[0]);
+ table.render({
+ ...codeTableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ return;
+ }
+ let libsConfig = [];
+ for (let i of message[1]) {
+ libsConfig.push(LibManager.compareLibConfig({ ...i }));
+ }
+ codeTableConfig.data = libsConfig;
+ table.render(codeTableConfig);
+ });
+ } else {
+ table.render({
+ ...codeTableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ }
+}
+
+LibManager.onclickManageLibs = () => {
+ // 显示mixly云端库
+ let mixlyTableConfig = {
+ id: 'del-mixly-lib-table',
+ elem: '#del-mixly-lib-page',
+ data: [],
+ defaultToolbar: [],
+ title: Msg.Lang['libManager.manage'],
+ cols: [[
+ { type: 'checkbox', unresize: false, align: "center" },
+ { field: 'name', title: Msg.Lang['libManager.lib.name'], sort: true, unresize: false, align: "center", minWidth: 150 },
+ { field: 'path', title: Msg.Lang['libManager.lib.path'], unresize: false, align: "center", minWidth: 300 }
+ ]],
+ limit: 1000,
+ skin: 'line',
+ even: false,
+ size: 'sm'
+ };
+ // 显示code云端库
+ let codeTableConfig = {
+ id: 'del-code-lib-table',
+ elem: '#del-code-lib-page',
+ data: [],
+ defaultToolbar: [],
+ title: Msg.Lang['libManager.manage'],
+ cols: mixlyTableConfig.cols,
+ limit: 1000,
+ skin: 'line',
+ even: false,
+ size: 'sm'
+ };
+ table.render({
+ ...mixlyTableConfig,
+ text: {
+ none: Msg.Lang['libManager.lib.local.reading'] + '...'
+ }
+ });
+ table.render({
+ ...codeTableConfig,
+ text: {
+ none: Msg.Lang['libManager.lib.local.reading'] + '...'
+ }
+ });
+ const thirdPartyPath = path.join(Env.boardDirPath, 'libraries/ThirdParty');
+ const arduinoLibPath = path.join(Env.boardDirPath, 'libraries/myLib');
+ const pythonLibPath = path.join(Env.boardDirPath, 'build/lib');
+ let codeLibPath = BOARD?.nav?.compile ? arduinoLibPath : pythonLibPath;
+ let needReadPathList = [{
+ path: thirdPartyPath,
+ tableConfig: mixlyTableConfig
+ }, {
+ path: codeLibPath,
+ tableConfig: codeTableConfig
+ }];
+ for (let needRead of needReadPathList) {
+ if (fs_plus.isDirectorySync(needRead.path)) {
+ fs.readdir(needRead.path, (error, readList) => {
+ if (error) {
+ console.log(error);
+ table.render({
+ ...needRead.tableConfig,
+ text: {
+ none: Msg.Lang['libManager.lib.local.readFailed']
+ }
+ });
+ return;
+ }
+ let data = [];
+ for (let read of readList) {
+ const extname = path.extname(read);
+ if (extname === '.json') continue;
+ data.push({
+ name: read,
+ path: path.join(needRead.path, read)
+ });
+ }
+ if (data.length)
+ table.render({
+ ...needRead.tableConfig,
+ data
+ });
+ else
+ table.render({
+ ...needRead.tableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ });
+ } else {
+ table.render({
+ ...needRead.tableConfig,
+ text: {
+ none: Msg.Lang['libManager.empty']
+ }
+ });
+ }
+ }
+}
+
+LibManager.showLocalImportDialog = (type) => {
+ const currentWindow = electron_remote.getCurrentWindow();
+ currentWindow.focus();
+ let layerNum;
+ dialog.showOpenDialog(currentWindow, {
+ title: Msg.Lang['libManager.import'],
+ defaultPath: Env.clientPath,
+ buttonLabel: Msg.Lang['nav.btn.ok'],
+ // 限制能够选择的文件类型
+ filters: LibManager.LOCAL_IMPORT_FILTERS[type],
+ properties: ['openFile', 'showHiddenFiles'],
+ message: Msg.Lang['libManager.import']
+ }).then(result => {
+ const selectedPath = result.filePaths[0];
+
+ if (!selectedPath) return;
+
+ console.log('待导入文件路径:', selectedPath);
+
+ LibManager.importFromLocal(type, selectedPath, () => {
+ layerNum = layer.open({
+ type: 1,
+ id: "import-local-lib-layer",
+ title: Msg.Lang['libManager.lib.importing'] + "...",
+ content: $('#mixly-loader-div'),
+ shade: LayerExt.SHADE_ALL,
+ closeBtn: 0,
+ success: function (layero) {
+ $("#mixly-loader-btn").css('display', 'none');
+ },
+ end: function () {
+ $("#mixly-loader-btn").css('display', 'inline-block');
+ $('#layui-layer-shade' + layerNum).remove();
+ }
+ });
+ }, (error) => {
+ console.log(error);
+ layer.msg(error, { time: 1000 });
+ }, (error) => {
+ layer.close(layerNum);
+ if (error) {
+ console.log(error);
+ layer.msg(Msg.Lang['libManager.lib.importFailed'], { time: 1000 });
+ } else {
+ layer.msg(Msg.Lang['libManager.lib.importSucc'], { time: 1000 });
+ }
+ });
+ }).catch(error => {
+ layer.close(layerNum);
+ console.log(error);
+ layer.msg(Msg.Lang['libManager.lib.importFailed'], { time: 1000 });
+ });
+}
+
+LibManager.importFromLocal = (type, inPath, sucFunc, errorFunc, endFunc) => {
+ const extname = path.extname(inPath);
+ const pyLibPath = path.join(Env.boardDirPath, './build/lib');
+ const thirdPartyPath = path.join(Env.boardDirPath, './libraries/ThirdParty');
+ if (fs_plus.isFileSync(inPath)) {
+ switch (extname) {
+ case '.py':
+ var dirPath = path.join(inPath, '../');
+ if (pyLibPath === dirPath) {
+ errorFunc(Msg.Lang['libManager.lib.exist']);
+ return;
+ }
+ sucFunc();
+ LibManager.importFromLocalWithFile(type, inPath, endFunc);
+ break;
+ case '.mil':
+ var dirPath = path.join(inPath, '../');
+ if (thirdPartyPath === dirPath) {
+ errorFunc(Msg.Lang['libManager.lib.exist']);
+ return;
+ }
+ sucFunc();
+ LibManager.importFromLocalWithFile(type, inPath, endFunc);
+ break;
+ case '.xml':
+ var dirPath = path.join(inPath, '../../');
+ if (dirPath === thirdPartyPath) {
+ errorFunc(Msg.Lang['libManager.lib.exist']);
+ return;
+ }
+ sucFunc();
+ LibManager.importFromLocalWithFile(type, inPath, endFunc);
+ break;
+ case '.zip':
+ sucFunc();
+ LibManager.importFromLocalWithZip(type, inPath, endFunc);
+ break;
+ default:
+ errorFunc(Msg.Lang['file.type.error']);
+ }
+ } else {
+ errorFunc(Msg.Lang['file.notExist']);
+ }
+}
+
+LibManager.importFromLocalWithFile = (type, filePath, endFunc) => {
+ const thirdPartyPath = path.join(Env.boardDirPath, './libraries/ThirdParty');
+ const extname = path.extname(filePath);
+ const basename = path.basename(filePath);
+ let copyPromiseList = [];
+ switch (extname) {
+ case '.xml':
+ const dirPath = path.dirname(filePath);
+ const dirName = path.basename(dirPath);
+ const desPath = path.join(thirdPartyPath, dirName)
+ copyPromiseList.push(LibManager.copyDir(dirPath, desPath));
+ break;
+ case '.mil':
+ const milPath = path.join(thirdPartyPath, path.basename(filePath));
+ copyPromiseList.push(LibManager.copyFile(filePath, milPath));
+ break;
+ case '.py':
+ const pyPath = path.join(Env.boardDirPath, 'build/lib', path.basename(filePath));
+ copyPromiseList.push(LibManager.copyFile(filePath, pyPath));
+ break;
+ default:
+ layer.msg(Msg.Lang['file.type.error'], { time: 1000 });
+ endFunc(Msg.Lang['file.type.error']);
+ }
+ Promise.all(copyPromiseList)
+ .then((message) => {
+ if (type === 'MIXLY') {
+ LibManager.reloadThirdPartyLibs();
+ }
+ endFunc(null);
+ })
+ .catch((error) => {
+ endFunc(error);
+ })
+ .finally(() => {
+ });
+}
+
+LibManager.importFromLocalWithZip = (type, filePath, endFunc) => {
+ const thirdPartyPath = path.join(Env.boardDirPath, './libraries/ThirdParty/');
+ const myLibPath = path.join(Env.boardDirPath, './libraries/myLib/');
+ let desPath;
+ switch (type) {
+ case 'MIXLY':
+ desPath = thirdPartyPath;
+ break;
+ case 'ARDUINO':
+ desPath = myLibPath;
+ break;
+ default:
+ endFunc(Msg.Lang['libManager.lib.unzipFailed']);
+ return;
+ }
+ LibManager.unZip(filePath, desPath, false, (error) => {
+ if (type === 'MIXLY')
+ LibManager.reloadThirdPartyLibs();
+ endFunc(error);
+ });
+}
+
+LibManager.delLibs = (type, libPathList, layerNum) => {
+ let delPromiseList = [];
+ for (let libPath of libPathList) {
+ delPromiseList.push(new Promise((resolve, reject) => {
+ fs_extra.remove(libPath, (error) => {
+ resolve(error);
+ });
+ }));
+ }
+ Promise.all(delPromiseList)
+ .then((message) => {
+ LibManager.onclickManageLibs();
+ if (type === 'MIXLY') {
+ LibManager.reloadThirdPartyLibs();
+ }
+ })
+ .catch((error) => {
+ console.log(error);
+ })
+ .finally(() => {
+ layer.close(layerNum);
+ });
+}
+
+LibManager.showDelLibsProgress = (sucFunc) => {
+ const layerNum = layer.open({
+ type: 1,
+ id: "del-local-lib-layer",
+ title: Msg.Lang['libManager.lib.deleting'] + "...",
+ content: $('#mixly-loader-div'),
+ shade: LayerExt.SHADE_ALL,
+ closeBtn: 0,
+ success: function (layero, index) {
+ $("#mixly-loader-btn").css('display', 'none');
+ sucFunc(index);
+ },
+ end: function () {
+ $('#layui-layer-shade' + layerNum).remove();
+ $("#mixly-loader-btn").css('display', 'inline-block');
+ }
+ });
+}
+
+LibManager.unZip = (inPath, desPath, delZip, endFunc = (errorMessage) => {}) => {
+ const dirName = path.basename(inPath, '.zip');
+ const unZipPath = path.join(desPath, dirName);
+ compressing.zip.uncompress(inPath, desPath, {
+ zipFileNameEncoding: 'GBK'
+ })
+ .then(() => {
+ if (delZip)
+ try {
+ fs.unlinkSync(inPath);
+ } catch (error) {
+ console.log(error);
+ }
+ endFunc(null);
+ })
+ .catch((error) => {
+ endFunc(error);
+ });
+}
+
+LibManager.copyDir = (startPath, endPath) => {
+ return new Promise((resolve, reject) => {
+ fs_extra.ensureDir(endPath)
+ .then(() => {
+ return fs_extra.emptyDir(endPath);
+ })
+ .then(() => {
+ return fs_extra.copy(startPath, endPath);
+ })
+ .then(() => {
+ resolve({ error: null, endPath });
+ })
+ .catch((error) => {
+ resolve({ error, endPath });
+ });
+ });
+}
+
+LibManager.copyFile = (startPath, endPath) => {
+ return new Promise((resolve, reject) => {
+ const endDirPath = path.dirname(endPath);
+ fs_extra.ensureDir(endDirPath)
+ .then(() => {
+ return fs_extra.copy(startPath, endPath);
+ })
+ .then(() => {
+ resolve({ error: null, endPath });
+ })
+ .catch((error) => {
+ resolve({ error, endPath });
+ });
+ });
+}
+
+LibManager.showCloudImportProgress = (importList, endFunc = (errorMessages) => {}) => {
+ const parentDom = $('
');
+ parentDom.css({
+ 'overflow': 'hidden',
+ 'width': '100%',
+ 'height': '100%',
+ 'display': 'none'
+ });
+ const childDom = $('
');
+ childDom.css({
+ 'overflow-x': 'hidden',
+ 'overflow-y': 'auto',
+ 'left': '5px',
+ 'right': '5px',
+ 'top': '5px',
+ 'bottom': '5px',
+ 'position': 'absolute'
+ });
+ for (let i in importList) {
+ const info = importList[i];
+ const panelConfig = {
+ panelId: i + '-panel-id',
+ name: info.name,
+ progressFilter: i + '-progress-filter',
+ progressStatusId: i + '-progress-status-id'
+ };
+ childDom.append(
+ XML.render(XML.TEMPLATE_STR.PROGRESS_BAR_DIV, panelConfig)
+ );
+ }
+ parentDom.append(childDom);
+ $('body').append(parentDom);
+ element.render('progress');
+ LayerExt.open({
+ title: Msg.Lang['libManager.lib.importing'] + '...',
+ id: 'setting-menu-layer1',
+ content: parentDom,
+ shade: LayerExt.SHADE_ALL,
+ area: ['40%', '60%'],
+ max: ['800px', (importList.length * 106 + 42) + 'px'],
+ min: ['500px', '100px'],
+ success: (layero, index) => {
+ layero.find('.layui-layer-setwin').css('display', 'none');
+ LibManager.importFromCloud(importList, layero, index);
+ },
+ end: () => {
+ parentDom.remove();
+ }
+ });
+}
+
+LibManager.importFromCloud = (importList, layero, layerIndex) => {
+ let importPromise = [];
+ for (let i in importList) {
+ importList[i].index = i;
+ importPromise.push(LibManager.importWithUrl(importList[i]));
+ }
+
+ Promise.all(importPromise)
+ .then((message) => {
+ let sucTimes = 0;
+ let failedTimes = 0;
+ for (let i of message) {
+ const progressStatusDom = $('#' + i.index + '-progress-status-id');
+ const panelDom = $('#' + i.index + '-panel-id');
+ const cardHeadDom = panelDom.children('.layui-card-header').first();
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ if (i.error) {
+ progressStatusDom.addClass('layui-icon-close-fill');
+ cardHeadDom.html(i.name + ' - ' + Msg.Lang['libManager.lib.importFailed']);
+ failedTimes++;
+ console.log(i.error);
+ } else {
+ progressStatusDom.addClass('layui-icon-ok-circle');
+ cardHeadDom.html(i.name + ' - ' + Msg.Lang['libManager.lib.importSucc']);
+ sucTimes++;
+ // BoardManager.writePackageConfig(i);
+ }
+ }
+ const sucIcon = `
`;
+ const errIcon = `
`;
+ layer.title(Msg.Lang['libManager.lib.importSucc'] + ' ' + sucTimes + sucIcon + ' ' + failedTimes + errIcon, layerIndex);
+ })
+ .catch((error) => {
+ layer.title(Msg.Lang['libManager.lib.importFailed'], layerIndex);
+ })
+ .finally(() => {
+ layero.find('.layui-layer-setwin').css('display', 'block');
+ LibManager.onclickImportLibs();
+ LibManager.reloadThirdPartyLibs();
+ });
+}
+
+LibManager.writeLibConfig = (info) => {
+ const {
+ desPath,
+ version,
+ name,
+ url
+ } = info;
+ if (!name) return;
+ const libDir = path.join(desPath, path.basename(url, '.zip'));
+ if (fs_plus.isDirectorySync(libDir)) {
+ const configPath = path.join(libDir, 'config.json');
+ const libConfig = {
+ version
+ };
+ try {
+ fs_extra.outputJsonSync(configPath, libConfig, {
+ spaces: ' '
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ }
+}
+
+/*{
+ "index": Number,
+ "desPath": String,
+ "error": Object,
+ "name": "TFT",
+ "version": "1.0.0",
+ "desc": "TFT彩色屏幕扩展库",
+ "url": "https://gitee.com/mixly2/cloud-libraries/raw/master/cloud-libs/c_common/TFT.zip"
+}*/
+LibManager.importWithUrl = (info) => {
+ return new Promise((resolve, reject) => {
+ if (!info.url) {
+ info.error = 'url读取出错';
+ resolve(info);
+ }
+ // 下载板卡文件
+ LibManager.downloadPromise(info, {
+ desPath: path.join(Env.clientPath, './download'),
+ url: info.downloadIndex ? info.url : 'None',
+ startMessage: Msg.Lang['libManager.lib.donwloading'] + '...',
+ endMessage: Msg.Lang['libManager.lib.donwloadSucc'],
+ errorMessage: Msg.Lang['libManager.lib.donwloadFailed']
+ })
+ .then((newInfo) => {
+ if (newInfo.error)
+ throw newInfo.error;
+ else
+ // 解压板卡文件
+ return LibManager.unZipPromise(newInfo, {
+ desPath: newInfo.desPath,
+ zipPath: newInfo.downloadPath,
+ startMessage: Msg.Lang['libManager.lib.unzipping'] + '...',
+ endMessage: Msg.Lang['libManager.lib.unzipSucc'],
+ errorMessage: Msg.Lang['libManager.lib.unzipFailed']
+ });
+ })
+ .then((newInfo) => {
+ if (newInfo.error)
+ throw newInfo.error;
+ const panelDom = $('#' + newInfo.index + '-panel-id');
+ const cardHeadDom = panelDom.children('.layui-card-header').first();
+ const progressStatusDom = $('#' + newInfo.index + '-progress-status-id');
+ if (!info.downloadIndex) {
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ cardHeadDom.html(newInfo.name + ' - ' + Msg.Lang['libManager.lib.latestVersion']);
+ progressStatusDom.addClass('layui-icon-ok-circle');
+ element.progress(newInfo.index + '-progress-filter', '100%');
+ element.render('progress', newInfo.index + '-progress-filter');
+ } else {
+ LibManager.writeLibConfig(newInfo);
+ }
+ resolve(newInfo);
+ })
+ .catch((error) => {
+ info.error = error;
+ resolve(info);
+ });
+ });
+}
+
+LibManager.downloadPromise = (info, config) => {
+ return new Promise((resolve, reject) => {
+ const DEFAULT_CONFIG = {
+ desPath: path.join(Env.clientPath, './download'),
+ url: null,
+ startMessage: Msg.Lang['libManager.lib.donwloading'] + '...',
+ endMessage: Msg.Lang['libManager.lib.donwloadSucc'],
+ errorMessage: Msg.Lang['libManager.lib.donwloadFailed']
+ };
+ if (typeof config !== 'object')
+ config = DEFAULT_CONFIG
+ else
+ config = { ...DEFAULT_CONFIG, ...config };
+
+ const {
+ desPath,
+ url,
+ startMessage,
+ endMessage,
+ errorMessage
+ } = config;
+
+ if (!url || url === 'None' || !desPath) {
+ info.error = null;
+ info.downloadPath = null;
+ resolve(info);
+ return;
+ }
+
+ try {
+ fs_extra.ensureDirSync(desPath);
+ } catch (error) {
+ info.error = error;
+ info.downloadPath = null;
+ resolve(info);
+ return;
+ }
+ const panelDom = $('#' + info.index + '-panel-id');
+ const cardHeadDom = panelDom.children('.layui-card-header').first();
+ cardHeadDom.html(info.name + ' - ' + startMessage);
+ const progressStatusDom = $('#' + info.index + '-progress-status-id');
+ progressStatusDom.removeClass('layui-icon-ok-circle layui-icon-close-fill');
+ progressStatusDom.addClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ element.progress(info.index + '-progress-filter', '0%');
+ element.render('progress', info.index + '-progress-filter');
+ CloudDownload.download(url, desPath, {
+ progress: (stats) => {
+ const { speed, progress } = stats;
+ const speedUnit = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
+ let type = 0;
+ let nowProgress = parseInt(progress);
+ nowProgress = nowProgress > 100 ? 100 : nowProgress;
+ let nowSpeed = speed;
+ for (let i = 0; i < 3; i++) {
+ if (nowSpeed / 1000 > 1) {
+ nowSpeed /= 1024;
+ type++;
+ } else {
+ break;
+ }
+ }
+
+ nowSpeed = nowSpeed.toFixed(1);
+ cardHeadDom.html(info.name + ' - ' + startMessage + ' - ' + nowSpeed + speedUnit[type]);
+ element.progress(info.index + '-progress-filter', parseInt(progress) + '%');
+ element.render('progress', info.index + '-progress-filter');
+ },
+ error: (message) => {
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ progressStatusDom.addClass('layui-icon-close-fill');
+ cardHeadDom.html(info.name + ' - ' + errorMessage);
+ },
+ end: (downloadInfo) => {
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ progressStatusDom.addClass('layui-icon-ok-circle');
+ cardHeadDom.html(info.name + ' - ' + endMessage);
+ },
+ timeout: this.error
+ })
+ .then((message) => {
+ if (message[0])
+ throw message[0];
+ info.error = null;
+ info.downloadPath = message[1];
+ resolve(info);
+ })
+ .catch((error) => {
+ info.error = error;
+ info.downloadPath = null;
+ resolve(info);
+ });
+ });
+}
+
+LibManager.unZipPromise = (info, config) => {
+ return new Promise((resolve, reject) => {
+ const DEFAULT_CONFIG = {
+ desPath: path.join(Env.clientPath, './download'),
+ zipPath: null,
+ startMessage: Msg.Lang['libManager.lib.unzipping'] + '...',
+ endMessage: Msg.Lang['libManager.lib.unzipSucc'],
+ errorMessage: Msg.Lang['libManager.lib.unzipFailed'],
+ delZip: true
+ };
+ if (typeof config !== 'object')
+ config = DEFAULT_CONFIG
+ else
+ config = { ...DEFAULT_CONFIG, ...config };
+
+ const {
+ zipPath,
+ desPath,
+ delZip,
+ startMessage,
+ endMessage,
+ errorMessage
+ } = config;
+
+ if (!zipPath || !desPath) {
+ info.error = null;
+ info.unZipPath = null;
+ resolve(info);
+ return;
+ }
+ const panelDom = $('#' + info.index + '-panel-id');
+ const cardHeadDom = panelDom.children('.layui-card-header').first();
+ cardHeadDom.html(info.name + ' - ' + startMessage);
+ const progressStatusDom = $('#' + info.index + '-progress-status-id');
+ progressStatusDom.removeClass('layui-icon-ok-circle layui-icon-close-fill');
+ progressStatusDom.addClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ element.progress(info.index + '-progress-filter', '0%');
+ element.render('progress', info.index + '-progress-filter');
+ LibManager.unZip(zipPath, desPath, delZip, (error) => {
+ if (error) {
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ progressStatusDom.addClass('layui-icon-close-fill');
+ cardHeadDom.html(info.name + ' - ' + errorMessage);
+ info.error = error;
+ info.unZipPath = null;
+ resolve(info);
+ } else {
+ progressStatusDom.removeClass('layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop');
+ progressStatusDom.addClass('layui-icon-ok-circle');
+ cardHeadDom.html(info.name + ' - ' + endMessage);
+ info.error = null;
+ info.unZipPath = desPath;
+ element.progress(info.index + '-progress-filter', '100%');
+ element.render('progress', info.index + '-progress-filter');
+ resolve(info);
+ }
+ })
+ });
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/loader.js b/mixly/common/modules/mixly-modules/electron/loader.js
new file mode 100644
index 00000000..47d07be9
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/loader.js
@@ -0,0 +1,64 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Url');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.Electron.Serial');
+goog.require('Mixly.Electron.PythonShell');
+goog.require('Mixly.Electron.Events');
+goog.provide('Mixly.Electron.Loader');
+
+const {
+ Url,
+ Config,
+ Env,
+ Electron
+} = Mixly;
+
+const { BOARD } = Config;
+
+const {
+ Serial,
+ PythonShell,
+ Loader
+} = Electron;
+
+
+Loader.onbeforeunload = function(reload = false) {
+ const pageReload = (href) => {
+ if (!reload) {
+ window.location.replace(href);
+ } else {
+ window.location.reload(true);
+ }
+ }
+ let href = path.join(Env.srcDirPath, 'index.html') + '?' + Url.jsonToUrl({ boardType: BOARD.boardType ?? 'None' });
+ let endPromise = [];
+ const { mainStatusBarTabs } = Mixly;
+ Serial.getCurrentPortsName().map((name) => {
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(name);
+ if (statusBarSerial) {
+ endPromise.push(statusBarSerial.close());
+ }
+ });
+ endPromise.push(PythonShell.stop());
+ Promise.all(endPromise)
+ .finally(() => {
+ pageReload(href);
+ });
+};
+
+Loader.closePort = (serialport) => {
+ return new Promise((resolve, reject) => {
+ serialport.close(() => {
+ resolve();
+ });
+ })
+}
+
+Loader.reload = () => {
+ Loader.onbeforeunload(true);
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/python-shell.js b/mixly/common/modules/mixly-modules/electron/python-shell.js
new file mode 100644
index 00000000..ebe716c2
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/python-shell.js
@@ -0,0 +1,214 @@
+goog.loadJs('electron', () => {
+
+goog.require('dayjs.duration');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.MString');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.PythonShell');
+
+const {
+ Env,
+ Msg,
+ Debug,
+ Workspace,
+ MString,
+ Electron
+} = Mixly;
+
+const iconv_lite = Mixly.require('iconv-lite');
+const python_shell = Mixly.require('python-shell');
+const child_process = Mixly.require('node:child_process');
+const fs_extra = Mixly.require('fs-extra');
+
+
+class PythonShell {
+ static {
+ this.ENCODING = Env.currentPlatform == 'win32' ? 'cp936' : 'utf-8';
+ this.pythonShell = null;
+
+ this.init = function () {
+ this.pythonShell = new PythonShell();
+ }
+
+ this.run = function () {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ return this.pythonShell.run(code);
+ }
+
+ this.stop = function () {
+ return this.pythonShell.stop();
+ }
+ }
+
+ #shell_ = null;
+ #statusBarTerminal_ = null;
+ #options_ = {
+ pythonPath: Env.python3Path,
+ pythonOptions: ['-u'],
+ encoding: 'binary',
+ mode: 'utf-8'
+ };
+ #cursor_ = {
+ row: 0,
+ column: 0
+ };
+ #prompt_ = '';
+ #waittingForInput_ = false;
+ #running_ = false;
+ #onCursorChangeEvent_ = () => this.#onCursorChange_();
+ #commands_ = [
+ {
+ name: 'REPL-Enter',
+ bindKey: 'Enter',
+ exec: (editor) => {
+ const session = editor.getSession();
+ const cursor = session.selection.getCursor();
+ if (cursor.row === this.#cursor_.row) {
+ const newPos = this.#statusBarTerminal_.getEndPos();
+ let str = this.#statusBarTerminal_.getValueRange(this.#cursor_, newPos);
+ str = str.replace(this.#prompt_, '');
+ this.#shell_.stdin.write(escape(str) + '\n');
+ this.#statusBarTerminal_.addValue('\n');
+ this.#exitInput_();
+ return true;
+ }
+ return false;
+ }
+ }, {
+ name: 'REPL-ChangeEditor',
+ bindKey: 'Delete|Ctrl-X|Backspace',
+ exec: (editor) => {
+ const session = editor.getSession();
+ const cursor = session.selection.getCursor();
+ if (cursor.row < this.#cursor_.row || cursor.column <= this.#cursor_.column) {
+ return true;
+ }
+ return false;
+ }
+ }
+ ];
+ constructor(config) {
+ Object.assign(this.#options_, config);
+ const { mainStatusBarTabs } = Mixly;
+ this.#statusBarTerminal_ = mainStatusBarTabs.getStatusBarById('output');
+ }
+
+ #addEventsListener_() {
+ const { stdout, stderr } = this.#shell_;
+ stdout.setEncoding('binary');
+ stdout.on('data', (data) => {
+ data = iconv_lite.decode(Buffer.from(data, 'binary'), PythonShell.ENCODING);
+ data = MString.decode(data);
+ this.#statusBarTerminal_.addValue(data);
+ const keyIndex = data.lastIndexOf('>>>');
+ if (keyIndex !== -1) {
+ this.#prompt_ = data.substring(keyIndex);
+ setTimeout(() => this.#enterInput_(), 500);
+ }
+ });
+ stderr.setEncoding('binary');
+ stderr.on('data', (data) => {
+ data = iconv_lite.decode(Buffer.from(data, 'binary'), PythonShell.ENCODING);
+ data = MString.decode(data);
+ data = data.replace(/(? {
+ try {
+ code = code.replace(/(? {
+ this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
+ const startTime = Number(new Date());
+ this.#shell_ = new python_shell.PythonShell(Env.pyFilePath, this.#options_);
+ this.#running_ = true;
+ this.#addEventsListener_();
+ this.#shell_.on('close', (code) => {
+ this.#running_ = false;
+ const endTime = Number(new Date());
+ const duration = dayjs.duration(endTime - startTime).format('HH:mm:ss.SSS');
+ this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}(${Msg.Lang['shell.timeCost']} ${duration})==`);
+ });
+ })
+ .catch(Debug.error);
+ }
+
+ stop() {
+ return new Promise((resolve, reject) => {
+ if (this.#waittingForInput_) {
+ this.#exitInput_();
+ }
+ if (this.#running_) {
+ this.#shell_.childProcess.on('exit', () => {
+ resolve();
+ });
+ this.#shell_.stdin.end();
+ this.#shell_.stdout.end();
+ if (Env.currentPlatform === 'win32') {
+ child_process.exec(`taskkill /pid ${this.#shell_.childProcess.pid} /f /t`);
+ } else {
+ this.#shell_.kill('SIGTERM');
+ }
+ } else {
+ resolve();
+ }
+ });
+ }
+}
+
+Electron.PythonShell = PythonShell;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/serial.js b/mixly/common/modules/mixly-modules/electron/serial.js
new file mode 100644
index 00000000..22f4a6db
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/serial.js
@@ -0,0 +1,272 @@
+goog.loadJs('electron', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.MArray');
+goog.require('Mixly.Config');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.Serial');
+
+const child_process = Mixly.require('node:child_process');
+const serialport = Mixly.require('serialport');
+
+const {
+ SerialPort,
+ ReadlineParser,
+ ByteLengthParser
+} = serialport;
+
+const {
+ Serial,
+ Env,
+ Msg,
+ Debug,
+ MArray,
+ Config,
+ Electron
+} = Mixly;
+
+const { BOARD } = Config;
+
+
+class ElectronSerial extends Serial {
+ static {
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.getPorts = async function () {
+ return new Promise((resolve, reject) => {
+ if (Env.currentPlatform === 'linux') {
+ child_process.exec('ls /dev/ttyACM* /dev/tty*USB*', (err, stdout, stderr) => {
+ let portsName = MArray.unique(stdout.split('\n'));
+ let newPorts = [];
+ for (let i = 0; i < portsName.length; i++) {
+ if (!portsName[i]) {
+ continue;
+ }
+ newPorts.push({
+ vendorId: 'None',
+ productId: 'None',
+ name: portsName[i]
+ });
+ }
+ resolve(newPorts);
+ });
+ } else {
+ SerialPort.list().then(ports => {
+ let newPorts = [];
+ for (let i = 0; i < ports.length; i++) {
+ let port = ports[i];
+ newPorts.push({
+ vendorId: port.vendorId,
+ productId: port.productId,
+ name: port.path
+ });
+ }
+ resolve(newPorts);
+ }).catch(reject);
+ }
+ });
+ }
+
+ this.refreshPorts = function () {
+ this.getPorts()
+ .then((ports) => {
+ Serial.renderSelectBox(ports);
+ })
+ .catch(Debug.error);
+ }
+ }
+
+ #serialport_ = null;
+ #parserBytes_ = null;
+ #parserLine_ = null;
+
+ constructor(port) {
+ super(port);
+ }
+
+ #addEventsListener_() {
+ this.#parserBytes_.on('data', (buffer) => {
+ this.onBuffer(buffer);
+ });
+
+ this.#parserLine_.on('data', (str) => {
+ this.onString(str);
+ });
+
+ this.#serialport_.on('error', (error) => {
+ this.onError(String(error));
+ this.onClose(1);
+ });
+
+ this.#serialport_.on('open', () => {
+ this.onOpen();
+ });
+
+ this.#serialport_.on('close', () => {
+ this.onClose(1);
+ });
+ }
+
+ async open(baud) {
+ return new Promise((resolve, reject) => {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPort = this.getPortName();
+ if (!portsName.includes(currentPort)) {
+ reject('无可用串口');
+ return;
+ }
+ if (this.isOpened()) {
+ resolve();
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+
+ this.#serialport_ = new SerialPort({
+ path: currentPort,
+ baudRate: baud, // 波特率
+ dataBits: 8, // 数据位
+ parity: 'none', // 奇偶校验
+ stopBits: 1, // 停止位
+ flowControl: false,
+ autoOpen: false, // 不自动打开
+ rtscts: BOARD?.serial?.rts ?? false
+ }, false);
+ this.#parserBytes_ = this.#serialport_.pipe(new ByteLengthParser({ length: 1 }));
+ this.#parserLine_ = this.#serialport_.pipe(new ReadlineParser());
+ this.#serialport_.open((error) => {
+ if (error) {
+ this.onError(error);
+ reject(error);
+ } else {
+ super.open(baud);
+ this.setBaudRate(baud);
+ resolve();
+ }
+ });
+ this.#addEventsListener_();
+ });
+ }
+
+ async close() {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ super.close();
+ this.#serialport_.close((error) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
+ });
+ }
+
+ async setBaudRate(baud) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()
+ || this.getRawBaudRate() === baud
+ || !this.baudRateIsLegal(baud)) {
+ resolve();
+ return;
+ }
+ this.#serialport_.update({ baudRate: baud }, (error) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ super.setBaudRate(baud);
+ this.setDTRAndRTS(this.getDTR(), this.getRTS()).finally(resolve);
+ });
+ });
+ }
+
+ async send(data) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ this.#serialport_.write(data, (error) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
+ });
+ }
+
+ async sendString(str) {
+ return this.send(str);
+ }
+
+ async sendBuffer(buffer) {
+ return this.send(buffer);
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ this.#serialport_.set({ dtr, rts }, (error) => {
+ if (error) {
+ reject(error);
+ } else {
+ super.setDTRAndRTS(dtr, rts);
+ resolve();
+ }
+ });
+ });
+ }
+
+ async setDTR(dtr) {
+ return this.setDTRAndRTS(dtr, this.getRTS());
+ }
+
+ async setRTS(rts) {
+ return this.setDTRAndRTS(this.getDTR(), rts);
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ }
+ }
+
+ async dispose() {
+ await super.dispose();
+ this.#parserBytes_ = null;
+ this.#parserLine_ = null;
+ this.#serialport_ = null;
+ }
+}
+
+Electron.Serial = ElectronSerial;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/shell.js b/mixly/common/modules/mixly-modules/electron/shell.js
new file mode 100644
index 00000000..50b4d5c7
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/shell.js
@@ -0,0 +1,96 @@
+goog.loadJs('electron', () => {
+
+goog.require('dayjs.duration');
+goog.require('Mixly.Env');
+goog.require('Mixly.MString');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.Shell');
+
+const {
+ Env,
+ MString,
+ Electron
+} = Mixly;
+
+const child_process = Mixly.require('node:child_process');
+const iconv_lite = Mixly.require('iconv-lite');
+
+class Shell {
+ static {
+ this.ENCODING = Env.currentPlatform == 'win32' ? 'cp936' : 'utf-8';
+ }
+
+ #shell_ = null;
+ #killed_ = false;
+ constructor() {}
+
+ #addEventsListener_() {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const { stdout, stderr } = this.#shell_;
+ stdout.on('data', (data) => {
+ if (data.length > 1000) {
+ return;
+ }
+ data = iconv_lite.decode(Buffer.from(data, 'binary'), 'utf-8');
+ statusBarTerminal.addValue(data);
+ });
+ stderr.on('data', (data) => {
+ let lines = data.split('\n');
+ for (let i in lines) {
+ let encoding = 'utf-8';
+ if (lines[i].indexOf('can\'t open device') !== -1) {
+ encoding = Shell.ENCODING;
+ }
+ lines[i] = iconv_lite.decode(Buffer.from(lines[i], 'binary'), encoding);
+ }
+ data = lines.join('\n');
+ data = MString.decode(data);
+ statusBarTerminal.addValue(data);
+ });
+ }
+
+ exec(command) {
+ return new Promise((resolve, reject) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const startTime = Number(new Date());
+ this.#shell_ = child_process.exec(command, {
+ maxBuffer: 4096 * 1000000,
+ encoding: 'binary'
+ });
+ this.#addEventsListener_();
+ this.#shell_.on('close', (code) => {
+ this.#killed_ = true;
+ if (code === null) {
+ code = 1;
+ }
+ const endTime = Number(new Date());
+ const duration = dayjs.duration(endTime - startTime).format('HH:mm:ss.SSS');
+ const info = { code, time: duration };
+ resolve(info);
+ });
+ });
+ }
+
+ kill() {
+ if (this.#killed_) {
+ return;
+ }
+ this.#shell_.stdin.end();
+ this.#shell_.stdout.end();
+ if (Env.currentPlatform === 'win32') {
+ child_process.exec(`taskkill /pid ${this.#shell_.pid} /f /t`);
+ } else {
+ this.#shell_.kill('SIGTERM');
+ }
+ }
+
+ getShell() {
+ return this.#shell_;
+ }
+}
+
+Mixly.Electron.Shell = Shell;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/wiki-generator.js b/mixly/common/modules/mixly-modules/electron/wiki-generator.js
new file mode 100644
index 00000000..93a50ef8
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/wiki-generator.js
@@ -0,0 +1,154 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Blockly');
+goog.require('Mustache');
+goog.require('Mixly.Env');
+goog.require('Mixly.IdGenerator');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.WikiGenerator');
+
+const {
+ Env,
+ IdGenerator,
+ Electron
+} = Mixly;
+const fs_extra = Mixly.require('fs-extra');
+const fs = Mixly.require('fs');
+
+class WikiGenerator {
+ static {
+ this.WIKI_PAGE_FILE = goog.readFileSync(path.join(Env.templatePath, 'markdown/wiki-page-file.md'));
+ this.WIKI_PAGE_DIR = goog.readFileSync(path.join(Env.templatePath, 'markdown/wiki-page-dir.md'));
+ }
+
+ #$xml_ = null;
+ #desPath_ = '';
+ #tree_ = [];
+ constructor(workspace, generator, $xml, desPath) {
+ this.#$xml_ = $xml;
+ this.#desPath_ = desPath;
+ this.workspace = workspace;
+ this.generator = generator;
+ }
+
+ buildTree($nodes) {
+ let output = [];
+ for (let i = 0; i < $nodes.length; i++) {
+ let child = {};
+ child.id = $nodes[i].getAttribute('id') ?? IdGenerator.generate();
+ if ($nodes[i].nodeName == 'CATEGORY') {
+ child.name = $nodes[i].getAttribute('name') ?? child.id;
+ child.children = this.buildTree($($nodes[i]).children());
+ output.push(child);
+ } else if ($nodes[i].nodeName == 'BLOCK') {
+ child.name = $nodes[i].getAttribute('type') ?? child.id;
+ child.children = false;
+ child.xml = $nodes[i].outerHTML;
+ output.push(child);
+ }
+ }
+ return output;
+ }
+
+ async generate() {
+ let output = this.buildTree(this.#$xml_.children());
+ fs_extra.ensureDirSync('./outputBlock/');
+ fs_extra.emptyDirSync('./outputBlock/');
+ let info = await this.generateWikiPage('./outputBlock/', {
+ title: 'Micropython ESP32',
+ order: 1
+ }, output);
+ fs_extra.outputFileSync(path.join('./outputBlock/', 'README.md'), info.md);
+ }
+
+ generateImage(desPath) {
+ return new Promise((resolve, reject) => {
+ Blockly.Screenshot.workspaceToSvg_(this.workspace, (datauri) => {
+ const base64 = datauri.replace(/^data:image\/\w+;base64,/, '');
+ const dataBuffer = new Buffer(base64, 'base64');
+ fs_extra.outputFile(desPath, dataBuffer, (error) => {
+ resolve(error);
+ });
+ });
+ });
+ }
+
+ async generateWikiPage(rootPath, parentInfo, nodes) {
+ let config = {
+ id: parentInfo.id,
+ title: parentInfo.title,
+ order: parentInfo.order
+ };
+ let blocksNum = 0;
+ for (let node of nodes) {
+ if (!node.children) {
+ blocksNum += 1;
+ }
+ }
+ config.index = !!blocksNum;
+ if (blocksNum) {
+ let blocks = [];
+ for (let node of nodes) {
+ if (node.children) {
+ continue;
+ }
+ this.workspace.clear();
+ let xmlNode = Blockly.utils.xml.textToDom(`
${node.xml} `);
+ Blockly.Xml.domToWorkspace(xmlNode, this.workspace);
+ let code = this.generator.workspaceToCode(this.workspace) || '';
+ code = code.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/g, function (s) {
+ try {
+ return decodeURIComponent(s.replace(/_/g, '%'));
+ } catch (error) {
+ return s;
+ }
+ });
+ await this.generateImage(path.join(rootPath, '../assets', parentInfo.title, node.id + '.png'))
+ blocks.push({
+ imgPath: `./assets/${parentInfo.title}/${node.id}.png`,
+ code: code,
+ type: node.name
+ });
+ }
+ config.blocks = blocks;
+ return {
+ file: true,
+ md: Mustache.render(WikiGenerator.WIKI_PAGE_FILE, config)
+ };
+ } else {
+ let index = 0;
+ for (let i in nodes) {
+ if (!nodes[i].children) {
+ continue;
+ }
+ index += 1;
+ let order = String(index * 5);
+ let mdIndex = Array(4 - Math.min(order.length, 4)).fill('0').join('') + order;
+ let desPath = path.join(rootPath, mdIndex + '-' + nodes[i].id);
+ let info = await this.generateWikiPage(desPath, {
+ id: nodes[i].id,
+ title: nodes[i].name,
+ order: order,
+ }, nodes[i].children);
+ if (info.file) {
+ fs_extra.outputFileSync(`${desPath}.md`, info.md);
+ } else if (!info.isEmpty) {
+ fs_extra.outputFileSync(path.join(desPath, 'README.md'), info.md);
+ } else if (info.isEmpty) {
+ index -= 1;
+ }
+ }
+ config.blocks = [];
+ return {
+ file: false,
+ isEmpty: !index,
+ md: Mustache.render(WikiGenerator.WIKI_PAGE_DIR, config)
+ };
+ }
+ }
+}
+
+Electron.WikiGenerator = WikiGenerator;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/electron/wiki-manager.js b/mixly/common/modules/mixly-modules/electron/wiki-manager.js
new file mode 100644
index 00000000..1bdb03b3
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/electron/wiki-manager.js
@@ -0,0 +1,284 @@
+goog.loadJs('electron', () => {
+
+goog.require('path');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Electron');
+goog.provide('Mixly.Electron.WikiManager');
+
+const {
+ Config,
+ Env,
+ Msg,
+ Electron
+} = Mixly;
+
+const { WikiManager } = Electron;
+
+const { BOARD } = Config;
+
+const fs = Mixly.require('fs');
+const fs_plus = Mixly.require('fs-plus');
+const fs_extra = Mixly.require('fs-extra');
+const path = Mixly.require('path');
+const json2md = Mixly.require('json2md');
+const electron_localshortcut = Mixly.require('electron-localshortcut');
+const electron_remote = Mixly.require('@electron/remote');
+const { ipcMain } = electron_remote;
+
+
+class WikiPage {
+ constructor(indexPath, gotoInfo = null) {
+ this.gotoInfo = gotoInfo;
+ this.updateContentFile();
+ this.win = Electron.newBrowserWindow(indexPath);
+ this.isDestroyed = false;
+ this.addReceiveCommandEvent();
+ this.addLocalShortcutEvent();
+ this.win.on('close', () => {
+ this.isDestroyed = true;
+ });
+ $(window).unload(() => {
+ if (!this.isDestroyed)
+ this.win.close();
+ });
+ }
+
+ addLocalShortcutEvent() {
+ //打开或关闭开发者工具
+ electron_localshortcut.register(this.win, 'CmdOrCtrl+Shift+I', () => {
+ if (!this.isDestroyed)
+ this.win.webContents.toggleDevTools();
+ });
+
+ //重载页面
+ electron_localshortcut.register(this.win, 'CmdOrCtrl+R', () => {
+ this.reload();
+ });
+ }
+
+ addReceiveCommandEvent() {
+ ipcMain.on('command', (event, command) => {
+ if (typeof command !== 'object') return;
+
+ switch (command.func) {
+ case 'getPath':
+ this.updateWiki();
+ break;
+ }
+ });
+ }
+
+ sendCommand(command) {
+ if (this.isDestroyed || typeof command !== 'object') return;
+ this.win.webContents.send('command', command);
+ }
+
+ reload() {
+ if (!this.isDestroyed) {
+ this.updateContentFile();
+ this.win.reload();
+ }
+ }
+
+ getPagePath(contentPath, contentList) {
+ if (typeof contentList !== 'object' || !contentPath.length) return null;
+ if (contentPath.length === 1) {
+ for (let key in contentList) {
+ const child = contentList[key];
+ if (child?.link?.title !== contentPath[0]) {
+ continue;
+ }
+ const { title, source } = child.link;
+ if (title !== contentPath[0] || typeof source !== 'string') {
+ return null;
+ }
+ try {
+ const filePath = source.match(/(?<=(\?file=))[^\s]*/g);
+ if (filePath?.length) {
+ return filePath[0];
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ return null;
+ }
+ return null;
+ } else {
+ for (let key in contentList) {
+ const child = contentList[key];
+ if (child
+ && child.length === 2
+ && child[0].h5 === contentPath[0]) {
+ let childPath = [ ...contentPath ];
+ childPath.shift();
+ return this.getPagePath(childPath, child[1].ul);
+ }
+ }
+ }
+ }
+
+ goto(pageList, scrollPos) {
+ const args = [];
+ const pagePath = this.getPagePath(pageList, this.contentList);
+ if (!pageList) return;
+ args.push(pagePath);
+ scrollPos && args.push(scrollPos);
+ this.sendCommand({
+ func: 'goto',
+ args
+ });
+ this.win.focus();
+ }
+
+ updateContentFile() {
+ const wikiContentPath = path.join(Env.boardDirPath, 'wiki/content.md');
+ const defaultWikiPath = path.join(Env.boardDirPath, 'wiki/wiki-libs/' + Msg.nowLang);
+ const wikiHomePagePath = path.join(defaultWikiPath, 'home');
+ const thirdPartyLibsPath = path.join(Env.boardDirPath, 'libraries/ThirdParty/');
+ const changelogPath = path.join(Env.clientPath, 'CHANGELOG');
+ const wikiList = [];
+ if (fs_plus.isFileSync(wikiHomePagePath + '.md'))
+ wikiList.push({
+ h4: {
+ link: {
+ title: Msg.Lang['wiki.home'],
+ source: '?file=' + encodeURIComponent(wikiHomePagePath)
+ }
+ }
+ });
+ if (fs_plus.isDirectorySync(defaultWikiPath)) {
+ const childContentList = this.getContentJson(defaultWikiPath, BOARD.boardType);
+ if (childContentList)
+ wikiList.push(childContentList);
+ }
+ if (fs_plus.isDirectorySync(thirdPartyLibsPath)) {
+ const libsName = fs.readdirSync(thirdPartyLibsPath);
+ for (let name of libsName) {
+ const libWikiPath = path.join(thirdPartyLibsPath, name , 'wiki', Msg.nowLang);
+ if (fs_plus.isDirectorySync(libWikiPath)) {
+ const childContentList = this.getContentJson(libWikiPath, name);
+ if (childContentList) {
+ wikiList.push(childContentList);
+ }
+ }
+ }
+ }
+ this.contentList = wikiList;
+ try {
+ const md = json2md(wikiList);
+ const lineList = md.split('\n');
+ for (let i = 0; i < lineList.length; i++) {
+ if (!lineList[i].replaceAll(' ', '')) {
+ lineList.splice(i, 1);
+ i--;
+ } else {
+ if (!lineList[i].indexOf('#####'))
+ lineList[i] = '\n' + lineList[i];
+ }
+ }
+ fs_extra.outputFile(wikiContentPath, lineList.join('\n'));
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ updateWiki() {
+ const args = [
+ {
+ default: path.join(Env.boardDirPath, 'wiki/wiki-libs/'),
+ thirdParty: path.join(Env.boardDirPath, 'libraries/ThirdParty/'),
+ content: path.join(Env.boardDirPath, 'wiki/content.md')
+ }
+ ];
+ if (this.gotoInfo) {
+ const { page, scrollPos } = this.gotoInfo;
+ const pagePath = this.getPagePath(this.gotoInfo.page, this.contentList);
+ if (pagePath) {
+ const goto = [];
+ goto.push(pagePath);
+ scrollPos && goto.push(scrollPos);
+ args[0].goto = goto;
+ }
+ this.gotoInfo = null;
+ }
+ this.sendCommand({
+ func: 'setPath',
+ args
+ });
+ }
+
+ getContentJson(dirPath, title = null) {
+ const dirNameList = path.basename(dirPath).split('-');
+ if (dirNameList.length !== 2 && !title) return null;
+ const contentList = [];
+ contentList.push({ h5: title ?? dirNameList[1] });
+ contentList.push({ ul: [] });
+ const { ul } = contentList[1];
+ const keyList = fs.readdirSync(dirPath);
+ for (let key of keyList) {
+ const nowPath = path.join(dirPath, key);
+ if (fs_plus.isDirectorySync(nowPath)) {
+ const childContentList = this.getContentJson(nowPath);
+ if (childContentList && childContentList[1].ul.length)
+ ul.push(childContentList);
+ } else {
+ const extname = path.extname(key);
+ if (extname !== '.md') continue;
+ const fileNameList = path.basename(key, '.md').split('-');
+ if (fileNameList.length !== 2) continue;
+ const newPath = path.join(path.dirname(nowPath), path.basename(key, '.md'));
+ ul.push({ link: { title: fileNameList[1], source: '?file=' + encodeURIComponent(newPath) + ' \"' + fileNameList[1] + '\"' } });
+ }
+ }
+ return contentList;
+ }
+}
+
+WikiManager.WikiPage = WikiPage;
+
+WikiManager.openWiki = (gotoInfo) => {
+ const goto = (gotoInfo && typeof gotoInfo === 'object') ? gotoInfo[Msg.nowLang] : null;
+ if (!WikiManager.wiki || WikiManager.wiki.isDestroyed) {
+ const wikiPath = path.join(Env.indexDirPath, '../common/wiki/index.html');
+ if (fs_plus.isFileSync(wikiPath)) {
+ WikiManager.wiki = new WikiPage(wikiPath, goto);
+ } else {
+ layer.msg(Msg.Lang['wiki.pageNotFound'], { time: 1000 });
+ }
+ } else {
+ const { win } = WikiManager.wiki;
+ win && win.focus();
+ if (goto) {
+ const { page, scrollPos } = goto;
+ WikiManager.wiki.goto(page, scrollPos);
+ }
+ }
+}
+
+WikiManager.registerContextMenu = () => {
+ const openWikiPage = {
+ displayText: Msg.Lang['wiki.open'],
+ preconditionFn: function(scope) {
+ const { wiki } = scope.block;
+ if (typeof wiki === 'object') {
+ if (typeof wiki[Msg.nowLang] === 'object'
+ && typeof wiki[Msg.nowLang].page === 'object') {
+ return 'enabled';
+ }
+ }
+ return 'hidden';
+ },
+ callback: function(scope) {
+ const { wiki } = scope.block;
+ WikiManager.openWiki(wiki);
+ },
+ scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
+ id: 'wiki_open',
+ weight: 200
+ };
+ Blockly.ContextMenuRegistry.registry.register(openWikiPage);
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-compiler/arduino-shell.js b/mixly/common/modules/mixly-modules/web-compiler/arduino-shell.js
new file mode 100644
index 00000000..c1c15955
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-compiler/arduino-shell.js
@@ -0,0 +1,399 @@
+goog.loadJs('web', () => {
+
+goog.require('layui');
+goog.require('AVRUploader');
+goog.require('esptooljs');
+goog.require('CryptoJS');
+goog.require('dayjs.duration');
+goog.require('Mixly.Boards');
+goog.require('Mixly.Debug');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.Web.Serial');
+goog.require('Mixly.Web.SerialTransport');
+goog.require('Mixly.WebCompiler');
+goog.provide('Mixly.WebCompiler.ArduShell');
+
+const {
+ Boards,
+ Debug,
+ LayerExt,
+ Msg,
+ Workspace,
+ LayerProgress,
+ Web,
+ WebCompiler
+} = Mixly;
+
+const { Serial, SerialTransport } = Web;
+const { layer } = layui;
+const { ESPLoader, Transport } = esptooljs;
+
+
+function hexToBinaryString (hex) {
+ let binaryString = '';
+ for (let i = 0; i < hex.length; i += 2) {
+ const byte = parseInt(hex.substr(i, 2), 16);
+ binaryString += String.fromCharCode(byte);
+ }
+ return binaryString;
+}
+
+class WebCompilerArduShell {
+ static {
+ this.mixlySocket = null;
+ this.socket = null;
+ this.shell = null;
+
+ this.getSocket = function () {
+ return this.socket;
+ }
+
+ this.getMixlySocket = function () {
+ return this.mixlySocket;
+ }
+
+ this.init = function (mixlySocket) {
+ this.mixlySocket = mixlySocket;
+ this.socket = mixlySocket.getSocket();
+ this.shell = new WebCompilerArduShell();
+ const socket = this.socket;
+
+ socket.on('arduino.dataEvent', (data) => {
+ if (data.length > 1000) {
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.addValue(data);
+ });
+
+ socket.on('arduino.errorEvent', (data) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ try {
+ data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
+ data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
+ } catch (error) {
+ Debug.error(error);
+ }
+ statusBarTerminal.addValue(data);
+ });
+ }
+
+ this.initCompile = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`);
+ this.shell.compile(code)
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
+ });
+ }
+
+ this.initUpload = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
+ closePromise
+ .then(() => {
+ return this.shell.upload(port, code)
+ })
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ if (info.code || !Serial.portIsLegal(port)) {
+ return;
+ }
+ mainStatusBarTabs.add('serial', port);
+ mainStatusBarTabs.changeTo(port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.open()
+ .then(() => {
+ const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
+ if (!baudRates?.length) {
+ return statusBarSerial.setBaudRate(9600);
+ } else {
+ return statusBarSerial.setBaudRate(baudRates[0] - 0);
+ }
+ })
+ .catch(Debug.error);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
+ });
+ }
+
+ this.endCallback = function (code, time) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ let message = '';
+ if (code) {
+ message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
+ statusBarTerminal.addValue(`\n==${message}==\n`);
+ } else {
+ message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
+ statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
+ dayjs.duration(time).format('HH:mm:ss.SSS')
+ })==\n`);
+ }
+ layer.msg(message, { time: 1000 });
+ }
+ }
+
+ #running_ = false;
+ #upload_ = false;
+ #killing_ = false;
+ #layer_ = null;
+
+ constructor() {
+ this.#layer_ = new LayerProgress({
+ width: 200,
+ cancelValue: Msg.Lang['nav.btn.stop'],
+ skin: 'layui-anim layui-anim-scale',
+ cancel: () => {
+ if (this.#killing_) {
+ return false;
+ }
+ this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
+ this.#killing_ = true;
+ this.kill().catch(Debug.error);
+ return false;
+ },
+ cancelDisplay: false
+ });
+ }
+
+ async compile(code) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = false;
+ this.#killing_ = false;
+ this.showProgress();
+ const key = Boards.getSelectedBoardCommandParam();
+ const config = { key, code };
+ const mixlySocket = WebCompilerArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.compile', config, (response) => {
+ this.hideProgress();
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async upload(port, code) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = true;
+ this.#killing_ = false;
+ this.showProgress();
+ const key = Boards.getSelectedBoardCommandParam();
+ const config = { key, code };
+ const mixlySocket = WebCompilerArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.upload', config, async (response) => {
+ if (response.error) {
+ this.hideProgress();
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ this.hideProgress();
+ reject(error);
+ return;
+ }
+ if (result.code !== 0) {
+ this.hideProgress();
+ resolve(result);
+ return;
+ }
+ const { files } = result;
+ try {
+ const keys = Boards.getSelectedBoardKey().split(':');
+ if (`${keys[0]}:${keys[1]}` === 'arduino:avr') {
+ await this.uploadWithAVRUploader(port, files);
+ } else {
+ await this.uploadWithEsptool(port, files);
+ }
+ } catch (error) {
+ this.hideProgress();
+ reject(error);
+ return;
+ }
+ this.hideProgress();
+ result.files = null;
+ resolve(result);
+ });
+ });
+ }
+
+ async uploadWithEsptool(port, files) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ let esploader = null;
+ let transport = null;
+ let baudrate = 115200;
+ let eraseAll = true;
+ try {
+ const keys = Boards.getSelectedBoardKey().split(':');
+ if (`${keys[0]}:${keys[1]}` === 'esp32:esp32') {
+ baudrate = Boards.getSelectedBoardConfigParam('UploadSpeed') || 115200;
+ eraseAll = Boards.getSelectedBoardConfigParam('EraseFlash') === 'all';
+ } else {
+ baudrate = Boards.getSelectedBoardConfigParam('baud') || 115200;
+ eraseAll = Boards.getSelectedBoardConfigParam('wipe') === 'all';
+ }
+ const serial = new Serial(port);
+ transport = new SerialTransport(serial, false);
+ esploader = new ESPLoader({
+ transport,
+ baudrate,
+ terminal: {
+ clean() {},
+ writeLine(data) {
+ statusBarTerminal.addValue(data + '\n');
+ },
+ write(data) {
+ statusBarTerminal.addValue(data);
+ }
+ }
+ });
+ let chip = await esploader.main();
+ } catch (error) {
+ await transport.disconnect();
+ throw new Error(error);
+ }
+ let data = [];
+ statusBarTerminal.addValue("\n");
+ for (let file of files) {
+ if (file.data && file.offset) {
+ data.push({
+ address: parseInt(file.offset, 16),
+ data: hexToBinaryString(file.data)
+ });
+ }
+ }
+ const flashOptions = {
+ fileArray: data,
+ flashSize: 'keep',
+ eraseAll,
+ compress: true,
+ calculateMD5Hash: (image) => CryptoJS.MD5(CryptoJS.enc.Latin1.parse(image))
+ };
+ try {
+ await esploader.writeFlash(flashOptions);
+ await transport.setDTR(false);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ await transport.setDTR(true);
+ await transport.disconnect();
+ } catch (error) {
+ await transport.disconnect();
+ throw new Error(error);
+ }
+ }
+
+ async uploadWithAVRUploader(port, files) {
+ const key = Boards.getSelectedBoardKey();
+ const boardId = key.split(':')[2];
+ let boardName = 'uno';
+ if (boardId === 'nano') {
+ const cpu = Boards.getSelectedBoardConfigParam('cpu');
+ if (cpu === 'atmega328old') {
+ boardName = 'nanoOldBootloader';
+ } else {
+ boardName = 'nano';
+ }
+ } else if (boardId === 'pro') {
+ boardName = 'proMini';
+ } else if (boardId === 'mega') {
+ boardName = 'mega';
+ } else if (boardId === 'leonardo') {
+ boardName = 'leonardo';
+ }
+ const serial = Serial.getPort(port);
+ const text = files[0].data;
+ await AVRUploader.upload(serial, boardName, text);
+ }
+
+ async kill() {
+ return new Promise(async (resolve, reject) => {
+ const mixlySocket = WebCompilerArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.kill', (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ showProgress() {
+ const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
+ this.#layer_.title(`${message}...`);
+ this.#layer_.show();
+ }
+
+ hideProgress() {
+ this.#layer_.hide();
+ }
+
+ isUploading() {
+ return this.#running_ && this.#upload_;
+ }
+
+ isCompiling() {
+ return this.#running_ && !this.#upload_;
+ }
+}
+
+WebCompiler.ArduShell = WebCompilerArduShell;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-compiler/loader.js b/mixly/common/modules/mixly-modules/web-compiler/loader.js
new file mode 100644
index 00000000..227135fa
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-compiler/loader.js
@@ -0,0 +1,54 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Debug');
+goog.require('Mixly.Config');
+goog.require('Mixly.StatusBarsManager');
+goog.require('Mixly.Socket');
+goog.require('Mixly.WebCompiler.ArduShell');
+goog.provide('Mixly.WebCompiler.Loader');
+
+const {
+ Debug,
+ Config,
+ StatusBarsManager,
+ Socket,
+ WebCompiler
+} = Mixly;
+
+const {
+ Loader,
+ ArduShell
+} = WebCompiler;
+
+const { SOFTWARE } = Config;
+
+
+Loader.init = function () {
+ let url = '';
+ if (SOFTWARE.webCompiler?.url) {
+ const info = new window.URL(SOFTWARE.webCompiler.url);
+ if (window.location.protocol === 'http:') {
+ info.protocol = 'ws:';
+ }
+ if (info.hostname === 'default') {
+ info.hostname = window.location.hostname;
+ }
+ url = info.origin;
+ } else {
+ url = `wss://${window.location.host}`;
+ }
+ const mixlySocket = new Socket(`${url}/compile`, {
+ path: '/mixly-socket/',
+ reconnection: true,
+ reconnectionDelayMax: 10000,
+ transports: ['websocket'],
+ protocols: ['my-protocol-v1']
+ });
+
+ const socket = mixlySocket.getSocket();
+ socket.on('connect', () => {});
+ socket.on('disconnect', () => {});
+ ArduShell.init(mixlySocket);
+}
+
+});
diff --git a/mixly/common/modules/mixly-modules/web-compiler/web-compiler.js b/mixly/common/modules/mixly-modules/web-compiler/web-compiler.js
new file mode 100644
index 00000000..781c9181
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-compiler/web-compiler.js
@@ -0,0 +1,6 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.WebCompiler');
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/ampy-fs.js b/mixly/common/modules/mixly-modules/web-socket/ampy-fs.js
new file mode 100644
index 00000000..5623b151
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/ampy-fs.js
@@ -0,0 +1,201 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mixly.Env');
+goog.require('Mixly.FS');
+goog.require('Mixly.Debug');
+goog.require('Mixly.MJson');
+goog.require('Mixly.WebSocket.Ampy');
+goog.provide('Mixly.WebSocket.AmpyFS');
+
+const {
+ Env,
+ FS,
+ Debug,
+ MJson,
+ WebSocket
+} = Mixly;
+
+const { Ampy } = WebSocket;
+
+
+class AmpyFS extends FS {
+ #ampy_ = null;
+ #port_ = '';
+ #baud_ = 115200;
+ #decoder_ = new TextDecoder('utf8');
+
+ constructor() {
+ super();
+ this.#ampy_ = new Ampy();
+ }
+
+ async rename(oldPath, newPath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.rename(this.#port_, this.#baud_, oldPath, newPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createFile(filePath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.mkfile(this.#port_, this.#baud_, filePath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readFile(filePath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.get(this.#port_, this.#baud_, filePath);
+ stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout));
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async writeFile(filePath, data) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.put(this.#port_, this.#baud_, filePath, data);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isFile(filePath) {
+ /*const [error, stdout] = await this.readDirectory(filePath);
+ if (error) {
+ return true;
+ }
+ return false;*/
+ let error = null;
+ if (path.extname(filePath)) {
+ return [error, true];
+ } else {
+ return [error, false];
+ }
+ }
+
+ async renameFile(oldFilePath, newFilePath) {
+ return this.rename(oldFilePath, newFilePath);
+ }
+
+ // async moveFile(oldFilePath, newFilePath) {}
+
+ // async copyFile(oldFilePath, newFilePath) {}
+
+ async deleteFile(filePath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.rm(this.#port_, this.#baud_, filePath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createDirectory(folderPath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.mkdir(this.#port_, this.#baud_, folderPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readDirectory(folderPath) {
+ let stdout = [], error = null;
+ try {
+ const output = await this.#ampy_.ls(this.#port_, this.#baud_, folderPath);
+ const dirs = Array.from(new Set(output.split('\r\n')));
+ for (let i in dirs) {
+ if (!dirs[i]) {
+ continue;
+ }
+ stdout.push(MJson.parse(dirs[i].replaceAll('\'', '"')));
+ }
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isDirectory(folderPath) {
+ /*const [error, stdout] = await this.readDirectory(folderPath);
+ if (error) {
+ return false;
+ }
+ return true;*/
+ let error = null;
+ if (path.extname(folderPath)) {
+ return [error, false];
+ } else {
+ return [error, true];
+ }
+ }
+
+ async isDirectoryEmpty(folderPath) {
+ /*const [error, stdout] = await this.readDirectory(folderPath);
+ let isEmpty = false;
+ if (error || !stdout.length) {
+ isEmpty = true;
+ }*/
+ return [null, false];
+ }
+
+ async renameDirectory(oldFolderPath, newFolderPath) {
+ return this.rename(oldFolderPath, newFolderPath);
+ }
+
+ // async moveDirectory(oldFolderPath, newFolderPath) {}
+
+ // async copyDirectory(oldFolderPath, newFolderPath) {}
+
+ async deleteDirectory(folderPath) {
+ let stdout = '', error = null;
+ try {
+ stdout = await this.#ampy_.rmdir(this.#port_, this.#baud_, folderPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ setPortName(port) {
+ this.#port_ = port;
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ setBaudRate(baud) {
+ this.#baud_ = baud;
+ }
+
+ getBaudRate() {
+ return this.#baud_;
+ }
+}
+
+WebSocket.AmpyFS = AmpyFS;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/ampy.js b/mixly/common/modules/mixly-modules/web-socket/ampy.js
new file mode 100644
index 00000000..1248bf25
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/ampy.js
@@ -0,0 +1,108 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mustache');
+goog.require('Mixly.Ampy');
+goog.require('Mixly.Env');
+goog.require('Mixly.Serial');
+goog.require('Mixly.WebSocket');
+goog.provide('Mixly.WebSocket.Ampy');
+
+const {
+ Ampy,
+ Env,
+ Serial,
+ WebSocket
+} = Mixly;
+
+
+class AmpyExt extends Ampy {
+ static {
+ this.mixlySocket = null;
+ this.socket = null;
+
+ this.getSocket = function () {
+ return this.socket;
+ }
+
+ this.getMixlySocket = function () {
+ return this.mixlySocket;
+ }
+
+ this.init = function (mixlySocket) {
+ this.mixlySocket = mixlySocket;
+ this.socket = mixlySocket.getSocket();
+ }
+ }
+
+ constructor() {
+ super();
+ }
+
+ async ls(port, baud, folderPath) {
+ return this.exec('ampy.ls', port, baud, folderPath);
+ }
+
+ async get(port, baud, filePath) {
+ return this.exec('ampy.get', port, baud, filePath);
+ }
+
+ async mkdir(port, baud, folderPath) {
+ return this.exec('ampy.mkdir', port, baud, folderPath);
+ }
+
+ async mkfile(port, baud, filePath) {
+ return this.exec('ampy.mkfile', port, baud, filePath);
+ }
+
+ async isdir(port, baud, folderPath) {
+ return this.exec('ampy.isdir', port, baud, folderPath);
+ }
+
+ async isfile(port, baud, filePath) {
+ return this.exec('ampy.isfile', port, baud, filePath);
+ }
+
+ async put(port, baud, filePath, data) {
+ return this.exec('ampy.put', port, baud, filePath, data);
+ }
+
+ async rm(port, baud, filePath) {
+ return this.exec('ampy.rm', port, baud, filePath);
+ }
+
+ async rmdir(port, baud, folderPath) {
+ return this.exec('ampy.rmdir', port, baud, folderPath);
+ }
+
+ async rename(port, baud, oldPath, newPath) {
+ return this.exec('ampy.rename', port, baud, oldPath, newPath);
+ }
+
+ async run(port, baud, filePath) {
+ return this.exec('ampy.run', port, baud, filePath);
+ }
+
+ async exec(eventType, port, ...args) {
+ const portsName = Serial.getCurrentPortsName();
+ if (!portsName.includes(port)) {
+ throw new Error('无可用串口');
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+ const mixlySocket = AmpyExt.getMixlySocket();
+ const output = await mixlySocket.emitAsync(eventType, port, ...args);
+ if (output[0]) {
+ throw new Error(output[0]);
+ }
+ return output[1];
+ }
+}
+
+WebSocket.Ampy = AmpyExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/arduino-shell.js b/mixly/common/modules/mixly-modules/web-socket/arduino-shell.js
new file mode 100644
index 00000000..84f71d16
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/arduino-shell.js
@@ -0,0 +1,275 @@
+goog.loadJs('web', () => {
+
+goog.require('layui');
+goog.require('dayjs.duration');
+goog.require('Mixly.Boards');
+goog.require('Mixly.Debug');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.WebSocket.Serial');
+goog.provide('Mixly.WebSocket.ArduShell');
+
+const {
+ Boards,
+ Debug,
+ LayerExt,
+ Msg,
+ Workspace,
+ LayerProgress,
+ WebSocket
+} = Mixly;
+
+const { Serial } = WebSocket;
+
+const { layer } = layui;
+
+
+class WebSocketArduShell {
+ static {
+ this.mixlySocket = null;
+ this.socket = null;
+ this.shell = null;
+
+ this.getSocket = function () {
+ return this.socket;
+ }
+
+ this.getMixlySocket = function () {
+ return this.mixlySocket;
+ }
+
+ this.init = function (mixlySocket) {
+ this.mixlySocket = mixlySocket;
+ this.socket = mixlySocket.getSocket();
+ this.shell = new WebSocketArduShell();
+ const socket = this.socket;
+
+ socket.on('arduino.dataEvent', (data) => {
+ if (data.length > 1000) {
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.addValue(data);
+ });
+
+ socket.on('arduino.errorEvent', (data) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ try {
+ data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
+ data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
+ } catch (error) {
+ Debug.error(error);
+ }
+ statusBarTerminal.addValue(data);
+ });
+ }
+
+ this.initCompile = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`);
+ this.shell.compile(code)
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
+ });
+ }
+
+ this.initUpload = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
+ closePromise
+ .then(() => {
+ return this.shell.upload(port, code)
+ })
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ if (info.code || !Serial.portIsLegal(port)) {
+ return;
+ }
+ mainStatusBarTabs.add('serial', port);
+ mainStatusBarTabs.changeTo(port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.open()
+ .then(() => {
+ const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
+ if (!baudRates?.length) {
+ return statusBarSerial.setBaudRate(9600);
+ } else {
+ return statusBarSerial.setBaudRate(baudRates[0] - 0);
+ }
+ })
+ .catch(Debug.error);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
+ });
+ }
+
+ this.endCallback = function (code, time) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ let message = '';
+ if (code) {
+ message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
+ statusBarTerminal.addValue(`\n==${message}==\n`);
+ } else {
+ message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
+ statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
+ dayjs.duration(time).format('HH:mm:ss.SSS')
+ })==\n`);
+ }
+ layer.msg(message, { time: 1000 });
+ }
+ }
+
+ #running_ = false;
+ #upload_ = false;
+ #killing_ = false;
+ #layer_ = null;
+
+ constructor() {
+ this.#layer_ = new LayerProgress({
+ width: 200,
+ cancelValue: Msg.Lang['nav.btn.stop'],
+ skin: 'layui-anim layui-anim-scale',
+ cancel: () => {
+ if (this.#killing_) {
+ return false;
+ }
+ this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
+ this.#killing_ = true;
+ this.kill().catch(Debug.error);
+ return false;
+ },
+ cancelDisplay: false
+ });
+ }
+
+ async compile(code) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = false;
+ this.#killing_ = false;
+ this.showProgress();
+ const key = Boards.getSelectedBoardCommandParam();
+ const config = { key, code };
+ const mixlySocket = WebSocketArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.compile', config, (response) => {
+ this.hideProgress();
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async upload(port, code) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = true;
+ this.#killing_ = false;
+ this.showProgress();
+ const key = Boards.getSelectedBoardCommandParam();
+ const config = { key, code, port };
+ const mixlySocket = WebSocketArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.upload', config, (response) => {
+ this.hideProgress();
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async kill() {
+ return new Promise(async (resolve, reject) => {
+ const mixlySocket = WebSocketArduShell.getMixlySocket();
+ mixlySocket.emit('arduino.kill', (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ showProgress() {
+ const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
+ this.#layer_.title(`${message}...`);
+ this.#layer_.show();
+ }
+
+ hideProgress() {
+ this.#layer_.hide();
+ }
+
+ isUploading() {
+ return this.#running_ && this.#upload_;
+ }
+
+ isCompiling() {
+ return this.#running_ && !this.#upload_;
+ }
+}
+
+WebSocket.ArduShell = WebSocketArduShell;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/burn-upload.js b/mixly/common/modules/mixly-modules/web-socket/burn-upload.js
new file mode 100644
index 00000000..ec301439
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/burn-upload.js
@@ -0,0 +1,350 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('layui');
+goog.require('dayjs.duration');
+goog.require('Mixly.Debug');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Env');
+goog.require('Mixly.Config');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.MString');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.WebSocket.Serial');
+goog.provide('Mixly.WebSocket.BU');
+
+const {
+ Debug,
+ LayerExt,
+ Config,
+ Msg,
+ Env,
+ Workspace,
+ MString,
+ LayerProgress,
+ WebSocket
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+const { Serial } = WebSocket;
+
+const { layer } = layui;
+
+
+class WebSocketBU {
+ static {
+ this.mixlySocket = null;
+ this.socket = null;
+ this.shell = null;
+
+ this.getSocket = function () {
+ return this.socket;
+ }
+
+ this.getMixlySocket = function () {
+ return this.mixlySocket;
+ }
+
+ this.init = function (mixlySocket) {
+ this.mixlySocket = mixlySocket;
+ this.socket = mixlySocket.getSocket();
+ this.shell = new WebSocketBU();
+ const socket = this.socket;
+
+ socket.on('micropython.dataEvent', (data) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.addValue(data);
+ });
+
+ socket.on('micropython.errorEvent', (data) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.addValue(data);
+ });
+ }
+
+ this.initBurn = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.burning']}...\n`);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
+ closePromise
+ .then(() => {
+ return this.shell.burn(port);
+ })
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.burnFailed']}==\n`);
+ });
+ }
+
+ this.initUpload = function () {
+ if (!this.mixlySocket.isConnected()) {
+ layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
+ return;
+ }
+ const port = Serial.getSelectedPortName();
+ if (!port) {
+ layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
+ time: 1000
+ });
+ return;
+ }
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ mainStatusBarTabs.show();
+ statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
+ closePromise
+ .then(() => {
+ return this.shell.upload(port, code)
+ })
+ .then((info) => {
+ this.endCallback(info.code, info.time);
+ if (info.code || !Serial.portIsLegal(port)) {
+ return;
+ }
+ mainStatusBarTabs.add('serial', port);
+ mainStatusBarTabs.changeTo(port);
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
+ statusBarSerial.open()
+ .then(() => {
+ return statusBarSerial.setBaudRate(115200);
+ })
+ .catch(Debug.error);
+ })
+ .catch((error) => {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
+ });
+ }
+
+ this.endCallback = function (code, time) {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ mainStatusBarTabs.changeTo('output');
+ let message = '';
+ if (code) {
+ message = (this.shell.isBurning() ? Msg.Lang['shell.burnFailed'] : Msg.Lang['shell.uploadFailed']);
+ statusBarTerminal.addValue(`\n==${message}==\n`);
+ } else {
+ message = (this.shell.isBurning() ? Msg.Lang['shell.burnSucc'] : Msg.Lang['shell.uploadSucc']);
+ statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
+ dayjs.duration(time).format('HH:mm:ss.SSS')
+ })==\n`);
+ }
+ layer.msg(message, { time: 1000 });
+ }
+ }
+
+ #running_ = false;
+ #upload_ = false;
+ #killing_ = false;
+ #layer_ = null;
+
+ constructor() {
+ this.#layer_ = new LayerProgress({
+ width: 200,
+ cancelValue: Msg.Lang['nav.btn.stop'],
+ skin: 'layui-anim layui-anim-scale',
+ cancel: () => {
+ if (this.#killing_) {
+ return false;
+ }
+ this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
+ this.#killing_ = true;
+ this.kill().catch(Debug.error);
+ return false;
+ },
+ cancelDisplay: false
+ });
+ }
+
+ async burn(port) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = false;
+ this.#killing_ = false;
+ this.showProgress();
+ const config = {
+ boardDirPath: `.${Env.boardDirPath}`,
+ port,
+ command: SELECTED_BOARD.burn.command
+ };
+ const mixlySocket = WebSocketBU.getMixlySocket();
+ mixlySocket.emit('micropython.burn', config, (response) => {
+ this.hideProgress();
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async upload(port, code) {
+ return new Promise(async (resolve, reject) => {
+ this.#running_ = true;
+ this.#upload_ = true;
+ this.#killing_ = false;
+ this.showProgress();
+ const importsMap = this.getImportModules(code);
+ let libraries = {};
+ for (let key in importsMap) {
+ const filename = importsMap[key]['__name__'];
+ const data = goog.readFileSync(importsMap[key]['__path__']);
+ libraries[filename] = data;
+ }
+ const config = {
+ boardDirPath: `.${Env.boardDirPath}`,
+ command: SELECTED_BOARD.upload.command,
+ filePath: SELECTED_BOARD.upload.filePath,
+ port, code, libraries
+ };
+
+ const mixlySocket = WebSocketBU.getMixlySocket();
+ mixlySocket.emit('micropython.upload', config, (response) => {
+ this.hideProgress();
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ getImportModulesName(code) {
+ // 正则表达式: 匹配 import 或 from 导入语句
+ const importRegex = /(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)/g;
+
+ let imports = [];
+ let match;
+ while ((match = importRegex.exec(code)) !== null) {
+ if (match[1]) {
+ imports.push(match[1]); // 'import module'
+ }
+ if (match[2]) {
+ imports.push(match[2]); // 'from module import ...'
+ }
+ }
+ return imports;
+ }
+
+ getImportModules(code) {
+ let importsMap = {};
+ const libPath = SELECTED_BOARD.upload.libPath;
+ for (let i = libPath.length - 1; i >= 0; i--) {
+ const dirname = MString.tpl(libPath[i], { indexPath: Env.boardDirPath });
+ const map = goog.readJsonSync(path.join(dirname, 'map.json'));
+ if (!(map && map instanceof Object)) {
+ continue;
+ }
+ for (let key in map) {
+ importsMap[key] = structuredClone(map[key]);
+ importsMap[key]['__path__'] = path.join(dirname, map[key]['__name__']);
+ }
+ }
+
+ let usedMap = {};
+ let currentImports = this.getImportModulesName(code);
+ while (currentImports.length) {
+ let temp = [];
+ for (let moduleName of currentImports) {
+ let moduleInfo = importsMap[moduleName];
+ if (!moduleInfo) {
+ continue;
+ }
+ usedMap[moduleName] = moduleInfo;
+ const moduleImports = moduleInfo['__require__'];
+ if (!moduleImports) {
+ continue;
+ }
+ for (let name of moduleImports) {
+ if (usedMap[name] || !importsMap[name] || temp.includes(name)) {
+ continue;
+ }
+ temp.push(name);
+ }
+ }
+ currentImports = temp;
+ }
+ return usedMap;
+ }
+
+ async kill() {
+ return new Promise(async (resolve, reject) => {
+ const mixlySocket = WebSocketBU.getMixlySocket();
+ mixlySocket.emit('micropython.kill', (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ showProgress() {
+ const message = this.isBurning() ? Msg.Lang['shell.burning'] : Msg.Lang['shell.uploading'];
+ this.#layer_.title(`${message}...`);
+ this.#layer_.show();
+ }
+
+ hideProgress() {
+ this.#layer_.hide();
+ }
+
+ isUploading() {
+ return this.#running_ && this.#upload_;
+ }
+
+ isBurning() {
+ return this.#running_ && !this.#upload_;
+ }
+}
+
+WebSocket.BU = WebSocketBU;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/file.js b/mixly/common/modules/mixly-modules/web-socket/file.js
new file mode 100644
index 00000000..c06cc82a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/file.js
@@ -0,0 +1,11 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Web.File');
+goog.provide('Mixly.WebSocket.File');
+
+
+const { Web, WebSocket } = Mixly;
+
+WebSocket.File = Web.File;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/loader.js b/mixly/common/modules/mixly-modules/web-socket/loader.js
new file mode 100644
index 00000000..9cd9654c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/loader.js
@@ -0,0 +1,101 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Debug');
+goog.require('Mixly.Config');
+goog.require('Mixly.StatusBarsManager');
+goog.require('Mixly.Socket');
+goog.require('Mixly.WebSocket.Serial');
+goog.require('Mixly.WebSocket.ArduShell');
+goog.require('Mixly.WebSocket.BU');
+goog.require('Mixly.WebSocket.Ampy');
+goog.provide('Mixly.WebSocket.Loader');
+
+const {
+ Debug,
+ Config,
+ StatusBarsManager,
+ Socket,
+ WebSocket
+} = Mixly;
+
+const {
+ Loader,
+ Serial,
+ ArduShell,
+ BU,
+ Ampy
+} = WebSocket;
+
+const { SOFTWARE } = Config;
+
+
+Loader.init = function () {
+ let url = '';
+ if (SOFTWARE.webSocket?.url) {
+ const info = new window.URL(SOFTWARE.webSocket.url);
+ if (window.location.protocol === 'http:') {
+ info.protocol = 'ws:';
+ }
+ if (info.hostname === 'default') {
+ info.hostname = window.location.hostname;
+ }
+ url = info.origin;
+ } else {
+ url = `wss://${window.location.host}`;
+ }
+ const mixlySocket = new Socket(`${url}/all`, {
+ path: '/mixly-socket/',
+ reconnection: true,
+ reconnectionDelayMax: 10000,
+ transports: ['websocket'],
+ protocols: ['my-protocol-v1']
+ });
+
+ const socket = mixlySocket.getSocket();
+
+ socket.on('connect', () => {
+ Serial.getPorts()
+ .then((ports) => {
+ let portsName = [];
+ for (let port of ports) {
+ portsName.push(port.name);
+ }
+ const { mainStatusBarTabs } = Mixly;
+ let keys = mainStatusBarTabs.keys();
+ const statusBarType = StatusBarsManager.typesRegistry.getItem('serial');
+ for (let key of keys) {
+ const statusBar = mainStatusBarTabs.getStatusBarById(key);
+ if (!(statusBar instanceof statusBarType)) {
+ continue;
+ }
+ const portName = statusBar.getPortName();
+ if (!portsName.includes(portName)) {
+ continue;
+ }
+ socket.emit('serial.create', portName);
+ }
+ Serial.renderSelectBox(ports);
+ })
+ .catch(Debug.error);
+ });
+
+ socket.on('disconnect', () => {
+ const { mainStatusBarTabs } = Mixly;
+ let keys = mainStatusBarTabs.keys();
+ const statusBarType = StatusBarsManager.typesRegistry.getItem('serial');
+ for (let key of keys) {
+ const statusBar = mainStatusBarTabs.getStatusBarById(key);
+ if (statusBar instanceof statusBarType) {
+ statusBar.close().catch(Debug.error);
+ }
+ }
+ Serial.refreshPorts();
+ });
+
+ Serial.init(mixlySocket);
+ ArduShell.init(mixlySocket);
+ BU.init(mixlySocket);
+ Ampy.init(mixlySocket);
+}
+
+});
diff --git a/mixly/common/modules/mixly-modules/web-socket/serial.js b/mixly/common/modules/mixly-modules/web-socket/serial.js
new file mode 100644
index 00000000..644c7125
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/serial.js
@@ -0,0 +1,360 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Registry');
+goog.require('Mixly.WebSocket');
+goog.provide('Mixly.WebSocket.Serial');
+
+
+const {
+ Serial,
+ Env,
+ Msg,
+ Debug,
+ Registry,
+ WebSocket
+} = Mixly;
+
+
+class WebSocketSerial extends Serial {
+ static {
+ this.eventRegistry = new Registry();
+ this.mixlySocket = null;
+ this.socket = null;
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.renderSelectBox = function (ports) {
+ return Serial.renderSelectBox(ports);
+ }
+
+ this.getPorts = async function () {
+ return new Promise((resolve, reject) => {
+ if (this.socket.connected) {
+ this.socket.emit('serial.getPorts', (response) => {
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ } else {
+ resolve([]);
+ }
+ });
+ }
+
+ this.refreshPorts = function () {
+ this.getPorts()
+ .then((ports) => {
+ Serial.renderSelectBox(ports);
+ })
+ .catch(Debug.error);
+ }
+
+ this.init = function (mixlySocket) {
+ this.mixlySocket = mixlySocket;
+ this.socket = mixlySocket.getSocket();
+ const socket = this.socket;
+
+ socket.on('serial.attachEvent', () => {
+ this.refreshPorts();
+ });
+
+ socket.on('serial.detachEvent', () => {
+ this.refreshPorts();
+ });
+
+ socket.on('serial.bufferEvent', (port, buffer) => {
+ const eventName = `${port}-buffer`;
+ if (!this.eventRegistry.hasKey(eventName)) {
+ return;
+ }
+ const event = this.eventRegistry.getItem(eventName);
+ event(buffer);
+ });
+
+ socket.on('serial.stringEvent', (port, str) => {
+ const eventName = `${port}-string`;
+ if (!this.eventRegistry.hasKey(eventName)) {
+ return;
+ }
+ const event = this.eventRegistry.getItem(eventName);
+ event(str);
+ });
+
+ socket.on('serial.errorEvent', (port, error) => {
+ const eventName = `${port}-error`;
+ if (!this.eventRegistry.hasKey(eventName)) {
+ return;
+ }
+ const event = this.eventRegistry.getItem(eventName);
+ event(error);
+ });
+
+ socket.on('serial.openEvent', (port) => {
+ const eventName = `${port}-open`;
+ if (!this.eventRegistry.hasKey(eventName)) {
+ return;
+ }
+ const event = this.eventRegistry.getItem(eventName);
+ event();
+ });
+
+ socket.on('serial.closeEvent', (port, code) => {
+ const eventName = `${port}-close`;
+ if (!this.eventRegistry.hasKey(eventName)) {
+ return;
+ }
+ const event = this.eventRegistry.getItem(eventName);
+ event(code);
+ });
+ }
+
+ this.getSocket = function () {
+ return this.socket;
+ }
+
+ this.getMixlySocket = function () {
+ return this.mixlySocket;
+ }
+
+ this.getEventRegistry = function () {
+ return this.eventRegistry;
+ }
+ }
+
+ constructor(port) {
+ super(port);
+ this.#addEventsListener_();
+ const socket = WebSocketSerial.getSocket();
+ if (socket.connected) {
+ socket.emit('serial.create', port);
+ }
+ }
+
+ #addEventsListener_() {
+ const port = this.getPortName();
+ const eventRegistry = WebSocketSerial.getEventRegistry();
+ eventRegistry.register(`${port}-buffer`, (buffer) => {
+ this.onBuffer(buffer);
+ });
+ eventRegistry.register(`${port}-string`, (str) => {
+ this.onString(str);
+ });
+ eventRegistry.register(`${port}-error`, (error) => {
+ this.onError(error);
+ this.onClose(1);
+ });
+ eventRegistry.register(`${port}-open`, () => {
+ this.onOpen();
+ });
+ eventRegistry.register(`${port}-close`, (code) => {
+ this.onClose(code);
+ });
+ }
+
+ async open(baud) {
+ return new Promise((resolve, reject) => {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPort = this.getPortName();
+ if (!portsName.includes(currentPort)) {
+ reject('无可用串口');
+ return;
+ }
+ if (this.isOpened()) {
+ resolve();
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.open', currentPort, baud, (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ this.onError(error);
+ reject(error);
+ } else {
+ super.open(baud);
+ this.setBaudRate(baud);
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async close() {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ super.close();
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.close', this.getPortName(), (response) => {
+ if (response.error) {
+ this.onClose(1);
+ resolve(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async setBaudRate(baud) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()
+ || this.getRawBaudRate() === baud
+ || !this.baudRateIsLegal(baud)) {
+ resolve();
+ return;
+ }
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.setBaudRate', this.getPortName(), baud, (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error,] = response;
+ if (error) {
+ reject(error);
+ } else {
+ super.setBaudRate(baud);
+ this.setDTRAndRTS(this.getDTR(), this.getRTS()).finally(resolve);
+ }
+ });
+ });
+ }
+
+ async send(data) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.send', this.getPortName(), data, (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async sendString(str) {
+ return this.send(str);
+ }
+
+ async sendBuffer(buffer) {
+ return this.send(buffer);
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ return new Promise((resolve, reject) => {
+ if (!this.isOpened()) {
+ resolve();
+ return;
+ }
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.setDTRAndRTS', this.getPortName(), dtr, rts, (response) => {
+ if (response.error) {
+ reject(response.error);
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ super.setDTRAndRTS(dtr, rts);
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async setDTR(dtr) {
+ return this.setDTRAndRTS(dtr, this.getRTS());
+ }
+
+ async setRTS(rts) {
+ return this.setDTRAndRTS(this.getDTR(), rts);
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ }
+ }
+
+ async #awaitDispose_() {
+ return new Promise((resolve, reject) => {
+ const mixlySocket = WebSocketSerial.getMixlySocket();
+ mixlySocket.emit('serial.dispose', this.getPortName(), (response) => {
+ if (response.error) {
+ resolve();
+ return;
+ }
+ const [error, result] = response;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ async dispose() {
+ const port = this.getPortName();
+ const eventRegistry = WebSocketSerial.getEventRegistry();
+ eventRegistry.unregister(`${port}-buffer`);
+ eventRegistry.unregister(`${port}-string`);
+ eventRegistry.unregister(`${port}-error`);
+ eventRegistry.unregister(`${port}-open`);
+ eventRegistry.unregister(`${port}-close`);
+ await super.dispose();
+ await this.#awaitDispose_();
+ }
+}
+
+WebSocket.Serial = WebSocketSerial;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web-socket/web-socket.js b/mixly/common/modules/mixly-modules/web-socket/web-socket.js
new file mode 100644
index 00000000..c045b486
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web-socket/web-socket.js
@@ -0,0 +1,6 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.WebSocket');
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/ampy-fs.js b/mixly/common/modules/mixly-modules/web/ampy-fs.js
new file mode 100644
index 00000000..37762015
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/ampy-fs.js
@@ -0,0 +1,292 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mixly.FS');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Web.Serial');
+goog.require('Mixly.Web.Ampy');
+goog.provide('Mixly.Web.AmpyFS');
+
+const { FS, Debug, Web } = Mixly;
+const { Serial, Ampy } = Web;
+
+
+class AmpyFS extends FS {
+ #ampy_ = null;
+ #port_ = '';
+ #baud_ = 115200;
+
+ constructor() {
+ super();
+ this.#ampy_ = Ampy;
+ }
+
+ async getAmpy() {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(this.#port_);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+ const serial = new Serial(this.#port_);
+ const ampy = new Ampy(serial);
+ return ampy;
+ }
+
+ async rename(oldPath, newPath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.rename(oldPath, newPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createFile(filePath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.mkfile(filePath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readFile(filePath, encoding = 'utf8') {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.get(filePath, encoding);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async writeFile(filePath, data) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.put(filePath, data);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isFile(filePath) {
+ let error = null;
+ if (path.extname(filePath)) {
+ return [error, true];
+ } else {
+ return [error, false];
+ }
+ }
+
+ async renameFile(oldFilePath, newFilePath) {
+ return this.rename(oldFilePath, newFilePath);
+ }
+
+ async moveFile(oldFilePath, newFilePath) {
+ return this.rename(oldFilePath, newFilePath);
+ }
+
+ async copyFile(oldFilePath, newFilePath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.cpfile(oldFilePath, newFilePath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async deleteFile(filePath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.rm(filePath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async createDirectory(folderPath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.mkdir(folderPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async readDirectory(folderPath) {
+ let stdout = [], error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.ls(folderPath, false, false);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async isDirectory(folderPath) {
+ let error = null;
+ if (path.extname(folderPath)) {
+ return [error, false];
+ } else {
+ return [error, true];
+ }
+ }
+
+ async isDirectoryEmpty(folderPath) {
+ return [null, false];
+ }
+
+ async renameDirectory(oldFolderPath, newFolderPath) {
+ return this.rename(oldFolderPath, newFolderPath);
+ }
+
+ async moveDirectory(oldFolderPath, newFolderPath) {
+ return this.rename(oldFolderPath, newFolderPath);
+ }
+
+ async copyDirectory(oldFolderPath, newFolderPath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.cpdir(oldFolderPath, newFolderPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ async deleteDirectory(folderPath) {
+ let stdout = '', error = null, ampy = null;
+ try {
+ ampy = await this.getAmpy();
+ await ampy.enter();
+ stdout = await ampy.rmdir(folderPath);
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ try {
+ await ampy.exit();
+ await ampy.dispose();
+ } catch (e) {
+ error = e;
+ Debug.error(error);
+ }
+ return [error, stdout];
+ }
+
+ setPortName(port) {
+ this.#port_ = port;
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ setBaudRate(baud) {
+ this.#baud_ = baud;
+ }
+
+ getBaudRate() {
+ return this.#baud_;
+ }
+}
+
+Web.AmpyFS = AmpyFS;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/ampy.js b/mixly/common/modules/mixly-modules/web/ampy.js
new file mode 100644
index 00000000..19249ff5
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/ampy.js
@@ -0,0 +1,429 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mustache');
+goog.require('Mixly.Env');
+goog.require('Mixly.Events');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Ampy');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.Ampy');
+
+const {
+ Env,
+ Events,
+ Msg,
+ Ampy,
+ Web
+} = Mixly;
+
+
+class AmpyExt extends Ampy {
+ static {
+ this.LS = goog.readFileSync(path.join(Env.templatePath, 'python/ls.py'));
+ this.LS_RECURSIVE = goog.readFileSync(path.join(Env.templatePath, 'python/ls-recursive.py'));
+ this.LS_LONG_FORMAT = goog.readFileSync(path.join(Env.templatePath, 'python/ls-long-format.py'));
+ this.MKDIR = goog.readFileSync(path.join(Env.templatePath, 'python/mkdir.py'));
+ this.MKFILE = goog.readFileSync(path.join(Env.templatePath, 'python/mkfile.py'));
+ this.RENAME = goog.readFileSync(path.join(Env.templatePath, 'python/rename.py'));
+ this.RM = goog.readFileSync(path.join(Env.templatePath, 'python/rm.py'));
+ this.RMDIR = goog.readFileSync(path.join(Env.templatePath, 'python/rmdir.py'));
+ this.GET = goog.readFileSync(path.join(Env.templatePath, 'python/get.py'));
+ this.CWD = goog.readFileSync(path.join(Env.templatePath, 'python/cwd.py'));
+ this.CPDIR = goog.readFileSync(path.join(Env.templatePath, 'python/cpdir.py'));
+ this.CPFILE = goog.readFileSync(path.join(Env.templatePath, 'python/cpfile.py'));
+ }
+
+ #device_ = null;
+ #receiveTemp_ = [];
+ #writeBuffer_ = true;
+ #active_ = false;
+ #dataLength_ = 256;
+ #events_ = new Events(['message', 'replaceMessage'])
+ constructor(device, writeBuffer = true, dataLength = 256) {
+ super();
+ this.#device_ = device;
+ this.#writeBuffer_ = writeBuffer;
+ this.#dataLength_ = dataLength;
+ this.#addEventsListener_();
+ }
+
+ #addEventsListener_() {
+ this.#device_.bind('onChar', (char) => {
+ if (['\r', '\n'].includes(char)) {
+ this.#receiveTemp_.push('');
+ } else {
+ let line = this.#receiveTemp_.pop() ?? '';
+ this.#receiveTemp_.push(line + char);
+ }
+ });
+ }
+
+ bind(...args) {
+ return this.#events_.bind(...args);
+ }
+
+ message(message) {
+ this.#events_.run('message', message);
+ }
+
+ replaceMessage(lineNumber, message) {
+ this.#events_.run('replaceMessage', lineNumber, message);
+ }
+
+ getProgressMessage(name, percent) {
+ const sended = parseInt(percent * 45);
+ const left = percent === 0 ? '' : Array(sended).fill('=').join('');
+ const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
+ return `${name} → |${left}${right}| ${(percent * 100).toFixed(1)}%`;
+ }
+
+ isActive() {
+ return this.#active_;
+ }
+
+ async readUntil(ending, withEnding = true, timeout = 5000) {
+ const startTime = Number(new Date());
+ let nowTime = startTime;
+ let readStr = '';
+ while (nowTime - startTime < timeout) {
+ const nowTime = Number(new Date());
+ let len = this.#receiveTemp_.length;
+ for (let i = 0; i < len; i++) {
+ const data = this.#receiveTemp_.shift();
+ let index = data.toLowerCase().indexOf(ending);
+ if (index !== -1) {
+ if (withEnding) {
+ index += ending.length;
+ }
+ this.#receiveTemp_.unshift(data.substring(index));
+ readStr += data.substring(0, index);
+ return readStr;
+ } else {
+ readStr += data;
+ if (i !== len - 1) {
+ readStr += '\n';
+ }
+ }
+ }
+ if (nowTime - startTime >= timeout) {
+ return '';
+ }
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.dataReadInterrupt']);
+ }
+ await this.#device_.sleep(100);
+ }
+ }
+
+ async interrupt(timeout = 1000) {
+ for (let i = 0; i < 5; i++) {
+ // 中断两次
+ await this.#device_.sendBuffer([0x0D, 0x03]);
+ await this.#device_.sleep(100);
+ await this.#device_.sendBuffer([0x03]);
+ await this.#device_.sleep(100);
+ if (await this.readUntil('>>>', true, timeout)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ async enterRawREPL(timeout = 1000) {
+ for (let i = 0; i < 5; i++) {
+ await this.#device_.sendBuffer([0x01]);
+ await this.#device_.sleep(100);
+ if (await this.readUntil('raw repl; ctrl-b to exit', true, timeout)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ async exitRawREPL(timeout = 5000) {
+ await this.#device_.sendBuffer([0x02]);
+ await this.#device_.sleep(100);
+ let succeed = false;
+ if (await this.readUntil('>>>', true, timeout)) {
+ succeed = true;
+ }
+ return succeed;
+ }
+
+ async exitREPL(timeout = 5000) {
+ await this.#device_.sendBuffer([0x04]);
+ await this.#device_.sleep(100);
+ let succeed = false;
+ if (await this.readUntil('soft reboot', false, timeout)) {
+ succeed = true;
+ }
+ return succeed;
+ }
+
+ async exec(str, timeout = 5000) {
+ if (this.#writeBuffer_) {
+ const buffer = this.#device_.encode(str);
+ const len = Math.ceil(buffer.length / this.#dataLength_);
+ for (let i = 0; i < len; i++) {
+ const start = i * this.#dataLength_;
+ const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
+ const writeBuffer = buffer.slice(start, end);
+ await this.#device_.sendBuffer(writeBuffer);
+ await this.#device_.sleep(10);
+ }
+ } else {
+ for (let i = 0; i < str.length / this.#dataLength_; i++) {
+ const start = i * this.#dataLength_;
+ const end = Math.min((i + 1) * this.#dataLength_, str.length);
+ let data = str.substring(start, end);
+ await this.#device_.sendString(data);
+ await this.#device_.sleep(10);
+ }
+ }
+ await this.#device_.sendBuffer([0x04]);
+ return await this.follow(timeout);
+ }
+
+ async follow(timeout = 1000) {
+ let data = await this.readUntil('\x04', true, timeout);
+ if (data.length < 1) {
+ throw new Error(Msg.Lang['ampy.waitingFirstEOFTimeout']);
+ }
+ let start = data.toLowerCase().lastIndexOf('ok');
+ if (start === -1) {
+ start = 0;
+ } else {
+ start += 2;
+ }
+ data = data.substring(start, data.length - 1);
+ let dataError = await this.readUntil('\x04', true, timeout);
+ if (dataError.length < 1) {
+ throw new Error(Msg.Lang['ampy.secondEOFTimeout']);
+ }
+ dataError = dataError.substring(0, dataError.length - 1);
+ return { data, dataError };
+ }
+
+ async enter() {
+ if (this.isActive()) {
+ return;
+ }
+ this.#active_ = true;
+ await this.#device_.open(115200);
+ await this.#device_.sleep(500);
+ await this.#device_.sendBuffer([0x02]);
+ if (!await this.interrupt()) {
+ throw new Error(Msg.Lang['ampy.interruptFailed']);
+ }
+ if (!await this.enterRawREPL()) {
+ throw new Error(Msg.Lang['ampy.enterRawREPLFailed']);
+ }
+ }
+
+ async exit() {
+ if (!this.isActive()) {
+ return;
+ }
+ if (!await this.exitRawREPL()) {
+ throw new Error(Msg.Lang['ampy.exitRawREPLFailed']);
+ }
+ /*if (!await this.exitREPL()) {
+ throw new Error(Msg.Lang['ampy.exitREPLFailed']);
+ }*/
+ await this.#device_.close();
+ this.#active_ = false;
+ }
+
+ async get(filename, encoding = 'utf8', timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.GET, {
+ path: filename
+ });
+ const { data, dataError } = await this.exec(code, timeout);
+ if (dataError) {
+ return '';
+ }
+ if (encoding === 'utf8') {
+ return this.#device_.decode(this.unhexlify(data));
+ } else {
+ return this.unhexlify(data);
+ }
+ }
+
+ async put(filename, data, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ this.message(`Writing ${filename}...\n`);
+ this.message(this.getProgressMessage('', 0));
+ await this.exec(`file = open('${filename}', 'wb')`, timeout);
+ let buffer = null;
+ if (data.constructor === String) {
+ buffer = this.#device_.encode(data);
+ } else if (data.constructor === ArrayBuffer) {
+ buffer = new Uint8Array(data);
+ } else {
+ buffer = data;
+ }
+ const len = Math.ceil(buffer.length / 64);
+ if (!len) {
+ this.replaceMessage(-1, this.getProgressMessage('', 1));
+ }
+ let sendedLength = 0;
+ for (let i = 0; i < len; i++) {
+ const writeBuffer = buffer.slice(i * 64, Math.min((i + 1) * 64, buffer.length));
+ sendedLength += writeBuffer.length;
+ const percent = sendedLength / buffer.length;
+ this.replaceMessage(-1, this.getProgressMessage('', percent));
+ let writeStr = '';
+ for (let num of writeBuffer) {
+ let numStr = num.toString(16);
+ if (numStr.length === 1) {
+ numStr = '0' + numStr;
+ }
+ writeStr += '\\x' + numStr;
+ }
+ await this.exec(`file.write(b'${writeStr}')`, timeout);
+ }
+ await this.exec('file.close()', timeout);
+ this.message('\n');
+ }
+
+ async ls(directory = '/', longFormat = true, recursive = false, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ let code = '';
+ if (longFormat) {
+ code = Mustache.render(AmpyExt.LS_LONG_FORMAT, {
+ path: directory
+ });
+ } else if (recursive) {
+ code = Mustache.render(AmpyExt.LS_RECURSIVE, {
+ path: directory
+ });
+ } else {
+ code = Mustache.render(AmpyExt.LS, {
+ path: directory
+ });
+ }
+ const { data, dataError } = await this.exec(code, timeout);
+ if (dataError) {
+ return [];
+ }
+ return JSON.parse(data.replaceAll('\'', '\"'));
+ }
+
+ async mkdir(directory, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.MKDIR, {
+ path: directory
+ });
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async mkfile(file, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.MKFILE, {
+ path: file
+ });
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async rename(oldname, newname, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.RENAME, {
+ oldPath: oldname,
+ newPath: newname
+ });
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async rm(filename, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.RM, {
+ path: filename
+ });
+ await this.exec(code);
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async rmdir(directory, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.RMDIR, {
+ path: directory
+ });
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async cpdir(oldname, newname, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.CPDIR, {
+ oldPath: oldname,
+ newPath: newname
+ });
+ const { data, dataError } = await this.exec(code, timeout);
+ console.log(data, dataError)
+ return !dataError;
+ }
+
+ async cpfile(oldname, newname, timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.CPFILE, {
+ oldPath: oldname,
+ newPath: newname
+ });
+ const { dataError } = await this.exec(code, timeout);
+ return !dataError;
+ }
+
+ async cwd(timeout = 5000) {
+ if (!this.isActive()) {
+ throw new Error(Msg.Lang['ampy.portIsNotOpen']);
+ }
+ const code = Mustache.render(AmpyExt.CWD, {});
+ const { data, dataError } = await this.exec(code, timeout);
+ if (dataError) {
+ return '/';
+ }
+ return data;
+ }
+
+ getDevice() {
+ return this.#device_;
+ }
+
+ async dispose() {
+ this.#active_ = false;
+ await this.#device_.dispose();
+ this.#device_ = null;
+ this.#events_.reset();
+ this.#events_ = null;
+ }
+}
+
+Web.Ampy = AmpyExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/bluetooth.js b/mixly/common/modules/mixly-modules/web/bluetooth.js
new file mode 100644
index 00000000..fbc03643
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/bluetooth.js
@@ -0,0 +1,185 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.Bluetooth');
+
+const { Web } = Mixly;
+const { Bluetooth } = Web;
+
+Bluetooth.output = [];
+Bluetooth.mtu = 100;
+Bluetooth.encoder = new TextEncoder('utf-8');
+Bluetooth.decoder = new TextDecoder('utf-8');
+Bluetooth.nordicUartServiceUuid = 0xfff0;
+Bluetooth.uartRxCharacteristicUuid = 0xfff2;
+Bluetooth.uartTxCharacteristicUuid = 0xfff1;
+Bluetooth.obj = null;
+Bluetooth.server = null;
+Bluetooth.service = null;
+Bluetooth.uartRxCharacteristic = null;
+Bluetooth.uartTxCharacteristic = null;
+Bluetooth.name = 'bluetooth';
+
+Bluetooth.connect = (baud = 115200, onDataLine = (message) => {}) => {
+ return new Promise((resolve, reject) => {
+ if (Bluetooth.isConnected()) {
+ resolve();
+ return;
+ }
+ navigator.bluetooth.requestDevice({
+ // filters: [{name: ['Mixly']}]
+ optionalServices: [Bluetooth.nordicUartServiceUuid],
+ acceptAllDevices: true
+ })
+ .then((device) => {
+ Bluetooth.obj = device;
+ return device.gatt.connect();
+ })
+ .then((server) => {
+ Bluetooth.server = server;
+ return server.getPrimaryService(Bluetooth.nordicUartServiceUuid);
+ })
+ .then((service) => {
+ Bluetooth.service = service;
+ return service.getCharacteristic(Bluetooth.uartRxCharacteristicUuid);
+ })
+ .then((uartRxCharacteristic) => {
+ Bluetooth.uartRxCharacteristic = uartRxCharacteristic;
+ return Bluetooth.service.getCharacteristic(Bluetooth.uartTxCharacteristicUuid);
+ })
+ .then((uartTxCharacteristic) => {
+ Bluetooth.uartTxCharacteristic = uartTxCharacteristic;
+ return uartTxCharacteristic.startNotifications();
+ })
+ .then(() => {
+ Bluetooth.onDataLine = onDataLine;
+ Bluetooth.addReadEvent(onDataLine);
+ resolve();
+ })
+ .catch((error) => {
+ Bluetooth.obj = null;
+ Bluetooth.server = null;
+ Bluetooth.service = null;
+ Bluetooth.uartRxCharacteristic = null;
+ Bluetooth.uartTxCharacteristic = null;
+ reject(error);
+ });
+ });
+}
+
+Bluetooth.close = async () => {
+ if (Bluetooth.isConnected()) {
+ await Bluetooth.obj.gatt.disconnect();
+ Bluetooth.obj = null;
+ Bluetooth.server = null;
+ Bluetooth.service = null;
+ Bluetooth.uartRxCharacteristic = null;
+ Bluetooth.uartTxCharacteristic = null;
+ }
+}
+
+Bluetooth.isConnected = () => {
+ return Bluetooth.obj && Bluetooth.obj.gatt.connected;
+}
+
+Bluetooth.addReadEvent = (onDataLine = (message) => {}) => {
+ Bluetooth.uartTxCharacteristic.addEventListener('characteristicvaluechanged', event => {
+ let data = Bluetooth.decoder.decode(event.target.value);
+ let dataList = data.split('\n');
+ if (!dataList.length) {
+ return;
+ }
+ let endStr = '';
+ if (Bluetooth.output.length) {
+ endStr = Bluetooth.output.pop();
+ Bluetooth.output.push(endStr + dataList.shift());
+ if (dataList.length) {
+ // console.log(Bluetooth.output[Bluetooth.output.length - 1]);
+ onDataLine(Bluetooth.output[Bluetooth.output.length - 1]);
+ }
+ }
+ let i = 0;
+ for (let value of dataList) {
+ i++;
+ Bluetooth.output.push(value);
+ if (i < dataList.length) {
+ // console.log(value);
+ onDataLine(value);
+ }
+ }
+ while (Bluetooth.output.length > 500) {
+ Bluetooth.output.shift();
+ }
+ });
+}
+
+Bluetooth.AddOnConnectEvent = (onConnect) => {
+}
+
+Bluetooth.AddOnDisconnectEvent = (onDisconnect) => {
+ Bluetooth.obj.addEventListener('gattserverdisconnected', () => {
+ onDisconnect();
+ });
+}
+
+Bluetooth.writeString = async (str) => {
+ let buffer = Bluetooth.encoder.encode(str);
+ await Bluetooth.writeByteArr(buffer);
+}
+
+Bluetooth.writeByteArr = async (buffer) => {
+ buffer = new Uint8Array(buffer);
+ for (let chunk = 0; chunk < Math.ceil(buffer.length / Bluetooth.mtu); chunk++) {
+ let start = Bluetooth.mtu * chunk;
+ let end = Bluetooth.mtu * (chunk + 1);
+ await Bluetooth.uartRxCharacteristic.writeValueWithResponse(buffer.slice(start, end))
+ .catch(error => {
+ if (error == "NetworkError: GATT operation already in progress.") {
+ Bluetooth.writeByteArr(buffer);
+ }
+ else {
+ return Promise.reject(error);
+ }
+ });
+ }
+ await Bluetooth.sleep(200);
+}
+
+Bluetooth.writeCtrlA = async () => {
+ await Bluetooth.writeByteArr([1, 13, 10]);
+}
+
+Bluetooth.writeCtrlB = async () => {
+ await Bluetooth.writeByteArr([2, 13, 10]);
+}
+
+Bluetooth.writeCtrlC = async () => {
+ await Bluetooth.writeByteArr([3, 13, 10]);
+}
+
+Bluetooth.writeCtrlD = async () => {
+ await Bluetooth.writeByteArr([3, 4]);
+}
+
+Bluetooth.write = async (type, data, dataTail) => {
+ switch (type) {
+ case 'string':
+ return Bluetooth.writeString(data + dataTail);
+ break;
+ default:
+ await Bluetooth.writeByteArr(data);
+ return Bluetooth.writeString(dataTail);
+ }
+}
+
+Bluetooth.setSignals = async (dtr, rts) => {
+}
+
+Bluetooth.setBaudRate = async (baud) => {
+}
+
+Bluetooth.sleep = (ms) => {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/burn-upload.js b/mixly/common/modules/mixly-modules/web/burn-upload.js
new file mode 100644
index 00000000..245bb5bc
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/burn-upload.js
@@ -0,0 +1,738 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('BoardId');
+goog.require('FSWrapper');
+goog.require('DAPWrapper');
+goog.require('PartialFlashing');
+goog.require('esptooljs');
+goog.require('CryptoJS');
+goog.require('JSZip');
+goog.require('Mixly.Env');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.Config');
+goog.require('Mixly.MFile');
+goog.require('Mixly.Boards');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.require('Mixly.Debug');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.MString');
+goog.require('Mixly.LayerFirmware');
+goog.require('Mixly.LayerProgress');
+goog.require('Mixly.Web.Serial');
+goog.require('Mixly.Web.Ampy');
+goog.require('Mixly.Web.KFlash');
+goog.require('Mixly.Web.SerialTransport');
+goog.provide('Mixly.Web.BU');
+
+const {
+ Env,
+ Web,
+ LayerExt,
+ Config,
+ MFile,
+ Boards,
+ Msg,
+ Workspace,
+ Debug,
+ HTMLTemplate,
+ MString,
+ LayerFirmware,
+ LayerProgress
+} = Mixly;
+
+const {
+ Serial,
+ BU,
+ Ampy,
+ KFlash,
+ SerialTransport
+} = Web;
+
+const { BOARD, SELECTED_BOARD } = Config;
+const { ESPLoader } = esptooljs;
+
+
+BU.uploading = false;
+BU.burning = false;
+BU.firmwareLayer = new LayerFirmware({
+ width: 400,
+ title: Msg.Lang['nav.btn.burn'],
+ cancelValue: false,
+ cancel: false,
+ cancelDisplay: false
+});
+BU.firmwareLayer.bind('burn', (info) => {
+ const { web } = SELECTED_BOARD;
+ BU.burnWithEsptool(info, web.burn.erase);
+});
+BU.progressLayer = new LayerProgress({
+ width: 200,
+ cancelValue: false,
+ cancel: false,
+ cancelDisplay: false
+});
+
+if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ FSWrapper.setupFilesystem(path.join(Env.boardDirPath, 'build'));
+}
+
+BU.requestPort = async () => {
+ await Serial.requestPort();
+}
+
+const readBinFile = (path, offset) => {
+ return new Promise((resolve, reject) => {
+ fetch(path)
+ .then((response) => {
+ return response.blob();
+ })
+ .then((blob) => {
+ const reader = new FileReader();
+ reader.onload = function (event) {
+ resolve({
+ address: parseInt(offset),
+ data: event.target.result
+ });
+ };
+ reader.onerror = function (error) {
+ throw(error);
+ }
+ reader.readAsBinaryString(blob);
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+}
+
+const readBinFileAsArrayBuffer = (path, offset) => {
+ return new Promise((resolve, reject) => {
+ fetch(path)
+ .then((response) => {
+ return response.blob();
+ })
+ .then((blob) => {
+ const reader = new FileReader();
+ reader.onload = function (event) {
+ resolve({
+ address: parseInt(offset),
+ data: event.target.result
+ });
+ };
+ reader.onerror = function (error) {
+ throw(error);
+ }
+ reader.readAsArrayBuffer(blob);
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+}
+
+const decodeKfpkgFromArrayBuffer = async(arrayBuf) => {
+ const zip = await JSZip.loadAsync(arrayBuf);
+ const manifestEntry = zip.file('flash-list.json');
+ if (!manifestEntry) {
+ throw new Error('kfpkg is missing flash-list.json');
+ }
+ const manifestText = await manifestEntry.async('string');
+ const manifest = JSON.parse(manifestText);
+
+ const items = [];
+ for (const f of manifest.files || []) {
+ const entry = zip.file(f.bin);
+ if (!entry) {
+ throw new Error(`Missing files in package: ${f.bin}`);
+ }
+ const data = new Uint8Array(await entry.async('uint8array'));
+ items.push({
+ address: f.address >>> 0,
+ filename: f.bin,
+ sha256Prefix: !!f.sha256Prefix,
+ data
+ });
+ }
+
+ return { manifest, items };
+}
+
+BU.initBurn = async () => {
+ if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ await BU.burnWithDAP();
+ } else if (['MixGo AI'].includes(BOARD.boardType)) {
+ const { web } = SELECTED_BOARD;
+ const boardKey = Boards.getSelectedBoardKey();
+ if (!web?.burn?.binFile) {
+ return;
+ }
+ if (typeof web.burn.binFile !== 'object') {
+ return;
+ }
+ await BU.burnWithKFlash(web.burn.binFile, web.burn.erase);
+ } else {
+ const { web } = SELECTED_BOARD;
+ const boardKey = Boards.getSelectedBoardKey();
+ if (!web?.burn?.binFile) {
+ return;
+ }
+ if (typeof web.burn.binFile !== 'object') {
+ return;
+ }
+ if (web.burn.special && web.burn.special instanceof Array) {
+ BU.burnWithSpecialBin();
+ } else {
+ await BU.burnWithEsptool(web.burn.binFile, web.burn.erase);
+ }
+ }
+}
+
+BU.burnWithDAP = async () => {
+ const { mainStatusBarTabs } = Mixly;
+ let portName = Serial.getSelectedPortName();
+ if (!portName) {
+ try {
+ await BU.requestPort();
+ portName = Serial.getSelectedPortName();
+ if (!portName) {
+ return;
+ }
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ }
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+
+ const { web } = SELECTED_BOARD;
+ const { burn } = web;
+ const hexStr = goog.readFileSync(path.join(Env.boardDirPath, burn.filePath));
+ const hex2Blob = new Blob([ hexStr ], { type: 'text/plain' });
+ const buffer = await hex2Blob.arrayBuffer();
+ if (!buffer) {
+ layer.msg(Msg.Lang['shell.bin.readFailed'], { time: 1000 });
+ return;
+ }
+ BU.burning = true;
+ BU.uploading = false;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue(`${Msg.Lang['shell.burning']}...\n`);
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.changeTo('output');
+ const port = Serial.getPort(portName);
+ const webUSB = new DAPjs.WebUSB(port);
+ const dapLink = new DAPjs.DAPLink(webUSB);
+ try {
+ await dapLink.connect();
+ await dapLink.setSerialBaudrate(115200);
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ let prevPercent = 0;
+ dapLink.on(DAPjs.DAPLink.EVENT_PROGRESS, progress => {
+ const nowPercent = Math.floor(progress * 100);
+ if (nowPercent > prevPercent) {
+ prevPercent = nowPercent;
+ } else {
+ return;
+ }
+ const nowProgressLen = Math.floor(nowPercent / 2);
+ const leftStr = new Array(nowProgressLen).fill('=').join('');
+ const rightStr = (new Array(50 - nowProgressLen).fill('-')).join('');
+ statusBarTerminal.addValue(`[${leftStr}${rightStr}] ${nowPercent}%\n`);
+ });
+ BU.progressLayer.title(`${Msg.Lang['shell.burning']}...`);
+ BU.progressLayer.show();
+ try {
+ await dapLink.flash(buffer);
+ BU.progressLayer.hide();
+ layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnSucc']}==\n`);
+ } catch (error) {
+ Debug.error(error);
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ } finally {
+ dapLink.removeAllListeners(DAPjs.DAPLink.EVENT_PROGRESS);
+ await dapLink.disconnect();
+ await webUSB.close();
+ await port.close();
+ }
+}
+
+BU.burnWithEsptool = async (binFile, erase) => {
+ const { mainStatusBarTabs } = Mixly;
+ let portName = Serial.getSelectedPortName();
+ if (!portName) {
+ try {
+ await BU.requestPort();
+ portName = Serial.getSelectedPortName();
+ if (!portName) {
+ return;
+ }
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ }
+ const port = Serial.getPort(portName);
+ if (['HIDDevice'].includes(port.constructor.name)) {
+ layer.msg(Msg.Lang['burn.notSupport'], { time: 1000 });
+ return;
+ }
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue(Msg.Lang['shell.burning'] + '...\n');
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.changeTo('output');
+ BU.progressLayer.title(`${Msg.Lang['shell.burning']}...`);
+ BU.progressLayer.show();
+ let esploader = null;
+ let transport = null;
+ try {
+ const baudrate = Boards.getSelectedBoardConfigParam('BurnSpeed') ?? '460800';
+ const serial = new Serial(portName);
+ transport = new SerialTransport(serial, false);
+ esploader = new ESPLoader({
+ transport,
+ baudrate,
+ terminal: {
+ clean() {
+ statusBarTerminal.setValue(Msg.Lang['shell.burning'] + '...\n');
+ },
+ writeLine(data) {
+ statusBarTerminal.addValue(data + '\n');
+ },
+ write(data) {
+ statusBarTerminal.addValue(data);
+ }
+ }
+ });
+ await esploader.main();
+ } catch (error) {
+ Debug.error(error);
+ statusBarTerminal.addValue(`\n${error.toString()}\n`);
+ try {
+ await transport.disconnect();
+ } catch (error) {
+ Debug.error(error);
+ }
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ return;
+ }
+
+ statusBarTerminal.addValue(Msg.Lang['shell.bin.reading'] + "...");
+ let firmwarePromise = [];
+ statusBarTerminal.addValue("\n");
+ for (let i of binFile) {
+ if (i.path && i.offset) {
+ let absolutePath = path.join(Env.boardDirPath, i.path);
+ firmwarePromise.push(readBinFile(absolutePath, i.offset));
+ }
+ }
+ let data = null;
+ try {
+ data = await Promise.all(firmwarePromise);
+ } catch (error) {
+ statusBarTerminal.addValue("Failed!\n" + Msg.Lang['shell.bin.readFailed'] + "!\n");
+ statusBarTerminal.addValue("\n" + error + "\n", true);
+ try {
+ await transport.disconnect();
+ } catch (error) {
+ Debug.error(error);
+ }
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ return;
+ }
+ statusBarTerminal.addValue("Done!\n");
+ BU.burning = true;
+ BU.uploading = false;
+ const flashOptions = {
+ fileArray: data,
+ flashSize: 'keep',
+ eraseAll: erase,
+ compress: true,
+ calculateMD5Hash: (image) => CryptoJS.MD5(CryptoJS.enc.Latin1.parse(image))
+ };
+ try {
+ await esploader.writeFlash(flashOptions);
+ await transport.setDTR(false);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ await transport.setDTR(true);
+ BU.progressLayer.hide();
+ layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnSucc']}==\n`);
+ } catch (error) {
+ Debug.error(error);
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ } finally {
+ try {
+ await transport.disconnect();
+ } catch (error) {
+ Debug.error(error);
+ }
+ }
+}
+
+BU.burnWithKFlash = async (binFile, erase) => {
+ const { mainStatusBarTabs } = Mixly;
+ let portName = Serial.getSelectedPortName();
+ if (!portName) {
+ try {
+ await BU.requestPort();
+ portName = Serial.getSelectedPortName();
+ if (!portName) {
+ return;
+ }
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ }
+ const port = Serial.getPort(portName);
+ if (['HIDDevice', 'USBDevice'].includes(port.constructor.name)) {
+ layer.msg(Msg.Lang['burn.notSupport'], { time: 1000 });
+ return;
+ }
+ const statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ statusBarTerminal.setValue(Msg.Lang['shell.burning'] + '...\n');
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.changeTo('output');
+ BU.progressLayer.title(`${Msg.Lang['shell.burning']}...`);
+ BU.progressLayer.show();
+
+ let data = [];
+ try {
+ for (let i of binFile) {
+ if (i.path && i.offset) {
+ const extname = path.extname(i.path);
+ const absolutePath = path.join(Env.boardDirPath, i.path);
+ const info = await readBinFileAsArrayBuffer(absolutePath, i.offset);
+ if (extname === '.kfpkg') {
+ const result = await decodeKfpkgFromArrayBuffer(info.data);
+ data.push(...result.items);
+ } else {
+ data.push(info);
+ }
+ }
+ }
+ } catch (error) {
+ statusBarTerminal.addValue(`\n[ERROR] ${error}\n`, true);
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ return;
+ }
+ let serial = null;
+ try {
+ serial = new Serial(portName);
+ const kflash = new KFlash(serial);
+ kflash.bind('message', (message) => {
+ statusBarTerminal.addValue(message);
+ });
+ kflash.bind('replaceMessage', (lineNumber, message) => {
+ statusBarTerminal.replaceLine(lineNumber, message);
+ });
+ await kflash.enter();
+ for (let item of data) {
+ await kflash.write(item.data, item.address, item.sha256Prefix ?? true, item?.filename ?? 'main.bin');
+ }
+ BU.progressLayer.hide();
+ layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
+ statusBarTerminal.appendLine(`==${Msg.Lang['shell.burnSucc']}==\n`);
+ } catch (error) {
+ statusBarTerminal.appendLine(`[ERROR] ${error.message}\n`);
+ BU.progressLayer.hide();
+ statusBarTerminal.appendLine(`==${Msg.Lang['shell.burnFailed']}==\n`);
+ } finally {
+ try {
+ serial && await serial.close();
+ } catch (error) {
+ Debug.error(error);
+ }
+ }
+}
+
+BU.getImportModulesName = (code) => {
+ // 正则表达式: 匹配 import 或 from 导入语句
+ const importRegex = /(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)/g;
+
+ let imports = [];
+ let match;
+ while ((match = importRegex.exec(code)) !== null) {
+ if (match[1]) {
+ imports.push(match[1]); // 'import module'
+ }
+ if (match[2]) {
+ imports.push(match[2]); // 'from module import ...'
+ }
+ }
+ return imports;
+}
+
+BU.getImportModules = (code) => {
+ let importsMap = {};
+ const libPath = SELECTED_BOARD.upload.libPath;
+ for (let i = libPath.length - 1; i >= 0; i--) {
+ const dirname = MString.tpl(libPath[i], { indexPath: Env.boardDirPath });
+ const map = goog.readJsonSync(path.join(dirname, 'map.json'));
+ if (!(map && map instanceof Object)) {
+ continue;
+ }
+ for (let key in map) {
+ importsMap[key] = structuredClone(map[key]);
+ importsMap[key]['__path__'] = path.join(dirname, map[key]['__name__']);
+ }
+ }
+
+ let usedMap = {};
+ let currentImports = BU.getImportModulesName(code);
+ while (currentImports.length) {
+ let temp = [];
+ for (let moduleName of currentImports) {
+ let moduleInfo = importsMap[moduleName];
+ if (!moduleInfo) {
+ continue;
+ }
+ usedMap[moduleName] = moduleInfo;
+ const moduleImports = moduleInfo['__require__'];
+ if (!moduleImports) {
+ continue;
+ }
+ for (let name of moduleImports) {
+ if (usedMap[name] || !importsMap[name] || temp.includes(name)) {
+ continue;
+ }
+ temp.push(name);
+ }
+ }
+ currentImports = temp;
+ }
+ return usedMap;
+}
+
+BU.initUpload = async () => {
+ let portName = Serial.getSelectedPortName();
+ if (!portName) {
+ try {
+ await BU.requestPort();
+ portName = Serial.getSelectedPortName();
+ if (!portName) {
+ return;
+ }
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ }
+ if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ await BU.uploadWithDAP(portName);
+ } else {
+ await BU.uploadWithAmpy(portName);
+ }
+}
+
+BU.uploadWithDAP = async (portName) => {
+ const { mainStatusBarTabs } = Mixly;
+ if (!portName) {
+ try {
+ await BU.requestPort();
+ portName = Serial.getSelectedPortName();
+ if (!portName) {
+ return;
+ }
+ } catch (error) {
+ Debug.error(error);
+ return;
+ }
+ }
+ let statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ if (statusBarSerial) {
+ await statusBarSerial.close();
+ }
+
+ const port = Serial.getPort(portName);
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const dapWrapper = new DAPWrapper(port, {
+ event: () => {},
+ log: () => {}
+ });
+ const partialFlashing = new PartialFlashing(dapWrapper, {
+ event: () => {}
+ });
+
+ let boardId = 0x9901;
+ const boardKey = Boards.getSelectedBoardKey();
+ if (boardKey === 'micropython:nrf51822:v2') {
+ boardId = 0x9903;
+ }
+
+ BU.burning = false;
+ BU.uploading = true;
+ statusBarTerminal.setValue(Msg.Lang['shell.uploading'] + '...\n');
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.changeTo('output');
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const code = editor.getCode();
+ FSWrapper.writeFile('main.py', code);
+ const importsMap = BU.getImportModules(code);
+ for (let key in importsMap) {
+ const filename = importsMap[key]['__name__'];
+ const data = goog.readFileSync(importsMap[key]['__path__']);
+ FSWrapper.writeFile(filename, data);
+ }
+ BU.progressLayer.title(`${Msg.Lang['shell.uploading']}...`);
+ BU.progressLayer.show();
+ try {
+ let prevPercent = 0;
+ await partialFlashing.flashAsync(new BoardId(0x9900), FSWrapper, progress => {
+ const nowPercent = Math.floor(progress * 100);
+ if (nowPercent > prevPercent) {
+ prevPercent = nowPercent;
+ } else {
+ return;
+ }
+ const nowProgressLen = Math.floor(nowPercent / 2);
+ const leftStr = new Array(nowProgressLen).fill('=').join('');
+ const rightStr = (new Array(50 - nowProgressLen).fill('-')).join('');
+ statusBarTerminal.addValue(`[${leftStr}${rightStr}] ${nowPercent}%\n`);
+ });
+ BU.progressLayer.hide();
+ layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
+ if (!statusBarSerial) {
+ mainStatusBarTabs.add('serial', portName);
+ statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ }
+ statusBarSerial.setValue('');
+ mainStatusBarTabs.changeTo(portName);
+ await statusBarSerial.open();
+ } catch (error) {
+ await dapWrapper.disconnectAsync();
+ Debug.error(error);
+ BU.progressLayer.hide();
+ statusBarTerminal.addValue(`${error}\n`);
+ statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
+ }
+}
+
+BU.uploadWithAmpy = async (portName) => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ let statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ BU.burning = false;
+ BU.uploading = true;
+ statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
+ mainStatusBarTabs.show();
+ mainStatusBarTabs.changeTo('output');
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const port = Serial.getPort(portName);
+ BU.progressLayer.title(`${Msg.Lang['shell.uploading']}...`);
+ BU.progressLayer.show();
+ const serial = new Serial(portName);
+ const ampy = new Ampy(serial);
+ ampy.bind('message', (message) => {
+ statusBarTerminal.addValue(message);
+ });
+ ampy.bind('replaceMessage', (lineNumber, message) => {
+ statusBarTerminal.replaceLine(lineNumber, message);
+ });
+ const code = editor.getCode();
+ let closePromise = Promise.resolve();
+ if (statusBarSerial) {
+ closePromise = statusBarSerial.close();
+ }
+ try {
+ await closePromise;
+ await ampy.enter();
+ await ampy.put('main.py', code);
+ /*const importsMap = BU.getImportModules(code);
+ let libraries = {};
+ for (let key in importsMap) {
+ const filename = importsMap[key]['__name__'];
+ const data = goog.readFileSync(importsMap[key]['__path__']);
+ libraries[filename] = {
+ data,
+ size: importsMap[key]['__size__']
+ };
+ }
+ let cwd = await ampy.cwd();
+ const rootInfo = await ampy.ls(cwd);
+ let rootMap = {};
+ for (let item of rootInfo) {
+ rootMap[item[0]] = item[1];
+ }
+ if (cwd === '/') {
+ cwd = '';
+ }
+ if (libraries && libraries instanceof Object) {
+ for (let key in libraries) {
+ if (rootMap[`${cwd}/${key}`] !== undefined && rootMap[`${cwd}/${key}`] === libraries[key].size) {
+ statusBarTerminal.addValue(`Writing ${key} (Skipped)\n`);
+ continue;
+ }
+ await ampy.put(key, libraries[key].data);
+ }
+ }*/
+ await ampy.exit();
+ await ampy.dispose();
+ BU.progressLayer.hide();
+ layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
+ statusBarTerminal.appendLine(`==${Msg.Lang['shell.uploadSucc']}==\n`);
+ if (!statusBarSerial) {
+ mainStatusBarTabs.add('serial', portName);
+ statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
+ }
+ statusBarSerial.setValue('');
+ mainStatusBarTabs.changeTo(portName);
+ await statusBarSerial.open();
+ } catch (error) {
+ ampy.dispose();
+ BU.progressLayer.hide();
+ Debug.error(error);
+ statusBarTerminal.appendLine(`[ERROR] ${error.message}\n`);
+ statusBarTerminal.appendLine(`==${Msg.Lang['shell.uploadFailed']}==\n`);
+ }
+}
+
+/**
+ * @function 特殊固件的烧录
+ * @return {void}
+ **/
+BU.burnWithSpecialBin = () => {
+ const { mainStatusBarTabs } = Mixly;
+ const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
+ const firmwares = SELECTED_BOARD.web.burn.special;
+ let menu = [];
+ let firmwareMap = {};
+ for (let firmware of firmwares) {
+ if (!firmware?.name && !firmware?.binFile) continue;
+ menu.push({
+ id: firmware.name,
+ text: firmware.name
+ });
+ firmwareMap[firmware.name] = firmware.binFile;
+ }
+ BU.firmwareLayer.setMap(firmwareMap);
+ BU.firmwareLayer.setMenu(menu);
+ BU.firmwareLayer.show();
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/file-tree.js b/mixly/common/modules/mixly-modules/web/file-tree.js
new file mode 100644
index 00000000..a968a7c1
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/file-tree.js
@@ -0,0 +1,47 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mixly.FileTree');
+goog.require('Mixly.Web.FS');
+goog.provide('Mixly.Web.FileTree');
+
+const { FileTree, Web } = Mixly;
+const { FS } = Web;
+
+class FileTreeExt extends FileTree {
+ constructor() {
+ super(FS);
+ }
+
+ async readFolder(inPath) {
+ const fs = this.getFS();
+ const status = await fs.isDirectory(inPath);
+ let output = [];
+ if (!status) {
+ return output;
+ }
+ const children = await fs.readDirectory(inPath);
+ for (let data of children) {
+ const dataPath = path.join(inPath, data);
+ if (await fs.isDirectory(dataPath)) {
+ const isDirEmpty = await fs.isDirectoryEmpty(dataPath);
+ output.push({
+ type: 'folder',
+ id: dataPath,
+ children: !isDirEmpty
+ });
+ } else {
+ output.push({
+ type: 'file',
+ id: dataPath,
+ children: false
+ });
+ }
+ }
+ return output;
+ }
+}
+
+Web.FileTree = FileTreeExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/file.js b/mixly/common/modules/mixly-modules/web/file.js
new file mode 100644
index 00000000..93468011
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/file.js
@@ -0,0 +1,218 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Blockly');
+goog.require('Mixly.MFile');
+goog.require('Mixly.Title');
+goog.require('Mixly.LayerNewFile');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Workspace');
+goog.provide('Mixly.Web.File');
+
+const {
+ MFile,
+ Web,
+ LayerNewFile,
+ Msg,
+ Title,
+ Workspace
+} = Mixly;
+
+const { MSG } = Blockly.Msg;
+
+const { File } = Web;
+
+const platform = goog.platform();
+
+File.obj = null;
+File.newFileLayer = new LayerNewFile();
+File.newFileLayer.bind('empty', () => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const blockEditor = editor.getPage('block').getEditor();
+ const codeEditor = editor.getPage('code').getEditor();
+ const generator = Blockly.generator;
+ blockEditor.clear();
+ blockEditor.scrollCenter();
+ Blockly.hideChaff();
+ codeEditor.setValue(generator.workspaceToCode(blockEditor) || '', -1);
+ Title.updateTitle(Title.title);
+ File.obj = null;
+});
+
+
+File.getFileTypes = (filters) => {
+ let fileTypes = [];
+ if (platform === 'mobile') {
+ fileTypes.push({
+ description: 'Mixly File',
+ accept: {
+ 'application/octet-stream': filters
+ }
+ });
+ } else {
+ fileTypes.push({
+ description: 'Mixly File',
+ accept: {
+ 'application/xml': filters
+ }
+ });
+ }
+ return fileTypes;
+}
+
+File.open = async () => {
+ if (window.location.protocol === 'https:') {
+ let filters = [];
+ MFile.openFilters.map((data) => {
+ filters.push(`.${data}`);
+ });
+ const fileConfig = {
+ multiple: false,
+ types: File.getFileTypes(filters),
+ excludeAcceptAllOption: true
+ };
+ try {
+ const [ obj ] = await window.showOpenFilePicker(fileConfig);
+ if (!obj) {
+ return;
+ }
+ File.obj = obj;
+ const extname = path.extname(obj.name);
+ const fileInfo = await File.obj.getFile();
+ if (!fileInfo) {
+ return;
+ }
+ File.parseData(extname, await fileInfo.text());
+ Title.updateTitle(`${obj.name} - ${Title.title}`);
+ } catch (error) {
+ console.log(error);
+ }
+ } else {
+ const filters = '.' + MFile.openFilters.join(',.');
+ MFile.openFile(filters, 'text', (fileObj) => {
+ let { data, filename } = fileObj;
+ const extname = path.extname(filename);
+ File.parseData(extname, data);
+ Title.updateTitle(`${filename} - ${Title.title}`);
+ });
+ }
+}
+
+File.parseData = (extname, text) => {
+ const index = extname.indexOf(' ');
+ if (index !== -1) {
+ extname = extname.substring(0, index);
+ }
+ if (['.bin', '.hex'].includes(extname)) {
+ MFile.loadHex(text);
+ } else if (['.mix', '.xml', '.ino', '.py'].includes(extname)) {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ editor.setValue(text, extname);
+ } else {
+ layer.msg(Msg.Lang['file.type.error'], { time: 1000 });
+ File.obj = null;
+ }
+}
+
+File.save = async () => {
+ window.userEvents && window.userEvents.addRecord({
+ operation: 'save'
+ });
+ if (!File.obj) {
+ File.saveAs();
+ return;
+ }
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ let text = '';
+ let extname = path.extname(File.obj.name);
+ const index = extname.indexOf(' ');
+ if (index !== -1) {
+ extname = extname.substring(0, index);
+ }
+ if (['.mix', '.xml'].includes(extname)) {
+ text = editor.getValue();
+ } else if (['.ino', '.py'].includes(extname)) {
+ text = editor.getCode();
+ } else {
+ return;
+ }
+ try {
+ let currentLayero = null;
+ const loadIndex = layer.msg(Msg.Lang['file.saving'], {
+ icon: 16,
+ shade: 0,
+ time: 0,
+ success: function(layero) {
+ currentLayero = layero;
+ }
+ });
+ const writer = await File.obj.createWritable({
+ keepExistingData: true
+ });
+ await writer.write(text);
+ await writer.close();
+ let $content = currentLayero.children('.layui-layer-content');
+ $content.html(`
${Msg.Lang['file.saveSucc']}`);
+ currentLayero = null;
+ $content = null;
+ setTimeout(() => {
+ layer.close(loadIndex);
+ }, 500);
+ } catch (error) {
+ console.log(error);
+ }
+}
+
+File.saveAs = async () => {
+ let filters = [];
+ MFile.saveFilters.map((data) => {
+ filters.push(`.${data.extensions[0]}`);
+ });
+ const fileConfig = {
+ types: File.getFileTypes(filters),
+ suggestedName: 'mixly.mix'
+ };
+ try {
+ const obj = await window.showSaveFilePicker(fileConfig);
+ if (!obj) {
+ return;
+ }
+ File.obj = obj;
+ File.save();
+ Title.updateTitle(`${obj.name} - ${Title.title}`);
+ } catch (error) {
+ console.log(error);
+ }
+}
+
+File.new = async () => {
+ const mainWorkspace = Workspace.getMain();
+ const editor = mainWorkspace.getEditorsManager().getActive();
+ const blockEditor = editor.getPage('block').getEditor();
+ const codeEditor = editor.getPage('code').getEditor();
+ const generator = Blockly.generator;
+ const blocksList = blockEditor.getAllBlocks();
+ if (editor.getPageType() === 'code') {
+ const code = codeEditor.getValue(),
+ workspaceToCode = generator.workspaceToCode(blockEditor) || '';
+ if (!blocksList.length && workspaceToCode === code) {
+ layer.msg(Msg.Lang['editor.codeEditorEmpty'], { time: 1000 });
+ Title.updateTitle(Title.title);
+ File.obj = null;
+ return;
+ }
+ } else {
+ if (!blocksList.length) {
+ layer.msg(Msg.Lang['editor.blockEditorEmpty'], { time: 1000 });
+ Title.updateTitle(Title.title);
+ File.obj = null;
+ return;
+ }
+ }
+ File.newFileLayer.show();
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/footerlayer-example.js b/mixly/common/modules/mixly-modules/web/footerlayer-example.js
new file mode 100644
index 00000000..75b44f49
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/footerlayer-example.js
@@ -0,0 +1,87 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.MJson');
+goog.require('Mixly.FooterLayerExample');
+goog.require('Mixly.Boards');
+goog.provide('Mixly.Web.FooterLayerExample');
+
+const {
+ Config,
+ Env,
+ FooterLayerExample,
+ MJson,
+ Boards,
+ Web
+} = Mixly;
+
+const { BOARD } = Config;
+
+class FooterLayerExampleExt extends FooterLayerExample {
+ static DIR_TREE = MJson.get(path.join(Env.boardDirPath, 'examples/map.json')) ?? [];
+
+ constructor(element) {
+ super(element);
+ }
+
+ getRoot() {
+ const { DIR_TREE } = FooterLayerExampleExt;
+ let exampleList = [];
+ if (DIR_TREE instanceof Object) {
+ exampleList = [{
+ title: BOARD.boardType,
+ id: '',
+ children: []
+ }];
+ }
+ return exampleList;
+ }
+
+ getChildren(inPath) {
+ const { DIR_TREE } = FooterLayerExampleExt;
+ let pathList = [];
+ if (inPath) {
+ pathList = inPath.split('/');
+ }
+ let obj = DIR_TREE;
+ for (let key of pathList) {
+ if (!key) {
+ continue;
+ }
+ if (obj[key]) {
+ obj = obj[key];
+ } else {
+ return [];
+ }
+ }
+ if (!(obj instanceof Object)) {
+ return [];
+ }
+ let exampleList = [];
+ for (let key in obj) {
+ if (!(obj[key] instanceof Object)) {
+ continue;
+ }
+ const exampleObj = {
+ title: obj[key]['__name__'],
+ id: inPath ? (inPath + '/' + key) : key
+ };
+ if (!obj[key]['__file__']) {
+ exampleObj.children = [];
+ }
+ exampleList.push(exampleObj);
+ }
+ return exampleList;
+ }
+
+ dataToWorkspace(inPath) {
+ const data = goog.readFileSync(path.join(Env.boardDirPath, 'examples', inPath));
+ this.updateCode(path.extname(inPath), data);
+ }
+}
+
+Web.FooterLayerExample = FooterLayerExampleExt;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/fs.js b/mixly/common/modules/mixly-modules/web/fs.js
new file mode 100644
index 00000000..a0b15828
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/fs.js
@@ -0,0 +1,161 @@
+goog.loadJs('web', () => {
+
+goog.require('workerpool');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.FS');
+
+const { FS } = Mixly.Web;
+
+FS.pool = workerpool.pool('../common/modules/mixly-modules/workers/web/file-system-access.js', {
+ workerOpts: {
+ name: 'fileSystemAccess'
+ },
+ workerType: 'web'
+});
+
+FS.showOpenFilePicker = async () => {
+ return new Promise((resolve, reject) => {
+ resolve();
+ });
+}
+
+FS.showDirectoryPicker = async () => {
+ const directoryHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
+ const permissionStatus = await directoryHandle.requestPermission({ mode: 'readwrite' });
+ if (permissionStatus !== 'granted') {
+ throw new Error('readwrite access to directory not granted');
+ }
+ await FS.pool.exec('addFileSystemHandler', [directoryHandle]);
+ return directoryHandle;
+}
+
+FS.showSaveFilePicker = async () => {
+ return new Promise((resolve, reject) => {
+
+ });
+}
+
+FS.readFile = (path) => {
+ return new Promise(async (resolve, reject) => {
+ const [data] = await FS.pool.exec('readFile', [path, 'utf8']);
+ resolve(data);
+ });
+}
+
+FS.writeFile = (path, data) => {
+ return new Promise(async (resolve, reject) => {
+ const [error, entries] = await FS.pool.exec('writeFile', [path, data, 'utf8']);
+ if (error) {
+ reject(error);
+ } else {
+ resolve(entries);
+ }
+ });
+}
+
+FS.isFile = (path) => {
+ return new Promise(async (resolve, reject) => {
+ const [_, stats] = await FS.pool.exec('stat', [path]);
+ if (!stats) {
+ resolve(false);
+ return;
+ }
+ if (stats.mode === 33188) {
+ resolve(true);
+ } else {
+ resolve(false);
+ }
+ });
+}
+
+FS.renameFile = (oldFilePath, newFilePath) => {
+ return new Promise(async (resolve, reject) => {
+ const [error] = await FS.pool.exec('rename', [oldFilePath, newFilePath]);
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
+}
+
+FS.moveFile = (oldFilePath, newFilePath) => {
+ return FS.renameFile(oldFilePath, newFilePath);
+}
+
+FS.deleteFile = (filePath) => {
+ return new Promise(async (resolve, reject) => {
+ const [error] = await FS.pool.exec('unlink', [filePath]);
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
+}
+
+FS.createDirectory = (folderPath) => {
+ return new Promise(async (resolve, reject) => {
+ const [error] = await FS.pool.exec('mkdir', [folderPath, 0o777]);
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
+}
+
+FS.readDirectory = (path) => {
+ return new Promise(async (resolve, reject) => {
+ const [error, entries] = await FS.pool.exec('readdir', [path]);
+ if (error) {
+ reject(error);
+ } else {
+ resolve(entries);
+ }
+ });
+}
+
+FS.isDirectory = (path) => {
+ return new Promise(async (resolve, reject) => {
+ const [_, stats] = await FS.pool.exec('stat', [path]);
+ if (!stats) {
+ resolve(false);
+ return;
+ }
+ if (stats.mode === 33188) {
+ resolve(false);
+ } else {
+ resolve(true);
+ }
+ });
+}
+
+FS.isDirectoryEmpty = async (path) => {
+ return !(await FS.readDirectory(path) ?? []).length;
+}
+
+FS.moveDirectory = (oldFolderPath, newFolderPath) => {
+ return new Promise(async (resolve, reject) => {
+ resolve();
+ });
+}
+
+FS.copyDirectory = (oldFolderPath, newFolderPath) => {
+ return new Promise(async (resolve, reject) => {
+ resolve();
+ });
+}
+
+FS.deleteDirectory = (folderPath) => {
+ return new Promise(async (resolve, reject) => {
+ const [error] = await FS.pool.exec('rmdir', [folderPath]);
+ if (!error) {
+ resolve();
+ } else {
+ reject();
+ }
+ });
+}
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/hid.js b/mixly/common/modules/mixly-modules/web/hid.js
new file mode 100644
index 00000000..c739fe87
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/hid.js
@@ -0,0 +1,247 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Config');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.HID');
+
+const {
+ Serial,
+ Config,
+ Web
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+class WebHID extends Serial {
+ static {
+ this.type = 'hid';
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.refreshPorts = function () {
+ Serial.refreshPorts();;
+ }
+
+ this.requestPort = async function () {
+ let options = SELECTED_BOARD?.web?.devices?.hid;
+ if (!options || typeof(options) !== 'object') {
+ options = {
+ filters: []
+ };
+ }
+ const devices = await navigator.hid.requestDevice(options);
+ if (!devices.length) {
+ return;
+ }
+ for (let device of devices) {
+ this.addPort(device);
+ }
+ this.refreshPorts();
+ }
+
+ this.getPort = function (name) {
+ return Serial.nameToPortRegistry.getItem(name);
+ }
+
+ this.addPort = function (device) {
+ if (Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ let name = '';
+ for (let i = 1; i <= 20; i++) {
+ name = `hid${i}`;
+ if (Serial.nameToPortRegistry.hasKey(name)) {
+ continue;
+ }
+ break;
+ }
+ Serial.portToNameRegistry.register(device, name);
+ Serial.nameToPortRegistry.register(name, device);
+ }
+
+ this.removePort = function (device) {
+ if (!Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ const name = Serial.portToNameRegistry.getItem(device);
+ if (!name) {
+ return;
+ }
+ Serial.portToNameRegistry.unregister(device);
+ Serial.nameToPortRegistry.unregister(name);
+ }
+
+ this.addEventsListener = function () {
+ navigator?.hid?.addEventListener('connect', (event) => {
+ this.addPort(event.device);
+ this.refreshPorts();
+ });
+
+ navigator?.hid?.addEventListener('disconnect', (event) => {
+ event.device.onclose && event.device.onclose();
+ this.removePort(event.device);
+ this.refreshPorts();
+ });
+ }
+
+ this.init = function () {
+ navigator?.hid?.getDevices().then((devices) => {
+ for (let device of devices) {
+ this.addPort(device);
+ }
+ });
+ this.addEventsListener();
+ }
+ }
+
+ #device_ = null;
+ #keepReading_ = null;
+ #reader_ = null;
+ #writer_ = null;
+ #stringTemp_ = '';
+ #dataLength_ = 31;
+ constructor(port) {
+ super(port);
+ this.#device_ = WebHID.getPort(port);
+ }
+
+ #addEventsListener_() {
+ this.#device_.oninputreport = (event) => {
+ const { data } = event;
+ const length = Math.min(data.getUint8(0) + 1, data.byteLength);
+ let buffer = [];
+ for (let i = 1; i < length; i++) {
+ buffer.push(data.getUint8(i));
+ }
+ this.onBuffer(buffer);
+ };
+
+ this.#device_.onclose = () => {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ this.#stringTemp_ = '';
+ this.onClose(1);
+ }
+ }
+
+ async open(baud) {
+ return new Promise((resolve, reject) => {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPortName = this.getPortName();
+ if (!portsName.includes(currentPortName)) {
+ reject('no device available');
+ return;
+ }
+ if (this.isOpened()) {
+ resolve();
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+ this.#device_ = WebHID.getPort(currentPortName);
+ this.#device_.open()
+ .then(() => {
+ super.open(baud);
+ super.setBaudRate(baud);
+ this.onOpen();
+ this.#addEventsListener_();
+ resolve();
+ })
+ .catch(reject);
+ });
+ }
+
+ async close() {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ await this.#device_.close();
+ this.#stringTemp_ = '';
+ this.#device_.oninputreport = null;
+ this.#device_.onclose = null;
+ this.onClose(1);
+ }
+
+ async setBaudRate(baud) {
+ return Promise.resolve();
+ }
+
+ async sendString(str) {
+ const buffer = this.encode(str);
+ return this.sendBuffer(buffer);
+ }
+
+ async sendBuffer(buffer) {
+ if (buffer.constructor.name !== 'Uint8Array') {
+ buffer = new Uint8Array(buffer);
+ }
+ const len = Math.ceil(buffer.length / this.#dataLength_);
+ for (let i = 0; i < len; i++) {
+ const start = i * this.#dataLength_;
+ const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
+ const writeBuffer = buffer.slice(start, end);
+ let temp = new Uint8Array(end - start + 1);
+ temp[0] = writeBuffer.length;
+ temp.set(writeBuffer, 1);
+ await this.#device_.sendReport(0, temp);
+ await this.sleep(10);
+ }
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ return Promise.resolve();
+ }
+
+ async setDTR(dtr) {
+ return this.setDTRAndRTS(dtr, this.getRTS());
+ }
+
+ async setRTS(rts) {
+ return this.setDTRAndRTS(this.getDTR(), rts);
+ }
+
+ getVID() {
+ return this.#device_.vendorId;
+ }
+
+ getPID() {
+ return this.#device_.productId;
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ if (['\r', '\n'].includes(char)) {
+ super.onString(this.#stringTemp_);
+ this.#stringTemp_ = '';
+ } else {
+ this.#stringTemp_ += char;
+ }
+ }
+ }
+}
+
+Web.HID = WebHID;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/kflash.js b/mixly/common/modules/mixly-modules/web/kflash.js
new file mode 100644
index 00000000..40488af9
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/kflash.js
@@ -0,0 +1,531 @@
+goog.loadJs('web', () => {
+
+goog.require('crc');
+goog.require('pako');
+goog.require('struct');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Events');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.KFlash');
+
+const {
+ Debug,
+ Events,
+ Web
+} = Mixly;
+
+const ISP_PROG = '789cbcbc7d5c1357d6007c6792c924080a0e1890d82201a2acebc38a4aab960d428862eb636b85ea56171d10b5dacafa416dcb4a48263122453a60c0604bb182b25bd71535ada8011569edf787623fb4688080a2420522cac77befcc04d0769f7dff787faffec2ccbdf7dc73cf3df79c73cfb91f33f1e0dd331f5ec300061efdb7695144c4a64541f0a746bfe9161c00363b7bcca6dd2f44e8d4588c2e069bab9b8bc5ea62b1385d1ca6d169b0785d3ca6d569b179ba79d87cdd7c2c4197802dd02dc09ed53d8b3da77b0e5ba85b88fdafee7f8b576d2ac52336794780c500747c077f8b31f884bfc5387cc2df62117cc2df62317cc2df62023ee16fb1043ee16f31099ff0b7580a9ff0b758069ff0b7d8033ee16ff128f884bfc59ef0097f8bbde013fe168f864ff85b3c063ee1cfe283015617532cff9b0c50f85117f537bc6f53a8ba93c2fc40343636ffb5a08db12b7dc635de0b0e9a4eff8ce346bc6d2c5d84e32b7dc6375a7058371bbfbd49a9ee1c2ddafb0e4e4fc4f4477140efc041f6aa89d86b20287013088a40ef58baa4c9a37574fbd88e715de3ef3fd157d658de54d97ab8fd68c789ae93f7cff4cdbd1edf9cd0b6f0f60b9d89ddcb7a57f4dfb8ded27cb3edceed5f3b7bba1ff40ef4ab82306f55888fb76ad2446fd5943f79ab8262c6aa429e1fab9ab472ac6a4afa585590ce571592efab9af4beaf6acabf7d5541b5e35421df8c534dba3e4e35a5731cac1f00eb07c0fa01b07e00ac1f08eb07c2fa81b07e20ac3f01d69f00eb4f80f527c0fa4fc2fa4fc2fa4fc2fa4f6e0a0d9a7e2fd47bba87d117a79710e06ed0918501a57723364edae4ed37bd2c79eeca1b2bf1bf95fd6deea61b9b2429e529f1a92da992ade55be3335a323cd654ae49587b73adc71b956f24bc79f3cdd1eb0faf5fb8e1ce86d17f3ffcf785dbef6c2761df4c413a6f7222e6639aa8f32183b1b1a660dd5852895126a58e2243305f5388ce970cc5fc4ca13a3f320c1b670ad38d235598dca4d2c9c94998bf6992ce9f9c8c059826eb02c8706cbc295c379efc031668fa832e909c82294c53740af28fd804d31f7513c8a9d813a6a9ba27c8ffc19e34fd8fee490ac382d038010047a9d1a3696cebb8f6f11d4f7405df0febab6c3cdc74b4f544fbc98e335d67ef5fe84bb8beb0f985b6c4dbcb3a5774afea5ddd7ff3fa9de65fdb7a6e3fe81ce8be171c01f6f4de0bf69ebea71f2f93947b548e3e3cf6e8b813e34f3e71a6acacbcbcb2f2f0e1a3474f9c3879f2cc99b9fbe32b12feb1f05f2f5425da9655afb0dfd8df5271f31f77fef56b558fed41f5809d97adb99fbb650bc995f24830b02c02c099551a22945f74978bfe8d037ce5977fdbb47b1ab803ff8e9d6e0b06c09a8def409a969d1cbc72eeaa1babf04d659be66ebeb159925a9e1abfba65b524a33c23fef596d73dd656ae4d5877739dc79b956f26bc75f3add11b0e6f58f8ea9d57476f3fbc7d6cd0d1a01726fe3a71ecb4a3d35e88fc35725cc88990c4d09ed071334fcc4c8cea891a3fe9e4a465931f4c1e3febe4ac65b31fcc7e62ca99292bfe38f0c727a2cf44aff8f3c09fdddc8df191346d52a93b192dad7001426351a39e349aee65c4456c028bbcbbed166f94937ca42c19d3c460d95972020314c1b45848d85713d9429b2b805c04b22851ec715a2402b8de0da369b2c027cbd439ccb9b98c595b55d78deb3ee561f547ddb0088fbede749db98b68b100d45ad0030b09b25893ea88f933a1bd5fb812f82b4b66b472584789bbae217a1006049bf661eeb9181fbec5d026be56e8cf0246ae5e0c06db066940df2397624034efa51f27686743547c4fd27eb2c05cfd276b7ef29a5742cff6f7c722d3480c53159d0b34f50c5e3947688bea2b0be909c771f8f6d9e78b75a1812602d05d2740a04902749a4006a6e8669cf523715d7ef1bb53f7740e280f9240f90109304d76164188b4dd831f7d9769171d242591c409c0104a4060d76d3c35af7fc353c3cc13e8f996cf5ff32da4a6483a448df2033906e918775cbc6b314502bc9ab0a9a79a1ea363fd301d8476ea9ea2cf2802e0cccdfcf8a224bf1cc7130dbf4bd9161ba24aa421206507bf4bb5ef5a8c30ce961e57d39baf8040528ab0ef80d8d7b4021efbd43dc5ef6eb0454a61ad7952d49f5292ecb5633ec5da4900ce39f18c46d2ae1477027e541b3b6f40192256f263ce9ab4e74be840d2cd5bdf9863dfbd087f459f7dbf5817865aa6375f86adc23ec5073230f56a3360c7c156df0978f74461fa233de038ca5c028c0471f453fbb6f4b0f6fea44ac3164d7087a44be9db09e44d0050e20f8ef2f29d75a5b969825bd6bf7f46780b6ad069428577f5d57f6b266a2c119c5ea7d5a17ceebd7175be66b2f01eb4ba50f347e13d39b558f327377cca3ecd0c373cad48e6a475b6eb582d27b7fb25aea3eff3124cb88e5de2dfa4aea3823e5595d032c8bd2b9f29cb49dcdd4728e148223c39d859aec3ba783ea7622e166fd670b991aec33c97b37e2adb886fbe9182693490d39385fe645d982fbcd57ca24394f27dff52b19eab3dc75589dadfbf833c7888a7887455fea02db4613ec1ad85da704304f060187e3c817b3cc1e77aad0ea03155822e3037058d2ce643119d40d22ed0d227d2744208c28ccab6a59bb54acf4e604d22b0fdf0c926318075b9c60413cb73255dc11d539ba6352a27bcefc6fe73596af6ea49d2a9c67c8d203f5f107142d9032cd4318a19403de5fb89f9b064563f92350ebbf831eced4a3f37deaccbbc6cbab1667de2964a840348464a6ccd776e3916d2756ed8e5e9658d0bbb5ee8486c5fd6baa2c9d814d69770bfd23c75e73403eb01bca95100b78c82e3999353206b2f4f96a4dcd83c775bd936fc8d1bebe6be52f60aa6590971b0aede31b34d0fd4274df4a6265066b21a4830a124cc4c6fb6024a068da36d14f89659c8044a6420dbc2ca65b88ead3684c454cb9ec6a2f75eef5b6e9b90fec27d6aaf044fec5ad6b1aa955a7613937f0ee5dcf39fff237b257f5d59ebc5c6857d2bda8dede54de186a9e6a3e61339abf6068e0a0cb6eedc39917e4f065a92e3a15720597b63dbdc37cadec0dfbcf1cadcf5982614d1062d8bd52083f62d0a63c5002fd4d27732447a9507b861c936e56a794e8b054e1b00253770dcfe214fe9d709ca8d0970d426b9397e88b58821eda125d1c5d5e662f552db87ffa57e7f9252c26329d46cb0b3e41151f479413f740534924f91d69965d6565b9c40de0380287e4f26b29ef5e0873c3e2df93b21a4832fe854256795958963c28c1719374dea8f8edb20de51d1e7adb0e5e80b1617d227d59b103b7c1369c93747623f91f128f6b0d7ddd8116e96f40e5656f68cd6208c124829872b6deb6ea8c9e85d5fafda6ae986cfba199bf6c557c306aaf79c0097f22c1097fec2ea4db3e50d029d8ab3caf0c431efa31e8f7e943253fa30366d3a8f6dc9abbfc5667c95c7c6e35a3de64f36269e9ff5f65f7d3a3da1234a2605d083ea8a1487629506229e85da2ad32c6c97f4ed47f3f0d30620580909f408bcbbc5487f59e253acbc90e75bf2ff5a4938037c46e28a67034d72902dd7b1fe9f7f6951665500ff676bd36a49badb092047462bbebeb171eee6b2cdf8b61b6b31cd44a4ff490e8b73c037c5468a413e4999e240b985dedb027c5b154d3638bb9c2950fe530cda6c8a56ffa63a3bd2466f2f497b702bb2dfe106c106cc310fc9558d3a9811de52ad0401dc29f59f052f62d68d64de2a553230fe899f182fd3c863112f684c5fef0744f50428237fc8b39a9cbfb27b4851ed3b7e5a7a6dc313706001453afb584b1838692989dd961edc11d69ed02ae95ad874d272d430d5106ece3689ea0ae1583080d0220ba3af3f00ce92e8791cdc203f85fca737bb406ed29252914a0664398483ea768d11f4610f169a9f34bf590f4b58a70a28837a812ee9c3838a18734e6f030f536a13f8fd0f2c34b3f70b1b2afbe2277dfd1238c61a48791da4fc529efe422aa08c0c382b41ef5bc00dc9d5b7f5f597802e0cf54b54df0ce672cf6e60633ebacd32c4b455e4f2bc89aafea480bb6cbd6b907ebb073727edcfea02fc9c31776dd95a644d451a09389437511378450e989c6ab2437d3bafaa9ec43267e8545bba09496654ad4daf2d00a2f906484795538589e67f00e6caaad27a61de31b04a16b9b6176093a1cd12d5b2c55fe19bcb36f2d8e57590b7c48156cd4a5e2a453762b6446e39e4b6f91f30f38571b6220b3f6350affd1eeadb79288362ac8cfc264f14df041298b912f4d605564996bf8d8509a35eb1e5b408f65d0f79130c7ba987bd3fcb3dbbe188208df0d1d20627609230fb0e772bd7561ecfce8af16109b5d847037d457efe6e2d4b668945623fe8efca23398fd1b92d1dcd50480a04d9eae761d577047fe113997b7ebddf99ca4b1be477903b57fd23df73e247615c9f8e9248012531e23a8dbc1b9534cf164a9e146cd16cbe46f315b94b8ae6f75982accfd069dc584b7f853601ea3d6b943c2597406849d859f350a9fa571e834690a9a069f22ed8a6386e062b0eea97f7c2329979bae71aa659b1e8f62f8a45fd3f21dd42165929ce0716d4ea07e474812aafeb763dd49704260220dbbd5f8cb9b5f0334e2311f471324290708fe7a0feee27df07c8be1050efa047c0d1947c1e7a0350c2a03feac94b80a31ff9fdd093f5a4bd9b718a38e225b4d84711355ec35e3bfd90228f7839fc9b0784f20714f1b5579d8dd74e4957a5018bb342cb423b6d82342349ae321980a88e04539b08b1405b6f59ea75489da43dbc696aa390e7ca4e0d4e15f03e44140ad2d983e82a89a1039b81cec9684b6a271e549612a037299a817eb396363981399e7ebb01ce58fd362b0c3a108dd60632c6e1dd3c60d6d6424b5ee3458feb0645b57046f56cb3b192afbdfa931c3e97fa50ce11af54fb33b1e1263afd1cce66b840ade3f47576ab64f0999d27f7783a3c9d31a46343c380cf0dd80fc939f54e095aebd9b3272dab1cfa6736e849da8012fef613f0097ffbc5f0097ffb45f0097ffb71f884bffd187cc2df7e009ff00767f7eeee31ec1e065cca65f71018f717bf94bbd4c6ee5101be0d63b6332becc19eac5af984bce83d8ef473f7f7d996a697b7571aa63265d0533376093235da52cfcd4f47cf3432da334d0affb2d4e876b336faeb53f3c2fb0862eafdf0aeac35f9ce30c13667f93f2b1d8a3263f52af128264777570f3d01d64506153b2942ec21c8b6875e2ec6288b4a443f4702591af40cfcbb00aab12d2353de9ba79cd4e511c64448cbdc734040ff8b3ed70fc5fd5f38c1981734cf4a85f739e51b259bc3989694541491f03af76b25b38316a2c4ce4a13438aa095dc3018c954a833fd90a5086e94405f4ae97f1c2803ea80328200cae9f03789f050ce81cf2002578610f8c4789d06c6ac1e8265ba619173de7a97a28f7f4b73cd6914defae634f16fa641707f4e2ba2154fe1a49e9ba9627b2c7e0045b4be3c55b1cff0d682e914caef0be90e3eed007c9ae870d717d27785f201771f84f1e89f334ff799d022af67fd7c6ce1bc3fa75d9a023dbd90ae1758d2b058f954d70bc86b5646885f10eade95ae21e20f08be71e91de97a59fca76e4ff936a39df349aa4db14ee7d46991b6e81b082c20bdecfec58e6f5b8d7de55d5fb65f6efab17145dfaafbabbbd675bcdafe7eceb49d6866bd545fb2a1ae1e8d9efe690f40b954801e4b7ac3114da0b3487f4ca35b802da0c1a849b2dbcb8c87342b18617d6207ed45841144b5c79698c45df4a6bf60cb76499315eb7d17d15d15617171c74be8af0daa6b4c9851807f47915cc62430e51645a3901346fb13ded53bf7aa090d5dec0150295f521392d9a68ff2c0caad91a3acea2a8345ad7c6a2f38de77a8741806b4f627fde9d21f0fea4fca80fe9819d4cafaaf941be94d56f0a3714eb2e7ebbab0446302936da113a15c435c957bcdbb9054299faa04db7a75da968df19bcb374bb6b5ac8d5f57be4ef2ca8d37e7be55f6169e796343342383f219350a64e9127471d4825135cb6da815d169d44a5c5cb9c921f31a10417e4993d9ad64109d2df1a637ec93c2f7799f5e142ff25c5f2d2ec1bd72ad5e041cd509e0a6a5fa7c8188eeee024468b5f800466590eae5e7ca514ff839f809bfe67f6b914fc26990931423bdeb4ffad88e686017dc1ac49ea3163c1cf47ad6fcec7c9be7fa578de1c2aa4d4d3aa2a22c8dca70d9a83452aaf46f028a76f1d7772cd58663318ac6b8f3374d370df4a67e70d3244d8e365e66a2859a59db5e8a2b4b9bf0de4be7fd934fc211e1b8bd37d26bdf10b7757d9da7c38c6e1a83322b4dcb3b3dd7bbb99f1ccc6bbbcb856835db14ebe9e60a00c7fecdab38bd691fb8690883ad0954feede5b8d9268b5a91fcd27934facf3f1c1ec520c9d26f26db6e9ae87bffc07e3455d737e04fc1b14323576d6cc0714ba5c1115bdad37c5c5f0df95f6d0698a65656586df50804e955f4829f011ae915dc3843c805a53f669e5e65bce6f64a5f7939ee86a1da648a79f9bc22f9066ab91a4591617dc35164c2fd855de13b0fef64c5c40b581cb1a0ff2fe6f660e6b7f1238a1e451f430d396400fd4fd3dfcb8021f907bb3e4986d16f3e047af9648c36656017991798ecbdf3776121709e1f75dd3e21ddd85ed65ade84a2c217eea3987145fbaad6d54dac690b9cb71a71fa0d17ce76918336b49e86bcc32e0bfe6fada216d3fc5e9c58b69e8b13616ce8b873ac2fdc2c8ce44a96dcf8a4a50d5a10f3a7bfd28d1631db301de85428560894cbc16872a105a4b11272901ef73d00af9955f468935891e2bfde774da7b5cc5274c7f756511a95260745ce95a55443018c253a44d867f95abad929651b5280d564123d868d20073335b4ec18f04f49b7464a2c6afa853a8ffe2846253a26c32dcdc87e12b768fc98b77f4a7fbc00f1f30529ebf4058aa62a490180811e501e6db8c93d4f36b4b269e3c0ae3bbb6e96b4045bbc1d6ea9ab79115a67ecd2e96f102dc04ada8803707e5713b413463c0de721654ec93e1803aafdf89c2e989341e6db59897a094b36928ee6962eb62104b39aeac967ecbeebe97912a0585370b7e46615d90426db661a66d82d2d9082d3271ab9e7f1138d0826d8a248639d72a070ee83b83a9e147d6406f48d96715e7a4c1598a852132a261e71a36acf058e23552d124c8eea3f7df9475f9a82bcf66ef76d058eac3b810d103a36927200910de2589526a9da9a068a5b3aad12117acb57355fcb5c38ccdd18f942cbac1dd18c68fa0e8c0e100710a1ecd6192046dafc80ed8902f43e8902e161b5f7067bf3e4887711aeef518e4444a7c0992440a4809a82898ee500f68df383b4732f8834ed55f76be92f9ce1fc8c4b9a503d8a24dfcca27de9fea4c85d7fc1c20a66eda4f7f5f85362315e3664979233fb93c273e831dd41f46606a3adb3275afaa480ddd5be5354e10926bec779a0a5ce9df4b8f57256feac887e871ca7b7c9b0c4022ac51764398576b6eb21b43f4d6f75018ee23fbbdee24b9c993077a34bc1e53ee57a6b4925956801f9f1f4f50b6236310d588d4651a0711c081c370e8c962cdc43119241ef55b47f9d84d05a2f87a9abc635008ef3a8fe9c86b3d276ef56d006eea41fff269e6ebe00ac9213442df4ff90bc5c006c623dc4d822c944397e7c8e0be66c25d3ed1401e54502e5656ddd7d365185598d17c8a536ef75b4a40ef8aeeedceb78a56140faaac6e69be2bdbe736fd58e3d6afa2f4f88786956ac89240b40d99edd7715bc34db9ca7b8a7dd799275fa03cfbb9eb7144ee59171d81fabe8cfa410b2643592ad9216c51a54c7a77a9a21e0748cdcf795fe28c7db2d8d5ea7f355bc6c345f6baeaa345db5136999e4ac82fe8ccc5d85c7f5333de0cc40efe9f1e0da389ef657eaad8740a7a5259ea0565b056d5db495488ddc5901961564fa55657c00223d1ea823dfa804df0e8d6be35f2186806e18bdd38427f4543c07830bb104344a91446a4cb5498b85ef953be1d806bbd653ebc6f3ba7a24ed5fcae90b308771b623534bbf9a2462b4cae9d0f2c1f12e49ebb44642fb4e375b0094b44f9ce221fdf59f70a68acc89f9f83d0e5fb86bb5d9aeb7e500cab07320a688c9a9cd531e7b080ec45799724081b3e09623c3f433cb8cc286e6f8535baaa145c104bb01f9cc69e9c99e0f746790471dbe87f6ade3d6a3f55a0238726d0304d16fabaa3f00f6e54492753082d8a7a5efd4834a13bdb1d9437f81000cc910fbf31a061c379d03d0966edd02cbf22f8e2c617b3240bf8ddd6a1aa48c923ebaa35ea2af2280fe2803f4760213d9194c7f86c04567185c544588f41f31229661c4ca231940f41133a83c9604941fb930e52927a63cd220521eab17293f4ac395a7e422e8718a95a7c8c12db6dd2bdf27df2794a7ea071cb7202d4dce9eec2cfade017195b10ed0db2fe1aca66750a7ed97d36c378c1d224d3d40348f849a5c0fc22d4a4062576d91a66ea08f87796437a45c091a8043de037ba4d336dbe5cd9cff399f8fa4639f13d2f3f8b463c984f4b25663fb54c3a3f3d25126a03d3ea525f9d159275488cbceb1953ba735b9e3c0b254fd4cc320b4b0386bde82b1a3c48354ef8541fa419408cd64d443d9203f9ba589a05f53fa006009683e7b81791c378c4a47c1d9ecd5ea3e3a69ed0cde2a988cdeaf5499fe8015a5d1d21e406d710e46174a5309f3c4dbe9d62a591ba0bd7bc414310aa72f1c12ff68ad32af8de1e5338361db0280f2d86ce8c51a061399e09d6c6f00a86a5b8b29e74c1f14e4f1ee013b7d712d56f45ac92b457715b7ae196694a17d7a66279d2f09d64f170f5619ac6ad14903a0deb83898b0973692fe55322d462b7a3c5812b6f965efb8a23525eb29897990da29c614778b6e516496984ead97b2928df8b726baff02502497b42a524adae1bc2ca21df5e218316b12e3fec9450eff9422a7ff9aa25b55a6140c512e6f00689de1759ef228ac2485c9a96a4bc394c7a66389eef8ade99be38a35251d8af5255d34d902908d86f8441abb62237c1795dc1745c1b13089c5457de9f632e88108a3f4d35a3b7d61ad17ea2b472f23c6509f11fd886e16d1dd58ef41911b713ab31e40fa5a217ded14a2fa354435ac812b9215d715298a66c51a455b514a95716d4c663c9153659a8ed11b2f8faf5a7b0b205aab98148c6d19070a9a8bd26a894486f726049e6f77f7a4b101f1ec6a35e44087fffa22d89b1eae37b01dd14ad41b462ce67ba4873d2ae97bdee68e344bbf7cde46abd7c94ad617ad71f765a80f77eac5bfdb870df5e0b77df8d6fc47fbefb5dff93bed5f1f6effc2751bec45c7e020f458e9f09d0e9c68413014490cf65aaf59e82227d03fcd0cb2bd9f0e5e2cde508568e4686ba99770b46daf17fb27fb5ff74ff16fe668a3eb718a84fe334960be7481c337adc0e9ff4ad1ad32c6cdabe4f5d871ff35fe6dfeebfd6f73749270d44902ff14d1498a458a4edfcd302df2edaeb5559a824d1bec6e5a1bab37d82f9a275455b5b57163e3f6ed3e3c8de2d070c354c6922403ca7f925f4b9a2c47382b5bc8cf07281ffa075f97c4e69f53d49425e32968b717d59a6ad0178662109eb3caa57be8f90478794da5e5e55637148a0632f95502180fdcb6a1d8d3bd8abbb049d2c5ba48ef4ac354c334f309b3f93384cd1c8fc50b12fed6a3b5f57e21185abffcd298602c5b3b776d1883569684f521e2117af8faaff727a138880eec06fd19fd021637658cb08e31afb5df866a096b6063dddc38cacc34721cd94fd6b8391294339223ce338f7204f341168735a5b58435551a76d0c1ad12684d03eae6cc373b7919516322391c5d3929aa65d91e13f8f80edb4362e63b07b42535304e13a3382d73095a2b2fe356cbf37398cfa6acd77dc19271240d2dbb7bed063cab9c231623ee2843c48075d9fa514f14eb61bcc6458a26c0a78bb9b5178a044190cf93e81d4e184bbb71346ee4ca5c7240d42b1629c3c400ad2e2bbe57acc734a707b9151f17299df1933bee3c6033e760da7c87ffa297ce77da1f919932f230e45029c7f3e5233954ffcf473934bc767bbc168dfa48de64dd5d8f4e3df07bb5e3ffc3da11bf2a7c1d51b7cfc1c33c52d2364db31e08efe3b0f9cf950ef7b7d463ce9a707371bde7eb6128fa5f4c80cca5027629cc5def8653cffc3d8e374e471c9ff296ee53774e4d24cc91f0b4d7fc826851fac3b1108b3d981c6e958da322abd1fd56f32d0f0bae0dd398ec89d6471318b4228fd648f5165e3b280b298a61d15a2ad212e87300c42dfa1d893791063de7ac1ff28a34caa26e403fd7083e5a4a2734425fe7d17d83a752321b4487203d7ba244ca5231a07f944817ad41fdcdcf29ae57c44d787bcefae273735e82cf14cfd775f18827fd49fa44a82d13ba41897a8b964e7549a3487510414626b90091f3c7affa3358936bbbfffaee3cab85c4d0daaa7f23920c248394254a44bf44221fd417d5cf77cc6b9db25e591a829dfee6715e0605215e7e6cf37c1d8b6760cb386c39dce818dbdd53a2fef817d832e3044bb57481538cea4fb0b14e092781994bfe64d77fcc608e9db20ed1c704f0ca511e9981cd963d1da3d3fa384a9e7ff9bc6379d05d14f95f342e3472a3ac2d1d5a3bc9b45716cf961d5277daddebd409ad8fcba0ba1e491636fff8b961590cfaf5ff92c5c68f86252cab0941869bff136c8d7d78e46bda1f1df9619b38d520e98231fc0f286e9fef12e42b05f9d3449a218d255dfd910693bab328ace04f5999bed3c4f45fd13e8c403ffdb81ce83443ab49650847b9c531bebbaf8ac852bbf3b34ae1984de8c6872193df1341c8fef82ab259edf8aee701f2ad59a3b1b860151ffbfae55a5aa0d7feaff2ca95368453b78b6df20506a705faf2d097ad2c894d6066431a515af94fd34141bf97129a421b6d249e402719c29ac20d870d9276a17f89fae3d057318a07e597904f5ae7645dcd8391c61ee0bf5af4148131e6742b3de113f1498bb94d64130f16bfbd7bdd494bd1abfe77bc6f5190a6a216ce639ad5d0c4fb8ada3c1841b63b81608f8b77d1ee1dc9c6b660666eca8d64645fe5f70607e1ccb1c8bf059b57785d7f5c3c68b902e177b436e6be7dd8b26b1dbdd34fa6ffc8087d763f4cf9d147a08a6801bb52197361f3498bff3a7a825462b98456349aafd2729b447e195230e3f235eef9cce59f8bee14dd24e6f1d4214f5de17050d2fb14d9f1249de212530d51805f3360b455967ae09dfa4d1e57737acf152b2907568b1cbc4d565a8a68ca480eee86b8a0377037df46ccfbd0eefdaaf75dff9bcb6dd4650bf0d1d22ba19fd3900602499328502217a2e2f03d2c1f1517d549186d60924a1d0999a1485d9e67419cfab7f3db5d7776df2c68f3bd5377fa79a8ebf5b03e8a8a29b48a92560fa80694e3845131855651b81c17ccc98051310ba3620aada2b4d5dda71a54582059cf47c5304af15f9d6e75dc720e78c2a898f71186e6522d9c293e2413dc3345a9549829b468a650253c3a53cc480f6b3ecccde6c16d92dbd30cf4cf30d66a80f35da904208bcddc6549a09e7030423ad31d7d8c2d5bedf85fa2ef57ed3369446a6f9e2cad388fb5c178fa2c825434c248276b72297c172b1a31cdcde48494ca14af95fc6a38d23e4917d23f73ce5483f92b96dc4c2a1a15ed8c56f1b55b9f9036bd0822a02516e6899eb2d47073985bf3ce61b7853933689babd3caed26439bb5dc6e710e422fbce2ad53edc826481ba5edd01a49394f244df1f53b5ae61cf26b866c880cfb14c1210c720e034aa13d5ca57f085668ff0d473f2067ba395ad33392a3e48cdffa6b7c4daede41729abb5e63ebc87aa63ffd7e3d61fca6ba6ba96f3e327e531fad85fe8f262c0da8efce29488e337f41d608d34cd3e37ab4e737bf0d49226c6f991c6280f15df8c4d27c5279981c70dc760e4c6db20471b44d117ccb5eb9065986504638219625ec4d9904b8361eaed14b483bcb52e59707072949cb3a4c63698096a89a5c35f414f61a050b9f294dcbb65cb287bbfd58198fa3d4e1de89acb9278735a15df9ab82ee7c884e63629a6f6c01e9173bbe6c0f371f36cb0f70fbc37d96039c3d7860ec2bbb5fdef56debe5a6a3869982bc80f485c68b0c0fa97928e07dcd7288b3f3a388f9964af8b6b3f221e5e18d8b3e32702b2805c9a213329060b212bd3072954d76c700ac5986d78a1d13e6f7cb0fa1bde3f92a268eaf1fced5f74d163d2543f12e58dc3df36bab490ce6f645df9fd92578462b443365b0e4e9afad4cd7e0b4f6a9ade11d7c0958beeaf5b3197846d9d655abcfa686377eb92391318708f4a75232191e2376bc13f79022762f1678a5a7e43380a582df4b3b6911559100d797688a36e43b645fe85ba0edd6462676e3f4eb97c18179ba5b251b26e41575a2dde006bcd956054b441026708704446ee9c6ab1a60fc230122fa672926fa84009464a3887a7d1ea07dd10efd468220fffdeefe310d03a9f6ff7f5af834aff8ab82f98c36a0b4dad483cf86b81c6f5d1e3830cfec74b732fba56e7cf7fcd910f35963f5ff4bdc9fda96a72f6b15cd96e02f74bc3c57146e84767514286b9ced71533ddbe0548bb417003ac5b6b02bb1ddd8b4a20949d851837e9611f33997395d5f2f06e69cfcfa9279315a3adf29d9369d7e518dcd16c7c594245b4789c1819c03e7f475e741716d02935dcc43ef3bc741bfe714b30410b9f359228b80dea0033e4994476bd404ca0ff4100326e7d0ad44eb81d2895a47bef341e67464f5018ea0fa673b166c1c0c24bb06cb4cf46231d069678b5dea6d3608b9dbd987f02fb1bb5bc99cb5c536fcee58a0fe4dbd743bd7b2711dc678e8eb1e0cdeee2d499ea8c5420ee4d0d94efcf7f75bdc94644e772cc81ae8b44f65e44a6e9fb8b56025a60d8df59b7768dee4855692e867075c63742a7afb779895ecee45a7dae8e4fbb8952c7c403372ace0baff593882e41d9c9188ea7a069f79afd306e11e2238c7aafb0310aeefba1dcd4409d7bfecfbf6fee52ec9ede0b61f3ba0d7689ed6c4ca309cf2f81a673d80a82c8d32c9c1ac267d1d098adbf8b9c5636dcbb6f837cadf90bce9c59de25b84d366e403cf6cc48bb357cf349419db6c7aad4e845f53e298b8d6aaf4ecc4fb93ced47cd99add4e492270374e7466625bb3084252300ec02e6c8390d7718a2cc569aa793465527130709e9b4b198c20fa1afd130910348ae6a834180942e8e86b684d0e5aeb47b072387c9a3d791cfe6a8485250d805e4c82cca49335d1068996d6920051d9cbd1c792a564b811e2f1284b638d723093c3c36859c96e093dea9227ed28f766255904ed75692c7dabdcc3318ab8837a0e7dce0e965884435fbf78a28d1ea5c168b044e41857f72bc28de6cac9a5fc1ba2b713f7b1f12939f8c2ce8d231c9150ed4c836383ebd78dd229eed38cd77fd54eb00d97d3afb8c07069e92fbf6acfd43c63cfaef9a39d95d448745ac7844b5d65ab59d338100de99640ba1dbe755dac298c4b8f03666d28848c90d0e24b6359895ae278f252d3a1f813e7e9174940684fd6c8b4d428c89d12a7549a4cbf44405e7d8d4712a56a25de299ee22f58f423a907210e92f6b8349abe510e39d621a1b16e4fe877900e7977a3bbbfa8978c5606611791347109875c83e597aed5c2f6b0507d3c1c6d827940cdf003b7f77a104b72959eefe301a5bf43cd3e276c5b9a3cdf46206b80ff67cad41f3e7d50aa7697ce7eac34ebdf11fe17872299e413c441423bc51f6207d2e45adbfbda335cbb0ce4286a15b6a8be6ed36bc4509e511a2f5e6620aa15c9e8fd4ceb33ef598d24a0250d8021f5175c83ba1624d71a5bcb6a4672a2fd926d42fa99a639dee83ce1d4ae69f767f69d6c3cdb7aa13dbae95e98ba93f709ea0f6626461ba699fea7c2e989a9c6808e315b1e5e89f573305acecf9fe42ae7bcd8f2867254c6bdff93fc0003b9a5255abaa807102a2eef03e70775b621887f906518e84f24e25f56eb54dd83e8aec509d3bf545d9e3aae85dbae97d5dfdc421ebdf2605d29f72c6f2e45255c9b53c87731d07b8fa74f7b29dc58b67a076d21d07c9ffa9130f6a7510fa0e68cc0dafd5011475b7bc42fabad46d720e7e14fbebc974ef81a70341d70eee59eff228b86e8ac208b3080d680f8f3bf9e7174800d302a8e8a3fb8f6e8b4c76dfd490786fbf52fb20003acf46b82bf61b609d2b2c48ef98435210e571a368580235e1a7eb6562fc1fdb0fc48a210b769c7201f31247249377e4f058ee8f21cb72b06f89527e10cb686ff899e324a85f99dc67ca88cfb634e5af4f524086e9d668051bf30f757a49fb48435e93f22c161c39cbf3ff5c6f4ad53d64f5a13f137497b76d1a456849762e200bfaa854e99859b10afee858223f272745aae7c31ef191d582cac115cc34a79b8c3a6b3460e8e2b0f4d9cb683af615ce43e658f95224f6caab99291172298d414defffab21d7960d35a4f1866093e58cd95b9ab2f0a50042dd46fe07d30990cfa607b910fb58efeef3e58d0f78ffb60b250e88371f56faefaef3e58f285ffe483d59cffcf3e58cde7c33ed8a3e754641c5f679a8e1aa71a2b052f33b5cdbd129a7c87f3d8f8feb6fe06ee861baef1d6301c68791cae79082eb96d043ec76ff0fd3c84af6504beebbfc1f7d310bea611f8aefd06df8f43f8ae8fc0f7b3fe1a01d05a82a49d1d8806b9daa9067af78078570ca10dbcb205047ed70cac57ba81b54183b183dda4f5521dc83553afdfc702c788007bff3e16fdf66cb90dd03b7e11edd3300b69c68ecf345532478d7cabccb560a340d18fc3ad967eaf5b18b8e28c9a968c46a7e4ff8cf6b3de961e2dd21f9702a87ff30e5baa886ea0af2340e0133300415415d66155ad1f01742ada71ae79808abd37486d7912509b28c0fa3d05bcdae8bda371733c3dba01ed7083069c89a7c735006bd10ecc7a6547ccd1c28083d622297c97c65cb2eb26d5d91da6d103270a9547fcb061edb9f0e4d6a6f18d6502e5a99f09dc3a441171c07df21ac6b902b77273bc10aff63811af688c0a841ce239b50550033ca7223739c03ec82d17d69f4368ab9c6900f18a252270c439e853c9cee02cb111276269e2cc237c23be73f30dd40ff32da8168b652fd9d4e6bac7b946412e1cb6d032917866a1fe2902dd31da331354310d20f2f227e81e51561d08344a8043d33230c25e780ff7b7f9bcd0dfbd23fb3badf0397be60c382b2657e0313e320d3a6b37cd20442dbec18c3b52a588ac07d959136b2df57f45762c86ee41f092a6e1b65a64423b12415a0bca929f5f82eaa213974a5bc59aec2cf90c0fb46e15939d155a67b171e729628ba0bdfc771a6f87a32da210d3a05e553f5869c07db3e5d86e2fcdf25d42ccfad0e67a805b4da4b43aa91b8fd28c415c0cb9918c4ef4572575e17353221b2af02a57055ed488694a9a782b8a6c33531be33395a12459121d3a33fd35b7befc625932ead370df8623b4fa127784c668e9db158095878228e84d47172a62b8b5bd5a84357309c4aca5ef56000fc2068161f41e0be7849c80faa25842b3cd5a748e87599ebe4563143413bc05231fb14ee3c1f054942ec0e2e5fb21d744225f4a0abca9a444353b2e0ccabc0a9480bd7bce1496ccd51f24faa82d3d98f5b209637656ef2907552de3b089a4b2f4529f075900665af461649fb5c1823139d5b00397f29441ae3e22cc315ef270a709d346117f5834317ed762769c0a54ee51969e18bd2b9650e5c3fa273c1d7ef503e5c9fced1e8aecc02b4df4ba0ae835778cc95c42afb6e1d97ef20ab452a8f2e8d59d2c2c8ab149fa40ff388471275969d9925774cecd09e8c1e1f49d0ac9f01ad1c2a6171a11a6a986fe247a8d4dcc925924369f7ebb1baf344479fc61112b03a24042c671f75b4b492c1777ec35015a5a88622e52e0988b98ec98f074ff881d996e9d7678f5165f17cda035a77eee84f33de8ad502612b74980673039d3a20c82febbc5044eee91a5c154300994212440f72544da34b59fc346004f4ae22d46bc39bc07ad9a112ac7f8fa01426bb9006556d712cdee69005124f09c69b14982201e6e654d02e6a2b8d972410a58e3eae7294990f8844517af3c02f11f83fe5f582de9d87b6100d5807ebd64a6e5c5585d58f1d9fea8e641260d4a70d6bd0a6d04e299b7986eaec0918f654d5a02c693272d3bc92815f03c096589894763bd240fdda11ce632ac71b302e07e50e23cb7e9a20b3d876491eb3b1924ae323971e4bfc8ebd15a8c495b697907e2dd05c34e0e663a65d18ae845a4c7d3e9923e74b326a163613baa7b6fb23a02f18d2a6800330ba2c820106c898b154d26812e84da23c6b17728bf3028410573b1c9ba30df73149925dd14a58dd894268e70b7cbb7a9551fe5da2ca265f53bd2a636e196b2541bc4175d1057732f431c61463cd871af828cd8b47b7e843228041bbe79339541376fa6c2b1ecd4d2372a46e382049221db74b3a0fe31e693966c531419b748f1975eebd55ccf9fb2b350ea652ef5ec50ca5ae807baadfd36772a14dcb6d60da56680abd64fedee94069cb67e3354b604cd58fd4b864a5351fae192a1f22d28ed7a7ea89cbbb577f7f9a1f24294fea570a8fc004adb0b6d3649dca29f976566580b8fc3feb8b6f7e65dcdfdfcc7ec2c3edf5a5807d01eee6d9b3b7d096ccba8b3bb53cd203de38ba1b26eb032e39ba132028bc9f861a8cc0fc3325287ca42b128b26f3075a874064cdf1f5c3954ae81e98ec19543e54b60ba71f0c050792a4cd70c1e182adf02d35983ef0f9533300d06df1f2a2f44e981d0a1f20328dd173adc2b946e9c68e722fdd60adc3dbe5aaf91e38ba04be211a64c39c28978b5eb0292740ecf63259f5fb8cae12f4978acc6c55e6e5414499ca434d40aa991b28152c3b28152c3b28152c3b281528fc80687f5d9860ff9917f04ebcb8f607df911ac2f3f8215a578895932542ab432542e48d850392f51cff33c4d7c4ca22eb79de6f31f93a8c447242af111894a7c44a2121f91a8c447242af131894a1c92a0a1725e82560e950b123454ce4bd081a1725e42de1f2ae725f4fda1725ec24287ca05091b2a3f8ed2f7270e950b1236c43d5e7f8b4fbbd3bcfe161f8ff161a0378056ed057fe5e3c91a61eff9c96006dd30d069dc770cf6db58b40ae4a890209f27900c1dbcaaa51b9d38da957282a87a15089567e6118e28ed15909937c1519cb3cf397407f71bf79708782942ab378a862f6cee14897d611f7ac7afda902d6fd3d2ce8ab021dbd7ccebc690e55b9c6b46d2d66cfd224ff11dd20a3e779f19c919ebcae86bb60de770fd77fcc0f1a7e485cc0ce8af92ae2791d4f5e67d9157f2ada0157f3d6dae455af28bc6ee4e233985753b602bbface5ebaf386d46f2c620791bc3d5bf166373972089d340896be6724e9b4b5620498a223b07bbf9d1f82b778fc7457640bdf94518e115282fe6119c3f5f9bc195a19228121bfc99c39329bf24d480e38b212d6778edbfd6cf8ded9cd861ec53cedd3e8e604fcdfd2df653677b8f8fc47e6aee48eca7e2511e2a432d101857e342fa6977d9c896ff79e1f9e3a8e5b7b4c32dafaf6738d8bef8dfb6dc7721fff4c896fbe247b6dcf718f6f60b1338ec73168ce8d767329e9284980c948b30a316382a2f5eade6a8893deb57bbb536bf1be2587fee8b2a3e0fb574565ecbf22331473b0c33a53eb31ac95cbe966e479e052f73a6f3c33287244c383fe44768f837252ed35cb543cf19174a7ca3ea7661d0233f38f2fccac226e82113dd1ea15f98b534e8f6e4bef0331fd3f03b4741e72bcd090cff9e7cae9259289c5b2d9dcfed18f23b54b5958c1b06e82b0dc16ef89a4ae411f2f0f17c5ecd4758e8a3fbfa36d857d121133068b3b4d4b65e60fd3e0eb05d5d2242c688035ffc003cb303514d8811ddc76d3c743d38afadf90d34b4dd62a815f3bcb498c6da5081e5e638729cf785b92b03c60b75a4fbab11b74493c598781e98e7c6815a42789ed98185225c8418b361021ff7dfe62c4b7bc51437e7558779ce234b13dcc8f3df1d858dbcf714db476846a6456d324d1d4fe52226877e9b0cc334d71bdc7708158b904f686d9083dc1c4615b9a41b60a11b6c68fc043ffb3eea7dc95f4fe7e4960ee796f6f0b9d6062db83a023aa8db9d4f62bd23f24befb9f3ebc172fb08f85fb9fcc5d68628b00fb63f7b49373e6324be0e77791aa81b89ef8e3bdf02aedb08c1a226ffe86ec504747699900b7e40b97db148ca030eba734b1bf85c4429a342e7059eb7b9cb1a2fa1b253da5e6ee40f2ea0b6748f2142ad723f80a42210ca049c95b892c0250cf842b8e15cd384f27efe2b8a57326deed51d3e0f51d46c73dfa273e791d83776f7ea0e478df6ba3bddc8a73bdde95ff834c48399edeebed7032647f715a6c9af17faf43316cae3be6e776b5ddd1074c363d0353ff0d0a7623fb6fd16daf91874e36537b4dfefe0763d060dbe77434f1e01ed7e3b5435c2727ccbc7fbdab53b62d1a9427714740f460fd9597c597dda8ed86813d1cc59b817ac0d2ad85afe5766ade5045abfed49e1f351047eea5b29c48260a2204cf1b95cc84194d2c254db2f0c1c517d1ac1ad4f0cafbda6e94f5ad0b74a4ab468e4d81e17a0f35b70bee5b4e492589dca5caf585b7c4eba21dbe29eb1f7e5f8681d7b9d038a95fd0dd2d4661bee47255dc284f5d3bfebb4b30ae9c27a10c890203bcb77e595042267cb4f0573d1e972fe54f917420dd48ab2945cc6d7ac7b4358fb583aabb0281653a173253b62b150c8174254df3d287a8a00fab07918db23c19b07309532a81514c1989de3c3872d2f76dad0f762ace21383e88e7fb839b8239011835c2ddd5d815bf6a05348615b2909186329e4d6ddff2eac77e9792b99958b95166a69ff1e1cda646b8f98af61dc424922841a9a3785fdf72cc1d6eec44aa11d2fec11b0c76fa6241b0558e675be4f4ca6701ac08495ce6c9ada08713739bd71b9b0ee1d8ab1d196008d28ce6d25c12fd9a97800b5f4076cf692e378d9ea40dfe9a06ca335a90e50f75d6302f7d4039b6b0cc87cdb62e1eaa74517eba36460b6a44e1d98180f4415f51e986a9b5559eef4e0df978f787f867bd71fa8f7a0327ac6048e8b07ba50b6590394e5362ed7671fd2745d6877319f438ed285f6162b0fda3c3ed7224842fa79bdb28cc48ace45467d0d02e52aa0c953965678c6e429cbe114adba527f45cb7db1464565b85c57ea8b1660aa746bd16757e058a27b8457606cbc5b5454cf9f86e17926a1294996b7c0df549e679a75c26dc0cd58696a287fcecab114f76387642cf4a5e842c54ae5619b94fbda8b9cc0d12a19334fc7ca3f8191b6f4e017f43e3f922d84325e84ce644d5a72b848542505fa49b14037896d9d0494c114c64ab3f0aa1d72d1d922e5479f00e5a92b00d2825799eaf140f17170928994dc07d26f8399694d82fffb2078359c67b95d9be6cf8473690ff81361eef360fbbbccf31cfbfc1e0eb7bde6b947dbf67fbced23b0ed6357008a94c153beb1d6cb3dd8ed01452cf26c3ecd53706b1c313e5e9aa155ec539dc84a04a1b9c2066b14fd6fedd62d799933e6fac5e42fcdddf15591963b990dc7023dafd41768a3c8dec14c79efdb05f5df6bb7e4c1515ac0957c869e2af82c5ab00d8e5211972afa0ccd9cc2aa1fa78dceae9316425be4e0fb77ae869f7fa70ad141d0b3c2de1023fb54b0c209829def1b5e2545a78004ddeea07c6780326871244d4367774f0b36f06e9905ad1509b607a6507d94c39f8f0e6b4a68e4f6fd843b8a208e8554ca6d9c5fd61e6d61e27d1d7caae266b4c59fae34d04f3600f931f4c528c3ade8026972409d40f572658478e84469560f5af54d60ca52f0b5fc0e5370238f47d582a80db62846505b7a54a0cff1287da309815f8e4acb0e3a724629ba9f4f423f3649d1ecffa9e2b6a213dd32cfce52a416bd52d25cd456f48aa773d72dc5ca1d9d0ca9b80eed64aaff5a457ad14d5f67411acd96832267110d6be099f222c70e1aea0bb7aa66acd069679aa01553ef5a45ef3d00b2e52b353ab6ca548147122e889d515d9738d8fa012435c23adc07c335f26fa355e1ebf16895eea4658703adca293f22015a23e46abe57dfa7d37e6147bb8398f67fa29a3d318893db938ceaf644b9fd0f61af1ed92d44238346058dcea6c9eacefe2437ec69873b862ced9a70dd7d8612b864b787ce53f60ab2f2501f9505f0dd8ad44207a355342b9f0ac574716ea82c171a9f30065fcbaf5dfbafed7fd131563508f9d6dc66ef7ff10b9b70e3bdf3d13bf18d5d6e0ca5ed01754377fc9f425f9819e921ea3bf99d0d74aaf45e88ba53a0b373e8ab34adc3d407dd1aa61edc11fcaebb6eeacd7711f5e291a74479ca6ef9afa50b9b00a2f8926da8fe4dcb79384286b89d2529fd8343bdbdb5a177f80cbf7b2736a1b1d280b82bf06be83b0ce09770834fdbd01ab06023d42d8a35b4bc1757a4f4cb154df4bc0ed86b31ec3dfc41b947b4e16bc39811f45d1f3a972e7c4923e8469b5dbe07ed5fac1e8bcee008769712bcf3a04d68b79d97f713fd4ba24d878dff2a6ff0d48571bbd60fbe8f2dbcc168910e2927bb8e71b37c45fd316ecf19bd7f481e45725582665bc0847179071a8e0abbed366e3ffe0806321319cdcbb168b71dedf61e35fd4be51ade6d8f2d74c88f23fcc70f734fd5a5c35c6dce1790fc13edb6676749b574713960780fe1839e7feaa03fd49f74dd8ebe41caef2407376e4223ce9f234ee36765ed3f4a684a3e1dc67c272d3e6968cd19cb2f4b46dfacbb12ab7b074fc1e54bf298d550179f8a5c72092f889d5548c11908638bce61f957623f39c75b3c49136a45188fea11df6578fed1b2c653b26661e4e911503739a8212e9b4afb13673e72a2a1484bcb5bc43fc7069a5c8372d4bf909677d1597edea76a78977b569225433c2d234b3030dc6ef271bfe611edfd7508ee303a41f0a88e6fb1f3b302aa2dc0bf8ba2d9f0f6a9add39a366d1447b8a511ed7b95a5cd4dbbb19a971e63b7b05fde6369e46c7d53e612213a6de367b6a16f453a396b2e47a739554ba3a1cdf5200a68bef7aa8f8a5b2a2d994965c9451c77a16db7f8bf06ff6bfb6dcbd38d4d658dfc79b765ade816fe8aa6558dee7bec35d7c37304dada28dfd9c2dc52915b6629a22d1fa39dfbb61ccb7819da775f4c90458e489319f8d27481c45719a1c28a68e8df8d7709dfff00575971966795b8100fdfa95be0f7ece58230861925b4f33e6b30e08ea29c4108835789f7e27205ec89e7ae344b20c49e336afd688fb092556e3d7db7dcbac26d51de95fbc9d0fee0ab61c2b7b9c03e3ea72ec39dd368e5730ea4bb736af65ae4dc79dbf86fe19c69c2f854457cf45e65c41b98309f66a35e8a669809d43361fee5f2e84d1560878325b3e0acd18dc3a7274b9841e4a887f8b2c244779bee678330df65a159fbe3fa85058ae48f5ed34799093add2596a494273f7ac60e5fbf52b02afaabc2bee15ba8e67197823ec6d5735c6fe841dc3d64ffed093d543b3b0b7d5f047d5b049d9045a730467e5f44d0a8f3686616f0ff0de1a7df21c7b3e343851136155e867a1b6e0ef494436d3061f4584f09cfa3b4c8489309f8d3670cfe34718e4915e4f412cb145608fee797421bdff2d2a8fd13fafa50b0a580f6a6773bf8364daffa3a102739cef223f29d057a1aac2125b7caf42256126b0980ad99cd53e59510a7c7ce549d0a7b8e5ed787b39e50863c8b45722817d4a89c70cb3f00fabee09a2a722708b69659a31bf551a340097dc071b9a03fa9a42678af3eca03b0d0333df51a9ab7e19b67a4ec073cba7861b17ea60c4009bdd70088e778bac8b468383ef9e788e7d0098f75ccef7f8b049df7e05a794d7fda84e9cb7301bde56770b9a0a066be9d6572c15466aadbe7cdd09ff022ca52751729c64b24d8c92d65a9a2e3b984fec42840779503e5917f00b72cad2e5c58c84335a667a7e94fe412bbe939af3a9c3dbf12cf85da84f14a449cc39e43bc3b645b9a7eb1d5d855d651defe65d3b78d897dcbeeafe85ad5b1ba7d5debabee9b7807a800f758932f5e449acbdbe99788fae86246ab8ce805e81b402d29f16bcbd74ad6dd7863ee9b656fe26fdd583f7743d906f455a08b16cb07686c429e43de5aa42904f3a51574e5ce8255a736d1371b8094f6a461cc26ecd037eea6a09e471a76e202bd0b234d2a4c340b7aa6dc98cef7615e89de05c7115f9823df0747d7ebc3236cce3f3eb6c077367743659564afa8ca64045f424a27d617a5c5886716f8a72d2c2872d2be50f2d28a9c682507695d95e9017eb160557124518947172e2b54a496bc56d2a88c988f9524c3be7aa0952fe52c578a978d0ef000eb8cd79857dd5ec2c1ccf12beddc1c316c7d59e883f271a5a6126a44f16702fdcf4417faaf9cb39d21e70c2a56f2967cd8ee96e6b96b69e7a25a85e78475815928c63ab53dff3354b75f8eea9eda5eecd8942c8e403f1ecff274f4bd13f4f5136481f92fa01cced9141e043297b24b35226ae9a780ededdd8e85476e3b04d865dd223a4b86ce356ea7737b71463b5a764d8eee27b0e6801a6c812e2ef4204dc8a4ee9bfaa5d958a895640659d9d7359101bda0ca7c48dd66a74999849543bcb28eed5e0b6ae31c75641fdbb04094f9e29fb258976b7be4e6cd18fbd343d1c7f5146a47ef9410dad1e215f2a5bb5893bc06d3b2e3fe007e78bbcadcabee1ff7b136f2c21b18ddd2201162d0a29d92c37b4ae61eb6649b74f38905a76da815d9827d36af4f86be4bb88da72ad2b70b9d86ac31e76db1c93e67e5e701fd2589be73b2dd4b5b1bb7d6aed312dae7ed282dd3a6dbf8fce76d949f01d0af9bbdfb0374f1d48b045ef59219ef4fd2b16d79ba772233ccf8052612c685c8c7a0df22b1700bcbdc1fa4d32aa0869218cbb40e865b22493226f225131e9961c2ab8ca4889593f85c13ddd7002a2d1409a19d10babb015aaed641985340e085794b7755457580e59003bd7fa697748ca6323603af73322dba7b458b6f7a7a69e9d1373d687f0f406574a1b344a206e023a6e49b017f2fef86bc5f9e096bcbd414d9b8dd2b8bfeab0b98619d07d25c2d2d7920a5090f69e68b2c2c639b5e04994bd038fc49b732f75b39e5db235a9e1b935b5cb3a9d417fc8733d9e37aa1a40480bad389c604c33213c2ec422b37b8cb97d13ac6b9bab038a26c85b1d9c6361900168778996e27e216fa62f15eda4fed1307268af3ed937fbc6d9f31c0977e684f1d60ec8476df2d76eb66106cda7756e8eba89b52d857d14d69b009e914bb15f656431b4e406ec581952ca595032287d05491dd6a686945f4d91e295ac742377fccef55257e00d0a8474ecf024b76b19087b9e7cc3c5ee28118d23be6018e78c8f23cd4231eb27238de57481c416171b0373879876d0a019da7834d310f28d200d6da824d4bbfbd6adff660a2f853fb4ad80baf2c9dd6319abc49b97c41adddab4668037b80c336c807c02f8e967800222ea034346ec3805716c2a7d3ae3d1e1ab7e5c16d1be20be201165f1bb7eff470eac3e3bfafadf7a0b6f6bf48416dfd7716b5ad77bb2ebc6d17bb146aea181927cdf428a4a96f8bafc9afeea264484fb104bf83b5e787eebccee3f581825a5a5500b9c3dd933c7d9eb59c0714a7a331098efadfd7d1f73fe374d4f0fb3a7ad10235541379611b46ed9003baf9928785415a7a60e5b09662504b193bc569e981115ada18fdb8966ef88d96c624ace4b474bea0a56b052d9d3f82873109bf27cdb4d8036c0a1aff1fe439330963d9cb629c5d22c67b776ddb154914e0910623d4d61378155388b350efcb0a0f17d0dd06406871f9c93d992f62bb235d06b0321797875b285f0bd0ed4652b69c3d511093572e8e6c6a029c362e23a56c922f2076405d26efff995ed4e1e1d665f455153aefa604cab7f9a6842efa2fba4c4a055d762c72fd0a35cecc49b0ee8198cef210f3ba4cad5b0664b685c6305e2709d758a4edaeb1508ac7baee63714f733a19bd639ffdb95f278ab7d99fbf76dbbeed575e03bfb1a50e6c784c03390a73380dd4731a58fcff8d067278198efe3ca88145bfaf81088ad3402979835a170e428fff170d14938d54c6784103b936749c069a380d348ed440844fa7fda19ad7409e03d7ab86a5684bf53d15d4b2242a096a999eca706d67258d7f6ecbcbcca72fb400e2eebe5b281a9ad0067363e88b2d38138fa9be1d471111b197f202f605920d3591972a00da55c742cd5f31da4da572b4daa3c53457edb5dadbf097b9a4dbe68e69bdbea07c6144ba7b68d562341ccd58f37b1419113b9b70a9237d3f0054b76b7b7fd2bff599bb5ef08bc91dfa0a419c4e7bd536bca2b398201a11e536320b042edd82f5cbdbf2a08d18a353595fecc6282200500590b35b9ac760bb7dea2842f6d456b14eab2ba5c7ca70a8599f0fad7548ace412411777cf9acd54c0797c624d14a906bd798466a11fba6bb9553c7fd73b2445746ca7b6746fa7e41a51e612a47503be4b7335b9c5e7509ff995117e9d0e41a2f9354b1499d40d65dd3526c0715ddefd9082923221af0ae6c9196e5f21b892db879c4d54a8d13784d197c0310d7feb476411fccaafd01a5b8c8f4e23ac2071abf6ea52619dfdd00e7ace86fc73d916d89f64ceff4ac47cf8b2b4cae05679015a638cab9cb00badd67819ce187c9a86d770327d59f974500e7dab6c4bb3cd5d4f5eeeaeb7f900aa87d636bd0cbab86833bdc780f330aa0f7888900f26ec12cade333cc203d6321d5c84981971b6e5ba6d64ea07fb8474743fe24cd3d42efe8e84e0d7d594d0d3bdc38dbb5e896624edeefbafea513792d17d01c53af42d3bfe3be8c3df459f6628beab0c91004cf3fbf64e5f418c3a6ce6bef1b982148b9e9680ca9d997fd14f3703fd6c19c012cc59ba102be901e8af2c40b166a7384686be07286eededc334dcca5488182cb7b1f27070cdb22b76a13151d8cdae791f0b253c1c546a7fe692eb76cb112efa68e8b55118f69372ba4f88724e7eb0f2a9894ae5f43f29954fad9c78d830cd3c334719a10b5146c42895739e9fa89c9e3e11a627b60cde7c78c75569164ec9c728564633671a798cc967c25bb3dbb26fd33b65405f2123f29d0b19be24e8e294004238fd92d5adaf106ef2370cddce67c41ec20dde537acb635f392dea02a886f095d3835d1ea8defb39c5e74ad6bfef2847bbfa355c9c54e3f5e9ff89b966dd922901c2fbedfea4e876dd41043759cbf65460f4be1689c81282d1f34989086221727c6e3d8e21e8d43086a0b61f8d09c6f2a13305357f096fd7437e89a66321ab52f5d3754ad1d39852ffb42e58340b0bd6cfd24d14cdc626ea67eb824473b0a081b756649ec984dc0fcdce2a197ba633bb37fb129610bd2bbb3dbb156fc4fbb29bf0f60403452ec2f55a31283e475b9dd26a8f901834a37c99a18c988229a7bf8c5db69ce93d9c14e389be4f77f2d23e2d7dd50976fb7034f1d6ea0a4d112f529ed8b81b69d5a6242cba9dc3e678cd1b3cc792be603f8979f42fc73bb0b8430b429fd35f24017f2b8d95941294a414b79a3c06024d2470dc6a89cb96cb4ddcfec0afbd689f33169d1b20c4b9b685c2773582262a52a1243409b2b52fbccf5d92f5a4626d74e38d8cdc73d969c55fa13b2bbaafa814b11a0bc10eeab59d1e6627fab6fe7eb2d3a32cedb81d7d9beeaa4daf1a05268e8aee70349003e8065d74079512077ad1f7e4f11fecc1696569fa04123024fdee455c347514a42b07b0e346e3f4771290b9c2ea351accca896ed2d867bf5d2d6248fd45d760b5d71d60653cfaa79a1c5b4f0f4437f9d974cfd5c1d6c4d203f6b234bc03cfb0eef4e867c8d9a65bf86cd34d80eaccea083479f43b36dd1ea8859006f0b1ad2c4d548f66d02c09edef026569b07e1043426c0893f899a1f21aa11c95c112dc0795634f43fa73407417ea1de2bdd2b37334b680790ed362cfe916e89ea564ba31278df4621b76c7288aeff4b09a2420fa3e76c18aeecadd67b786009473a643577f36c3ac82391971d2335d1fdfcdce38bbb5dad8a0a6977d2dae9d27df01c7492abdbf6fe1e125ca084f6ce1d640c60f5432d4184fccf1d59549cae93f83a37b4ef4565eda17ffc62efaeb729c979993f785f5976301b5af79abc3e23390e4e8211d87ceb2924e0f65c4cf808947b5bfdc1ae3c9484ef41e4e44dfff3b0af1d05f5f780c4bf21184a571228fc598f1fe39df0084c13c84c1fc9f30740896f49f0843cd040987a13c83950029bdbb4132b2b731f3fe9ff6de04ae896bfd1f3e936d121605230404351871a1ad55e3be50628188556aad806b0b0e8b581565116d4bcb164258448c8a8a0b5ab77a7bfdb5a61a2b95c50db555ab15b0b65a34026ad5caad082890f739331308a35deef2fe7f9ff7ffdee1f3e599b36fcf9c6532df73b64e3e1054a0a5df9fdf550c6d45b8ac812bf66bf4dda0b493aff6b12eed0aebd2b2a9d4ecc6a984ca9854c693461535ab868e774830136fd46d1c6fa1d019723d24432ac4f1de71578cb88f46151c6d3950d95bed9543bd63ecf2e42517e158510f1ca77e85c02130d15420723f10a25cfd00e1a7173fb18a115f13ff68c1cfadcace74a97e4297f05be8f0dd70f818b46c1cd61ea23fa33dfa44016ffd45fe785b44f9daa21807790fcc24c4cf8fd23654a5512785509a07eff7d34ae31f3711fda9beb62b4dc9f662a19ada5abf12e27480f0c4dddfe0bf20a582ef668b9694c2bd1cee6d6d519291ff922d8a30ea13d3d1b18a89b6d20f6ccd7ad296009d1d6d8b4aeea68dce46250f32252c4ffd86d78f309beddb28088c7818814f5563ce7d2afb840a66f88d38577a729940a6a3f9f167a9aa1082b9979c65f247bc910bfa9fb2973fd21631e563e605353bf6df3df06076a9464d3937f21e465155e40b621c7cca743de419737ff7649251ffec691bfd0dbbfe99187a15be42dc6023251d44981b40a5dfc34faef0136c17de1fee878aa07eae195f29d4f4461d23d5762f1dd553f8323d82e03d97d8d1e705a30afd1ebe664ee79850a3657d084cd9e4b65547db42a894b64138b7c41bd48667885a678f96666ba6e9a3040e4b17a50c2c7994da7a6129351bef19d92e534e794a97b0a4952ec19a678356682eb3b3a4b2ad535dd9deb49aad9f3c463bc92fd645116cbd8716e036f0ba39248bdd5b7e85eb4d8ddafb47a6e78e3240bca271cd78d6cd84ad3078371764e0af07f6fc7d4ba49bb6cd7c2ec7bb767fa40db4e565cdd1c74cac68ed902caf5cf644af95f8b48b92663657c90732bc322e6b166918579fdf8e27fa276aecbc6e1a32ead180c4b4d3307fc92c244954f2f893bc9fdadb4286e40ec9a26c9a9c9346e39c8ebbebf5803d23e0bd2951f716ce6e60725ab19b3f4e8b087f7fcde58c7b1aff9beceefd71d4a4225ed642c85b86bfe69e65078a85d05a3d4f27e21db4a8ee648fe38975b5f87736fee926742052aa7d623e5acbaf7862a678150e52d11a3ee558e1703c915fb1177d9257d71e992585d6196fe3a5ba368f5afb8b7b420875f23ea2bedbaa68195798d16a1ef5a3f4c9d336a9d041206d7ada3dc58f72de8326e2af32ba6bfc14e8a61d1e8fa86a11f48bc5191b54b47ea206bbdd6aea5a752f29d960374ef3a99f543c54545c7f98271535990bb524ff93ee4dedc515877954e05567c6fca47d954c2af2b0a316ee75de4214934f7c4aee14e7aa09a1faa196f25313d2c49e88a89814559c5b8fe0a929859e0034a8e411336ad425ea2aa4f49371d346144fa8938e126fe09124bad82b7b7631f1c6a669aae2b69092c741873ec93bdcde784848e75f6214da7cd2fd5afb78ad5a659a4bb6d71edb931899057d2cbf58db8a1ec048152aa0b29f617e8960692eb5ee1972cbed0dbdb76bd1d2dc5cff79ae4f347a718a9d545c6ebb483b232350b3a299fdfaa711ebbb97eef847540184869c6f320d59147d4c0af119ec3e25a84dbff0d2fcf1af01888fbf8f5aa1c19a70198767daf38de31f494e09d5e3edf7aa16fd3827e4e816ec4e3f098c7b80e34d6861c8e7b022a948860ed6e2efa0864499161598c34a712aa675bfdcc635a6ac7f80888154d01aa1eae2f1a8f1da02a2d8de48e86d4245c78a866b4b320e1d129269ea26b350845be0ad4352d2c36e532ddd6b100d765272981d8cb67c9c860235a1dd87c67e733c0af7db4b4aa96fa7d26d52974b541c875629805897893cf61d8fc212b71ce5bf8618bf7aabaad0be0fb498c007ef08414c3365dbde4e79c3346b0de4ff51f7de4575b9b9a5383f9a690da590d3e905fcd4a8e29cfb3efc0a19b23fa121352f79d7a4d2330a936375fbadc4f1dabf11e3c9261e2ec7e043c2572a8d78de33eeb1144604dc3fbb562c88da68eaf741bfe898476ffe04b644c433f82f9080bd624d3dfec2325e205f10459c5424d7a39ba51b4f5c2ae58e2cf9dfe09185d1b214359ea5f81dc523c683263c62e0d1415731c94d3ad796dfc08e1bd20f9e752f2fe5613bde4d76867333515f41a6e15e56e1d2605390488fa3ef95dcc77113fd756abd4d3219c4faa567a121fcb1309e25b7f19666d03b60317df116af6c134fb8add8de5345bc71f728d817d1fd6c13debbbdcdc8cdbbae02c7af517f6ac4396e7cc6cdf17563678eefb2393e5bcae48ea44aee77c6b495ae05acbd2917295b3c32213e5d1a183b3e11bf654bf4676a48a76e39c684d7ce2fa753bdf95c3d2db1aaa7242393ea7c633fad46fdd511a1da945eef2c515379f536ad36d2c40ff0d7f8fc4ff2eadba573c723a9aec94cb9d8f2f42b7a22dd39acf398cdff49f71fdaa5ba7ab3b45bb9d3522df146b1fd1015d313e0563fa785f43e80d9f594569b4fa7a6bc24153feaee394d9958c8c7b109bddcd8f8a93c5b5e6a149e8d53bd9ef2a84c924fad58c2bf95b8278298a294b4f8786758be8a967f9c1a85634903ad4ca948c573da1a434821dfb46c498b77f6396d90512a29778a8cd2273e4349ae0c2b5d3af70d786a5a90c9c9b67d472976fff499307b6c35aec74fec1b7ac62007b149632350d8bdd51db7ff9cc7318ff22eaa448a11eec441985351d5072cf3b4d6c3cdec9acd9692093f8c4c3cbd42da9d70de1a285b89e7b99901e593cb47bd9f0365bf84c3db31e1bfdf6709fff8f023363c09e1570e48cc58a1ef06e127ab20dc0a08f7ea4fe574b8c32d072ba9ef8e74ce0f99319d4fb90be34589993d7008d5682f089177432f9a2ea2aa82675be63de09b19a3cca2c4250f587d7f0dcf1bf13eccade84088903cda6259a7a9ecac566acc9ab88d72152ec5b3bd6b21d4693581358fd292bd32a22645a9ec14635adb8f472926b4b65f483c4aaff95eb0e2637e656ca19c848b703c6e92a7662a582e2b9fb2f58d21211712a5223ba210fab2515aea84175f96488fb3a31423602539f43ef2de74aee530c4373c87faa9ba47cca398ea8204fafdf088725231f46b84579b253057a57efab16b7affa07a0b17ace9a11a3b3c27baf45a30e5fb9d33fb3cbcfad74b5ff480ea257c87293dd6f5c04443d45354999732f07c454694946f47e0b8a00620ae3f29ff5d28ff1c291f629a4355db088a6da6ab366bcbdfe83ded407060a28adeaffc6006553157a2f83284285881e73e91038e6e5e67aa82d6c7f155748def36946f268e0fafba15b0ea7e9db84ce7406ac7273a7271a2fea46326d54310c883b5496a08f576353a1eb220eaabeddef72f24e67f631d63cd0d98af4c4fad8479f90c5c62ddd74c4d0bfba8c81c93e2cb44e228b40435ff7c57fdf811f4632ace47f9b8cd5aa7d23a58256e262e6714e68a1035f773fe5258890c0961de0b7c4de0fde70ec2ba046a7a3ea73cd5503f9371ba0fb3a8efed51ca94e2ac792a2a64bab07c1aaca48299b6533bd12b29a8abc0c483197a58f150c7e73b611d38ba19d7536ff5901c6ecca1972817e1247abd9328f031d86c22f22f1ac80062ebb4921c6aa36d0fcac5be073653bf85102559a0e5a7182d5f7ff19fd5f1b27394543881d696b933368d97cc561593efab4ab2a9992da8d05682a879cbf8b8b5e6415d7c48dcf8fd77244c7b9c82e77b148eed8696fade16a5a8e7cc1e2ff1528d873ed0142c373dfffc982e7b9d67daec14ef8f9f0e7919d5473894793a124a55246e1bba75af9fe9aaffc740ff87e4400f533e6e48cedf8f51427b9ba4897ba2162432f5722bf1f97aa1ae3fe85aff5f41fd7be172f0a28ab5f3547c35f4ec57ea117e7f43cdafe1596ae43efb04a9ec0a336408f2f2f375d4b5c72bfb12ea7700b31ec66f234894e29f1aa59a8cfbf5f5dba9c98f04ccb851b2d9f3506a54dbf83699774d7ea92c81de37a4014a39f43384fb53baa43f5bf4b8c4d29f1e00fd976724e6f480fe77f4901cfc5c620d639e4cd3b4e6a7f829679eccd3f78f16e0770625a07343724c95e79bad4becb39fea2bec8573a929851ec61f6b3f2e93903cacd5db82cefafb13fa04e84793853d85fe25958a11dfa2a32d5803aabbead36eca4de8acb7259cb12696db2a8e3cc39af8f5b33fd544f94e68df1e38a4651c28b4796a6677c4dbe6597e20243051f1e55c265792a39b159fbf4454d1fd6cb92df4b35ddb6f6bc45d1cd31642f1f933743aca3f4a9a634be0fdf7b9b930fd587f24a6e6dd36d3dce96daa80ded368edccb0b5f4ee3eb3f9384da687df7bb50452fd0531a91ecc8e36329aeb590d9afb7931527cf912a3b973af77ed6fd6bd72734d8ff27107b3a719997ac3e1bf30a6264e4ac47ba399e6257f5e3e45b60aafbba32fe12704d7fe8544360fe7b3788a1139c87bdd4e3a9ca9b2fa40177d5f6db2137e46096db3ae692ceb6e1f63cca3ffd141aed86709fa2eced3e493bdaa11b7b66a44406967b832c392af2f24a6d4a75642784d17ff99096017837707d8dc7607d664b551bfbad9dac2fc23cc492af9c229e651f7fb78d74929417ca118e331adb150d1b38187dfc07be906b9969c4aabf684e7c5315031342550e1d4c0dbb24a31217fea90f492db5f870dbf3daaa6e4fe1689f77def66bcafa5f765ef9f52cb983d17e4535323fb45be2cb6ec062bcfda9f4e4d1122da86c9f31ac7f2b6996921293cfcae605385f5b942835c99587c26a741bafc1144e0000d7f2c312d6d44ca34c8e99bcc1eb55ee9cc2f32bbfd86dc1f7e67d46dd1632654e864a6071f28dd48ed8c9c1499365adbf756047ec34aca15139afa6c5427f951efd65a7ecf51b1bb44042605615e1b6613d89f913a8d46deeb15634e39f0f791c384fee39deb9072e119bc57e530c5984ab9ce9f925d41697bd4c382562bf6570cc56d2e21bb91eb2a8e1630f1fa4dc831b1bb6004107ef38df39707dfdf9fc5b869c6e15f43e6dcb1fc7afeceed21596c2e26b0f32632ed2b1209df48fb52d7776bcdf3dfc531f108478585c9bea7bfa49f94eceb26796cee2618b0ce142069a52436286d74ba9cd2a889a4b1613d1b8d5b7c7b86b78da60a06f215bbfa138d55fcbd8261e39d5f22d2fe968e96e7110325bfa66ed8e26bda98d89266d4f5356514b4be65c4dff8fe509ab6d77758dac8748706988ba4d4076b5237dc2c1d02ffcf965a4e2a62ce28c2bfee78651d481fcefede55f3393e3fbc9157a87d6c969235dda9c8261e3e4350134004b4cd19fc06251d2bc49c1aeeaf40f4371ca2749b01192fa31996f7e7a3da42f6eb4c3d1b5badf52429e89271023a5780f7ba9be78bbf5d9d75920a2191fb4c6a6a13a2b22208f750fc36dfa546916c84de6795b15f8625c6a2fe26972073127d2a34bdfbb24ee8b718e17d3520ff8cf6cdfd9ddd969956edd77112ddd4ae27d1d1f7f43974fd32ce59be847e805d0a0542f47bf1150da0776466eedfc69a384033299a39af504ffa1112a1a2afb0ddd4bb913e41e32db541d488f8552264c868443a52b1e634c2e7a862bfbb397ec37ec7af620b6610e4aba3fdb13b7d5a07f8e143fd51da3a24f4a3165623cc59d591a6350c07402ff2c35f8608c356f0ab45489f20439bca31534cdf381009d336ddc5cc3fc59a3a745ca61395eb4d7d2a5b5393dfca7b60d48b2208429d563d00b96d2009b760914aef24e251578408734770a8e332e98a81a85cef7a0f9f31a4c87f8226c95a9c557a93338e03a7510b2b653f4440ea69552494c2ad2a03bd95a7d85edfee569001bec16fcf46bafc745a5503518b4cba81f6b3b61e49ab3390c99971f783314143e8481c8f627b75bbc9ada95de12ea419161a420aee2d32da2dbf1ad16e3db0db4da114624ef1c77b17ad92b5381d2e480b56a3c3058afc46549ea67f224326696dfbf3fed6e7617febf3b8fe084715a5cfd4100a370229dc09942f1c214e92f2abc5a8c5b9c54558afd82e6a376d723243fba80dc2069456294406d113bafd0ed3ed877fb155927b79fcab039021310a1d2f905e25095c77fa1092c02d74b4e0788162cd134857b1fd49fbb4d2b4104f02ef77242d90f125bfe8494fd0147e48ad59526fdab2976e61a15fe77986ecef676d92bb7ca7fe845b5023d1f1bbbedff5d2349927d1f9cb3ef52329680b51ac013d83347644b5c992a0c4b530320a09aa57936048ba65af6279b3f0ac9e0c478a1e0224d59c34af72e5870cc6a5829634f5fab4ddfaf768c51a21f22bd593be482fdc45603f64bb69abb1bda154af8d408ab550c34edcf058ab134a7b2fc727c7e093cdbc74072ca71a3e0a78d0c14cf8d52bfdaddaac8ef386927fb373e870fb073f84406921048fcaa9e7e9d437fd95a206c40f1621e12d0707ef0df8cef51e3c15ebabdbfb692ca7a0e1dfbb85be113f26cd6c346afc1d4fb405a7a86ffa29491c92442e0ede05f49325ae43a3d24d1115ade305dff99c35422cdb8dad4b4a57cd9cdfa575885f3bbfa944b53b43f1ee8b4ccde33d843c89fd5a4b6dd4b7e3701e514932e8755cf1593c6397f76bc66737e35d6766dc09bebd5f37249dba24b41b6ff3b9cf0c5d8aefb12954c514bedbec00541e542cfc8ad77bca789dc6674a3ae5efc52f0c1a816e86146b1a79af6c11de518e5c83b6ee4babd611811ad5b8eb396c7e2e941bd32ab05dd8588b5dd1e9b346c65fd2084db6c536f45842a9e5d79c9a33e5234c5336b51706f9a2723a85de01c5822fd100487713cf3a5dfbdb845fe75e359316ed5c8477ab39903e31a0dc68890d1dbf39e2668729f468d288bbc6b410e81fac182eba8b98f546f85a78b4a15f87cd944a1e3df9fbbe345904227ca51264435d2279fd34d63a3740a3b2ec7077ccc4ef6f4eab0e478dd5f83f8e4d7302874a2b0847f83bd1011a21ebd7e7a02958d73ebfb4834973c4a37f837100d4061bd76726fbfe2d69d5bea8b149552a25855d5ad05861fed8fd6c6ab297360d746588262d6420da74fce68a4aba45f9c103d18ec836673d1985a8de55289aeeb9b6de02ddd952dfceee14f0b34a1846e23eaddebcca49e1588930a3d6c20ba65ac62e1ff0787fba8da65fb3a8d5c2b62bdbcaafbe8d0a65ccf909fa0d248f3a2be2291c083ab5843cbdf636a27a548b06d0b9da74a18353f498691d0f7a47288543032a2f9590a66df5ad161fa1eb3bb852956905b791e92ad9e1263f12dd9016d29f084bfc0a4af71805952a1c6fa2ac8c16191ff7eb22c55a189d363f6e77fc45d183403b353696981e461b35eab6201b8da24783e594e14ba78c58db99fd95b0ae8fd279a78b5af7a71fd00dcf3a9ac56ade0d989d4af0778454f55ede8ea81619757ca050afed8fee0a2c6d50d0febcc651efe22f165113f57a4bcf42a1a483e1178ad7de3da9741b87593eee3e944d93400abd135b3799f37ca464329f4aad77a62635f508535369f52e52723a9fcaa8771e4d12c949a3679d11aa15eeb7d12b6a9e6c18e9727b966f5a703ae1418ac87e050aa76ae45e46a9e4fc593e38d7945d134aab4e270668f0a94b2d96dd0fb4eebefc605f62aa8f7e830cedd980fbfae57952d1233135f930d20cfcc2cf0def7afafda91e6921be041ef971bfd29087c782b49070c2f0a41eda93caafef416d9538ccf3e587a41319e49e82b46a12b5e451eb49076aaa9c60734036759e3c92863578538565a7f9d08f85921d518a2d021831a9cb03bbe337de4d6645fe6398c1faa29b02c51a3c529bb63e6e9f76885bdfaa639a808463f8eb2a2aa59e27251d48537efdc3d15ae4836b282c11d7d060a8a1c164cbb1aef566af16c97a93370f8dc65f91914ddd679d81581cb08b2bf8772593c00509c0e5630811e54692c995c6e7fd3a825f4732e090146a70c0067e10f4e9e57836a27010210d597ec8d4d7e6e11e4d67cb8626371ea28224302b10a04bc58afcfe448b61ecf21977f8733c51bfe621e94c7f1b7c7b4ecd709dd4d69f1f08b37db6d60ea7cdec8f9436fb3b4fa63fa4cb9963a9c308aad297df4f3329fed63246f778ef9dd3eec9e0adda194fa8613dede76139dfec127df612f30e2b2225800a28429d36352b35011d7146a7859c15f4d3b4f5c43aa3701e2b64fd44bd55ba7c79bf86010f026b67dc14354eb95bd57aadf9c6e35b781fe9f483ba5159acbfa4dee5173496564e9ebd7f994dfcbdf029d1fba36d16d5bdefffc19e0f441f4e8327a49f25edf789531734fd34e51dfd5f917e6207cbb166adebd982089ac72ee9788632069fefd0aa35aeb51dbdc39a61672de9d624d23b9130ebc7442a7b17ef66a262530bea1851f2761b997885828eb492031a3bc688fd3b8c8a4d1284739565a9b7847e1a3b07766d1ccf9fe981d2667af0a24fa68578f00664b77c931a22954ce1eb6def09145e4f797a328b6fd1e1447356bad4aebc3d650abf329d909cb7735883d87727d4394d267b8fde490b392798a269e969b8f7d46237d724dbd27a4ee3eec08739a14ec8ce553627981b56cc59a7135dd6e2a760e706c2bf4a237660f36ee89883383689a04fe65de874dba639867b7a1823ce75f48e6b9482229f574ab3b4b3c4a6b5f5ed1def7526e41f727798e562caac6f67c2b45db3c4139aabccdee533d648f7e13c3fe3b0e5fd5a06344e6908ae15b5053e987177cecd1badb79aeb1eb36762b1ba219f273c75ae4337ca46df5b36257e7fbccdaa7bd15316ed5f64f35edd07fe1feef970b09576a0d90484e8a769615bcbf3ae25348a1c7caa43cb96edd7594294c5509b248261e73b4eb408e9d484e4604ab7cb1d34c1b5a5f3ac9998bf97327147982cfe7c42e737749c22a3fbb434a1c5e23bf9a52f8c9d1a51f376a746c8dfc6da80b5824a1140dfe98176665399644f5e885e9bc597668356ecbfc72bd1597ab128b3980d5936843f2f0ca91625f532d84de5a785a8209630de717267c18c74aaae1529af68f9937aaad664413d58f4c6c7c7cea1436f06516f17f172908da5662674baf90ca05e2fea96340287efb4ad192b7650acd1219d84350f37398d6f7171d08bc2f847b5297efc2a35e8eda55cd39627ed299e86919f439f3c85cfac9d24a4a22fac7b7a3d6bcf8f34a4c7abda9cab681ddc033a68d18e9aac0e0decc368e0398da5b435098319add2d6dfb3d22a26e70b0da089970e59692853eb9106db5d3ef38f5934d4123bacb06e295c25c8e9d82a2821ae01d51ab64e5f929432ba391b7fa379fd465349b69e74900c79942a7b79799b4c3134057fafe95165bef64c31c2b19f6242be5c3162988762a8cae3d66f75bfdefbe561bd7fcdf0c7254fbd407bc7654f4ad79d1f13a4511717d4a38da77e3ac57cb398fc8562a8442476c811b3a92ec964ef6a5ea5ba099d356af7b25c3595f3d4f9922f9563830ac96766bc5b7d613a8cb0339b905e50c44b1ba892658abd2d2c372f583b31fdd25a939bb0071e45735c32c50aaf0667b67f16806e8989296e05b01a0e81b99733ccbd7e10213c8b6fc9532d52383688937ad949609a8a79d2bbd878093e8432f524edb6c08cc2438cdbb211af43dc9bda7324628b1e0cb09364b2f745284792e9c2a6e9097960ede5e635bd725c2004d39fac4b9b5dee265b0c69d92dfe1f6ab304ddd25842d5bc44c984efe06f3aa89e367836c453881b7ae4aa076451854fd1786d3ac23b2b8dcbc26fd9a53645bc4fd5d4fa96506825fe27e00feff4af9034f490da0d9352b37d08fa974f30278d93daa9a4c2fed46c9b77fa653075e597bac0726ef7ce18e44028e4021476e89af6ad636932425c10448f1f1f53df931fe5b830f5b27b2d5b4a0fc9256a9e70829d6b47591fe647156aebc5508f76782dea7da721513750358d29e3679bbd2facebe520b173b53068cb3e7fd9c5527f45f78e47a5cdde2130b4b5a07fa88bb56da824979af648d051a7f5859b2444e10f1295be40c2a32e930e29ea312ede05788ebc51c2aef292eb4630798cc864b54aec793ec7d29e5acbef6a3fe0338024c53946558e64904b5d2efba4dca242d6d86e91446a8ff953dfd6f10aaba621698188b7459294e65d006b27bc2b6a30bd9f4c4cb1d60833b7c3f4cc0de7a0314fe1d084be2ac5a3966ab48e2c3776ea02712a473244f396511644bff7fc88cd85eb7e9dc58f8fcbfe1c2a4bd283e2091d185f111f5a34a6d357683bb5f6d36e1df12a53d42e124ef9b7d5412f13a036adab6fb773b5d42caa1c2fc8456fd91544d1bf8d664ae7f74626f7d63a7816ecb0668346ba7eb57d79a21b39b0e9abedb8edd8fab8044f029d1b4d1c9b83ef848b0b66d37cebb80268537dcee2543bd748cb9b75640a28ba60e7daf1a6dd4cf916f11c5c0f6ad6f5b2cc0d8af21d24eb7a1decfc4d3066bc200749e7b92353f6a66f2e49a695b2e55f9c637966ce5a4a8ceebb36429ebbd12b18c7866e84d1d453782fc79509e1176dd1413b9b8e3a7a003d5df7b4f91eddd3667b38d2736497866e06cd441535eb739e5d2f31fb6d75d1290863f98eee648eabb8179bf64983e62b150efb55b99e7cf484bad828b2f84b26f6e7f271ec2184230e4db93759bef5aedcd20bf7ea173222331e6a2c6984be9252d4994a72d9b00796d094b8097568c257af1c82323ae69ad264618e98e3d491c713d0d758e272723ce46e2325c37ae0be5e775a2351b80e733c85df6ab834b55796a64c33bd5df4c4aa7c46abf21db62adfe189a56c7ebed54cfdd448ebce86fa73f30db89f645a3a20fcec45dc9be55beaf76a8babd46e7a5b5a484ab7add95ffd0a4fbf25ef5f88d934420f59eecabeec742f3b01b1dae60e54b836d8c0486dfbe05788c32eff576c4bfdd2027da6d815d6ed4c7e3e3339a43febec397d44c3f6ad71b1d69cd0379246e01eca5449b677f6b63e7b2c3aeb37992de3ee94a96dab0ae663566fee28a9bd8f8352bb952808a19f05750eeb3bc26fdc1d654e01c16ada954951d48c22418e649c65b67299f215f2d648b02e2fb0cc745e6774b9c33c697c3acc0bd497ec8acfb6f04c5b3f6bc1ee4c0f1af1da70cb781cd670d0523b353b71d93503db0e75e47f77676feab3a5c3df27963b9f4ff0d3cad6e701763461caf92ad4078c2711470b75123e1e013e110f935a46815bba62ad4e454d7f0a2360b99ba35a9ad36236bddbb4cd494d25d78b6f69a5a21a9e5e98ccc7dff9b4c9f0575fd2c426929a6183bf60e5e32fb684fd5b0e768c4e9b3b7359936fc94ff2a68efc6eeccc65e82eeb5c96f54f5397bb6924f4b787792ded7f37da75b41dbac3d4d56e6f8befe4e2890da6a936ebd22aee9af5da816d985f849fe25bf4773e9aec1d4daf1ca51c2a787cb0a3596675754838107f61647f50d81fe7bcb294aaab053b5c82b10715c9d5c8de5848f6448aa267480f65beacc56525eaa5e474c1d87dd20c19a204d5e8a0f681117f07a721715c49cfce1bdb6469a7a17f4daf43a6f79eb62b9249e8be35e4b0fd8775bd77b6b92a9225885fd162c6bbf62bb5012a53457d3bcc5d363d157b6ea142044821267a288a6cd1655dda5e5b343e3b8ab07f49d15f22ca574bb515449aa74e4425fdd27132badc5531f453d1658de54d70d92ba951f915065d806ae726c5e7a3897e9a349881f14710fdd246a478f0c7121e696353e4fc7184bcfdfd773e28f9a0cf87ed8bdf5952b204e66e8ad4648da7a2ff57227a6fc052cf97a28d785c36499b5299baae459883cadc7b9ac5bd24e16c3fed21153c5eb6330a664ff21497ce1954d1dfa5368494b86b99ab95ad354984f7f09ed74dcb709d5ec67b14f1f4a2ef78f49783dd2bee9becabef5fd66948ba8d325ce92f5f4d4ee47d895aa3a632ef217c2a0675251de67702bcf7383fe2987bd8d8e57662ef1af7081b8da8d532bb2bfb06af1509bfcef7e862360fc959f82dad50786b197e7bc48cf5b51473c7f7f38b863ab8a9725c8c083fc773ab8252cea5a945684b39df4f84f0fe4eccdb5d5770d1715cf0f97513923a77acc2bf55e57e73e443bd6c2c3a5a90498dbd29f41ddfb33fe1267a6cf610d988347e1dfb7a663a9e57f4f7247c4ef52ec23bb0ef60f7040dd5ee6b0d6b20fcacdf7bba846d34e1dd8c1a8dbab35bfcf08e7e1392f0eee21b6fe698989d838c02948cd3d7870827e94f3e361b9e1480fe0ad168d1a38fbdb4d4cfa705caba3d881830ec1cb6495a3d24dde916d3bf91f63914c34f924fc4a1a5a2a2562f2d0eef50e6e05358e5af2acc1850f6c5b69e65469143729b73639e5154f35a655e5b70cada545942defc3c26a7e33438a78ecb4b1af119eeb805bcaf789f2b4f2cd46ae507ef1e7e70b4e178cbe936ef6f9919c6de60afdb5e8f32a9759221192fa38cda51ec6e4445829d51f8c93910ea08b1ed87274f084f9846889fb18467d846728ed9235043263963db8867de1b287d05e648c29348c2c8f6bb9c31ddc1f43fe28c59769249bec172c696d1b53237ad5a4230bc317ca2e4fa6fd28c3a626bbd5e88f0d3984cf765bb159f7f8a145f7f8a5c6bbf502be42da82dc4fbd1fcbf6126aba2a885f73b0caa47039e6350adf3b130a8b6a0df67505dd058f64d285b4cf5601854a9893483ea9185415533d58a4175ff2f32a842e8eff0ef776550759c0c7b9e6550d1f55214e0d5dab186ffe65f6650ddef6050ddff7f9941757f41d40b1854f7ffcf32a876b9fe2bfca95b515df853b368fed4dd17f2a7caae7abd883f8535e66833bbaa0ef857f953d6b1c8fdfe15fe141dc3232606a47a317fcabab42c7f8aeea9eaaf3dc79f0a78317fca3a95a2717fc09fa2e38daa7c8e3f15f062fe54e773e733f239fed4c64efed416f41c7fea32e64f59851f66cd9f5af387fca932c7e7f8533a2bfe94d4f67d532acd9fcaaf7f3fc6a1a8fb7f843fc53cf1595ed739fc29a6d77993cb9f2af0c5bbeef82660fe14732f48b0e64fedb0e64f31e3dfb43fe64f31b1f45f8ef953ccfded6556fca98d7f993ff533c39f829a67faf337f6a7532e983f2561f9532d88bec7aca8f5e42e0b479697f0cf7064db42a8e47f9949b5fdd9009a3fc27ccd1ef0b22bfb962f95e5a68f2f50d37a3a8f6652312d30a98349c5983d6826d575999afe6efa1d2b26151d76e0bbdecd05fef43eacb31926d59eae4c2ae6edce449a4945a78afab34c2a26572f7730a9e4ccd7837f894925e9c2a462fa0d19cba4a2733af02d0e938a7966fb7098544cbf25c5bfedb14caa6e7fc0a422582695c38b98549b7deecda3f23b98543f6ff5609954d7ff4526d50f7f814935ed392655d47f8249157494786397ebf262afec309a471540f3a8e6d33caa369647e5c8e1513df8a7785413383caa299807c5acc476606ddf9fde9547b59ce151e5ec627854a7052fe251316dfcc48a47757d91358f8a717ffca73caaf57f8d4775ac0b8f6af69ff0a8fefe9fe051e5d03caad9ff091ed5b017f2a878899847b5333ce6d1af3f5af3a878899d3c2a5ee23fc7a38a38f86ff3a8e8bea6def98f785469233a795403d83eb948ed956dea26dc526cbfde8778e3ec518b7de827ff511e159d3b6db77f994745872725ff3c8f6a13e651e5d6f794a8a9d52fe251e9d35fc8a3cabbd2ae4fb7e6516df3b1f0a82efeb8e777795438362b1ed5ea0e1e95eb531e05bd109560e1516dcdf1cee8f8b5d0eb775854cb318b6a8f76bef1b60dc3a16aeb69c5a14a7e4c73a8be28bd6db3c32ccc1ef66327836a0d694aff3306153d53c30c2ae684bbed94731706d5643cd35d38cec2a07af53906151d1e33a898f08510fe050caaeecf31a8ac6788c91b29372e832a8661505576615031e3d7df680615ad0bea5ffe8c43d439e393af855907cda0baf72f30a83ae329caa57a320caa42016650ad717e11836a338f195ff70e548c8095e4d0fba864dd9e2e0caa37ab0bfce8df6d3c5fc411b14a2f8372c70caaf2119841758f615031f3911fff7ae97d522957864175ef5f62505de8f81ab02809ca4f33a8eefd1506153de7f19771195456f1ad84f27530a8f4e8771854c7690695a3207001ac4e6ea91906152ff1abedde8f180655678cc971304fc10caa3e0c836ac7d74c4dd7767b9e41d5194abe0cf4e32f30a8b6a01731a8accaf31ed4cf1f31a898f9a0e8390655f98b19549d31a3484a66c5a0ca5a8719545a5f86415560db8392d9f7c066864175ef9f665075a614fa2ed5836150dd9b7b795d71fa4c0b83eaca6354982df84b0c2aabf69803cf771706d5bd99c582cd3e5bb79842d6dc7a017feafbcddf322d76eab7df7d369819fa4c863f859f8de7f95356e59961cd9f3af44ff0a7acf423f0dfe14fb1fd1d531f017f953ff58a357fca8ffe5df7d68bf85338f61236f6a2d7ff127f8a7e2e4f5f7f9e3f65a56faf71f9535bd05fe74f59b5ffb87f8e3f65d5bea35ec89f62ca39c5b31ceb8d4680b5a6aa40f17921c2bca6922efc29abf61bfe97f9533f75f2a7ca7d9fe34f95cde45715b0ebbcb3983bd5992acd9fa235d7f38c853f55883af9539db9f119fc62fed4ad28963f351ff3a7987577f871863fb505fd2e7faa0af3a7acf45d61b2b7f0a72ceb6e1feac5fc29abf6965bf3a72ce1cade4df8fa42229547a677c97f6f980361e6547edb3d5883ddef644ee9059839f5eb7dcc42c2ec11d1e3c0dbf83bf3e1e9aea72604e8ea0b42e9b700b6ee33e79dc45c8f1dd9bb2fba2f3ef40d668ce85b1e77bf904ef346e69222f7c5e2d0de5bdda753018d6867477ed06b63efcef3157ab68d9e77b293c1329a007fbabd08c7a8c9def4cda76a6ad363ab5045e385373b7923ccd95c9dae3ee6804b6d334f19ad992aec6e62831edbec2e65cac2fe1e7946f4d82bfd707a6bdca69b847f9626b5006a41d688144502fcbd87d8bd8c39010ca7e0468a50fe793df9a83bd554cfb89663d6083e2b59c89edb6ed09ce231ac02b666c67aa53b9de78fd298a564b260f9e63067eabd0a815eb40c9f8ddb7a1a594e5780d51f9ffab502e1932f2ce7bee213263acf02c1bf582826609f429ea96f5d2b3ee302eef9109ad7624cf163d24bfea6c5e8bea8f7f2c056cc08c0cc8019cdc18fe73c7ae7fe823b91b717d51cd43db8c81f2f425e5943d253a61001b96f48a652628988ffca4944519fdaf2315f27919c7cf6a260badd62f7d0e2f4425e6e5e610e09fd8b3b3ab7a1f8643a9f7a720515a6cb50717a7f58cf913ed1157b3433346c79bdbc729de8b7f64fe9d64b0ab2309814472448b14b82544569e36c117f6c3652d9ce36da2d8ecc6042d624315ca126238410d32126ec2204df456e2d4e5fa7520cdd45f89e74b397a042810451719f120372c5a18b32aa2c6b1bcd3cdf3d9b95924d3ec5dabd2aac4bde5bf139f2389c264f71a405e9cc3875c2cfc262b8b56ad2fb3bdfe77d70ebbd498b772ee62dc1ac86019833c5e8a5d62b57d560b7d862f67979a411efe7e811a5bf2ae427c9da42cad3a4df8bf886e63b48d9dc489fdcdd98d796dfb696da043651fbd0d87aa19a9249f9ae4594930956d515a837b6d9b61051eecd88ea750a51599588da20a67d2b47d7a0711ad021f583d2b85017f44a11fe428a5a7387875d898bd259cd3c9340dc8e6d857e948b09cd5f2d397d4cdff023d63c7c12af1ced582885bcb5b9d079ab16f1952b5d08e56f90b74419c1e6ad970b6158f809a272c5229c4a4b48ef7d94b38c30dca940a68de2764a2a166177fd111724a9573eac4014ea26a00ac5885a07799d4b121a757e397336725bc8831b1af5f5522ae80e6a0b696b69349a6e45b553eebf21d3d23dad296ad347a75a6b8d20378b5bef1a4d31bf81dd4da3c9966c097a963239c8f8afe558a7fedfcb71efe5c18f236f5b9eabc0d619cdcc737530e770d66f83b16eb4cdd4ff88b5a33cd9d0f40b4a19fc2047fa63233fb7c22b0befc44a096ac5d2906bfcd502bfbce5091adf0b3d85c21f72bad918b2f722622a1170689fa17e17f24aaf3df169d900963b8a4e139e52d2c9ac1795f1f1fb40fc959494fc8eef96351a15934d3e8675be8421e431ba9ea3ece54b347c70cad690fd379f69ec9e8ff2e3105a2814249516a68f6e35f45c8788290da5867a5fe28713fa0201b13fdb7eaa2ac074826c4e0a51a5291fdf8332482b6da004ca15b5a84eabbff6943ff1b89b5084f36e87cff6d057fdc457deae43cb1384ea409950383b475f30010d5f8f771e7e19fdb0fa9df56dce63fd9593e7127ab113a2c2ab1c64a3f03901fe1159a2831bd64d3ab8213523c5979892f28670ea5823bedb9f2d99ea683444ad23765f1c56d68f2d77f261c2530fe59692506efc857f60134f2afa8e5fa81d858a45553e4ad96364a8fa09e1bc180a5ad1f2443f5229dcebb3bb34928d21f42053f625a56eda51adca75f761a67ab75409e9482e4a6174a2024964af5605ec30e2bd5b093fa17a6229be93a87b97daabed4b85ea1d91de32c2afcd9998ac0af0886c0b4972d6579244c3eae3ce2db995b99ab48f5cda720db54282821e294eee8a5edcb378b2fb1a1bb2a0a5d2b448faae0d4f5f45f20ca55308c3c1df90f2fd7ba825cfa051f30b57778735040f1948334f49fecc3364f8f3a5d784bc920da33653094f616d982af32ec0e1c76dc7312ae3ee21431384ce955ec371f279ca2613e24987e80d9a7a88e11e4f69730d6279c2d3570b7947371c2ea01aeb11de3779dc66fc7bb3f406c96bcc533655a25499be52c03b5ad098230d11f0127294b678df650dcfa0a985780a78fa99b6bc7e05473bf75d5e2fad06ffebe9723db98d941ffc8294da7ac230ba9ef0de365e54e613bb59b9229818dfed3bd58c0d06f3bbc830fa2932c47d8494ab4fa0f602e5387fc2d03dcae77281a1db4355aab372d4056418de44786fb8b6c130fa0641f81a56bfe343ad682294dd2eaaa423fa2025791829337ef6512e8576d794f8188c1791a1a40419b44f7c085feaf14524fdd20995ac371cae44a30a0c4baf104ab242a5975541c86ad528527a6524df501f8598fddca7adbd94775828957d89cee751734891b45a8634d952a7c7fc1ff2cee60993f323db9c4f6fa082bfb3933625a291788fe8685744e5dc15d8aba9f4bb02fccdb6b4a90939fa53820a8154ab86f235c1fceda8d673df9ccc01e997b50bb43a7fcab6ba4fae3f655fdd47e36f92553f2540cb7ed81f9c79ddb83f735529251323a9568b7e309a7a8bdb715ef02ed34921336449b2fcc85539a737ec88906a6bcd544035ca4d933a5de1cfcf95868c405b8b756a2aad8597abd667349aa9ac1684f75f4d0a3a9a218d9e8dbcb49b8a4dbdc4ed84dff95293084becebba51e8ffe93de99344d44feb719c2d8dee2e0f4a937c97d74f4b6d90f0a44f9af07ed2c4619ed45861c6bb49f72b70dc076e2e904fa3d6dc66eca735b9434e83d57ca9bf33126669fc0c19953e52d287a42657dbe9858ff86e5ae829ce55c2fc0609366d3354d623a5d315783eabd1b45c7a1ffd5c65c86da44f4c40bb2b744c2e56b7e0dda7335aa0cf9608f4898d78f7e98c6a01e6ae2a0fed455e05de9a91fbdc84642bfe424019b40ba54ca6320f23bd1889c76beff8185c6e23437d33626227d441465caf7a5283ced2f5aa97c188f01389705ab8fea1256c45c7a5d183d1c42ff4b26abe7dcafc5cfbb4147f13121dd727ba22530e5976dea8acd5a2dee56c0ed35be8bdab5bd07af88fcf0df128daada6dbac7234c23111ea14ff5387c1ce45dc7edeb82312a7d3e6dc72d8ba07293f6c7d8aed8c9adffacb51db0cfd651eb12346fa3d8c817df557457ce90f8d7ce98d277c436310323c0946d299957c65dc6fc46aa1b4ef48b45a240daa8219473391d27fb3ae3cf7ec6a7d9f51885adf4c18eaab884d2669601f1813a42a8f08bc8a958a403bdc870ca3dea9e946ad158b95e0c7f557aae789658687b50495d22d5e5f50890e643c5d4d9d2605e5eacafd98553eccf474db810dcad287c4d30dfa82d388aa2617ef88d248a4e006bae2951414e894e49490abafec83f4ed00e746bee1612561785845e8839da02e3c77510e2778741a79dd165bd2786b9f893cd1ae515f905d5fadc71a73a11edaf8119f3a51cf930a90c0758ba1200329ab1e22c3b6d5e8529ec1fb3b88b39a3054d5a1634618f95aa5a05386ea27089e7671058230e2f1e46d1fe53ae8ffe169b7f84ff14f31e2bc607365e9aa8b540fbe58595b4b383e38ada3f2cf84e29aa2b2c905fa822ac82b7581747635260551bdf6a215db0f6c3078ff4c3cd9f074f5699da391d29e11e0ef6edc32f12fa990e393a484cef1595228055d1c2f14aa0c21dd09e5badd48d9f77fd0fc1ce56b358432e636a1bc2a24566c1fb5d130ea67e2f88669a5d4e633884e378d9c87d33dad9b6d1c2f7ee443d7466e90d14d206ec5dfb628af68607e4a899b11c42f1e2f16aa94eb8510632d618999981c6154dec6e66454592aed26859cd59a9509ed68762d2e25e5c017504838834e4d47be4cb7e5970f09832603e90b60c6f93d39bd2df882739bf383d5296a4d80f4c94768f6434a448ec1bf02a6fc8dea7b82470985b3f4d59e7c4d36fef66f2d496d59889475109f48142cdd5085a8d745a85ced7aca80edf4a211b8757744b7b90695c2cc69fd99f614f5d9524a7081f7749b32bd12190e3f2494ddaf809e98f9fa1b7d106e9b7fe8f4c17d61340e30e69f84fe71c31544b94ad03f745f14419fb9ee0a32b949dadb82dbde29d1ad574bfc31239ed29d9c62ef4fa59c9c426d104c913e69677aaa297a4d208c0295d0660756077c9a7f12fab38db711e52641a00781ebd5d33e93a8a975b791c9958951afab3713ea25466a768d0db55e2cc2cf8489242f60cd773219bafdacc23d9af2b59f0943469d8ad5ff094933472627e4acc86afbcc243c7199d1857a11a30b7faebd4b8c269b131718cdbff44f69b2a6b45393977f4339319a4c3ca0d69f1941b7700a3986d1a7589d53316871efbd4899f75095bbda50026d9e51a5c29a1cabf32c35e9ceb4625d4e1948f7d66f3689a438f7be98cf8904bdb72a679991458f95e1820e7d335437d231e25116b7a421a3da27c1684a3dd38c677ff83ba31f72938c6ee950a64efdcdaca7f5b7986cf451ae87f056fa9ba29e566aa5bf62467f0d89b01eda463a2bef804b2d94ab07df8672108ba89ec20152dd6db374ac2b4af123de342c2d8531b8db20a9735fa40f31f3a5054e48bf6205f22e005d3e0ea362f6e3fef82483c7fd34ea943bfa154f90a39f74d4693355b8a7df5becf908585b53023c0c413fc00c4ba51cf52bcc202a7df2f3a41b609c9827a24bab4c3fcc945678c52756176d54c63421cab17b9fcebc6c2da59f1489b87f972765f3429ee3a9ce6784ea21e6c5ea4c3d841734fe3ba25302898073ce6daee5ea61fbe04959cb3c292642f82d76c56e29816dae52ac73f3489887439f2b823e37edac7b5248a00c9f430a73f3f555f0147923989f475412b9a9d4a6bb282837c55f724ae35fe24cf811013b229242da9ca4c1246168fc0a1d776a585d992b4cfbc8b50566aa9e04731640e799e6bc45f8bd86d206f47ef49b043caf3ebd73e8933e6e90226596a5260209a79c58dd2bf884107a3fc6b256c2d34de467c64fc0f03cea5485088ff7f9db943d4f2243f035149463e8730e51bea324c592d7548519ae88d63675b5006bbed33665df934859790329375c00ed008d1b550612e6734157d0b063ca982c64ead5bd39e0d80c4b7a8d6e308f5f05fda4df3365cf2c9412087d669e2ba373ef42afe88f77f180b8c510b733c45d77a123cefc634c7f67d09df5b9b9da942fd97058b3226f7e31d683f3a5f8e96834621b8f63259a7179ab8a356ae87d7e928eca30e339cef0829b067cf28ce4a73623ee5140cf0e61795a77bd34c5ff7c29731fababc5ae01e761ee21417acd6a74e8b0a9b7a4dd6443ded2d70f44230fa5046efa167aba8dd0d3d94a10f443927af330e8ed4c8557da4d1249bbbe3e043dc9101e02bdd8b8f0a75a83492cdc1ff1a569fbc2cfa67dcd9c7fa1dfe0841c23cbd34ead959275e607b036863917da5d77368f507ff52b7e732025e1c979bb9a379a44c9527593f995ed5f9dd20beb0815f4a98705fa2a19b1e35689ec6cde5958d94c92fd90b73c316580dfc091db9505f56876ae63941bfe855c54ed63a8aea6df826f2a1f4fd6f8ccbf24f4f728c26f2554fe71726734bbd4b213c6d8f3f88b16fdcc463e355388705f804fb4a0cfcea05b2df45a7e64a1b68e3f5ebbd7a710d620301f945d41b08e0a4e72965e1111cbf36ed2dfbae2bc6852f5eb1ff31bf37686137ed773f197afb02253a7f89eed480dd7023e03049f0082cf5891217c1e4efdc7d1393807f9dfe447e9b5649977813ec417c5f684beb3c8c394a28ef8c9929be44b6e90dbafd4d4c57a744caddbee463af1f1ba59d9f336c265af28936cbf0bbdb393d9002b4bc2b7d138f8a410d6b4a3f93757e338bd65fac3cee8873c8fa8b5a2d89e49328f88a4e024277d908888ce9b246bcbfd215798dadeb321f754eea6e37145cec8b2d3022e8b47548a3a4976c8f8db00dc923b16e2771d42be34b3d92cad6ae4b7e5ea0b4622652249e8832af9d7735b5c70e90ccd32226580becf4844658b9d977f24edd6cd67dc367cb60cf56385e4d3e3b9f8ac6d9efd85b61069b79a8f09f5fac93a3f7c46329577d82517fae0c32e9beaf48db042f1a3d276a37c92b8884f6f96ecd3a94d054f5aa3ab52d4d26e8f3e5e3f595aa0c56f4990b01b7d9e10ffa1735bf00d67fc5d3236e7a651ce0f514a051ebd08ffa4106942fbc7c3d26638b5e47de43c3ff7ad5c2a47dc33aec805a52653af97d9eac9b256eacdf66ed8b794ff1d9445c93fa1c2bd849bb01ba2ce4709dcc423117e7b21d96e28e84628ab1fd2f3f61ff20c30cf399f4704367c232d90217d4222ccf2379dd7f951b9876d72fd3493a98cc336509e044b791c49cb793744607e51e3ace2cc7d98c12f305c7582391225fa0db9912305ca8db548b9f02aa11c8360862c26fc8c7a59029da2f4ea05580d92c45a21a4aa4e99ac6f4c44a635c2fb2609f950dae482ce1e33d45712443df6fb7763ee455873c8a07dae7af2f17ba6b5627df505fe5ae1f55c8dfae6a66bb2f379b82e615e510ae38e71e23f3cc85ca366f2d81bb5a5d83e65f2f2632647d19d1de4a163267b515d2dcc93741760a652508724e798f61b5c24519b0aebda975cd3df9985661fb29c4783cf114df1871caaf5e2357c7ca2a8573a354788c60b927d18bd46fb5079d24c37a10059de135b87f94e88c350ef08117e27ed952ebadd193274172a278a88a291cb07d48a1e1c48ef77374bd31912095f949a7c074e8d6614333c899e38e57ba153c2f787dbd3e74fbe8c325ad95f7b9eb68550924634dccf2da8d247a8ab5b864f4f24fc6a4bd9b2b129d5bcb05cc99b714ad627615b8779f4c2303e055dc3148ca6bf9f89dd48e1d05e69de99bd7f61ce6685de1a6209b5c7b1402edd1b115b3efd176afc4e9f39a30f875718f72ee58f11e3f987bf52c8272c3960c24ed29b7a46b4d2fe0c7b174fd2dc3476cd2712bc289f65395df3b9254ce1d088b644281ce17fc0bcf29f02143de16e9ac2a911fd344de10cf7cb1532f8bfcafdeed76113ee6e993fa1f6eb08f7075f2f716ffc3ac1bd2d351912418b279acd9f01ae01867b9bcde9806f017b7ccce6fd80d3800f01f75566f32c90aac5d1514b23c2e511b1b131b11e3668aeafdfa420f57cb9dfd2f88858b9ffe2b0b885f26931e11188bd2ceeb40b7618278f5e1a1d2f4f088b8d873b08149bb02cfe85fe16c7c42c43366c3c094b17862d0d5f0c295b05c25a1a131b210f4f58b26c9cdc33cede06bd1e961017211fbad273e8b0518b57be2cf79bfe7a87099c516c44d45ccfa1c3c3e70ff28c1b2c9f68e5f3f75cece92c44fe9ef320cfc8c12fcbffd019c7e03204d1f5cd9593df7ab1bd457e37fd8fdd77bdf6c7ee455718b98f95475859c6caef587995957758f98895a892916256bab052ceca311cb30fc73c9995d359b98c952b5999c9ca35ac3cc23197b1f20c2bef70cc8f38e6668e195575358b396607d6ecc2ca411cf3508e790cc7ecc3314fe698a773ccb338e6508e7921c7bcacaa6b7daee4989339e64c8ed9e17b363e560ee2988772cc6338661f8e7932c73c9d639ec53187b272212b9339e64c56ae61e5e71cf3115696b1f22ac75cc331dfe1981f71cccd1c3362f55ccc4a39c73c88631eca318fe1987d38e6c91cf3748e7916c71cca312f64e532566672cc6b38e68d1c7364fc503996fed3e5f1114b96c5c486c54647c461fb6172c67d382b95ac1cc1ca91ac1cc5cad18c8ceb882f2e6c0574c7d0f145c7418f1c47d726b8b3f18675f80b8b8d4a5812b1343eeed5d888f884d8a5f215618b13222cfec32cfe873fe7dfda6364189bbf30367f616cfec2d8fc8559f2d711cfdb2fce1f1b4f1c1b4f1c1b4f1c1b4f9c259e31ac1ccbca614359398ca98f3196746676d6abfcf5b0c58b23626977365c3c1b2e9e09f77e446c0c76981c161bfe4a62742ce4105ba1d830b91cdbcf60aa282c3c3c36220ef21cb78cb17f3b3e8c7a4fbe2c861efdc01cc5daab17c72c085bdce910cfdacf5c181b11166e653f94b57f5166e10275a0dd411d681937f4d5c86538dd2e95f86a646cd89208ab6ca038365c577f0885b1e9f9272ca5e2a36396feb116807f369eb0e1bf17ae6b005007c6ff08568e64e528568e66cb31fc45f98b43716cf838367c1c1b3e8e0d1f67093f86956359396c282b8731f5c5c613cfc613cfc613cfc6b36f0c3bceb2b29995f2b1ecf3cacac9ac3c3d8e7d7e5973fba83f960ba099dfa3db83a997c8b084c5f1f2c888786aa19579714c58b8b57b5c3c4c9b687334a840142850f4d2b8f8d804baca69fb25d17161ec54af3332b882e5833c17270c86c9d638b9951f9c00d42e15b32202ab7458bcdc73e898c52b87c817472c1d87e7452f638d1e475bbe0cadc0d88587c5878d63e75f717444e33c8785bf1c09b1c5e33bb7b94397b013404e5abf970fba60ff5e465e947a97f43b2b0ffd777ef5dff955d7f9ce7fe7575dcdffb7cfafa09318af1cb6c4cfaa3b7a33215e1e13295f12b124267695bd0dd38dbc3dfbedd75553a732fe954b42acfcc7ad8a7b376225ac89293c1e87cb17ac92d3ab5acfc5e1f2c4e8f885782d8997a15dfaa317a41bb4342e61190ceef1b88f5a158763c3718c93870d9de88957b861c358391c4b0f4e8cffe225628480632db436f078ff66225617f9cf78e6ff659f2b4733edf9841d57a7b3e6dbe3197994959b2730f229eb6f086b064d41048007e0f785fa78ed732404880024400c90006c00b6003bf0630fe806e80e70406bf88e207b407829c89e209d00ce702f03b8f45d835c01bd006e0077406f401f405f801ce001e8075000fa033c0103000301830083015e8097002f035e010c01bc0a180a1806180e50024600460246014603c600c602c601c60326002602bc01af017c20af2ac8e72490af83f405e907d21fcaaa064c060400a600de004c054c0304829f3701d3016f0166403dbc0d7226b805411cc1801030cf02cc06cc01f35cc03cc07cc03b60f72e201410065800a000e18008708f0444011602a2c16e11e03dc062c012c052400c6019b82f07c402e200f160970058014804ac84165e05781ff001e0434012e023874de86340322005900a4803a443180d2003a085f832013a4016201b9003c805ac06e401f05f3e60edd04d480f5807f7eb011b0005808d804d10d7664021600b602b601b603ba008b003b013fc7d02d805d80dd803d80bf6fb009f02f603fe06f80cf077c001c0ff003e077f5f000e020c802f0187c0fe30c0083802f80a7014500cf81a700c500228059401ca01c70127002701a700a70115803380b38073806f00df02ce032e002e02be035c025c067c0fb8d2174f8dd6c0f4620daa065c05fc003574adef26f423dcff04b80eb8016df033c81af07f13700b6002dc06d402ea00f5803b80bb807b805f00f7010f000f01bf021e011a00ff00fc06780c68043c0134019a012d80a780678056401ba01d60ee4b770033d8aee46d46f49fce9a67e27f57e69bcdb7008f00ed00bb77cce65e8041803180c9805980858095804c4011e0734009e05bc035c01dc01380e85db3d9193000300ae00f08064402560032003e8039805840166023600fe04bc071c077801ac043402bc026d46c76018c02a48799cdeb00fe703f00100c88047c04580dd802d80f3802380db8021801fe6f837c0c10c07d4f802f6026a03f201c100f380eb80ab8076805382f8034001300c1808580f701ab01fb016580ab80fb00310579063c0608c2cde63e20c70166002201a9808d80cf0127018f001700556cb8db009f08b33910f00e6001600d600fe021a05b24e40550c2ba79c37d157b8fdd4bc07c1950071812056503b9089001780ab001bb5e518cdf87567162390fecb30047001bc1ee6064a77b1dd88d8a60b08005b643c883d5a37eac54d012af58de4d8c0e87e981f744f948f98001722bab0913e56318dfb05859b600e6161e13e541aa1933df7d7be69bd3df1dfe3bf6c3de1d69190f3dfa052c85057774b87c59586c74fcaa7eacfd8a776171b5e2dd0509911e1303836042c35eafe2d45f9d1e1bb328828235fef0a1c3c6befa5ec4d2f0d855f111ef26c6c4be17b72c8c8ae8b07a252e3e6c6978d8e298a5701bfedeab8ba317bc1a1e1b0d8bb6b857f14f03432836de4130b3f10c1fccbcdca7f345bf09c1cb3b58a825ca3dfafdc7d28f8c8d88888d8f897b3506e6634be36357e14cf883e58c996fbe6d29e70be65d9d59b49a4fad9c1916f7de7f385ff110651c5b33092b67c62c9b1e1b1d831b07d7937c71745cfcd480b767be1bf0f6bb7ed3a6cf9c3d48be6ca56fc4e2b05511e1383353c17d6ec2cae971b101e1f3e583e58351bf95a0ab803eacf45fd5691ec002db6133cf0a4300a11f30580198c722f0fd4e33be9fc7bacfb1c2ba0fb14ec345f0787cb804ec25e45ca23fb9c87ff312ff2f5f2fa740df9fcbc0dbea1ee36172e7bd2fb8cd63dd83412e60ef3f0379f02fc03a5e2e4ea633d23d15fab174c66c01d76f95555c07b2baba796533725176a7dde7d97f9c3637fd399cf47f4a61ec9c53193fad6cfa8b21ed2f01ed0087d4cef4b1bf6556693e62efb1fdf3b37dcbd5fa5a5733dfa7abb9fdb580407ffa2e7a69242d035581b45c1ac6bc271b3a6cb872c4c851a3c78c0d5b408547445a4276daab26bdeeebe7df61cf4a39e71acab9e481618172ecef9580a591f817d35572265c5723026fb4f4f30ca725d48cb9d5dc6c7e64ae31979993cdc88cda512b6a468f500d2ab3ac55201ff2d68b277628524cafdcd87ee1f219c1f53d576dc253bfb97cef1f577a7806dc7e65d48837df3e70e13dcfd71a373ef9febd59c41eedb1b2906bbb53f7ec3c347c58fd15c13f4cc3bf1fbedcfdb2e2c7e10f03d6cc19ebbd53fcf6b3f620c7fca7635ddfa73c5f1f56f6d98425b76efc38ffbb27860f3f2bbafd75f887fedf5d0ff2967bf55f6050576f54bcb6fbeed8dd03ef7dbccfebb5b9b6ff9892b2fb81cb8da8a3ebc6fafdbdcf1717d7bdb6bcd4f0a96ed5c2922153e7de9c76b264ddfabab3af4d9b38f1c3a3a35eb23fded67a212cdd31f1dbbfdd7e2da87cdfae401f69d889ef5f8a70bd354b66bcd4f09a4df527cb2676f9d7fcdaf96fb9d7334edbb357b299b65f11b176ca94e4d79ca69edd5c20cfe2cd083de999307cd18c0b9b875f18346defedf9134fcd1f9fbd6ded9cc5332e4ccf6e1834d13e426697909291b0e2ece469915b9a674dff7ae6bc886f4fe777db73a0c8d5f4897f1f8df15efb46b735c5d7736fb46c7df6cb47fbdf7a7d246f34f1c5a8c5ca98d33fe7dc4d3eab3eaadfa76a9efeccf3bd8ae5ef954ea8dc73d11cd077e5f1ef3b7452fe68eba1652fedfbfba9add15df554507ad66df5088eee22f9d725ae5c3be4b3c5ee393b7446f8bc5d11ef793babeb75564e7ff3ed8059208774719dfefaea2f60ae738c4191d53dc6742bf31eb82f61cd47409e64efbb1d84f9e35f8075bc5cf43fc4c88fc0df975f32660bb87ec758c5e55cdcd56d2d6bbe6c65eff2f51fa7cd4dff28277d6fd66e259bee3bacbc7214fa43482712107bb0337decefaa55fab3d8f4b13dddd87fd29f747a18842fae77b93cd3a1eb25e8b8580b157d112fb818f749f4c57bc1855de57fa44cff87ae5ddf9acd185f03b6b1083fd769c6f7db58f72d56d87301d6638f3afa4ee66516c1bed32298774c5e42f6fda8a0b38fc5575923fb5ef909fb7eb3a9abfbffd72f8295dd94e7ff36a2c792bafb3784c8f67f3547ffbdfe7bfdf7faeff5dfebff4fd71d765cb5c8668e143777952e1c398823c770e4648e9cc5910b397225476672e4468edcc7914738f20c475ee5c83b1cd9cc91e296aed285230771e4188e9ccc91b338722147aee4c84c8edcc891fb38f208479ee1c8ab1c7987239b3952fcb4ab74e1c8411c3986232773e42c8e5cc8912b393293233772e43e8e3cc2916738f22a47dee1c8668e143feb2a5d387210478ee1c8c91c398b231772e44a8ecce4c88d1cb98f238f70e4198ebcca917738b29923c5ad5da50b470ee2c8311c3999236771e4428e5cc991991cb99123f771e4118e3cc3915739f20e473673a4b8adab74e1c8411c3986232773e42c8e5cc8912b393293233772e43e8e3cc2916738f22a47dee1c8668e14b777952e1c398823c770e4648e9cc5910b397225476672e4468edcc7914738f20c475ee5c83b1cd9cc91627357e9c2918338720c474ee6c8591cb9902357726426476ee4c87d1c798423cf70e4558eb45c66f31fbdeffde7afff747cffa9ebf5ffcbddd77ec9b46b4961d7f675f995d5ab078cacf9e93f23cd5d2efc5e43f5f6eb01017f90c33f73672ecb7ba3ce9899f7463ecb6c3aca95fcc05bf6a711fdc5ebff019e470e55';
+
+
+class ISPResponse {
+ static ISPOperation = {
+ ISP_ECHO: 0xc1,
+ ISP_NOP: 0xc2,
+ ISP_MEMORY_WRITE: 0xc3,
+ ISP_MEMORY_READ: 0xc4,
+ ISP_MEMORY_BOOT: 0xc5,
+ ISP_DEBUG_INFO: 0xd1,
+ ISP_CHANGE_BAUDRATE: 0xc6,
+ };
+
+ static ErrorCode = {
+ ISP_RET_DEFAULT: 0,
+ ISP_RET_OK: 0xe0,
+ ISP_RET_BAD_DATA_LEN: 0xe1,
+ ISP_RET_BAD_DATA_CHECKSUM: 0xe2,
+ ISP_RET_INVALID_COMMAND: 0xe3,
+ };
+
+ static parse(data) {
+ let op = parseInt(data[1]);
+ let reason = parseInt(data[2]);
+ let text = '';
+
+ try {
+ for (let code in ISPResponse.ISPOperation) {
+ if (
+ ISPResponse.ISPOperation[code] === op &&
+ ISPResponse.ISPOperation.ISP_DEBUG_INFO
+ ) {
+ text = String.fromCharCode(...text.slice(2));
+ }
+ }
+ } catch (error) {
+ Debug.error(error);
+ }
+
+ return [op, reason, text];
+ }
+}
+
+class FlashModeResponse {
+ static Operation = {
+ ISP_DEBUG_INFO: 0xd1,
+ ISP_NOP: 0xd2,
+ ISP_FLASH_ERASE: 0xd3,
+ ISP_FLASH_WRITE: 0xd4,
+ ISP_REBOOT: 0xd5,
+ ISP_UARTHS_BAUDRATE_SET: 0xd6,
+ FLASHMODE_FLASH_INIT: 0xd7,
+ };
+
+ static ErrorCode = {
+ ISP_RET_DEFAULT: 0x00,
+ ISP_RET_OK: 0xe0,
+ ISP_RET_BAD_DATA_LEN: 0xe1,
+ ISP_RET_BAD_DATA_CHECKSUM: 0xe2,
+ ISP_RET_INVALID_COMMAND: 0xe3,
+ ISP_RET_BAD_INITIALIZATION: 0xe4,
+ };
+
+ static parse(data) {
+ let op = parseInt(data[1]);
+ let reason = parseInt(data[2]);
+ let text = '';
+
+ if (op === FlashModeResponse.Operation.ISP_DEBUG_INFO)
+ text = String.fromCharCode(...text.slice(2));
+ return [op, reason, text];
+ }
+}
+
+class MAIXLoader {
+ ISP_RECEIVE_TIMEOUT = 5000;
+ MAX_RETRY_TIMES = 10;
+ ISP_FLASH_SECTOR_SIZE = 4096;
+ DATAFRAME_SIZE = 1024;
+ ISP_FLASH_DATA_FRAME_SIZE = this.ISP_FLASH_SECTOR_SIZE * 16;
+ ISP_BYTEARRAY = ISP_PROG.match(/.{1,2}/g).map((e) => parseInt(e, 16));
+ ISP_COMPRESSED = pako.inflate(new Uint8Array(this.ISP_BYTEARRAY));
+
+ #port_ = null;
+ #readedBuffer_ = [];
+ #events_ = new Events(['message', 'replaceMessage']);
+
+ constructor(port) {
+ this.#port_ = port;
+ this.#port_.bind('onByte', (data) => {
+ this.#readedBuffer_.push(data);
+ });
+ }
+
+ bind(...args) {
+ return this.#events_.bind(...args);
+ }
+
+ message(message) {
+ this.#events_.run('message', message);
+ }
+
+ replaceMessage(lineNumber, message) {
+ this.#events_.run('replaceMessage', lineNumber, message);
+ }
+
+ async write(packet) {
+ let handlePacket = [];
+
+ packet.forEach((e) => {
+ if (e === 0xc0) {
+ handlePacket.push(0xdb, 0xdc);
+ } else if (e === 0xdb) {
+ handlePacket.push(0xdb, 0xdd);
+ } else {
+ handlePacket.push(e);
+ }
+ });
+
+ const uint8 = new Uint8Array([0xc0, ...handlePacket, 0xc0]);
+ await this.#port_.sendBuffer(uint8);
+ }
+
+ async resetToISP() {
+ await this.#port_.setDTR(0);
+ await this.#port_.setRTS(0);
+ await this.#port_.sleep(100);
+
+ await this.#port_.setDTR(0);
+ await this.#port_.setRTS(1);
+ await this.#port_.sleep(100);
+
+ await this.#port_.setDTR(1);
+ await this.#port_.setRTS(0);
+ await this.#port_.sleep(100);
+ }
+
+ async resetToBoot() {
+ await this.#port_.setDTR(0);
+ await this.#port_.setRTS(0);
+ await this.#port_.sleep(100);
+
+ await this.#port_.setDTR(0);
+ await this.#port_.setRTS(1);
+ await this.#port_.sleep(100);
+
+ await this.#port_.setRTS(0);
+ await this.#port_.setDTR(0);
+ await this.#port_.sleep(100);
+ }
+
+ async recvOneReturn() {
+ const startTime = Date.now();
+ let output = [];
+ let times = 0;
+ while (true) {
+ if (!this.#port_.isOpened()) {
+ throw new Error('The port has been closed');
+ }
+ if (Date.now() - startTime > this.ISP_RECEIVE_TIMEOUT) {
+ throw new Error('Data wait timeout');
+ }
+ if (this.#readedBuffer_.length) {
+ const byte = this.#readedBuffer_.shift();
+ if (byte === 0xc0) {
+ times += 1;
+ if (times === 2) {
+ output.push(byte);
+ break;
+ }
+ }
+ if (times > 0) {
+ output.push(byte);
+ }
+ } else {
+ await this.#port_.sleep(10);
+ }
+ }
+ return output;
+ }
+
+ async recvDebug() {
+ const resp = await this.recvOneReturn();
+ const result = ISPResponse.parse(resp);
+ const op = result[0];
+ const reason = result[1];
+ if (
+ reason !== ISPResponse.ErrorCode.ISP_RET_DEFAULT &&
+ reason !== ISPResponse.ErrorCode.ISP_RET_OK
+ ) {
+ return false;
+ }
+ return true;
+ }
+
+ async greeting() {
+ await this.#port_.sendBuffer(
+ new Uint8Array([
+ 0xc0, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc0,
+ ])
+ );
+ ISPResponse.parse(await this.recvOneReturn());
+ }
+
+ async flashGreeting() {
+ let retryCount = 0;
+ while (true) {
+ await this.#port_.sendBuffer(
+ new Uint8Array([
+ 0xc0, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc0,
+ ])
+ );
+ retryCount++;
+
+ let op = 0;
+ let reason = 0;
+
+ const result = FlashModeResponse.parse(await this.recvOneReturn());
+ op = result[0];
+ reason = result[1];
+
+ if (
+ op === FlashModeResponse.Operation.ISP_NOP &&
+ reason === FlashModeResponse.ErrorCode.ISP_RET_OK
+ ) {
+ this.message('[INFO] Boot to Flashmode Successfully\n');
+ break;
+ } else {
+ if (retryCount > this.MAX_RETRY_TIMES) {
+ throw new Error('Failed to Connect to K210\'s Stub');
+ }
+ await this.#port_.sleep(100);
+ }
+ }
+ }
+
+ getProgressMessage(name, percent) {
+ const sended = parseInt(percent * 45);
+ const left = percent === 0 ? '' : Array(sended).fill('=').join('');
+ const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
+ return `${name}: |${left}${right}| ${(percent * 100).toFixed(1)}%`;
+ }
+
+ async flashDataFrame(data, address = 0x80000000) {
+ data = Array.from(data);
+ const dataLength = data.length;
+ let sendedLength = 0;
+ const len = Math.ceil(dataLength / this.DATAFRAME_SIZE);
+ this.message('\n');
+ for (let i = 0; i < len; i++) {
+ let start = i * this.DATAFRAME_SIZE;
+ let end = Math.min(start + this.DATAFRAME_SIZE, dataLength);
+ const chunk = data.slice(start, end);
+ while (true) {
+ const opP = new Uint8Array(
+ struct('
0) {
+ this.replaceMessage(-1, message);
+ } else {
+ this.message(message);
+ }
+ address += this.DATAFRAME_SIZE;
+ break;
+ }
+ }
+ }
+ this.message('\n\n');
+ }
+
+ async installFlashBootloader(data) {
+ await this.flashDataFrame(data, 0x80000000);
+ }
+
+ async installDefaultFlashBootloader() {
+ await this.installFlashBootloader(this.ISP_COMPRESSED);
+ }
+
+ async changeBaudrate(baudrate = 2000000) {
+ this.message(`[INFO] Selected Baudrate: ${baudrate}\n`);
+ const baudrateP = new Uint8Array(struct(' this.MAX_RETRY_TIMES) {
+ throw new Error('Failed to initialize flash');
+ }
+ await this.#port_.sleep(100);
+ }
+ }
+ }
+
+ async sha256Digest(data) {
+ const hashBuffer = await crypto.subtle.digest('SHA-256', new Uint8Array(data));
+ return new Uint8Array(hashBuffer);
+ }
+
+ formatHex(num) {
+ return '0x' + num.toString(16).padStart(8, '0');
+ }
+
+ async flashFirmware(firmware, aesKey = null, address = 0, sha256Prefix = true, filename = '') {
+ this.message(`[INFO] Writing ${filename} into ${this.formatHex(address)}\n`);
+ let firmwareBin = Array.from(new Uint8Array(firmware));
+ if (sha256Prefix) {
+ const aesCipherFlag = 0x00;
+ const dataLengthInfo = Array.from(new Uint8Array(struct(' 0) {
+ this.replaceMessage(-1, message);
+ } else {
+ this.message(`\n${message}`);
+ }
+ address += this.ISP_FLASH_DATA_FRAME_SIZE;
+ break;
+ }
+ }
+ }
+ this.message('\n\n');
+ }
+
+ dispose() {
+ this.#port_ = null;
+ this.#events_.reset();
+ this.#events_ = null;
+ this.#readedBuffer_ = null;
+ }
+}
+
+class KFlash {
+ #port_ = null;
+ #loader_ = null;
+
+ constructor(port) {
+ this.#port_ = port;
+ this.#loader_ = new MAIXLoader(port);
+ }
+
+ bind(...args) {
+ return this.#loader_.bind(...args);
+ }
+
+ async enter() {
+ await this.#port_.open(115200);
+ }
+
+ async write(firmware, address = 0x0, sha256Prefix = true, filename = '') {
+ this.#loader_.message('[INFO] Trying to Enter the ISP Mode...');
+ let retryCount = 0;
+ while (true) {
+ try {
+ retryCount += 1;
+ if (retryCount > 15) {
+ throw new Error('No valid Kendryte K210 found in Auto Detect, Check Your Connection');
+ }
+ try {
+ this.#loader_.message('.');
+ await this.#loader_.resetToISP();
+ await this.#loader_.greeting();
+ break;
+ } catch (error) {
+ console.log(error);
+ throw new Error('Timeout error');
+ }
+ } catch {
+ throw new Error('Greeting fail, check serial port');
+ }
+ }
+ this.#loader_.message('\n');
+
+ await this.#loader_.installDefaultFlashBootloader();
+ await this.#loader_.boot();
+ this.#loader_.message('[INFO] Wait For 0.1 second for ISP to Boot\n');
+ await this.#port_.sleep(100);
+ await this.#loader_.flashGreeting();
+ await this.#loader_.changeBaudrate();
+ this.#loader_.message('[INFO] Baudrate changed, greeting with ISP again ...\n');
+ await this.#loader_.flashGreeting();
+ await this.#loader_.initFlash();
+ await this.#loader_.flashFirmware(firmware, null, address, sha256Prefix, filename);
+ await this.#loader_.resetToBoot();
+ }
+
+ dispose() {
+ this.#loader_.dispose();
+ this.#loader_ = null;
+ this.#port_ = null;
+ }
+}
+
+Web.KFlash = KFlash;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/serial-transport.js b/mixly/common/modules/mixly-modules/web/serial-transport.js
new file mode 100644
index 00000000..05c9eacb
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/serial-transport.js
@@ -0,0 +1,231 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.SerialTransport');
+
+const { Web } = Mixly;
+
+
+class Transport {
+ slipReaderEnabled = false;
+ baudrate = 0;
+
+ traceLog = '';
+ lastTraceTime = Date.now();
+
+ buffer = new Uint8Array(0);
+ serial = null;
+ tracing = false;
+
+ _DTR_state = false;
+
+ SLIP_END = 0xc0;
+ SLIP_ESC = 0xdb;
+ SLIP_ESC_END = 0xdc;
+ SLIP_ESC_ESC = 0xdd;
+
+ constructor(serial, tracing = false, enableSlipReader = true) {
+ this.serial = serial;
+ this.tracing = tracing;
+ this.slipReaderEnabled = enableSlipReader;
+
+ this.serial.bind('onBuffer', (data) => {
+ if (!(data instanceof Uint8Array)) {
+ data = new Uint8Array(data);
+ }
+ this.buffer = this.appendArray(this.buffer, data);
+
+ if (this.tracing) {
+ this.trace(`Read ${data.length} bytes: ${this.hexConvert(data)}`);
+ }
+ });
+ }
+
+ getInfo() {
+ const VID = this.serial.getVID?.();
+ const PID = this.serial.getPID?.();
+ return (VID && PID)
+ ? `WebDevice VendorID 0x${VID.toString(16)} ProductID 0x${PID.toString(16)}`
+ : '';
+ }
+
+ getPid() {
+ return this.serial.getPID?.();
+ }
+
+ trace(message) {
+ const delta = Date.now() - this.lastTraceTime;
+ const traceMessage = `TRACE +${delta}ms ${message}`;
+ this.lastTraceTime = Date.now();
+
+ // console.log(traceMessage);
+ this.traceLog += traceMessage + '\n';
+ }
+
+ async returnTrace() {
+ await navigator.clipboard.writeText(this.traceLog);
+ }
+
+ hexify(arr) {
+ return Array.from(arr)
+ .map(b => b.toString(16).padStart(2, '0'))
+ .join(' ')
+ .padEnd(16, ' ');
+ }
+
+ hexConvert(arr, autoSplit = true) {
+ if (!autoSplit || arr.length <= 16) {
+ return this.hexify(arr);
+ }
+
+ let out = '';
+ let s = arr;
+ while (s.length) {
+ const line = s.slice(0, 16);
+ const ascii = String.fromCharCode(...line)
+ .replace(/[^\x20-\x7E]/g, '.');
+ s = s.slice(16);
+ out += `\n ${this.hexify(line.slice(0,8))} ${this.hexify(line.slice(8))} | ${ascii}`;
+ }
+ return out;
+ }
+
+ appendArray(a, b) {
+ const out = new Uint8Array(a.length + b.length);
+ out.set(a);
+ out.set(b, a.length);
+ return out;
+ }
+
+ inWaiting() {
+ return this.buffer.length;
+ }
+
+ async flushInput() {
+ this.buffer = new Uint8Array(0);
+ }
+
+ slipWriter(data) {
+ const out = [this.SLIP_END];
+ for (const b of data) {
+ if (b === this.SLIP_END) out.push(this.SLIP_ESC, this.SLIP_ESC_END);
+ else if (b === this.SLIP_ESC) out.push(this.SLIP_ESC, this.SLIP_ESC_ESC);
+ else out.push(b);
+ }
+ out.push(this.SLIP_END);
+ return new Uint8Array(out);
+ }
+
+ async write(data) {
+ const out = this.slipReaderEnabled ? this.slipWriter(data) : data;
+
+ if (this.tracing) {
+ this.trace(`Write ${out.length} bytes: ${this.hexConvert(out)}`);
+ }
+ await this.serial.sendBuffer(out);
+ }
+
+ async newRead(numBytes, timeout) {
+ const start = Date.now();
+
+ while (this.buffer.length < numBytes) {
+ if (!this.serial.isOpened()) return null;
+ if (Date.now() - start > timeout) break;
+ await this.sleep(5);
+ }
+
+ const out = this.buffer.slice(0, numBytes);
+ this.buffer = this.buffer.slice(numBytes);
+ return out;
+ }
+
+ async *read(timeout = 1000) {
+ let partial = null;
+ let escaping = false;
+ let successfulSlip = false;
+
+ while (true) {
+ const chunk = await this.newRead(1, timeout);
+ if (!chunk || chunk.length === 0) {
+ const msg = partial
+ ? 'Packet content transfer stopped'
+ : successfulSlip
+ ? 'Serial stream stopped'
+ : 'No serial data received';
+ throw new Error(msg);
+ }
+
+ const byte = chunk[0];
+
+ if (partial === null) {
+ if (byte === this.SLIP_END) {
+ partial = new Uint8Array(0);
+ }
+ continue;
+ }
+
+ if (escaping) {
+ escaping = false;
+ if (byte === this.SLIP_ESC_END)
+ partial = this.appendArray(partial, new Uint8Array([this.SLIP_END]));
+ else if (byte === this.SLIP_ESC_ESC)
+ partial = this.appendArray(partial, new Uint8Array([this.SLIP_ESC]));
+ else
+ throw new Error(`Invalid SLIP escape 0xdb 0x${byte.toString(16)}`);
+ continue;
+ }
+
+ if (byte === this.SLIP_ESC) {
+ escaping = true;
+ } else if (byte === this.SLIP_END) {
+ if (this.tracing) {
+ this.trace(`Received packet: ${this.hexConvert(partial)}`);
+ }
+ successfulSlip = true;
+ yield partial;
+ partial = null;
+ } else {
+ partial = this.appendArray(partial, new Uint8Array([byte]));
+ }
+ }
+ }
+
+ detectPanicHandler(input) {
+ const text = new TextDecoder().decode(input);
+ const guru = /G?uru Meditation Error/;
+ const fatal = /F?atal exception \(\d+\)/;
+ if (guru.test(text) || fatal.test(text)) {
+ throw new Error('Guru Meditation Error detected');
+ }
+ }
+
+ async setRTS(state) {
+ await this.serial.setRTS(state);
+ await this.setDTR(this._DTR_state);
+ }
+
+ async setDTR(state) {
+ this._DTR_state = state;
+ await this.serial.setDTR(state);
+ }
+
+ async connect(baud = 115200) {
+ await this.serial.open(baud);
+ this.baudrate = baud;
+ if (this.tracing) this.trace(`Serial opened @${baud}`);
+ }
+
+ async disconnect() {
+ await this.serial.close();
+ this.buffer = new Uint8Array(0);
+ if (this.tracing) this.trace('Serial closed');
+ }
+
+ sleep(ms) {
+ return new Promise(r => setTimeout(r, ms));
+ }
+}
+
+Web.SerialTransport = Transport;
+
+});
diff --git a/mixly/common/modules/mixly-modules/web/serial.js b/mixly/common/modules/mixly-modules/web/serial.js
new file mode 100644
index 00000000..62017376
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/serial.js
@@ -0,0 +1,215 @@
+goog.loadJs('web', () => {
+
+goog.require('path');
+goog.require('Mixly.Config');
+goog.require('Mixly.Env');
+goog.require('Mixly.Msg');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Serial');
+goog.require('Mixly.LayerExt');
+goog.require('Mixly.HTMLTemplate');
+goog.require('Mixly.Web.SerialPort');
+goog.require('Mixly.Web.USB');
+goog.require('Mixly.Web.USBMini');
+goog.require('Mixly.Web.HID');
+goog.provide('Mixly.Web.Serial');
+
+const {
+ Config,
+ Env,
+ Msg,
+ Registry,
+ Serial,
+ LayerExt,
+ HTMLTemplate,
+ Web
+} = Mixly;
+
+const {
+ SerialPort,
+ USB,
+ USBMini,
+ HID
+} = Web;
+
+const { BOARD } = Config;
+
+const platform = goog.platform();
+const fullPlatform = goog.fullPlatform();
+
+
+class WebSerial extends Serial {
+ static {
+ this.devicesRegistry = new Registry();
+ this.type = Serial.type;
+ this.DEVICES_SELECT_LAYER = new HTMLTemplate(
+ goog.readFileSync(path.join(Env.templatePath, 'html/devices-select-layer.html'))
+ );
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.refreshPorts = function () {
+ Serial.refreshPorts();
+ }
+
+ this.requestPort = function () {
+ if (this.devicesRegistry.length() < 1) {
+ throw Error('can not find any device handler');
+ } else if (this.devicesRegistry.length() === 1) {
+ const keys = this.devicesRegistry.keys();
+ return this.devicesRegistry.getItem(keys[0]).requestPort();
+ }
+ const msg = {
+ serialMsg: Msg.Lang['layer.devices.serial'],
+ serialStatus: this.devicesRegistry.hasKey('serial') ? '' : 'disabled',
+ hidMsg: Msg.Lang['layer.devices.hid'],
+ hidStatus: this.devicesRegistry.hasKey('hid') ? '' : 'disabled',
+ usbMsg: Msg.Lang['layer.devices.usb'],
+ usbStatus: (
+ this.devicesRegistry.hasKey('usb') || this.devicesRegistry.hasKey('usbmini')
+ ) ? '' : 'disabled'
+ };
+ return new Promise((resolve, reject) => {
+ let selected = false;
+ const layerNum = LayerExt.open({
+ title: [Msg.Lang['layer.devices.select'], '36px'],
+ area: ['400px', '150px'],
+ max: false,
+ min: false,
+ content: this.DEVICES_SELECT_LAYER.render(msg),
+ shade: LayerExt.SHADE_ALL,
+ resize: false,
+ success: function (layero, index) {
+ $(layero).on('click', 'button', (event) => {
+ selected = true;
+ layer.close(layerNum);
+ const $btn = $(event.currentTarget);
+ let mId = $btn.attr('m-id');
+ if (mId === 'usb' && WebSerial.devicesRegistry.hasKey('usbmini')) {
+ mId = 'usbmini';
+ }
+ const Device = WebSerial.devicesRegistry.getItem(mId);
+ Device.requestPort().then(resolve).catch(reject);
+ });
+ },
+ end: function () {
+ if (!selected) {
+ reject('user not select any device');
+ }
+ $(`#layui-layer-shade${layerNum}`).remove();
+ }
+ });
+ });
+ }
+
+ this.getHandler = function (device) {
+ if (device.constructor.name === 'SerialPort') {
+ return SerialPort;
+ } else if (device.constructor.name === 'HIDDevice') {
+ return HID;
+ } else if (device.constructor.name === 'USBDevice') {
+ if (this.devicesRegistry.hasKey('usbmini')) {
+ return USBMini;
+ } else {
+ return USB;
+ }
+ }
+ return null;
+ }
+
+ this.getPort = function (name) {
+ return Serial.nameToPortRegistry.getItem(name);
+ }
+
+ this.addPort = function (device) {
+ const handler = this.getHandler(device);
+ if (!handler) {
+ return;
+ }
+ handler.addPort(device);
+ }
+
+ this.removePort = function (device) {
+ const handler = this.getHandler(device);
+ if (!handler) {
+ return;
+ }
+ handler.removePort(device);
+ }
+
+ this.addEventsListener = function () {}
+
+ this.init = function () {
+ if (Env.hasSocketServer) {
+ return;
+ }
+ if (platform === 'win32' && fullPlatform !== 'win10') {
+ if (BOARD?.web?.devices?.hid) {
+ this.devicesRegistry.register('hid', HID);
+ HID.init();
+ }
+ if (BOARD?.web?.devices?.serial) {
+ this.devicesRegistry.register('serial', SerialPort);
+ SerialPort.init();
+ }
+ if (BOARD?.web?.devices?.usb) {
+ if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ this.devicesRegistry.register('usb', USB);
+ USB.init();
+ }
+ }
+ } else if (platform === 'mobile') {
+ if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ this.devicesRegistry.register('usb', USB);
+ USB.init();
+ } else {
+ this.devicesRegistry.register('usbmini', USBMini);
+ USBMini.init();
+ }
+ } else {
+ if (BOARD?.web?.devices?.serial) {
+ this.devicesRegistry.register('serial', SerialPort);
+ SerialPort.init();
+ } else if (BOARD?.web?.devices?.usb) {
+ if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
+ this.devicesRegistry.register('usb', USB);
+ USB.init();
+ } else {
+ this.devicesRegistry.register('usbmini', USBMini);
+ USBMini.init();
+ }
+ } else if (BOARD?.web?.devices?.hid) {
+ this.devicesRegistry.register('hid', HID);
+ HID.init();
+ }
+ }
+ }
+
+ this.init();
+ }
+
+ constructor(port) {
+ super(port);
+ const device = WebSerial.getPort(port);
+ const handler = WebSerial.getHandler(device);
+ if (!handler) {
+ return;
+ }
+ return new handler(port);
+ }
+}
+
+
+Web.Serial = WebSerial;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/serialport.js b/mixly/common/modules/mixly-modules/web/serialport.js
new file mode 100644
index 00000000..ebadf309
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/serialport.js
@@ -0,0 +1,279 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Debug');
+goog.require('Mixly.Config');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.SerialPort');
+
+const {
+ Serial,
+ Debug,
+ Config,
+ Web
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+class WebSerialPort extends Serial {
+ static {
+ this.type = 'serialport';
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.refreshPorts = function () {
+ Serial.refreshPorts();;
+ }
+
+ this.requestPort = async function () {
+ let options = SELECTED_BOARD?.web?.devices?.serial;
+ if (!options || typeof(options) !== 'object') {
+ options = {
+ filters: []
+ };
+ }
+ const serialport = await navigator.serial.requestPort(options);
+ this.addPort(serialport);
+ this.refreshPorts();
+ }
+
+ this.getPort = function (name) {
+ return Serial.nameToPortRegistry.getItem(name);
+ }
+
+ this.addPort = function (serialport) {
+ if (this.portToNameRegistry.hasKey(serialport)) {
+ return;
+ }
+ let name = '';
+ for (let i = 1; i <= 20; i++) {
+ name = `serial${i}`;
+ if (Serial.nameToPortRegistry.hasKey(name)) {
+ continue;
+ }
+ break;
+ }
+ Serial.portToNameRegistry.register(serialport, name);
+ Serial.nameToPortRegistry.register(name, serialport);
+ }
+
+ this.removePort = function (serialport) {
+ if (!Serial.portToNameRegistry.hasKey(serialport)) {
+ return;
+ }
+ const name = Serial.portToNameRegistry.getItem(serialport);
+ if (!name) {
+ return;
+ }
+ Serial.portToNameRegistry.unregister(serialport);
+ Serial.nameToPortRegistry.unregister(name);
+ }
+
+ this.addEventsListener = function () {
+ navigator?.serial?.addEventListener('connect', (event) => {
+ this.addPort(event.target);
+ this.refreshPorts();
+ });
+
+ navigator?.serial?.addEventListener('disconnect', (event) => {
+ this.removePort(event.target);
+ this.refreshPorts();
+ });
+ }
+
+ this.init = function () {
+ navigator?.serial?.getPorts().then((serialports) => {
+ for (let serialport of serialports) {
+ this.addPort(serialport);
+ }
+ });
+ this.addEventsListener();
+ }
+ }
+
+ #serialport_ = null;
+ #keepReading_ = null;
+ #reader_ = null;
+ #writer_ = null;
+ #stringTemp_ = '';
+ constructor(port) {
+ super(port);
+ this.#serialport_ = WebSerialPort.getPort(port);
+ }
+
+ #addEventsListener_() {
+ this.#addReadEventListener_();
+ }
+
+ async #addReadEventListener_() {
+ const { readable } = this.#serialport_;
+ while (readable && this.#keepReading_) {
+ this.#reader_ = readable.getReader();
+ try {
+ while (true) {
+ const { value, done } = await this.#reader_.read();
+ value !== undefined && this.onBuffer(value);
+ if (done) {
+ break;
+ }
+ }
+ } catch (error) {
+ this.#keepReading_ = false;
+ Debug.error(error);
+ } finally {
+ this.#reader_ && this.#reader_.releaseLock();
+ await this.close();
+ }
+ }
+ }
+
+ async open(baud) {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPortName = this.getPortName();
+ if (!portsName.includes(currentPortName)) {
+ throw Error('no device available');
+ return;
+ }
+ if (this.isOpened()) {
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+ this.#serialport_ = WebSerialPort.getPort(currentPortName);
+ await this.#serialport_.open({ baudRate: baud });
+ super.open(baud);
+ super.setBaudRate(baud);
+ this.#keepReading_ = true;
+ this.onOpen();
+ this.#addEventsListener_();
+ }
+
+ async #waitForUnlock_(timeout) {
+ while (
+ (this.#serialport_.readable && this.#serialport_.readable.locked) ||
+ (this.#serialport_.writable && this.#serialport_.writable.locked)
+ ) {
+ await this.sleep(timeout);
+ }
+ }
+
+ async close() {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ if (this.#serialport_.readable?.locked) {
+ this.#keepReading_ = false;
+ await this.#reader_?.cancel();
+ }
+ await this.#waitForUnlock_(400);
+ this.#reader_ = undefined;
+ await this.#serialport_.close();
+ this.#stringTemp_ = '';
+ this.onClose(1);
+ }
+
+ async setBaudRate(baud) {
+ if (!this.isOpened()
+ || this.getRawBaudRate() === baud
+ || !this.baudRateIsLegal(baud)) {
+ return;
+ }
+ await this.close();
+ await this.open(baud);
+ }
+
+ async sendString(str) {
+ const buffer = this.encode(str);
+ return this.sendBuffer(buffer);
+ }
+
+ async sendBuffer(buffer) {
+ const { writable } = this.#serialport_;
+ const writer = writable.getWriter();
+ if (buffer.constructor.name !== 'Uint8Array') {
+ buffer = new Uint8Array(buffer);
+ }
+ try {
+ await writer.write(buffer);
+ writer.releaseLock();
+ } catch (error) {
+ writer.releaseLock();
+ throw Error(error);
+ }
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ if (!this.isOpened()) {
+ return;
+ }
+ await this.#serialport_.setSignals({
+ dataTerminalReady: dtr,
+ requestToSend: rts
+ });
+ super.setDTRAndRTS(dtr, rts);
+ }
+
+ async setDTR(dtr) {
+ if (!this.isOpened()) {
+ return;
+ }
+ await this.#serialport_.setSignals({
+ dataTerminalReady: dtr
+ });
+ super.setDTR(dtr);
+ }
+
+ async setRTS(rts) {
+ if (!this.isOpened()) {
+ return;
+ }
+ await this.#serialport_.setSignals({
+ requestToSend: rts
+ });
+ super.setRTS(rts);
+ }
+
+ getVID() {
+ const info = this.#serialport_.getInfo();
+ return info.usbVendorId;
+ }
+
+ getPID() {
+ const info = this.#serialport_.getInfo();
+ return info.usbProductId;
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ if (['\r', '\n'].includes(char)) {
+ super.onString(this.#stringTemp_);
+ this.#stringTemp_ = '';
+ } else {
+ this.#stringTemp_ += char;
+ }
+ }
+ }
+}
+
+Web.SerialPort = WebSerialPort;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/usb-mini.js b/mixly/common/modules/mixly-modules/web/usb-mini.js
new file mode 100644
index 00000000..44daa549
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/usb-mini.js
@@ -0,0 +1,350 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly.Serial');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Config');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.USBMini');
+
+const {
+ Serial,
+ Registry,
+ Config,
+ Web
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+class USBMini extends Serial {
+ static {
+ this.type = 'usb';
+ this.serialNumberToNameRegistry = new Registry();
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.refreshPorts = function () {
+ Serial.refreshPorts();;
+ }
+
+ this.requestPort = async function () {
+ let options = SELECTED_BOARD?.web?.devices?.usb;
+ if (!options || typeof(options) !== 'object') {
+ options = {
+ filters: []
+ };
+ }
+ const device = await navigator.usb.requestDevice(options);
+ this.addPort(device);
+ this.refreshPorts();
+ }
+
+ this.getPort = function (name) {
+ return Serial.nameToPortRegistry.getItem(name);
+ }
+
+ this.addPort = function (device) {
+ if (Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ const { serialNumber } = device;
+ let name = this.serialNumberToNameRegistry.getItem(serialNumber);
+ if (!name) {
+ for (let i = 1; i <= 20; i++) {
+ name = `usb${i}`;
+ if (Serial.nameToPortRegistry.hasKey(name)) {
+ continue;
+ }
+ break;
+ }
+ this.serialNumberToNameRegistry.register(serialNumber, name);
+ }
+ Serial.portToNameRegistry.register(device, name);
+ Serial.nameToPortRegistry.register(name, device);
+ }
+
+ this.removePort = function (device) {
+ if (!Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ const name = Serial.portToNameRegistry.getItem(device);
+ if (!name) {
+ return;
+ }
+ Serial.portToNameRegistry.unregister(device);
+ Serial.nameToPortRegistry.unregister(name);
+ }
+
+ this.addEventsListener = function () {
+ navigator?.usb?.addEventListener('connect', (event) => {
+ this.addPort(event.device);
+ this.refreshPorts();
+ });
+
+ navigator?.usb?.addEventListener('disconnect', (event) => {
+ event.device.onclose && event.device.onclose();
+ this.removePort(event.device);
+ this.refreshPorts();
+ });
+ }
+
+ this.init = function () {
+ navigator?.usb?.getDevices().then((devices) => {
+ for (let device of devices) {
+ this.addPort(device);
+ }
+ });
+ this.addEventsListener();
+ }
+ }
+
+ #device_ = null;
+ #keepReading_ = null;
+ #reader_ = null;
+ #serialPolling_ = false;
+ #stringTemp_ = '';
+ #defaultClass_ = 0xFF;
+ #defaultConfiguration_ = 1;
+ #endpointIn_ = null;
+ #endpointOut_ = null;
+ #ctrlInterfaceNumber_ = -1;
+ #dataInterfaceNumber_ = -1;
+ #dataLength_ = 64;
+ constructor(port) {
+ super(port);
+ this.#device_ = USBMini.getPort(port);
+ }
+
+ #addEventsListener_() {
+ this.#addReadEventListener_();
+ }
+
+ #addReadEventListener_() {
+ this.#reader_ = this.#startSerialRead_();
+
+ this.#device_.onclose = () => {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ this.#stringTemp_ = '';
+ this.onClose(1);
+ }
+ }
+
+ async #read_() {
+ let result;
+ if (this.#endpointIn_) {
+ result = await this.#device_.transferIn(this.#endpointIn_, 64);
+ } else {
+ result = await this.#device_.controlTransferIn({
+ requestType: 'class',
+ recipient: 'interface',
+ request: 0x01,
+ value: 0x100,
+ index: this.#dataInterfaceNumber_
+ }, 64);
+ }
+ return result?.data;
+ }
+
+ async #write_(data) {
+ if (this.#endpointOut_) {
+ await this.#device_.transferOut(this.#endpointOut_, data);
+ } else {
+ await this.#device_.controlTransferOut({
+ requestType: 'class',
+ recipient: 'interface',
+ request: 0x09,
+ value: 0x200,
+ index: this.#dataInterfaceNumber_
+ }, data);
+ }
+ }
+
+ async #startSerialRead_(serialDelay = 1) {
+ this.#serialPolling_ = true;
+ try {
+ while (this.#serialPolling_ ) {
+ const data = await this.#read_();
+ if (data !== undefined) {
+ const numberArray = Array.prototype.slice.call(new Uint8Array(data.buffer));
+ this.onBuffer(numberArray);
+ }
+ await new Promise(resolve => setTimeout(resolve, serialDelay));
+ }
+ } catch (_) {}
+ }
+
+ async open(baud) {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPortName = this.getPortName();
+ if (!portsName.includes(currentPortName)) {
+ throw new Error('no device available');
+ }
+ if (this.isOpened()) {
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+ this.#device_ = USBMini.getPort(currentPortName);
+ await this.#device_.open();
+ await this.#device_.selectConfiguration(this.#defaultConfiguration_);
+ const interfaces = this.#device_.configuration.interfaces.filter(iface => {
+ return iface.alternates[0].interfaceClass === this.#defaultClass_;
+ });
+ let selectedInterface = interfaces.find(iface => iface.alternates[0].endpoints.length > 0);
+ if (!selectedInterface) {
+ selectedInterface = interfaces[0];
+ }
+ this.#dataInterfaceNumber_ = selectedInterface.interfaceNumber;
+ const { endpoints } = selectedInterface.alternates[0];
+ for (const endpoint of endpoints) {
+ if (endpoint.direction === 'in') {
+ this.#endpointIn_ = endpoint.endpointNumber;
+ } else if (endpoint.direction === 'out') {
+ this.#endpointOut_ = endpoint.endpointNumber;
+ }
+ }
+ try {
+ await this.#device_.claimInterface(0);
+ this.#ctrlInterfaceNumber_ = 0;
+ } catch (_) {
+ this.#ctrlInterfaceNumber_ = -1;
+ }
+ await this.#device_.claimInterface(this.#dataInterfaceNumber_);
+ super.open(baud);
+ await this.setBaudRate(baud);
+ this.onOpen();
+ this.#addEventsListener_();
+ }
+
+ async close() {
+ if (!this.isOpened()) {
+ return;
+ }
+ this.#serialPolling_ = false;
+ super.close();
+ await this.#device_.close();
+ if (this.#reader_) {
+ await this.#reader_;
+ }
+ this.#device_ = null;
+ this.onClose(1);
+ }
+
+ async setBaudRate(baud) {
+ if (!this.isOpened() || this.getRawBaudRate() === baud) {
+ return;
+ }
+ if (this.#ctrlInterfaceNumber_ !== -1) {
+ const dwDTERate = new Uint8Array([
+ baud & 0xFF,
+ (baud >> 8) & 0xFF,
+ (baud >> 16) & 0xFF,
+ (baud >> 24) & 0xFF
+ ]);
+ const bCharFormat = 0x00;
+ const bParityType = 0x00;
+ const bDataBits = 0x08;
+ const lineCoding = new Uint8Array([
+ ...dwDTERate,
+ bCharFormat,
+ bParityType,
+ bDataBits
+ ]);
+ await this.#device_.controlTransferOut({
+ requestType: 'class',
+ recipient: 'interface',
+ request: 0x20,
+ value: 0x0000,
+ index: this.#ctrlInterfaceNumber_
+ }, lineCoding);
+ }
+ await super.setBaudRate(baud);
+ }
+
+ async sendString(str) {
+ const buffer = this.encode(str);
+ return this.sendBuffer(buffer);
+ }
+
+ async sendBuffer(buffer) {
+ if (buffer.constructor.name !== 'Uint8Array') {
+ buffer = new Uint8Array(buffer);
+ }
+ const len = Math.ceil(buffer.length / this.#dataLength_);
+ for (let i = 0; i < len; i++) {
+ const start = i * this.#dataLength_;
+ const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
+ const writeBuffer = buffer.slice(start, end);
+ await this.#write_(writeBuffer)
+ await this.sleep(5);
+ }
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ if (!this.isOpened()
+ || (this.getDTR() === dtr && this.getRTS() === rts)) {
+ return;
+ }
+ if (this.#ctrlInterfaceNumber_ !== -1) {
+ await this.#device_.controlTransferOut({
+ requestType: 'class',
+ recipient: 'interface',
+ request: 0x22,
+ value: dtr | (rts << 1),
+ index: this.#ctrlInterfaceNumber_
+ });
+ }
+ await super.setDTRAndRTS(dtr, rts);
+ }
+
+ async setDTR(dtr) {
+ return this.setDTRAndRTS(dtr, this.getRTS());
+ }
+
+ async setRTS(rts) {
+ return this.setDTRAndRTS(this.getDTR(), rts);
+ }
+
+ getVID() {
+ return this.#device_.vendorId;
+ }
+
+ getPID() {
+ return this.#device_.productId;
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ if (['\r', '\n'].includes(char)) {
+ super.onString(this.#stringTemp_);
+ this.#stringTemp_ = '';
+ } else {
+ this.#stringTemp_ += char;
+ }
+ }
+ }
+}
+
+Web.USBMini = USBMini;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/usb.js b/mixly/common/modules/mixly-modules/web/usb.js
new file mode 100644
index 00000000..14c489ba
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/usb.js
@@ -0,0 +1,258 @@
+goog.loadJs('web', () => {
+
+goog.require('DAPjs');
+goog.require('Mixly.Serial');
+goog.require('Mixly.Registry');
+goog.require('Mixly.Config');
+goog.require('Mixly.Web');
+goog.provide('Mixly.Web.USB');
+
+const {
+ Serial,
+ Registry,
+ Config,
+ Web
+} = Mixly;
+
+const { SELECTED_BOARD } = Config;
+
+class USB extends Serial {
+ static {
+ this.type = 'usb';
+ this.serialNumberToNameRegistry = new Registry();
+
+ this.getConfig = function () {
+ return Serial.getConfig();
+ }
+
+ this.getSelectedPortName = function () {
+ return Serial.getSelectedPortName();
+ }
+
+ this.getCurrentPortsName = function () {
+ return Serial.getCurrentPortsName();
+ }
+
+ this.refreshPorts = function () {
+ Serial.refreshPorts();;
+ }
+
+ this.requestPort = async function () {
+ let options = SELECTED_BOARD?.web?.devices?.usb;
+ if (!options || typeof(options) !== 'object') {
+ options = {
+ filters: []
+ };
+ }
+ const device = await navigator.usb.requestDevice(options);
+ this.addPort(device);
+ this.refreshPorts();
+ }
+
+ this.getPort = function (name) {
+ return Serial.nameToPortRegistry.getItem(name);
+ }
+
+ this.addPort = function (device) {
+ if (Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ const { serialNumber } = device;
+ let name = this.serialNumberToNameRegistry.getItem(serialNumber);
+ if (!name) {
+ for (let i = 1; i <= 20; i++) {
+ name = `usb${i}`;
+ if (Serial.nameToPortRegistry.hasKey(name)) {
+ continue;
+ }
+ break;
+ }
+ this.serialNumberToNameRegistry.register(serialNumber, name);
+ }
+ Serial.portToNameRegistry.register(device, name);
+ Serial.nameToPortRegistry.register(name, device);
+ }
+
+ this.removePort = function (device) {
+ if (!Serial.portToNameRegistry.hasKey(device)) {
+ return;
+ }
+ const name = Serial.portToNameRegistry.getItem(device);
+ if (!name) {
+ return;
+ }
+ Serial.portToNameRegistry.unregister(device);
+ Serial.nameToPortRegistry.unregister(name);
+ }
+
+ this.addEventsListener = function () {
+ navigator?.usb?.addEventListener('connect', (event) => {
+ this.addPort(event.device);
+ this.refreshPorts();
+ });
+
+ navigator?.usb?.addEventListener('disconnect', (event) => {
+ event.device.onclose && event.device.onclose();
+ this.removePort(event.device);
+ this.refreshPorts();
+ });
+ }
+
+ this.init = function () {
+ navigator?.usb?.getDevices().then((devices) => {
+ for (let device of devices) {
+ this.addPort(device);
+ }
+ });
+ this.addEventsListener();
+ }
+ }
+
+ #device_ = null;
+ #webUSB_ = null;
+ #dapLink_ = null;
+ #keepReading_ = null;
+ #reader_ = null;
+ #writer_ = null;
+ #stringTemp_ = '';
+ constructor(port) {
+ super(port);
+ this.#device_ = USB.getPort(port);
+ }
+
+ #addEventsListener_() {
+ this.#addReadEventListener_();
+ }
+
+ #addReadEventListener_() {
+ this.#reader_ = this.#startSerialRead_();
+
+ this.#device_.onclose = () => {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ this.#stringTemp_ = '';
+ this.onClose(1);
+ }
+ }
+
+ async #startSerialRead_(serialDelay = 1, autoConnect = false) {
+ this.#dapLink_.serialPolling = true;
+
+ while (this.#dapLink_.serialPolling) {
+ const data = await this.#dapLink_.serialRead();
+ if (data !== undefined) {
+ const numberArray = Array.prototype.slice.call(new Uint8Array(data));
+ this.onBuffer(numberArray);
+ }
+ await new Promise(resolve => setTimeout(resolve, serialDelay));
+ }
+ }
+
+ async open(baud) {
+ const portsName = Serial.getCurrentPortsName();
+ const currentPortName = this.getPortName();
+ if (!portsName.includes(currentPortName)) {
+ throw new Error('no device available');
+ }
+ if (this.isOpened()) {
+ return;
+ }
+ baud = baud ?? this.getBaudRate();
+ this.#device_ = USB.getPort(currentPortName);
+ this.#webUSB_ = new DAPjs.WebUSB(this.#device_);
+ this.#dapLink_ = new DAPjs.DAPLink(this.#webUSB_);
+ await this.#dapLink_.connect();
+ super.open(baud);
+ await this.setBaudRate(baud);
+ this.onOpen();
+ this.#addEventsListener_();
+ }
+
+ async close() {
+ if (!this.isOpened()) {
+ return;
+ }
+ super.close();
+ this.#dapLink_.stopSerialRead();
+ if (this.#reader_) {
+ await this.#reader_;
+ }
+ await this.#dapLink_.disconnect();
+ this.#dapLink_ = null;
+ await this.#webUSB_.close();
+ this.#webUSB_ = null;
+ await this.#device_.close();
+ this.#device_ = null;
+ this.onClose(1);
+ }
+
+ async setBaudRate(baud) {
+ if (!this.isOpened() || this.getRawBaudRate() === baud) {
+ return;
+ }
+ await this.#dapLink_.setSerialBaudrate(baud);
+ await super.setBaudRate(baud);
+ }
+
+ async sendString(str) {
+ return this.#dapLink_.serialWrite(str);
+ }
+
+ async sendBuffer(buffer) {
+ if (buffer.constructor.name !== 'Uint8Array') {
+ buffer.unshift(buffer.length);
+ buffer = new Uint8Array(buffer);
+ }
+ await this.#dapLink_.send(132, buffer);
+ }
+
+ async setDTRAndRTS(dtr, rts) {
+ if (!this.isOpened()
+ || (this.getDTR() === dtr && this.getRTS() === rts)) {
+ return;
+ }
+ await super.setDTRAndRTS(dtr, rts);
+ }
+
+ async setDTR(dtr) {
+ return this.setDTRAndRTS(dtr, this.getRTS());
+ }
+
+ async setRTS(rts) {
+ return this.setDTRAndRTS(this.getDTR(), rts);
+ }
+
+ getVID() {
+ return this.#device_.vendorId;
+ }
+
+ getPID() {
+ return this.#device_.productId;
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ for (let i = 0; i < buffer.length; i++) {
+ super.onByte(buffer[i]);
+ }
+ const string = this.decodeBuffer(buffer);
+ if (!string) {
+ return;
+ }
+ for (let char of string) {
+ super.onChar(char);
+ if (['\r', '\n'].includes(char)) {
+ super.onString(this.#stringTemp_);
+ this.#stringTemp_ = '';
+ } else {
+ this.#stringTemp_ += char;
+ }
+ }
+ }
+}
+
+Web.USB = USB;
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/web/web.js b/mixly/common/modules/mixly-modules/web/web.js
new file mode 100644
index 00000000..eefb7bd6
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/web/web.js
@@ -0,0 +1,6 @@
+goog.loadJs('web', () => {
+
+goog.require('Mixly');
+goog.provide('Mixly.Web');
+
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/index.js b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/index.js
new file mode 100644
index 00000000..18250530
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/index.js
@@ -0,0 +1,53 @@
+importScripts('./libastyle.js');
+importScripts('../../../../web-modules/workerpool.min.js');
+
+
+const ASTYLE_CONFIG = `
+# Braces / 基本风格
+style=google # google 样式
+
+# 缩进
+indent=spaces=2 # 使用 2 空格
+indent-switches # switch 里的 case 缩进
+indent-cases # case 对齐
+indent-after-parens # 括号之后新起一行也缩进
+indent-preproc-block # 缩进宏块
+indent-preproc-cond # 缩进条件宏
+
+# Padding / 空格
+pad-comma # 参数逗号后加空格
+pad-oper # 运算符周围加空格
+pad-header # 关键字后添加空格 (if ( … ))
+
+# 对齐
+align-pointer=name # 把指针符号 * 放在类型名边
+align-reference=name # 引用符号 & 同上
+
+# Formatting / 格式化行为
+attach-return-type # 合并返回类型和函数名
+attach-return-type-decl # 对声明也合并
+add-braces # 强制加大括号
+keep-one-line-blocks # 保留一行 {} 块
+keep-one-line-statements # if/else 等保留一行语句
+close-templates # 模板 <> 处理
+break-after-logical # long expression 后换行
+break-blocks # 块首尾换行
+
+# 其他
+delete-empty-lines # 删除空行
+max-continuation-indent=4 # 设置表达式延续缩进最大值
+`;
+
+async function init() {
+ await self.cppFormatter.init();
+}
+
+async function format(code) {
+ const result = await self.cppFormatter.format(code, ASTYLE_CONFIG);
+ return result;
+}
+
+workerpool.worker({
+ init,
+ format
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.js b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.js
new file mode 100644
index 00000000..02b9bc3d
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.js
@@ -0,0 +1,5753 @@
+// Copyright 2010 The Emscripten Authors. All rights reserved.
+// Emscripten is available under two separate licenses, the MIT license and the
+// University of Illinois/NCSA Open Source License. Both these licenses can be
+// found in the LICENSE file.
+
+// The Module object: Our interface to the outside world. We import
+// and export values on it. There are various ways Module can be used:
+// 1. Not defined. We create it here
+// 2. A function parameter, function(Module) { ..generated code.. }
+// 3. pre-run appended it, var Module = {}; ..generated code..
+// 4. External script tag defines var Module.
+// We need to check if Module already exists (e.g. case 3 above).
+// Substitution will be replaced with actual code on later stage of the build,
+// this way Closure Compiler will not mangle it (e.g. case 4. above).
+// Note that if you want to run closure, and also to use Module
+// after the generated code, you will need to define var Module = {};
+// before the code. Then that object will be used in the code, and you
+// can continue to use Module afterwards as well.
+var Module = typeof Module !== 'undefined' ? Module : {};
+
+// --pre-jses are emitted after the Module integration code, so that they can
+// refer to Module (if they choose; they can also define Module)
+// {{PRE_JSES}}
+
+// Sometimes an existing Module object exists with properties
+// meant to overwrite the default module functionality. Here
+// we collect those properties and reapply _after_ we configure
+// the current environment's defaults to avoid having to be so
+// defensive during initialization.
+var moduleOverrides = {};
+var key;
+for (key in Module) {
+ if (Module.hasOwnProperty(key)) {
+ moduleOverrides[key] = Module[key];
+ }
+}
+
+var arguments_ = [];
+var thisProgram = './this.program';
+var quit_ = function (status, toThrow) {
+ throw toThrow;
+};
+
+// Determine the runtime environment we are in. You can customize this by
+// setting the ENVIRONMENT setting at compile time (see settings.js).
+
+var ENVIRONMENT_IS_WEB = false;
+var ENVIRONMENT_IS_WORKER = false;
+var ENVIRONMENT_IS_NODE = false;
+var ENVIRONMENT_HAS_NODE = false;
+var ENVIRONMENT_IS_SHELL = false;
+ENVIRONMENT_IS_WEB = typeof window === 'object';
+ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
+// A web environment like Electron.js can have Node enabled, so we must
+// distinguish between Node-enabled environments and Node environments per se.
+// This will allow the former to do things like mount NODEFS.
+// Extended check using process.versions fixes issue #8816.
+// (Also makes redundant the original check that 'require' is a function.)
+ENVIRONMENT_HAS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
+ENVIRONMENT_IS_NODE = ENVIRONMENT_HAS_NODE && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER;
+ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+
+if (Module['ENVIRONMENT']) {
+ throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)');
+}
+
+
+
+// `/` should be present at the end if `scriptDirectory` is not empty
+var scriptDirectory = '';
+function locateFile(path) {
+ if (Module['locateFile']) {
+ return Module['locateFile'](path, scriptDirectory);
+ }
+ return scriptDirectory + path;
+}
+
+// Hooks that are implemented differently in different runtime environments.
+var read_,
+ readAsync,
+ readBinary,
+ setWindowTitle;
+
+var nodeFS;
+var nodePath;
+
+if (ENVIRONMENT_IS_NODE) {
+ scriptDirectory = __dirname + '/';
+
+
+ read_ = function shell_read(filename, binary) {
+ if (!nodeFS) nodeFS = require('fs');
+ if (!nodePath) nodePath = require('path');
+ filename = nodePath['normalize'](filename);
+ return nodeFS['readFileSync'](filename, binary ? null : 'utf8');
+ };
+
+ readBinary = function readBinary(filename) {
+ var ret = read_(filename, true);
+ if (!ret.buffer) {
+ ret = new Uint8Array(ret);
+ }
+ assert(ret.buffer);
+ return ret;
+ };
+
+
+
+
+ if (process['argv'].length > 1) {
+ thisProgram = process['argv'][1].replace(/\\/g, '/');
+ }
+
+ arguments_ = process['argv'].slice(2);
+
+ if (typeof module !== 'undefined') {
+ module['exports'] = Module;
+ }
+
+ process['on']('uncaughtException', function (ex) {
+ // suppress ExitStatus exceptions from showing an error
+ if (!(ex instanceof ExitStatus)) {
+ throw ex;
+ }
+ });
+
+ process['on']('unhandledRejection', abort);
+
+ quit_ = function (status) {
+ process['exit'](status);
+ };
+
+ Module['inspect'] = function () { return '[Emscripten Module object]'; };
+
+
+} else
+ if (ENVIRONMENT_IS_SHELL) {
+
+
+ if (typeof read != 'undefined') {
+ read_ = function shell_read(f) {
+ return read(f);
+ };
+ }
+
+ readBinary = function readBinary(f) {
+ var data;
+ if (typeof readbuffer === 'function') {
+ return new Uint8Array(readbuffer(f));
+ }
+ data = read(f, 'binary');
+ assert(typeof data === 'object');
+ return data;
+ };
+
+ if (typeof scriptArgs != 'undefined') {
+ arguments_ = scriptArgs;
+ } else if (typeof arguments != 'undefined') {
+ arguments_ = arguments;
+ }
+
+ if (typeof quit === 'function') {
+ quit_ = function (status) {
+ quit(status);
+ };
+ }
+
+ if (typeof print !== 'undefined') {
+ // Prefer to use print/printErr where they exist, as they usually work better.
+ if (typeof console === 'undefined') console = {};
+ console.log = print;
+ console.warn = console.error = typeof printErr !== 'undefined' ? printErr : print;
+ }
+ } else
+
+ // Note that this includes Node.js workers when relevant (pthreads is enabled).
+ // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
+ // ENVIRONMENT_HAS_NODE.
+ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
+ if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled
+ scriptDirectory = self.location.href;
+ } else if (document.currentScript) { // web
+ scriptDirectory = document.currentScript.src;
+ }
+ // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
+ // otherwise, slice off the final part of the url to find the script directory.
+ // if scriptDirectory does not contain a slash, lastIndexOf will return -1,
+ // and scriptDirectory will correctly be replaced with an empty string.
+ if (scriptDirectory.indexOf('blob:') !== 0) {
+ scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/') + 1);
+ } else {
+ scriptDirectory = '';
+ }
+
+
+ // Differentiate the Web Worker from the Node Worker case, as reading must
+ // be done differently.
+ {
+
+
+ read_ = function shell_read(url) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ xhr.send(null);
+ return xhr.responseText;
+ };
+
+ if (ENVIRONMENT_IS_WORKER) {
+ readBinary = function readBinary(url) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ xhr.responseType = 'arraybuffer';
+ xhr.send(null);
+ return new Uint8Array(xhr.response);
+ };
+ }
+
+ readAsync = function readAsync(url, onload, onerror) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function xhr_onload() {
+ if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
+ onload(xhr.response);
+ return;
+ }
+ onerror();
+ };
+ xhr.onerror = onerror;
+ xhr.send(null);
+ };
+
+
+
+
+ }
+
+ setWindowTitle = function (title) { document.title = title };
+ } else {
+ throw new Error('environment detection error');
+ }
+
+
+// Set up the out() and err() hooks, which are how we can print to stdout or
+// stderr, respectively.
+var out = Module['print'] || console.log.bind(console);
+var err = Module['printErr'] || console.warn.bind(console);
+
+// Merge back in the overrides
+for (key in moduleOverrides) {
+ if (moduleOverrides.hasOwnProperty(key)) {
+ Module[key] = moduleOverrides[key];
+ }
+}
+// Free the object hierarchy contained in the overrides, this lets the GC
+// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array.
+moduleOverrides = null;
+
+// Emit code to handle expected values on the Module object. This applies Module.x
+// to the proper local x. This has two benefits: first, we only emit it if it is
+// expected to arrive, and second, by using a local everywhere else that can be
+// minified.
+if (Module['arguments']) arguments_ = Module['arguments']; if (!Object.getOwnPropertyDescriptor(Module, 'arguments')) Object.defineProperty(Module, 'arguments', { configurable: true, get: function () { abort('Module.arguments has been replaced with plain arguments_') } });
+if (Module['thisProgram']) thisProgram = Module['thisProgram']; if (!Object.getOwnPropertyDescriptor(Module, 'thisProgram')) Object.defineProperty(Module, 'thisProgram', { configurable: true, get: function () { abort('Module.thisProgram has been replaced with plain thisProgram') } });
+if (Module['quit']) quit_ = Module['quit']; if (!Object.getOwnPropertyDescriptor(Module, 'quit')) Object.defineProperty(Module, 'quit', { configurable: true, get: function () { abort('Module.quit has been replaced with plain quit_') } });
+
+// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
+// Assertions on removed incoming Module JS APIs.
+assert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead');
+assert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead');
+assert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead');
+assert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead');
+assert(typeof Module['read'] === 'undefined', 'Module.read option was removed (modify read_ in JS)');
+assert(typeof Module['readAsync'] === 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)');
+assert(typeof Module['readBinary'] === 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)');
+assert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)');
+if (!Object.getOwnPropertyDescriptor(Module, 'read')) Object.defineProperty(Module, 'read', { configurable: true, get: function () { abort('Module.read has been replaced with plain read_') } });
+if (!Object.getOwnPropertyDescriptor(Module, 'readAsync')) Object.defineProperty(Module, 'readAsync', { configurable: true, get: function () { abort('Module.readAsync has been replaced with plain readAsync') } });
+if (!Object.getOwnPropertyDescriptor(Module, 'readBinary')) Object.defineProperty(Module, 'readBinary', { configurable: true, get: function () { abort('Module.readBinary has been replaced with plain readBinary') } });
+// TODO: add when SDL2 is fixed if (!Object.getOwnPropertyDescriptor(Module, 'setWindowTitle')) Object.defineProperty(Module, 'setWindowTitle', { configurable: true, get: function() { abort('Module.setWindowTitle has been replaced with plain setWindowTitle') } });
+var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js';
+var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js';
+var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js';
+var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js';
+
+
+// TODO remove when SDL2 is fixed (also see above)
+
+
+
+// Copyright 2017 The Emscripten Authors. All rights reserved.
+// Emscripten is available under two separate licenses, the MIT license and the
+// University of Illinois/NCSA Open Source License. Both these licenses can be
+// found in the LICENSE file.
+
+// {{PREAMBLE_ADDITIONS}}
+
+var STACK_ALIGN = 16;
+
+// stack management, and other functionality that is provided by the compiled code,
+// should not be used before it is ready
+stackSave = stackRestore = stackAlloc = function () {
+ abort('cannot use the stack before compiled code is ready to run, and has provided stack access');
+};
+
+function staticAlloc(size) {
+ abort('staticAlloc is no longer available at runtime; instead, perform static allocations at compile time (using makeStaticAlloc)');
+}
+
+function dynamicAlloc(size) {
+ assert(DYNAMICTOP_PTR);
+ var ret = HEAP32[DYNAMICTOP_PTR >> 2];
+ var end = (ret + size + 15) & -16;
+ if (end > _emscripten_get_heap_size()) {
+ abort('failure to dynamicAlloc - memory growth etc. is not supported there, call malloc/sbrk directly');
+ }
+ HEAP32[DYNAMICTOP_PTR >> 2] = end;
+ return ret;
+}
+
+function alignMemory(size, factor) {
+ if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default
+ return Math.ceil(size / factor) * factor;
+}
+
+function getNativeTypeSize(type) {
+ switch (type) {
+ case 'i1': case 'i8': return 1;
+ case 'i16': return 2;
+ case 'i32': return 4;
+ case 'i64': return 8;
+ case 'float': return 4;
+ case 'double': return 8;
+ default: {
+ if (type[type.length - 1] === '*') {
+ return 4; // A pointer
+ } else if (type[0] === 'i') {
+ var bits = parseInt(type.substr(1));
+ assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type);
+ return bits / 8;
+ } else {
+ return 0;
+ }
+ }
+ }
+}
+
+function warnOnce(text) {
+ if (!warnOnce.shown) warnOnce.shown = {};
+ if (!warnOnce.shown[text]) {
+ warnOnce.shown[text] = 1;
+ err(text);
+ }
+}
+
+var asm2wasmImports = { // special asm2wasm imports
+ "f64-rem": function (x, y) {
+ return x % y;
+ },
+ "debugger": function () {
+ debugger;
+ }
+};
+
+
+
+
+// Wraps a JS function as a wasm function with a given signature.
+function convertJsFunctionToWasm(func, sig) {
+
+ // If the type reflection proposal is available, use the new
+ // "WebAssembly.Function" constructor.
+ // Otherwise, construct a minimal wasm module importing the JS function and
+ // re-exporting it.
+ if (typeof WebAssembly.Function === "function") {
+ var typeNames = {
+ 'i': 'i32',
+ 'j': 'i64',
+ 'f': 'f32',
+ 'd': 'f64'
+ };
+ var type = {
+ parameters: [],
+ results: sig[0] == 'v' ? [] : [typeNames[sig[0]]]
+ };
+ for (var i = 1; i < sig.length; ++i) {
+ type.parameters.push(typeNames[sig[i]]);
+ }
+ return new WebAssembly.Function(type, func);
+ }
+
+ // The module is static, with the exception of the type section, which is
+ // generated based on the signature passed in.
+ var typeSection = [
+ 0x01, // id: section,
+ 0x00, // length: 0 (placeholder)
+ 0x01, // count: 1
+ 0x60, // form: func
+ ];
+ var sigRet = sig.slice(0, 1);
+ var sigParam = sig.slice(1);
+ var typeCodes = {
+ 'i': 0x7f, // i32
+ 'j': 0x7e, // i64
+ 'f': 0x7d, // f32
+ 'd': 0x7c, // f64
+ };
+
+ // Parameters, length + signatures
+ typeSection.push(sigParam.length);
+ for (var i = 0; i < sigParam.length; ++i) {
+ typeSection.push(typeCodes[sigParam[i]]);
+ }
+
+ // Return values, length + signatures
+ // With no multi-return in MVP, either 0 (void) or 1 (anything else)
+ if (sigRet == 'v') {
+ typeSection.push(0x00);
+ } else {
+ typeSection = typeSection.concat([0x01, typeCodes[sigRet]]);
+ }
+
+ // Write the overall length of the type section back into the section header
+ // (excepting the 2 bytes for the section id and length)
+ typeSection[1] = typeSection.length - 2;
+
+ // Rest of the module is static
+ var bytes = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm")
+ 0x01, 0x00, 0x00, 0x00, // version: 1
+ ].concat(typeSection, [
+ 0x02, 0x07, // import section
+ // (import "e" "f" (func 0 (type 0)))
+ 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00,
+ 0x07, 0x05, // export section
+ // (export "f" (func 0 (type 0)))
+ 0x01, 0x01, 0x66, 0x00, 0x00,
+ ]));
+
+ // We can compile this wasm module synchronously because it is very small.
+ // This accepts an import (at "e.f"), that it reroutes to an export (at "f")
+ var module = new WebAssembly.Module(bytes);
+ var instance = new WebAssembly.Instance(module, {
+ 'e': {
+ 'f': func
+ }
+ });
+ var wrappedFunc = instance.exports['f'];
+ return wrappedFunc;
+}
+
+// Add a wasm function to the table.
+function addFunctionWasm(func, sig) {
+ var table = wasmTable;
+ var ret = table.length;
+
+ // Grow the table
+ try {
+ table.grow(1);
+ } catch (err) {
+ if (!err instanceof RangeError) {
+ throw err;
+ }
+ throw 'Unable to grow wasm table. Use a higher value for RESERVED_FUNCTION_POINTERS or set ALLOW_TABLE_GROWTH.';
+ }
+
+ // Insert new element
+ try {
+ // Attempting to call this with JS function will cause of table.set() to fail
+ table.set(ret, func);
+ } catch (err) {
+ if (!err instanceof TypeError) {
+ throw err;
+ }
+ assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction');
+ var wrapped = convertJsFunctionToWasm(func, sig);
+ table.set(ret, wrapped);
+ }
+
+ return ret;
+}
+
+function removeFunctionWasm(index) {
+ // TODO(sbc): Look into implementing this to allow re-using of table slots
+}
+
+// 'sig' parameter is required for the llvm backend but only when func is not
+// already a WebAssembly function.
+function addFunction(func, sig) {
+ assert(typeof func !== 'undefined');
+
+ return addFunctionWasm(func, sig);
+}
+
+function removeFunction(index) {
+ removeFunctionWasm(index);
+}
+
+var funcWrappers = {};
+
+function getFuncWrapper(func, sig) {
+ if (!func) return; // on null pointer, return undefined
+ assert(sig);
+ if (!funcWrappers[sig]) {
+ funcWrappers[sig] = {};
+ }
+ var sigCache = funcWrappers[sig];
+ if (!sigCache[func]) {
+ // optimize away arguments usage in common cases
+ if (sig.length === 1) {
+ sigCache[func] = function dynCall_wrapper() {
+ return dynCall(sig, func);
+ };
+ } else if (sig.length === 2) {
+ sigCache[func] = function dynCall_wrapper(arg) {
+ return dynCall(sig, func, [arg]);
+ };
+ } else {
+ // general case
+ sigCache[func] = function dynCall_wrapper() {
+ return dynCall(sig, func, Array.prototype.slice.call(arguments));
+ };
+ }
+ }
+ return sigCache[func];
+}
+
+
+function makeBigInt(low, high, unsigned) {
+ return unsigned ? ((+((low >>> 0))) + ((+((high >>> 0))) * 4294967296.0)) : ((+((low >>> 0))) + ((+((high | 0))) * 4294967296.0));
+}
+
+function dynCall(sig, ptr, args) {
+ if (args && args.length) {
+ assert(args.length == sig.length - 1);
+ assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
+ return Module['dynCall_' + sig].apply(null, [ptr].concat(args));
+ } else {
+ assert(sig.length == 1);
+ assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
+ return Module['dynCall_' + sig].call(null, ptr);
+ }
+}
+
+var tempRet0 = 0;
+
+var setTempRet0 = function (value) {
+ tempRet0 = value;
+};
+
+var getTempRet0 = function () {
+ return tempRet0;
+};
+
+function getCompilerSetting(name) {
+ throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work';
+}
+
+var Runtime = {
+ // helpful errors
+ getTempRet0: function () { abort('getTempRet0() is now a top-level function, after removing the Runtime object. Remove "Runtime."') },
+ staticAlloc: function () { abort('staticAlloc() is now a top-level function, after removing the Runtime object. Remove "Runtime."') },
+ stackAlloc: function () { abort('stackAlloc() is now a top-level function, after removing the Runtime object. Remove "Runtime."') },
+};
+
+// The address globals begin at. Very low in memory, for code size and optimization opportunities.
+// Above 0 is static memory, starting with globals.
+// Then the stack.
+// Then 'dynamic' memory for sbrk.
+var GLOBAL_BASE = 1024;
+
+
+
+
+// === Preamble library stuff ===
+
+// Documentation for the public APIs defined in this file must be updated in:
+// site/source/docs/api_reference/preamble.js.rst
+// A prebuilt local version of the documentation is available at:
+// site/build/text/docs/api_reference/preamble.js.txt
+// You can also build docs locally as HTML or other formats in site/
+// An online HTML version (which may be of a different version of Emscripten)
+// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
+
+
+var wasmBinary; if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; if (!Object.getOwnPropertyDescriptor(Module, 'wasmBinary')) Object.defineProperty(Module, 'wasmBinary', { configurable: true, get: function () { abort('Module.wasmBinary has been replaced with plain wasmBinary') } });
+var noExitRuntime; if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime']; if (!Object.getOwnPropertyDescriptor(Module, 'noExitRuntime')) Object.defineProperty(Module, 'noExitRuntime', { configurable: true, get: function () { abort('Module.noExitRuntime has been replaced with plain noExitRuntime') } });
+
+
+if (typeof WebAssembly !== 'object') {
+ abort('No WebAssembly support found. Build with -s WASM=0 to target JavaScript instead.');
+}
+
+
+// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking.
+// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties)
+
+/** @type {function(number, number, string, boolean=)} */
+function setValue(ptr, value, type, noSafe) {
+ type = type || 'i8';
+ if (type.charAt(type.length - 1) === '*') type = 'i32'; // pointers are 32-bit
+ switch (type) {
+ case 'i1': HEAP8[((ptr) >> 0)] = value; break;
+ case 'i8': HEAP8[((ptr) >> 0)] = value; break;
+ case 'i16': HEAP16[((ptr) >> 1)] = value; break;
+ case 'i32': HEAP32[((ptr) >> 2)] = value; break;
+ case 'i64': (tempI64 = [value >>> 0, (tempDouble = value, (+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble) / 4294967296.0))), 4294967295.0)) | 0) >>> 0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296.0))))) >>> 0) : 0)], HEAP32[((ptr) >> 2)] = tempI64[0], HEAP32[(((ptr) + (4)) >> 2)] = tempI64[1]); break;
+ case 'float': HEAPF32[((ptr) >> 2)] = value; break;
+ case 'double': HEAPF64[((ptr) >> 3)] = value; break;
+ default: abort('invalid type for setValue: ' + type);
+ }
+}
+
+/** @type {function(number, string, boolean=)} */
+function getValue(ptr, type, noSafe) {
+ type = type || 'i8';
+ if (type.charAt(type.length - 1) === '*') type = 'i32'; // pointers are 32-bit
+ switch (type) {
+ case 'i1': return HEAP8[((ptr) >> 0)];
+ case 'i8': return HEAP8[((ptr) >> 0)];
+ case 'i16': return HEAP16[((ptr) >> 1)];
+ case 'i32': return HEAP32[((ptr) >> 2)];
+ case 'i64': return HEAP32[((ptr) >> 2)];
+ case 'float': return HEAPF32[((ptr) >> 2)];
+ case 'double': return HEAPF64[((ptr) >> 3)];
+ default: abort('invalid type for getValue: ' + type);
+ }
+ return null;
+}
+
+
+
+
+
+// Wasm globals
+
+var wasmMemory;
+
+// In fastcomp asm.js, we don't need a wasm Table at all.
+// In the wasm backend, we polyfill the WebAssembly object,
+// so this creates a (non-native-wasm) table for us.
+var wasmTable = new WebAssembly.Table({
+ 'initial': 479,
+ 'maximum': 479 + 20,
+ 'element': 'anyfunc'
+});
+
+
+//========================================
+// Runtime essentials
+//========================================
+
+// whether we are quitting the application. no code should run after this.
+// set in exit() and abort()
+var ABORT = false;
+
+// set by exit() and abort(). Passed to 'onExit' handler.
+// NOTE: This is also used as the process return code code in shell environments
+// but only when noExitRuntime is false.
+var EXITSTATUS = 0;
+
+/** @type {function(*, string=)} */
+function assert(condition, text) {
+ if (!condition) {
+ abort('Assertion failed: ' + text);
+ }
+}
+
+// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
+function getCFunc(ident) {
+ var func = Module['_' + ident]; // closure exported function
+ assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported');
+ return func;
+}
+
+// C calling interface.
+function ccall(ident, returnType, argTypes, args, opts) {
+ // For fast lookup of conversion functions
+ var toC = {
+ 'string': function (str) {
+ var ret = 0;
+ if (str !== null && str !== undefined && str !== 0) { // null string
+ // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0'
+ var len = (str.length << 2) + 1;
+ ret = stackAlloc(len);
+ stringToUTF8(str, ret, len);
+ }
+ return ret;
+ },
+ 'array': function (arr) {
+ var ret = stackAlloc(arr.length);
+ writeArrayToMemory(arr, ret);
+ return ret;
+ }
+ };
+
+ function convertReturnValue(ret) {
+ if (returnType === 'string') return UTF8ToString(ret);
+ if (returnType === 'boolean') return Boolean(ret);
+ return ret;
+ }
+
+ var func = getCFunc(ident);
+ var cArgs = [];
+ var stack = 0;
+ assert(returnType !== 'array', 'Return type should not be "array".');
+ if (args) {
+ for (var i = 0; i < args.length; i++) {
+ var converter = toC[argTypes[i]];
+ if (converter) {
+ if (stack === 0) stack = stackSave();
+ cArgs[i] = converter(args[i]);
+ } else {
+ cArgs[i] = args[i];
+ }
+ }
+ }
+ var ret = func.apply(null, cArgs);
+
+ ret = convertReturnValue(ret);
+ if (stack !== 0) stackRestore(stack);
+ return ret;
+}
+
+function cwrap(ident, returnType, argTypes, opts) {
+ return function () {
+ return ccall(ident, returnType, argTypes, arguments, opts);
+ }
+}
+
+var ALLOC_NORMAL = 0; // Tries to use _malloc()
+var ALLOC_STACK = 1; // Lives for the duration of the current function call
+var ALLOC_DYNAMIC = 2; // Cannot be freed except through sbrk
+var ALLOC_NONE = 3; // Do not allocate
+
+// allocate(): This is for internal use. You can use it yourself as well, but the interface
+// is a little tricky (see docs right below). The reason is that it is optimized
+// for multiple syntaxes to save space in generated code. So you should
+// normally not use allocate(), and instead allocate memory using _malloc(),
+// initialize it with setValue(), and so forth.
+// @slab: An array of data, or a number. If a number, then the size of the block to allocate,
+// in *bytes* (note that this is sometimes confusing: the next parameter does not
+// affect this!)
+// @types: Either an array of types, one for each byte (or 0 if no type at that position),
+// or a single type which is used for the entire block. This only matters if there
+// is initial data - if @slab is a number, then this does not matter at all and is
+// ignored.
+// @allocator: How to allocate memory, see ALLOC_*
+/** @type {function((TypedArray|Array|number), string, number, number=)} */
+function allocate(slab, types, allocator, ptr) {
+ var zeroinit, size;
+ if (typeof slab === 'number') {
+ zeroinit = true;
+ size = slab;
+ } else {
+ zeroinit = false;
+ size = slab.length;
+ }
+
+ var singleType = typeof types === 'string' ? types : null;
+
+ var ret;
+ if (allocator == ALLOC_NONE) {
+ ret = ptr;
+ } else {
+ ret = [_malloc,
+ stackAlloc,
+ dynamicAlloc][allocator](Math.max(size, singleType ? 1 : types.length));
+ }
+
+ if (zeroinit) {
+ var stop;
+ ptr = ret;
+ assert((ret & 3) == 0);
+ stop = ret + (size & ~3);
+ for (; ptr < stop; ptr += 4) {
+ HEAP32[((ptr) >> 2)] = 0;
+ }
+ stop = ret + size;
+ while (ptr < stop) {
+ HEAP8[((ptr++) >> 0)] = 0;
+ }
+ return ret;
+ }
+
+ if (singleType === 'i8') {
+ if (slab.subarray || slab.slice) {
+ HEAPU8.set(/** @type {!Uint8Array} */(slab), ret);
+ } else {
+ HEAPU8.set(new Uint8Array(slab), ret);
+ }
+ return ret;
+ }
+
+ var i = 0, type, typeSize, previousType;
+ while (i < size) {
+ var curr = slab[i];
+
+ type = singleType || types[i];
+ if (type === 0) {
+ i++;
+ continue;
+ }
+ assert(type, 'Must know what type to store in allocate!');
+
+ if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later
+
+ setValue(ret + i, curr, type);
+
+ // no need to look up size unless type changes, so cache it
+ if (previousType !== type) {
+ typeSize = getNativeTypeSize(type);
+ previousType = type;
+ }
+ i += typeSize;
+ }
+
+ return ret;
+}
+
+// Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready
+function getMemory(size) {
+ if (!runtimeInitialized) return dynamicAlloc(size);
+ return _malloc(size);
+}
+
+
+
+
+/** @type {function(number, number=)} */
+function Pointer_stringify(ptr, length) {
+ abort("this function has been removed - you should use UTF8ToString(ptr, maxBytesToRead) instead!");
+}
+
+// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns
+// a copy of that string as a Javascript String object.
+
+function AsciiToString(ptr) {
+ var str = '';
+ while (1) {
+ var ch = HEAPU8[((ptr++) >> 0)];
+ if (!ch) return str;
+ str += String.fromCharCode(ch);
+ }
+}
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP.
+
+function stringToAscii(str, outPtr) {
+ return writeAsciiToMemory(str, outPtr, false);
+}
+
+
+// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns
+// a copy of that string as a Javascript String object.
+
+var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
+
+/**
+ * @param {number} idx
+ * @param {number=} maxBytesToRead
+ * @return {string}
+ */
+function UTF8ArrayToString(u8Array, idx, maxBytesToRead) {
+ var endIdx = idx + maxBytesToRead;
+ var endPtr = idx;
+ // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.
+ // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.
+ // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity)
+ while (u8Array[endPtr] && !(endPtr >= endIdx)) ++endPtr;
+
+ if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) {
+ return UTF8Decoder.decode(u8Array.subarray(idx, endPtr));
+ } else {
+ var str = '';
+ // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that
+ while (idx < endPtr) {
+ // For UTF8 byte structure, see:
+ // http://en.wikipedia.org/wiki/UTF-8#Description
+ // https://www.ietf.org/rfc/rfc2279.txt
+ // https://tools.ietf.org/html/rfc3629
+ var u0 = u8Array[idx++];
+ if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
+ var u1 = u8Array[idx++] & 63;
+ if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
+ var u2 = u8Array[idx++] & 63;
+ if ((u0 & 0xF0) == 0xE0) {
+ u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
+ } else {
+ if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string on the asm.js/wasm heap to a JS string!');
+ u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (u8Array[idx++] & 63);
+ }
+
+ if (u0 < 0x10000) {
+ str += String.fromCharCode(u0);
+ } else {
+ var ch = u0 - 0x10000;
+ str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
+ }
+ }
+ }
+ return str;
+}
+
+// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a
+// copy of that string as a Javascript String object.
+// maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit
+// this parameter to scan the string until the first \0 byte. If maxBytesToRead is
+// passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the
+// middle, then the string will cut short at that byte index (i.e. maxBytesToRead will
+// not produce a string of exact length [ptr, ptr+maxBytesToRead[)
+// N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may
+// throw JS JIT optimizations off, so it is worth to consider consistently using one
+// style or the other.
+/**
+ * @param {number} ptr
+ * @param {number=} maxBytesToRead
+ * @return {string}
+ */
+function UTF8ToString(ptr, maxBytesToRead) {
+ return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';
+}
+
+// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx',
+// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP.
+// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write.
+// Parameters:
+// str: the Javascript string to copy.
+// outU8Array: the array to copy to. Each index in this array is assumed to be one 8-byte element.
+// outIdx: The starting offset in the array to begin the copying.
+// maxBytesToWrite: The maximum number of bytes this function can write to the array.
+// This count should include the null terminator,
+// i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else.
+// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator.
+// Returns the number of bytes written, EXCLUDING the null terminator.
+
+function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) {
+ if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes.
+ return 0;
+
+ var startIdx = outIdx;
+ var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator.
+ for (var i = 0; i < str.length; ++i) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629
+ var u = str.charCodeAt(i); // possibly a lead surrogate
+ if (u >= 0xD800 && u <= 0xDFFF) {
+ var u1 = str.charCodeAt(++i);
+ u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF);
+ }
+ if (u <= 0x7F) {
+ if (outIdx >= endIdx) break;
+ outU8Array[outIdx++] = u;
+ } else if (u <= 0x7FF) {
+ if (outIdx + 1 >= endIdx) break;
+ outU8Array[outIdx++] = 0xC0 | (u >> 6);
+ outU8Array[outIdx++] = 0x80 | (u & 63);
+ } else if (u <= 0xFFFF) {
+ if (outIdx + 2 >= endIdx) break;
+ outU8Array[outIdx++] = 0xE0 | (u >> 12);
+ outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63);
+ outU8Array[outIdx++] = 0x80 | (u & 63);
+ } else {
+ if (outIdx + 3 >= endIdx) break;
+ if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF).');
+ outU8Array[outIdx++] = 0xF0 | (u >> 18);
+ outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63);
+ outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63);
+ outU8Array[outIdx++] = 0x80 | (u & 63);
+ }
+ }
+ // Null-terminate the pointer to the buffer.
+ outU8Array[outIdx] = 0;
+ return outIdx - startIdx;
+}
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP.
+// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write.
+// Returns the number of bytes written, EXCLUDING the null terminator.
+
+function stringToUTF8(str, outPtr, maxBytesToWrite) {
+ assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
+ return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
+}
+
+// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte.
+function lengthBytesUTF8(str) {
+ var len = 0;
+ for (var i = 0; i < str.length; ++i) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ var u = str.charCodeAt(i); // possibly a lead surrogate
+ if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
+ if (u <= 0x7F) ++len;
+ else if (u <= 0x7FF) len += 2;
+ else if (u <= 0xFFFF) len += 3;
+ else len += 4;
+ }
+ return len;
+}
+
+
+// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns
+// a copy of that string as a Javascript String object.
+
+var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined;
+function UTF16ToString(ptr) {
+ assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!');
+ var endPtr = ptr;
+ // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.
+ // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.
+ var idx = endPtr >> 1;
+ while (HEAP16[idx]) ++idx;
+ endPtr = idx << 1;
+
+ if (endPtr - ptr > 32 && UTF16Decoder) {
+ return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr));
+ } else {
+ var i = 0;
+
+ var str = '';
+ while (1) {
+ var codeUnit = HEAP16[(((ptr) + (i * 2)) >> 1)];
+ if (codeUnit == 0) return str;
+ ++i;
+ // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through.
+ str += String.fromCharCode(codeUnit);
+ }
+ }
+}
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP.
+// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write.
+// Parameters:
+// str: the Javascript string to copy.
+// outPtr: Byte address in Emscripten HEAP where to write the string to.
+// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null
+// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else.
+// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator.
+// Returns the number of bytes written, EXCLUDING the null terminator.
+
+function stringToUTF16(str, outPtr, maxBytesToWrite) {
+ assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!');
+ assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
+ // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.
+ if (maxBytesToWrite === undefined) {
+ maxBytesToWrite = 0x7FFFFFFF;
+ }
+ if (maxBytesToWrite < 2) return 0;
+ maxBytesToWrite -= 2; // Null terminator.
+ var startPtr = outPtr;
+ var numCharsToWrite = (maxBytesToWrite < str.length * 2) ? (maxBytesToWrite / 2) : str.length;
+ for (var i = 0; i < numCharsToWrite; ++i) {
+ // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP.
+ var codeUnit = str.charCodeAt(i); // possibly a lead surrogate
+ HEAP16[((outPtr) >> 1)] = codeUnit;
+ outPtr += 2;
+ }
+ // Null-terminate the pointer to the HEAP.
+ HEAP16[((outPtr) >> 1)] = 0;
+ return outPtr - startPtr;
+}
+
+// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte.
+
+function lengthBytesUTF16(str) {
+ return str.length * 2;
+}
+
+function UTF32ToString(ptr) {
+ assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!');
+ var i = 0;
+
+ var str = '';
+ while (1) {
+ var utf32 = HEAP32[(((ptr) + (i * 4)) >> 2)];
+ if (utf32 == 0)
+ return str;
+ ++i;
+ // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ if (utf32 >= 0x10000) {
+ var ch = utf32 - 0x10000;
+ str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
+ } else {
+ str += String.fromCharCode(utf32);
+ }
+ }
+}
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP.
+// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write.
+// Parameters:
+// str: the Javascript string to copy.
+// outPtr: Byte address in Emscripten HEAP where to write the string to.
+// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null
+// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else.
+// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator.
+// Returns the number of bytes written, EXCLUDING the null terminator.
+
+function stringToUTF32(str, outPtr, maxBytesToWrite) {
+ assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!');
+ assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
+ // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.
+ if (maxBytesToWrite === undefined) {
+ maxBytesToWrite = 0x7FFFFFFF;
+ }
+ if (maxBytesToWrite < 4) return 0;
+ var startPtr = outPtr;
+ var endPtr = startPtr + maxBytesToWrite - 4;
+ for (var i = 0; i < str.length; ++i) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ var codeUnit = str.charCodeAt(i); // possibly a lead surrogate
+ if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) {
+ var trailSurrogate = str.charCodeAt(++i);
+ codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF);
+ }
+ HEAP32[((outPtr) >> 2)] = codeUnit;
+ outPtr += 4;
+ if (outPtr + 4 > endPtr) break;
+ }
+ // Null-terminate the pointer to the HEAP.
+ HEAP32[((outPtr) >> 2)] = 0;
+ return outPtr - startPtr;
+}
+
+// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte.
+
+function lengthBytesUTF32(str) {
+ var len = 0;
+ for (var i = 0; i < str.length; ++i) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ var codeUnit = str.charCodeAt(i);
+ if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate.
+ len += 4;
+ }
+
+ return len;
+}
+
+// Allocate heap space for a JS string, and write it there.
+// It is the responsibility of the caller to free() that memory.
+function allocateUTF8(str) {
+ var size = lengthBytesUTF8(str) + 1;
+ var ret = _malloc(size);
+ if (ret) stringToUTF8Array(str, HEAP8, ret, size);
+ return ret;
+}
+
+// Allocate stack space for a JS string, and write it there.
+function allocateUTF8OnStack(str) {
+ var size = lengthBytesUTF8(str) + 1;
+ var ret = stackAlloc(size);
+ stringToUTF8Array(str, HEAP8, ret, size);
+ return ret;
+}
+
+// Deprecated: This function should not be called because it is unsafe and does not provide
+// a maximum length limit of how many bytes it is allowed to write. Prefer calling the
+// function stringToUTF8Array() instead, which takes in a maximum length that can be used
+// to be secure from out of bounds writes.
+/** @deprecated */
+function writeStringToMemory(string, buffer, dontAddNull) {
+ warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!');
+
+ var /** @type {number} */ lastChar, /** @type {number} */ end;
+ if (dontAddNull) {
+ // stringToUTF8Array always appends null. If we don't want to do that, remember the
+ // character that existed at the location where the null will be placed, and restore
+ // that after the write (below).
+ end = buffer + lengthBytesUTF8(string);
+ lastChar = HEAP8[end];
+ }
+ stringToUTF8(string, buffer, Infinity);
+ if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character.
+}
+
+function writeArrayToMemory(array, buffer) {
+ assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)')
+ HEAP8.set(array, buffer);
+}
+
+function writeAsciiToMemory(str, buffer, dontAddNull) {
+ for (var i = 0; i < str.length; ++i) {
+ assert(str.charCodeAt(i) === str.charCodeAt(i) & 0xff);
+ HEAP8[((buffer++) >> 0)] = str.charCodeAt(i);
+ }
+ // Null-terminate the pointer to the HEAP.
+ if (!dontAddNull) HEAP8[((buffer) >> 0)] = 0;
+}
+
+
+
+
+// Memory management
+
+var PAGE_SIZE = 16384;
+var WASM_PAGE_SIZE = 65536;
+var ASMJS_PAGE_SIZE = 16777216;
+
+function alignUp(x, multiple) {
+ if (x % multiple > 0) {
+ x += multiple - (x % multiple);
+ }
+ return x;
+}
+
+var HEAP,
+ /** @type {ArrayBuffer} */
+ buffer,
+ /** @type {Int8Array} */
+ HEAP8,
+ /** @type {Uint8Array} */
+ HEAPU8,
+ /** @type {Int16Array} */
+ HEAP16,
+ /** @type {Uint16Array} */
+ HEAPU16,
+ /** @type {Int32Array} */
+ HEAP32,
+ /** @type {Uint32Array} */
+ HEAPU32,
+ /** @type {Float32Array} */
+ HEAPF32,
+ /** @type {Float64Array} */
+ HEAPF64;
+
+function updateGlobalBufferAndViews(buf) {
+ buffer = buf;
+ Module['HEAP8'] = HEAP8 = new Int8Array(buf);
+ Module['HEAP16'] = HEAP16 = new Int16Array(buf);
+ Module['HEAP32'] = HEAP32 = new Int32Array(buf);
+ Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf);
+ Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf);
+ Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf);
+ Module['HEAPF32'] = HEAPF32 = new Float32Array(buf);
+ Module['HEAPF64'] = HEAPF64 = new Float64Array(buf);
+}
+
+var STATIC_BASE = 1024,
+ STACK_BASE = 5265728,
+ STACKTOP = STACK_BASE,
+ STACK_MAX = 22848,
+ DYNAMIC_BASE = 5265728,
+ DYNAMICTOP_PTR = 22688;
+
+assert(STACK_BASE % 16 === 0, 'stack must start aligned');
+assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
+
+
+
+var TOTAL_STACK = 5242880;
+if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime')
+
+var INITIAL_TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 16777216; if (!Object.getOwnPropertyDescriptor(Module, 'TOTAL_MEMORY')) Object.defineProperty(Module, 'TOTAL_MEMORY', { configurable: true, get: function () { abort('Module.TOTAL_MEMORY has been replaced with plain INITIAL_TOTAL_MEMORY') } });
+
+assert(INITIAL_TOTAL_MEMORY >= TOTAL_STACK, 'TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')');
+
+// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
+assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined,
+ 'JS engine does not provide full typed array support');
+
+
+
+
+
+
+// In standalone mode, the wasm creates the memory, and the user can't provide it.
+// In non-standalone/normal mode, we create the memory here.
+
+// Create the main memory. (Note: this isn't used in STANDALONE_WASM mode since the wasm
+// memory is created in the wasm, not in JS.)
+
+if (Module['wasmMemory']) {
+ wasmMemory = Module['wasmMemory'];
+} else {
+ wasmMemory = new WebAssembly.Memory({
+ 'initial': INITIAL_TOTAL_MEMORY / WASM_PAGE_SIZE
+ ,
+ 'maximum': INITIAL_TOTAL_MEMORY / WASM_PAGE_SIZE
+ });
+}
+
+
+if (wasmMemory) {
+ buffer = wasmMemory.buffer;
+}
+
+// If the user provides an incorrect length, just use that length instead rather than providing the user to
+// specifically provide the memory length with Module['TOTAL_MEMORY'].
+INITIAL_TOTAL_MEMORY = buffer.byteLength;
+assert(INITIAL_TOTAL_MEMORY % WASM_PAGE_SIZE === 0);
+updateGlobalBufferAndViews(buffer);
+
+HEAP32[DYNAMICTOP_PTR >> 2] = DYNAMIC_BASE;
+
+
+
+
+// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode.
+function writeStackCookie() {
+ assert((STACK_MAX & 3) == 0);
+ // The stack grows downwards
+ HEAPU32[(STACK_MAX >> 2) + 1] = 0x02135467;
+ HEAPU32[(STACK_MAX >> 2) + 2] = 0x89BACDFE;
+ // Also test the global address 0 for integrity.
+ // We don't do this with ASan because ASan does its own checks for this.
+ HEAP32[0] = 0x63736d65; /* 'emsc' */
+}
+
+function checkStackCookie() {
+ var cookie1 = HEAPU32[(STACK_MAX >> 2) + 1];
+ var cookie2 = HEAPU32[(STACK_MAX >> 2) + 2];
+ if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) {
+ abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16));
+ }
+ // Also test the global address 0 for integrity.
+ // We don't do this with ASan because ASan does its own checks for this.
+ if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!');
+}
+
+function abortStackOverflow(allocSize) {
+ abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!');
+}
+
+
+
+
+// Endianness check (note: assumes compiler arch was little-endian)
+(function () {
+ var h16 = new Int16Array(1);
+ var h8 = new Int8Array(h16.buffer);
+ h16[0] = 0x6373;
+ if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!';
+})();
+
+function abortFnPtrError(ptr, sig) {
+ abort("Invalid function pointer " + ptr + " called with signature '" + sig + "'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this). Build with ASSERTIONS=2 for more info.");
+}
+
+
+
+function callRuntimeCallbacks(callbacks) {
+ while (callbacks.length > 0) {
+ var callback = callbacks.shift();
+ if (typeof callback == 'function') {
+ callback();
+ continue;
+ }
+ var func = callback.func;
+ if (typeof func === 'number') {
+ if (callback.arg === undefined) {
+ Module['dynCall_v'](func);
+ } else {
+ Module['dynCall_vi'](func, callback.arg);
+ }
+ } else {
+ func(callback.arg === undefined ? null : callback.arg);
+ }
+ }
+}
+
+var __ATPRERUN__ = []; // functions called before the runtime is initialized
+var __ATINIT__ = []; // functions called during startup
+var __ATMAIN__ = []; // functions called when main() is to be run
+var __ATEXIT__ = []; // functions called during shutdown
+var __ATPOSTRUN__ = []; // functions called after the main() is called
+
+var runtimeInitialized = false;
+var runtimeExited = false;
+
+
+function preRun() {
+
+ if (Module['preRun']) {
+ if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
+ while (Module['preRun'].length) {
+ addOnPreRun(Module['preRun'].shift());
+ }
+ }
+
+ callRuntimeCallbacks(__ATPRERUN__);
+}
+
+function initRuntime() {
+ checkStackCookie();
+ assert(!runtimeInitialized);
+ runtimeInitialized = true;
+ if (!Module["noFSInit"] && !FS.init.initialized) FS.init();
+ TTY.init();
+ callRuntimeCallbacks(__ATINIT__);
+}
+
+function preMain() {
+ checkStackCookie();
+ FS.ignorePermissions = false;
+ callRuntimeCallbacks(__ATMAIN__);
+}
+
+function exitRuntime() {
+ checkStackCookie();
+ runtimeExited = true;
+}
+
+function postRun() {
+ checkStackCookie();
+
+ if (Module['postRun']) {
+ if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
+ while (Module['postRun'].length) {
+ addOnPostRun(Module['postRun'].shift());
+ }
+ }
+
+ callRuntimeCallbacks(__ATPOSTRUN__);
+}
+
+function addOnPreRun(cb) {
+ __ATPRERUN__.unshift(cb);
+}
+
+function addOnInit(cb) {
+ __ATINIT__.unshift(cb);
+}
+
+function addOnPreMain(cb) {
+ __ATMAIN__.unshift(cb);
+}
+
+function addOnExit(cb) {
+}
+
+function addOnPostRun(cb) {
+ __ATPOSTRUN__.unshift(cb);
+}
+
+function unSign(value, bits, ignore) {
+ if (value >= 0) {
+ return value;
+ }
+ return bits <= 32 ? 2 * Math.abs(1 << (bits - 1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
+ : Math.pow(2, bits) + value;
+}
+function reSign(value, bits, ignore) {
+ if (value <= 0) {
+ return value;
+ }
+ var half = bits <= 32 ? Math.abs(1 << (bits - 1)) // abs is needed if bits == 32
+ : Math.pow(2, bits - 1);
+ if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that
+ // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
+ // TODO: In i64 mode 1, resign the two parts separately and safely
+ value = -2 * half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
+ }
+ return value;
+}
+
+
+assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');
+assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');
+assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');
+assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');
+
+var Math_abs = Math.abs;
+var Math_cos = Math.cos;
+var Math_sin = Math.sin;
+var Math_tan = Math.tan;
+var Math_acos = Math.acos;
+var Math_asin = Math.asin;
+var Math_atan = Math.atan;
+var Math_atan2 = Math.atan2;
+var Math_exp = Math.exp;
+var Math_log = Math.log;
+var Math_sqrt = Math.sqrt;
+var Math_ceil = Math.ceil;
+var Math_floor = Math.floor;
+var Math_pow = Math.pow;
+var Math_imul = Math.imul;
+var Math_fround = Math.fround;
+var Math_round = Math.round;
+var Math_min = Math.min;
+var Math_max = Math.max;
+var Math_clz32 = Math.clz32;
+var Math_trunc = Math.trunc;
+
+
+
+// A counter of dependencies for calling run(). If we need to
+// do asynchronous work before running, increment this and
+// decrement it. Incrementing must happen in a place like
+// Module.preRun (used by emcc to add file preloading).
+// Note that you can add dependencies in preRun, even though
+// it happens right before run - run will be postponed until
+// the dependencies are met.
+var runDependencies = 0;
+var runDependencyWatcher = null;
+var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled
+var runDependencyTracking = {};
+
+function getUniqueRunDependency(id) {
+ var orig = id;
+ while (1) {
+ if (!runDependencyTracking[id]) return id;
+ id = orig + Math.random();
+ }
+ return id;
+}
+
+function addRunDependency(id) {
+ runDependencies++;
+
+ if (Module['monitorRunDependencies']) {
+ Module['monitorRunDependencies'](runDependencies);
+ }
+
+ if (id) {
+ assert(!runDependencyTracking[id]);
+ runDependencyTracking[id] = 1;
+ if (runDependencyWatcher === null && typeof setInterval !== 'undefined') {
+ // Check for missing dependencies every few seconds
+ runDependencyWatcher = setInterval(function () {
+ if (ABORT) {
+ clearInterval(runDependencyWatcher);
+ runDependencyWatcher = null;
+ return;
+ }
+ var shown = false;
+ for (var dep in runDependencyTracking) {
+ if (!shown) {
+ shown = true;
+ err('still waiting on run dependencies:');
+ }
+ err('dependency: ' + dep);
+ }
+ if (shown) {
+ err('(end of list)');
+ }
+ }, 10000);
+ }
+ } else {
+ err('warning: run dependency added without ID');
+ }
+}
+
+function removeRunDependency(id) {
+ runDependencies--;
+
+ if (Module['monitorRunDependencies']) {
+ Module['monitorRunDependencies'](runDependencies);
+ }
+
+ if (id) {
+ assert(runDependencyTracking[id]);
+ delete runDependencyTracking[id];
+ } else {
+ err('warning: run dependency removed without ID');
+ }
+ if (runDependencies == 0) {
+ if (runDependencyWatcher !== null) {
+ clearInterval(runDependencyWatcher);
+ runDependencyWatcher = null;
+ }
+ if (dependenciesFulfilled) {
+ var callback = dependenciesFulfilled;
+ dependenciesFulfilled = null;
+ callback(); // can add another dependenciesFulfilled
+ }
+ }
+}
+
+Module["preloadedImages"] = {}; // maps url to image data
+Module["preloadedAudios"] = {}; // maps url to audio data
+
+
+function abort(what) {
+ if (Module['onAbort']) {
+ Module['onAbort'](what);
+ }
+
+ what += '';
+ out(what);
+ err(what);
+
+ ABORT = true;
+ EXITSTATUS = 1;
+
+ var output = 'abort(' + what + ') at ' + stackTrace();
+ what = output;
+
+ // Throw a wasm runtime error, because a JS error might be seen as a foreign
+ // exception, which means we'd run destructors on it. We need the error to
+ // simply make the program stop.
+ throw new WebAssembly.RuntimeError(what);
+}
+
+
+var memoryInitializer = null;
+
+
+
+
+
+
+
+// Copyright 2017 The Emscripten Authors. All rights reserved.
+// Emscripten is available under two separate licenses, the MIT license and the
+// University of Illinois/NCSA Open Source License. Both these licenses can be
+// found in the LICENSE file.
+
+// Prefix of data URIs emitted by SINGLE_FILE and related options.
+var dataURIPrefix = 'data:application/octet-stream;base64,';
+
+// Indicates whether filename is a base64 data URI.
+function isDataURI(filename) {
+ return String.prototype.startsWith ?
+ filename.startsWith(dataURIPrefix) :
+ filename.indexOf(dataURIPrefix) === 0;
+}
+
+
+
+
+var wasmBinaryFile = 'libastyle.wasm';
+if (!isDataURI(wasmBinaryFile)) {
+ wasmBinaryFile = locateFile(wasmBinaryFile);
+}
+
+function getBinary() {
+ try {
+ if (wasmBinary) {
+ return new Uint8Array(wasmBinary);
+ }
+
+ if (readBinary) {
+ return readBinary(wasmBinaryFile);
+ } else {
+ throw "both async and sync fetching of the wasm failed";
+ }
+ }
+ catch (err) {
+ abort(err);
+ }
+}
+
+function getBinaryPromise() {
+ // if we don't have the binary yet, and have the Fetch api, use that
+ // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web
+ if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function') {
+ return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {
+ if (!response['ok']) {
+ throw "failed to load wasm binary file at '" + wasmBinaryFile + "'";
+ }
+ return response['arrayBuffer']();
+ }).catch(function () {
+ return getBinary();
+ });
+ }
+ // Otherwise, getBinary should be able to get it synchronously
+ return new Promise(function (resolve, reject) {
+ resolve(getBinary());
+ });
+}
+
+
+
+// Create the wasm instance.
+// Receives the wasm imports, returns the exports.
+function createWasm() {
+ // prepare imports
+ var info = {
+ 'env': asmLibraryArg,
+ 'wasi_snapshot_preview1': asmLibraryArg
+ };
+ // Load the wasm module and create an instance of using native support in the JS engine.
+ // handle a generated wasm instance, receiving its exports and
+ // performing other necessary setup
+ function receiveInstance(instance, module) {
+ var exports = instance.exports;
+ Module['asm'] = exports;
+ removeRunDependency('wasm-instantiate');
+ }
+ // we can't run yet (except in a pthread, where we have a custom sync instantiator)
+ addRunDependency('wasm-instantiate');
+
+
+ // Async compilation can be confusing when an error on the page overwrites Module
+ // (for example, if the order of elements is wrong, and the one defining Module is
+ // later), so we save Module and check it later.
+ var trueModule = Module;
+ function receiveInstantiatedSource(output) {
+ // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance.
+ // receiveInstance() will swap in the exports (to Module.asm) so they can be called
+ assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?');
+ trueModule = null;
+ // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.
+ // When the regression is fixed, can restore the above USE_PTHREADS-enabled path.
+ receiveInstance(output['instance']);
+ }
+
+
+ function instantiateArrayBuffer(receiver) {
+ return getBinaryPromise().then(function (binary) {
+ return WebAssembly.instantiate(binary, info);
+ }).then(receiver, function (reason) {
+ err('failed to asynchronously prepare wasm: ' + reason);
+ abort(reason);
+ });
+ }
+
+ // Prefer streaming instantiation if available.
+ function instantiateAsync() {
+ if (!wasmBinary &&
+ typeof WebAssembly.instantiateStreaming === 'function' &&
+ !isDataURI(wasmBinaryFile) &&
+ typeof fetch === 'function') {
+ fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {
+ var result = WebAssembly.instantiateStreaming(response, info);
+ return result.then(receiveInstantiatedSource, function (reason) {
+ // We expect the most common failure cause to be a bad MIME type for the binary,
+ // in which case falling back to ArrayBuffer instantiation should work.
+ err('wasm streaming compile failed: ' + reason);
+ err('falling back to ArrayBuffer instantiation');
+ instantiateArrayBuffer(receiveInstantiatedSource);
+ });
+ });
+ } else {
+ return instantiateArrayBuffer(receiveInstantiatedSource);
+ }
+ }
+ // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
+ // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel
+ // to any other async startup actions they are performing.
+ if (Module['instantiateWasm']) {
+ try {
+ var exports = Module['instantiateWasm'](info, receiveInstance);
+ return exports;
+ } catch (e) {
+ err('Module.instantiateWasm callback failed with error: ' + e);
+ return false;
+ }
+ }
+
+ instantiateAsync();
+ return {}; // no exports yet; we'll fill them in later
+}
+
+
+// Globals used by JS i64 conversions
+var tempDouble;
+var tempI64;
+
+// === Body ===
+
+var ASM_CONSTS = {
+
+};
+
+
+
+
+// STATICTOP = STATIC_BASE + 21824;
+/* global initializers */ __ATINIT__.push({ func: function () { ___wasm_call_ctors() } });
+
+
+
+
+/* no memory initializer */
+// {{PRE_LIBRARY}}
+
+
+function demangle(func) {
+ warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling');
+ return func;
+}
+
+function demangleAll(text) {
+ var regex =
+ /\b_Z[\w\d_]+/g;
+ return text.replace(regex,
+ function (x) {
+ var y = demangle(x);
+ return x === y ? x : (y + ' [' + x + ']');
+ });
+}
+
+function jsStackTrace() {
+ var err = new Error();
+ if (!err.stack) {
+ // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown,
+ // so try that as a special-case.
+ try {
+ throw new Error(0);
+ } catch (e) {
+ err = e;
+ }
+ if (!err.stack) {
+ return '(no stack trace available)';
+ }
+ }
+ return err.stack.toString();
+}
+
+function stackTrace() {
+ var js = jsStackTrace();
+ if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace']();
+ return demangleAll(js);
+}
+
+
+function _atexit(func, arg) {
+ warnOnce('atexit() called, but EXIT_RUNTIME is not set, so atexits() will not be called. set EXIT_RUNTIME to 1 (see the FAQ)');
+ __ATEXIT__.unshift({ func: func, arg: arg });
+} function ___cxa_atexit(
+) {
+ return _atexit.apply(null, arguments)
+}
+
+function ___handle_stack_overflow() {
+ abort('stack overflow')
+}
+
+function ___lock() { }
+
+
+function ___setErrNo(value) {
+ if (Module['___errno_location']) HEAP32[((Module['___errno_location']()) >> 2)] = value;
+ else err('failed to set errno from JS');
+ return value;
+} function ___map_file(pathname, size) {
+ ___setErrNo(63);
+ return -1;
+}
+
+
+
+
+var PATH = {
+ splitPath: function (filename) {
+ var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
+ return splitPathRe.exec(filename).slice(1);
+ }, normalizeArray: function (parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length - 1; i >= 0; i--) {
+ var last = parts[i];
+ if (last === '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
+ }
+ }
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up; up--) {
+ parts.unshift('..');
+ }
+ }
+ return parts;
+ }, normalize: function (path) {
+ var isAbsolute = path.charAt(0) === '/',
+ trailingSlash = path.substr(-1) === '/';
+ // Normalize the path
+ path = PATH.normalizeArray(path.split('/').filter(function (p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+ return (isAbsolute ? '/' : '') + path;
+ }, dirname: function (path) {
+ var result = PATH.splitPath(path),
+ root = result[0],
+ dir = result[1];
+ if (!root && !dir) {
+ // No dirname whatsoever
+ return '.';
+ }
+ if (dir) {
+ // It has a dirname, strip trailing slash
+ dir = dir.substr(0, dir.length - 1);
+ }
+ return root + dir;
+ }, basename: function (path) {
+ // EMSCRIPTEN return '/'' for '/', not an empty string
+ if (path === '/') return '/';
+ var lastSlash = path.lastIndexOf('/');
+ if (lastSlash === -1) return path;
+ return path.substr(lastSlash + 1);
+ }, extname: function (path) {
+ return PATH.splitPath(path)[3];
+ }, join: function () {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return PATH.normalize(paths.join('/'));
+ }, join2: function (l, r) {
+ return PATH.normalize(l + '/' + r);
+ }
+};
+
+
+var PATH_FS = {
+ resolve: function () {
+ var resolvedPath = '',
+ resolvedAbsolute = false;
+ for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0) ? arguments[i] : FS.cwd();
+ // Skip empty and invalid entries
+ if (typeof path !== 'string') {
+ throw new TypeError('Arguments to path.resolve must be strings');
+ } else if (!path) {
+ return ''; // an invalid portion invalidates the whole thing
+ }
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
+ }
+ // At this point the path should be resolved to a full absolute path, but
+ // handle relative paths to be safe (might happen when process.cwd() fails)
+ resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function (p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+ }, relative: function (from, to) {
+ from = PATH_FS.resolve(from).substr(1);
+ to = PATH_FS.resolve(to).substr(1);
+ function trim(arr) {
+ var start = 0;
+ for (; start < arr.length; start++) {
+ if (arr[start] !== '') break;
+ }
+ var end = arr.length - 1;
+ for (; end >= 0; end--) {
+ if (arr[end] !== '') break;
+ }
+ if (start > end) return [];
+ return arr.slice(start, end - start + 1);
+ }
+ var fromParts = trim(from.split('/'));
+ var toParts = trim(to.split('/'));
+ var length = Math.min(fromParts.length, toParts.length);
+ var samePartsLength = length;
+ for (var i = 0; i < length; i++) {
+ if (fromParts[i] !== toParts[i]) {
+ samePartsLength = i;
+ break;
+ }
+ }
+ var outputParts = [];
+ for (var i = samePartsLength; i < fromParts.length; i++) {
+ outputParts.push('..');
+ }
+ outputParts = outputParts.concat(toParts.slice(samePartsLength));
+ return outputParts.join('/');
+ }
+};
+
+var TTY = {
+ ttys: [], init: function () {
+ // https://github.com/emscripten-core/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // currently, FS.init does not distinguish if process.stdin is a file or TTY
+ // // device, it always assumes it's a TTY device. because of this, we're forcing
+ // // process.stdin to UTF8 encoding to at least make stdin reading compatible
+ // // with text files until FS.init can be refactored.
+ // process['stdin']['setEncoding']('utf8');
+ // }
+ }, shutdown: function () {
+ // https://github.com/emscripten-core/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)?
+ // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation
+ // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists?
+ // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle
+ // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call
+ // process['stdin']['pause']();
+ // }
+ }, register: function (dev, ops) {
+ TTY.ttys[dev] = { input: [], output: [], ops: ops };
+ FS.registerDevice(dev, TTY.stream_ops);
+ }, stream_ops: {
+ open: function (stream) {
+ var tty = TTY.ttys[stream.node.rdev];
+ if (!tty) {
+ throw new FS.ErrnoError(43);
+ }
+ stream.tty = tty;
+ stream.seekable = false;
+ }, close: function (stream) {
+ // flush any pending line data
+ stream.tty.ops.flush(stream.tty);
+ }, flush: function (stream) {
+ stream.tty.ops.flush(stream.tty);
+ }, read: function (stream, buffer, offset, length, pos /* ignored */) {
+ if (!stream.tty || !stream.tty.ops.get_char) {
+ throw new FS.ErrnoError(60);
+ }
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ var result;
+ try {
+ result = stream.tty.ops.get_char(stream.tty);
+ } catch (e) {
+ throw new FS.ErrnoError(29);
+ }
+ if (result === undefined && bytesRead === 0) {
+ throw new FS.ErrnoError(6);
+ }
+ if (result === null || result === undefined) break;
+ bytesRead++;
+ buffer[offset + i] = result;
+ }
+ if (bytesRead) {
+ stream.node.timestamp = Date.now();
+ }
+ return bytesRead;
+ }, write: function (stream, buffer, offset, length, pos) {
+ if (!stream.tty || !stream.tty.ops.put_char) {
+ throw new FS.ErrnoError(60);
+ }
+ try {
+ for (var i = 0; i < length; i++) {
+ stream.tty.ops.put_char(stream.tty, buffer[offset + i]);
+ }
+ } catch (e) {
+ throw new FS.ErrnoError(29);
+ }
+ if (length) {
+ stream.node.timestamp = Date.now();
+ }
+ return i;
+ }
+ }, default_tty_ops: {
+ get_char: function (tty) {
+ if (!tty.input.length) {
+ var result = null;
+ if (ENVIRONMENT_IS_NODE) {
+ // we will read data by chunks of BUFSIZE
+ var BUFSIZE = 256;
+ var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE);
+ var bytesRead = 0;
+
+ try {
+ bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null);
+ } catch (e) {
+ // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes,
+ // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0.
+ if (e.toString().indexOf('EOF') != -1) bytesRead = 0;
+ else throw e;
+ }
+
+ if (bytesRead > 0) {
+ result = buf.slice(0, bytesRead).toString('utf-8');
+ } else {
+ result = null;
+ }
+ } else
+ if (typeof window != 'undefined' &&
+ typeof window.prompt == 'function') {
+ // Browser.
+ result = window.prompt('Input: '); // returns null on cancel
+ if (result !== null) {
+ result += '\n';
+ }
+ } else if (typeof readline == 'function') {
+ // Command line.
+ result = readline();
+ if (result !== null) {
+ result += '\n';
+ }
+ }
+ if (!result) {
+ return null;
+ }
+ tty.input = intArrayFromString(result, true);
+ }
+ return tty.input.shift();
+ }, put_char: function (tty, val) {
+ if (val === null || val === 10) {
+ out(UTF8ArrayToString(tty.output, 0));
+ tty.output = [];
+ } else {
+ if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle.
+ }
+ }, flush: function (tty) {
+ if (tty.output && tty.output.length > 0) {
+ out(UTF8ArrayToString(tty.output, 0));
+ tty.output = [];
+ }
+ }
+ }, default_tty1_ops: {
+ put_char: function (tty, val) {
+ if (val === null || val === 10) {
+ err(UTF8ArrayToString(tty.output, 0));
+ tty.output = [];
+ } else {
+ if (val != 0) tty.output.push(val);
+ }
+ }, flush: function (tty) {
+ if (tty.output && tty.output.length > 0) {
+ err(UTF8ArrayToString(tty.output, 0));
+ tty.output = [];
+ }
+ }
+ }
+};
+
+var MEMFS = {
+ ops_table: null, mount: function (mount) {
+ return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0);
+ }, createNode: function (parent, name, mode, dev) {
+ if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {
+ // no supported
+ throw new FS.ErrnoError(63);
+ }
+ if (!MEMFS.ops_table) {
+ MEMFS.ops_table = {
+ dir: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr,
+ lookup: MEMFS.node_ops.lookup,
+ mknod: MEMFS.node_ops.mknod,
+ rename: MEMFS.node_ops.rename,
+ unlink: MEMFS.node_ops.unlink,
+ rmdir: MEMFS.node_ops.rmdir,
+ readdir: MEMFS.node_ops.readdir,
+ symlink: MEMFS.node_ops.symlink
+ },
+ stream: {
+ llseek: MEMFS.stream_ops.llseek
+ }
+ },
+ file: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr
+ },
+ stream: {
+ llseek: MEMFS.stream_ops.llseek,
+ read: MEMFS.stream_ops.read,
+ write: MEMFS.stream_ops.write,
+ allocate: MEMFS.stream_ops.allocate,
+ mmap: MEMFS.stream_ops.mmap,
+ msync: MEMFS.stream_ops.msync
+ }
+ },
+ link: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr,
+ readlink: MEMFS.node_ops.readlink
+ },
+ stream: {}
+ },
+ chrdev: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr
+ },
+ stream: FS.chrdev_stream_ops
+ }
+ };
+ }
+ var node = FS.createNode(parent, name, mode, dev);
+ if (FS.isDir(node.mode)) {
+ node.node_ops = MEMFS.ops_table.dir.node;
+ node.stream_ops = MEMFS.ops_table.dir.stream;
+ node.contents = {};
+ } else if (FS.isFile(node.mode)) {
+ node.node_ops = MEMFS.ops_table.file.node;
+ node.stream_ops = MEMFS.ops_table.file.stream;
+ node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity.
+ // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
+ // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
+ // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
+ node.contents = null;
+ } else if (FS.isLink(node.mode)) {
+ node.node_ops = MEMFS.ops_table.link.node;
+ node.stream_ops = MEMFS.ops_table.link.stream;
+ } else if (FS.isChrdev(node.mode)) {
+ node.node_ops = MEMFS.ops_table.chrdev.node;
+ node.stream_ops = MEMFS.ops_table.chrdev.stream;
+ }
+ node.timestamp = Date.now();
+ // add the new node to the parent
+ if (parent) {
+ parent.contents[name] = node;
+ }
+ return node;
+ }, getFileDataAsRegularArray: function (node) {
+ if (node.contents && node.contents.subarray) {
+ var arr = [];
+ for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]);
+ return arr; // Returns a copy of the original data.
+ }
+ return node.contents; // No-op, the file contents are already in a JS array. Return as-is.
+ }, getFileDataAsTypedArray: function (node) {
+ if (!node.contents) return new Uint8Array;
+ if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
+ return new Uint8Array(node.contents);
+ }, expandFileStorage: function (node, newCapacity) {
+ var prevCapacity = node.contents ? node.contents.length : 0;
+ if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.
+ // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
+ // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
+ // avoid overshooting the allocation cap by a very large margin.
+ var CAPACITY_DOUBLING_MAX = 1024 * 1024;
+ newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) | 0);
+ if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
+ var oldContents = node.contents;
+ node.contents = new Uint8Array(newCapacity); // Allocate new storage.
+ if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.
+ return;
+ }, resizeFileStorage: function (node, newSize) {
+ if (node.usedBytes == newSize) return;
+ if (newSize == 0) {
+ node.contents = null; // Fully decommit when requesting a resize to zero.
+ node.usedBytes = 0;
+ return;
+ }
+ if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store.
+ var oldContents = node.contents;
+ node.contents = new Uint8Array(new ArrayBuffer(newSize)); // Allocate new storage.
+ if (oldContents) {
+ node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
+ }
+ node.usedBytes = newSize;
+ return;
+ }
+ // Backing with a JS array.
+ if (!node.contents) node.contents = [];
+ if (node.contents.length > newSize) node.contents.length = newSize;
+ else while (node.contents.length < newSize) node.contents.push(0);
+ node.usedBytes = newSize;
+ }, node_ops: {
+ getattr: function (node) {
+ var attr = {};
+ // device numbers reuse inode numbers.
+ attr.dev = FS.isChrdev(node.mode) ? node.id : 1;
+ attr.ino = node.id;
+ attr.mode = node.mode;
+ attr.nlink = 1;
+ attr.uid = 0;
+ attr.gid = 0;
+ attr.rdev = node.rdev;
+ if (FS.isDir(node.mode)) {
+ attr.size = 4096;
+ } else if (FS.isFile(node.mode)) {
+ attr.size = node.usedBytes;
+ } else if (FS.isLink(node.mode)) {
+ attr.size = node.link.length;
+ } else {
+ attr.size = 0;
+ }
+ attr.atime = new Date(node.timestamp);
+ attr.mtime = new Date(node.timestamp);
+ attr.ctime = new Date(node.timestamp);
+ // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize),
+ // but this is not required by the standard.
+ attr.blksize = 4096;
+ attr.blocks = Math.ceil(attr.size / attr.blksize);
+ return attr;
+ }, setattr: function (node, attr) {
+ if (attr.mode !== undefined) {
+ node.mode = attr.mode;
+ }
+ if (attr.timestamp !== undefined) {
+ node.timestamp = attr.timestamp;
+ }
+ if (attr.size !== undefined) {
+ MEMFS.resizeFileStorage(node, attr.size);
+ }
+ }, lookup: function (parent, name) {
+ throw FS.genericErrors[44];
+ }, mknod: function (parent, name, mode, dev) {
+ return MEMFS.createNode(parent, name, mode, dev);
+ }, rename: function (old_node, new_dir, new_name) {
+ // if we're overwriting a directory at new_name, make sure it's empty.
+ if (FS.isDir(old_node.mode)) {
+ var new_node;
+ try {
+ new_node = FS.lookupNode(new_dir, new_name);
+ } catch (e) {
+ }
+ if (new_node) {
+ for (var i in new_node.contents) {
+ throw new FS.ErrnoError(55);
+ }
+ }
+ }
+ // do the internal rewiring
+ delete old_node.parent.contents[old_node.name];
+ old_node.name = new_name;
+ new_dir.contents[new_name] = old_node;
+ old_node.parent = new_dir;
+ }, unlink: function (parent, name) {
+ delete parent.contents[name];
+ }, rmdir: function (parent, name) {
+ var node = FS.lookupNode(parent, name);
+ for (var i in node.contents) {
+ throw new FS.ErrnoError(55);
+ }
+ delete parent.contents[name];
+ }, readdir: function (node) {
+ var entries = ['.', '..'];
+ for (var key in node.contents) {
+ if (!node.contents.hasOwnProperty(key)) {
+ continue;
+ }
+ entries.push(key);
+ }
+ return entries;
+ }, symlink: function (parent, newname, oldpath) {
+ var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0);
+ node.link = oldpath;
+ return node;
+ }, readlink: function (node) {
+ if (!FS.isLink(node.mode)) {
+ throw new FS.ErrnoError(28);
+ }
+ return node.link;
+ }
+ }, stream_ops: {
+ read: function (stream, buffer, offset, length, position) {
+ var contents = stream.node.contents;
+ if (position >= stream.node.usedBytes) return 0;
+ var size = Math.min(stream.node.usedBytes - position, length);
+ assert(size >= 0);
+ if (size > 8 && contents.subarray) { // non-trivial, and typed array
+ buffer.set(contents.subarray(position, position + size), offset);
+ } else {
+ for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
+ }
+ return size;
+ }, write: function (stream, buffer, offset, length, position, canOwn) {
+ // The data buffer should be a typed array view
+ assert(!(buffer instanceof ArrayBuffer));
+
+ if (!length) return 0;
+ var node = stream.node;
+ node.timestamp = Date.now();
+
+ if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
+ if (canOwn) {
+ assert(position === 0, 'canOwn must imply no weird position inside the file');
+ node.contents = buffer.subarray(offset, offset + length);
+ node.usedBytes = length;
+ return length;
+ } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
+ node.contents = new Uint8Array(buffer.subarray(offset, offset + length));
+ node.usedBytes = length;
+ return length;
+ } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?
+ node.contents.set(buffer.subarray(offset, offset + length), position);
+ return length;
+ }
+ }
+
+ // Appending to an existing file and we need to reallocate, or source data did not come as a typed array.
+ MEMFS.expandFileStorage(node, position + length);
+ if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); // Use typed array write if available.
+ else {
+ for (var i = 0; i < length; i++) {
+ node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not.
+ }
+ }
+ node.usedBytes = Math.max(node.usedBytes, position + length);
+ return length;
+ }, llseek: function (stream, offset, whence) {
+ var position = offset;
+ if (whence === 1) {
+ position += stream.position;
+ } else if (whence === 2) {
+ if (FS.isFile(stream.node.mode)) {
+ position += stream.node.usedBytes;
+ }
+ }
+ if (position < 0) {
+ throw new FS.ErrnoError(28);
+ }
+ return position;
+ }, allocate: function (stream, offset, length) {
+ MEMFS.expandFileStorage(stream.node, offset + length);
+ stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);
+ }, mmap: function (stream, buffer, offset, length, position, prot, flags) {
+ // The data buffer should be a typed array view
+ assert(!(buffer instanceof ArrayBuffer));
+ if (!FS.isFile(stream.node.mode)) {
+ throw new FS.ErrnoError(43);
+ }
+ var ptr;
+ var allocated;
+ var contents = stream.node.contents;
+ // Only make a new copy when MAP_PRIVATE is specified.
+ if (!(flags & 2) &&
+ contents.buffer === buffer.buffer) {
+ // We can't emulate MAP_SHARED when the file is not backed by the buffer
+ // we're mapping to (e.g. the HEAP buffer).
+ allocated = false;
+ ptr = contents.byteOffset;
+ } else {
+ // Try to avoid unnecessary slices.
+ if (position > 0 || position + length < stream.node.usedBytes) {
+ if (contents.subarray) {
+ contents = contents.subarray(position, position + length);
+ } else {
+ contents = Array.prototype.slice.call(contents, position, position + length);
+ }
+ }
+ allocated = true;
+ // malloc() can lead to growing the heap. If targeting the heap, we need to
+ // re-acquire the heap buffer object in case growth had occurred.
+ var fromHeap = (buffer.buffer == HEAP8.buffer);
+ ptr = _malloc(length);
+ if (!ptr) {
+ throw new FS.ErrnoError(48);
+ }
+ (fromHeap ? HEAP8 : buffer).set(contents, ptr);
+ }
+ return { ptr: ptr, allocated: allocated };
+ }, msync: function (stream, buffer, offset, length, mmapFlags) {
+ if (!FS.isFile(stream.node.mode)) {
+ throw new FS.ErrnoError(43);
+ }
+ if (mmapFlags & 2) {
+ // MAP_PRIVATE calls need not to be synced back to underlying fs
+ return 0;
+ }
+
+ var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false);
+ // should we check if bytesWritten and length are the same?
+ return 0;
+ }
+ }
+};
+
+var ERRNO_MESSAGES = { 0: "Success", 1: "Arg list too long", 2: "Permission denied", 3: "Address already in use", 4: "Address not available", 5: "Address family not supported by protocol family", 6: "No more processes", 7: "Socket already connected", 8: "Bad file number", 9: "Trying to read unreadable message", 10: "Mount device busy", 11: "Operation canceled", 12: "No children", 13: "Connection aborted", 14: "Connection refused", 15: "Connection reset by peer", 16: "File locking deadlock error", 17: "Destination address required", 18: "Math arg out of domain of func", 19: "Quota exceeded", 20: "File exists", 21: "Bad address", 22: "File too large", 23: "Host is unreachable", 24: "Identifier removed", 25: "Illegal byte sequence", 26: "Connection already in progress", 27: "Interrupted system call", 28: "Invalid argument", 29: "I/O error", 30: "Socket is already connected", 31: "Is a directory", 32: "Too many symbolic links", 33: "Too many open files", 34: "Too many links", 35: "Message too long", 36: "Multihop attempted", 37: "File or path name too long", 38: "Network interface is not configured", 39: "Connection reset by network", 40: "Network is unreachable", 41: "Too many open files in system", 42: "No buffer space available", 43: "No such device", 44: "No such file or directory", 45: "Exec format error", 46: "No record locks available", 47: "The link has been severed", 48: "Not enough core", 49: "No message of desired type", 50: "Protocol not available", 51: "No space left on device", 52: "Function not implemented", 53: "Socket is not connected", 54: "Not a directory", 55: "Directory not empty", 56: "State not recoverable", 57: "Socket operation on non-socket", 59: "Not a typewriter", 60: "No such device or address", 61: "Value too large for defined data type", 62: "Previous owner died", 63: "Not super-user", 64: "Broken pipe", 65: "Protocol error", 66: "Unknown protocol", 67: "Protocol wrong type for socket", 68: "Math result not representable", 69: "Read only file system", 70: "Illegal seek", 71: "No such process", 72: "Stale file handle", 73: "Connection timed out", 74: "Text file busy", 75: "Cross-device link", 100: "Device not a stream", 101: "Bad font file fmt", 102: "Invalid slot", 103: "Invalid request code", 104: "No anode", 105: "Block device required", 106: "Channel number out of range", 107: "Level 3 halted", 108: "Level 3 reset", 109: "Link number out of range", 110: "Protocol driver not attached", 111: "No CSI structure available", 112: "Level 2 halted", 113: "Invalid exchange", 114: "Invalid request descriptor", 115: "Exchange full", 116: "No data (for no delay io)", 117: "Timer expired", 118: "Out of streams resources", 119: "Machine is not on the network", 120: "Package not installed", 121: "The object is remote", 122: "Advertise error", 123: "Srmount error", 124: "Communication error on send", 125: "Cross mount point (not really error)", 126: "Given log. name not unique", 127: "f.d. invalid for this operation", 128: "Remote address changed", 129: "Can access a needed shared lib", 130: "Accessing a corrupted shared lib", 131: ".lib section in a.out corrupted", 132: "Attempting to link in too many libs", 133: "Attempting to exec a shared library", 135: "Streams pipe error", 136: "Too many users", 137: "Socket type not supported", 138: "Not supported", 139: "Protocol family not supported", 140: "Can't send after socket shutdown", 141: "Too many references", 142: "Host is down", 148: "No medium (in tape drive)", 156: "Level 2 not synchronized" };
+
+var ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135 }; var FS = {
+ root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: "/", initialized: false, ignorePermissions: true, trackingDelegate: {}, tracking: { openFlags: { READ: 1, WRITE: 2 } }, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, handleFSError: function (e) {
+ if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace();
+ return ___setErrNo(e.errno);
+ }, lookupPath: function (path, opts) {
+ path = PATH_FS.resolve(FS.cwd(), path);
+ opts = opts || {};
+
+ if (!path) return { path: '', node: null };
+
+ var defaults = {
+ follow_mount: true,
+ recurse_count: 0
+ };
+ for (var key in defaults) {
+ if (opts[key] === undefined) {
+ opts[key] = defaults[key];
+ }
+ }
+
+ if (opts.recurse_count > 8) { // max recursive lookup of 8
+ throw new FS.ErrnoError(32);
+ }
+
+ // split the path
+ var parts = PATH.normalizeArray(path.split('/').filter(function (p) {
+ return !!p;
+ }), false);
+
+ // start at the root
+ var current = FS.root;
+ var current_path = '/';
+
+ for (var i = 0; i < parts.length; i++) {
+ var islast = (i === parts.length - 1);
+ if (islast && opts.parent) {
+ // stop resolving
+ break;
+ }
+
+ current = FS.lookupNode(current, parts[i]);
+ current_path = PATH.join2(current_path, parts[i]);
+
+ // jump to the mount's root node if this is a mountpoint
+ if (FS.isMountpoint(current)) {
+ if (!islast || (islast && opts.follow_mount)) {
+ current = current.mounted.root;
+ }
+ }
+
+ // by default, lookupPath will not follow a symlink if it is the final path component.
+ // setting opts.follow = true will override this behavior.
+ if (!islast || opts.follow) {
+ var count = 0;
+ while (FS.isLink(current.mode)) {
+ var link = FS.readlink(current_path);
+ current_path = PATH_FS.resolve(PATH.dirname(current_path), link);
+
+ var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });
+ current = lookup.node;
+
+ if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
+ throw new FS.ErrnoError(32);
+ }
+ }
+ }
+ }
+
+ return { path: current_path, node: current };
+ }, getPath: function (node) {
+ var path;
+ while (true) {
+ if (FS.isRoot(node)) {
+ var mount = node.mount.mountpoint;
+ if (!path) return mount;
+ return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path;
+ }
+ path = path ? node.name + '/' + path : node.name;
+ node = node.parent;
+ }
+ }, hashName: function (parentid, name) {
+ var hash = 0;
+
+
+ for (var i = 0; i < name.length; i++) {
+ hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0;
+ }
+ return ((parentid + hash) >>> 0) % FS.nameTable.length;
+ }, hashAddNode: function (node) {
+ var hash = FS.hashName(node.parent.id, node.name);
+ node.name_next = FS.nameTable[hash];
+ FS.nameTable[hash] = node;
+ }, hashRemoveNode: function (node) {
+ var hash = FS.hashName(node.parent.id, node.name);
+ if (FS.nameTable[hash] === node) {
+ FS.nameTable[hash] = node.name_next;
+ } else {
+ var current = FS.nameTable[hash];
+ while (current) {
+ if (current.name_next === node) {
+ current.name_next = node.name_next;
+ break;
+ }
+ current = current.name_next;
+ }
+ }
+ }, lookupNode: function (parent, name) {
+ var err = FS.mayLookup(parent);
+ if (err) {
+ throw new FS.ErrnoError(err, parent);
+ }
+ var hash = FS.hashName(parent.id, name);
+ for (var node = FS.nameTable[hash]; node; node = node.name_next) {
+ var nodeName = node.name;
+ if (node.parent.id === parent.id && nodeName === name) {
+ return node;
+ }
+ }
+ // if we failed to find it in the cache, call into the VFS
+ return FS.lookup(parent, name);
+ }, createNode: function (parent, name, mode, rdev) {
+ if (!FS.FSNode) {
+ FS.FSNode = function (parent, name, mode, rdev) {
+ if (!parent) {
+ parent = this; // root node sets parent to itself
+ }
+ this.parent = parent;
+ this.mount = parent.mount;
+ this.mounted = null;
+ this.id = FS.nextInode++;
+ this.name = name;
+ this.mode = mode;
+ this.node_ops = {};
+ this.stream_ops = {};
+ this.rdev = rdev;
+ };
+
+ FS.FSNode.prototype = {};
+
+ // compatibility
+ var readMode = 292 | 73;
+ var writeMode = 146;
+
+ // NOTE we must use Object.defineProperties instead of individual calls to
+ // Object.defineProperty in order to make closure compiler happy
+ Object.defineProperties(FS.FSNode.prototype, {
+ read: {
+ get: function () { return (this.mode & readMode) === readMode; },
+ set: function (val) { val ? this.mode |= readMode : this.mode &= ~readMode; }
+ },
+ write: {
+ get: function () { return (this.mode & writeMode) === writeMode; },
+ set: function (val) { val ? this.mode |= writeMode : this.mode &= ~writeMode; }
+ },
+ isFolder: {
+ get: function () { return FS.isDir(this.mode); }
+ },
+ isDevice: {
+ get: function () { return FS.isChrdev(this.mode); }
+ }
+ });
+ }
+
+ var node = new FS.FSNode(parent, name, mode, rdev);
+
+ FS.hashAddNode(node);
+
+ return node;
+ }, destroyNode: function (node) {
+ FS.hashRemoveNode(node);
+ }, isRoot: function (node) {
+ return node === node.parent;
+ }, isMountpoint: function (node) {
+ return !!node.mounted;
+ }, isFile: function (mode) {
+ return (mode & 61440) === 32768;
+ }, isDir: function (mode) {
+ return (mode & 61440) === 16384;
+ }, isLink: function (mode) {
+ return (mode & 61440) === 40960;
+ }, isChrdev: function (mode) {
+ return (mode & 61440) === 8192;
+ }, isBlkdev: function (mode) {
+ return (mode & 61440) === 24576;
+ }, isFIFO: function (mode) {
+ return (mode & 61440) === 4096;
+ }, isSocket: function (mode) {
+ return (mode & 49152) === 49152;
+ }, flagModes: { "r": 0, "rs": 1052672, "r+": 2, "w": 577, "wx": 705, "xw": 705, "w+": 578, "wx+": 706, "xw+": 706, "a": 1089, "ax": 1217, "xa": 1217, "a+": 1090, "ax+": 1218, "xa+": 1218 }, modeStringToFlags: function (str) {
+ var flags = FS.flagModes[str];
+ if (typeof flags === 'undefined') {
+ throw new Error('Unknown file open mode: ' + str);
+ }
+ return flags;
+ }, flagsToPermissionString: function (flag) {
+ var perms = ['r', 'w', 'rw'][flag & 3];
+ if ((flag & 512)) {
+ perms += 'w';
+ }
+ return perms;
+ }, nodePermissions: function (node, perms) {
+ if (FS.ignorePermissions) {
+ return 0;
+ }
+ // return 0 if any user, group or owner bits are set.
+ if (perms.indexOf('r') !== -1 && !(node.mode & 292)) {
+ return 2;
+ } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) {
+ return 2;
+ } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) {
+ return 2;
+ }
+ return 0;
+ }, mayLookup: function (dir) {
+ var err = FS.nodePermissions(dir, 'x');
+ if (err) return err;
+ if (!dir.node_ops.lookup) return 2;
+ return 0;
+ }, mayCreate: function (dir, name) {
+ try {
+ var node = FS.lookupNode(dir, name);
+ return 20;
+ } catch (e) {
+ }
+ return FS.nodePermissions(dir, 'wx');
+ }, mayDelete: function (dir, name, isdir) {
+ var node;
+ try {
+ node = FS.lookupNode(dir, name);
+ } catch (e) {
+ return e.errno;
+ }
+ var err = FS.nodePermissions(dir, 'wx');
+ if (err) {
+ return err;
+ }
+ if (isdir) {
+ if (!FS.isDir(node.mode)) {
+ return 54;
+ }
+ if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) {
+ return 10;
+ }
+ } else {
+ if (FS.isDir(node.mode)) {
+ return 31;
+ }
+ }
+ return 0;
+ }, mayOpen: function (node, flags) {
+ if (!node) {
+ return 44;
+ }
+ if (FS.isLink(node.mode)) {
+ return 32;
+ } else if (FS.isDir(node.mode)) {
+ if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write
+ (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only)
+ return 31;
+ }
+ }
+ return FS.nodePermissions(node, FS.flagsToPermissionString(flags));
+ }, MAX_OPEN_FDS: 4096, nextfd: function (fd_start, fd_end) {
+ fd_start = fd_start || 0;
+ fd_end = fd_end || FS.MAX_OPEN_FDS;
+ for (var fd = fd_start; fd <= fd_end; fd++) {
+ if (!FS.streams[fd]) {
+ return fd;
+ }
+ }
+ throw new FS.ErrnoError(33);
+ }, getStream: function (fd) {
+ return FS.streams[fd];
+ }, createStream: function (stream, fd_start, fd_end) {
+ if (!FS.FSStream) {
+ FS.FSStream = function () { };
+ FS.FSStream.prototype = {};
+ // compatibility
+ Object.defineProperties(FS.FSStream.prototype, {
+ object: {
+ get: function () { return this.node; },
+ set: function (val) { this.node = val; }
+ },
+ isRead: {
+ get: function () { return (this.flags & 2097155) !== 1; }
+ },
+ isWrite: {
+ get: function () { return (this.flags & 2097155) !== 0; }
+ },
+ isAppend: {
+ get: function () { return (this.flags & 1024); }
+ }
+ });
+ }
+ // clone it, so we can return an instance of FSStream
+ var newStream = new FS.FSStream();
+ for (var p in stream) {
+ newStream[p] = stream[p];
+ }
+ stream = newStream;
+ var fd = FS.nextfd(fd_start, fd_end);
+ stream.fd = fd;
+ FS.streams[fd] = stream;
+ return stream;
+ }, closeStream: function (fd) {
+ FS.streams[fd] = null;
+ }, chrdev_stream_ops: {
+ open: function (stream) {
+ var device = FS.getDevice(stream.node.rdev);
+ // override node's stream ops with the device's
+ stream.stream_ops = device.stream_ops;
+ // forward the open call
+ if (stream.stream_ops.open) {
+ stream.stream_ops.open(stream);
+ }
+ }, llseek: function () {
+ throw new FS.ErrnoError(70);
+ }
+ }, major: function (dev) {
+ return ((dev) >> 8);
+ }, minor: function (dev) {
+ return ((dev) & 0xff);
+ }, makedev: function (ma, mi) {
+ return ((ma) << 8 | (mi));
+ }, registerDevice: function (dev, ops) {
+ FS.devices[dev] = { stream_ops: ops };
+ }, getDevice: function (dev) {
+ return FS.devices[dev];
+ }, getMounts: function (mount) {
+ var mounts = [];
+ var check = [mount];
+
+ while (check.length) {
+ var m = check.pop();
+
+ mounts.push(m);
+
+ check.push.apply(check, m.mounts);
+ }
+
+ return mounts;
+ }, syncfs: function (populate, callback) {
+ if (typeof (populate) === 'function') {
+ callback = populate;
+ populate = false;
+ }
+
+ FS.syncFSRequests++;
+
+ if (FS.syncFSRequests > 1) {
+ console.log('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work');
+ }
+
+ var mounts = FS.getMounts(FS.root.mount);
+ var completed = 0;
+
+ function doCallback(err) {
+ assert(FS.syncFSRequests > 0);
+ FS.syncFSRequests--;
+ return callback(err);
+ }
+
+ function done(err) {
+ if (err) {
+ if (!done.errored) {
+ done.errored = true;
+ return doCallback(err);
+ }
+ return;
+ }
+ if (++completed >= mounts.length) {
+ doCallback(null);
+ }
+ };
+
+ // sync all mounts
+ mounts.forEach(function (mount) {
+ if (!mount.type.syncfs) {
+ return done(null);
+ }
+ mount.type.syncfs(mount, populate, done);
+ });
+ }, mount: function (type, opts, mountpoint) {
+ if (typeof type === 'string') {
+ // The filesystem was not included, and instead we have an error
+ // message stored in the variable.
+ throw type;
+ }
+ var root = mountpoint === '/';
+ var pseudo = !mountpoint;
+ var node;
+
+ if (root && FS.root) {
+ throw new FS.ErrnoError(10);
+ } else if (!root && !pseudo) {
+ var lookup = FS.lookupPath(mountpoint, { follow_mount: false });
+
+ mountpoint = lookup.path; // use the absolute path
+ node = lookup.node;
+
+ if (FS.isMountpoint(node)) {
+ throw new FS.ErrnoError(10);
+ }
+
+ if (!FS.isDir(node.mode)) {
+ throw new FS.ErrnoError(54);
+ }
+ }
+
+ var mount = {
+ type: type,
+ opts: opts,
+ mountpoint: mountpoint,
+ mounts: []
+ };
+
+ // create a root node for the fs
+ var mountRoot = type.mount(mount);
+ mountRoot.mount = mount;
+ mount.root = mountRoot;
+
+ if (root) {
+ FS.root = mountRoot;
+ } else if (node) {
+ // set as a mountpoint
+ node.mounted = mount;
+
+ // add the new mount to the current mount's children
+ if (node.mount) {
+ node.mount.mounts.push(mount);
+ }
+ }
+
+ return mountRoot;
+ }, unmount: function (mountpoint) {
+ var lookup = FS.lookupPath(mountpoint, { follow_mount: false });
+
+ if (!FS.isMountpoint(lookup.node)) {
+ throw new FS.ErrnoError(28);
+ }
+
+ // destroy the nodes for this mount, and all its child mounts
+ var node = lookup.node;
+ var mount = node.mounted;
+ var mounts = FS.getMounts(mount);
+
+ Object.keys(FS.nameTable).forEach(function (hash) {
+ var current = FS.nameTable[hash];
+
+ while (current) {
+ var next = current.name_next;
+
+ if (mounts.indexOf(current.mount) !== -1) {
+ FS.destroyNode(current);
+ }
+
+ current = next;
+ }
+ });
+
+ // no longer a mountpoint
+ node.mounted = null;
+
+ // remove this mount from the child mounts
+ var idx = node.mount.mounts.indexOf(mount);
+ assert(idx !== -1);
+ node.mount.mounts.splice(idx, 1);
+ }, lookup: function (parent, name) {
+ return parent.node_ops.lookup(parent, name);
+ }, mknod: function (path, mode, dev) {
+ var lookup = FS.lookupPath(path, { parent: true });
+ var parent = lookup.node;
+ var name = PATH.basename(path);
+ if (!name || name === '.' || name === '..') {
+ throw new FS.ErrnoError(28);
+ }
+ var err = FS.mayCreate(parent, name);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ if (!parent.node_ops.mknod) {
+ throw new FS.ErrnoError(63);
+ }
+ return parent.node_ops.mknod(parent, name, mode, dev);
+ }, create: function (path, mode) {
+ mode = mode !== undefined ? mode : 438 /* 0666 */;
+ mode &= 4095;
+ mode |= 32768;
+ return FS.mknod(path, mode, 0);
+ }, mkdir: function (path, mode) {
+ mode = mode !== undefined ? mode : 511 /* 0777 */;
+ mode &= 511 | 512;
+ mode |= 16384;
+ return FS.mknod(path, mode, 0);
+ }, mkdirTree: function (path, mode) {
+ var dirs = path.split('/');
+ var d = '';
+ for (var i = 0; i < dirs.length; ++i) {
+ if (!dirs[i]) continue;
+ d += '/' + dirs[i];
+ try {
+ FS.mkdir(d, mode);
+ } catch (e) {
+ if (e.errno != 20) throw e;
+ }
+ }
+ }, mkdev: function (path, mode, dev) {
+ if (typeof (dev) === 'undefined') {
+ dev = mode;
+ mode = 438 /* 0666 */;
+ }
+ mode |= 8192;
+ return FS.mknod(path, mode, dev);
+ }, symlink: function (oldpath, newpath) {
+ if (!PATH_FS.resolve(oldpath)) {
+ throw new FS.ErrnoError(44);
+ }
+ var lookup = FS.lookupPath(newpath, { parent: true });
+ var parent = lookup.node;
+ if (!parent) {
+ throw new FS.ErrnoError(44);
+ }
+ var newname = PATH.basename(newpath);
+ var err = FS.mayCreate(parent, newname);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ if (!parent.node_ops.symlink) {
+ throw new FS.ErrnoError(63);
+ }
+ return parent.node_ops.symlink(parent, newname, oldpath);
+ }, rename: function (old_path, new_path) {
+ var old_dirname = PATH.dirname(old_path);
+ var new_dirname = PATH.dirname(new_path);
+ var old_name = PATH.basename(old_path);
+ var new_name = PATH.basename(new_path);
+ // parents must exist
+ var lookup, old_dir, new_dir;
+ try {
+ lookup = FS.lookupPath(old_path, { parent: true });
+ old_dir = lookup.node;
+ lookup = FS.lookupPath(new_path, { parent: true });
+ new_dir = lookup.node;
+ } catch (e) {
+ throw new FS.ErrnoError(10);
+ }
+ if (!old_dir || !new_dir) throw new FS.ErrnoError(44);
+ // need to be part of the same mount
+ if (old_dir.mount !== new_dir.mount) {
+ throw new FS.ErrnoError(75);
+ }
+ // source must exist
+ var old_node = FS.lookupNode(old_dir, old_name);
+ // old path should not be an ancestor of the new path
+ var relative = PATH_FS.relative(old_path, new_dirname);
+ if (relative.charAt(0) !== '.') {
+ throw new FS.ErrnoError(28);
+ }
+ // new path should not be an ancestor of the old path
+ relative = PATH_FS.relative(new_path, old_dirname);
+ if (relative.charAt(0) !== '.') {
+ throw new FS.ErrnoError(55);
+ }
+ // see if the new path already exists
+ var new_node;
+ try {
+ new_node = FS.lookupNode(new_dir, new_name);
+ } catch (e) {
+ // not fatal
+ }
+ // early out if nothing needs to change
+ if (old_node === new_node) {
+ return;
+ }
+ // we'll need to delete the old entry
+ var isdir = FS.isDir(old_node.mode);
+ var err = FS.mayDelete(old_dir, old_name, isdir);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ // need delete permissions if we'll be overwriting.
+ // need create permissions if new doesn't already exist.
+ err = new_node ?
+ FS.mayDelete(new_dir, new_name, isdir) :
+ FS.mayCreate(new_dir, new_name);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ if (!old_dir.node_ops.rename) {
+ throw new FS.ErrnoError(63);
+ }
+ if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) {
+ throw new FS.ErrnoError(10);
+ }
+ // if we are going to change the parent, check write permissions
+ if (new_dir !== old_dir) {
+ err = FS.nodePermissions(old_dir, 'w');
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ }
+ try {
+ if (FS.trackingDelegate['willMovePath']) {
+ FS.trackingDelegate['willMovePath'](old_path, new_path);
+ }
+ } catch (e) {
+ console.log("FS.trackingDelegate['willMovePath']('" + old_path + "', '" + new_path + "') threw an exception: " + e.message);
+ }
+ // remove the node from the lookup hash
+ FS.hashRemoveNode(old_node);
+ // do the underlying fs rename
+ try {
+ old_dir.node_ops.rename(old_node, new_dir, new_name);
+ } catch (e) {
+ throw e;
+ } finally {
+ // add the node back to the hash (in case node_ops.rename
+ // changed its name)
+ FS.hashAddNode(old_node);
+ }
+ try {
+ if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path);
+ } catch (e) {
+ console.log("FS.trackingDelegate['onMovePath']('" + old_path + "', '" + new_path + "') threw an exception: " + e.message);
+ }
+ }, rmdir: function (path) {
+ var lookup = FS.lookupPath(path, { parent: true });
+ var parent = lookup.node;
+ var name = PATH.basename(path);
+ var node = FS.lookupNode(parent, name);
+ var err = FS.mayDelete(parent, name, true);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ if (!parent.node_ops.rmdir) {
+ throw new FS.ErrnoError(63);
+ }
+ if (FS.isMountpoint(node)) {
+ throw new FS.ErrnoError(10);
+ }
+ try {
+ if (FS.trackingDelegate['willDeletePath']) {
+ FS.trackingDelegate['willDeletePath'](path);
+ }
+ } catch (e) {
+ console.log("FS.trackingDelegate['willDeletePath']('" + path + "') threw an exception: " + e.message);
+ }
+ parent.node_ops.rmdir(parent, name);
+ FS.destroyNode(node);
+ try {
+ if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);
+ } catch (e) {
+ console.log("FS.trackingDelegate['onDeletePath']('" + path + "') threw an exception: " + e.message);
+ }
+ }, readdir: function (path) {
+ var lookup = FS.lookupPath(path, { follow: true });
+ var node = lookup.node;
+ if (!node.node_ops.readdir) {
+ throw new FS.ErrnoError(54);
+ }
+ return node.node_ops.readdir(node);
+ }, unlink: function (path) {
+ var lookup = FS.lookupPath(path, { parent: true });
+ var parent = lookup.node;
+ var name = PATH.basename(path);
+ var node = FS.lookupNode(parent, name);
+ var err = FS.mayDelete(parent, name, false);
+ if (err) {
+ // According to POSIX, we should map EISDIR to EPERM, but
+ // we instead do what Linux does (and we must, as we use
+ // the musl linux libc).
+ throw new FS.ErrnoError(err);
+ }
+ if (!parent.node_ops.unlink) {
+ throw new FS.ErrnoError(63);
+ }
+ if (FS.isMountpoint(node)) {
+ throw new FS.ErrnoError(10);
+ }
+ try {
+ if (FS.trackingDelegate['willDeletePath']) {
+ FS.trackingDelegate['willDeletePath'](path);
+ }
+ } catch (e) {
+ console.log("FS.trackingDelegate['willDeletePath']('" + path + "') threw an exception: " + e.message);
+ }
+ parent.node_ops.unlink(parent, name);
+ FS.destroyNode(node);
+ try {
+ if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);
+ } catch (e) {
+ console.log("FS.trackingDelegate['onDeletePath']('" + path + "') threw an exception: " + e.message);
+ }
+ }, readlink: function (path) {
+ var lookup = FS.lookupPath(path);
+ var link = lookup.node;
+ if (!link) {
+ throw new FS.ErrnoError(44);
+ }
+ if (!link.node_ops.readlink) {
+ throw new FS.ErrnoError(28);
+ }
+ return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link));
+ }, stat: function (path, dontFollow) {
+ var lookup = FS.lookupPath(path, { follow: !dontFollow });
+ var node = lookup.node;
+ if (!node) {
+ throw new FS.ErrnoError(44);
+ }
+ if (!node.node_ops.getattr) {
+ throw new FS.ErrnoError(63);
+ }
+ return node.node_ops.getattr(node);
+ }, lstat: function (path) {
+ return FS.stat(path, true);
+ }, chmod: function (path, mode, dontFollow) {
+ var node;
+ if (typeof path === 'string') {
+ var lookup = FS.lookupPath(path, { follow: !dontFollow });
+ node = lookup.node;
+ } else {
+ node = path;
+ }
+ if (!node.node_ops.setattr) {
+ throw new FS.ErrnoError(63);
+ }
+ node.node_ops.setattr(node, {
+ mode: (mode & 4095) | (node.mode & ~4095),
+ timestamp: Date.now()
+ });
+ }, lchmod: function (path, mode) {
+ FS.chmod(path, mode, true);
+ }, fchmod: function (fd, mode) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ throw new FS.ErrnoError(8);
+ }
+ FS.chmod(stream.node, mode);
+ }, chown: function (path, uid, gid, dontFollow) {
+ var node;
+ if (typeof path === 'string') {
+ var lookup = FS.lookupPath(path, { follow: !dontFollow });
+ node = lookup.node;
+ } else {
+ node = path;
+ }
+ if (!node.node_ops.setattr) {
+ throw new FS.ErrnoError(63);
+ }
+ node.node_ops.setattr(node, {
+ timestamp: Date.now()
+ // we ignore the uid / gid for now
+ });
+ }, lchown: function (path, uid, gid) {
+ FS.chown(path, uid, gid, true);
+ }, fchown: function (fd, uid, gid) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ throw new FS.ErrnoError(8);
+ }
+ FS.chown(stream.node, uid, gid);
+ }, truncate: function (path, len) {
+ if (len < 0) {
+ throw new FS.ErrnoError(28);
+ }
+ var node;
+ if (typeof path === 'string') {
+ var lookup = FS.lookupPath(path, { follow: true });
+ node = lookup.node;
+ } else {
+ node = path;
+ }
+ if (!node.node_ops.setattr) {
+ throw new FS.ErrnoError(63);
+ }
+ if (FS.isDir(node.mode)) {
+ throw new FS.ErrnoError(31);
+ }
+ if (!FS.isFile(node.mode)) {
+ throw new FS.ErrnoError(28);
+ }
+ var err = FS.nodePermissions(node, 'w');
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ node.node_ops.setattr(node, {
+ size: len,
+ timestamp: Date.now()
+ });
+ }, ftruncate: function (fd, len) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ throw new FS.ErrnoError(8);
+ }
+ if ((stream.flags & 2097155) === 0) {
+ throw new FS.ErrnoError(28);
+ }
+ FS.truncate(stream.node, len);
+ }, utime: function (path, atime, mtime) {
+ var lookup = FS.lookupPath(path, { follow: true });
+ var node = lookup.node;
+ node.node_ops.setattr(node, {
+ timestamp: Math.max(atime, mtime)
+ });
+ }, open: function (path, flags, mode, fd_start, fd_end) {
+ if (path === "") {
+ throw new FS.ErrnoError(44);
+ }
+ flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags;
+ mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode;
+ if ((flags & 64)) {
+ mode = (mode & 4095) | 32768;
+ } else {
+ mode = 0;
+ }
+ var node;
+ if (typeof path === 'object') {
+ node = path;
+ } else {
+ path = PATH.normalize(path);
+ try {
+ var lookup = FS.lookupPath(path, {
+ follow: !(flags & 131072)
+ });
+ node = lookup.node;
+ } catch (e) {
+ // ignore
+ }
+ }
+ // perhaps we need to create the node
+ var created = false;
+ if ((flags & 64)) {
+ if (node) {
+ // if O_CREAT and O_EXCL are set, error out if the node already exists
+ if ((flags & 128)) {
+ throw new FS.ErrnoError(20);
+ }
+ } else {
+ // node doesn't exist, try to create it
+ node = FS.mknod(path, mode, 0);
+ created = true;
+ }
+ }
+ if (!node) {
+ throw new FS.ErrnoError(44);
+ }
+ // can't truncate a device
+ if (FS.isChrdev(node.mode)) {
+ flags &= ~512;
+ }
+ // if asked only for a directory, then this must be one
+ if ((flags & 65536) && !FS.isDir(node.mode)) {
+ throw new FS.ErrnoError(54);
+ }
+ // check permissions, if this is not a file we just created now (it is ok to
+ // create and write to a file with read-only permissions; it is read-only
+ // for later use)
+ if (!created) {
+ var err = FS.mayOpen(node, flags);
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ }
+ // do truncation if necessary
+ if ((flags & 512)) {
+ FS.truncate(node, 0);
+ }
+ // we've already handled these, don't pass down to the underlying vfs
+ flags &= ~(128 | 512);
+
+ // register the stream with the filesystem
+ var stream = FS.createStream({
+ node: node,
+ path: FS.getPath(node), // we want the absolute path to the node
+ flags: flags,
+ seekable: true,
+ position: 0,
+ stream_ops: node.stream_ops,
+ // used by the file family libc calls (fopen, fwrite, ferror, etc.)
+ ungotten: [],
+ error: false
+ }, fd_start, fd_end);
+ // call the new stream's open function
+ if (stream.stream_ops.open) {
+ stream.stream_ops.open(stream);
+ }
+ if (Module['logReadFiles'] && !(flags & 1)) {
+ if (!FS.readFiles) FS.readFiles = {};
+ if (!(path in FS.readFiles)) {
+ FS.readFiles[path] = 1;
+ console.log("FS.trackingDelegate error on read file: " + path);
+ }
+ }
+ try {
+ if (FS.trackingDelegate['onOpenFile']) {
+ var trackingFlags = 0;
+ if ((flags & 2097155) !== 1) {
+ trackingFlags |= FS.tracking.openFlags.READ;
+ }
+ if ((flags & 2097155) !== 0) {
+ trackingFlags |= FS.tracking.openFlags.WRITE;
+ }
+ FS.trackingDelegate['onOpenFile'](path, trackingFlags);
+ }
+ } catch (e) {
+ console.log("FS.trackingDelegate['onOpenFile']('" + path + "', flags) threw an exception: " + e.message);
+ }
+ return stream;
+ }, close: function (stream) {
+ if (FS.isClosed(stream)) {
+ throw new FS.ErrnoError(8);
+ }
+ if (stream.getdents) stream.getdents = null; // free readdir state
+ try {
+ if (stream.stream_ops.close) {
+ stream.stream_ops.close(stream);
+ }
+ } catch (e) {
+ throw e;
+ } finally {
+ FS.closeStream(stream.fd);
+ }
+ stream.fd = null;
+ }, isClosed: function (stream) {
+ return stream.fd === null;
+ }, llseek: function (stream, offset, whence) {
+ if (FS.isClosed(stream)) {
+ throw new FS.ErrnoError(8);
+ }
+ if (!stream.seekable || !stream.stream_ops.llseek) {
+ throw new FS.ErrnoError(70);
+ }
+ if (whence != 0 && whence != 1 && whence != 2) {
+ throw new FS.ErrnoError(28);
+ }
+ stream.position = stream.stream_ops.llseek(stream, offset, whence);
+ stream.ungotten = [];
+ return stream.position;
+ }, read: function (stream, buffer, offset, length, position) {
+ if (length < 0 || position < 0) {
+ throw new FS.ErrnoError(28);
+ }
+ if (FS.isClosed(stream)) {
+ throw new FS.ErrnoError(8);
+ }
+ if ((stream.flags & 2097155) === 1) {
+ throw new FS.ErrnoError(8);
+ }
+ if (FS.isDir(stream.node.mode)) {
+ throw new FS.ErrnoError(31);
+ }
+ if (!stream.stream_ops.read) {
+ throw new FS.ErrnoError(28);
+ }
+ var seeking = typeof position !== 'undefined';
+ if (!seeking) {
+ position = stream.position;
+ } else if (!stream.seekable) {
+ throw new FS.ErrnoError(70);
+ }
+ var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position);
+ if (!seeking) stream.position += bytesRead;
+ return bytesRead;
+ }, write: function (stream, buffer, offset, length, position, canOwn) {
+ if (length < 0 || position < 0) {
+ throw new FS.ErrnoError(28);
+ }
+ if (FS.isClosed(stream)) {
+ throw new FS.ErrnoError(8);
+ }
+ if ((stream.flags & 2097155) === 0) {
+ throw new FS.ErrnoError(8);
+ }
+ if (FS.isDir(stream.node.mode)) {
+ throw new FS.ErrnoError(31);
+ }
+ if (!stream.stream_ops.write) {
+ throw new FS.ErrnoError(28);
+ }
+ if (stream.flags & 1024) {
+ // seek to the end before writing in append mode
+ FS.llseek(stream, 0, 2);
+ }
+ var seeking = typeof position !== 'undefined';
+ if (!seeking) {
+ position = stream.position;
+ } else if (!stream.seekable) {
+ throw new FS.ErrnoError(70);
+ }
+ var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
+ if (!seeking) stream.position += bytesWritten;
+ try {
+ if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path);
+ } catch (e) {
+ console.log("FS.trackingDelegate['onWriteToFile']('" + stream.path + "') threw an exception: " + e.message);
+ }
+ return bytesWritten;
+ }, allocate: function (stream, offset, length) {
+ if (FS.isClosed(stream)) {
+ throw new FS.ErrnoError(8);
+ }
+ if (offset < 0 || length <= 0) {
+ throw new FS.ErrnoError(28);
+ }
+ if ((stream.flags & 2097155) === 0) {
+ throw new FS.ErrnoError(8);
+ }
+ if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) {
+ throw new FS.ErrnoError(43);
+ }
+ if (!stream.stream_ops.allocate) {
+ throw new FS.ErrnoError(138);
+ }
+ stream.stream_ops.allocate(stream, offset, length);
+ }, mmap: function (stream, buffer, offset, length, position, prot, flags) {
+ // User requests writing to file (prot & PROT_WRITE != 0).
+ // Checking if we have permissions to write to the file unless
+ // MAP_PRIVATE flag is set. According to POSIX spec it is possible
+ // to write to file opened in read-only mode with MAP_PRIVATE flag,
+ // as all modifications will be visible only in the memory of
+ // the current process.
+ if ((prot & 2) !== 0
+ && (flags & 2) === 0
+ && (stream.flags & 2097155) !== 2) {
+ throw new FS.ErrnoError(2);
+ }
+ if ((stream.flags & 2097155) === 1) {
+ throw new FS.ErrnoError(2);
+ }
+ if (!stream.stream_ops.mmap) {
+ throw new FS.ErrnoError(43);
+ }
+ return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags);
+ }, msync: function (stream, buffer, offset, length, mmapFlags) {
+ if (!stream || !stream.stream_ops.msync) {
+ return 0;
+ }
+ return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags);
+ }, munmap: function (stream) {
+ return 0;
+ }, ioctl: function (stream, cmd, arg) {
+ if (!stream.stream_ops.ioctl) {
+ throw new FS.ErrnoError(59);
+ }
+ return stream.stream_ops.ioctl(stream, cmd, arg);
+ }, readFile: function (path, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'r';
+ opts.encoding = opts.encoding || 'binary';
+ if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') {
+ throw new Error('Invalid encoding type "' + opts.encoding + '"');
+ }
+ var ret;
+ var stream = FS.open(path, opts.flags);
+ var stat = FS.stat(path);
+ var length = stat.size;
+ var buf = new Uint8Array(length);
+ FS.read(stream, buf, 0, length, 0);
+ if (opts.encoding === 'utf8') {
+ ret = UTF8ArrayToString(buf, 0);
+ } else if (opts.encoding === 'binary') {
+ ret = buf;
+ }
+ FS.close(stream);
+ return ret;
+ }, writeFile: function (path, data, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'w';
+ var stream = FS.open(path, opts.flags, opts.mode);
+ if (typeof data === 'string') {
+ var buf = new Uint8Array(lengthBytesUTF8(data) + 1);
+ var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length);
+ FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn);
+ } else if (ArrayBuffer.isView(data)) {
+ FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn);
+ } else {
+ throw new Error('Unsupported data type');
+ }
+ FS.close(stream);
+ }, cwd: function () {
+ return FS.currentPath;
+ }, chdir: function (path) {
+ var lookup = FS.lookupPath(path, { follow: true });
+ if (lookup.node === null) {
+ throw new FS.ErrnoError(44);
+ }
+ if (!FS.isDir(lookup.node.mode)) {
+ throw new FS.ErrnoError(54);
+ }
+ var err = FS.nodePermissions(lookup.node, 'x');
+ if (err) {
+ throw new FS.ErrnoError(err);
+ }
+ FS.currentPath = lookup.path;
+ }, createDefaultDirectories: function () {
+ FS.mkdir('/tmp');
+ FS.mkdir('/home');
+ FS.mkdir('/home/web_user');
+ }, createDefaultDevices: function () {
+ // create /dev
+ FS.mkdir('/dev');
+ // setup /dev/null
+ FS.registerDevice(FS.makedev(1, 3), {
+ read: function () { return 0; },
+ write: function (stream, buffer, offset, length, pos) { return length; }
+ });
+ FS.mkdev('/dev/null', FS.makedev(1, 3));
+ // setup /dev/tty and /dev/tty1
+ // stderr needs to print output using Module['printErr']
+ // so we register a second tty just for it.
+ TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
+ TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
+ FS.mkdev('/dev/tty', FS.makedev(5, 0));
+ FS.mkdev('/dev/tty1', FS.makedev(6, 0));
+ // setup /dev/[u]random
+ var random_device;
+ if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') {
+ // for modern web browsers
+ var randomBuffer = new Uint8Array(1);
+ random_device = function () { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; };
+ } else
+ if (ENVIRONMENT_IS_NODE) {
+ // for nodejs with or without crypto support included
+ try {
+ var crypto_module = require('crypto');
+ // nodejs has crypto support
+ random_device = function () { return crypto_module['randomBytes'](1)[0]; };
+ } catch (e) {
+ // nodejs doesn't have crypto support
+ }
+ } else { }
+ if (!random_device) {
+ // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
+ random_device = function () { abort("no cryptographic support found for random_device. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };"); };
+ }
+ FS.createDevice('/dev', 'random', random_device);
+ FS.createDevice('/dev', 'urandom', random_device);
+ // we're not going to emulate the actual shm device,
+ // just create the tmp dirs that reside in it commonly
+ FS.mkdir('/dev/shm');
+ FS.mkdir('/dev/shm/tmp');
+ }, createSpecialDirectories: function () {
+ // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the name of the stream for fd 6 (see test_unistd_ttyname)
+ FS.mkdir('/proc');
+ FS.mkdir('/proc/self');
+ FS.mkdir('/proc/self/fd');
+ FS.mount({
+ mount: function () {
+ var node = FS.createNode('/proc/self', 'fd', 16384 | 511 /* 0777 */, 73);
+ node.node_ops = {
+ lookup: function (parent, name) {
+ var fd = +name;
+ var stream = FS.getStream(fd);
+ if (!stream) throw new FS.ErrnoError(8);
+ var ret = {
+ parent: null,
+ mount: { mountpoint: 'fake' },
+ node_ops: { readlink: function () { return stream.path } }
+ };
+ ret.parent = ret; // make it look like a simple root node
+ return ret;
+ }
+ };
+ return node;
+ }
+ }, {}, '/proc/self/fd');
+ }, createStandardStreams: function () {
+ // TODO deprecate the old functionality of a single
+ // input / output callback and that utilizes FS.createDevice
+ // and instead require a unique set of stream ops
+
+ // by default, we symlink the standard streams to the
+ // default tty devices. however, if the standard streams
+ // have been overwritten we create a unique device for
+ // them instead.
+ if (Module['stdin']) {
+ FS.createDevice('/dev', 'stdin', Module['stdin']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdin');
+ }
+ if (Module['stdout']) {
+ FS.createDevice('/dev', 'stdout', null, Module['stdout']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdout');
+ }
+ if (Module['stderr']) {
+ FS.createDevice('/dev', 'stderr', null, Module['stderr']);
+ } else {
+ FS.symlink('/dev/tty1', '/dev/stderr');
+ }
+
+ // open default streams for the stdin, stdout and stderr devices
+ var stdin = FS.open('/dev/stdin', 'r');
+ var stdout = FS.open('/dev/stdout', 'w');
+ var stderr = FS.open('/dev/stderr', 'w');
+ assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')');
+ assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')');
+ assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')');
+ }, ensureErrnoError: function () {
+ if (FS.ErrnoError) return;
+ FS.ErrnoError = function ErrnoError(errno, node) {
+ this.node = node;
+ this.setErrno = function (errno) {
+ this.errno = errno;
+ for (var key in ERRNO_CODES) {
+ if (ERRNO_CODES[key] === errno) {
+ this.code = key;
+ break;
+ }
+ }
+ };
+ this.setErrno(errno);
+ this.message = ERRNO_MESSAGES[errno];
+
+ // Try to get a maximally helpful stack trace. On Node.js, getting Error.stack
+ // now ensures it shows what we want.
+ if (this.stack) {
+ // Define the stack property for Node.js 4, which otherwise errors on the next line.
+ Object.defineProperty(this, "stack", { value: (new Error).stack, writable: true });
+ this.stack = demangleAll(this.stack);
+ }
+ };
+ FS.ErrnoError.prototype = new Error();
+ FS.ErrnoError.prototype.constructor = FS.ErrnoError;
+ // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info)
+ [44].forEach(function (code) {
+ FS.genericErrors[code] = new FS.ErrnoError(code);
+ FS.genericErrors[code].stack = '';
+ });
+ }, staticInit: function () {
+ FS.ensureErrnoError();
+
+ FS.nameTable = new Array(4096);
+
+ FS.mount(MEMFS, {}, '/');
+
+ FS.createDefaultDirectories();
+ FS.createDefaultDevices();
+ FS.createSpecialDirectories();
+
+ FS.filesystems = {
+ 'MEMFS': MEMFS,
+ };
+ }, init: function (input, output, error) {
+ assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
+ FS.init.initialized = true;
+
+ FS.ensureErrnoError();
+
+ // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
+ Module['stdin'] = input || Module['stdin'];
+ Module['stdout'] = output || Module['stdout'];
+ Module['stderr'] = error || Module['stderr'];
+
+ FS.createStandardStreams();
+ }, quit: function () {
+ FS.init.initialized = false;
+ // force-flush all streams, so we get musl std streams printed out
+ var fflush = Module['_fflush'];
+ if (fflush) fflush(0);
+ // close all of our streams
+ for (var i = 0; i < FS.streams.length; i++) {
+ var stream = FS.streams[i];
+ if (!stream) {
+ continue;
+ }
+ FS.close(stream);
+ }
+ }, getMode: function (canRead, canWrite) {
+ var mode = 0;
+ if (canRead) mode |= 292 | 73;
+ if (canWrite) mode |= 146;
+ return mode;
+ }, joinPath: function (parts, forceRelative) {
+ var path = PATH.join.apply(null, parts);
+ if (forceRelative && path[0] == '/') path = path.substr(1);
+ return path;
+ }, absolutePath: function (relative, base) {
+ return PATH_FS.resolve(base, relative);
+ }, standardizePath: function (path) {
+ return PATH.normalize(path);
+ }, findObject: function (path, dontResolveLastLink) {
+ var ret = FS.analyzePath(path, dontResolveLastLink);
+ if (ret.exists) {
+ return ret.object;
+ } else {
+ ___setErrNo(ret.error);
+ return null;
+ }
+ }, analyzePath: function (path, dontResolveLastLink) {
+ // operate from within the context of the symlink's target
+ try {
+ var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ path = lookup.path;
+ } catch (e) {
+ }
+ var ret = {
+ isRoot: false, exists: false, error: 0, name: null, path: null, object: null,
+ parentExists: false, parentPath: null, parentObject: null
+ };
+ try {
+ var lookup = FS.lookupPath(path, { parent: true });
+ ret.parentExists = true;
+ ret.parentPath = lookup.path;
+ ret.parentObject = lookup.node;
+ ret.name = PATH.basename(path);
+ lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ ret.exists = true;
+ ret.path = lookup.path;
+ ret.object = lookup.node;
+ ret.name = lookup.node.name;
+ ret.isRoot = lookup.path === '/';
+ } catch (e) {
+ ret.error = e.errno;
+ };
+ return ret;
+ }, createFolder: function (parent, name, canRead, canWrite) {
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.mkdir(path, mode);
+ }, createPath: function (parent, path, canRead, canWrite) {
+ parent = typeof parent === 'string' ? parent : FS.getPath(parent);
+ var parts = path.split('/').reverse();
+ while (parts.length) {
+ var part = parts.pop();
+ if (!part) continue;
+ var current = PATH.join2(parent, part);
+ try {
+ FS.mkdir(current);
+ } catch (e) {
+ // ignore EEXIST
+ }
+ parent = current;
+ }
+ return current;
+ }, createFile: function (parent, name, properties, canRead, canWrite) {
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.create(path, mode);
+ }, createDataFile: function (parent, name, data, canRead, canWrite, canOwn) {
+ var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
+ var mode = FS.getMode(canRead, canWrite);
+ var node = FS.create(path, mode);
+ if (data) {
+ if (typeof data === 'string') {
+ var arr = new Array(data.length);
+ for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
+ data = arr;
+ }
+ // make sure we can write to the file
+ FS.chmod(node, mode | 146);
+ var stream = FS.open(node, 'w');
+ FS.write(stream, data, 0, data.length, 0, canOwn);
+ FS.close(stream);
+ FS.chmod(node, mode);
+ }
+ return node;
+ }, createDevice: function (parent, name, input, output) {
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(!!input, !!output);
+ if (!FS.createDevice.major) FS.createDevice.major = 64;
+ var dev = FS.makedev(FS.createDevice.major++, 0);
+ // Create a fake device that a set of stream ops to emulate
+ // the old behavior.
+ FS.registerDevice(dev, {
+ open: function (stream) {
+ stream.seekable = false;
+ },
+ close: function (stream) {
+ // flush any pending line data
+ if (output && output.buffer && output.buffer.length) {
+ output(10);
+ }
+ },
+ read: function (stream, buffer, offset, length, pos /* ignored */) {
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ var result;
+ try {
+ result = input();
+ } catch (e) {
+ throw new FS.ErrnoError(29);
+ }
+ if (result === undefined && bytesRead === 0) {
+ throw new FS.ErrnoError(6);
+ }
+ if (result === null || result === undefined) break;
+ bytesRead++;
+ buffer[offset + i] = result;
+ }
+ if (bytesRead) {
+ stream.node.timestamp = Date.now();
+ }
+ return bytesRead;
+ },
+ write: function (stream, buffer, offset, length, pos) {
+ for (var i = 0; i < length; i++) {
+ try {
+ output(buffer[offset + i]);
+ } catch (e) {
+ throw new FS.ErrnoError(29);
+ }
+ }
+ if (length) {
+ stream.node.timestamp = Date.now();
+ }
+ return i;
+ }
+ });
+ return FS.mkdev(path, mode, dev);
+ }, createLink: function (parent, name, target, canRead, canWrite) {
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ return FS.symlink(target, path);
+ }, forceLoadFile: function (obj) {
+ if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
+ var success = true;
+ if (typeof XMLHttpRequest !== 'undefined') {
+ throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
+ } else if (read_) {
+ // Command-line.
+ try {
+ // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
+ // read() will try to parse UTF8.
+ obj.contents = intArrayFromString(read_(obj.url), true);
+ obj.usedBytes = obj.contents.length;
+ } catch (e) {
+ success = false;
+ }
+ } else {
+ throw new Error('Cannot load without read() or XMLHttpRequest.');
+ }
+ if (!success) ___setErrNo(29);
+ return success;
+ }, createLazyFile: function (parent, name, url, canRead, canWrite) {
+ // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
+ function LazyUint8Array() {
+ this.lengthKnown = false;
+ this.chunks = []; // Loaded chunks. Index is the chunk number
+ }
+ LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) {
+ if (idx > this.length - 1 || idx < 0) {
+ return undefined;
+ }
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = (idx / this.chunkSize) | 0;
+ return this.getter(chunkNum)[chunkOffset];
+ };
+ LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) {
+ this.getter = getter;
+ };
+ LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+ var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip";
+
+ var chunkSize = 1024 * 1024; // Chunk size in bytes
+
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function (from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength - 1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function (chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum + 1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength - 1); // if datalength-1 is selected, this is the last block
+ if (typeof (lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof (lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+
+ if (usesGzip || !datalength) {
+ // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length
+ chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file
+ datalength = this.getter(0).length;
+ chunkSize = datalength;
+ console.log("LazyFiles on gzip forces download of the whole file when length is accessed");
+ }
+
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ };
+ if (typeof XMLHttpRequest !== 'undefined') {
+ if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperties(lazyArray, {
+ length: {
+ get: function () {
+ if (!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
+ },
+ chunkSize: {
+ get: function () {
+ if (!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
+ }
+ });
+
+ var properties = { isDevice: false, contents: lazyArray };
+ } else {
+ var properties = { isDevice: false, url: url };
+ }
+
+ var node = FS.createFile(parent, name, properties, canRead, canWrite);
+ // This is a total hack, but I want to get this lazy file code out of the
+ // core of MEMFS. If we want to keep this lazy file concept I feel it should
+ // be its own thin LAZYFS proxying calls to MEMFS.
+ if (properties.contents) {
+ node.contents = properties.contents;
+ } else if (properties.url) {
+ node.contents = null;
+ node.url = properties.url;
+ }
+ // Add a function that defers querying the file size until it is asked the first time.
+ Object.defineProperties(node, {
+ usedBytes: {
+ get: function () { return this.contents.length; }
+ }
+ });
+ // override each stream op with one that tries to force load the lazy file first
+ var stream_ops = {};
+ var keys = Object.keys(node.stream_ops);
+ keys.forEach(function (key) {
+ var fn = node.stream_ops[key];
+ stream_ops[key] = function forceLoadLazyFile() {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(29);
+ }
+ return fn.apply(null, arguments);
+ };
+ });
+ // use a custom read function
+ stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(29);
+ }
+ var contents = stream.node.contents;
+ if (position >= contents.length)
+ return 0;
+ var size = Math.min(contents.length - position, length);
+ assert(size >= 0);
+ if (contents.slice) { // normal array
+ for (var i = 0; i < size; i++) {
+ buffer[offset + i] = contents[position + i];
+ }
+ } else {
+ for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
+ buffer[offset + i] = contents.get(position + i);
+ }
+ }
+ return size;
+ };
+ node.stream_ops = stream_ops;
+ return node;
+ }, createPreloadedFile: function (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) {
+ Browser.init(); // XXX perhaps this method should move onto Browser?
+ // TODO we should allow people to just pass in a complete filename instead
+ // of parent and name being that we just join them anyways
+ var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent;
+ var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname
+ function processData(byteArray) {
+ function finish(byteArray) {
+ if (preFinish) preFinish();
+ if (!dontCreateFile) {
+ FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
+ }
+ if (onload) onload();
+ removeRunDependency(dep);
+ }
+ var handled = false;
+ Module['preloadPlugins'].forEach(function (plugin) {
+ if (handled) return;
+ if (plugin['canHandle'](fullname)) {
+ plugin['handle'](byteArray, fullname, finish, function () {
+ if (onerror) onerror();
+ removeRunDependency(dep);
+ });
+ handled = true;
+ }
+ });
+ if (!handled) finish(byteArray);
+ }
+ addRunDependency(dep);
+ if (typeof url == 'string') {
+ Browser.asyncLoad(url, function (byteArray) {
+ processData(byteArray);
+ }, onerror);
+ } else {
+ processData(url);
+ }
+ }, indexedDB: function () {
+ return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+ }, DB_NAME: function () {
+ return 'EM_FS_' + window.location.pathname;
+ }, DB_VERSION: 20, DB_STORE_NAME: "FILE_DATA", saveFilesToDB: function (paths, onload, onerror) {
+ onload = onload || function () { };
+ onerror = onerror || function () { };
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = function openRequest_onupgradeneeded() {
+ console.log('creating db');
+ var db = openRequest.result;
+ db.createObjectStore(FS.DB_STORE_NAME);
+ };
+ openRequest.onsuccess = function openRequest_onsuccess() {
+ var db = openRequest.result;
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function (path) {
+ var putRequest = files.put(FS.analyzePath(path).object.contents, path);
+ putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() };
+ putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
+ }, loadFilesFromDB: function (paths, onload, onerror) {
+ onload = onload || function () { };
+ onerror = onerror || function () { };
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = onerror; // no database to load from
+ openRequest.onsuccess = function openRequest_onsuccess() {
+ var db = openRequest.result;
+ try {
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
+ } catch (e) {
+ onerror(e);
+ return;
+ }
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function (path) {
+ var getRequest = files.get(path);
+ getRequest.onsuccess = function getRequest_onsuccess() {
+ if (FS.analyzePath(path).exists) {
+ FS.unlink(path);
+ }
+ FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
+ ok++;
+ if (ok + fail == total) finish();
+ };
+ getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
+ }
+}; var SYSCALLS = {
+ DEFAULT_POLLMASK: 5, mappings: {}, umask: 511, calculateAt: function (dirfd, path) {
+ if (path[0] !== '/') {
+ // relative path
+ var dir;
+ if (dirfd === -100) {
+ dir = FS.cwd();
+ } else {
+ var dirstream = FS.getStream(dirfd);
+ if (!dirstream) throw new FS.ErrnoError(8);
+ dir = dirstream.path;
+ }
+ path = PATH.join2(dir, path);
+ }
+ return path;
+ }, doStat: function (func, path, buf) {
+ try {
+ var stat = func(path);
+ } catch (e) {
+ if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) {
+ // an error occurred while trying to look up the path; we should just report ENOTDIR
+ return -54;
+ }
+ throw e;
+ }
+ HEAP32[((buf) >> 2)] = stat.dev;
+ HEAP32[(((buf) + (4)) >> 2)] = 0;
+ HEAP32[(((buf) + (8)) >> 2)] = stat.ino;
+ HEAP32[(((buf) + (12)) >> 2)] = stat.mode;
+ HEAP32[(((buf) + (16)) >> 2)] = stat.nlink;
+ HEAP32[(((buf) + (20)) >> 2)] = stat.uid;
+ HEAP32[(((buf) + (24)) >> 2)] = stat.gid;
+ HEAP32[(((buf) + (28)) >> 2)] = stat.rdev;
+ HEAP32[(((buf) + (32)) >> 2)] = 0;
+ (tempI64 = [stat.size >>> 0, (tempDouble = stat.size, (+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble) / 4294967296.0))), 4294967295.0)) | 0) >>> 0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296.0))))) >>> 0) : 0)], HEAP32[(((buf) + (40)) >> 2)] = tempI64[0], HEAP32[(((buf) + (44)) >> 2)] = tempI64[1]);
+ HEAP32[(((buf) + (48)) >> 2)] = 4096;
+ HEAP32[(((buf) + (52)) >> 2)] = stat.blocks;
+ HEAP32[(((buf) + (56)) >> 2)] = (stat.atime.getTime() / 1000) | 0;
+ HEAP32[(((buf) + (60)) >> 2)] = 0;
+ HEAP32[(((buf) + (64)) >> 2)] = (stat.mtime.getTime() / 1000) | 0;
+ HEAP32[(((buf) + (68)) >> 2)] = 0;
+ HEAP32[(((buf) + (72)) >> 2)] = (stat.ctime.getTime() / 1000) | 0;
+ HEAP32[(((buf) + (76)) >> 2)] = 0;
+ (tempI64 = [stat.ino >>> 0, (tempDouble = stat.ino, (+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble) / 4294967296.0))), 4294967295.0)) | 0) >>> 0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296.0))))) >>> 0) : 0)], HEAP32[(((buf) + (80)) >> 2)] = tempI64[0], HEAP32[(((buf) + (84)) >> 2)] = tempI64[1]);
+ return 0;
+ }, doMsync: function (addr, stream, len, flags) {
+ var buffer = new Uint8Array(HEAPU8.subarray(addr, addr + len));
+ FS.msync(stream, buffer, 0, len, flags);
+ }, doMkdir: function (path, mode) {
+ // remove a trailing slash, if one - /a/b/ has basename of '', but
+ // we want to create b in the context of this function
+ path = PATH.normalize(path);
+ if (path[path.length - 1] === '/') path = path.substr(0, path.length - 1);
+ FS.mkdir(path, mode, 0);
+ return 0;
+ }, doMknod: function (path, mode, dev) {
+ // we don't want this in the JS API as it uses mknod to create all nodes.
+ switch (mode & 61440) {
+ case 32768:
+ case 8192:
+ case 24576:
+ case 4096:
+ case 49152:
+ break;
+ default: return -28;
+ }
+ FS.mknod(path, mode, dev);
+ return 0;
+ }, doReadlink: function (path, buf, bufsize) {
+ if (bufsize <= 0) return -28;
+ var ret = FS.readlink(path);
+
+ var len = Math.min(bufsize, lengthBytesUTF8(ret));
+ var endChar = HEAP8[buf + len];
+ stringToUTF8(ret, buf, bufsize + 1);
+ // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!)
+ // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write.
+ HEAP8[buf + len] = endChar;
+
+ return len;
+ }, doAccess: function (path, amode) {
+ if (amode & ~7) {
+ // need a valid mode
+ return -28;
+ }
+ var node;
+ var lookup = FS.lookupPath(path, { follow: true });
+ node = lookup.node;
+ if (!node) {
+ return -44;
+ }
+ var perms = '';
+ if (amode & 4) perms += 'r';
+ if (amode & 2) perms += 'w';
+ if (amode & 1) perms += 'x';
+ if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) {
+ return -2;
+ }
+ return 0;
+ }, doDup: function (path, flags, suggestFD) {
+ var suggest = FS.getStream(suggestFD);
+ if (suggest) FS.close(suggest);
+ return FS.open(path, flags, 0, suggestFD, suggestFD).fd;
+ }, doReadv: function (stream, iov, iovcnt, offset) {
+ var ret = 0;
+ for (var i = 0; i < iovcnt; i++) {
+ var ptr = HEAP32[(((iov) + (i * 8)) >> 2)];
+ var len = HEAP32[(((iov) + (i * 8 + 4)) >> 2)];
+ var curr = FS.read(stream, HEAP8, ptr, len, offset);
+ if (curr < 0) return -1;
+ ret += curr;
+ if (curr < len) break; // nothing more to read
+ }
+ return ret;
+ }, doWritev: function (stream, iov, iovcnt, offset) {
+ var ret = 0;
+ for (var i = 0; i < iovcnt; i++) {
+ var ptr = HEAP32[(((iov) + (i * 8)) >> 2)];
+ var len = HEAP32[(((iov) + (i * 8 + 4)) >> 2)];
+ var curr = FS.write(stream, HEAP8, ptr, len, offset);
+ if (curr < 0) return -1;
+ ret += curr;
+ }
+ return ret;
+ }, varargs: 0, get: function (varargs) {
+ SYSCALLS.varargs += 4;
+ var ret = HEAP32[(((SYSCALLS.varargs) - (4)) >> 2)];
+ return ret;
+ }, getStr: function () {
+ var ret = UTF8ToString(SYSCALLS.get());
+ return ret;
+ }, getStreamFromFD: function (fd) {
+ // TODO: when all syscalls use wasi, can remove the next line
+ if (fd === undefined) fd = SYSCALLS.get();
+ var stream = FS.getStream(fd);
+ if (!stream) throw new FS.ErrnoError(8);
+ return stream;
+ }, get64: function () {
+ var low = SYSCALLS.get(), high = SYSCALLS.get();
+ if (low >= 0) assert(high === 0);
+ else assert(high === -1);
+ return low;
+ }, getZero: function () {
+ assert(SYSCALLS.get() === 0);
+ }
+}; function __emscripten_syscall_munmap(addr, len) {
+ if (addr === -1 || len === 0) {
+ return -28;
+ }
+ // TODO: support unmmap'ing parts of allocations
+ var info = SYSCALLS.mappings[addr];
+ if (!info) return 0;
+ if (len === info.len) {
+ var stream = FS.getStream(info.fd);
+ SYSCALLS.doMsync(addr, stream, len, info.flags);
+ FS.munmap(stream);
+ SYSCALLS.mappings[addr] = null;
+ if (info.allocated) {
+ _free(info.malloc);
+ }
+ }
+ return 0;
+} function ___syscall91(which, varargs) {
+ SYSCALLS.varargs = varargs;
+ try {
+ // munmap
+ var addr = SYSCALLS.get(), len = SYSCALLS.get();
+ return __emscripten_syscall_munmap(addr, len);
+ } catch (e) {
+ if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);
+ return -e.errno;
+ }
+}
+
+function ___unlock() { }
+
+function _abort() {
+ abort();
+}
+
+function _emscripten_get_heap_size() {
+ return HEAP8.length;
+}
+
+function _emscripten_get_sbrk_ptr() {
+ return 22688;
+}
+
+function _emscripten_memcpy_big(dest, src, num) {
+ HEAPU8.set(HEAPU8.subarray(src, src + num), dest);
+}
+
+
+function abortOnCannotGrowMemory(requestedSize) {
+ abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ');
+} function _emscripten_resize_heap(requestedSize) {
+ abortOnCannotGrowMemory(requestedSize);
+}
+
+
+
+var ENV = {}; function _emscripten_get_environ() {
+ if (!_emscripten_get_environ.strings) {
+ // Default values.
+ var env = {
+ 'USER': 'web_user',
+ 'LOGNAME': 'web_user',
+ 'PATH': '/',
+ 'PWD': '/',
+ 'HOME': '/home/web_user',
+ // Browser language detection #8751
+ 'LANG': ((typeof navigator === 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8',
+ '_': thisProgram
+ };
+ // Apply the user-provided values, if any.
+ for (var x in ENV) {
+ env[x] = ENV[x];
+ }
+ var strings = [];
+ for (var x in env) {
+ strings.push(x + '=' + env[x]);
+ }
+ _emscripten_get_environ.strings = strings;
+ }
+ return _emscripten_get_environ.strings;
+} function _environ_get(__environ, environ_buf) {
+ var strings = _emscripten_get_environ();
+ var bufSize = 0;
+ strings.forEach(function (string, i) {
+ var ptr = environ_buf + bufSize;
+ HEAP32[(((__environ) + (i * 4)) >> 2)] = ptr;
+ writeAsciiToMemory(string, ptr);
+ bufSize += string.length + 1;
+ });
+ return 0;
+}
+
+function _environ_sizes_get(penviron_count, penviron_buf_size) {
+ var strings = _emscripten_get_environ();
+ HEAP32[((penviron_count) >> 2)] = strings.length;
+ var bufSize = 0;
+ strings.forEach(function (string) {
+ bufSize += string.length + 1;
+ });
+ HEAP32[((penviron_buf_size) >> 2)] = bufSize;
+ return 0;
+}
+
+function _fd_close(fd) {
+ try {
+
+ var stream = SYSCALLS.getStreamFromFD(fd);
+ FS.close(stream);
+ return 0;
+ } catch (e) {
+ if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);
+ return e.errno;
+ }
+}
+
+function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {
+ try {
+
+ var stream = SYSCALLS.getStreamFromFD(fd);
+ var HIGH_OFFSET = 0x100000000; // 2^32
+ // use an unsigned operator on low and shift high by 32-bits
+ var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0);
+
+ var DOUBLE_LIMIT = 0x20000000000000; // 2^53
+ // we also check for equality since DOUBLE_LIMIT + 1 == DOUBLE_LIMIT
+ if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) {
+ return -61;
+ }
+
+ FS.llseek(stream, offset, whence);
+ (tempI64 = [stream.position >>> 0, (tempDouble = stream.position, (+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble) / 4294967296.0))), 4294967295.0)) | 0) >>> 0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble))) >>> 0)) / 4294967296.0))))) >>> 0) : 0)], HEAP32[((newOffset) >> 2)] = tempI64[0], HEAP32[(((newOffset) + (4)) >> 2)] = tempI64[1]);
+ if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state
+ return 0;
+ } catch (e) {
+ if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);
+ return e.errno;
+ }
+}
+
+function _fd_write(fd, iov, iovcnt, pnum) {
+ try {
+
+ var stream = SYSCALLS.getStreamFromFD(fd);
+ var num = SYSCALLS.doWritev(stream, iov, iovcnt);
+ HEAP32[((pnum) >> 2)] = num
+ return 0;
+ } catch (e) {
+ if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);
+ return e.errno;
+ }
+}
+
+
+function _memcpy(dest, src, num) {
+ dest = dest | 0; src = src | 0; num = num | 0;
+ var ret = 0;
+ var aligned_dest_end = 0;
+ var block_aligned_dest_end = 0;
+ var dest_end = 0;
+ // Test against a benchmarked cutoff limit for when HEAPU8.set() becomes faster to use.
+ if ((num | 0) >= 8192) {
+ _emscripten_memcpy_big(dest | 0, src | 0, num | 0) | 0;
+ return dest | 0;
+ }
+
+ ret = dest | 0;
+ dest_end = (dest + num) | 0;
+ if ((dest & 3) == (src & 3)) {
+ // The initial unaligned < 4-byte front.
+ while (dest & 3) {
+ if ((num | 0) == 0) return ret | 0;
+ HEAP8[((dest) >> 0)] = ((HEAP8[((src) >> 0)]) | 0);
+ dest = (dest + 1) | 0;
+ src = (src + 1) | 0;
+ num = (num - 1) | 0;
+ }
+ aligned_dest_end = (dest_end & -4) | 0;
+ block_aligned_dest_end = (aligned_dest_end - 64) | 0;
+ while ((dest | 0) <= (block_aligned_dest_end | 0)) {
+ HEAP32[((dest) >> 2)] = ((HEAP32[((src) >> 2)]) | 0);
+ HEAP32[(((dest) + (4)) >> 2)] = ((HEAP32[(((src) + (4)) >> 2)]) | 0);
+ HEAP32[(((dest) + (8)) >> 2)] = ((HEAP32[(((src) + (8)) >> 2)]) | 0);
+ HEAP32[(((dest) + (12)) >> 2)] = ((HEAP32[(((src) + (12)) >> 2)]) | 0);
+ HEAP32[(((dest) + (16)) >> 2)] = ((HEAP32[(((src) + (16)) >> 2)]) | 0);
+ HEAP32[(((dest) + (20)) >> 2)] = ((HEAP32[(((src) + (20)) >> 2)]) | 0);
+ HEAP32[(((dest) + (24)) >> 2)] = ((HEAP32[(((src) + (24)) >> 2)]) | 0);
+ HEAP32[(((dest) + (28)) >> 2)] = ((HEAP32[(((src) + (28)) >> 2)]) | 0);
+ HEAP32[(((dest) + (32)) >> 2)] = ((HEAP32[(((src) + (32)) >> 2)]) | 0);
+ HEAP32[(((dest) + (36)) >> 2)] = ((HEAP32[(((src) + (36)) >> 2)]) | 0);
+ HEAP32[(((dest) + (40)) >> 2)] = ((HEAP32[(((src) + (40)) >> 2)]) | 0);
+ HEAP32[(((dest) + (44)) >> 2)] = ((HEAP32[(((src) + (44)) >> 2)]) | 0);
+ HEAP32[(((dest) + (48)) >> 2)] = ((HEAP32[(((src) + (48)) >> 2)]) | 0);
+ HEAP32[(((dest) + (52)) >> 2)] = ((HEAP32[(((src) + (52)) >> 2)]) | 0);
+ HEAP32[(((dest) + (56)) >> 2)] = ((HEAP32[(((src) + (56)) >> 2)]) | 0);
+ HEAP32[(((dest) + (60)) >> 2)] = ((HEAP32[(((src) + (60)) >> 2)]) | 0);
+ dest = (dest + 64) | 0;
+ src = (src + 64) | 0;
+ }
+ while ((dest | 0) < (aligned_dest_end | 0)) {
+ HEAP32[((dest) >> 2)] = ((HEAP32[((src) >> 2)]) | 0);
+ dest = (dest + 4) | 0;
+ src = (src + 4) | 0;
+ }
+ } else {
+ // In the unaligned copy case, unroll a bit as well.
+ aligned_dest_end = (dest_end - 4) | 0;
+ while ((dest | 0) < (aligned_dest_end | 0)) {
+ HEAP8[((dest) >> 0)] = ((HEAP8[((src) >> 0)]) | 0);
+ HEAP8[(((dest) + (1)) >> 0)] = ((HEAP8[(((src) + (1)) >> 0)]) | 0);
+ HEAP8[(((dest) + (2)) >> 0)] = ((HEAP8[(((src) + (2)) >> 0)]) | 0);
+ HEAP8[(((dest) + (3)) >> 0)] = ((HEAP8[(((src) + (3)) >> 0)]) | 0);
+ dest = (dest + 4) | 0;
+ src = (src + 4) | 0;
+ }
+ }
+ // The remaining unaligned < 4 byte tail.
+ while ((dest | 0) < (dest_end | 0)) {
+ HEAP8[((dest) >> 0)] = ((HEAP8[((src) >> 0)]) | 0);
+ dest = (dest + 1) | 0;
+ src = (src + 1) | 0;
+ }
+ return ret | 0;
+}
+
+function _memset(ptr, value, num) {
+ ptr = ptr | 0; value = value | 0; num = num | 0;
+ var end = 0, aligned_end = 0, block_aligned_end = 0, value4 = 0;
+ end = (ptr + num) | 0;
+
+ value = value & 0xff;
+ if ((num | 0) >= 67 /* 64 bytes for an unrolled loop + 3 bytes for unaligned head*/) {
+ while ((ptr & 3) != 0) {
+ HEAP8[((ptr) >> 0)] = value;
+ ptr = (ptr + 1) | 0;
+ }
+
+ aligned_end = (end & -4) | 0;
+ value4 = value | (value << 8) | (value << 16) | (value << 24);
+
+ block_aligned_end = (aligned_end - 64) | 0;
+
+ while ((ptr | 0) <= (block_aligned_end | 0)) {
+ HEAP32[((ptr) >> 2)] = value4;
+ HEAP32[(((ptr) + (4)) >> 2)] = value4;
+ HEAP32[(((ptr) + (8)) >> 2)] = value4;
+ HEAP32[(((ptr) + (12)) >> 2)] = value4;
+ HEAP32[(((ptr) + (16)) >> 2)] = value4;
+ HEAP32[(((ptr) + (20)) >> 2)] = value4;
+ HEAP32[(((ptr) + (24)) >> 2)] = value4;
+ HEAP32[(((ptr) + (28)) >> 2)] = value4;
+ HEAP32[(((ptr) + (32)) >> 2)] = value4;
+ HEAP32[(((ptr) + (36)) >> 2)] = value4;
+ HEAP32[(((ptr) + (40)) >> 2)] = value4;
+ HEAP32[(((ptr) + (44)) >> 2)] = value4;
+ HEAP32[(((ptr) + (48)) >> 2)] = value4;
+ HEAP32[(((ptr) + (52)) >> 2)] = value4;
+ HEAP32[(((ptr) + (56)) >> 2)] = value4;
+ HEAP32[(((ptr) + (60)) >> 2)] = value4;
+ ptr = (ptr + 64) | 0;
+ }
+
+ while ((ptr | 0) < (aligned_end | 0)) {
+ HEAP32[((ptr) >> 2)] = value4;
+ ptr = (ptr + 4) | 0;
+ }
+ }
+ // The remaining bytes.
+ while ((ptr | 0) < (end | 0)) {
+ HEAP8[((ptr) >> 0)] = value;
+ ptr = (ptr + 1) | 0;
+ }
+ return (end - num) | 0;
+}
+
+function _setTempRet0($i) {
+ setTempRet0(($i) | 0);
+}
+
+
+
+function __isLeapYear(year) {
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
+}
+
+function __arraySum(array, index) {
+ var sum = 0;
+ for (var i = 0; i <= index; sum += array[i++]);
+ return sum;
+}
+
+
+var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; function __addDays(date, days) {
+ var newDate = new Date(date.getTime());
+ while (days > 0) {
+ var leap = __isLeapYear(newDate.getFullYear());
+ var currentMonth = newDate.getMonth();
+ var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth];
+
+ if (days > daysInCurrentMonth - newDate.getDate()) {
+ // we spill over to next month
+ days -= (daysInCurrentMonth - newDate.getDate() + 1);
+ newDate.setDate(1);
+ if (currentMonth < 11) {
+ newDate.setMonth(currentMonth + 1)
+ } else {
+ newDate.setMonth(0);
+ newDate.setFullYear(newDate.getFullYear() + 1);
+ }
+ } else {
+ // we stay in current month
+ newDate.setDate(newDate.getDate() + days);
+ return newDate;
+ }
+ }
+
+ return newDate;
+} function _strftime(s, maxsize, format, tm) {
+ // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
+
+ var tm_zone = HEAP32[(((tm) + (40)) >> 2)];
+
+ var date = {
+ tm_sec: HEAP32[((tm) >> 2)],
+ tm_min: HEAP32[(((tm) + (4)) >> 2)],
+ tm_hour: HEAP32[(((tm) + (8)) >> 2)],
+ tm_mday: HEAP32[(((tm) + (12)) >> 2)],
+ tm_mon: HEAP32[(((tm) + (16)) >> 2)],
+ tm_year: HEAP32[(((tm) + (20)) >> 2)],
+ tm_wday: HEAP32[(((tm) + (24)) >> 2)],
+ tm_yday: HEAP32[(((tm) + (28)) >> 2)],
+ tm_isdst: HEAP32[(((tm) + (32)) >> 2)],
+ tm_gmtoff: HEAP32[(((tm) + (36)) >> 2)],
+ tm_zone: tm_zone ? UTF8ToString(tm_zone) : ''
+ };
+
+ var pattern = UTF8ToString(format);
+
+ // expand format
+ var EXPANSION_RULES_1 = {
+ '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013
+ '%D': '%m/%d/%y', // Equivalent to %m / %d / %y
+ '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d
+ '%h': '%b', // Equivalent to %b
+ '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation
+ '%R': '%H:%M', // Replaced by the time in 24-hour notation
+ '%T': '%H:%M:%S', // Replaced by the time
+ '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation
+ '%X': '%H:%M:%S', // Replaced by the locale's appropriate time representation
+ // Modified Conversion Specifiers
+ '%Ec': '%c', // Replaced by the locale's alternative appropriate date and time representation.
+ '%EC': '%C', // Replaced by the name of the base year (period) in the locale's alternative representation.
+ '%Ex': '%m/%d/%y', // Replaced by the locale's alternative date representation.
+ '%EX': '%H:%M:%S', // Replaced by the locale's alternative time representation.
+ '%Ey': '%y', // Replaced by the offset from %EC (year only) in the locale's alternative representation.
+ '%EY': '%Y', // Replaced by the full alternative year representation.
+ '%Od': '%d', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading zeros if there is any alternative symbol for zero; otherwise, with leading characters.
+ '%Oe': '%e', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading characters.
+ '%OH': '%H', // Replaced by the hour (24-hour clock) using the locale's alternative numeric symbols.
+ '%OI': '%I', // Replaced by the hour (12-hour clock) using the locale's alternative numeric symbols.
+ '%Om': '%m', // Replaced by the month using the locale's alternative numeric symbols.
+ '%OM': '%M', // Replaced by the minutes using the locale's alternative numeric symbols.
+ '%OS': '%S', // Replaced by the seconds using the locale's alternative numeric symbols.
+ '%Ou': '%u', // Replaced by the weekday as a number in the locale's alternative representation (Monday=1).
+ '%OU': '%U', // Replaced by the week number of the year (Sunday as the first day of the week, rules corresponding to %U ) using the locale's alternative numeric symbols.
+ '%OV': '%V', // Replaced by the week number of the year (Monday as the first day of the week, rules corresponding to %V ) using the locale's alternative numeric symbols.
+ '%Ow': '%w', // Replaced by the number of the weekday (Sunday=0) using the locale's alternative numeric symbols.
+ '%OW': '%W', // Replaced by the week number of the year (Monday as the first day of the week) using the locale's alternative numeric symbols.
+ '%Oy': '%y', // Replaced by the year (offset from %C ) using the locale's alternative numeric symbols.
+ };
+ for (var rule in EXPANSION_RULES_1) {
+ pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]);
+ }
+
+ var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+ var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+
+ function leadingSomething(value, digits, character) {
+ var str = typeof value === 'number' ? value.toString() : (value || '');
+ while (str.length < digits) {
+ str = character[0] + str;
+ }
+ return str;
+ }
+
+ function leadingNulls(value, digits) {
+ return leadingSomething(value, digits, '0');
+ }
+
+ function compareByDay(date1, date2) {
+ function sgn(value) {
+ return value < 0 ? -1 : (value > 0 ? 1 : 0);
+ }
+
+ var compare;
+ if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) {
+ if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) {
+ compare = sgn(date1.getDate() - date2.getDate());
+ }
+ }
+ return compare;
+ }
+
+ function getFirstWeekStartDate(janFourth) {
+ switch (janFourth.getDay()) {
+ case 0: // Sunday
+ return new Date(janFourth.getFullYear() - 1, 11, 29);
+ case 1: // Monday
+ return janFourth;
+ case 2: // Tuesday
+ return new Date(janFourth.getFullYear(), 0, 3);
+ case 3: // Wednesday
+ return new Date(janFourth.getFullYear(), 0, 2);
+ case 4: // Thursday
+ return new Date(janFourth.getFullYear(), 0, 1);
+ case 5: // Friday
+ return new Date(janFourth.getFullYear() - 1, 11, 31);
+ case 6: // Saturday
+ return new Date(janFourth.getFullYear() - 1, 11, 30);
+ }
+ }
+
+ function getWeekBasedYear(date) {
+ var thisDate = __addDays(new Date(date.tm_year + 1900, 0, 1), date.tm_yday);
+
+ var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4);
+ var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4);
+
+ var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
+ var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
+
+ if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) {
+ // this date is after the start of the first week of this year
+ if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) {
+ return thisDate.getFullYear() + 1;
+ } else {
+ return thisDate.getFullYear();
+ }
+ } else {
+ return thisDate.getFullYear() - 1;
+ }
+ }
+
+ var EXPANSION_RULES_2 = {
+ '%a': function (date) {
+ return WEEKDAYS[date.tm_wday].substring(0, 3);
+ },
+ '%A': function (date) {
+ return WEEKDAYS[date.tm_wday];
+ },
+ '%b': function (date) {
+ return MONTHS[date.tm_mon].substring(0, 3);
+ },
+ '%B': function (date) {
+ return MONTHS[date.tm_mon];
+ },
+ '%C': function (date) {
+ var year = date.tm_year + 1900;
+ return leadingNulls((year / 100) | 0, 2);
+ },
+ '%d': function (date) {
+ return leadingNulls(date.tm_mday, 2);
+ },
+ '%e': function (date) {
+ return leadingSomething(date.tm_mday, 2, ' ');
+ },
+ '%g': function (date) {
+ // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year.
+ // In this system, weeks begin on a Monday and week 1 of the year is the week that includes
+ // January 4th, which is also the week that includes the first Thursday of the year, and
+ // is also the first week that contains at least four days in the year.
+ // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of
+ // the last week of the preceding year; thus, for Saturday 2nd January 1999,
+ // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th,
+ // or 31st is a Monday, it and any following days are part of week 1 of the following year.
+ // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01.
+
+ return getWeekBasedYear(date).toString().substring(2);
+ },
+ '%G': function (date) {
+ return getWeekBasedYear(date);
+ },
+ '%H': function (date) {
+ return leadingNulls(date.tm_hour, 2);
+ },
+ '%I': function (date) {
+ var twelveHour = date.tm_hour;
+ if (twelveHour == 0) twelveHour = 12;
+ else if (twelveHour > 12) twelveHour -= 12;
+ return leadingNulls(twelveHour, 2);
+ },
+ '%j': function (date) {
+ // Day of the year (001-366)
+ return leadingNulls(date.tm_mday + __arraySum(__isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1), 3);
+ },
+ '%m': function (date) {
+ return leadingNulls(date.tm_mon + 1, 2);
+ },
+ '%M': function (date) {
+ return leadingNulls(date.tm_min, 2);
+ },
+ '%n': function () {
+ return '\n';
+ },
+ '%p': function (date) {
+ if (date.tm_hour >= 0 && date.tm_hour < 12) {
+ return 'AM';
+ } else {
+ return 'PM';
+ }
+ },
+ '%S': function (date) {
+ return leadingNulls(date.tm_sec, 2);
+ },
+ '%t': function () {
+ return '\t';
+ },
+ '%u': function (date) {
+ return date.tm_wday || 7;
+ },
+ '%U': function (date) {
+ // Replaced by the week number of the year as a decimal number [00,53].
+ // The first Sunday of January is the first day of week 1;
+ // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+ var janFirst = new Date(date.tm_year + 1900, 0, 1);
+ var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7 - janFirst.getDay());
+ var endDate = new Date(date.tm_year + 1900, date.tm_mon, date.tm_mday);
+
+ // is target date after the first Sunday?
+ if (compareByDay(firstSunday, endDate) < 0) {
+ // calculate difference in days between first Sunday and endDate
+ var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth() - 1) - 31;
+ var firstSundayUntilEndJanuary = 31 - firstSunday.getDate();
+ var days = firstSundayUntilEndJanuary + februaryFirstUntilEndMonth + endDate.getDate();
+ return leadingNulls(Math.ceil(days / 7), 2);
+ }
+
+ return compareByDay(firstSunday, janFirst) === 0 ? '01' : '00';
+ },
+ '%V': function (date) {
+ // Replaced by the week number of the year (Monday as the first day of the week)
+ // as a decimal number [01,53]. If the week containing 1 January has four
+ // or more days in the new year, then it is considered week 1.
+ // Otherwise, it is the last week of the previous year, and the next week is week 1.
+ // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
+ var janFourthThisYear = new Date(date.tm_year + 1900, 0, 4);
+ var janFourthNextYear = new Date(date.tm_year + 1901, 0, 4);
+
+ var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
+ var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
+
+ var endDate = __addDays(new Date(date.tm_year + 1900, 0, 1), date.tm_yday);
+
+ if (compareByDay(endDate, firstWeekStartThisYear) < 0) {
+ // if given date is before this years first week, then it belongs to the 53rd week of last year
+ return '53';
+ }
+
+ if (compareByDay(firstWeekStartNextYear, endDate) <= 0) {
+ // if given date is after next years first week, then it belongs to the 01th week of next year
+ return '01';
+ }
+
+ // given date is in between CW 01..53 of this calendar year
+ var daysDifference;
+ if (firstWeekStartThisYear.getFullYear() < date.tm_year + 1900) {
+ // first CW of this year starts last year
+ daysDifference = date.tm_yday + 32 - firstWeekStartThisYear.getDate()
+ } else {
+ // first CW of this year starts this year
+ daysDifference = date.tm_yday + 1 - firstWeekStartThisYear.getDate();
+ }
+ return leadingNulls(Math.ceil(daysDifference / 7), 2);
+ },
+ '%w': function (date) {
+ return date.tm_wday;
+ },
+ '%W': function (date) {
+ // Replaced by the week number of the year as a decimal number [00,53].
+ // The first Monday of January is the first day of week 1;
+ // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+ var janFirst = new Date(date.tm_year, 0, 1);
+ var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7 - janFirst.getDay() + 1);
+ var endDate = new Date(date.tm_year + 1900, date.tm_mon, date.tm_mday);
+
+ // is target date after the first Monday?
+ if (compareByDay(firstMonday, endDate) < 0) {
+ var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth() - 1) - 31;
+ var firstMondayUntilEndJanuary = 31 - firstMonday.getDate();
+ var days = firstMondayUntilEndJanuary + februaryFirstUntilEndMonth + endDate.getDate();
+ return leadingNulls(Math.ceil(days / 7), 2);
+ }
+ return compareByDay(firstMonday, janFirst) === 0 ? '01' : '00';
+ },
+ '%y': function (date) {
+ // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year]
+ return (date.tm_year + 1900).toString().substring(2);
+ },
+ '%Y': function (date) {
+ // Replaced by the year as a decimal number (for example, 1997). [ tm_year]
+ return date.tm_year + 1900;
+ },
+ '%z': function (date) {
+ // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ).
+ // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich).
+ var off = date.tm_gmtoff;
+ var ahead = off >= 0;
+ off = Math.abs(off) / 60;
+ // convert from minutes into hhmm format (which means 60 minutes = 100 units)
+ off = (off / 60) * 100 + (off % 60);
+ return (ahead ? '+' : '-') + String("0000" + off).slice(-4);
+ },
+ '%Z': function (date) {
+ return date.tm_zone;
+ },
+ '%%': function () {
+ return '%';
+ }
+ };
+ for (var rule in EXPANSION_RULES_2) {
+ if (pattern.indexOf(rule) >= 0) {
+ pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date));
+ }
+ }
+
+ var bytes = intArrayFromString(pattern, false);
+ if (bytes.length > maxsize) {
+ return 0;
+ }
+
+ writeArrayToMemory(bytes, s);
+ return bytes.length - 1;
+} function _strftime_l(s, maxsize, format, tm) {
+ return _strftime(s, maxsize, format, tm); // no locale support yet
+}
+FS.staticInit();;
+var ASSERTIONS = true;
+
+// Copyright 2017 The Emscripten Authors. All rights reserved.
+// Emscripten is available under two separate licenses, the MIT license and the
+// University of Illinois/NCSA Open Source License. Both these licenses can be
+// found in the LICENSE file.
+
+/** @type {function(string, boolean=, number=)} */
+function intArrayFromString(stringy, dontAddNull, length) {
+ var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1;
+ var u8array = new Array(len);
+ var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);
+ if (dontAddNull) u8array.length = numBytesWritten;
+ return u8array;
+}
+
+function intArrayToString(array) {
+ var ret = [];
+ for (var i = 0; i < array.length; i++) {
+ var chr = array[i];
+ if (chr > 0xFF) {
+ if (ASSERTIONS) {
+ assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.');
+ }
+ chr &= 0xFF;
+ }
+ ret.push(String.fromCharCode(chr));
+ }
+ return ret.join('');
+}
+
+
+// ASM_LIBRARY EXTERN PRIMITIVES: Int8Array,Int32Array
+
+var asmGlobalArg = {};
+var asmLibraryArg = { "__cxa_atexit": ___cxa_atexit, "__handle_stack_overflow": ___handle_stack_overflow, "__lock": ___lock, "__map_file": ___map_file, "__syscall91": ___syscall91, "__unlock": ___unlock, "abort": _abort, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "environ_get": _environ_get, "environ_sizes_get": _environ_sizes_get, "fd_close": _fd_close, "fd_seek": _fd_seek, "fd_write": _fd_write, "memory": wasmMemory, "setTempRet0": _setTempRet0, "strftime_l": _strftime_l, "table": wasmTable };
+var asm = createWasm();
+var real____wasm_call_ctors = asm["__wasm_call_ctors"];
+asm["__wasm_call_ctors"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real____wasm_call_ctors.apply(null, arguments);
+};
+
+var real__AStyleMain = asm["AStyleMain"];
+asm["AStyleMain"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__AStyleMain.apply(null, arguments);
+};
+
+var real__AStyleMainUtf16 = asm["AStyleMainUtf16"];
+asm["AStyleMainUtf16"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__AStyleMainUtf16.apply(null, arguments);
+};
+
+var real__AStyleGetVersion = asm["AStyleGetVersion"];
+asm["AStyleGetVersion"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__AStyleGetVersion.apply(null, arguments);
+};
+
+var real__fflush = asm["fflush"];
+asm["fflush"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__fflush.apply(null, arguments);
+};
+
+var real____errno_location = asm["__errno_location"];
+asm["__errno_location"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real____errno_location.apply(null, arguments);
+};
+
+var real__setThrew = asm["setThrew"];
+asm["setThrew"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__setThrew.apply(null, arguments);
+};
+
+var real__malloc = asm["malloc"];
+asm["malloc"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__malloc.apply(null, arguments);
+};
+
+var real__free = asm["free"];
+asm["free"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real__free.apply(null, arguments);
+};
+
+var real____set_stack_limit = asm["__set_stack_limit"];
+asm["__set_stack_limit"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real____set_stack_limit.apply(null, arguments);
+};
+
+var real_stackSave = asm["stackSave"];
+asm["stackSave"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_stackSave.apply(null, arguments);
+};
+
+var real_stackAlloc = asm["stackAlloc"];
+asm["stackAlloc"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_stackAlloc.apply(null, arguments);
+};
+
+var real_stackRestore = asm["stackRestore"];
+asm["stackRestore"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_stackRestore.apply(null, arguments);
+};
+
+var real___growWasmMemory = asm["__growWasmMemory"];
+asm["__growWasmMemory"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real___growWasmMemory.apply(null, arguments);
+};
+
+var real_dynCall_ii = asm["dynCall_ii"];
+asm["dynCall_ii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_ii.apply(null, arguments);
+};
+
+var real_dynCall_iiii = asm["dynCall_iiii"];
+asm["dynCall_iiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiii.apply(null, arguments);
+};
+
+var real_dynCall_jiji = asm["dynCall_jiji"];
+asm["dynCall_jiji"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_jiji.apply(null, arguments);
+};
+
+var real_dynCall_iidiiii = asm["dynCall_iidiiii"];
+asm["dynCall_iidiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iidiiii.apply(null, arguments);
+};
+
+var real_dynCall_vii = asm["dynCall_vii"];
+asm["dynCall_vii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_vii.apply(null, arguments);
+};
+
+var real_dynCall_vi = asm["dynCall_vi"];
+asm["dynCall_vi"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_vi.apply(null, arguments);
+};
+
+var real_dynCall_viiiiii = asm["dynCall_viiiiii"];
+asm["dynCall_viiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_viiiiii.apply(null, arguments);
+};
+
+var real_dynCall_viiiii = asm["dynCall_viiiii"];
+asm["dynCall_viiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_viiiii.apply(null, arguments);
+};
+
+var real_dynCall_viiii = asm["dynCall_viiii"];
+asm["dynCall_viiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_viiii.apply(null, arguments);
+};
+
+var real_dynCall_iii = asm["dynCall_iii"];
+asm["dynCall_iii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iii.apply(null, arguments);
+};
+
+var real_dynCall_viii = asm["dynCall_viii"];
+asm["dynCall_viii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_viii.apply(null, arguments);
+};
+
+var real_dynCall_v = asm["dynCall_v"];
+asm["dynCall_v"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_v.apply(null, arguments);
+};
+
+var real_dynCall_iiiii = asm["dynCall_iiiii"];
+asm["dynCall_iiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiii.apply(null, arguments);
+};
+
+var real_dynCall_iiiiii = asm["dynCall_iiiiii"];
+asm["dynCall_iiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiii.apply(null, arguments);
+};
+
+var real_dynCall_iiiiiiiii = asm["dynCall_iiiiiiiii"];
+asm["dynCall_iiiiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiiiiii.apply(null, arguments);
+};
+
+var real_dynCall_iiiiiii = asm["dynCall_iiiiiii"];
+asm["dynCall_iiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiiii.apply(null, arguments);
+};
+
+var real_dynCall_iiiiij = asm["dynCall_iiiiij"];
+asm["dynCall_iiiiij"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiij.apply(null, arguments);
+};
+
+var real_dynCall_iiiiid = asm["dynCall_iiiiid"];
+asm["dynCall_iiiiid"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiid.apply(null, arguments);
+};
+
+var real_dynCall_iiiiijj = asm["dynCall_iiiiijj"];
+asm["dynCall_iiiiijj"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiijj.apply(null, arguments);
+};
+
+var real_dynCall_iiiiiiii = asm["dynCall_iiiiiiii"];
+asm["dynCall_iiiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiiiii.apply(null, arguments);
+};
+
+var real_dynCall_iiiiiijj = asm["dynCall_iiiiiijj"];
+asm["dynCall_iiiiiijj"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_iiiiiijj.apply(null, arguments);
+};
+
+var real_dynCall_viijii = asm["dynCall_viijii"];
+asm["dynCall_viijii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_viijii.apply(null, arguments);
+};
+
+var real_dynCall_ji = asm["dynCall_ji"];
+asm["dynCall_ji"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return real_dynCall_ji.apply(null, arguments);
+};
+
+Module["asm"] = asm;
+var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["__wasm_call_ctors"].apply(null, arguments)
+};
+
+var _AStyleMain = Module["_AStyleMain"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["AStyleMain"].apply(null, arguments)
+};
+
+var _AStyleMainUtf16 = Module["_AStyleMainUtf16"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["AStyleMainUtf16"].apply(null, arguments)
+};
+
+var _AStyleGetVersion = Module["_AStyleGetVersion"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["AStyleGetVersion"].apply(null, arguments)
+};
+
+var _fflush = Module["_fflush"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["fflush"].apply(null, arguments)
+};
+
+var ___errno_location = Module["___errno_location"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["__errno_location"].apply(null, arguments)
+};
+
+var _setThrew = Module["_setThrew"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["setThrew"].apply(null, arguments)
+};
+
+var _malloc = Module["_malloc"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["malloc"].apply(null, arguments)
+};
+
+var _free = Module["_free"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["free"].apply(null, arguments)
+};
+
+var ___set_stack_limit = Module["___set_stack_limit"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["__set_stack_limit"].apply(null, arguments)
+};
+
+var stackSave = Module["stackSave"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["stackSave"].apply(null, arguments)
+};
+
+var stackAlloc = Module["stackAlloc"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["stackAlloc"].apply(null, arguments)
+};
+
+var stackRestore = Module["stackRestore"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["stackRestore"].apply(null, arguments)
+};
+
+var __growWasmMemory = Module["__growWasmMemory"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["__growWasmMemory"].apply(null, arguments)
+};
+
+var dynCall_ii = Module["dynCall_ii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_ii"].apply(null, arguments)
+};
+
+var dynCall_iiii = Module["dynCall_iiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiii"].apply(null, arguments)
+};
+
+var dynCall_jiji = Module["dynCall_jiji"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_jiji"].apply(null, arguments)
+};
+
+var dynCall_iidiiii = Module["dynCall_iidiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iidiiii"].apply(null, arguments)
+};
+
+var dynCall_vii = Module["dynCall_vii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_vii"].apply(null, arguments)
+};
+
+var dynCall_vi = Module["dynCall_vi"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_vi"].apply(null, arguments)
+};
+
+var dynCall_viiiiii = Module["dynCall_viiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_viiiiii"].apply(null, arguments)
+};
+
+var dynCall_viiiii = Module["dynCall_viiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_viiiii"].apply(null, arguments)
+};
+
+var dynCall_viiii = Module["dynCall_viiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_viiii"].apply(null, arguments)
+};
+
+var dynCall_iii = Module["dynCall_iii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iii"].apply(null, arguments)
+};
+
+var dynCall_viii = Module["dynCall_viii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_viii"].apply(null, arguments)
+};
+
+var dynCall_v = Module["dynCall_v"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_v"].apply(null, arguments)
+};
+
+var dynCall_iiiii = Module["dynCall_iiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiii"].apply(null, arguments)
+};
+
+var dynCall_iiiiii = Module["dynCall_iiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiii"].apply(null, arguments)
+};
+
+var dynCall_iiiiiiiii = Module["dynCall_iiiiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiiiiii"].apply(null, arguments)
+};
+
+var dynCall_iiiiiii = Module["dynCall_iiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiiii"].apply(null, arguments)
+};
+
+var dynCall_iiiiij = Module["dynCall_iiiiij"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiij"].apply(null, arguments)
+};
+
+var dynCall_iiiiid = Module["dynCall_iiiiid"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiid"].apply(null, arguments)
+};
+
+var dynCall_iiiiijj = Module["dynCall_iiiiijj"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiijj"].apply(null, arguments)
+};
+
+var dynCall_iiiiiiii = Module["dynCall_iiiiiiii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiiiii"].apply(null, arguments)
+};
+
+var dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_iiiiiijj"].apply(null, arguments)
+};
+
+var dynCall_viijii = Module["dynCall_viijii"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_viijii"].apply(null, arguments)
+};
+
+var dynCall_ji = Module["dynCall_ji"] = function () {
+ assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
+ assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
+ return Module["asm"]["dynCall_ji"].apply(null, arguments)
+};
+
+
+
+
+// === Auto-generated postamble setup entry stuff ===
+
+Module['asm'] = asm;
+
+if (!Object.getOwnPropertyDescriptor(Module, "intArrayFromString")) Module["intArrayFromString"] = function () { abort("'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "intArrayToString")) Module["intArrayToString"] = function () { abort("'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+Module["ccall"] = ccall;
+Module["cwrap"] = cwrap;
+if (!Object.getOwnPropertyDescriptor(Module, "setValue")) Module["setValue"] = function () { abort("'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getValue")) Module["getValue"] = function () { abort("'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "allocate")) Module["allocate"] = function () { abort("'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getMemory")) Module["getMemory"] = function () { abort("'getMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "AsciiToString")) Module["AsciiToString"] = function () { abort("'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stringToAscii")) Module["stringToAscii"] = function () { abort("'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "UTF8ArrayToString")) Module["UTF8ArrayToString"] = function () { abort("'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+Module["UTF8ToString"] = UTF8ToString;
+if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8Array")) Module["stringToUTF8Array"] = function () { abort("'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8")) Module["stringToUTF8"] = function () { abort("'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF8")) Module["lengthBytesUTF8"] = function () { abort("'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "UTF16ToString")) Module["UTF16ToString"] = function () { abort("'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF16")) Module["stringToUTF16"] = function () { abort("'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF16")) Module["lengthBytesUTF16"] = function () { abort("'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "UTF32ToString")) Module["UTF32ToString"] = function () { abort("'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF32")) Module["stringToUTF32"] = function () { abort("'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF32")) Module["lengthBytesUTF32"] = function () { abort("'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8")) Module["allocateUTF8"] = function () { abort("'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function () { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addOnPreRun")) Module["addOnPreRun"] = function () { abort("'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addOnInit")) Module["addOnInit"] = function () { abort("'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addOnPreMain")) Module["addOnPreMain"] = function () { abort("'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addOnExit")) Module["addOnExit"] = function () { abort("'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addOnPostRun")) Module["addOnPostRun"] = function () { abort("'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "writeStringToMemory")) Module["writeStringToMemory"] = function () { abort("'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "writeArrayToMemory")) Module["writeArrayToMemory"] = function () { abort("'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "writeAsciiToMemory")) Module["writeAsciiToMemory"] = function () { abort("'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "addRunDependency")) Module["addRunDependency"] = function () { abort("'addRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "removeRunDependency")) Module["removeRunDependency"] = function () { abort("'removeRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function () { abort("'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS")) Module["FS"] = function () { abort("'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createFolder")) Module["FS_createFolder"] = function () { abort("'FS_createFolder' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createPath")) Module["FS_createPath"] = function () { abort("'FS_createPath' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createDataFile")) Module["FS_createDataFile"] = function () { abort("'FS_createDataFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createPreloadedFile")) Module["FS_createPreloadedFile"] = function () { abort("'FS_createPreloadedFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createLazyFile")) Module["FS_createLazyFile"] = function () { abort("'FS_createLazyFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createLink")) Module["FS_createLink"] = function () { abort("'FS_createLink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_createDevice")) Module["FS_createDevice"] = function () { abort("'FS_createDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "FS_unlink")) Module["FS_unlink"] = function () { abort("'FS_unlink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") };
+if (!Object.getOwnPropertyDescriptor(Module, "GL")) Module["GL"] = function () { abort("'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "dynamicAlloc")) Module["dynamicAlloc"] = function () { abort("'dynamicAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "loadDynamicLibrary")) Module["loadDynamicLibrary"] = function () { abort("'loadDynamicLibrary' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "loadWebAssemblyModule")) Module["loadWebAssemblyModule"] = function () { abort("'loadWebAssemblyModule' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getLEB")) Module["getLEB"] = function () { abort("'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getFunctionTables")) Module["getFunctionTables"] = function () { abort("'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "alignFunctionTables")) Module["alignFunctionTables"] = function () { abort("'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "registerFunctions")) Module["registerFunctions"] = function () { abort("'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+Module["addFunction"] = addFunction;
+if (!Object.getOwnPropertyDescriptor(Module, "removeFunction")) Module["removeFunction"] = function () { abort("'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function () { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "prettyPrint")) Module["prettyPrint"] = function () { abort("'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "makeBigInt")) Module["makeBigInt"] = function () { abort("'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function () { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getCompilerSetting")) Module["getCompilerSetting"] = function () { abort("'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "print")) Module["print"] = function () { abort("'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "printErr")) Module["printErr"] = function () { abort("'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "getTempRet0")) Module["getTempRet0"] = function () { abort("'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "setTempRet0")) Module["setTempRet0"] = function () { abort("'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "callMain")) Module["callMain"] = function () { abort("'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "abort")) Module["abort"] = function () { abort("'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "Pointer_stringify")) Module["Pointer_stringify"] = function () { abort("'Pointer_stringify' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "warnOnce")) Module["warnOnce"] = function () { abort("'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stackSave")) Module["stackSave"] = function () { abort("'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stackRestore")) Module["stackRestore"] = function () { abort("'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "stackAlloc")) Module["stackAlloc"] = function () { abort("'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "establishStackSpace")) Module["establishStackSpace"] = function () { abort("'establishStackSpace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+Module["writeStackCookie"] = writeStackCookie;
+Module["checkStackCookie"] = checkStackCookie;
+Module["abortStackOverflow"] = abortStackOverflow; if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NORMAL")) Object.defineProperty(Module, "ALLOC_NORMAL", { configurable: true, get: function () { abort("'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } });
+if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_STACK")) Object.defineProperty(Module, "ALLOC_STACK", { configurable: true, get: function () { abort("'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } });
+if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_DYNAMIC")) Object.defineProperty(Module, "ALLOC_DYNAMIC", { configurable: true, get: function () { abort("'ALLOC_DYNAMIC' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } });
+if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NONE")) Object.defineProperty(Module, "ALLOC_NONE", { configurable: true, get: function () { abort("'ALLOC_NONE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } });
+if (!Object.getOwnPropertyDescriptor(Module, "calledRun")) Object.defineProperty(Module, "calledRun", { configurable: true, get: function () { abort("'calledRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") } });
+
+
+
+var calledRun;
+
+
+/**
+ * @constructor
+ * @this {ExitStatus}
+ */
+function ExitStatus(status) {
+ this.name = "ExitStatus";
+ this.message = "Program terminated with exit(" + status + ")";
+ this.status = status;
+}
+
+var calledMain = false;
+
+
+dependenciesFulfilled = function runCaller() {
+ // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
+ if (!calledRun) run();
+ if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled
+};
+
+
+
+
+
+/** @type {function(Array=)} */
+function run(args) {
+ args = args || arguments_;
+
+ if (runDependencies > 0) {
+ return;
+ }
+
+ writeStackCookie();
+
+ preRun();
+
+ if (runDependencies > 0) return; // a preRun added a dependency, run will be called later
+
+ function doRun() {
+ // run may have just been called through dependencies being fulfilled just in this very frame,
+ // or while the async setStatus time below was happening
+ if (calledRun) return;
+ calledRun = true;
+
+ if (ABORT) return;
+
+ initRuntime();
+
+ preMain();
+
+ if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']();
+
+ assert(!Module['_main'], 'compiled without a main, but one is present. if you added it from JS, use Module["onRuntimeInitialized"]');
+
+ postRun();
+ }
+
+ if (Module['setStatus']) {
+ Module['setStatus']('Running...');
+ setTimeout(function () {
+ setTimeout(function () {
+ Module['setStatus']('');
+ }, 1);
+ doRun();
+ }, 1);
+ } else {
+ doRun();
+ }
+ checkStackCookie();
+}
+Module['run'] = run;
+
+function checkUnflushedContent() {
+ // Compiler settings do not allow exiting the runtime, so flushing
+ // the streams is not possible. but in ASSERTIONS mode we check
+ // if there was something to flush, and if so tell the user they
+ // should request that the runtime be exitable.
+ // Normally we would not even include flush() at all, but in ASSERTIONS
+ // builds we do so just for this check, and here we see if there is any
+ // content to flush, that is, we check if there would have been
+ // something a non-ASSERTIONS build would have not seen.
+ // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0
+ // mode (which has its own special function for this; otherwise, all
+ // the code is inside libc)
+ var print = out;
+ var printErr = err;
+ var has = false;
+ out = err = function (x) {
+ has = true;
+ }
+ try { // it doesn't matter if it fails
+ var flush = Module['_fflush'];
+ if (flush) flush(0);
+ // also flush in the JS FS layer
+ ['stdout', 'stderr'].forEach(function (name) {
+ var info = FS.analyzePath('/dev/' + name);
+ if (!info) return;
+ var stream = info.object;
+ var rdev = stream.rdev;
+ var tty = TTY.ttys[rdev];
+ if (tty && tty.output && tty.output.length) {
+ has = true;
+ }
+ });
+ } catch (e) { }
+ out = print;
+ err = printErr;
+ if (has) {
+ warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.');
+ }
+}
+
+function exit(status, implicit) {
+ checkUnflushedContent();
+
+ // if this is just main exit-ing implicitly, and the status is 0, then we
+ // don't need to do anything here and can just leave. if the status is
+ // non-zero, though, then we need to report it.
+ // (we may have warned about this earlier, if a situation justifies doing so)
+ if (implicit && noExitRuntime && status === 0) {
+ return;
+ }
+
+ if (noExitRuntime) {
+ // if exit() was called, we may warn the user if the runtime isn't actually being shut down
+ if (!implicit) {
+ err('program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)');
+ }
+ } else {
+
+ ABORT = true;
+ EXITSTATUS = status;
+
+ exitRuntime();
+
+ if (Module['onExit']) Module['onExit'](status);
+ }
+
+ quit_(status, new ExitStatus(status));
+}
+
+if (Module['preInit']) {
+ if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']];
+ while (Module['preInit'].length > 0) {
+ Module['preInit'].pop()();
+ }
+}
+
+
+noExitRuntime = true;
+
+
+const AStyleMain = cwrap('AStyleMain', 'string', [
+ 'string',
+ 'string',
+ 'number',
+ 'number'
+]);
+
+let mallocList = [];
+const malloc = addFunction((numBytes) => {
+ const result = _malloc(numBytes);
+ mallocList.push(result);
+ return result;
+}, 'ii');
+
+// This has to be a global function since emscripten doesn't support removeFunction()
+// with WASM, and we have a limited number of entries in the callback table, so we
+// can't create a new function for every function call
+let activeOutputCallback = null;
+const outputHandler = addFunction((code, msg) => {
+ if (activeOutputCallback) {
+ activeOutputCallback(code, UTF8ToString(msg));
+ }
+}, 'vii');
+
+let libReadyCallback;
+
+const ready = new Promise((resolve) => {
+ libReadyCallback = resolve;
+});
+
+async function format_code(code, options = '', outputCallback) {
+ await ready;
+ activeOutputCallback = outputCallback;
+ try {
+ return AStyleMain(code, options, outputHandler, malloc);
+ } finally {
+ activeOutputCallback = null;
+ // Free all memory allocation by AStyle
+ for (const item of mallocList) {
+ _free(item);
+ }
+ mallocList = [];
+ }
+}
+
+Module.onRuntimeInitialized = libReadyCallback;
+
+
+self.cppFormatter = {};
+self.cppFormatter.format = format_code;
+self.cppFormatter.init = async function () {
+ await ready;
+}
+
+run();
+
+// {{MODULE_ADDITIONS}}
+
+
+
diff --git a/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.wasm b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.wasm
new file mode 100644
index 00000000..60cd3a42
Binary files /dev/null and b/mixly/common/modules/mixly-modules/workers/common/cpp-formatter/libastyle.wasm differ
diff --git a/mixly/common/modules/mixly-modules/workers/common/python-formatter/index.js b/mixly/common/modules/mixly-modules/workers/common/python-formatter/index.js
new file mode 100644
index 00000000..7b2b1a8c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/python-formatter/index.js
@@ -0,0 +1,17 @@
+importScripts('./ruff_fmt.js');
+importScripts('../../../../web-modules/workerpool.min.js');
+
+
+async function init() {
+ await self.pythonFormatter.init();
+}
+
+async function format(code) {
+ const result = await self.pythonFormatter.format(code);
+ return result;
+}
+
+workerpool.worker({
+ init,
+ format
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt.js b/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt.js
new file mode 100644
index 00000000..bbcc059c
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt.js
@@ -0,0 +1,572 @@
+let wasm;
+
+function addHeapObject(obj) {
+ if (heap_next === heap.length) heap.push(heap.length + 1);
+ const idx = heap_next;
+ heap_next = heap[idx];
+
+ heap[idx] = obj;
+ return idx;
+}
+
+function debugString(val) {
+ // primitive types
+ const type = typeof val;
+ if (type == 'number' || type == 'boolean' || val == null) {
+ return `${val}`;
+ }
+ if (type == 'string') {
+ return `"${val}"`;
+ }
+ if (type == 'symbol') {
+ const description = val.description;
+ if (description == null) {
+ return 'Symbol';
+ } else {
+ return `Symbol(${description})`;
+ }
+ }
+ if (type == 'function') {
+ const name = val.name;
+ if (typeof name == 'string' && name.length > 0) {
+ return `Function(${name})`;
+ } else {
+ return 'Function';
+ }
+ }
+ // objects
+ if (Array.isArray(val)) {
+ const length = val.length;
+ let debug = '[';
+ if (length > 0) {
+ debug += debugString(val[0]);
+ }
+ for(let i = 1; i < length; i++) {
+ debug += ', ' + debugString(val[i]);
+ }
+ debug += ']';
+ return debug;
+ }
+ // Test for built-in
+ const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
+ let className;
+ if (builtInMatches && builtInMatches.length > 1) {
+ className = builtInMatches[1];
+ } else {
+ // Failed to match the standard '[object ClassName]'
+ return toString.call(val);
+ }
+ if (className == 'Object') {
+ // we're a user defined class or Object
+ // JSON.stringify avoids problems with cycles, and is generally much
+ // easier than looping through ownProperties of `val`.
+ try {
+ return 'Object(' + JSON.stringify(val) + ')';
+ } catch (_) {
+ return 'Object';
+ }
+ }
+ // errors
+ if (val instanceof Error) {
+ return `${val.name}: ${val.message}\n${val.stack}`;
+ }
+ // TODO we could test for more things here, like `Set`s and `Map`s.
+ return className;
+}
+
+function dropObject(idx) {
+ if (idx < 132) return;
+ heap[idx] = heap_next;
+ heap_next = idx;
+}
+
+function getArrayU8FromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
+}
+
+let cachedDataViewMemory0 = null;
+function getDataViewMemory0() {
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
+ }
+ return cachedDataViewMemory0;
+}
+
+function getStringFromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return decodeText(ptr, len);
+}
+
+let cachedUint8ArrayMemory0 = null;
+function getUint8ArrayMemory0() {
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
+ }
+ return cachedUint8ArrayMemory0;
+}
+
+function getObject(idx) { return heap[idx]; }
+
+function handleError(f, args) {
+ try {
+ return f.apply(this, args);
+ } catch (e) {
+ wasm.__wbindgen_export3(addHeapObject(e));
+ }
+}
+
+let heap = new Array(128).fill(undefined);
+heap.push(undefined, null, true, false);
+
+let heap_next = heap.length;
+
+function isLikeNone(x) {
+ return x === undefined || x === null;
+}
+
+function passStringToWasm0(arg, malloc, realloc) {
+ if (realloc === undefined) {
+ const buf = cachedTextEncoder.encode(arg);
+ const ptr = malloc(buf.length, 1) >>> 0;
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
+ WASM_VECTOR_LEN = buf.length;
+ return ptr;
+ }
+
+ let len = arg.length;
+ let ptr = malloc(len, 1) >>> 0;
+
+ const mem = getUint8ArrayMemory0();
+
+ let offset = 0;
+
+ for (; offset < len; offset++) {
+ const code = arg.charCodeAt(offset);
+ if (code > 0x7F) break;
+ mem[ptr + offset] = code;
+ }
+ if (offset !== len) {
+ if (offset !== 0) {
+ arg = arg.slice(offset);
+ }
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
+ const ret = cachedTextEncoder.encodeInto(arg, view);
+
+ offset += ret.written;
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
+ }
+
+ WASM_VECTOR_LEN = offset;
+ return ptr;
+}
+
+function takeObject(idx) {
+ const ret = getObject(idx);
+ dropObject(idx);
+ return ret;
+}
+
+let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+cachedTextDecoder.decode();
+const MAX_SAFARI_DECODE_BYTES = 2146435072;
+let numBytesDecoded = 0;
+function decodeText(ptr, len) {
+ numBytesDecoded += len;
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+ cachedTextDecoder.decode();
+ numBytesDecoded = len;
+ }
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
+}
+
+const cachedTextEncoder = new TextEncoder();
+
+if (!('encodeInto' in cachedTextEncoder)) {
+ cachedTextEncoder.encodeInto = function (arg, view) {
+ const buf = cachedTextEncoder.encode(arg);
+ view.set(buf);
+ return {
+ read: arg.length,
+ written: buf.length
+ };
+ }
+}
+
+let WASM_VECTOR_LEN = 0;
+
+/**
+ * Format the entire Python source code string.
+ * @param {string} input
+ * @param {string | null} [path]
+ * @param {Config | null} [config]
+ * @returns {string}
+ */
+function format_code(input, path, config) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ var ptr1 = isLikeNone(path) ? 0 : passStringToWasm0(path, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ var len1 = WASM_VECTOR_LEN;
+ wasm.format(retptr, ptr0, len0, ptr1, len1, isLikeNone(config) ? 0 : addHeapObject(config));
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_export4(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+ * Format a specific range of the Python source code string.
+ * @param {string} input
+ * @param {TextRange} range
+ * @param {string | null} [path]
+ * @param {Config | null} [config]
+ * @returns {PrintedRange}
+ */
+function format_range(input, range, path, config) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ var ptr1 = isLikeNone(path) ? 0 : passStringToWasm0(path, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ var len1 = WASM_VECTOR_LEN;
+ wasm.format_range(retptr, ptr0, len0, addHeapObject(range), ptr1, len1, isLikeNone(config) ? 0 : addHeapObject(config));
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return takeObject(r0);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+}
+
+const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']);
+
+async function __wbg_load(module, imports) {
+ if (typeof Response === 'function' && module instanceof Response) {
+ if (typeof WebAssembly.instantiateStreaming === 'function') {
+ try {
+ return await WebAssembly.instantiateStreaming(module, imports);
+ } catch (e) {
+ const validResponse = module.ok && EXPECTED_RESPONSE_TYPES.has(module.type);
+
+ if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
+ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
+
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ const bytes = await module.arrayBuffer();
+ return await WebAssembly.instantiate(bytes, imports);
+ } else {
+ const instance = await WebAssembly.instantiate(module, imports);
+
+ if (instance instanceof WebAssembly.Instance) {
+ return { instance, module };
+ } else {
+ return instance;
+ }
+ }
+}
+
+function __wbg_get_imports() {
+ const imports = {};
+ imports.wbg = {};
+ imports.wbg.__wbg_Error_52673b7de5a0ca89 = function(arg0, arg1) {
+ const ret = Error(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_Number_2d1dcfcf4ec51736 = function(arg0) {
+ const ret = Number(getObject(arg0));
+ return ret;
+ };
+ imports.wbg.__wbg_String_8f0eb39a4a4c2f66 = function(arg0, arg1) {
+ const ret = String(getObject(arg1));
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
+ };
+ imports.wbg.__wbg___wbindgen_bigint_get_as_i64_6e32f5e6aff02e1d = function(arg0, arg1) {
+ const v = getObject(arg1);
+ const ret = typeof(v) === 'bigint' ? v : undefined;
+ getDataViewMemory0().setBigInt64(arg0 + 8 * 1, isLikeNone(ret) ? BigInt(0) : ret, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
+ };
+ imports.wbg.__wbg___wbindgen_boolean_get_dea25b33882b895b = function(arg0) {
+ const v = getObject(arg0);
+ const ret = typeof(v) === 'boolean' ? v : undefined;
+ return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0;
+ };
+ imports.wbg.__wbg___wbindgen_debug_string_adfb662ae34724b6 = function(arg0, arg1) {
+ const ret = debugString(getObject(arg1));
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
+ };
+ imports.wbg.__wbg___wbindgen_in_0d3e1e8f0c669317 = function(arg0, arg1) {
+ const ret = getObject(arg0) in getObject(arg1);
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_is_bigint_0e1a2e3f55cfae27 = function(arg0) {
+ const ret = typeof(getObject(arg0)) === 'bigint';
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_is_function_8d400b8b1af978cd = function(arg0) {
+ const ret = typeof(getObject(arg0)) === 'function';
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_is_object_ce774f3490692386 = function(arg0) {
+ const val = getObject(arg0);
+ const ret = typeof(val) === 'object' && val !== null;
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_is_undefined_f6b95eab589e0269 = function(arg0) {
+ const ret = getObject(arg0) === undefined;
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_jsval_eq_b6101cc9cef1fe36 = function(arg0, arg1) {
+ const ret = getObject(arg0) === getObject(arg1);
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_jsval_loose_eq_766057600fdd1b0d = function(arg0, arg1) {
+ const ret = getObject(arg0) == getObject(arg1);
+ return ret;
+ };
+ imports.wbg.__wbg___wbindgen_number_get_9619185a74197f95 = function(arg0, arg1) {
+ const obj = getObject(arg1);
+ const ret = typeof(obj) === 'number' ? obj : undefined;
+ getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
+ };
+ imports.wbg.__wbg___wbindgen_string_get_a2a31e16edf96e42 = function(arg0, arg1) {
+ const obj = getObject(arg1);
+ const ret = typeof(obj) === 'string' ? obj : undefined;
+ var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
+ var len1 = WASM_VECTOR_LEN;
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
+ };
+ imports.wbg.__wbg___wbindgen_throw_dd24417ed36fc46e = function(arg0, arg1) {
+ throw new Error(getStringFromWasm0(arg0, arg1));
+ };
+ imports.wbg.__wbg_call_abb4ff46ce38be40 = function() { return handleError(function (arg0, arg1) {
+ const ret = getObject(arg0).call(getObject(arg1));
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_done_62ea16af4ce34b24 = function(arg0) {
+ const ret = getObject(arg0).done;
+ return ret;
+ };
+ imports.wbg.__wbg_entries_83c79938054e065f = function(arg0) {
+ const ret = Object.entries(getObject(arg0));
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_get_6b7bd52aca3f9671 = function(arg0, arg1) {
+ const ret = getObject(arg0)[arg1 >>> 0];
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_get_af9dab7e9603ea93 = function() { return handleError(function (arg0, arg1) {
+ const ret = Reflect.get(getObject(arg0), getObject(arg1));
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_get_with_ref_key_1dc361bd10053bfe = function(arg0, arg1) {
+ const ret = getObject(arg0)[getObject(arg1)];
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_instanceof_ArrayBuffer_f3320d2419cd0355 = function(arg0) {
+ let result;
+ try {
+ result = getObject(arg0) instanceof ArrayBuffer;
+ } catch (_) {
+ result = false;
+ }
+ const ret = result;
+ return ret;
+ };
+ imports.wbg.__wbg_instanceof_Map_084be8da74364158 = function(arg0) {
+ let result;
+ try {
+ result = getObject(arg0) instanceof Map;
+ } catch (_) {
+ result = false;
+ }
+ const ret = result;
+ return ret;
+ };
+ imports.wbg.__wbg_instanceof_Uint8Array_da54ccc9d3e09434 = function(arg0) {
+ let result;
+ try {
+ result = getObject(arg0) instanceof Uint8Array;
+ } catch (_) {
+ result = false;
+ }
+ const ret = result;
+ return ret;
+ };
+ imports.wbg.__wbg_isArray_51fd9e6422c0a395 = function(arg0) {
+ const ret = Array.isArray(getObject(arg0));
+ return ret;
+ };
+ imports.wbg.__wbg_isSafeInteger_ae7d3f054d55fa16 = function(arg0) {
+ const ret = Number.isSafeInteger(getObject(arg0));
+ return ret;
+ };
+ imports.wbg.__wbg_iterator_27b7c8b35ab3e86b = function() {
+ const ret = Symbol.iterator;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_length_22ac23eaec9d8053 = function(arg0) {
+ const ret = getObject(arg0).length;
+ return ret;
+ };
+ imports.wbg.__wbg_length_d45040a40c570362 = function(arg0) {
+ const ret = getObject(arg0).length;
+ return ret;
+ };
+ imports.wbg.__wbg_new_1ba21ce319a06297 = function() {
+ const ret = new Object();
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_new_6421f6084cc5bc5a = function(arg0) {
+ const ret = new Uint8Array(getObject(arg0));
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_next_138a17bbf04e926c = function(arg0) {
+ const ret = getObject(arg0).next;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_next_3cfe5c0fe2a4cc53 = function() { return handleError(function (arg0) {
+ const ret = getObject(arg0).next();
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_prototypesetcall_dfe9b766cdc1f1fd = function(arg0, arg1, arg2) {
+ Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
+ };
+ imports.wbg.__wbg_set_3f1d0b984ed272ed = function(arg0, arg1, arg2) {
+ getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
+ };
+ imports.wbg.__wbg_value_57b7b035e117f7ee = function(arg0) {
+ const ret = getObject(arg0).value;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) {
+ // Cast intrinsic for `Ref(String) -> Externref`.
+ const ret = getStringFromWasm0(arg0, arg1);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_cast_4625c577ab2ec9ee = function(arg0) {
+ // Cast intrinsic for `U64 -> Externref`.
+ const ret = BigInt.asUintN(64, arg0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_cast_9ae0607507abb057 = function(arg0) {
+ // Cast intrinsic for `I64 -> Externref`.
+ const ret = arg0;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) {
+ // Cast intrinsic for `F64 -> Externref`.
+ const ret = arg0;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
+ const ret = getObject(arg0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
+ takeObject(arg0);
+ };
+
+ return imports;
+}
+
+function __wbg_finalize_init(instance, module) {
+ wasm = instance.exports;
+ __wbg_init.__wbindgen_wasm_module = module;
+ cachedDataViewMemory0 = null;
+ cachedUint8ArrayMemory0 = null;
+
+
+
+ return wasm;
+}
+
+function initSync(module) {
+ if (wasm !== undefined) return wasm;
+
+
+ if (typeof module !== 'undefined') {
+ if (Object.getPrototypeOf(module) === Object.prototype) {
+ ({module} = module)
+ } else {
+ console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
+ }
+ }
+
+ const imports = __wbg_get_imports();
+ if (!(module instanceof WebAssembly.Module)) {
+ module = new WebAssembly.Module(module);
+ }
+ const instance = new WebAssembly.Instance(module, imports);
+ return __wbg_finalize_init(instance, module);
+}
+
+async function __wbg_init(module_or_path) {
+ if (wasm !== undefined) return wasm;
+
+
+ if (typeof module_or_path !== 'undefined') {
+ if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
+ ({module_or_path} = module_or_path)
+ } else {
+ console.warn('using deprecated parameters for the initialization function; pass a single object instead')
+ }
+ }
+
+ if (typeof module_or_path === 'undefined') {
+ module_or_path = new URL('ruff_fmt_bg.wasm', self.location.origin + self.location.pathname);
+ }
+ const imports = __wbg_get_imports();
+
+ if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
+ module_or_path = fetch(module_or_path);
+ }
+
+ const { instance, module } = await __wbg_load(await module_or_path, imports);
+
+ return __wbg_finalize_init(instance, module);
+}
+
+// export { initSync };
+// export default __wbg_init;
+
+self.pythonFormatter = {};
+self.pythonFormatter.format = format_code;
+self.pythonFormatter.formatRange = format_range;
+self.pythonFormatter.init = __wbg_init;
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt_bg.wasm b/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt_bg.wasm
new file mode 100644
index 00000000..d50b7d14
Binary files /dev/null and b/mixly/common/modules/mixly-modules/workers/common/python-formatter/ruff_fmt_bg.wasm differ
diff --git a/mixly/common/modules/mixly-modules/workers/common/serial-worker.js b/mixly/common/modules/mixly-modules/workers/common/serial-worker.js
new file mode 100644
index 00000000..f6dcb196
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/serial-worker.js
@@ -0,0 +1,188 @@
+class SerialWorker {
+ #receiveBuffer_ = [];
+ #bufferLength_ = 0;
+ #encoder_ = new TextEncoder();
+ #decoder_ = new TextDecoder('utf-8');
+ #baud_ = 115200;
+ #dtr_ = false;
+ #rts_ = false;
+ #isOpened_ = false;
+ #port_ = '';
+ constructor(port) {
+ this.#port_ = port;
+ this.resetBuffer();
+ }
+
+ decodeBuffer(buffer) {
+ let output = '';
+ for (let i in buffer) {
+ output += this.decodeByte();
+ }
+ return output;
+ }
+
+ /* UTF-8编码方式
+ * ------------------------------------------------------------
+ * |1字节 0xxxxxxx |
+ * |2字节 110xxxxx 10xxxxxx |
+ * |3字节 1110xxxx 10xxxxxx 10xxxxxx |
+ * |4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx|
+ * ------------------------------------------------------------
+ **/
+ decodeByte(byte) {
+ let output = '';
+ if ((byte & 0x80) === 0x00) {
+ // 1字节
+ this.#receiveBuffer_ = [];
+ this.#bufferLength_ = 0;
+ output += String.fromCharCode(byte);
+ } else if ((byte & 0xc0) === 0x80) {
+ /*
+ * 2字节以上的中间字节,10xxxxxx
+ * 如果没有起始头,则丢弃这个字节
+ * 如果不是2字节及以上的起始头,则丢弃这个字节
+ **/
+ if (!this.#receiveBuffer_.length || this.#bufferLength_ < 2) {
+ return output;
+ }
+ this.#receiveBuffer_.push(byte);
+ if (this.#bufferLength_ === this.#receiveBuffer_.length) {
+ output += this.#decoder_.decode(new Uint8Array(this.#receiveBuffer_));
+ this.#receiveBuffer_ = [];
+ }
+ } else {
+ // 2字节以上的起始头
+ if (this.#receiveBuffer_) {
+ this.#receiveBuffer_ = [];
+ }
+ this.#bufferLength_ = this.#getBufferLength_(byte);
+ this.#receiveBuffer_.push(byte);
+ }
+ return output;
+ }
+
+ #getBufferLength_(data) {
+ let len = 2;
+ if ((data & 0xFC) === 0xFC) {
+ len = 6;
+ } else if ((data & 0xF8) === 0xF8) {
+ len = 5;
+ } else if ((data & 0xF0) === 0xF0) {
+ len = 4;
+ } else if ((data & 0xE0) === 0xE0) {
+ len = 3;
+ } else if ((data & 0xC0) === 0xC0) {
+ len = 2;
+ }
+ return len;
+ }
+
+ resetBuffer() {
+ this.#receiveBuffer_ = [];
+ this.#bufferLength_ = 0;
+ }
+
+ open() {}
+
+ close() {}
+
+ toggle() {
+ if (this.isOpened()) {
+ this.close();
+ } else {
+ this.open();
+ }
+ }
+
+ setBaudRate(baud) {
+ this.#baud_ = baud;
+ }
+
+ setDTR(dtr) {
+ this.#dtr_ = dtr;
+ }
+
+ setRTS(rts) {
+ this.#rts_ = rts;
+ }
+
+ setDTRAndRTS(dtr, rts) {
+ this.#dtr_ = dtr;
+ this.#rts_ = rts;
+ }
+
+ getPortName() {
+ return this.#port_;
+ }
+
+ getBaudRate() {
+ return this.#baud_;
+ }
+
+ getDTR() {
+ return this.#dtr_;
+ }
+
+ getRTS() {
+ return this.#rts_;
+ }
+
+ sendString(str) {}
+
+ sendBuffer(buffer) {}
+
+ onBuffer(buffer) {
+ const data = this.decodeBuffer(buffer);
+ if (!data) {
+ return;
+ }
+ self.postMessage({
+ port: this.getPortName(),
+ event: 'onBuffer',
+ message: data
+ });
+ }
+
+ onOpen() {
+ this.#isOpened_ = true;
+ self.postMessage({
+ port: this.getPortName(),
+ event: 'onOpen'
+ });
+ }
+
+ onClose(code) {
+ this.#isOpened_ = false;
+ self.postMessage({
+ port: this.getPortName(),
+ event: 'onClose',
+ message: code
+ });
+ }
+
+ onError(error) {
+ self.postMessage({
+ port: this.getPortName(),
+ event: 'onError',
+ message: error
+ });
+ }
+
+ isOpened() {
+ return this.#isOpened_;
+ }
+
+ config(info) {
+ if (typeof info !== Object) {
+ return;
+ }
+ this.#baud_ = info.baud;
+ this.setBaudRate(this.#baud_);
+ this.#dtr_ = info.dtr;
+ this.setDTR(this.#dtr_);
+ this.#rts_ = info.rts;
+ this.setDTR(this.#rts_);
+ }
+}
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/common/tree-sitter/index.js b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/index.js
new file mode 100644
index 00000000..51ac969a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/index.js
@@ -0,0 +1,141 @@
+importScripts('./tree-sitter.js');
+importScripts('../../../../web-modules/workerpool.min.js');
+
+
+const terms = [
+ 'type',
+ 'scope',
+ 'function',
+ 'variable',
+ 'number',
+ 'string',
+ 'comment',
+ 'constant',
+ 'directive',
+ 'control',
+ 'operator',
+ 'modifier',
+ 'punctuation'
+];
+
+let parser;
+let tree = null;
+let language = null;
+
+
+class Language {
+ constructor(grammarJson) {
+ this.simpleTerms = {};
+ this.complexTerms = [];
+ this.complexScopes = {};
+ for (const t in grammarJson.simpleTerms)
+ this.simpleTerms[t] = grammarJson.simpleTerms[t];
+ for (const t in grammarJson.complexTerms)
+ this.complexTerms[t] = grammarJson.complexTerms[t];
+ for (const t in grammarJson.complexScopes)
+ this.complexScopes[t] = grammarJson.complexScopes[t];
+ this.complexDepth = 0;
+ this.complexOrder = false;
+ for (const s in this.complexScopes) {
+ const depth = s.split('>').length;
+ if (depth > this.complexDepth)
+ this.complexDepth = depth;
+ if (s.indexOf('[') >= 0)
+ this.complexOrder = true;
+ }
+ this.complexDepth--;
+ }
+}
+
+async function init(languageWasmPath, grammarJson) {
+ await TreeSitter.init();
+
+ parser = new TreeSitter();
+ const lang = await TreeSitter.Language.load(languageWasmPath);
+ console.log(lang.version)
+ parser.setLanguage(lang);
+ language = new Language(grammarJson);
+
+ tree = null;
+}
+
+function buildDecorations(tree) {
+ const result = [];
+ const stack = [];
+ let node = tree.rootNode.firstChild;
+ while (stack.length > 0 || node) {
+ if (node) {
+ stack.push(node);
+ node = node.firstChild;
+ }
+ else {
+ node = stack.pop();
+ let type = node.type;
+ if (!node.isNamed())
+ type = '"' + type + '"';
+ let term = null;
+ if (!language.complexTerms.includes(type)) {
+ term = language.simpleTerms[type];
+ }
+ else {
+ let desc = type;
+ let scopes = [desc];
+ let parent = node.parent;
+ for (let i = 0; i < language.complexDepth && parent; i++) {
+ let parentType = parent.type;
+ if (!parent.isNamed())
+ parentType = '"' + parentType + '"';
+ desc = parentType + ' > ' + desc;
+ scopes.push(desc);
+ parent = parent.parent;
+ }
+ if (language.complexOrder) {
+ let index = 0;
+ let sibling = node.previousSibling;
+ while (sibling) {
+ if (sibling.type === node.type)
+ index++;
+ sibling = sibling.previousSibling;
+ }
+ let rindex = -1;
+ sibling = node.nextSibling;
+ while (sibling) {
+ if (sibling.type === node.type)
+ rindex--;
+ sibling = sibling.nextSibling;
+ }
+ const orderScopes = [];
+ for (let i = 0; i < scopes.length; i++)
+ orderScopes.push(scopes[i], scopes[i] + '[' + index + ']', scopes[i] + '[' + rindex + ']');
+ scopes = orderScopes;
+ }
+ for (const d of scopes)
+ if (d in language.complexScopes)
+ term = language.complexScopes[d];
+ }
+ if (terms.includes(term)) {
+ if (!result[term]) {
+ result[term] = [];
+ }
+ result[term].push({
+ startLineNumber: node.startPosition.row + 1,
+ startColumn: node.startPosition.column + 1,
+ endLineNumber: node.endPosition.row + 1,
+ endColumn: node.endPosition.column + 1
+ });
+ }
+ node = node.nextSibling;
+ }
+ }
+ return result;
+}
+
+function update(text) {
+ const tree = parser.parse(text);
+ return buildDecorations(tree);
+}
+
+workerpool.worker({
+ init,
+ update
+});
diff --git a/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-cpp.wasm b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-cpp.wasm
new file mode 100644
index 00000000..6ada0a40
Binary files /dev/null and b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-cpp.wasm differ
diff --git a/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-python.wasm b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-python.wasm
new file mode 100644
index 00000000..05fdeb2d
Binary files /dev/null and b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter-python.wasm differ
diff --git a/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter.js b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter.js
new file mode 100644
index 00000000..f6ee3112
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/tree-sitter.js
@@ -0,0 +1 @@
+var Module=void 0!==Module?Module:{};!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():self.TreeSitter=t()}(0,function(){var e,t={};for(e in Module)Module.hasOwnProperty(e)&&(t[e]=Module[e]);var r,n,o=[],s=function(e,t){throw t},_=!1,a=!1;_="object"==typeof window,a="function"==typeof importScripts,r="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,n=!_&&!r&&!a;var u,i,l,d,c="";r?(c=a?require("path").dirname(c)+"/":__dirname+"/",u=function(e,t){return l||(l=require("fs")),d||(d=require("path")),e=d.normalize(e),l.readFileSync(e,t?null:"utf8")},i=function(e){var t=u(e,!0);return t.buffer||(t=new Uint8Array(t)),q(t.buffer),t},process.argv.length>1&&process.argv[1].replace(/\\/g,"/"),o=process.argv.slice(2),"undefined"!=typeof module&&(module.exports=Module),process.on("uncaughtException",function(e){if(!(e instanceof Ze))throw e}),process.on("unhandledRejection",de),s=function(e){process.exit(e)},Module.inspect=function(){return"[Emscripten Module object]"}):n?("undefined"!=typeof read&&(u=function(e){return read(e)}),i=function(e){var t;return"function"==typeof readbuffer?new Uint8Array(readbuffer(e)):(q("object"==typeof(t=read(e,"binary"))),t)},"undefined"!=typeof scriptArgs?o=scriptArgs:void 0!==arguments&&(o=arguments),"function"==typeof quit&&(s=function(e){quit(e)}),"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(_||a)&&(a?c=self.location.href:document.currentScript&&(c=document.currentScript.src),c=0!==c.indexOf("blob:")?c.substr(0,c.lastIndexOf("/")+1):"",u=function(e){var t=new XMLHttpRequest;return t.open("GET",e,!1),t.send(null),t.responseText},a&&(i=function(e){var t=new XMLHttpRequest;return t.open("GET",e,!1),t.responseType="arraybuffer",t.send(null),new Uint8Array(t.response)}),function(e,t,r){var n=new XMLHttpRequest;n.open("GET",e,!0),n.responseType="arraybuffer",n.onload=function(){200==n.status||0==n.status&&n.response?t(n.response):r()},n.onerror=r,n.send(null)});var m=Module.print||console.log.bind(console),f=Module.printErr||console.warn.bind(console);for(e in t)t.hasOwnProperty(e)&&(Module[e]=t[e]);t=null,Module.arguments&&(o=Module.arguments),Module.thisProgram&&Module.thisProgram,Module.quit&&(s=Module.quit);var p=16;function h(e){var t=D[K>>2],r=t+e+15&-16;return r>Ee()&&de(),D[K>>2]=r,t}function w(e,t){return t||(t=p),Math.ceil(e/t)*t}function g(e){switch(e){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:if("*"===e[e.length-1])return 4;if("i"===e[0]){var t=parseInt(e.substr(1));return q(t%8==0,"getNativeTypeSize invalid bits "+t+", type "+e),t/8}return 0}}var M={nextHandle:1,loadedLibs:{"-1":{refcount:1/0,name:"__self__",module:Module,global:!0}},loadedLibNames:{__self__:-1}};function y(e,t){t=t||{global:!0,nodelete:!0};var r,n=M.loadedLibNames[e];if(n)return r=M.loadedLibs[n],t.global&&!r.global&&(r.global=!0,"loading"!==r.module&&a(r.module)),t.nodelete&&r.refcount!==1/0&&(r.refcount=1/0),r.refcount++,t.loadAsync?Promise.resolve(n):n;function o(){if(t.fs){var r=t.fs.readFile(e,{encoding:"binary"});return r instanceof Uint8Array||(r=new Uint8Array(lib_data)),t.loadAsync?Promise.resolve(r):r}return t.loadAsync?(n=e,fetch(n,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load binary file at '"+n+"'";return e.arrayBuffer()}).then(function(e){return new Uint8Array(e)})):i(e);var n}function s(e){return E(e,t)}function _(){if(void 0!==Module.preloadedWasm&&void 0!==Module.preloadedWasm[e]){var r=Module.preloadedWasm[e];return t.loadAsync?Promise.resolve(r):r}return t.loadAsync?o().then(function(e){return s(e)}):s(o())}function a(e){for(var t in e)if(e.hasOwnProperty(t)){var r;r="_"+t,Module.hasOwnProperty(r)||(Module[r]=e[t])}}function u(e){r.global&&a(e),r.module=e}return n=M.nextHandle++,r={refcount:t.nodelete?1/0:1,name:e,module:"loading",global:t.global},M.loadedLibNames[e]=n,M.loadedLibs[n]=r,t.loadAsync?_().then(function(e){return u(e),n}):(u(_()),n)}function b(e,t,r,n){var o={};for(var s in e){var _=e[s];"object"==typeof _&&(_=_.value),"number"==typeof _&&(_+=t),o[s]=_,n&&(n["_"+s]=_)}return o}function E(e,t){q(1836278016==new Uint32Array(new Uint8Array(e.subarray(0,24)).buffer)[0],"need to see wasm magic number"),q(0===e[8],"need the dylink section to be first");var r=9;function n(){for(var t=0,n=1;;){var o=e[r++];if(t+=(127&o)*n,n*=128,!(128&o))break}return t}n();q(6===e[r]),q(e[++r]==="d".charCodeAt(0)),q(e[++r]==="y".charCodeAt(0)),q(e[++r]==="l".charCodeAt(0)),q(e[++r]==="i".charCodeAt(0)),q(e[++r]==="n".charCodeAt(0)),q(e[++r]==="k".charCodeAt(0)),r++;for(var o=n(),s=n(),_=n(),a=n(),u=n(),i=[],l=0;l=0,a=0;return e[t]=function(){if(!a){var e=m(n,0,_);a=I(e,s)}return a}}return t.startsWith("invoke_")?e[t]=We:e[t]=function(){return m(t).apply(null,arguments)}}}),g={global:{NaN:NaN,Infinity:1/0},"global.Math":Math,env:h,wasi_snapshot_preview1:h};function M(e,t){var n=b(e.exports,r,0,t),o=n.__post_instantiate;return o&&(ee?o():Q.push(o)),n}return t.loadAsync?WebAssembly.instantiate(e,g).then(function(e){return M(e.instance,c)}):M(new WebAssembly.Instance(new WebAssembly.Module(e),g),c)}return t.loadAsync?Promise.all(i.map(function(e){return y(e,t)})).then(function(){return f()}):(i.forEach(function(e){y(e,t)}),f())}function v(e,t){var r=R,n=r.length;try{r.grow(1)}catch(e){if(!(e instanceof RangeError))throw e;throw"Unable to grow wasm table. Use a higher value for RESERVED_FUNCTION_POINTERS or set ALLOW_TABLE_GROWTH."}try{r.set(n,e)}catch(s){if(!(s instanceof TypeError))throw s;q(void 0!==t,"Missing signature argument to addFunction");var o=function(e,t){if("function"==typeof WebAssembly.Function){for(var r={i:"i32",j:"i64",f:"f32",d:"f64"},n={parameters:[],results:"v"==t[0]?[]:[r[t[0]]]},o=1;o>0]=t;break;case"i16":j[e>>1]=t;break;case"i32":D[e>>2]=t;break;case"i64":he=[t>>>0,(pe=t,+re(pe)>=1?pe>0?(0|se(+oe(pe/4294967296),4294967295))>>>0:~~+ne((pe-+(~~pe>>>0))/4294967296)>>>0:0)],D[e>>2]=he[0],D[e+4>>2]=he[1];break;case"float":B[e>>2]=t;break;case"double":H[e>>3]=t;break;default:de("invalid type for setValue: "+r)}}function P(e,t,r){switch("*"===(t=t||"i8").charAt(t.length-1)&&(t="i32"),t){case"i1":case"i8":return O[e>>0];case"i16":return j[e>>1];case"i32":case"i64":return D[e>>2];case"float":return B[e>>2];case"double":return H[e>>3];default:de("invalid type for getValue: "+t)}return null}A=w(A,1),Module.wasmBinary&&(S=Module.wasmBinary),Module.noExitRuntime&&(N=Module.noExitRuntime),"object"!=typeof WebAssembly&&f("no native wasm support detected");var R=new WebAssembly.Table({initial:12,element:"anyfunc"}),C=!1;function q(e,t){e||de("Assertion failed: "+t)}var T=3;function F(e){return ee?Pe(e):h(e)}var L="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function $(e,t,r){for(var n=t+r,o=t;e[o]&&!(o>=n);)++o;if(o-t>16&&e.subarray&&L)return L.decode(e.subarray(t,o));for(var s="";t>10,56320|1023&i)}}else s+=String.fromCharCode((31&_)<<6|a)}else s+=String.fromCharCode(_)}return s}function W(e,t){return e?$(U,e,t):""}"undefined"!=typeof TextDecoder&&new TextDecoder("utf-16le");var Z,O,U,j,D,B,H;function z(e){Z=e,Module.HEAP8=O=new Int8Array(e),Module.HEAP16=j=new Int16Array(e),Module.HEAP32=D=new Int32Array(e),Module.HEAPU8=U=new Uint8Array(e),Module.HEAPU16=new Uint16Array(e),Module.HEAPU32=new Uint32Array(e),Module.HEAPF32=B=new Float32Array(e),Module.HEAPF64=H=new Float64Array(e)}var K=7984,G=Module.TOTAL_MEMORY||33554432;function V(e){for(;e.length>0;){var t=e.shift();if("function"!=typeof t){var r=t.func;"number"==typeof r?void 0===t.arg?Module.dynCall_v(r):Module.dynCall_vi(r,t.arg):r(void 0===t.arg?null:t.arg)}else t()}}(x=Module.wasmMemory?Module.wasmMemory:new WebAssembly.Memory({initial:G/65536}))&&(Z=x.buffer),G=Z.byteLength,z(Z),D[K>>2]=5251024;var X=[],Q=[],Y=[],J=[],ee=!1;function te(e){X.unshift(e)}var re=Math.abs,ne=Math.ceil,oe=Math.floor,se=Math.min,_e=0,ae=null,ue=null;function ie(e){_e++,Module.monitorRunDependencies&&Module.monitorRunDependencies(_e)}function le(e){if(_e--,Module.monitorRunDependencies&&Module.monitorRunDependencies(_e),0==_e&&(null!==ae&&(clearInterval(ae),ae=null),ue)){var t=ue;ue=null,t()}}function de(e){throw Module.onAbort&&Module.onAbort(e),m(e+=""),f(e),C=!0,1,e="abort("+e+"). Build with -s ASSERTIONS=1 for more info.",new WebAssembly.RuntimeError(e)}Module.preloadedImages={},Module.preloadedAudios={},Module.preloadedWasm={},te(function(){if(Module.dynamicLibraries&&Module.dynamicLibraries.length>0&&!i)return ie(),void Promise.all(Module.dynamicLibraries.map(function(e){return y(e,{loadAsync:!0,global:!0,nodelete:!0})})).then(function(){le()});var e;(e=Module.dynamicLibraries)&&e.forEach(function(e){y(e,{global:!0,nodelete:!0})})});var ce="data:application/octet-stream;base64,";function me(e){return String.prototype.startsWith?e.startsWith(ce):0===e.indexOf(ce)}var fe,pe,he,we="tree-sitter.wasm";function ge(){try{if(S)return new Uint8Array(S);if(i)return i(we);throw"both async and sync fetching of the wasm failed"}catch(e){de(e)}}function Me(){de()}function ye(){de()}me(we)||(fe=we,we=Module.locateFile?Module.locateFile(fe,c):c+fe),Q.push({func:function(){Fe()}},{func:function(){ke()}}),Module._abort=Me;var be=r||"undefined"!=typeof dateNow||1;function Ee(){return U.length}function ve(e){try{return x.grow(e-Z.byteLength+65535>>16),z(x.buffer),1}catch(e){}}function Ie(e,t,r){if(ot){const e=W(r);ot(e,0!==t)}}ye=r?function(){var e=process.hrtime();return 1e3*e[0]+e[1]/1e6}:"undefined"!=typeof dateNow?dateNow:function(){return performance.now()};var Se=A,Ne={__memory_base:1024,__stack_pointer:5251024,__table_base:1,abort:Me,clock_gettime:function(e,t){var r,n;if(0===e)r=Date.now();else{if(1!==e&&4!==e||!be)return n=28,Module.___errno_location&&(D[Module.___errno_location()>>2]=n),-1;r=ye()}return D[t>>2]=r/1e3|0,D[t+4>>2]=r%1e3*1e3*1e3|0,0},emscripten_memcpy_big:function(e,t,r){U.set(U.subarray(t,t+r),e)},emscripten_resize_heap:function(e){var t=Ee();if(e>2147418112)return!1;for(var r,n,o=1;o<=4;o*=2){var s=t*(1+.2/o);if(s=Math.min(s,e+100663296),ve(Math.min(2147418112,((r=Math.max(16777216,e,s))%(n=65536)>0&&(r+=n-r%n),r))))return!0}return!1},exit:function(e){Ue(e)},fp$tree_sitter_log_callback$viii:function(){return Module._fp$tree_sitter_log_callback$viii.apply(null,arguments)},g$TRANSFER_BUFFER:function(){return Module._TRANSFER_BUFFER},g$__THREW__:function(){return Module.___THREW__},g$__cxa_new_handler:function(){return Module.___cxa_new_handler},g$__threwValue:function(){return Module.___threwValue},memory:x,table:R,tree_sitter_parse_callback:function(e,t,r,n,o){var s=nt(t,{row:r,column:n});"string"==typeof s?(k(o,s.length,"i32"),function(e,t,r){if(void 0===r&&(r=2147483647),r<2)return 0;for(var n=(r-=2)<2*e.length?r/2:e.length,o=0;o>1]=s,t+=2}j[t>>1]=0}(s,e,10240)):k(o,0,"i32")}},xe=function(){var e={env:Ne,wasi_snapshot_preview1:Ne};function t(e,t){var r=e.exports;r=b(r,A),Module.asm=r,le()}function r(e){t(e.instance)}function n(t){return(S||!_&&!a||"function"!=typeof fetch?new Promise(function(e,t){e(ge())}):fetch(we,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+we+"'";return e.arrayBuffer()}).catch(function(){return ge()})).then(function(t){return WebAssembly.instantiate(t,e)}).then(t,function(e){f("failed to asynchronously prepare wasm: "+e),de(e)})}if(ie(),Module.instantiateWasm)try{return Module.instantiateWasm(e,t)}catch(e){return f("Module.instantiateWasm callback failed with error: "+e),!1}return function(){if(S||"function"!=typeof WebAssembly.instantiateStreaming||me(we)||"function"!=typeof fetch)return n(r);fetch(we,{credentials:"same-origin"}).then(function(t){return WebAssembly.instantiateStreaming(t,e).then(r,function(e){f("wasm streaming compile failed: "+e),f("falling back to ArrayBuffer instantiation"),n(r)})})}(),{}}();Module.asm=xe;var Ae,ke=Module.___wasm_call_ctors=function(){return(ke=Module.___wasm_call_ctors=Module.asm.__wasm_call_ctors).apply(null,arguments)},Pe=(Module._calloc=function(){return(Module._calloc=Module.asm.calloc).apply(null,arguments)},Module._ts_language_symbol_count=function(){return(Module._ts_language_symbol_count=Module.asm.ts_language_symbol_count).apply(null,arguments)},Module._ts_language_version=function(){return(Module._ts_language_version=Module.asm.ts_language_version).apply(null,arguments)},Module._ts_language_field_count=function(){return(Module._ts_language_field_count=Module.asm.ts_language_field_count).apply(null,arguments)},Module._ts_language_symbol_name=function(){return(Module._ts_language_symbol_name=Module.asm.ts_language_symbol_name).apply(null,arguments)},Module._ts_language_symbol_type=function(){return(Module._ts_language_symbol_type=Module.asm.ts_language_symbol_type).apply(null,arguments)},Module._ts_language_field_name_for_id=function(){return(Module._ts_language_field_name_for_id=Module.asm.ts_language_field_name_for_id).apply(null,arguments)},Module._memcpy=function(){return(Module._memcpy=Module.asm.memcpy).apply(null,arguments)},Module._free=function(){return(Module._free=Module.asm.free).apply(null,arguments)},Module._malloc=function(){return(Pe=Module._malloc=Module.asm.malloc).apply(null,arguments)}),Re=(Module._ts_parser_delete=function(){return(Module._ts_parser_delete=Module.asm.ts_parser_delete).apply(null,arguments)},Module._ts_parser_set_language=function(){return(Module._ts_parser_set_language=Module.asm.ts_parser_set_language).apply(null,arguments)},Module._memcmp=function(){return(Module._memcmp=Module.asm.memcmp).apply(null,arguments)},Module._ts_query_new=function(){return(Module._ts_query_new=Module.asm.ts_query_new).apply(null,arguments)},Module._iswspace=function(){return(Module._iswspace=Module.asm.iswspace).apply(null,arguments)},Module._ts_query_delete=function(){return(Module._ts_query_delete=Module.asm.ts_query_delete).apply(null,arguments)},Module._iswalnum=function(){return(Module._iswalnum=Module.asm.iswalnum).apply(null,arguments)},Module._ts_query_pattern_count=function(){return(Module._ts_query_pattern_count=Module.asm.ts_query_pattern_count).apply(null,arguments)},Module._ts_query_capture_count=function(){return(Module._ts_query_capture_count=Module.asm.ts_query_capture_count).apply(null,arguments)},Module._ts_query_string_count=function(){return(Module._ts_query_string_count=Module.asm.ts_query_string_count).apply(null,arguments)},Module._ts_query_capture_name_for_id=function(){return(Module._ts_query_capture_name_for_id=Module.asm.ts_query_capture_name_for_id).apply(null,arguments)},Module._ts_query_string_value_for_id=function(){return(Module._ts_query_string_value_for_id=Module.asm.ts_query_string_value_for_id).apply(null,arguments)},Module._ts_query_predicates_for_pattern=function(){return(Module._ts_query_predicates_for_pattern=Module.asm.ts_query_predicates_for_pattern).apply(null,arguments)},Module._ts_tree_delete=function(){return(Module._ts_tree_delete=Module.asm.ts_tree_delete).apply(null,arguments)},Module._ts_init=function(){return(Module._ts_init=Module.asm.ts_init).apply(null,arguments)},Module._ts_parser_new_wasm=function(){return(Module._ts_parser_new_wasm=Module.asm.ts_parser_new_wasm).apply(null,arguments)},Module._ts_parser_enable_logger_wasm=function(){return(Module._ts_parser_enable_logger_wasm=Module.asm.ts_parser_enable_logger_wasm).apply(null,arguments)},Module._ts_parser_parse_wasm=function(){return(Module._ts_parser_parse_wasm=Module.asm.ts_parser_parse_wasm).apply(null,arguments)},Module._ts_tree_root_node_wasm=function(){return(Module._ts_tree_root_node_wasm=Module.asm.ts_tree_root_node_wasm).apply(null,arguments)},Module._ts_tree_edit_wasm=function(){return(Module._ts_tree_edit_wasm=Module.asm.ts_tree_edit_wasm).apply(null,arguments)},Module._ts_tree_get_changed_ranges_wasm=function(){return(Module._ts_tree_get_changed_ranges_wasm=Module.asm.ts_tree_get_changed_ranges_wasm).apply(null,arguments)},Module._ts_tree_cursor_new_wasm=function(){return(Module._ts_tree_cursor_new_wasm=Module.asm.ts_tree_cursor_new_wasm).apply(null,arguments)},Module._ts_tree_cursor_delete_wasm=function(){return(Module._ts_tree_cursor_delete_wasm=Module.asm.ts_tree_cursor_delete_wasm).apply(null,arguments)},Module._ts_tree_cursor_reset_wasm=function(){return(Module._ts_tree_cursor_reset_wasm=Module.asm.ts_tree_cursor_reset_wasm).apply(null,arguments)},Module._ts_tree_cursor_goto_first_child_wasm=function(){return(Module._ts_tree_cursor_goto_first_child_wasm=Module.asm.ts_tree_cursor_goto_first_child_wasm).apply(null,arguments)},Module._ts_tree_cursor_goto_next_sibling_wasm=function(){return(Module._ts_tree_cursor_goto_next_sibling_wasm=Module.asm.ts_tree_cursor_goto_next_sibling_wasm).apply(null,arguments)},Module._ts_tree_cursor_goto_parent_wasm=function(){return(Module._ts_tree_cursor_goto_parent_wasm=Module.asm.ts_tree_cursor_goto_parent_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_node_type_id_wasm=function(){return(Module._ts_tree_cursor_current_node_type_id_wasm=Module.asm.ts_tree_cursor_current_node_type_id_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_node_is_named_wasm=function(){return(Module._ts_tree_cursor_current_node_is_named_wasm=Module.asm.ts_tree_cursor_current_node_is_named_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_node_is_missing_wasm=function(){return(Module._ts_tree_cursor_current_node_is_missing_wasm=Module.asm.ts_tree_cursor_current_node_is_missing_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_node_id_wasm=function(){return(Module._ts_tree_cursor_current_node_id_wasm=Module.asm.ts_tree_cursor_current_node_id_wasm).apply(null,arguments)},Module._ts_tree_cursor_start_position_wasm=function(){return(Module._ts_tree_cursor_start_position_wasm=Module.asm.ts_tree_cursor_start_position_wasm).apply(null,arguments)},Module._ts_tree_cursor_end_position_wasm=function(){return(Module._ts_tree_cursor_end_position_wasm=Module.asm.ts_tree_cursor_end_position_wasm).apply(null,arguments)},Module._ts_tree_cursor_start_index_wasm=function(){return(Module._ts_tree_cursor_start_index_wasm=Module.asm.ts_tree_cursor_start_index_wasm).apply(null,arguments)},Module._ts_tree_cursor_end_index_wasm=function(){return(Module._ts_tree_cursor_end_index_wasm=Module.asm.ts_tree_cursor_end_index_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_field_id_wasm=function(){return(Module._ts_tree_cursor_current_field_id_wasm=Module.asm.ts_tree_cursor_current_field_id_wasm).apply(null,arguments)},Module._ts_tree_cursor_current_node_wasm=function(){return(Module._ts_tree_cursor_current_node_wasm=Module.asm.ts_tree_cursor_current_node_wasm).apply(null,arguments)},Module._ts_node_symbol_wasm=function(){return(Module._ts_node_symbol_wasm=Module.asm.ts_node_symbol_wasm).apply(null,arguments)},Module._ts_node_child_count_wasm=function(){return(Module._ts_node_child_count_wasm=Module.asm.ts_node_child_count_wasm).apply(null,arguments)},Module._ts_node_named_child_count_wasm=function(){return(Module._ts_node_named_child_count_wasm=Module.asm.ts_node_named_child_count_wasm).apply(null,arguments)},Module._ts_node_child_wasm=function(){return(Module._ts_node_child_wasm=Module.asm.ts_node_child_wasm).apply(null,arguments)},Module._ts_node_named_child_wasm=function(){return(Module._ts_node_named_child_wasm=Module.asm.ts_node_named_child_wasm).apply(null,arguments)},Module._ts_node_child_by_field_id_wasm=function(){return(Module._ts_node_child_by_field_id_wasm=Module.asm.ts_node_child_by_field_id_wasm).apply(null,arguments)},Module._ts_node_next_sibling_wasm=function(){return(Module._ts_node_next_sibling_wasm=Module.asm.ts_node_next_sibling_wasm).apply(null,arguments)},Module._ts_node_prev_sibling_wasm=function(){return(Module._ts_node_prev_sibling_wasm=Module.asm.ts_node_prev_sibling_wasm).apply(null,arguments)},Module._ts_node_next_named_sibling_wasm=function(){return(Module._ts_node_next_named_sibling_wasm=Module.asm.ts_node_next_named_sibling_wasm).apply(null,arguments)},Module._ts_node_prev_named_sibling_wasm=function(){return(Module._ts_node_prev_named_sibling_wasm=Module.asm.ts_node_prev_named_sibling_wasm).apply(null,arguments)},Module._ts_node_parent_wasm=function(){return(Module._ts_node_parent_wasm=Module.asm.ts_node_parent_wasm).apply(null,arguments)},Module._ts_node_descendant_for_index_wasm=function(){return(Module._ts_node_descendant_for_index_wasm=Module.asm.ts_node_descendant_for_index_wasm).apply(null,arguments)},Module._ts_node_named_descendant_for_index_wasm=function(){return(Module._ts_node_named_descendant_for_index_wasm=Module.asm.ts_node_named_descendant_for_index_wasm).apply(null,arguments)},Module._ts_node_descendant_for_position_wasm=function(){return(Module._ts_node_descendant_for_position_wasm=Module.asm.ts_node_descendant_for_position_wasm).apply(null,arguments)},Module._ts_node_named_descendant_for_position_wasm=function(){return(Module._ts_node_named_descendant_for_position_wasm=Module.asm.ts_node_named_descendant_for_position_wasm).apply(null,arguments)},Module._ts_node_start_point_wasm=function(){return(Module._ts_node_start_point_wasm=Module.asm.ts_node_start_point_wasm).apply(null,arguments)},Module._ts_node_end_point_wasm=function(){return(Module._ts_node_end_point_wasm=Module.asm.ts_node_end_point_wasm).apply(null,arguments)},Module._ts_node_start_index_wasm=function(){return(Module._ts_node_start_index_wasm=Module.asm.ts_node_start_index_wasm).apply(null,arguments)},Module._ts_node_end_index_wasm=function(){return(Module._ts_node_end_index_wasm=Module.asm.ts_node_end_index_wasm).apply(null,arguments)},Module._ts_node_to_string_wasm=function(){return(Module._ts_node_to_string_wasm=Module.asm.ts_node_to_string_wasm).apply(null,arguments)},Module._ts_node_children_wasm=function(){return(Module._ts_node_children_wasm=Module.asm.ts_node_children_wasm).apply(null,arguments)},Module._ts_node_named_children_wasm=function(){return(Module._ts_node_named_children_wasm=Module.asm.ts_node_named_children_wasm).apply(null,arguments)},Module._ts_node_descendants_of_type_wasm=function(){return(Module._ts_node_descendants_of_type_wasm=Module.asm.ts_node_descendants_of_type_wasm).apply(null,arguments)},Module._ts_node_is_named_wasm=function(){return(Module._ts_node_is_named_wasm=Module.asm.ts_node_is_named_wasm).apply(null,arguments)},Module._ts_node_has_changes_wasm=function(){return(Module._ts_node_has_changes_wasm=Module.asm.ts_node_has_changes_wasm).apply(null,arguments)},Module._ts_node_has_error_wasm=function(){return(Module._ts_node_has_error_wasm=Module.asm.ts_node_has_error_wasm).apply(null,arguments)},Module._ts_node_is_missing_wasm=function(){return(Module._ts_node_is_missing_wasm=Module.asm.ts_node_is_missing_wasm).apply(null,arguments)},Module._ts_query_matches_wasm=function(){return(Module._ts_query_matches_wasm=Module.asm.ts_query_matches_wasm).apply(null,arguments)},Module._ts_query_captures_wasm=function(){return(Module._ts_query_captures_wasm=Module.asm.ts_query_captures_wasm).apply(null,arguments)},Module._iswdigit=function(){return(Module._iswdigit=Module.asm.iswdigit).apply(null,arguments)},Module._iswalpha=function(){return(Module._iswalpha=Module.asm.iswalpha).apply(null,arguments)},Module._iswlower=function(){return(Module._iswlower=Module.asm.iswlower).apply(null,arguments)},Module._towupper=function(){return(Module._towupper=Module.asm.towupper).apply(null,arguments)},Module._memchr=function(){return(Module._memchr=Module.asm.memchr).apply(null,arguments)},Module._strlen=function(){return(Module._strlen=Module.asm.strlen).apply(null,arguments)},Module._setThrew=function(){return(Re=Module._setThrew=Module.asm.setThrew).apply(null,arguments)}),Ce=(Module.__Znwm=function(){return(Module.__Znwm=Module.asm._Znwm).apply(null,arguments)},Module.__ZdlPv=function(){return(Module.__ZdlPv=Module.asm._ZdlPv).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc).apply(null,arguments)},Module.__ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm=function(){return(Module.__ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm=Module.asm._ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm).apply(null,arguments)},Module.__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev=function(){return(Module.__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev=Module.asm._ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev).apply(null,arguments)},Module.__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw=function(){return(Module.__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw=Module.asm._ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw).apply(null,arguments)},Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_=function(){return(Module.__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_=Module.asm._ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_).apply(null,arguments)},Module.__ZNKSt3__220__vector_base_commonILb1EE20__throw_length_errorEv=function(){return(Module.__ZNKSt3__220__vector_base_commonILb1EE20__throw_length_errorEv=Module.asm._ZNKSt3__220__vector_base_commonILb1EE20__throw_length_errorEv).apply(null,arguments)},Module.stackSave=function(){return(Ce=Module.stackSave=Module.asm.stackSave).apply(null,arguments)}),qe=Module.stackAlloc=function(){return(qe=Module.stackAlloc=Module.asm.stackAlloc).apply(null,arguments)},Te=Module.stackRestore=function(){return(Te=Module.stackRestore=Module.asm.stackRestore).apply(null,arguments)},Fe=Module.___assign_got_enties=function(){return(Fe=Module.___assign_got_enties=Module.asm.__assign_got_enties).apply(null,arguments)},Le=(Module.dynCall_vi=function(){return(Module.dynCall_vi=Module.asm.dynCall_vi).apply(null,arguments)},{__cxa_new_handler:6112,__data_end:6960,__THREW__:6952,TRANSFER_BUFFER:1472,__threwValue:6956});for(var $e in Le)Module["_"+$e]=Se+Le[$e];for(var $e in Module.NAMED_GLOBALS=Le,Le)!function(e){var t=Module["_"+e];Module["g$_"+e]=function(){return t}}($e);function We(){var e=Ce();try{var t=Array.prototype.slice.call(arguments);return R.get(t[0]).apply(null,t.slice(1))}catch(t){if(Te(e),t!==t+0&&"longjmp"!==t)throw t;Re(1,0)}}function Ze(e){this.name="ExitStatus",this.message="Program terminated with exit("+e+")",this.status=e}Module._fp$tree_sitter_log_callback$viii=function(){q(Module._tree_sitter_log_callback||!0,"external function `tree_sitter_log_callback` is missing.perhaps a side module was not linked in? if this symbol was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=XX in the environment");var e=Module.asm.tree_sitter_log_callback;e||(e=Module._tree_sitter_log_callback),e||(e=Module._tree_sitter_log_callback),e||(e=Ie);var t=I(e,"viii");return Module._fp$tree_sitter_log_callback$viii=function(){return t},t},Module.asm=xe,Module.allocate=function(e,t,r,n){var o,s;"number"==typeof e?(o=!0,s=e):(o=!1,s=e.length);var _,a="string"==typeof t?t:null;if(_=r==T?n:[Pe,qe,h][r](Math.max(s,a?1:t.length)),o){var u;for(n=_,q(0==(3&_)),u=_+(-4&s);n>2]=0;for(u=_+s;n>0]=0;return _}if("i8"===a)return e.subarray||e.slice?U.set(e,_):U.set(new Uint8Array(e),_),_;for(var i,l,d,c=0;c0||(!function(){if(Module.preRun)for("function"==typeof Module.preRun&&(Module.preRun=[Module.preRun]);Module.preRun.length;)te(Module.preRun.shift());V(X)}(),_e>0||(Module.setStatus?(Module.setStatus("Running..."),setTimeout(function(){setTimeout(function(){Module.setStatus("")},1),t()},1)):t()))}function Ue(e,t){t&&N&&0===e||(N||(C=!0,e,!0,Module.onExit&&Module.onExit(e)),s(e,new Ze(e)))}if(ue=function e(){Ae||Oe(),Ae||(ue=e)},Module.run=Oe,Module.preInit)for("function"==typeof Module.preInit&&(Module.preInit=[Module.preInit]);Module.preInit.length>0;)Module.preInit.pop()();var je=!0;Module.noInitialRun&&(je=!1),N=!0,Oe();const De=Module,Be={},He=4,ze=5*He,Ke=2*He,Ge=2*He+2*Ke,Ve={row:0,column:0},Xe=/[\w-.]*/g,Qe=1,Ye=2,Je=/^_?tree_sitter_\w+/;var et,tt,rt,nt,ot,st=new Promise(e=>{Module.onRuntimeInitialized=e}).then(()=>{rt=De._ts_init(),et=P(rt,"i32"),tt=P(rt+He,"i32")});class Parser{static init(){return st}constructor(){if(null==rt)throw new Error("You must first call Parser.init() and wait for it to resolve.");De._ts_parser_new_wasm(),this[0]=P(rt,"i32"),this[1]=P(rt+He,"i32")}delete(){De._ts_parser_delete(this[0]),De._free(this[1])}setLanguage(e){let t;if(e){if(e.constructor!==Language)throw new Error("Argument must be a Language");{t=e[0];const r=De._ts_language_version(t);if(re.slice(t,n));else{if("function"!=typeof e)throw new Error("Argument must be a string or a function");nt=e}this.logCallback?(ot=this.logCallback,De._ts_parser_enable_logger_wasm(this[0],1)):(ot=null,De._ts_parser_enable_logger_wasm(this[0],0));let n=0,o=0;if(r&&r.includedRanges){n=r.includedRanges.length;let e=o=De._calloc(n,Ge);for(let t=0;t0){let e=r;for(let r=0;r0){let r=t;for(let t=0;t0){let r=t;for(let t=0;t0){let e=a;for(let t=0;t<_;t++)u[t]=dt(this.tree,e),e+=ze}return De._free(a),De._free(s),u}get nextSibling(){return lt(this),De._ts_node_next_sibling_wasm(this.tree[0]),dt(this.tree)}get previousSibling(){return lt(this),De._ts_node_prev_sibling_wasm(this.tree[0]),dt(this.tree)}get nextNamedSibling(){return lt(this),De._ts_node_next_named_sibling_wasm(this.tree[0]),dt(this.tree)}get previousNamedSibling(){return lt(this),De._ts_node_prev_named_sibling_wasm(this.tree[0]),dt(this.tree)}get parent(){return lt(this),De._ts_node_parent_wasm(this.tree[0]),dt(this.tree)}descendantForIndex(e,t=e){if("number"!=typeof e||"number"!=typeof t)throw new Error("Arguments must be numbers");lt(this);let r=rt+ze;return k(r,e,"i32"),k(r+He,t,"i32"),De._ts_node_descendant_for_index_wasm(this.tree[0]),dt(this.tree)}namedDescendantForIndex(e,t=e){if("number"!=typeof e||"number"!=typeof t)throw new Error("Arguments must be numbers");lt(this);let r=rt+ze;return k(r,e,"i32"),k(r+He,t,"i32"),De._ts_node_named_descendant_for_index_wasm(this.tree[0]),dt(this.tree)}descendantForPosition(e,t=e){if(!it(e)||!it(t))throw new Error("Arguments must be {row, column} objects");lt(this);let r=rt+ze;return ft(r,e),ft(r+Ke,t),De._ts_node_descendant_for_position_wasm(this.tree[0]),dt(this.tree)}namedDescendantForPosition(e,t=e){if(!it(e)||!it(t))throw new Error("Arguments must be {row, column} objects");lt(this);let r=rt+ze;return ft(r,e),ft(r+Ke,t),De._ts_node_named_descendant_for_position_wasm(this.tree[0]),dt(this.tree)}walk(){return lt(this),De._ts_tree_cursor_new_wasm(this.tree[0]),new TreeCursor(Be,this.tree)}toString(){lt(this);const e=De._ts_node_to_string_wasm(this.tree[0]),t=function(e){for(var t="";;){var r=U[e++>>0];if(!r)return t;t+=String.fromCharCode(r)}}(e);return De._free(e),t}}class TreeCursor{constructor(e,t){ut(e),this.tree=t,mt(this)}delete(){ct(this),De._ts_tree_cursor_delete_wasm(this.tree[0])}reset(e){lt(e),ct(this,rt+ze),De._ts_tree_cursor_reset_wasm(this.tree[0]),mt(this)}get nodeType(){return this.tree.language.types[this.nodeTypeId]||"ERROR"}get nodeTypeId(){return ct(this),De._ts_tree_cursor_current_node_type_id_wasm(this.tree[0])}get nodeId(){return ct(this),De._ts_tree_cursor_current_node_id_wasm(this.tree[0])}get nodeIsNamed(){return ct(this),1===De._ts_tree_cursor_current_node_is_named_wasm(this.tree[0])}get nodeIsMissing(){return ct(this),1===De._ts_tree_cursor_current_node_is_missing_wasm(this.tree[0])}get nodeText(){ct(this);const e=De._ts_tree_cursor_start_index_wasm(this.tree[0]),t=De._ts_tree_cursor_end_index_wasm(this.tree[0]);return _t(this.tree,e,t)}get startPosition(){return ct(this),De._ts_tree_cursor_start_position_wasm(this.tree[0]),pt(rt)}get endPosition(){return ct(this),De._ts_tree_cursor_end_position_wasm(this.tree[0]),pt(rt)}get startIndex(){return ct(this),De._ts_tree_cursor_start_index_wasm(this.tree[0])}get endIndex(){return ct(this),De._ts_tree_cursor_end_index_wasm(this.tree[0])}currentNode(){return ct(this),De._ts_tree_cursor_current_node_wasm(this.tree[0]),dt(this.tree)}currentFieldId(){return ct(this),De._ts_tree_cursor_current_field_id_wasm(this.tree[0])}currentFieldName(){return this.tree.language.fields[this.currentFieldId()]}gotoFirstChild(){ct(this);const e=De._ts_tree_cursor_goto_first_child_wasm(this.tree[0]);return mt(this),1===e}gotoNextSibling(){ct(this);const e=De._ts_tree_cursor_goto_next_sibling_wasm(this.tree[0]);return mt(this),1===e}gotoParent(){ct(this);const e=De._ts_tree_cursor_goto_parent_wasm(this.tree[0]);return mt(this),1===e}}class Language{constructor(e,t){ut(e),this[0]=t,this.types=new Array(De._ts_language_symbol_count(this[0]));for(let e=0,t=this.types.length;e=55296&&n<=57343&&(n=65536+((1023&n)<<10)|1023&e.charCodeAt(++r)),n<=127?++t:t+=n<=2047?2:n<=65535?3:4}return t}(e),r=De._malloc(t+1);(function(e,t,r,n){if(!(n>0))return 0;for(var o=r,s=r+n-1,_=0;_=55296&&a<=57343&&(a=65536+((1023&a)<<10)|1023&e.charCodeAt(++_)),a<=127){if(r>=s)break;t[r++]=a}else if(a<=2047){if(r+1>=s)break;t[r++]=192|a>>6,t[r++]=128|63&a}else if(a<=65535){if(r+2>=s)break;t[r++]=224|a>>12,t[r++]=128|a>>6&63,t[r++]=128|63&a}else{if(r+3>=s)break;t[r++]=240|a>>18,t[r++]=128|a>>12&63,t[r++]=128|a>>6&63,t[r++]=128|63&a}}t[r]=0})(e,U,r,t+1);const n=De._ts_query_new(this[0],r,t,rt,rt+He);if(!n){const t=P(rt+He,"i32"),n=W(r,P(rt,"i32")).length,o=e.substr(n,100),s=o.match(Xe)[0];let _;switch(t){case 2:_=new RangeError(`Bad node name '${s}'`);break;case 3:_=new RangeError(`Bad field name '${s}'`);break;case 4:_=new RangeError(`Bad capture name @${s}`);break;default:_=new SyntaxError(`Bad syntax at offset ${n}: '${o}'...`)}throw _.index=n,_.length=s.length,De._free(r),_}const o=De._ts_query_string_count(n),s=De._ts_query_capture_count(n),_=De._ts_query_pattern_count(n),a=new Array(s),u=new Array(o);for(let e=0;e0){if("string"!==o[0].type)throw new Error("Predicates must begin with a literal value");const t=o[0].value;let r=!0;switch(t){case"not-eq?":r=!1;case"eq?":if(3!==o.length)throw new Error(`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${o.length-1}`);if("capture"!==o[1].type)throw new Error(`First argument of \`eq?\` predicate must be a capture. Got "${o[1].value}"`);if("capture"===o[2].type){const t=o[1].name,n=o[2].name;c[e].push(function(e){let o,s;for(const r of e)r.name===t&&(o=r.node),r.name===n&&(s=r.node);return o.text===s.text===r})}else{const t=o[1].name,n=o[2].value;c[e].push(function(e){for(const o of e)if(o.name===t)return o.node.text===n===r;return!1})}break;case"match?":if(3!==o.length)throw new Error(`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${o.length-1}.`);if("capture"!==o[1].type)throw new Error(`First argument of \`match?\` predicate must be a capture. Got "${o[1].value}".`);if("string"!==o[2].type)throw new Error(`Second argument of \`match?\` predicate must be a string. Got @${o[2].value}.`);const n=o[1].name,s=new RegExp(o[2].value);c[e].push(function(e){for(const t of e)if(t.name===n)return s.test(t.node.text);return!1});break;case"set!":if(o.length<2||o.length>3)throw new Error(`Wrong number of arguments to \`set!\` predicate. Expected 1 or 2. Got ${o.length-1}.`);if(o.some(e=>"string"!==e.type))throw new Error('Arguments to `set!` predicate must be a strings.".');i[e]||(i[e]={}),i[e][o[1].value]=o[2]?o[2].value:null;break;case"is?":case"is-not?":if(o.length<2||o.length>3)throw new Error(`Wrong number of arguments to \`${t}\` predicate. Expected 1 or 2. Got ${o.length-1}.`);if(o.some(e=>"string"!==e.type))throw new Error(`Arguments to \`${t}\` predicate must be a strings.".`);const _="is?"===t?l:d;_[e]||(_[e]={}),_[e][o[1].value]=o[2]?o[2].value:null;break;default:throw new Error(`Unknown query predicate \`${o[0].value}\``)}o.length=0}}Object.freeze(i[e]),Object.freeze(l[e]),Object.freeze(d[e])}return De._free(r),new Query(Be,n,a,c,Object.freeze(i),Object.freeze(l),Object.freeze(d))}static load(e){let t;if("undefined"!=typeof process&&process.versions&&process.versions.node){const r=require("fs");t=Promise.resolve(r.readFileSync(e))}else t=fetch(e).then(e=>e.arrayBuffer().then(t=>{if(e.ok)return new Uint8Array(t);{const r=new TextDecoder("utf-8").decode(t);throw new Error(`Language.load failed with status ${e.status}.\n\n${r}`)}}));return t.then(e=>E(e,{loadAsync:!0})).then(e=>{const t=e[Object.keys(e).find(e=>Je.test(e)&&!e.includes("external_scanner_"))]();return new Language(Be,t)})}}class Query{constructor(e,t,r,n,o,s,_){ut(e),this[0]=t,this.captureNames=r,this.predicates=n,this.setProperties=o,this.assertedProperties=s,this.refutedProperties=_}delete(){De._ts_query_delete(this[0])}matches(e,t,r){t||(t=Ve),r||(r=Ve),lt(e),De._ts_query_matches_wasm(this[0],e.tree[0],t.row,t.column,r.row,r.column);const n=P(rt,"i32"),o=P(rt+He,"i32"),s=new Array(n);let _=o;for(let t=0;te(o))){s[t]={pattern:r,captures:o};const e=this.setProperties[r];e&&(s[t].setProperties=e);const n=this.assertedProperties[r];n&&(s[t].assertedProperties=n);const _=this.refutedProperties[r];_&&(s[t].refutedProperties=_)}}return De._free(o),s}captures(e,t,r){t||(t=Ve),r||(r=Ve),lt(e),De._ts_query_captures_wasm(this[0],e.tree[0],t.row,t.column,r.row,r.column);const n=P(rt,"i32"),o=P(rt+He,"i32"),s=[],_=[];let a=o;for(let t=0;te(_))){const e=_[n],r=this.setProperties[t];r&&(e.setProperties=r);const o=this.assertedProperties[t];o&&(e.assertedProperties=o);const a=this.refutedProperties[t];a&&(e.refutedProperties=a),s.push(e)}}return De._free(o),s}}function _t(e,t,r){const n=r-t;let o=e.textCallback(t,null,r);for(t+=o.length;t0))break;t+=n.length,o+=n}return t>r&&(o=o.slice(0,n)),o}function at(e,t,r,n){for(let o=0,s=n.length;o __defProp(target, "name", { value, configurable: true });
+
+// src/edit.ts
+var Edit = class {
+ static {
+ __name(this, "Edit");
+ }
+ /** The start position of the change. */
+ startPosition;
+ /** The end position of the change before the edit. */
+ oldEndPosition;
+ /** The end position of the change after the edit. */
+ newEndPosition;
+ /** The start index of the change. */
+ startIndex;
+ /** The end index of the change before the edit. */
+ oldEndIndex;
+ /** The end index of the change after the edit. */
+ newEndIndex;
+ constructor({
+ startIndex,
+ oldEndIndex,
+ newEndIndex,
+ startPosition,
+ oldEndPosition,
+ newEndPosition
+ }) {
+ this.startIndex = startIndex >>> 0;
+ this.oldEndIndex = oldEndIndex >>> 0;
+ this.newEndIndex = newEndIndex >>> 0;
+ this.startPosition = startPosition;
+ this.oldEndPosition = oldEndPosition;
+ this.newEndPosition = newEndPosition;
+ }
+ /**
+ * Edit a point and index to keep it in-sync with source code that has been edited.
+ *
+ * This function updates a single point's byte offset and row/column position
+ * based on an edit operation. This is useful for editing points without
+ * requiring a tree or node instance.
+ */
+ editPoint(point, index) {
+ let newIndex = index;
+ const newPoint = { ...point };
+ if (index >= this.oldEndIndex) {
+ newIndex = this.newEndIndex + (index - this.oldEndIndex);
+ const originalRow = point.row;
+ newPoint.row = this.newEndPosition.row + (point.row - this.oldEndPosition.row);
+ newPoint.column = originalRow === this.oldEndPosition.row ? this.newEndPosition.column + (point.column - this.oldEndPosition.column) : point.column;
+ } else if (index > this.startIndex) {
+ newIndex = this.newEndIndex;
+ newPoint.row = this.newEndPosition.row;
+ newPoint.column = this.newEndPosition.column;
+ }
+ return { point: newPoint, index: newIndex };
+ }
+ /**
+ * Edit a range to keep it in-sync with source code that has been edited.
+ *
+ * This function updates a range's start and end positions based on an edit
+ * operation. This is useful for editing ranges without requiring a tree
+ * or node instance.
+ */
+ editRange(range) {
+ const newRange = {
+ startIndex: range.startIndex,
+ startPosition: { ...range.startPosition },
+ endIndex: range.endIndex,
+ endPosition: { ...range.endPosition }
+ };
+ if (range.endIndex >= this.oldEndIndex) {
+ if (range.endIndex !== Number.MAX_SAFE_INTEGER) {
+ newRange.endIndex = this.newEndIndex + (range.endIndex - this.oldEndIndex);
+ newRange.endPosition = {
+ row: this.newEndPosition.row + (range.endPosition.row - this.oldEndPosition.row),
+ column: range.endPosition.row === this.oldEndPosition.row ? this.newEndPosition.column + (range.endPosition.column - this.oldEndPosition.column) : range.endPosition.column
+ };
+ if (newRange.endIndex < this.newEndIndex) {
+ newRange.endIndex = Number.MAX_SAFE_INTEGER;
+ newRange.endPosition = { row: Number.MAX_SAFE_INTEGER, column: Number.MAX_SAFE_INTEGER };
+ }
+ }
+ } else if (range.endIndex > this.startIndex) {
+ newRange.endIndex = this.startIndex;
+ newRange.endPosition = { ...this.startPosition };
+ }
+ if (range.startIndex >= this.oldEndIndex) {
+ newRange.startIndex = this.newEndIndex + (range.startIndex - this.oldEndIndex);
+ newRange.startPosition = {
+ row: this.newEndPosition.row + (range.startPosition.row - this.oldEndPosition.row),
+ column: range.startPosition.row === this.oldEndPosition.row ? this.newEndPosition.column + (range.startPosition.column - this.oldEndPosition.column) : range.startPosition.column
+ };
+ if (newRange.startIndex < this.newEndIndex) {
+ newRange.startIndex = Number.MAX_SAFE_INTEGER;
+ newRange.startPosition = { row: Number.MAX_SAFE_INTEGER, column: Number.MAX_SAFE_INTEGER };
+ }
+ } else if (range.startIndex > this.startIndex) {
+ newRange.startIndex = this.startIndex;
+ newRange.startPosition = { ...this.startPosition };
+ }
+ return newRange;
+ }
+};
+
+// src/constants.ts
+var SIZE_OF_SHORT = 2;
+var SIZE_OF_INT = 4;
+var SIZE_OF_CURSOR = 4 * SIZE_OF_INT;
+var SIZE_OF_NODE = 5 * SIZE_OF_INT;
+var SIZE_OF_POINT = 2 * SIZE_OF_INT;
+var SIZE_OF_RANGE = 2 * SIZE_OF_INT + 2 * SIZE_OF_POINT;
+var ZERO_POINT = { row: 0, column: 0 };
+var INTERNAL = /* @__PURE__ */ Symbol("INTERNAL");
+function assertInternal(x) {
+ if (x !== INTERNAL) throw new Error("Illegal constructor");
+}
+__name(assertInternal, "assertInternal");
+function isPoint(point) {
+ return !!point && typeof point.row === "number" && typeof point.column === "number";
+}
+__name(isPoint, "isPoint");
+function setModule(module2) {
+ C = module2;
+}
+__name(setModule, "setModule");
+var C;
+
+// src/lookahead_iterator.ts
+var LookaheadIterator = class {
+ static {
+ __name(this, "LookaheadIterator");
+ }
+ /** @internal */
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ language;
+ /** @internal */
+ constructor(internal, address, language) {
+ assertInternal(internal);
+ this[0] = address;
+ this.language = language;
+ }
+ /** Get the current symbol of the lookahead iterator. */
+ get currentTypeId() {
+ return C._ts_lookahead_iterator_current_symbol(this[0]);
+ }
+ /** Get the current symbol name of the lookahead iterator. */
+ get currentType() {
+ return this.language.types[this.currentTypeId] || "ERROR";
+ }
+ /** Delete the lookahead iterator, freeing its resources. */
+ delete() {
+ C._ts_lookahead_iterator_delete(this[0]);
+ this[0] = 0;
+ }
+ /**
+ * Reset the lookahead iterator.
+ *
+ * This returns `true` if the language was set successfully and `false`
+ * otherwise.
+ */
+ reset(language, stateId) {
+ if (C._ts_lookahead_iterator_reset(this[0], language[0], stateId)) {
+ this.language = language;
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Reset the lookahead iterator to another state.
+ *
+ * This returns `true` if the iterator was reset to the given state and
+ * `false` otherwise.
+ */
+ resetState(stateId) {
+ return Boolean(C._ts_lookahead_iterator_reset_state(this[0], stateId));
+ }
+ /**
+ * Returns an iterator that iterates over the symbols of the lookahead iterator.
+ *
+ * The iterator will yield the current symbol name as a string for each step
+ * until there are no more symbols to iterate over.
+ */
+ [Symbol.iterator]() {
+ return {
+ next: /* @__PURE__ */ __name(() => {
+ if (C._ts_lookahead_iterator_next(this[0])) {
+ return { done: false, value: this.currentType };
+ }
+ return { done: true, value: "" };
+ }, "next")
+ };
+ }
+};
+
+// src/tree.ts
+function getText(tree, startIndex, endIndex, startPosition) {
+ const length = endIndex - startIndex;
+ let result = tree.textCallback(startIndex, startPosition);
+ if (result) {
+ startIndex += result.length;
+ while (startIndex < endIndex) {
+ const string = tree.textCallback(startIndex, startPosition);
+ if (string && string.length > 0) {
+ startIndex += string.length;
+ result += string;
+ } else {
+ break;
+ }
+ }
+ if (startIndex > endIndex) {
+ result = result.slice(0, length);
+ }
+ }
+ return result ?? "";
+}
+__name(getText, "getText");
+var Tree = class _Tree {
+ static {
+ __name(this, "Tree");
+ }
+ /** @internal */
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ textCallback;
+ /** The language that was used to parse the syntax tree. */
+ language;
+ /** @internal */
+ constructor(internal, address, language, textCallback) {
+ assertInternal(internal);
+ this[0] = address;
+ this.language = language;
+ this.textCallback = textCallback;
+ }
+ /** Create a shallow copy of the syntax tree. This is very fast. */
+ copy() {
+ const address = C._ts_tree_copy(this[0]);
+ return new _Tree(INTERNAL, address, this.language, this.textCallback);
+ }
+ /** Delete the syntax tree, freeing its resources. */
+ delete() {
+ C._ts_tree_delete(this[0]);
+ this[0] = 0;
+ }
+ /** Get the root node of the syntax tree. */
+ get rootNode() {
+ C._ts_tree_root_node_wasm(this[0]);
+ return unmarshalNode(this);
+ }
+ /**
+ * Get the root node of the syntax tree, but with its position shifted
+ * forward by the given offset.
+ */
+ rootNodeWithOffset(offsetBytes, offsetExtent) {
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ C.setValue(address, offsetBytes, "i32");
+ marshalPoint(address + SIZE_OF_INT, offsetExtent);
+ C._ts_tree_root_node_with_offset_wasm(this[0]);
+ return unmarshalNode(this);
+ }
+ /**
+ * Edit the syntax tree to keep it in sync with source code that has been
+ * edited.
+ *
+ * You must describe the edit both in terms of byte offsets and in terms of
+ * row/column coordinates.
+ */
+ edit(edit) {
+ marshalEdit(edit);
+ C._ts_tree_edit_wasm(this[0]);
+ }
+ /** Create a new {@link TreeCursor} starting from the root of the tree. */
+ walk() {
+ return this.rootNode.walk();
+ }
+ /**
+ * Compare this old edited syntax tree to a new syntax tree representing
+ * the same document, returning a sequence of ranges whose syntactic
+ * structure has changed.
+ *
+ * For this to work correctly, this syntax tree must have been edited such
+ * that its ranges match up to the new tree. Generally, you'll want to
+ * call this method right after calling one of the [`Parser::parse`]
+ * functions. Call it on the old tree that was passed to parse, and
+ * pass the new tree that was returned from `parse`.
+ */
+ getChangedRanges(other) {
+ if (!(other instanceof _Tree)) {
+ throw new TypeError("Argument must be a Tree");
+ }
+ C._ts_tree_get_changed_ranges_wasm(this[0], other[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = unmarshalRange(address);
+ address += SIZE_OF_RANGE;
+ }
+ C._free(buffer);
+ }
+ return result;
+ }
+ /** Get the included ranges that were used to parse the syntax tree. */
+ getIncludedRanges() {
+ C._ts_tree_included_ranges_wasm(this[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = unmarshalRange(address);
+ address += SIZE_OF_RANGE;
+ }
+ C._free(buffer);
+ }
+ return result;
+ }
+};
+
+// src/tree_cursor.ts
+var TreeCursor = class _TreeCursor {
+ static {
+ __name(this, "TreeCursor");
+ }
+ /** @internal */
+ // @ts-expect-error: never read
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ // @ts-expect-error: never read
+ [1] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ // @ts-expect-error: never read
+ [2] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ // @ts-expect-error: never read
+ [3] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ tree;
+ /** @internal */
+ constructor(internal, tree) {
+ assertInternal(internal);
+ this.tree = tree;
+ unmarshalTreeCursor(this);
+ }
+ /** Creates a deep copy of the tree cursor. This allocates new memory. */
+ copy() {
+ const copy = new _TreeCursor(INTERNAL, this.tree);
+ C._ts_tree_cursor_copy_wasm(this.tree[0]);
+ unmarshalTreeCursor(copy);
+ return copy;
+ }
+ /** Delete the tree cursor, freeing its resources. */
+ delete() {
+ marshalTreeCursor(this);
+ C._ts_tree_cursor_delete_wasm(this.tree[0]);
+ this[0] = this[1] = this[2] = 0;
+ }
+ /** Get the tree cursor's current {@link Node}. */
+ get currentNode() {
+ marshalTreeCursor(this);
+ C._ts_tree_cursor_current_node_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get the numerical field id of this tree cursor's current node.
+ *
+ * See also {@link TreeCursor#currentFieldName}.
+ */
+ get currentFieldId() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_field_id_wasm(this.tree[0]);
+ }
+ /** Get the field name of this tree cursor's current node. */
+ get currentFieldName() {
+ return this.tree.language.fields[this.currentFieldId];
+ }
+ /**
+ * Get the depth of the cursor's current node relative to the original
+ * node that the cursor was constructed with.
+ */
+ get currentDepth() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_depth_wasm(this.tree[0]);
+ }
+ /**
+ * Get the index of the cursor's current node out of all of the
+ * descendants of the original node that the cursor was constructed with.
+ */
+ get currentDescendantIndex() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_descendant_index_wasm(this.tree[0]);
+ }
+ /** Get the type of the cursor's current node. */
+ get nodeType() {
+ return this.tree.language.types[this.nodeTypeId] || "ERROR";
+ }
+ /** Get the type id of the cursor's current node. */
+ get nodeTypeId() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_node_type_id_wasm(this.tree[0]);
+ }
+ /** Get the state id of the cursor's current node. */
+ get nodeStateId() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_node_state_id_wasm(this.tree[0]);
+ }
+ /** Get the id of the cursor's current node. */
+ get nodeId() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_node_id_wasm(this.tree[0]);
+ }
+ /**
+ * Check if the cursor's current node is *named*.
+ *
+ * Named nodes correspond to named rules in the grammar, whereas
+ * *anonymous* nodes correspond to string literals in the grammar.
+ */
+ get nodeIsNamed() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_node_is_named_wasm(this.tree[0]) === 1;
+ }
+ /**
+ * Check if the cursor's current node is *missing*.
+ *
+ * Missing nodes are inserted by the parser in order to recover from
+ * certain kinds of syntax errors.
+ */
+ get nodeIsMissing() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_current_node_is_missing_wasm(this.tree[0]) === 1;
+ }
+ /** Get the string content of the cursor's current node. */
+ get nodeText() {
+ marshalTreeCursor(this);
+ const startIndex = C._ts_tree_cursor_start_index_wasm(this.tree[0]);
+ const endIndex = C._ts_tree_cursor_end_index_wasm(this.tree[0]);
+ C._ts_tree_cursor_start_position_wasm(this.tree[0]);
+ const startPosition = unmarshalPoint(TRANSFER_BUFFER);
+ return getText(this.tree, startIndex, endIndex, startPosition);
+ }
+ /** Get the start position of the cursor's current node. */
+ get startPosition() {
+ marshalTreeCursor(this);
+ C._ts_tree_cursor_start_position_wasm(this.tree[0]);
+ return unmarshalPoint(TRANSFER_BUFFER);
+ }
+ /** Get the end position of the cursor's current node. */
+ get endPosition() {
+ marshalTreeCursor(this);
+ C._ts_tree_cursor_end_position_wasm(this.tree[0]);
+ return unmarshalPoint(TRANSFER_BUFFER);
+ }
+ /** Get the start index of the cursor's current node. */
+ get startIndex() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_start_index_wasm(this.tree[0]);
+ }
+ /** Get the end index of the cursor's current node. */
+ get endIndex() {
+ marshalTreeCursor(this);
+ return C._ts_tree_cursor_end_index_wasm(this.tree[0]);
+ }
+ /**
+ * Move this cursor to the first child of its current node.
+ *
+ * This returns `true` if the cursor successfully moved, and returns
+ * `false` if there were no children.
+ */
+ gotoFirstChild() {
+ marshalTreeCursor(this);
+ const result = C._ts_tree_cursor_goto_first_child_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move this cursor to the last child of its current node.
+ *
+ * This returns `true` if the cursor successfully moved, and returns
+ * `false` if there were no children.
+ *
+ * Note that this function may be slower than
+ * {@link TreeCursor#gotoFirstChild} because it needs to
+ * iterate through all the children to compute the child's position.
+ */
+ gotoLastChild() {
+ marshalTreeCursor(this);
+ const result = C._ts_tree_cursor_goto_last_child_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move this cursor to the parent of its current node.
+ *
+ * This returns `true` if the cursor successfully moved, and returns
+ * `false` if there was no parent node (the cursor was already on the
+ * root node).
+ *
+ * Note that the node the cursor was constructed with is considered the root
+ * of the cursor, and the cursor cannot walk outside this node.
+ */
+ gotoParent() {
+ marshalTreeCursor(this);
+ const result = C._ts_tree_cursor_goto_parent_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move this cursor to the next sibling of its current node.
+ *
+ * This returns `true` if the cursor successfully moved, and returns
+ * `false` if there was no next sibling node.
+ *
+ * Note that the node the cursor was constructed with is considered the root
+ * of the cursor, and the cursor cannot walk outside this node.
+ */
+ gotoNextSibling() {
+ marshalTreeCursor(this);
+ const result = C._ts_tree_cursor_goto_next_sibling_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move this cursor to the previous sibling of its current node.
+ *
+ * This returns `true` if the cursor successfully moved, and returns
+ * `false` if there was no previous sibling node.
+ *
+ * Note that this function may be slower than
+ * {@link TreeCursor#gotoNextSibling} due to how node
+ * positions are stored. In the worst case, this will need to iterate
+ * through all the children up to the previous sibling node to recalculate
+ * its position. Also note that the node the cursor was constructed with is
+ * considered the root of the cursor, and the cursor cannot walk outside this node.
+ */
+ gotoPreviousSibling() {
+ marshalTreeCursor(this);
+ const result = C._ts_tree_cursor_goto_previous_sibling_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move the cursor to the node that is the nth descendant of
+ * the original node that the cursor was constructed with, where
+ * zero represents the original node itself.
+ */
+ gotoDescendant(goalDescendantIndex) {
+ marshalTreeCursor(this);
+ C._ts_tree_cursor_goto_descendant_wasm(this.tree[0], goalDescendantIndex);
+ unmarshalTreeCursor(this);
+ }
+ /**
+ * Move this cursor to the first child of its current node that contains or
+ * starts after the given byte offset.
+ *
+ * This returns `true` if the cursor successfully moved to a child node, and returns
+ * `false` if no such child was found.
+ */
+ gotoFirstChildForIndex(goalIndex) {
+ marshalTreeCursor(this);
+ C.setValue(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalIndex, "i32");
+ const result = C._ts_tree_cursor_goto_first_child_for_index_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Move this cursor to the first child of its current node that contains or
+ * starts after the given byte offset.
+ *
+ * This returns the index of the child node if one was found, and returns
+ * `null` if no such child was found.
+ */
+ gotoFirstChildForPosition(goalPosition) {
+ marshalTreeCursor(this);
+ marshalPoint(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalPosition);
+ const result = C._ts_tree_cursor_goto_first_child_for_position_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ return result === 1;
+ }
+ /**
+ * Re-initialize this tree cursor to start at the original node that the
+ * cursor was constructed with.
+ */
+ reset(node) {
+ marshalNode(node);
+ marshalTreeCursor(this, TRANSFER_BUFFER + SIZE_OF_NODE);
+ C._ts_tree_cursor_reset_wasm(this.tree[0]);
+ unmarshalTreeCursor(this);
+ }
+ /**
+ * Re-initialize a tree cursor to the same position as another cursor.
+ *
+ * Unlike {@link TreeCursor#reset}, this will not lose parent
+ * information and allows reusing already created cursors.
+ */
+ resetTo(cursor) {
+ marshalTreeCursor(this, TRANSFER_BUFFER);
+ marshalTreeCursor(cursor, TRANSFER_BUFFER + SIZE_OF_CURSOR);
+ C._ts_tree_cursor_reset_to_wasm(this.tree[0], cursor.tree[0]);
+ unmarshalTreeCursor(this);
+ }
+};
+
+// src/node.ts
+var Node = class {
+ static {
+ __name(this, "Node");
+ }
+ /** @internal */
+ // @ts-expect-error: never read
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ _children;
+ /** @internal */
+ _namedChildren;
+ /** @internal */
+ constructor(internal, {
+ id,
+ tree,
+ startIndex,
+ startPosition,
+ other
+ }) {
+ assertInternal(internal);
+ this[0] = other;
+ this.id = id;
+ this.tree = tree;
+ this.startIndex = startIndex;
+ this.startPosition = startPosition;
+ }
+ /**
+ * The numeric id for this node that is unique.
+ *
+ * Within a given syntax tree, no two nodes have the same id. However:
+ *
+ * * If a new tree is created based on an older tree, and a node from the old tree is reused in
+ * the process, then that node will have the same id in both trees.
+ *
+ * * A node not marked as having changes does not guarantee it was reused.
+ *
+ * * If a node is marked as having changed in the old tree, it will not be reused.
+ */
+ id;
+ /** The byte index where this node starts. */
+ startIndex;
+ /** The position where this node starts. */
+ startPosition;
+ /** The tree that this node belongs to. */
+ tree;
+ /** Get this node's type as a numerical id. */
+ get typeId() {
+ marshalNode(this);
+ return C._ts_node_symbol_wasm(this.tree[0]);
+ }
+ /**
+ * Get the node's type as a numerical id as it appears in the grammar,
+ * ignoring aliases.
+ */
+ get grammarId() {
+ marshalNode(this);
+ return C._ts_node_grammar_symbol_wasm(this.tree[0]);
+ }
+ /** Get this node's type as a string. */
+ get type() {
+ return this.tree.language.types[this.typeId] || "ERROR";
+ }
+ /**
+ * Get this node's symbol name as it appears in the grammar, ignoring
+ * aliases as a string.
+ */
+ get grammarType() {
+ return this.tree.language.types[this.grammarId] || "ERROR";
+ }
+ /**
+ * Check if this node is *named*.
+ *
+ * Named nodes correspond to named rules in the grammar, whereas
+ * *anonymous* nodes correspond to string literals in the grammar.
+ */
+ get isNamed() {
+ marshalNode(this);
+ return C._ts_node_is_named_wasm(this.tree[0]) === 1;
+ }
+ /**
+ * Check if this node is *extra*.
+ *
+ * Extra nodes represent things like comments, which are not required
+ * by the grammar, but can appear anywhere.
+ */
+ get isExtra() {
+ marshalNode(this);
+ return C._ts_node_is_extra_wasm(this.tree[0]) === 1;
+ }
+ /**
+ * Check if this node represents a syntax error.
+ *
+ * Syntax errors represent parts of the code that could not be incorporated
+ * into a valid syntax tree.
+ */
+ get isError() {
+ marshalNode(this);
+ return C._ts_node_is_error_wasm(this.tree[0]) === 1;
+ }
+ /**
+ * Check if this node is *missing*.
+ *
+ * Missing nodes are inserted by the parser in order to recover from
+ * certain kinds of syntax errors.
+ */
+ get isMissing() {
+ marshalNode(this);
+ return C._ts_node_is_missing_wasm(this.tree[0]) === 1;
+ }
+ /** Check if this node has been edited. */
+ get hasChanges() {
+ marshalNode(this);
+ return C._ts_node_has_changes_wasm(this.tree[0]) === 1;
+ }
+ /**
+ * Check if this node represents a syntax error or contains any syntax
+ * errors anywhere within it.
+ */
+ get hasError() {
+ marshalNode(this);
+ return C._ts_node_has_error_wasm(this.tree[0]) === 1;
+ }
+ /** Get the byte index where this node ends. */
+ get endIndex() {
+ marshalNode(this);
+ return C._ts_node_end_index_wasm(this.tree[0]);
+ }
+ /** Get the position where this node ends. */
+ get endPosition() {
+ marshalNode(this);
+ C._ts_node_end_point_wasm(this.tree[0]);
+ return unmarshalPoint(TRANSFER_BUFFER);
+ }
+ /** Get the string content of this node. */
+ get text() {
+ return getText(this.tree, this.startIndex, this.endIndex, this.startPosition);
+ }
+ /** Get this node's parse state. */
+ get parseState() {
+ marshalNode(this);
+ return C._ts_node_parse_state_wasm(this.tree[0]);
+ }
+ /** Get the parse state after this node. */
+ get nextParseState() {
+ marshalNode(this);
+ return C._ts_node_next_parse_state_wasm(this.tree[0]);
+ }
+ /** Check if this node is equal to another node. */
+ equals(other) {
+ return this.tree === other.tree && this.id === other.id;
+ }
+ /**
+ * Get the node's child at the given index, where zero represents the first child.
+ *
+ * This method is fairly fast, but its cost is technically log(n), so if
+ * you might be iterating over a long list of children, you should use
+ * {@link Node#children} instead.
+ */
+ child(index) {
+ marshalNode(this);
+ C._ts_node_child_wasm(this.tree[0], index);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get this node's *named* child at the given index.
+ *
+ * See also {@link Node#isNamed}.
+ * This method is fairly fast, but its cost is technically log(n), so if
+ * you might be iterating over a long list of children, you should use
+ * {@link Node#namedChildren} instead.
+ */
+ namedChild(index) {
+ marshalNode(this);
+ C._ts_node_named_child_wasm(this.tree[0], index);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get this node's child with the given numerical field id.
+ *
+ * See also {@link Node#childForFieldName}. You can
+ * convert a field name to an id using {@link Language#fieldIdForName}.
+ */
+ childForFieldId(fieldId) {
+ marshalNode(this);
+ C._ts_node_child_by_field_id_wasm(this.tree[0], fieldId);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get the first child with the given field name.
+ *
+ * If multiple children may have the same field name, access them using
+ * {@link Node#childrenForFieldName}.
+ */
+ childForFieldName(fieldName) {
+ const fieldId = this.tree.language.fields.indexOf(fieldName);
+ if (fieldId !== -1) return this.childForFieldId(fieldId);
+ return null;
+ }
+ /** Get the field name of this node's child at the given index. */
+ fieldNameForChild(index) {
+ marshalNode(this);
+ const address = C._ts_node_field_name_for_child_wasm(this.tree[0], index);
+ if (!address) return null;
+ return C.AsciiToString(address);
+ }
+ /** Get the field name of this node's named child at the given index. */
+ fieldNameForNamedChild(index) {
+ marshalNode(this);
+ const address = C._ts_node_field_name_for_named_child_wasm(this.tree[0], index);
+ if (!address) return null;
+ return C.AsciiToString(address);
+ }
+ /**
+ * Get an array of this node's children with a given field name.
+ *
+ * See also {@link Node#children}.
+ */
+ childrenForFieldName(fieldName) {
+ const fieldId = this.tree.language.fields.indexOf(fieldName);
+ if (fieldId !== -1 && fieldId !== 0) return this.childrenForFieldId(fieldId);
+ return [];
+ }
+ /**
+ * Get an array of this node's children with a given field id.
+ *
+ * See also {@link Node#childrenForFieldName}.
+ */
+ childrenForFieldId(fieldId) {
+ marshalNode(this);
+ C._ts_node_children_by_field_id_wasm(this.tree[0], fieldId);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = unmarshalNode(this.tree, address);
+ address += SIZE_OF_NODE;
+ }
+ C._free(buffer);
+ }
+ return result;
+ }
+ /** Get the node's first child that contains or starts after the given byte offset. */
+ firstChildForIndex(index) {
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ C.setValue(address, index, "i32");
+ C._ts_node_first_child_for_byte_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the node's first named child that contains or starts after the given byte offset. */
+ firstNamedChildForIndex(index) {
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ C.setValue(address, index, "i32");
+ C._ts_node_first_named_child_for_byte_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get this node's number of children. */
+ get childCount() {
+ marshalNode(this);
+ return C._ts_node_child_count_wasm(this.tree[0]);
+ }
+ /**
+ * Get this node's number of *named* children.
+ *
+ * See also {@link Node#isNamed}.
+ */
+ get namedChildCount() {
+ marshalNode(this);
+ return C._ts_node_named_child_count_wasm(this.tree[0]);
+ }
+ /** Get this node's first child. */
+ get firstChild() {
+ return this.child(0);
+ }
+ /**
+ * Get this node's first named child.
+ *
+ * See also {@link Node#isNamed}.
+ */
+ get firstNamedChild() {
+ return this.namedChild(0);
+ }
+ /** Get this node's last child. */
+ get lastChild() {
+ return this.child(this.childCount - 1);
+ }
+ /**
+ * Get this node's last named child.
+ *
+ * See also {@link Node#isNamed}.
+ */
+ get lastNamedChild() {
+ return this.namedChild(this.namedChildCount - 1);
+ }
+ /**
+ * Iterate over this node's children.
+ *
+ * If you're walking the tree recursively, you may want to use the
+ * {@link TreeCursor} APIs directly instead.
+ */
+ get children() {
+ if (!this._children) {
+ marshalNode(this);
+ C._ts_node_children_wasm(this.tree[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ this._children = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ this._children[i2] = unmarshalNode(this.tree, address);
+ address += SIZE_OF_NODE;
+ }
+ C._free(buffer);
+ }
+ }
+ return this._children;
+ }
+ /**
+ * Iterate over this node's named children.
+ *
+ * See also {@link Node#children}.
+ */
+ get namedChildren() {
+ if (!this._namedChildren) {
+ marshalNode(this);
+ C._ts_node_named_children_wasm(this.tree[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ this._namedChildren = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ this._namedChildren[i2] = unmarshalNode(this.tree, address);
+ address += SIZE_OF_NODE;
+ }
+ C._free(buffer);
+ }
+ }
+ return this._namedChildren;
+ }
+ /**
+ * Get the descendants of this node that are the given type, or in the given types array.
+ *
+ * The types array should contain node type strings, which can be retrieved from {@link Language#types}.
+ *
+ * Additionally, a `startPosition` and `endPosition` can be passed in to restrict the search to a byte range.
+ */
+ descendantsOfType(types, startPosition = ZERO_POINT, endPosition = ZERO_POINT) {
+ if (!Array.isArray(types)) types = [types];
+ const symbols = [];
+ const typesBySymbol = this.tree.language.types;
+ for (const node_type of types) {
+ if (node_type == "ERROR") {
+ symbols.push(65535);
+ }
+ }
+ for (let i2 = 0, n = typesBySymbol.length; i2 < n; i2++) {
+ if (types.includes(typesBySymbol[i2])) {
+ symbols.push(i2);
+ }
+ }
+ const symbolsAddress = C._malloc(SIZE_OF_INT * symbols.length);
+ for (let i2 = 0, n = symbols.length; i2 < n; i2++) {
+ C.setValue(symbolsAddress + i2 * SIZE_OF_INT, symbols[i2], "i32");
+ }
+ marshalNode(this);
+ C._ts_node_descendants_of_type_wasm(
+ this.tree[0],
+ symbolsAddress,
+ symbols.length,
+ startPosition.row,
+ startPosition.column,
+ endPosition.row,
+ endPosition.column
+ );
+ const descendantCount = C.getValue(TRANSFER_BUFFER, "i32");
+ const descendantAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(descendantCount);
+ if (descendantCount > 0) {
+ let address = descendantAddress;
+ for (let i2 = 0; i2 < descendantCount; i2++) {
+ result[i2] = unmarshalNode(this.tree, address);
+ address += SIZE_OF_NODE;
+ }
+ }
+ C._free(descendantAddress);
+ C._free(symbolsAddress);
+ return result;
+ }
+ /** Get this node's next sibling. */
+ get nextSibling() {
+ marshalNode(this);
+ C._ts_node_next_sibling_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get this node's previous sibling. */
+ get previousSibling() {
+ marshalNode(this);
+ C._ts_node_prev_sibling_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get this node's next *named* sibling.
+ *
+ * See also {@link Node#isNamed}.
+ */
+ get nextNamedSibling() {
+ marshalNode(this);
+ C._ts_node_next_named_sibling_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get this node's previous *named* sibling.
+ *
+ * See also {@link Node#isNamed}.
+ */
+ get previousNamedSibling() {
+ marshalNode(this);
+ C._ts_node_prev_named_sibling_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the node's number of descendants, including one for the node itself. */
+ get descendantCount() {
+ marshalNode(this);
+ return C._ts_node_descendant_count_wasm(this.tree[0]);
+ }
+ /**
+ * Get this node's immediate parent.
+ * Prefer {@link Node#childWithDescendant} for iterating over this node's ancestors.
+ */
+ get parent() {
+ marshalNode(this);
+ C._ts_node_parent_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Get the node that contains `descendant`.
+ *
+ * Note that this can return `descendant` itself.
+ */
+ childWithDescendant(descendant) {
+ marshalNode(this);
+ marshalNode(descendant, 1);
+ C._ts_node_child_with_descendant_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the smallest node within this node that spans the given byte range. */
+ descendantForIndex(start2, end = start2) {
+ if (typeof start2 !== "number" || typeof end !== "number") {
+ throw new Error("Arguments must be numbers");
+ }
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ C.setValue(address, start2, "i32");
+ C.setValue(address + SIZE_OF_INT, end, "i32");
+ C._ts_node_descendant_for_index_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the smallest named node within this node that spans the given byte range. */
+ namedDescendantForIndex(start2, end = start2) {
+ if (typeof start2 !== "number" || typeof end !== "number") {
+ throw new Error("Arguments must be numbers");
+ }
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ C.setValue(address, start2, "i32");
+ C.setValue(address + SIZE_OF_INT, end, "i32");
+ C._ts_node_named_descendant_for_index_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the smallest node within this node that spans the given point range. */
+ descendantForPosition(start2, end = start2) {
+ if (!isPoint(start2) || !isPoint(end)) {
+ throw new Error("Arguments must be {row, column} objects");
+ }
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ marshalPoint(address, start2);
+ marshalPoint(address + SIZE_OF_POINT, end);
+ C._ts_node_descendant_for_position_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /** Get the smallest named node within this node that spans the given point range. */
+ namedDescendantForPosition(start2, end = start2) {
+ if (!isPoint(start2) || !isPoint(end)) {
+ throw new Error("Arguments must be {row, column} objects");
+ }
+ marshalNode(this);
+ const address = TRANSFER_BUFFER + SIZE_OF_NODE;
+ marshalPoint(address, start2);
+ marshalPoint(address + SIZE_OF_POINT, end);
+ C._ts_node_named_descendant_for_position_wasm(this.tree[0]);
+ return unmarshalNode(this.tree);
+ }
+ /**
+ * Create a new {@link TreeCursor} starting from this node.
+ *
+ * Note that the given node is considered the root of the cursor,
+ * and the cursor cannot walk outside this node.
+ */
+ walk() {
+ marshalNode(this);
+ C._ts_tree_cursor_new_wasm(this.tree[0]);
+ return new TreeCursor(INTERNAL, this.tree);
+ }
+ /**
+ * Edit this node to keep it in-sync with source code that has been edited.
+ *
+ * This function is only rarely needed. When you edit a syntax tree with
+ * the {@link Tree#edit} method, all of the nodes that you retrieve from
+ * the tree afterward will already reflect the edit. You only need to
+ * use {@link Node#edit} when you have a specific {@link Node} instance that
+ * you want to keep and continue to use after an edit.
+ */
+ edit(edit) {
+ if (this.startIndex >= edit.oldEndIndex) {
+ this.startIndex = edit.newEndIndex + (this.startIndex - edit.oldEndIndex);
+ let subbedPointRow;
+ let subbedPointColumn;
+ if (this.startPosition.row > edit.oldEndPosition.row) {
+ subbedPointRow = this.startPosition.row - edit.oldEndPosition.row;
+ subbedPointColumn = this.startPosition.column;
+ } else {
+ subbedPointRow = 0;
+ subbedPointColumn = this.startPosition.column;
+ if (this.startPosition.column >= edit.oldEndPosition.column) {
+ subbedPointColumn = this.startPosition.column - edit.oldEndPosition.column;
+ }
+ }
+ if (subbedPointRow > 0) {
+ this.startPosition.row += subbedPointRow;
+ this.startPosition.column = subbedPointColumn;
+ } else {
+ this.startPosition.column += subbedPointColumn;
+ }
+ } else if (this.startIndex > edit.startIndex) {
+ this.startIndex = edit.newEndIndex;
+ this.startPosition.row = edit.newEndPosition.row;
+ this.startPosition.column = edit.newEndPosition.column;
+ }
+ }
+ /** Get the S-expression representation of this node. */
+ toString() {
+ marshalNode(this);
+ const address = C._ts_node_to_string_wasm(this.tree[0]);
+ const result = C.AsciiToString(address);
+ C._free(address);
+ return result;
+ }
+};
+
+// src/marshal.ts
+function unmarshalCaptures(query, tree, address, patternIndex, result) {
+ for (let i2 = 0, n = result.length; i2 < n; i2++) {
+ const captureIndex = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const node = unmarshalNode(tree, address);
+ address += SIZE_OF_NODE;
+ result[i2] = { patternIndex, name: query.captureNames[captureIndex], node };
+ }
+ return address;
+}
+__name(unmarshalCaptures, "unmarshalCaptures");
+function marshalNode(node, index = 0) {
+ let address = TRANSFER_BUFFER + index * SIZE_OF_NODE;
+ C.setValue(address, node.id, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, node.startIndex, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, node.startPosition.row, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, node.startPosition.column, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, node[0], "i32");
+}
+__name(marshalNode, "marshalNode");
+function unmarshalNode(tree, address = TRANSFER_BUFFER) {
+ const id = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ if (id === 0) return null;
+ const index = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const row = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const column = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const other = C.getValue(address, "i32");
+ const result = new Node(INTERNAL, {
+ id,
+ tree,
+ startIndex: index,
+ startPosition: { row, column },
+ other
+ });
+ return result;
+}
+__name(unmarshalNode, "unmarshalNode");
+function marshalTreeCursor(cursor, address = TRANSFER_BUFFER) {
+ C.setValue(address + 0 * SIZE_OF_INT, cursor[0], "i32");
+ C.setValue(address + 1 * SIZE_OF_INT, cursor[1], "i32");
+ C.setValue(address + 2 * SIZE_OF_INT, cursor[2], "i32");
+ C.setValue(address + 3 * SIZE_OF_INT, cursor[3], "i32");
+}
+__name(marshalTreeCursor, "marshalTreeCursor");
+function unmarshalTreeCursor(cursor) {
+ cursor[0] = C.getValue(TRANSFER_BUFFER + 0 * SIZE_OF_INT, "i32");
+ cursor[1] = C.getValue(TRANSFER_BUFFER + 1 * SIZE_OF_INT, "i32");
+ cursor[2] = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, "i32");
+ cursor[3] = C.getValue(TRANSFER_BUFFER + 3 * SIZE_OF_INT, "i32");
+}
+__name(unmarshalTreeCursor, "unmarshalTreeCursor");
+function marshalPoint(address, point) {
+ C.setValue(address, point.row, "i32");
+ C.setValue(address + SIZE_OF_INT, point.column, "i32");
+}
+__name(marshalPoint, "marshalPoint");
+function unmarshalPoint(address) {
+ const result = {
+ row: C.getValue(address, "i32") >>> 0,
+ column: C.getValue(address + SIZE_OF_INT, "i32") >>> 0
+ };
+ return result;
+}
+__name(unmarshalPoint, "unmarshalPoint");
+function marshalRange(address, range) {
+ marshalPoint(address, range.startPosition);
+ address += SIZE_OF_POINT;
+ marshalPoint(address, range.endPosition);
+ address += SIZE_OF_POINT;
+ C.setValue(address, range.startIndex, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, range.endIndex, "i32");
+ address += SIZE_OF_INT;
+}
+__name(marshalRange, "marshalRange");
+function unmarshalRange(address) {
+ const result = {};
+ result.startPosition = unmarshalPoint(address);
+ address += SIZE_OF_POINT;
+ result.endPosition = unmarshalPoint(address);
+ address += SIZE_OF_POINT;
+ result.startIndex = C.getValue(address, "i32") >>> 0;
+ address += SIZE_OF_INT;
+ result.endIndex = C.getValue(address, "i32") >>> 0;
+ return result;
+}
+__name(unmarshalRange, "unmarshalRange");
+function marshalEdit(edit, address = TRANSFER_BUFFER) {
+ marshalPoint(address, edit.startPosition);
+ address += SIZE_OF_POINT;
+ marshalPoint(address, edit.oldEndPosition);
+ address += SIZE_OF_POINT;
+ marshalPoint(address, edit.newEndPosition);
+ address += SIZE_OF_POINT;
+ C.setValue(address, edit.startIndex, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, edit.oldEndIndex, "i32");
+ address += SIZE_OF_INT;
+ C.setValue(address, edit.newEndIndex, "i32");
+ address += SIZE_OF_INT;
+}
+__name(marshalEdit, "marshalEdit");
+function unmarshalLanguageMetadata(address) {
+ const major_version = C.getValue(address, "i32");
+ const minor_version = C.getValue(address += SIZE_OF_INT, "i32");
+ const patch_version = C.getValue(address += SIZE_OF_INT, "i32");
+ return { major_version, minor_version, patch_version };
+}
+__name(unmarshalLanguageMetadata, "unmarshalLanguageMetadata");
+
+// src/language.ts
+var LANGUAGE_FUNCTION_REGEX = /^tree_sitter_\w+$/;
+var Language = class _Language {
+ static {
+ __name(this, "Language");
+ }
+ /** @internal */
+ [0] = 0;
+ // Internal handle for Wasm
+ /**
+ * A list of all node types in the language. The index of each type in this
+ * array is its node type id.
+ */
+ types;
+ /**
+ * A list of all field names in the language. The index of each field name in
+ * this array is its field id.
+ */
+ fields;
+ /** @internal */
+ constructor(internal, address) {
+ assertInternal(internal);
+ this[0] = address;
+ this.types = new Array(C._ts_language_symbol_count(this[0]));
+ for (let i2 = 0, n = this.types.length; i2 < n; i2++) {
+ if (C._ts_language_symbol_type(this[0], i2) < 2) {
+ this.types[i2] = C.UTF8ToString(C._ts_language_symbol_name(this[0], i2));
+ }
+ }
+ this.fields = new Array(C._ts_language_field_count(this[0]) + 1);
+ for (let i2 = 0, n = this.fields.length; i2 < n; i2++) {
+ const fieldName = C._ts_language_field_name_for_id(this[0], i2);
+ if (fieldName !== 0) {
+ this.fields[i2] = C.UTF8ToString(fieldName);
+ } else {
+ this.fields[i2] = null;
+ }
+ }
+ }
+ /**
+ * Gets the name of the language.
+ */
+ get name() {
+ const ptr = C._ts_language_name(this[0]);
+ if (ptr === 0) return null;
+ return C.UTF8ToString(ptr);
+ }
+ /**
+ * Gets the ABI version of the language.
+ */
+ get abiVersion() {
+ return C._ts_language_abi_version(this[0]);
+ }
+ /**
+ * Get the metadata for this language. This information is generated by the
+ * CLI, and relies on the language author providing the correct metadata in
+ * the language's `tree-sitter.json` file.
+ */
+ get metadata() {
+ C._ts_language_metadata_wasm(this[0]);
+ const length = C.getValue(TRANSFER_BUFFER, "i32");
+ if (length === 0) return null;
+ return unmarshalLanguageMetadata(TRANSFER_BUFFER + SIZE_OF_INT);
+ }
+ /**
+ * Gets the number of fields in the language.
+ */
+ get fieldCount() {
+ return this.fields.length - 1;
+ }
+ /**
+ * Gets the number of states in the language.
+ */
+ get stateCount() {
+ return C._ts_language_state_count(this[0]);
+ }
+ /**
+ * Get the field id for a field name.
+ */
+ fieldIdForName(fieldName) {
+ const result = this.fields.indexOf(fieldName);
+ return result !== -1 ? result : null;
+ }
+ /**
+ * Get the field name for a field id.
+ */
+ fieldNameForId(fieldId) {
+ return this.fields[fieldId] ?? null;
+ }
+ /**
+ * Get the node type id for a node type name.
+ */
+ idForNodeType(type, named) {
+ const typeLength = C.lengthBytesUTF8(type);
+ const typeAddress = C._malloc(typeLength + 1);
+ C.stringToUTF8(type, typeAddress, typeLength + 1);
+ const result = C._ts_language_symbol_for_name(this[0], typeAddress, typeLength, named ? 1 : 0);
+ C._free(typeAddress);
+ return result || null;
+ }
+ /**
+ * Gets the number of node types in the language.
+ */
+ get nodeTypeCount() {
+ return C._ts_language_symbol_count(this[0]);
+ }
+ /**
+ * Get the node type name for a node type id.
+ */
+ nodeTypeForId(typeId) {
+ const name2 = C._ts_language_symbol_name(this[0], typeId);
+ return name2 ? C.UTF8ToString(name2) : null;
+ }
+ /**
+ * Check if a node type is named.
+ *
+ * @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/2-basic-parsing.html#named-vs-anonymous-nodes}
+ */
+ nodeTypeIsNamed(typeId) {
+ return C._ts_language_type_is_named_wasm(this[0], typeId) ? true : false;
+ }
+ /**
+ * Check if a node type is visible.
+ */
+ nodeTypeIsVisible(typeId) {
+ return C._ts_language_type_is_visible_wasm(this[0], typeId) ? true : false;
+ }
+ /**
+ * Get the supertypes ids of this language.
+ *
+ * @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types.html?highlight=supertype#supertype-nodes}
+ */
+ get supertypes() {
+ C._ts_language_supertypes_wasm(this[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = C.getValue(address, "i16");
+ address += SIZE_OF_SHORT;
+ }
+ }
+ return result;
+ }
+ /**
+ * Get the subtype ids for a given supertype node id.
+ */
+ subtypes(supertype) {
+ C._ts_language_subtypes_wasm(this[0], supertype);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = C.getValue(address, "i16");
+ address += SIZE_OF_SHORT;
+ }
+ }
+ return result;
+ }
+ /**
+ * Get the next state id for a given state id and node type id.
+ */
+ nextState(stateId, typeId) {
+ return C._ts_language_next_state(this[0], stateId, typeId);
+ }
+ /**
+ * Create a new lookahead iterator for this language and parse state.
+ *
+ * This returns `null` if state is invalid for this language.
+ *
+ * Iterating {@link LookaheadIterator} will yield valid symbols in the given
+ * parse state. Newly created lookahead iterators will return the `ERROR`
+ * symbol from {@link LookaheadIterator#currentType}.
+ *
+ * Lookahead iterators can be useful for generating suggestions and improving
+ * syntax error diagnostics. To get symbols valid in an `ERROR` node, use the
+ * lookahead iterator on its first leaf node state. For `MISSING` nodes, a
+ * lookahead iterator created on the previous non-extra leaf node may be
+ * appropriate.
+ */
+ lookaheadIterator(stateId) {
+ const address = C._ts_lookahead_iterator_new(this[0], stateId);
+ if (address) return new LookaheadIterator(INTERNAL, address, this);
+ return null;
+ }
+ /**
+ * Load a language from a WebAssembly module.
+ * The module can be provided as a path to a file or as a buffer.
+ */
+ static async load(input) {
+ let binary2;
+ if (input instanceof Uint8Array) {
+ binary2 = input;
+ } else if (globalThis.process?.versions.node) {
+ const fs2 = await import("fs/promises");
+ binary2 = await fs2.readFile(input);
+ } else {
+ const response = await fetch(input);
+ if (!response.ok) {
+ const body2 = await response.text();
+ throw new Error(`Language.load failed with status ${response.status}.
+
+${body2}`);
+ }
+ const retryResp = response.clone();
+ try {
+ binary2 = await WebAssembly.compileStreaming(response);
+ } catch (reason) {
+ console.error("wasm streaming compile failed:", reason);
+ console.error("falling back to ArrayBuffer instantiation");
+ binary2 = new Uint8Array(await retryResp.arrayBuffer());
+ }
+ }
+ const mod = await C.loadWebAssemblyModule(binary2, { loadAsync: true });
+ const symbolNames = Object.keys(mod);
+ const functionName = symbolNames.find((key) => LANGUAGE_FUNCTION_REGEX.test(key) && !key.includes("external_scanner_"));
+ if (!functionName) {
+ console.log(`Couldn't find language function in Wasm file. Symbols:
+${JSON.stringify(symbolNames, null, 2)}`);
+ throw new Error("Language.load failed: no language function found in Wasm file");
+ }
+ const languageAddress = mod[functionName]();
+ return new _Language(INTERNAL, languageAddress);
+ }
+};
+
+// lib/web-tree-sitter.mjs
+async function Module2(moduleArg = {}) {
+ var moduleRtn;
+ var Module = moduleArg;
+ // var ENVIRONMENT_IS_WEB = typeof window == "object";
+ // var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined";
+ // var ENVIRONMENT_IS_NODE = typeof process == "object" && process.versions?.node && process.type != "renderer";
+ var ENVIRONMENT_IS_WEB = false;
+ var ENVIRONMENT_IS_WORKER = true;
+ var ENVIRONMENT_IS_NODE = false;
+ // if (ENVIRONMENT_IS_NODE) {
+ // const { createRequire } = await import("module");
+ // var require = createRequire(import.meta.url);
+ // }
+ Module.currentQueryProgressCallback = null;
+ Module.currentProgressCallback = null;
+ Module.currentLogCallback = null;
+ Module.currentParseCallback = null;
+ var arguments_ = [];
+ var thisProgram = "./this.program";
+ var quit_ = /* @__PURE__ */ __name((status, toThrow) => {
+ throw toThrow;
+ }, "quit_");
+ var _scriptName = self.location.origin + self.location.pathname;
+ var scriptDirectory = "";
+ function locateFile(path) {
+ if (Module["locateFile"]) {
+ return Module["locateFile"](path, scriptDirectory);
+ }
+ return scriptDirectory + path;
+ }
+ __name(locateFile, "locateFile");
+ var readAsync, readBinary;
+ if (ENVIRONMENT_IS_NODE) {
+ var fs = require("fs");
+ if (_scriptName.startsWith("file:")) {
+ scriptDirectory = require("path").dirname(require("url").fileURLToPath(_scriptName)) + "/";
+ }
+ readBinary = /* @__PURE__ */ __name((filename) => {
+ filename = isFileURI(filename) ? new URL(filename) : filename;
+ var ret = fs.readFileSync(filename);
+ return ret;
+ }, "readBinary");
+ readAsync = /* @__PURE__ */ __name(async (filename, binary2 = true) => {
+ filename = isFileURI(filename) ? new URL(filename) : filename;
+ var ret = fs.readFileSync(filename, binary2 ? void 0 : "utf8");
+ return ret;
+ }, "readAsync");
+ if (process.argv.length > 1) {
+ thisProgram = process.argv[1].replace(/\\/g, "/");
+ }
+ arguments_ = process.argv.slice(2);
+ quit_ = /* @__PURE__ */ __name((status, toThrow) => {
+ process.exitCode = status;
+ throw toThrow;
+ }, "quit_");
+ } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
+ try {
+ scriptDirectory = new URL(".", _scriptName).href;
+ } catch {
+ }
+ {
+ if (ENVIRONMENT_IS_WORKER) {
+ readBinary = /* @__PURE__ */ __name((url) => {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, false);
+ xhr.responseType = "arraybuffer";
+ xhr.send(null);
+ return new Uint8Array(
+ /** @type{!ArrayBuffer} */
+ xhr.response
+ );
+ }, "readBinary");
+ }
+ readAsync = /* @__PURE__ */ __name(async (url) => {
+ if (isFileURI(url)) {
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, true);
+ xhr.responseType = "arraybuffer";
+ xhr.onload = () => {
+ if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
+ resolve(xhr.response);
+ return;
+ }
+ reject(xhr.status);
+ };
+ xhr.onerror = reject;
+ xhr.send(null);
+ });
+ }
+ var response = await fetch(url, {
+ credentials: "same-origin"
+ });
+ if (response.ok) {
+ return response.arrayBuffer();
+ }
+ throw new Error(response.status + " : " + response.url);
+ }, "readAsync");
+ }
+ } else {
+ }
+ var out = console.log.bind(console);
+ var err = console.error.bind(console);
+ var dynamicLibraries = [];
+ var wasmBinary;
+ var ABORT = false;
+ var EXITSTATUS;
+ var isFileURI = /* @__PURE__ */ __name((filename) => filename.startsWith("file://"), "isFileURI");
+ var readyPromiseResolve, readyPromiseReject;
+ var wasmMemory;
+ var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
+ var HEAP64, HEAPU64;
+ var HEAP_DATA_VIEW;
+ var runtimeInitialized = false;
+ function updateMemoryViews() {
+ var b = wasmMemory.buffer;
+ Module["HEAP8"] = HEAP8 = new Int8Array(b);
+ Module["HEAP16"] = HEAP16 = new Int16Array(b);
+ Module["HEAPU8"] = HEAPU8 = new Uint8Array(b);
+ Module["HEAPU16"] = HEAPU16 = new Uint16Array(b);
+ Module["HEAP32"] = HEAP32 = new Int32Array(b);
+ Module["HEAPU32"] = HEAPU32 = new Uint32Array(b);
+ Module["HEAPF32"] = HEAPF32 = new Float32Array(b);
+ Module["HEAPF64"] = HEAPF64 = new Float64Array(b);
+ Module["HEAP64"] = HEAP64 = new BigInt64Array(b);
+ Module["HEAPU64"] = HEAPU64 = new BigUint64Array(b);
+ Module["HEAP_DATA_VIEW"] = HEAP_DATA_VIEW = new DataView(b);
+ LE_HEAP_UPDATE();
+ }
+ __name(updateMemoryViews, "updateMemoryViews");
+ function initMemory() {
+ if (Module["wasmMemory"]) {
+ wasmMemory = Module["wasmMemory"];
+ } else {
+ var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 33554432;
+ wasmMemory = new WebAssembly.Memory({
+ "initial": INITIAL_MEMORY / 65536,
+ // In theory we should not need to emit the maximum if we want "unlimited"
+ // or 4GB of memory, but VMs error on that atm, see
+ // https://github.com/emscripten-core/emscripten/issues/14130
+ // And in the pthreads case we definitely need to emit a maximum. So
+ // always emit one.
+ "maximum": 32768
+ });
+ }
+ updateMemoryViews();
+ }
+ __name(initMemory, "initMemory");
+ var __RELOC_FUNCS__ = [];
+ function preRun() {
+ if (Module["preRun"]) {
+ if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]];
+ while (Module["preRun"].length) {
+ addOnPreRun(Module["preRun"].shift());
+ }
+ }
+ callRuntimeCallbacks(onPreRuns);
+ }
+ __name(preRun, "preRun");
+ function initRuntime() {
+ runtimeInitialized = true;
+ callRuntimeCallbacks(__RELOC_FUNCS__);
+ wasmExports["__wasm_call_ctors"]();
+ callRuntimeCallbacks(onPostCtors);
+ }
+ __name(initRuntime, "initRuntime");
+ function preMain() {
+ }
+ __name(preMain, "preMain");
+ function postRun() {
+ if (Module["postRun"]) {
+ if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]];
+ while (Module["postRun"].length) {
+ addOnPostRun(Module["postRun"].shift());
+ }
+ }
+ callRuntimeCallbacks(onPostRuns);
+ }
+ __name(postRun, "postRun");
+ function abort(what) {
+ Module["onAbort"]?.(what);
+ what = "Aborted(" + what + ")";
+ err(what);
+ ABORT = true;
+ what += ". Build with -sASSERTIONS for more info.";
+ var e = new WebAssembly.RuntimeError(what);
+ readyPromiseReject?.(e);
+ throw e;
+ }
+ __name(abort, "abort");
+ var wasmBinaryFile;
+ function findWasmBinary() {
+ if (Module["locateFile"]) {
+ return locateFile("web-tree-sitter.wasm");
+ }
+ return new URL("web-tree-sitter.wasm", self.location.origin + self.location.pathname).href;
+ }
+ __name(findWasmBinary, "findWasmBinary");
+ function getBinarySync(file) {
+ if (file == wasmBinaryFile && wasmBinary) {
+ return new Uint8Array(wasmBinary);
+ }
+ if (readBinary) {
+ return readBinary(file);
+ }
+ throw "both async and sync fetching of the wasm failed";
+ }
+ __name(getBinarySync, "getBinarySync");
+ async function getWasmBinary(binaryFile) {
+ if (!wasmBinary) {
+ try {
+ var response = await readAsync(binaryFile);
+ return new Uint8Array(response);
+ } catch {
+ }
+ }
+ return getBinarySync(binaryFile);
+ }
+ __name(getWasmBinary, "getWasmBinary");
+ async function instantiateArrayBuffer(binaryFile, imports) {
+ try {
+ var binary2 = await getWasmBinary(binaryFile);
+ var instance2 = await WebAssembly.instantiate(binary2, imports);
+ return instance2;
+ } catch (reason) {
+ err(`failed to asynchronously prepare wasm: ${reason}`);
+ abort(reason);
+ }
+ }
+ __name(instantiateArrayBuffer, "instantiateArrayBuffer");
+ async function instantiateAsync(binary2, binaryFile, imports) {
+ if (!binary2 && !isFileURI(binaryFile) && !ENVIRONMENT_IS_NODE) {
+ try {
+ var response = fetch(binaryFile, {
+ credentials: "same-origin"
+ });
+ var instantiationResult = await WebAssembly.instantiateStreaming(response, imports);
+ return instantiationResult;
+ } catch (reason) {
+ err(`wasm streaming compile failed: ${reason}`);
+ err("falling back to ArrayBuffer instantiation");
+ }
+ }
+ return instantiateArrayBuffer(binaryFile, imports);
+ }
+ __name(instantiateAsync, "instantiateAsync");
+ function getWasmImports() {
+ return {
+ "env": wasmImports,
+ "wasi_snapshot_preview1": wasmImports,
+ "GOT.mem": new Proxy(wasmImports, GOTHandler),
+ "GOT.func": new Proxy(wasmImports, GOTHandler)
+ };
+ }
+ __name(getWasmImports, "getWasmImports");
+ async function createWasm() {
+ function receiveInstance(instance2, module2) {
+ wasmExports = instance2.exports;
+ wasmExports = relocateExports(wasmExports, 1024);
+ var metadata2 = getDylinkMetadata(module2);
+ if (metadata2.neededDynlibs) {
+ dynamicLibraries = metadata2.neededDynlibs.concat(dynamicLibraries);
+ }
+ mergeLibSymbols(wasmExports, "main");
+ LDSO.init();
+ loadDylibs();
+ __RELOC_FUNCS__.push(wasmExports["__wasm_apply_data_relocs"]);
+ assignWasmExports(wasmExports);
+ return wasmExports;
+ }
+ __name(receiveInstance, "receiveInstance");
+ function receiveInstantiationResult(result2) {
+ return receiveInstance(result2["instance"], result2["module"]);
+ }
+ __name(receiveInstantiationResult, "receiveInstantiationResult");
+ var info2 = getWasmImports();
+ if (Module["instantiateWasm"]) {
+ return new Promise((resolve, reject) => {
+ Module["instantiateWasm"](info2, (mod, inst) => {
+ resolve(receiveInstance(mod, inst));
+ });
+ });
+ }
+ wasmBinaryFile ??= findWasmBinary();
+ var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info2);
+ var exports = receiveInstantiationResult(result);
+ return exports;
+ }
+ __name(createWasm, "createWasm");
+ class ExitStatus {
+ static {
+ __name(this, "ExitStatus");
+ }
+ name = "ExitStatus";
+ constructor(status) {
+ this.message = `Program terminated with exit(${status})`;
+ this.status = status;
+ }
+ }
+ var GOT = {};
+ var currentModuleWeakSymbols = /* @__PURE__ */ new Set([]);
+ var GOTHandler = {
+ get(obj, symName) {
+ var rtn = GOT[symName];
+ if (!rtn) {
+ rtn = GOT[symName] = new WebAssembly.Global({
+ "value": "i32",
+ "mutable": true
+ });
+ }
+ if (!currentModuleWeakSymbols.has(symName)) {
+ rtn.required = true;
+ }
+ return rtn;
+ }
+ };
+ var LE_ATOMICS_NATIVE_BYTE_ORDER = [];
+ var LE_HEAP_LOAD_F32 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getFloat32(byteOffset, true), "LE_HEAP_LOAD_F32");
+ var LE_HEAP_LOAD_F64 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getFloat64(byteOffset, true), "LE_HEAP_LOAD_F64");
+ var LE_HEAP_LOAD_I16 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getInt16(byteOffset, true), "LE_HEAP_LOAD_I16");
+ var LE_HEAP_LOAD_I32 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getInt32(byteOffset, true), "LE_HEAP_LOAD_I32");
+ var LE_HEAP_LOAD_I64 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getBigInt64(byteOffset, true), "LE_HEAP_LOAD_I64");
+ var LE_HEAP_LOAD_U32 = /* @__PURE__ */ __name((byteOffset) => HEAP_DATA_VIEW.getUint32(byteOffset, true), "LE_HEAP_LOAD_U32");
+ var LE_HEAP_STORE_F32 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setFloat32(byteOffset, value, true), "LE_HEAP_STORE_F32");
+ var LE_HEAP_STORE_F64 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setFloat64(byteOffset, value, true), "LE_HEAP_STORE_F64");
+ var LE_HEAP_STORE_I16 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setInt16(byteOffset, value, true), "LE_HEAP_STORE_I16");
+ var LE_HEAP_STORE_I32 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setInt32(byteOffset, value, true), "LE_HEAP_STORE_I32");
+ var LE_HEAP_STORE_I64 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setBigInt64(byteOffset, value, true), "LE_HEAP_STORE_I64");
+ var LE_HEAP_STORE_U32 = /* @__PURE__ */ __name((byteOffset, value) => HEAP_DATA_VIEW.setUint32(byteOffset, value, true), "LE_HEAP_STORE_U32");
+ var callRuntimeCallbacks = /* @__PURE__ */ __name((callbacks) => {
+ while (callbacks.length > 0) {
+ callbacks.shift()(Module);
+ }
+ }, "callRuntimeCallbacks");
+ var onPostRuns = [];
+ var addOnPostRun = /* @__PURE__ */ __name((cb) => onPostRuns.push(cb), "addOnPostRun");
+ var onPreRuns = [];
+ var addOnPreRun = /* @__PURE__ */ __name((cb) => onPreRuns.push(cb), "addOnPreRun");
+ var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder() : void 0;
+ var findStringEnd = /* @__PURE__ */ __name((heapOrArray, idx, maxBytesToRead, ignoreNul) => {
+ var maxIdx = idx + maxBytesToRead;
+ if (ignoreNul) return maxIdx;
+ while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx;
+ return idx;
+ }, "findStringEnd");
+ var UTF8ArrayToString = /* @__PURE__ */ __name((heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => {
+ var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul);
+ if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
+ return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
+ }
+ var str = "";
+ while (idx < endPtr) {
+ var u0 = heapOrArray[idx++];
+ if (!(u0 & 128)) {
+ str += String.fromCharCode(u0);
+ continue;
+ }
+ var u1 = heapOrArray[idx++] & 63;
+ if ((u0 & 224) == 192) {
+ str += String.fromCharCode((u0 & 31) << 6 | u1);
+ continue;
+ }
+ var u2 = heapOrArray[idx++] & 63;
+ if ((u0 & 240) == 224) {
+ u0 = (u0 & 15) << 12 | u1 << 6 | u2;
+ } else {
+ u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63;
+ }
+ if (u0 < 65536) {
+ str += String.fromCharCode(u0);
+ } else {
+ var ch = u0 - 65536;
+ str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
+ }
+ }
+ return str;
+ }, "UTF8ArrayToString");
+ var getDylinkMetadata = /* @__PURE__ */ __name((binary2) => {
+ var offset = 0;
+ var end = 0;
+ function getU8() {
+ return binary2[offset++];
+ }
+ __name(getU8, "getU8");
+ function getLEB() {
+ var ret = 0;
+ var mul = 1;
+ while (1) {
+ var byte = binary2[offset++];
+ ret += (byte & 127) * mul;
+ mul *= 128;
+ if (!(byte & 128)) break;
+ }
+ return ret;
+ }
+ __name(getLEB, "getLEB");
+ function getString() {
+ var len = getLEB();
+ offset += len;
+ return UTF8ArrayToString(binary2, offset - len, len);
+ }
+ __name(getString, "getString");
+ function getStringList() {
+ var count2 = getLEB();
+ var rtn = [];
+ while (count2--) rtn.push(getString());
+ return rtn;
+ }
+ __name(getStringList, "getStringList");
+ function failIf(condition, message) {
+ if (condition) throw new Error(message);
+ }
+ __name(failIf, "failIf");
+ if (binary2 instanceof WebAssembly.Module) {
+ var dylinkSection = WebAssembly.Module.customSections(binary2, "dylink.0");
+ failIf(dylinkSection.length === 0, "need dylink section");
+ binary2 = new Uint8Array(dylinkSection[0]);
+ end = binary2.length;
+ } else {
+ var int32View = new Uint32Array(new Uint8Array(binary2.subarray(0, 24)).buffer);
+ var magicNumberFound = int32View[0] == 1836278016 || int32View[0] == 6386541;
+ failIf(!magicNumberFound, "need to see wasm magic number");
+ failIf(binary2[8] !== 0, "need the dylink section to be first");
+ offset = 9;
+ var section_size = getLEB();
+ end = offset + section_size;
+ var name2 = getString();
+ failIf(name2 !== "dylink.0");
+ }
+ var customSection = {
+ neededDynlibs: [],
+ tlsExports: /* @__PURE__ */ new Set(),
+ weakImports: /* @__PURE__ */ new Set(),
+ runtimePaths: []
+ };
+ var WASM_DYLINK_MEM_INFO = 1;
+ var WASM_DYLINK_NEEDED = 2;
+ var WASM_DYLINK_EXPORT_INFO = 3;
+ var WASM_DYLINK_IMPORT_INFO = 4;
+ var WASM_DYLINK_RUNTIME_PATH = 5;
+ var WASM_SYMBOL_TLS = 256;
+ var WASM_SYMBOL_BINDING_MASK = 3;
+ var WASM_SYMBOL_BINDING_WEAK = 1;
+ while (offset < end) {
+ var subsectionType = getU8();
+ var subsectionSize = getLEB();
+ if (subsectionType === WASM_DYLINK_MEM_INFO) {
+ customSection.memorySize = getLEB();
+ customSection.memoryAlign = getLEB();
+ customSection.tableSize = getLEB();
+ customSection.tableAlign = getLEB();
+ } else if (subsectionType === WASM_DYLINK_NEEDED) {
+ customSection.neededDynlibs = getStringList();
+ } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) {
+ var count = getLEB();
+ while (count--) {
+ var symname = getString();
+ var flags2 = getLEB();
+ if (flags2 & WASM_SYMBOL_TLS) {
+ customSection.tlsExports.add(symname);
+ }
+ }
+ } else if (subsectionType === WASM_DYLINK_IMPORT_INFO) {
+ var count = getLEB();
+ while (count--) {
+ var modname = getString();
+ var symname = getString();
+ var flags2 = getLEB();
+ if ((flags2 & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
+ customSection.weakImports.add(symname);
+ }
+ }
+ } else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) {
+ customSection.runtimePaths = getStringList();
+ } else {
+ offset += subsectionSize;
+ }
+ }
+ return customSection;
+ }, "getDylinkMetadata");
+ function getValue(ptr, type = "i8") {
+ if (type.endsWith("*")) type = "*";
+ switch (type) {
+ case "i1":
+ return HEAP8[ptr];
+ case "i8":
+ return HEAP8[ptr];
+ case "i16":
+ return LE_HEAP_LOAD_I16((ptr >> 1) * 2);
+ case "i32":
+ return LE_HEAP_LOAD_I32((ptr >> 2) * 4);
+ case "i64":
+ return LE_HEAP_LOAD_I64((ptr >> 3) * 8);
+ case "float":
+ return LE_HEAP_LOAD_F32((ptr >> 2) * 4);
+ case "double":
+ return LE_HEAP_LOAD_F64((ptr >> 3) * 8);
+ case "*":
+ return LE_HEAP_LOAD_U32((ptr >> 2) * 4);
+ default:
+ abort(`invalid type for getValue: ${type}`);
+ }
+ }
+ __name(getValue, "getValue");
+ var newDSO = /* @__PURE__ */ __name((name2, handle2, syms) => {
+ var dso = {
+ refcount: Infinity,
+ name: name2,
+ exports: syms,
+ global: true
+ };
+ LDSO.loadedLibsByName[name2] = dso;
+ if (handle2 != void 0) {
+ LDSO.loadedLibsByHandle[handle2] = dso;
+ }
+ return dso;
+ }, "newDSO");
+ var LDSO = {
+ loadedLibsByName: {},
+ loadedLibsByHandle: {},
+ init() {
+ newDSO("__main__", 0, wasmImports);
+ }
+ };
+ var ___heap_base = 78240;
+ var alignMemory = /* @__PURE__ */ __name((size, alignment) => Math.ceil(size / alignment) * alignment, "alignMemory");
+ var getMemory = /* @__PURE__ */ __name((size) => {
+ if (runtimeInitialized) {
+ return _calloc(size, 1);
+ }
+ var ret = ___heap_base;
+ var end = ret + alignMemory(size, 16);
+ ___heap_base = end;
+ GOT["__heap_base"].value = end;
+ return ret;
+ }, "getMemory");
+ var isInternalSym = /* @__PURE__ */ __name((symName) => ["__cpp_exception", "__c_longjmp", "__wasm_apply_data_relocs", "__dso_handle", "__tls_size", "__tls_align", "__set_stack_limits", "_emscripten_tls_init", "__wasm_init_tls", "__wasm_call_ctors", "__start_em_asm", "__stop_em_asm", "__start_em_js", "__stop_em_js"].includes(symName) || symName.startsWith("__em_js__"), "isInternalSym");
+ var uleb128EncodeWithLen = /* @__PURE__ */ __name((arr) => {
+ const n = arr.length;
+ return [n % 128 | 128, n >> 7, ...arr];
+ }, "uleb128EncodeWithLen");
+ var wasmTypeCodes = {
+ "i": 127,
+ // i32
+ "p": 127,
+ // i32
+ "j": 126,
+ // i64
+ "f": 125,
+ // f32
+ "d": 124,
+ // f64
+ "e": 111
+ };
+ var generateTypePack = /* @__PURE__ */ __name((types) => uleb128EncodeWithLen(Array.from(types, (type) => {
+ var code = wasmTypeCodes[type];
+ return code;
+ })), "generateTypePack");
+ var convertJsFunctionToWasm = /* @__PURE__ */ __name((func2, sig) => {
+ var bytes = Uint8Array.of(
+ 0,
+ 97,
+ 115,
+ 109,
+ // magic ("\0asm")
+ 1,
+ 0,
+ 0,
+ 0,
+ // version: 1
+ 1,
+ ...uleb128EncodeWithLen([
+ 1,
+ // count: 1
+ 96,
+ // param types
+ ...generateTypePack(sig.slice(1)),
+ // return types (for now only supporting [] if `void` and single [T] otherwise)
+ ...generateTypePack(sig[0] === "v" ? "" : sig[0])
+ ]),
+ // The rest of the module is static
+ 2,
+ 7,
+ // import section
+ // (import "e" "f" (func 0 (type 0)))
+ 1,
+ 1,
+ 101,
+ 1,
+ 102,
+ 0,
+ 0,
+ 7,
+ 5,
+ // export section
+ // (export "f" (func 0 (type 0)))
+ 1,
+ 1,
+ 102,
+ 0,
+ 0
+ );
+ var module2 = new WebAssembly.Module(bytes);
+ var instance2 = new WebAssembly.Instance(module2, {
+ "e": {
+ "f": func2
+ }
+ });
+ var wrappedFunc = instance2.exports["f"];
+ return wrappedFunc;
+ }, "convertJsFunctionToWasm");
+ var wasmTableMirror = [];
+ var wasmTable = new WebAssembly.Table({
+ "initial": 31,
+ "element": "anyfunc"
+ });
+ var getWasmTableEntry = /* @__PURE__ */ __name((funcPtr) => {
+ var func2 = wasmTableMirror[funcPtr];
+ if (!func2) {
+ wasmTableMirror[funcPtr] = func2 = wasmTable.get(funcPtr);
+ }
+ return func2;
+ }, "getWasmTableEntry");
+ var updateTableMap = /* @__PURE__ */ __name((offset, count) => {
+ if (functionsInTableMap) {
+ for (var i2 = offset; i2 < offset + count; i2++) {
+ var item = getWasmTableEntry(i2);
+ if (item) {
+ functionsInTableMap.set(item, i2);
+ }
+ }
+ }
+ }, "updateTableMap");
+ var functionsInTableMap;
+ var getFunctionAddress = /* @__PURE__ */ __name((func2) => {
+ if (!functionsInTableMap) {
+ functionsInTableMap = /* @__PURE__ */ new WeakMap();
+ updateTableMap(0, wasmTable.length);
+ }
+ return functionsInTableMap.get(func2) || 0;
+ }, "getFunctionAddress");
+ var freeTableIndexes = [];
+ var getEmptyTableSlot = /* @__PURE__ */ __name(() => {
+ if (freeTableIndexes.length) {
+ return freeTableIndexes.pop();
+ }
+ return wasmTable["grow"](1);
+ }, "getEmptyTableSlot");
+ var setWasmTableEntry = /* @__PURE__ */ __name((idx, func2) => {
+ wasmTable.set(idx, func2);
+ wasmTableMirror[idx] = wasmTable.get(idx);
+ }, "setWasmTableEntry");
+ var addFunction = /* @__PURE__ */ __name((func2, sig) => {
+ var rtn = getFunctionAddress(func2);
+ if (rtn) {
+ return rtn;
+ }
+ var ret = getEmptyTableSlot();
+ try {
+ setWasmTableEntry(ret, func2);
+ } catch (err2) {
+ if (!(err2 instanceof TypeError)) {
+ throw err2;
+ }
+ var wrapped = convertJsFunctionToWasm(func2, sig);
+ setWasmTableEntry(ret, wrapped);
+ }
+ functionsInTableMap.set(func2, ret);
+ return ret;
+ }, "addFunction");
+ var updateGOT = /* @__PURE__ */ __name((exports, replace) => {
+ for (var symName in exports) {
+ if (isInternalSym(symName)) {
+ continue;
+ }
+ var value = exports[symName];
+ GOT[symName] ||= new WebAssembly.Global({
+ "value": "i32",
+ "mutable": true
+ });
+ if (replace || GOT[symName].value == 0) {
+ if (typeof value == "function") {
+ GOT[symName].value = addFunction(value);
+ } else if (typeof value == "number") {
+ GOT[symName].value = value;
+ } else {
+ err(`unhandled export type for '${symName}': ${typeof value}`);
+ }
+ }
+ }
+ }, "updateGOT");
+ var relocateExports = /* @__PURE__ */ __name((exports, memoryBase2, replace) => {
+ var relocated = {};
+ for (var e in exports) {
+ var value = exports[e];
+ if (typeof value == "object") {
+ value = value.value;
+ }
+ if (typeof value == "number") {
+ value += memoryBase2;
+ }
+ relocated[e] = value;
+ }
+ updateGOT(relocated, replace);
+ return relocated;
+ }, "relocateExports");
+ var isSymbolDefined = /* @__PURE__ */ __name((symName) => {
+ var existing = wasmImports[symName];
+ if (!existing || existing.stub) {
+ return false;
+ }
+ return true;
+ }, "isSymbolDefined");
+ var dynCall = /* @__PURE__ */ __name((sig, ptr, args2 = [], promising = false) => {
+ var func2 = getWasmTableEntry(ptr);
+ var rtn = func2(...args2);
+ function convert(rtn2) {
+ return rtn2;
+ }
+ __name(convert, "convert");
+ return convert(rtn);
+ }, "dynCall");
+ var stackSave = /* @__PURE__ */ __name(() => _emscripten_stack_get_current(), "stackSave");
+ var stackRestore = /* @__PURE__ */ __name((val) => __emscripten_stack_restore(val), "stackRestore");
+ var createInvokeFunction = /* @__PURE__ */ __name((sig) => (ptr, ...args2) => {
+ var sp = stackSave();
+ try {
+ return dynCall(sig, ptr, args2);
+ } catch (e) {
+ stackRestore(sp);
+ if (e !== e + 0) throw e;
+ _setThrew(1, 0);
+ if (sig[0] == "j") return 0n;
+ }
+ }, "createInvokeFunction");
+ var resolveGlobalSymbol = /* @__PURE__ */ __name((symName, direct = false) => {
+ var sym;
+ if (isSymbolDefined(symName)) {
+ sym = wasmImports[symName];
+ } else if (symName.startsWith("invoke_")) {
+ sym = wasmImports[symName] = createInvokeFunction(symName.split("_")[1]);
+ }
+ return {
+ sym,
+ name: symName
+ };
+ }, "resolveGlobalSymbol");
+ var onPostCtors = [];
+ var addOnPostCtor = /* @__PURE__ */ __name((cb) => onPostCtors.push(cb), "addOnPostCtor");
+ var UTF8ToString = /* @__PURE__ */ __name((ptr, maxBytesToRead, ignoreNul) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) : "", "UTF8ToString");
+ var loadWebAssemblyModule = /* @__PURE__ */ __name((binary, flags, libName, localScope, handle) => {
+ var metadata = getDylinkMetadata(binary);
+ function loadModule() {
+ var memAlign = Math.pow(2, metadata.memoryAlign);
+ var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0;
+ var tableBase = metadata.tableSize ? wasmTable.length : 0;
+ if (handle) {
+ HEAP8[handle + 8] = 1;
+ LE_HEAP_STORE_U32((handle + 12 >> 2) * 4, memoryBase);
+ LE_HEAP_STORE_I32((handle + 16 >> 2) * 4, metadata.memorySize);
+ LE_HEAP_STORE_U32((handle + 20 >> 2) * 4, tableBase);
+ LE_HEAP_STORE_I32((handle + 24 >> 2) * 4, metadata.tableSize);
+ }
+ if (metadata.tableSize) {
+ wasmTable.grow(metadata.tableSize);
+ }
+ var moduleExports;
+ function resolveSymbol(sym) {
+ var resolved = resolveGlobalSymbol(sym).sym;
+ if (!resolved && localScope) {
+ resolved = localScope[sym];
+ }
+ if (!resolved) {
+ resolved = moduleExports[sym];
+ }
+ return resolved;
+ }
+ __name(resolveSymbol, "resolveSymbol");
+ var proxyHandler = {
+ get(stubs, prop) {
+ switch (prop) {
+ case "__memory_base":
+ return memoryBase;
+ case "__table_base":
+ return tableBase;
+ }
+ if (prop in wasmImports && !wasmImports[prop].stub) {
+ var res = wasmImports[prop];
+ return res;
+ }
+ if (!(prop in stubs)) {
+ var resolved;
+ stubs[prop] = (...args2) => {
+ resolved ||= resolveSymbol(prop);
+ return resolved(...args2);
+ };
+ }
+ return stubs[prop];
+ }
+ };
+ var proxy = new Proxy({}, proxyHandler);
+ currentModuleWeakSymbols = metadata.weakImports;
+ var info = {
+ "GOT.mem": new Proxy({}, GOTHandler),
+ "GOT.func": new Proxy({}, GOTHandler),
+ "env": proxy,
+ "wasi_snapshot_preview1": proxy
+ };
+ function postInstantiation(module, instance) {
+ updateTableMap(tableBase, metadata.tableSize);
+ moduleExports = relocateExports(instance.exports, memoryBase);
+ if (!flags.allowUndefined) {
+ reportUndefinedSymbols();
+ }
+ function addEmAsm(addr, body) {
+ var args = [];
+ var arity = 0;
+ for (; arity < 16; arity++) {
+ if (body.indexOf("$" + arity) != -1) {
+ args.push("$" + arity);
+ } else {
+ break;
+ }
+ }
+ args = args.join(",");
+ var func = `(${args}) => { ${body} };`;
+ ASM_CONSTS[start] = eval(func);
+ }
+ __name(addEmAsm, "addEmAsm");
+ if ("__start_em_asm" in moduleExports) {
+ var start = moduleExports["__start_em_asm"];
+ var stop = moduleExports["__stop_em_asm"];
+ while (start < stop) {
+ var jsString = UTF8ToString(start);
+ addEmAsm(start, jsString);
+ start = HEAPU8.indexOf(0, start) + 1;
+ }
+ }
+ function addEmJs(name, cSig, body) {
+ var jsArgs = [];
+ cSig = cSig.slice(1, -1);
+ if (cSig != "void") {
+ cSig = cSig.split(",");
+ for (var i in cSig) {
+ var jsArg = cSig[i].split(" ").pop();
+ jsArgs.push(jsArg.replace("*", ""));
+ }
+ }
+ var func = `(${jsArgs}) => ${body};`;
+ moduleExports[name] = eval(func);
+ }
+ __name(addEmJs, "addEmJs");
+ for (var name in moduleExports) {
+ if (name.startsWith("__em_js__")) {
+ var start = moduleExports[name];
+ var jsString = UTF8ToString(start);
+ var parts = jsString.split("<::>");
+ addEmJs(name.replace("__em_js__", ""), parts[0], parts[1]);
+ delete moduleExports[name];
+ }
+ }
+ var applyRelocs = moduleExports["__wasm_apply_data_relocs"];
+ if (applyRelocs) {
+ if (runtimeInitialized) {
+ applyRelocs();
+ } else {
+ __RELOC_FUNCS__.push(applyRelocs);
+ }
+ }
+ var init = moduleExports["__wasm_call_ctors"];
+ if (init) {
+ if (runtimeInitialized) {
+ init();
+ } else {
+ addOnPostCtor(init);
+ }
+ }
+ return moduleExports;
+ }
+ __name(postInstantiation, "postInstantiation");
+ if (flags.loadAsync) {
+ return (async () => {
+ var instance2;
+ if (binary instanceof WebAssembly.Module) {
+ instance2 = new WebAssembly.Instance(binary, info);
+ } else {
+ ({ module: binary, instance: instance2 } = await WebAssembly.instantiate(binary, info));
+ }
+ return postInstantiation(binary, instance2);
+ })();
+ }
+ var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary);
+ var instance = new WebAssembly.Instance(module, info);
+ return postInstantiation(module, instance);
+ }
+ __name(loadModule, "loadModule");
+ flags = {
+ ...flags,
+ rpath: {
+ parentLibPath: libName,
+ paths: metadata.runtimePaths
+ }
+ };
+ if (flags.loadAsync) {
+ return metadata.neededDynlibs.reduce((chain, dynNeeded) => chain.then(() => loadDynamicLibrary(dynNeeded, flags, localScope)), Promise.resolve()).then(loadModule);
+ }
+ metadata.neededDynlibs.forEach((needed) => loadDynamicLibrary(needed, flags, localScope));
+ return loadModule();
+ }, "loadWebAssemblyModule");
+ var mergeLibSymbols = /* @__PURE__ */ __name((exports, libName2) => {
+ for (var [sym, exp] of Object.entries(exports)) {
+ const setImport = /* @__PURE__ */ __name((target) => {
+ if (!isSymbolDefined(target)) {
+ wasmImports[target] = exp;
+ }
+ }, "setImport");
+ setImport(sym);
+ const main_alias = "__main_argc_argv";
+ if (sym == "main") {
+ setImport(main_alias);
+ }
+ if (sym == main_alias) {
+ setImport("main");
+ }
+ }
+ }, "mergeLibSymbols");
+ var asyncLoad = /* @__PURE__ */ __name(async (url) => {
+ var arrayBuffer = await readAsync(url);
+ return new Uint8Array(arrayBuffer);
+ }, "asyncLoad");
+ function loadDynamicLibrary(libName2, flags2 = {
+ global: true,
+ nodelete: true
+ }, localScope2, handle2) {
+ var dso = LDSO.loadedLibsByName[libName2];
+ if (dso) {
+ if (!flags2.global) {
+ if (localScope2) {
+ Object.assign(localScope2, dso.exports);
+ }
+ } else if (!dso.global) {
+ dso.global = true;
+ mergeLibSymbols(dso.exports, libName2);
+ }
+ if (flags2.nodelete && dso.refcount !== Infinity) {
+ dso.refcount = Infinity;
+ }
+ dso.refcount++;
+ if (handle2) {
+ LDSO.loadedLibsByHandle[handle2] = dso;
+ }
+ return flags2.loadAsync ? Promise.resolve(true) : true;
+ }
+ dso = newDSO(libName2, handle2, "loading");
+ dso.refcount = flags2.nodelete ? Infinity : 1;
+ dso.global = flags2.global;
+ function loadLibData() {
+ if (handle2) {
+ var data = LE_HEAP_LOAD_U32((handle2 + 28 >> 2) * 4);
+ var dataSize = LE_HEAP_LOAD_U32((handle2 + 32 >> 2) * 4);
+ if (data && dataSize) {
+ var libData = HEAP8.slice(data, data + dataSize);
+ return flags2.loadAsync ? Promise.resolve(libData) : libData;
+ }
+ }
+ var libFile = locateFile(libName2);
+ if (flags2.loadAsync) {
+ return asyncLoad(libFile);
+ }
+ if (!readBinary) {
+ throw new Error(`${libFile}: file not found, and synchronous loading of external files is not available`);
+ }
+ return readBinary(libFile);
+ }
+ __name(loadLibData, "loadLibData");
+ function getExports() {
+ if (flags2.loadAsync) {
+ return loadLibData().then((libData) => loadWebAssemblyModule(libData, flags2, libName2, localScope2, handle2));
+ }
+ return loadWebAssemblyModule(loadLibData(), flags2, libName2, localScope2, handle2);
+ }
+ __name(getExports, "getExports");
+ function moduleLoaded(exports) {
+ if (dso.global) {
+ mergeLibSymbols(exports, libName2);
+ } else if (localScope2) {
+ Object.assign(localScope2, exports);
+ }
+ dso.exports = exports;
+ }
+ __name(moduleLoaded, "moduleLoaded");
+ if (flags2.loadAsync) {
+ return getExports().then((exports) => {
+ moduleLoaded(exports);
+ return true;
+ });
+ }
+ moduleLoaded(getExports());
+ return true;
+ }
+ __name(loadDynamicLibrary, "loadDynamicLibrary");
+ var reportUndefinedSymbols = /* @__PURE__ */ __name(() => {
+ for (var [symName, entry] of Object.entries(GOT)) {
+ if (entry.value == 0) {
+ var value = resolveGlobalSymbol(symName, true).sym;
+ if (!value && !entry.required) {
+ continue;
+ }
+ if (typeof value == "function") {
+ entry.value = addFunction(value, value.sig);
+ } else if (typeof value == "number") {
+ entry.value = value;
+ } else {
+ throw new Error(`bad export type for '${symName}': ${typeof value}`);
+ }
+ }
+ }
+ }, "reportUndefinedSymbols");
+ var runDependencies = 0;
+ var dependenciesFulfilled = null;
+ var removeRunDependency = /* @__PURE__ */ __name((id) => {
+ runDependencies--;
+ Module["monitorRunDependencies"]?.(runDependencies);
+ if (runDependencies == 0) {
+ if (dependenciesFulfilled) {
+ var callback = dependenciesFulfilled;
+ dependenciesFulfilled = null;
+ callback();
+ }
+ }
+ }, "removeRunDependency");
+ var addRunDependency = /* @__PURE__ */ __name((id) => {
+ runDependencies++;
+ Module["monitorRunDependencies"]?.(runDependencies);
+ }, "addRunDependency");
+ var loadDylibs = /* @__PURE__ */ __name(async () => {
+ if (!dynamicLibraries.length) {
+ reportUndefinedSymbols();
+ return;
+ }
+ addRunDependency("loadDylibs");
+ for (var lib of dynamicLibraries) {
+ await loadDynamicLibrary(lib, {
+ loadAsync: true,
+ global: true,
+ nodelete: true,
+ allowUndefined: true
+ });
+ }
+ reportUndefinedSymbols();
+ removeRunDependency("loadDylibs");
+ }, "loadDylibs");
+ var noExitRuntime = true;
+ function setValue(ptr, value, type = "i8") {
+ if (type.endsWith("*")) type = "*";
+ switch (type) {
+ case "i1":
+ HEAP8[ptr] = value;
+ break;
+ case "i8":
+ HEAP8[ptr] = value;
+ break;
+ case "i16":
+ LE_HEAP_STORE_I16((ptr >> 1) * 2, value);
+ break;
+ case "i32":
+ LE_HEAP_STORE_I32((ptr >> 2) * 4, value);
+ break;
+ case "i64":
+ LE_HEAP_STORE_I64((ptr >> 3) * 8, BigInt(value));
+ break;
+ case "float":
+ LE_HEAP_STORE_F32((ptr >> 2) * 4, value);
+ break;
+ case "double":
+ LE_HEAP_STORE_F64((ptr >> 3) * 8, value);
+ break;
+ case "*":
+ LE_HEAP_STORE_U32((ptr >> 2) * 4, value);
+ break;
+ default:
+ abort(`invalid type for setValue: ${type}`);
+ }
+ }
+ __name(setValue, "setValue");
+ var ___memory_base = new WebAssembly.Global({
+ "value": "i32",
+ "mutable": false
+ }, 1024);
+ var ___stack_high = 78240;
+ var ___stack_low = 12704;
+ var ___stack_pointer = new WebAssembly.Global({
+ "value": "i32",
+ "mutable": true
+ }, 78240);
+ var ___table_base = new WebAssembly.Global({
+ "value": "i32",
+ "mutable": false
+ }, 1);
+ var __abort_js = /* @__PURE__ */ __name(() => abort(""), "__abort_js");
+ __abort_js.sig = "v";
+ var getHeapMax = /* @__PURE__ */ __name(() => (
+ // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate
+ // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side
+ // for any code that deals with heap sizes, which would require special
+ // casing all heap size related code to treat 0 specially.
+ 2147483648
+ ), "getHeapMax");
+ var growMemory = /* @__PURE__ */ __name((size) => {
+ var oldHeapSize = wasmMemory.buffer.byteLength;
+ var pages = (size - oldHeapSize + 65535) / 65536 | 0;
+ try {
+ wasmMemory.grow(pages);
+ updateMemoryViews();
+ return 1;
+ } catch (e) {
+ }
+ }, "growMemory");
+ var _emscripten_resize_heap = /* @__PURE__ */ __name((requestedSize) => {
+ var oldSize = HEAPU8.length;
+ requestedSize >>>= 0;
+ var maxHeapSize = getHeapMax();
+ if (requestedSize > maxHeapSize) {
+ return false;
+ }
+ for (var cutDown = 1; cutDown <= 4; cutDown *= 2) {
+ var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown);
+ overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296);
+ var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536));
+ var replacement = growMemory(newSize);
+ if (replacement) {
+ return true;
+ }
+ }
+ return false;
+ }, "_emscripten_resize_heap");
+ _emscripten_resize_heap.sig = "ip";
+ var _fd_close = /* @__PURE__ */ __name((fd) => 52, "_fd_close");
+ _fd_close.sig = "ii";
+ var INT53_MAX = 9007199254740992;
+ var INT53_MIN = -9007199254740992;
+ var bigintToI53Checked = /* @__PURE__ */ __name((num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num), "bigintToI53Checked");
+ function _fd_seek(fd, offset, whence, newOffset) {
+ offset = bigintToI53Checked(offset);
+ return 70;
+ }
+ __name(_fd_seek, "_fd_seek");
+ _fd_seek.sig = "iijip";
+ var printCharBuffers = [null, [], []];
+ var printChar = /* @__PURE__ */ __name((stream, curr) => {
+ var buffer = printCharBuffers[stream];
+ if (curr === 0 || curr === 10) {
+ (stream === 1 ? out : err)(UTF8ArrayToString(buffer));
+ buffer.length = 0;
+ } else {
+ buffer.push(curr);
+ }
+ }, "printChar");
+ var _fd_write = /* @__PURE__ */ __name((fd, iov, iovcnt, pnum) => {
+ var num = 0;
+ for (var i2 = 0; i2 < iovcnt; i2++) {
+ var ptr = LE_HEAP_LOAD_U32((iov >> 2) * 4);
+ var len = LE_HEAP_LOAD_U32((iov + 4 >> 2) * 4);
+ iov += 8;
+ for (var j = 0; j < len; j++) {
+ printChar(fd, HEAPU8[ptr + j]);
+ }
+ num += len;
+ }
+ LE_HEAP_STORE_U32((pnum >> 2) * 4, num);
+ return 0;
+ }, "_fd_write");
+ _fd_write.sig = "iippp";
+ function _tree_sitter_log_callback(isLexMessage, messageAddress) {
+ if (Module.currentLogCallback) {
+ const message = UTF8ToString(messageAddress);
+ Module.currentLogCallback(message, isLexMessage !== 0);
+ }
+ }
+ __name(_tree_sitter_log_callback, "_tree_sitter_log_callback");
+ function _tree_sitter_parse_callback(inputBufferAddress, index, row, column, lengthAddress) {
+ const INPUT_BUFFER_SIZE = 10 * 1024;
+ const string = Module.currentParseCallback(index, {
+ row,
+ column
+ });
+ if (typeof string === "string") {
+ setValue(lengthAddress, string.length, "i32");
+ stringToUTF16(string, inputBufferAddress, INPUT_BUFFER_SIZE);
+ } else {
+ setValue(lengthAddress, 0, "i32");
+ }
+ }
+ __name(_tree_sitter_parse_callback, "_tree_sitter_parse_callback");
+ function _tree_sitter_progress_callback(currentOffset, hasError) {
+ if (Module.currentProgressCallback) {
+ return Module.currentProgressCallback({
+ currentOffset,
+ hasError
+ });
+ }
+ return false;
+ }
+ __name(_tree_sitter_progress_callback, "_tree_sitter_progress_callback");
+ function _tree_sitter_query_progress_callback(currentOffset) {
+ if (Module.currentQueryProgressCallback) {
+ return Module.currentQueryProgressCallback({
+ currentOffset
+ });
+ }
+ return false;
+ }
+ __name(_tree_sitter_query_progress_callback, "_tree_sitter_query_progress_callback");
+ var runtimeKeepaliveCounter = 0;
+ var keepRuntimeAlive = /* @__PURE__ */ __name(() => noExitRuntime || runtimeKeepaliveCounter > 0, "keepRuntimeAlive");
+ var _proc_exit = /* @__PURE__ */ __name((code) => {
+ EXITSTATUS = code;
+ if (!keepRuntimeAlive()) {
+ Module["onExit"]?.(code);
+ ABORT = true;
+ }
+ quit_(code, new ExitStatus(code));
+ }, "_proc_exit");
+ _proc_exit.sig = "vi";
+ var exitJS = /* @__PURE__ */ __name((status, implicit) => {
+ EXITSTATUS = status;
+ _proc_exit(status);
+ }, "exitJS");
+ var handleException = /* @__PURE__ */ __name((e) => {
+ if (e instanceof ExitStatus || e == "unwind") {
+ return EXITSTATUS;
+ }
+ quit_(1, e);
+ }, "handleException");
+ var lengthBytesUTF8 = /* @__PURE__ */ __name((str) => {
+ var len = 0;
+ for (var i2 = 0; i2 < str.length; ++i2) {
+ var c = str.charCodeAt(i2);
+ if (c <= 127) {
+ len++;
+ } else if (c <= 2047) {
+ len += 2;
+ } else if (c >= 55296 && c <= 57343) {
+ len += 4;
+ ++i2;
+ } else {
+ len += 3;
+ }
+ }
+ return len;
+ }, "lengthBytesUTF8");
+ var stringToUTF8Array = /* @__PURE__ */ __name((str, heap, outIdx, maxBytesToWrite) => {
+ if (!(maxBytesToWrite > 0)) return 0;
+ var startIdx = outIdx;
+ var endIdx = outIdx + maxBytesToWrite - 1;
+ for (var i2 = 0; i2 < str.length; ++i2) {
+ var u = str.codePointAt(i2);
+ if (u <= 127) {
+ if (outIdx >= endIdx) break;
+ heap[outIdx++] = u;
+ } else if (u <= 2047) {
+ if (outIdx + 1 >= endIdx) break;
+ heap[outIdx++] = 192 | u >> 6;
+ heap[outIdx++] = 128 | u & 63;
+ } else if (u <= 65535) {
+ if (outIdx + 2 >= endIdx) break;
+ heap[outIdx++] = 224 | u >> 12;
+ heap[outIdx++] = 128 | u >> 6 & 63;
+ heap[outIdx++] = 128 | u & 63;
+ } else {
+ if (outIdx + 3 >= endIdx) break;
+ heap[outIdx++] = 240 | u >> 18;
+ heap[outIdx++] = 128 | u >> 12 & 63;
+ heap[outIdx++] = 128 | u >> 6 & 63;
+ heap[outIdx++] = 128 | u & 63;
+ i2++;
+ }
+ }
+ heap[outIdx] = 0;
+ return outIdx - startIdx;
+ }, "stringToUTF8Array");
+ var stringToUTF8 = /* @__PURE__ */ __name((str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite), "stringToUTF8");
+ var stackAlloc = /* @__PURE__ */ __name((sz) => __emscripten_stack_alloc(sz), "stackAlloc");
+ var stringToUTF8OnStack = /* @__PURE__ */ __name((str) => {
+ var size = lengthBytesUTF8(str) + 1;
+ var ret = stackAlloc(size);
+ stringToUTF8(str, ret, size);
+ return ret;
+ }, "stringToUTF8OnStack");
+ var AsciiToString = /* @__PURE__ */ __name((ptr) => {
+ var str = "";
+ while (1) {
+ var ch = HEAPU8[ptr++];
+ if (!ch) return str;
+ str += String.fromCharCode(ch);
+ }
+ }, "AsciiToString");
+ var stringToUTF16 = /* @__PURE__ */ __name((str, outPtr, maxBytesToWrite) => {
+ maxBytesToWrite ??= 2147483647;
+ if (maxBytesToWrite < 2) return 0;
+ maxBytesToWrite -= 2;
+ var startPtr = outPtr;
+ var numCharsToWrite = maxBytesToWrite < str.length * 2 ? maxBytesToWrite / 2 : str.length;
+ for (var i2 = 0; i2 < numCharsToWrite; ++i2) {
+ var codeUnit = str.charCodeAt(i2);
+ LE_HEAP_STORE_I16((outPtr >> 1) * 2, codeUnit);
+ outPtr += 2;
+ }
+ LE_HEAP_STORE_I16((outPtr >> 1) * 2, 0);
+ return outPtr - startPtr;
+ }, "stringToUTF16");
+ LE_ATOMICS_NATIVE_BYTE_ORDER = new Int8Array(new Int16Array([1]).buffer)[0] === 1 ? [
+ /* little endian */
+ ((x) => x),
+ ((x) => x),
+ void 0,
+ ((x) => x)
+ ] : [
+ /* big endian */
+ ((x) => x),
+ ((x) => ((x & 65280) << 8 | (x & 255) << 24) >> 16),
+ void 0,
+ ((x) => x >> 24 & 255 | x >> 8 & 65280 | (x & 65280) << 8 | (x & 255) << 24)
+ ];
+ function LE_HEAP_UPDATE() {
+ HEAPU16.unsigned = ((x) => x & 65535);
+ HEAPU32.unsigned = ((x) => x >>> 0);
+ }
+ __name(LE_HEAP_UPDATE, "LE_HEAP_UPDATE");
+ {
+ initMemory();
+ if (Module["noExitRuntime"]) noExitRuntime = Module["noExitRuntime"];
+ if (Module["print"]) out = Module["print"];
+ if (Module["printErr"]) err = Module["printErr"];
+ if (Module["dynamicLibraries"]) dynamicLibraries = Module["dynamicLibraries"];
+ if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"];
+ if (Module["arguments"]) arguments_ = Module["arguments"];
+ if (Module["thisProgram"]) thisProgram = Module["thisProgram"];
+ if (Module["preInit"]) {
+ if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];
+ while (Module["preInit"].length > 0) {
+ Module["preInit"].shift()();
+ }
+ }
+ }
+ Module["setValue"] = setValue;
+ Module["getValue"] = getValue;
+ Module["UTF8ToString"] = UTF8ToString;
+ Module["stringToUTF8"] = stringToUTF8;
+ Module["lengthBytesUTF8"] = lengthBytesUTF8;
+ Module["AsciiToString"] = AsciiToString;
+ Module["stringToUTF16"] = stringToUTF16;
+ Module["loadWebAssemblyModule"] = loadWebAssemblyModule;
+ Module["LE_HEAP_STORE_I64"] = LE_HEAP_STORE_I64;
+ var ASM_CONSTS = {};
+ var _malloc, _calloc, _realloc, _free, _ts_range_edit, _memcmp, _ts_language_symbol_count, _ts_language_state_count, _ts_language_abi_version, _ts_language_name, _ts_language_field_count, _ts_language_next_state, _ts_language_symbol_name, _ts_language_symbol_for_name, _strncmp, _ts_language_symbol_type, _ts_language_field_name_for_id, _ts_lookahead_iterator_new, _ts_lookahead_iterator_delete, _ts_lookahead_iterator_reset_state, _ts_lookahead_iterator_reset, _ts_lookahead_iterator_next, _ts_lookahead_iterator_current_symbol, _ts_point_edit, _ts_parser_delete, _ts_parser_reset, _ts_parser_set_language, _ts_parser_set_included_ranges, _ts_query_new, _ts_query_delete, _iswspace, _iswalnum, _ts_query_pattern_count, _ts_query_capture_count, _ts_query_string_count, _ts_query_capture_name_for_id, _ts_query_capture_quantifier_for_id, _ts_query_string_value_for_id, _ts_query_predicates_for_pattern, _ts_query_start_byte_for_pattern, _ts_query_end_byte_for_pattern, _ts_query_is_pattern_rooted, _ts_query_is_pattern_non_local, _ts_query_is_pattern_guaranteed_at_step, _ts_query_disable_capture, _ts_query_disable_pattern, _ts_tree_copy, _ts_tree_delete, _ts_init, _ts_parser_new_wasm, _ts_parser_enable_logger_wasm, _ts_parser_parse_wasm, _ts_parser_included_ranges_wasm, _ts_language_type_is_named_wasm, _ts_language_type_is_visible_wasm, _ts_language_metadata_wasm, _ts_language_supertypes_wasm, _ts_language_subtypes_wasm, _ts_tree_root_node_wasm, _ts_tree_root_node_with_offset_wasm, _ts_tree_edit_wasm, _ts_tree_included_ranges_wasm, _ts_tree_get_changed_ranges_wasm, _ts_tree_cursor_new_wasm, _ts_tree_cursor_copy_wasm, _ts_tree_cursor_delete_wasm, _ts_tree_cursor_reset_wasm, _ts_tree_cursor_reset_to_wasm, _ts_tree_cursor_goto_first_child_wasm, _ts_tree_cursor_goto_last_child_wasm, _ts_tree_cursor_goto_first_child_for_index_wasm, _ts_tree_cursor_goto_first_child_for_position_wasm, _ts_tree_cursor_goto_next_sibling_wasm, _ts_tree_cursor_goto_previous_sibling_wasm, _ts_tree_cursor_goto_descendant_wasm, _ts_tree_cursor_goto_parent_wasm, _ts_tree_cursor_current_node_type_id_wasm, _ts_tree_cursor_current_node_state_id_wasm, _ts_tree_cursor_current_node_is_named_wasm, _ts_tree_cursor_current_node_is_missing_wasm, _ts_tree_cursor_current_node_id_wasm, _ts_tree_cursor_start_position_wasm, _ts_tree_cursor_end_position_wasm, _ts_tree_cursor_start_index_wasm, _ts_tree_cursor_end_index_wasm, _ts_tree_cursor_current_field_id_wasm, _ts_tree_cursor_current_depth_wasm, _ts_tree_cursor_current_descendant_index_wasm, _ts_tree_cursor_current_node_wasm, _ts_node_symbol_wasm, _ts_node_field_name_for_child_wasm, _ts_node_field_name_for_named_child_wasm, _ts_node_children_by_field_id_wasm, _ts_node_first_child_for_byte_wasm, _ts_node_first_named_child_for_byte_wasm, _ts_node_grammar_symbol_wasm, _ts_node_child_count_wasm, _ts_node_named_child_count_wasm, _ts_node_child_wasm, _ts_node_named_child_wasm, _ts_node_child_by_field_id_wasm, _ts_node_next_sibling_wasm, _ts_node_prev_sibling_wasm, _ts_node_next_named_sibling_wasm, _ts_node_prev_named_sibling_wasm, _ts_node_descendant_count_wasm, _ts_node_parent_wasm, _ts_node_child_with_descendant_wasm, _ts_node_descendant_for_index_wasm, _ts_node_named_descendant_for_index_wasm, _ts_node_descendant_for_position_wasm, _ts_node_named_descendant_for_position_wasm, _ts_node_start_point_wasm, _ts_node_end_point_wasm, _ts_node_start_index_wasm, _ts_node_end_index_wasm, _ts_node_to_string_wasm, _ts_node_children_wasm, _ts_node_named_children_wasm, _ts_node_descendants_of_type_wasm, _ts_node_is_named_wasm, _ts_node_has_changes_wasm, _ts_node_has_error_wasm, _ts_node_is_error_wasm, _ts_node_is_missing_wasm, _ts_node_is_extra_wasm, _ts_node_parse_state_wasm, _ts_node_next_parse_state_wasm, _ts_query_matches_wasm, _ts_query_captures_wasm, _memset, _memcpy, _memmove, _iswalpha, _iswblank, _iswdigit, _iswlower, _iswupper, _iswxdigit, _memchr, _strlen, _strcmp, _strncat, _strncpy, _towlower, _towupper, _setThrew, __emscripten_stack_restore, __emscripten_stack_alloc, _emscripten_stack_get_current, ___wasm_apply_data_relocs;
+ function assignWasmExports(wasmExports2) {
+ Module["_malloc"] = _malloc = wasmExports2["malloc"];
+ Module["_calloc"] = _calloc = wasmExports2["calloc"];
+ Module["_realloc"] = _realloc = wasmExports2["realloc"];
+ Module["_free"] = _free = wasmExports2["free"];
+ Module["_ts_range_edit"] = _ts_range_edit = wasmExports2["ts_range_edit"];
+ Module["_memcmp"] = _memcmp = wasmExports2["memcmp"];
+ Module["_ts_language_symbol_count"] = _ts_language_symbol_count = wasmExports2["ts_language_symbol_count"];
+ Module["_ts_language_state_count"] = _ts_language_state_count = wasmExports2["ts_language_state_count"];
+ Module["_ts_language_abi_version"] = _ts_language_abi_version = wasmExports2["ts_language_abi_version"];
+ Module["_ts_language_name"] = _ts_language_name = wasmExports2["ts_language_name"];
+ Module["_ts_language_field_count"] = _ts_language_field_count = wasmExports2["ts_language_field_count"];
+ Module["_ts_language_next_state"] = _ts_language_next_state = wasmExports2["ts_language_next_state"];
+ Module["_ts_language_symbol_name"] = _ts_language_symbol_name = wasmExports2["ts_language_symbol_name"];
+ Module["_ts_language_symbol_for_name"] = _ts_language_symbol_for_name = wasmExports2["ts_language_symbol_for_name"];
+ Module["_strncmp"] = _strncmp = wasmExports2["strncmp"];
+ Module["_ts_language_symbol_type"] = _ts_language_symbol_type = wasmExports2["ts_language_symbol_type"];
+ Module["_ts_language_field_name_for_id"] = _ts_language_field_name_for_id = wasmExports2["ts_language_field_name_for_id"];
+ Module["_ts_lookahead_iterator_new"] = _ts_lookahead_iterator_new = wasmExports2["ts_lookahead_iterator_new"];
+ Module["_ts_lookahead_iterator_delete"] = _ts_lookahead_iterator_delete = wasmExports2["ts_lookahead_iterator_delete"];
+ Module["_ts_lookahead_iterator_reset_state"] = _ts_lookahead_iterator_reset_state = wasmExports2["ts_lookahead_iterator_reset_state"];
+ Module["_ts_lookahead_iterator_reset"] = _ts_lookahead_iterator_reset = wasmExports2["ts_lookahead_iterator_reset"];
+ Module["_ts_lookahead_iterator_next"] = _ts_lookahead_iterator_next = wasmExports2["ts_lookahead_iterator_next"];
+ Module["_ts_lookahead_iterator_current_symbol"] = _ts_lookahead_iterator_current_symbol = wasmExports2["ts_lookahead_iterator_current_symbol"];
+ Module["_ts_point_edit"] = _ts_point_edit = wasmExports2["ts_point_edit"];
+ Module["_ts_parser_delete"] = _ts_parser_delete = wasmExports2["ts_parser_delete"];
+ Module["_ts_parser_reset"] = _ts_parser_reset = wasmExports2["ts_parser_reset"];
+ Module["_ts_parser_set_language"] = _ts_parser_set_language = wasmExports2["ts_parser_set_language"];
+ Module["_ts_parser_set_included_ranges"] = _ts_parser_set_included_ranges = wasmExports2["ts_parser_set_included_ranges"];
+ Module["_ts_query_new"] = _ts_query_new = wasmExports2["ts_query_new"];
+ Module["_ts_query_delete"] = _ts_query_delete = wasmExports2["ts_query_delete"];
+ Module["_iswspace"] = _iswspace = wasmExports2["iswspace"];
+ Module["_iswalnum"] = _iswalnum = wasmExports2["iswalnum"];
+ Module["_ts_query_pattern_count"] = _ts_query_pattern_count = wasmExports2["ts_query_pattern_count"];
+ Module["_ts_query_capture_count"] = _ts_query_capture_count = wasmExports2["ts_query_capture_count"];
+ Module["_ts_query_string_count"] = _ts_query_string_count = wasmExports2["ts_query_string_count"];
+ Module["_ts_query_capture_name_for_id"] = _ts_query_capture_name_for_id = wasmExports2["ts_query_capture_name_for_id"];
+ Module["_ts_query_capture_quantifier_for_id"] = _ts_query_capture_quantifier_for_id = wasmExports2["ts_query_capture_quantifier_for_id"];
+ Module["_ts_query_string_value_for_id"] = _ts_query_string_value_for_id = wasmExports2["ts_query_string_value_for_id"];
+ Module["_ts_query_predicates_for_pattern"] = _ts_query_predicates_for_pattern = wasmExports2["ts_query_predicates_for_pattern"];
+ Module["_ts_query_start_byte_for_pattern"] = _ts_query_start_byte_for_pattern = wasmExports2["ts_query_start_byte_for_pattern"];
+ Module["_ts_query_end_byte_for_pattern"] = _ts_query_end_byte_for_pattern = wasmExports2["ts_query_end_byte_for_pattern"];
+ Module["_ts_query_is_pattern_rooted"] = _ts_query_is_pattern_rooted = wasmExports2["ts_query_is_pattern_rooted"];
+ Module["_ts_query_is_pattern_non_local"] = _ts_query_is_pattern_non_local = wasmExports2["ts_query_is_pattern_non_local"];
+ Module["_ts_query_is_pattern_guaranteed_at_step"] = _ts_query_is_pattern_guaranteed_at_step = wasmExports2["ts_query_is_pattern_guaranteed_at_step"];
+ Module["_ts_query_disable_capture"] = _ts_query_disable_capture = wasmExports2["ts_query_disable_capture"];
+ Module["_ts_query_disable_pattern"] = _ts_query_disable_pattern = wasmExports2["ts_query_disable_pattern"];
+ Module["_ts_tree_copy"] = _ts_tree_copy = wasmExports2["ts_tree_copy"];
+ Module["_ts_tree_delete"] = _ts_tree_delete = wasmExports2["ts_tree_delete"];
+ Module["_ts_init"] = _ts_init = wasmExports2["ts_init"];
+ Module["_ts_parser_new_wasm"] = _ts_parser_new_wasm = wasmExports2["ts_parser_new_wasm"];
+ Module["_ts_parser_enable_logger_wasm"] = _ts_parser_enable_logger_wasm = wasmExports2["ts_parser_enable_logger_wasm"];
+ Module["_ts_parser_parse_wasm"] = _ts_parser_parse_wasm = wasmExports2["ts_parser_parse_wasm"];
+ Module["_ts_parser_included_ranges_wasm"] = _ts_parser_included_ranges_wasm = wasmExports2["ts_parser_included_ranges_wasm"];
+ Module["_ts_language_type_is_named_wasm"] = _ts_language_type_is_named_wasm = wasmExports2["ts_language_type_is_named_wasm"];
+ Module["_ts_language_type_is_visible_wasm"] = _ts_language_type_is_visible_wasm = wasmExports2["ts_language_type_is_visible_wasm"];
+ Module["_ts_language_metadata_wasm"] = _ts_language_metadata_wasm = wasmExports2["ts_language_metadata_wasm"];
+ Module["_ts_language_supertypes_wasm"] = _ts_language_supertypes_wasm = wasmExports2["ts_language_supertypes_wasm"];
+ Module["_ts_language_subtypes_wasm"] = _ts_language_subtypes_wasm = wasmExports2["ts_language_subtypes_wasm"];
+ Module["_ts_tree_root_node_wasm"] = _ts_tree_root_node_wasm = wasmExports2["ts_tree_root_node_wasm"];
+ Module["_ts_tree_root_node_with_offset_wasm"] = _ts_tree_root_node_with_offset_wasm = wasmExports2["ts_tree_root_node_with_offset_wasm"];
+ Module["_ts_tree_edit_wasm"] = _ts_tree_edit_wasm = wasmExports2["ts_tree_edit_wasm"];
+ Module["_ts_tree_included_ranges_wasm"] = _ts_tree_included_ranges_wasm = wasmExports2["ts_tree_included_ranges_wasm"];
+ Module["_ts_tree_get_changed_ranges_wasm"] = _ts_tree_get_changed_ranges_wasm = wasmExports2["ts_tree_get_changed_ranges_wasm"];
+ Module["_ts_tree_cursor_new_wasm"] = _ts_tree_cursor_new_wasm = wasmExports2["ts_tree_cursor_new_wasm"];
+ Module["_ts_tree_cursor_copy_wasm"] = _ts_tree_cursor_copy_wasm = wasmExports2["ts_tree_cursor_copy_wasm"];
+ Module["_ts_tree_cursor_delete_wasm"] = _ts_tree_cursor_delete_wasm = wasmExports2["ts_tree_cursor_delete_wasm"];
+ Module["_ts_tree_cursor_reset_wasm"] = _ts_tree_cursor_reset_wasm = wasmExports2["ts_tree_cursor_reset_wasm"];
+ Module["_ts_tree_cursor_reset_to_wasm"] = _ts_tree_cursor_reset_to_wasm = wasmExports2["ts_tree_cursor_reset_to_wasm"];
+ Module["_ts_tree_cursor_goto_first_child_wasm"] = _ts_tree_cursor_goto_first_child_wasm = wasmExports2["ts_tree_cursor_goto_first_child_wasm"];
+ Module["_ts_tree_cursor_goto_last_child_wasm"] = _ts_tree_cursor_goto_last_child_wasm = wasmExports2["ts_tree_cursor_goto_last_child_wasm"];
+ Module["_ts_tree_cursor_goto_first_child_for_index_wasm"] = _ts_tree_cursor_goto_first_child_for_index_wasm = wasmExports2["ts_tree_cursor_goto_first_child_for_index_wasm"];
+ Module["_ts_tree_cursor_goto_first_child_for_position_wasm"] = _ts_tree_cursor_goto_first_child_for_position_wasm = wasmExports2["ts_tree_cursor_goto_first_child_for_position_wasm"];
+ Module["_ts_tree_cursor_goto_next_sibling_wasm"] = _ts_tree_cursor_goto_next_sibling_wasm = wasmExports2["ts_tree_cursor_goto_next_sibling_wasm"];
+ Module["_ts_tree_cursor_goto_previous_sibling_wasm"] = _ts_tree_cursor_goto_previous_sibling_wasm = wasmExports2["ts_tree_cursor_goto_previous_sibling_wasm"];
+ Module["_ts_tree_cursor_goto_descendant_wasm"] = _ts_tree_cursor_goto_descendant_wasm = wasmExports2["ts_tree_cursor_goto_descendant_wasm"];
+ Module["_ts_tree_cursor_goto_parent_wasm"] = _ts_tree_cursor_goto_parent_wasm = wasmExports2["ts_tree_cursor_goto_parent_wasm"];
+ Module["_ts_tree_cursor_current_node_type_id_wasm"] = _ts_tree_cursor_current_node_type_id_wasm = wasmExports2["ts_tree_cursor_current_node_type_id_wasm"];
+ Module["_ts_tree_cursor_current_node_state_id_wasm"] = _ts_tree_cursor_current_node_state_id_wasm = wasmExports2["ts_tree_cursor_current_node_state_id_wasm"];
+ Module["_ts_tree_cursor_current_node_is_named_wasm"] = _ts_tree_cursor_current_node_is_named_wasm = wasmExports2["ts_tree_cursor_current_node_is_named_wasm"];
+ Module["_ts_tree_cursor_current_node_is_missing_wasm"] = _ts_tree_cursor_current_node_is_missing_wasm = wasmExports2["ts_tree_cursor_current_node_is_missing_wasm"];
+ Module["_ts_tree_cursor_current_node_id_wasm"] = _ts_tree_cursor_current_node_id_wasm = wasmExports2["ts_tree_cursor_current_node_id_wasm"];
+ Module["_ts_tree_cursor_start_position_wasm"] = _ts_tree_cursor_start_position_wasm = wasmExports2["ts_tree_cursor_start_position_wasm"];
+ Module["_ts_tree_cursor_end_position_wasm"] = _ts_tree_cursor_end_position_wasm = wasmExports2["ts_tree_cursor_end_position_wasm"];
+ Module["_ts_tree_cursor_start_index_wasm"] = _ts_tree_cursor_start_index_wasm = wasmExports2["ts_tree_cursor_start_index_wasm"];
+ Module["_ts_tree_cursor_end_index_wasm"] = _ts_tree_cursor_end_index_wasm = wasmExports2["ts_tree_cursor_end_index_wasm"];
+ Module["_ts_tree_cursor_current_field_id_wasm"] = _ts_tree_cursor_current_field_id_wasm = wasmExports2["ts_tree_cursor_current_field_id_wasm"];
+ Module["_ts_tree_cursor_current_depth_wasm"] = _ts_tree_cursor_current_depth_wasm = wasmExports2["ts_tree_cursor_current_depth_wasm"];
+ Module["_ts_tree_cursor_current_descendant_index_wasm"] = _ts_tree_cursor_current_descendant_index_wasm = wasmExports2["ts_tree_cursor_current_descendant_index_wasm"];
+ Module["_ts_tree_cursor_current_node_wasm"] = _ts_tree_cursor_current_node_wasm = wasmExports2["ts_tree_cursor_current_node_wasm"];
+ Module["_ts_node_symbol_wasm"] = _ts_node_symbol_wasm = wasmExports2["ts_node_symbol_wasm"];
+ Module["_ts_node_field_name_for_child_wasm"] = _ts_node_field_name_for_child_wasm = wasmExports2["ts_node_field_name_for_child_wasm"];
+ Module["_ts_node_field_name_for_named_child_wasm"] = _ts_node_field_name_for_named_child_wasm = wasmExports2["ts_node_field_name_for_named_child_wasm"];
+ Module["_ts_node_children_by_field_id_wasm"] = _ts_node_children_by_field_id_wasm = wasmExports2["ts_node_children_by_field_id_wasm"];
+ Module["_ts_node_first_child_for_byte_wasm"] = _ts_node_first_child_for_byte_wasm = wasmExports2["ts_node_first_child_for_byte_wasm"];
+ Module["_ts_node_first_named_child_for_byte_wasm"] = _ts_node_first_named_child_for_byte_wasm = wasmExports2["ts_node_first_named_child_for_byte_wasm"];
+ Module["_ts_node_grammar_symbol_wasm"] = _ts_node_grammar_symbol_wasm = wasmExports2["ts_node_grammar_symbol_wasm"];
+ Module["_ts_node_child_count_wasm"] = _ts_node_child_count_wasm = wasmExports2["ts_node_child_count_wasm"];
+ Module["_ts_node_named_child_count_wasm"] = _ts_node_named_child_count_wasm = wasmExports2["ts_node_named_child_count_wasm"];
+ Module["_ts_node_child_wasm"] = _ts_node_child_wasm = wasmExports2["ts_node_child_wasm"];
+ Module["_ts_node_named_child_wasm"] = _ts_node_named_child_wasm = wasmExports2["ts_node_named_child_wasm"];
+ Module["_ts_node_child_by_field_id_wasm"] = _ts_node_child_by_field_id_wasm = wasmExports2["ts_node_child_by_field_id_wasm"];
+ Module["_ts_node_next_sibling_wasm"] = _ts_node_next_sibling_wasm = wasmExports2["ts_node_next_sibling_wasm"];
+ Module["_ts_node_prev_sibling_wasm"] = _ts_node_prev_sibling_wasm = wasmExports2["ts_node_prev_sibling_wasm"];
+ Module["_ts_node_next_named_sibling_wasm"] = _ts_node_next_named_sibling_wasm = wasmExports2["ts_node_next_named_sibling_wasm"];
+ Module["_ts_node_prev_named_sibling_wasm"] = _ts_node_prev_named_sibling_wasm = wasmExports2["ts_node_prev_named_sibling_wasm"];
+ Module["_ts_node_descendant_count_wasm"] = _ts_node_descendant_count_wasm = wasmExports2["ts_node_descendant_count_wasm"];
+ Module["_ts_node_parent_wasm"] = _ts_node_parent_wasm = wasmExports2["ts_node_parent_wasm"];
+ Module["_ts_node_child_with_descendant_wasm"] = _ts_node_child_with_descendant_wasm = wasmExports2["ts_node_child_with_descendant_wasm"];
+ Module["_ts_node_descendant_for_index_wasm"] = _ts_node_descendant_for_index_wasm = wasmExports2["ts_node_descendant_for_index_wasm"];
+ Module["_ts_node_named_descendant_for_index_wasm"] = _ts_node_named_descendant_for_index_wasm = wasmExports2["ts_node_named_descendant_for_index_wasm"];
+ Module["_ts_node_descendant_for_position_wasm"] = _ts_node_descendant_for_position_wasm = wasmExports2["ts_node_descendant_for_position_wasm"];
+ Module["_ts_node_named_descendant_for_position_wasm"] = _ts_node_named_descendant_for_position_wasm = wasmExports2["ts_node_named_descendant_for_position_wasm"];
+ Module["_ts_node_start_point_wasm"] = _ts_node_start_point_wasm = wasmExports2["ts_node_start_point_wasm"];
+ Module["_ts_node_end_point_wasm"] = _ts_node_end_point_wasm = wasmExports2["ts_node_end_point_wasm"];
+ Module["_ts_node_start_index_wasm"] = _ts_node_start_index_wasm = wasmExports2["ts_node_start_index_wasm"];
+ Module["_ts_node_end_index_wasm"] = _ts_node_end_index_wasm = wasmExports2["ts_node_end_index_wasm"];
+ Module["_ts_node_to_string_wasm"] = _ts_node_to_string_wasm = wasmExports2["ts_node_to_string_wasm"];
+ Module["_ts_node_children_wasm"] = _ts_node_children_wasm = wasmExports2["ts_node_children_wasm"];
+ Module["_ts_node_named_children_wasm"] = _ts_node_named_children_wasm = wasmExports2["ts_node_named_children_wasm"];
+ Module["_ts_node_descendants_of_type_wasm"] = _ts_node_descendants_of_type_wasm = wasmExports2["ts_node_descendants_of_type_wasm"];
+ Module["_ts_node_is_named_wasm"] = _ts_node_is_named_wasm = wasmExports2["ts_node_is_named_wasm"];
+ Module["_ts_node_has_changes_wasm"] = _ts_node_has_changes_wasm = wasmExports2["ts_node_has_changes_wasm"];
+ Module["_ts_node_has_error_wasm"] = _ts_node_has_error_wasm = wasmExports2["ts_node_has_error_wasm"];
+ Module["_ts_node_is_error_wasm"] = _ts_node_is_error_wasm = wasmExports2["ts_node_is_error_wasm"];
+ Module["_ts_node_is_missing_wasm"] = _ts_node_is_missing_wasm = wasmExports2["ts_node_is_missing_wasm"];
+ Module["_ts_node_is_extra_wasm"] = _ts_node_is_extra_wasm = wasmExports2["ts_node_is_extra_wasm"];
+ Module["_ts_node_parse_state_wasm"] = _ts_node_parse_state_wasm = wasmExports2["ts_node_parse_state_wasm"];
+ Module["_ts_node_next_parse_state_wasm"] = _ts_node_next_parse_state_wasm = wasmExports2["ts_node_next_parse_state_wasm"];
+ Module["_ts_query_matches_wasm"] = _ts_query_matches_wasm = wasmExports2["ts_query_matches_wasm"];
+ Module["_ts_query_captures_wasm"] = _ts_query_captures_wasm = wasmExports2["ts_query_captures_wasm"];
+ Module["_memset"] = _memset = wasmExports2["memset"];
+ Module["_memcpy"] = _memcpy = wasmExports2["memcpy"];
+ Module["_memmove"] = _memmove = wasmExports2["memmove"];
+ Module["_iswalpha"] = _iswalpha = wasmExports2["iswalpha"];
+ Module["_iswblank"] = _iswblank = wasmExports2["iswblank"];
+ Module["_iswdigit"] = _iswdigit = wasmExports2["iswdigit"];
+ Module["_iswlower"] = _iswlower = wasmExports2["iswlower"];
+ Module["_iswupper"] = _iswupper = wasmExports2["iswupper"];
+ Module["_iswxdigit"] = _iswxdigit = wasmExports2["iswxdigit"];
+ Module["_memchr"] = _memchr = wasmExports2["memchr"];
+ Module["_strlen"] = _strlen = wasmExports2["strlen"];
+ Module["_strcmp"] = _strcmp = wasmExports2["strcmp"];
+ Module["_strncat"] = _strncat = wasmExports2["strncat"];
+ Module["_strncpy"] = _strncpy = wasmExports2["strncpy"];
+ Module["_towlower"] = _towlower = wasmExports2["towlower"];
+ Module["_towupper"] = _towupper = wasmExports2["towupper"];
+ _setThrew = wasmExports2["setThrew"];
+ __emscripten_stack_restore = wasmExports2["_emscripten_stack_restore"];
+ __emscripten_stack_alloc = wasmExports2["_emscripten_stack_alloc"];
+ _emscripten_stack_get_current = wasmExports2["emscripten_stack_get_current"];
+ ___wasm_apply_data_relocs = wasmExports2["__wasm_apply_data_relocs"];
+ }
+ __name(assignWasmExports, "assignWasmExports");
+ var wasmImports = {
+ /** @export */
+ __heap_base: ___heap_base,
+ /** @export */
+ __indirect_function_table: wasmTable,
+ /** @export */
+ __memory_base: ___memory_base,
+ /** @export */
+ __stack_high: ___stack_high,
+ /** @export */
+ __stack_low: ___stack_low,
+ /** @export */
+ __stack_pointer: ___stack_pointer,
+ /** @export */
+ __table_base: ___table_base,
+ /** @export */
+ _abort_js: __abort_js,
+ /** @export */
+ emscripten_resize_heap: _emscripten_resize_heap,
+ /** @export */
+ fd_close: _fd_close,
+ /** @export */
+ fd_seek: _fd_seek,
+ /** @export */
+ fd_write: _fd_write,
+ /** @export */
+ memory: wasmMemory,
+ /** @export */
+ tree_sitter_log_callback: _tree_sitter_log_callback,
+ /** @export */
+ tree_sitter_parse_callback: _tree_sitter_parse_callback,
+ /** @export */
+ tree_sitter_progress_callback: _tree_sitter_progress_callback,
+ /** @export */
+ tree_sitter_query_progress_callback: _tree_sitter_query_progress_callback
+ };
+ function callMain(args2 = []) {
+ var entryFunction = resolveGlobalSymbol("main").sym;
+ if (!entryFunction) return;
+ args2.unshift(thisProgram);
+ var argc = args2.length;
+ var argv = stackAlloc((argc + 1) * 4);
+ var argv_ptr = argv;
+ args2.forEach((arg) => {
+ LE_HEAP_STORE_U32((argv_ptr >> 2) * 4, stringToUTF8OnStack(arg));
+ argv_ptr += 4;
+ });
+ LE_HEAP_STORE_U32((argv_ptr >> 2) * 4, 0);
+ try {
+ var ret = entryFunction(argc, argv);
+ exitJS(
+ ret,
+ /* implicit = */
+ true
+ );
+ return ret;
+ } catch (e) {
+ return handleException(e);
+ }
+ }
+ __name(callMain, "callMain");
+ function run(args2 = arguments_) {
+ if (runDependencies > 0) {
+ dependenciesFulfilled = run;
+ return;
+ }
+ preRun();
+ if (runDependencies > 0) {
+ dependenciesFulfilled = run;
+ return;
+ }
+ function doRun() {
+ Module["calledRun"] = true;
+ if (ABORT) return;
+ initRuntime();
+ preMain();
+ readyPromiseResolve?.(Module);
+ Module["onRuntimeInitialized"]?.();
+ var noInitialRun = Module["noInitialRun"] || false;
+ if (!noInitialRun) callMain(args2);
+ postRun();
+ }
+ __name(doRun, "doRun");
+ if (Module["setStatus"]) {
+ Module["setStatus"]("Running...");
+ setTimeout(() => {
+ setTimeout(() => Module["setStatus"](""), 1);
+ doRun();
+ }, 1);
+ } else {
+ doRun();
+ }
+ }
+ __name(run, "run");
+ var wasmExports;
+ wasmExports = await createWasm();
+ run();
+ if (runtimeInitialized) {
+ moduleRtn = Module;
+ } else {
+ moduleRtn = new Promise((resolve, reject) => {
+ readyPromiseResolve = resolve;
+ readyPromiseReject = reject;
+ });
+ }
+ return moduleRtn;
+}
+__name(Module2, "Module");
+var web_tree_sitter_default = Module2;
+
+// src/bindings.ts
+var Module3 = null;
+async function initializeBinding(moduleOptions) {
+ return Module3 ??= await web_tree_sitter_default(moduleOptions);
+}
+__name(initializeBinding, "initializeBinding");
+function checkModule() {
+ return !!Module3;
+}
+__name(checkModule, "checkModule");
+
+// src/parser.ts
+var TRANSFER_BUFFER;
+var LANGUAGE_VERSION;
+var MIN_COMPATIBLE_VERSION;
+var Parser = class {
+ static {
+ __name(this, "Parser");
+ }
+ /** @internal */
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ [1] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ logCallback = null;
+ /** The parser's current language. */
+ language = null;
+ /**
+ * This must always be called before creating a Parser.
+ *
+ * You can optionally pass in options to configure the Wasm module, the most common
+ * one being `locateFile` to help the module find the `.wasm` file.
+ */
+ static async init(moduleOptions) {
+ setModule(await initializeBinding(moduleOptions));
+ TRANSFER_BUFFER = C._ts_init();
+ LANGUAGE_VERSION = C.getValue(TRANSFER_BUFFER, "i32");
+ MIN_COMPATIBLE_VERSION = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ }
+ /**
+ * Create a new parser.
+ */
+ constructor() {
+ this.initialize();
+ }
+ /** @internal */
+ initialize() {
+ if (!checkModule()) {
+ throw new Error("cannot construct a Parser before calling `init()`");
+ }
+ C._ts_parser_new_wasm();
+ this[0] = C.getValue(TRANSFER_BUFFER, "i32");
+ this[1] = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ }
+ /** Delete the parser, freeing its resources. */
+ delete() {
+ C._ts_parser_delete(this[0]);
+ C._free(this[1]);
+ this[0] = 0;
+ this[1] = 0;
+ }
+ /**
+ * Set the language that the parser should use for parsing.
+ *
+ * If the language was not successfully assigned, an error will be thrown.
+ * This happens if the language was generated with an incompatible
+ * version of the Tree-sitter CLI. Check the language's version using
+ * {@link Language#version} and compare it to this library's
+ * {@link LANGUAGE_VERSION} and {@link MIN_COMPATIBLE_VERSION} constants.
+ */
+ setLanguage(language) {
+ let address;
+ if (!language) {
+ address = 0;
+ this.language = null;
+ } else if (language.constructor === Language) {
+ address = language[0];
+ const version = C._ts_language_abi_version(address);
+ if (version < MIN_COMPATIBLE_VERSION || LANGUAGE_VERSION < version) {
+ throw new Error(
+ `Incompatible language version ${version}. Compatibility range ${MIN_COMPATIBLE_VERSION} through ${LANGUAGE_VERSION}.`
+ );
+ }
+ this.language = language;
+ } else {
+ throw new Error("Argument must be a Language");
+ }
+ C._ts_parser_set_language(this[0], address);
+ return this;
+ }
+ /**
+ * Parse a slice of UTF8 text.
+ *
+ * @param {string | ParseCallback} callback - The UTF8-encoded text to parse or a callback function.
+ *
+ * @param {Tree | null} [oldTree] - A previous syntax tree parsed from the same document. If the text of the
+ * document has changed since `oldTree` was created, then you must edit `oldTree` to match
+ * the new text using {@link Tree#edit}.
+ *
+ * @param {ParseOptions} [options] - Options for parsing the text.
+ * This can be used to set the included ranges, or a progress callback.
+ *
+ * @returns {Tree | null} A {@link Tree} if parsing succeeded, or `null` if:
+ * - The parser has not yet had a language assigned with {@link Parser#setLanguage}.
+ * - The progress callback returned true.
+ */
+ parse(callback, oldTree, options) {
+ if (typeof callback === "string") {
+ C.currentParseCallback = (index) => callback.slice(index);
+ } else if (typeof callback === "function") {
+ C.currentParseCallback = callback;
+ } else {
+ throw new Error("Argument must be a string or a function");
+ }
+ if (options?.progressCallback) {
+ C.currentProgressCallback = options.progressCallback;
+ } else {
+ C.currentProgressCallback = null;
+ }
+ if (this.logCallback) {
+ C.currentLogCallback = this.logCallback;
+ C._ts_parser_enable_logger_wasm(this[0], 1);
+ } else {
+ C.currentLogCallback = null;
+ C._ts_parser_enable_logger_wasm(this[0], 0);
+ }
+ let rangeCount = 0;
+ let rangeAddress = 0;
+ if (options?.includedRanges) {
+ rangeCount = options.includedRanges.length;
+ rangeAddress = C._calloc(rangeCount, SIZE_OF_RANGE);
+ let address = rangeAddress;
+ for (let i2 = 0; i2 < rangeCount; i2++) {
+ marshalRange(address, options.includedRanges[i2]);
+ address += SIZE_OF_RANGE;
+ }
+ }
+ const treeAddress = C._ts_parser_parse_wasm(
+ this[0],
+ this[1],
+ oldTree ? oldTree[0] : 0,
+ rangeAddress,
+ rangeCount
+ );
+ if (!treeAddress) {
+ C.currentParseCallback = null;
+ C.currentLogCallback = null;
+ C.currentProgressCallback = null;
+ return null;
+ }
+ if (!this.language) {
+ throw new Error("Parser must have a language to parse");
+ }
+ const result = new Tree(INTERNAL, treeAddress, this.language, C.currentParseCallback);
+ C.currentParseCallback = null;
+ C.currentLogCallback = null;
+ C.currentProgressCallback = null;
+ return result;
+ }
+ /**
+ * Instruct the parser to start the next parse from the beginning.
+ *
+ * If the parser previously failed because of a callback,
+ * then by default, it will resume where it left off on the
+ * next call to {@link Parser#parse} or other parsing functions.
+ * If you don't want to resume, and instead intend to use this parser to
+ * parse some other document, you must call `reset` first.
+ */
+ reset() {
+ C._ts_parser_reset(this[0]);
+ }
+ /** Get the ranges of text that the parser will include when parsing. */
+ getIncludedRanges() {
+ C._ts_parser_included_ranges_wasm(this[0]);
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const result = new Array(count);
+ if (count > 0) {
+ let address = buffer;
+ for (let i2 = 0; i2 < count; i2++) {
+ result[i2] = unmarshalRange(address);
+ address += SIZE_OF_RANGE;
+ }
+ C._free(buffer);
+ }
+ return result;
+ }
+ /** Set the logging callback that a parser should use during parsing. */
+ setLogger(callback) {
+ if (!callback) {
+ this.logCallback = null;
+ } else if (typeof callback !== "function") {
+ throw new Error("Logger callback must be a function");
+ } else {
+ this.logCallback = callback;
+ }
+ return this;
+ }
+ /** Get the parser's current logger. */
+ getLogger() {
+ return this.logCallback;
+ }
+};
+
+// src/query.ts
+var PREDICATE_STEP_TYPE_CAPTURE = 1;
+var PREDICATE_STEP_TYPE_STRING = 2;
+var QUERY_WORD_REGEX = /[\w-]+/g;
+var CaptureQuantifier = {
+ Zero: 0,
+ ZeroOrOne: 1,
+ ZeroOrMore: 2,
+ One: 3,
+ OneOrMore: 4
+};
+var isCaptureStep = /* @__PURE__ */ __name((step) => step.type === "capture", "isCaptureStep");
+var isStringStep = /* @__PURE__ */ __name((step) => step.type === "string", "isStringStep");
+var QueryErrorKind = {
+ Syntax: 1,
+ NodeName: 2,
+ FieldName: 3,
+ CaptureName: 4,
+ PatternStructure: 5
+};
+var QueryError = class _QueryError extends Error {
+ constructor(kind, info2, index, length) {
+ super(_QueryError.formatMessage(kind, info2));
+ this.kind = kind;
+ this.info = info2;
+ this.index = index;
+ this.length = length;
+ this.name = "QueryError";
+ }
+ static {
+ __name(this, "QueryError");
+ }
+ /** Formats an error message based on the error kind and info */
+ static formatMessage(kind, info2) {
+ switch (kind) {
+ case QueryErrorKind.NodeName:
+ return `Bad node name '${info2.word}'`;
+ case QueryErrorKind.FieldName:
+ return `Bad field name '${info2.word}'`;
+ case QueryErrorKind.CaptureName:
+ return `Bad capture name @${info2.word}`;
+ case QueryErrorKind.PatternStructure:
+ return `Bad pattern structure at offset ${info2.suffix}`;
+ case QueryErrorKind.Syntax:
+ return `Bad syntax at offset ${info2.suffix}`;
+ }
+ }
+};
+function parseAnyPredicate(steps, index, operator, textPredicates) {
+ if (steps.length !== 3) {
+ throw new Error(
+ `Wrong number of arguments to \`#${operator}\` predicate. Expected 2, got ${steps.length - 1}`
+ );
+ }
+ if (!isCaptureStep(steps[1])) {
+ throw new Error(
+ `First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}"`
+ );
+ }
+ const isPositive = operator === "eq?" || operator === "any-eq?";
+ const matchAll = !operator.startsWith("any-");
+ if (isCaptureStep(steps[2])) {
+ const captureName1 = steps[1].name;
+ const captureName2 = steps[2].name;
+ textPredicates[index].push((captures) => {
+ const nodes1 = [];
+ const nodes2 = [];
+ for (const c of captures) {
+ if (c.name === captureName1) nodes1.push(c.node);
+ if (c.name === captureName2) nodes2.push(c.node);
+ }
+ const compare = /* @__PURE__ */ __name((n1, n2, positive) => {
+ return positive ? n1.text === n2.text : n1.text !== n2.text;
+ }, "compare");
+ return matchAll ? nodes1.every((n1) => nodes2.some((n2) => compare(n1, n2, isPositive))) : nodes1.some((n1) => nodes2.some((n2) => compare(n1, n2, isPositive)));
+ });
+ } else {
+ const captureName = steps[1].name;
+ const stringValue = steps[2].value;
+ const matches = /* @__PURE__ */ __name((n) => n.text === stringValue, "matches");
+ const doesNotMatch = /* @__PURE__ */ __name((n) => n.text !== stringValue, "doesNotMatch");
+ textPredicates[index].push((captures) => {
+ const nodes = [];
+ for (const c of captures) {
+ if (c.name === captureName) nodes.push(c.node);
+ }
+ const test = isPositive ? matches : doesNotMatch;
+ return matchAll ? nodes.every(test) : nodes.some(test);
+ });
+ }
+}
+__name(parseAnyPredicate, "parseAnyPredicate");
+function parseMatchPredicate(steps, index, operator, textPredicates) {
+ if (steps.length !== 3) {
+ throw new Error(
+ `Wrong number of arguments to \`#${operator}\` predicate. Expected 2, got ${steps.length - 1}.`
+ );
+ }
+ if (steps[1].type !== "capture") {
+ throw new Error(
+ `First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
+ );
+ }
+ if (steps[2].type !== "string") {
+ throw new Error(
+ `Second argument of \`#${operator}\` predicate must be a string. Got @${steps[2].name}.`
+ );
+ }
+ const isPositive = operator === "match?" || operator === "any-match?";
+ const matchAll = !operator.startsWith("any-");
+ const captureName = steps[1].name;
+ const regex = new RegExp(steps[2].value);
+ textPredicates[index].push((captures) => {
+ const nodes = [];
+ for (const c of captures) {
+ if (c.name === captureName) nodes.push(c.node.text);
+ }
+ const test = /* @__PURE__ */ __name((text, positive) => {
+ return positive ? regex.test(text) : !regex.test(text);
+ }, "test");
+ if (nodes.length === 0) return !isPositive;
+ return matchAll ? nodes.every((text) => test(text, isPositive)) : nodes.some((text) => test(text, isPositive));
+ });
+}
+__name(parseMatchPredicate, "parseMatchPredicate");
+function parseAnyOfPredicate(steps, index, operator, textPredicates) {
+ if (steps.length < 2) {
+ throw new Error(
+ `Wrong number of arguments to \`#${operator}\` predicate. Expected at least 1. Got ${steps.length - 1}.`
+ );
+ }
+ if (steps[1].type !== "capture") {
+ throw new Error(
+ `First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
+ );
+ }
+ const isPositive = operator === "any-of?";
+ const captureName = steps[1].name;
+ const stringSteps = steps.slice(2);
+ if (!stringSteps.every(isStringStep)) {
+ throw new Error(
+ `Arguments to \`#${operator}\` predicate must be strings.".`
+ );
+ }
+ const values = stringSteps.map((s) => s.value);
+ textPredicates[index].push((captures) => {
+ const nodes = [];
+ for (const c of captures) {
+ if (c.name === captureName) nodes.push(c.node.text);
+ }
+ if (nodes.length === 0) return !isPositive;
+ return nodes.every((text) => values.includes(text)) === isPositive;
+ });
+}
+__name(parseAnyOfPredicate, "parseAnyOfPredicate");
+function parseIsPredicate(steps, index, operator, assertedProperties, refutedProperties) {
+ if (steps.length < 2 || steps.length > 3) {
+ throw new Error(
+ `Wrong number of arguments to \`#${operator}\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
+ );
+ }
+ if (!steps.every(isStringStep)) {
+ throw new Error(
+ `Arguments to \`#${operator}\` predicate must be strings.".`
+ );
+ }
+ const properties = operator === "is?" ? assertedProperties : refutedProperties;
+ if (!properties[index]) properties[index] = {};
+ properties[index][steps[1].value] = steps[2]?.value ?? null;
+}
+__name(parseIsPredicate, "parseIsPredicate");
+function parseSetDirective(steps, index, setProperties) {
+ if (steps.length < 2 || steps.length > 3) {
+ throw new Error(`Wrong number of arguments to \`#set!\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`);
+ }
+ if (!steps.every(isStringStep)) {
+ throw new Error(`Arguments to \`#set!\` predicate must be strings.".`);
+ }
+ if (!setProperties[index]) setProperties[index] = {};
+ setProperties[index][steps[1].value] = steps[2]?.value ?? null;
+}
+__name(parseSetDirective, "parseSetDirective");
+function parsePattern(index, stepType, stepValueId, captureNames, stringValues, steps, textPredicates, predicates, setProperties, assertedProperties, refutedProperties) {
+ if (stepType === PREDICATE_STEP_TYPE_CAPTURE) {
+ const name2 = captureNames[stepValueId];
+ steps.push({ type: "capture", name: name2 });
+ } else if (stepType === PREDICATE_STEP_TYPE_STRING) {
+ steps.push({ type: "string", value: stringValues[stepValueId] });
+ } else if (steps.length > 0) {
+ if (steps[0].type !== "string") {
+ throw new Error("Predicates must begin with a literal value");
+ }
+ const operator = steps[0].value;
+ switch (operator) {
+ case "any-not-eq?":
+ case "not-eq?":
+ case "any-eq?":
+ case "eq?":
+ parseAnyPredicate(steps, index, operator, textPredicates);
+ break;
+ case "any-not-match?":
+ case "not-match?":
+ case "any-match?":
+ case "match?":
+ parseMatchPredicate(steps, index, operator, textPredicates);
+ break;
+ case "not-any-of?":
+ case "any-of?":
+ parseAnyOfPredicate(steps, index, operator, textPredicates);
+ break;
+ case "is?":
+ case "is-not?":
+ parseIsPredicate(steps, index, operator, assertedProperties, refutedProperties);
+ break;
+ case "set!":
+ parseSetDirective(steps, index, setProperties);
+ break;
+ default:
+ predicates[index].push({ operator, operands: steps.slice(1) });
+ }
+ steps.length = 0;
+ }
+}
+__name(parsePattern, "parsePattern");
+var Query = class {
+ static {
+ __name(this, "Query");
+ }
+ /** @internal */
+ [0] = 0;
+ // Internal handle for Wasm
+ /** @internal */
+ exceededMatchLimit;
+ /** @internal */
+ textPredicates;
+ /** The names of the captures used in the query. */
+ captureNames;
+ /** The quantifiers of the captures used in the query. */
+ captureQuantifiers;
+ /**
+ * The other user-defined predicates associated with the given index.
+ *
+ * This includes predicates with operators other than:
+ * - `match?`
+ * - `eq?` and `not-eq?`
+ * - `any-of?` and `not-any-of?`
+ * - `is?` and `is-not?`
+ * - `set!`
+ */
+ predicates;
+ /** The properties for predicates with the operator `set!`. */
+ setProperties;
+ /** The properties for predicates with the operator `is?`. */
+ assertedProperties;
+ /** The properties for predicates with the operator `is-not?`. */
+ refutedProperties;
+ /** The maximum number of in-progress matches for this cursor. */
+ matchLimit;
+ /**
+ * Create a new query from a string containing one or more S-expression
+ * patterns.
+ *
+ * The query is associated with a particular language, and can only be run
+ * on syntax nodes parsed with that language. References to Queries can be
+ * shared between multiple threads.
+ *
+ * @link {@see https://tree-sitter.github.io/tree-sitter/using-parsers/queries}
+ */
+ constructor(language, source) {
+ const sourceLength = C.lengthBytesUTF8(source);
+ const sourceAddress = C._malloc(sourceLength + 1);
+ C.stringToUTF8(source, sourceAddress, sourceLength + 1);
+ const address = C._ts_query_new(
+ language[0],
+ sourceAddress,
+ sourceLength,
+ TRANSFER_BUFFER,
+ TRANSFER_BUFFER + SIZE_OF_INT
+ );
+ if (!address) {
+ const errorId = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const errorByte = C.getValue(TRANSFER_BUFFER, "i32");
+ const errorIndex = C.UTF8ToString(sourceAddress, errorByte).length;
+ const suffix = source.slice(errorIndex, errorIndex + 100).split("\n")[0];
+ const word = suffix.match(QUERY_WORD_REGEX)?.[0] ?? "";
+ C._free(sourceAddress);
+ switch (errorId) {
+ case QueryErrorKind.Syntax:
+ throw new QueryError(QueryErrorKind.Syntax, { suffix: `${errorIndex}: '${suffix}'...` }, errorIndex, 0);
+ case QueryErrorKind.NodeName:
+ throw new QueryError(errorId, { word }, errorIndex, word.length);
+ case QueryErrorKind.FieldName:
+ throw new QueryError(errorId, { word }, errorIndex, word.length);
+ case QueryErrorKind.CaptureName:
+ throw new QueryError(errorId, { word }, errorIndex, word.length);
+ case QueryErrorKind.PatternStructure:
+ throw new QueryError(errorId, { suffix: `${errorIndex}: '${suffix}'...` }, errorIndex, 0);
+ }
+ }
+ const stringCount = C._ts_query_string_count(address);
+ const captureCount = C._ts_query_capture_count(address);
+ const patternCount = C._ts_query_pattern_count(address);
+ const captureNames = new Array(captureCount);
+ const captureQuantifiers = new Array(patternCount);
+ const stringValues = new Array(stringCount);
+ for (let i2 = 0; i2 < captureCount; i2++) {
+ const nameAddress = C._ts_query_capture_name_for_id(
+ address,
+ i2,
+ TRANSFER_BUFFER
+ );
+ const nameLength = C.getValue(TRANSFER_BUFFER, "i32");
+ captureNames[i2] = C.UTF8ToString(nameAddress, nameLength);
+ }
+ for (let i2 = 0; i2 < patternCount; i2++) {
+ const captureQuantifiersArray = new Array(captureCount);
+ for (let j = 0; j < captureCount; j++) {
+ const quantifier = C._ts_query_capture_quantifier_for_id(address, i2, j);
+ captureQuantifiersArray[j] = quantifier;
+ }
+ captureQuantifiers[i2] = captureQuantifiersArray;
+ }
+ for (let i2 = 0; i2 < stringCount; i2++) {
+ const valueAddress = C._ts_query_string_value_for_id(
+ address,
+ i2,
+ TRANSFER_BUFFER
+ );
+ const nameLength = C.getValue(TRANSFER_BUFFER, "i32");
+ stringValues[i2] = C.UTF8ToString(valueAddress, nameLength);
+ }
+ const setProperties = new Array(patternCount);
+ const assertedProperties = new Array(patternCount);
+ const refutedProperties = new Array(patternCount);
+ const predicates = new Array(patternCount);
+ const textPredicates = new Array(patternCount);
+ for (let i2 = 0; i2 < patternCount; i2++) {
+ const predicatesAddress = C._ts_query_predicates_for_pattern(address, i2, TRANSFER_BUFFER);
+ const stepCount = C.getValue(TRANSFER_BUFFER, "i32");
+ predicates[i2] = [];
+ textPredicates[i2] = [];
+ const steps = new Array();
+ let stepAddress = predicatesAddress;
+ for (let j = 0; j < stepCount; j++) {
+ const stepType = C.getValue(stepAddress, "i32");
+ stepAddress += SIZE_OF_INT;
+ const stepValueId = C.getValue(stepAddress, "i32");
+ stepAddress += SIZE_OF_INT;
+ parsePattern(
+ i2,
+ stepType,
+ stepValueId,
+ captureNames,
+ stringValues,
+ steps,
+ textPredicates,
+ predicates,
+ setProperties,
+ assertedProperties,
+ refutedProperties
+ );
+ }
+ Object.freeze(textPredicates[i2]);
+ Object.freeze(predicates[i2]);
+ Object.freeze(setProperties[i2]);
+ Object.freeze(assertedProperties[i2]);
+ Object.freeze(refutedProperties[i2]);
+ }
+ C._free(sourceAddress);
+ this[0] = address;
+ this.captureNames = captureNames;
+ this.captureQuantifiers = captureQuantifiers;
+ this.textPredicates = textPredicates;
+ this.predicates = predicates;
+ this.setProperties = setProperties;
+ this.assertedProperties = assertedProperties;
+ this.refutedProperties = refutedProperties;
+ this.exceededMatchLimit = false;
+ }
+ /** Delete the query, freeing its resources. */
+ delete() {
+ C._ts_query_delete(this[0]);
+ this[0] = 0;
+ }
+ /**
+ * Iterate over all of the matches in the order that they were found.
+ *
+ * Each match contains the index of the pattern that matched, and a list of
+ * captures. Because multiple patterns can match the same set of nodes,
+ * one match may contain captures that appear *before* some of the
+ * captures from a previous match.
+ *
+ * @param {Node} node - The node to execute the query on.
+ *
+ * @param {QueryOptions} options - Options for query execution.
+ */
+ matches(node, options = {}) {
+ const startPosition = options.startPosition ?? ZERO_POINT;
+ const endPosition = options.endPosition ?? ZERO_POINT;
+ const startIndex = options.startIndex ?? 0;
+ const endIndex = options.endIndex ?? 0;
+ const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;
+ const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;
+ const startContainingIndex = options.startContainingIndex ?? 0;
+ const endContainingIndex = options.endContainingIndex ?? 0;
+ const matchLimit = options.matchLimit ?? 4294967295;
+ const maxStartDepth = options.maxStartDepth ?? 4294967295;
+ const progressCallback = options.progressCallback;
+ if (typeof matchLimit !== "number") {
+ throw new Error("Arguments must be numbers");
+ }
+ this.matchLimit = matchLimit;
+ if (endIndex !== 0 && startIndex > endIndex) {
+ throw new Error("`startIndex` cannot be greater than `endIndex`");
+ }
+ if (endPosition !== ZERO_POINT && (startPosition.row > endPosition.row || startPosition.row === endPosition.row && startPosition.column > endPosition.column)) {
+ throw new Error("`startPosition` cannot be greater than `endPosition`");
+ }
+ if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {
+ throw new Error("`startContainingIndex` cannot be greater than `endContainingIndex`");
+ }
+ if (endContainingPosition !== ZERO_POINT && (startContainingPosition.row > endContainingPosition.row || startContainingPosition.row === endContainingPosition.row && startContainingPosition.column > endContainingPosition.column)) {
+ throw new Error("`startContainingPosition` cannot be greater than `endContainingPosition`");
+ }
+ if (progressCallback) {
+ C.currentQueryProgressCallback = progressCallback;
+ }
+ marshalNode(node);
+ C._ts_query_matches_wasm(
+ this[0],
+ node.tree[0],
+ startPosition.row,
+ startPosition.column,
+ endPosition.row,
+ endPosition.column,
+ startIndex,
+ endIndex,
+ startContainingPosition.row,
+ startContainingPosition.column,
+ endContainingPosition.row,
+ endContainingPosition.column,
+ startContainingIndex,
+ endContainingIndex,
+ matchLimit,
+ maxStartDepth
+ );
+ const rawCount = C.getValue(TRANSFER_BUFFER, "i32");
+ const startAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const didExceedMatchLimit = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, "i32");
+ const result = new Array(rawCount);
+ this.exceededMatchLimit = Boolean(didExceedMatchLimit);
+ let filteredCount = 0;
+ let address = startAddress;
+ for (let i2 = 0; i2 < rawCount; i2++) {
+ const patternIndex = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const captureCount = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const captures = new Array(captureCount);
+ address = unmarshalCaptures(this, node.tree, address, patternIndex, captures);
+ if (this.textPredicates[patternIndex].every((p) => p(captures))) {
+ result[filteredCount] = { patternIndex, captures };
+ const setProperties = this.setProperties[patternIndex];
+ result[filteredCount].setProperties = setProperties;
+ const assertedProperties = this.assertedProperties[patternIndex];
+ result[filteredCount].assertedProperties = assertedProperties;
+ const refutedProperties = this.refutedProperties[patternIndex];
+ result[filteredCount].refutedProperties = refutedProperties;
+ filteredCount++;
+ }
+ }
+ result.length = filteredCount;
+ C._free(startAddress);
+ C.currentQueryProgressCallback = null;
+ return result;
+ }
+ /**
+ * Iterate over all of the individual captures in the order that they
+ * appear.
+ *
+ * This is useful if you don't care about which pattern matched, and just
+ * want a single, ordered sequence of captures.
+ *
+ * @param {Node} node - The node to execute the query on.
+ *
+ * @param {QueryOptions} options - Options for query execution.
+ */
+ captures(node, options = {}) {
+ const startPosition = options.startPosition ?? ZERO_POINT;
+ const endPosition = options.endPosition ?? ZERO_POINT;
+ const startIndex = options.startIndex ?? 0;
+ const endIndex = options.endIndex ?? 0;
+ const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;
+ const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;
+ const startContainingIndex = options.startContainingIndex ?? 0;
+ const endContainingIndex = options.endContainingIndex ?? 0;
+ const matchLimit = options.matchLimit ?? 4294967295;
+ const maxStartDepth = options.maxStartDepth ?? 4294967295;
+ const progressCallback = options.progressCallback;
+ if (typeof matchLimit !== "number") {
+ throw new Error("Arguments must be numbers");
+ }
+ this.matchLimit = matchLimit;
+ if (endIndex !== 0 && startIndex > endIndex) {
+ throw new Error("`startIndex` cannot be greater than `endIndex`");
+ }
+ if (endPosition !== ZERO_POINT && (startPosition.row > endPosition.row || startPosition.row === endPosition.row && startPosition.column > endPosition.column)) {
+ throw new Error("`startPosition` cannot be greater than `endPosition`");
+ }
+ if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {
+ throw new Error("`startContainingIndex` cannot be greater than `endContainingIndex`");
+ }
+ if (endContainingPosition !== ZERO_POINT && (startContainingPosition.row > endContainingPosition.row || startContainingPosition.row === endContainingPosition.row && startContainingPosition.column > endContainingPosition.column)) {
+ throw new Error("`startContainingPosition` cannot be greater than `endContainingPosition`");
+ }
+ if (progressCallback) {
+ C.currentQueryProgressCallback = progressCallback;
+ }
+ marshalNode(node);
+ C._ts_query_captures_wasm(
+ this[0],
+ node.tree[0],
+ startPosition.row,
+ startPosition.column,
+ endPosition.row,
+ endPosition.column,
+ startIndex,
+ endIndex,
+ startContainingPosition.row,
+ startContainingPosition.column,
+ endContainingPosition.row,
+ endContainingPosition.column,
+ startContainingIndex,
+ endContainingIndex,
+ matchLimit,
+ maxStartDepth
+ );
+ const count = C.getValue(TRANSFER_BUFFER, "i32");
+ const startAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
+ const didExceedMatchLimit = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, "i32");
+ const result = new Array();
+ this.exceededMatchLimit = Boolean(didExceedMatchLimit);
+ const captures = new Array();
+ let address = startAddress;
+ for (let i2 = 0; i2 < count; i2++) {
+ const patternIndex = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const captureCount = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ const captureIndex = C.getValue(address, "i32");
+ address += SIZE_OF_INT;
+ captures.length = captureCount;
+ address = unmarshalCaptures(this, node.tree, address, patternIndex, captures);
+ if (this.textPredicates[patternIndex].every((p) => p(captures))) {
+ const capture = captures[captureIndex];
+ const setProperties = this.setProperties[patternIndex];
+ capture.setProperties = setProperties;
+ const assertedProperties = this.assertedProperties[patternIndex];
+ capture.assertedProperties = assertedProperties;
+ const refutedProperties = this.refutedProperties[patternIndex];
+ capture.refutedProperties = refutedProperties;
+ result.push(capture);
+ }
+ }
+ C._free(startAddress);
+ C.currentQueryProgressCallback = null;
+ return result;
+ }
+ /** Get the predicates for a given pattern. */
+ predicatesForPattern(patternIndex) {
+ return this.predicates[patternIndex];
+ }
+ /**
+ * Disable a certain capture within a query.
+ *
+ * This prevents the capture from being returned in matches, and also
+ * avoids any resource usage associated with recording the capture.
+ */
+ disableCapture(captureName) {
+ const captureNameLength = C.lengthBytesUTF8(captureName);
+ const captureNameAddress = C._malloc(captureNameLength + 1);
+ C.stringToUTF8(captureName, captureNameAddress, captureNameLength + 1);
+ C._ts_query_disable_capture(this[0], captureNameAddress, captureNameLength);
+ C._free(captureNameAddress);
+ }
+ /**
+ * Disable a certain pattern within a query.
+ *
+ * This prevents the pattern from matching, and also avoids any resource
+ * usage associated with the pattern. This throws an error if the pattern
+ * index is out of bounds.
+ */
+ disablePattern(patternIndex) {
+ if (patternIndex >= this.predicates.length) {
+ throw new Error(
+ `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`
+ );
+ }
+ C._ts_query_disable_pattern(this[0], patternIndex);
+ }
+ /**
+ * Check if, on its last execution, this cursor exceeded its maximum number
+ * of in-progress matches.
+ */
+ didExceedMatchLimit() {
+ return this.exceededMatchLimit;
+ }
+ /** Get the byte offset where the given pattern starts in the query's source. */
+ startIndexForPattern(patternIndex) {
+ if (patternIndex >= this.predicates.length) {
+ throw new Error(
+ `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`
+ );
+ }
+ return C._ts_query_start_byte_for_pattern(this[0], patternIndex);
+ }
+ /** Get the byte offset where the given pattern ends in the query's source. */
+ endIndexForPattern(patternIndex) {
+ if (patternIndex >= this.predicates.length) {
+ throw new Error(
+ `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`
+ );
+ }
+ return C._ts_query_end_byte_for_pattern(this[0], patternIndex);
+ }
+ /** Get the number of patterns in the query. */
+ patternCount() {
+ return C._ts_query_pattern_count(this[0]);
+ }
+ /** Get the index for a given capture name. */
+ captureIndexForName(captureName) {
+ return this.captureNames.indexOf(captureName);
+ }
+ /** Check if a given pattern within a query has a single root node. */
+ isPatternRooted(patternIndex) {
+ return C._ts_query_is_pattern_rooted(this[0], patternIndex) === 1;
+ }
+ /** Check if a given pattern within a query has a single root node. */
+ isPatternNonLocal(patternIndex) {
+ return C._ts_query_is_pattern_non_local(this[0], patternIndex) === 1;
+ }
+ /**
+ * Check if a given step in a query is 'definite'.
+ *
+ * A query step is 'definite' if its parent pattern will be guaranteed to
+ * match successfully once it reaches the step.
+ */
+ isPatternGuaranteedAtStep(byteIndex) {
+ return C._ts_query_is_pattern_guaranteed_at_step(this[0], byteIndex) === 1;
+ }
+};
+
+// export {
+// CaptureQuantifier,
+// Edit,
+// LANGUAGE_VERSION,
+// Language,
+// LookaheadIterator,
+// MIN_COMPATIBLE_VERSION,
+// Node,
+// Parser,
+// Query,
+// Tree,
+// TreeCursor
+// };
diff --git a/mixly/common/modules/mixly-modules/workers/common/tree-sitter/web-tree-sitter.wasm b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/web-tree-sitter.wasm
new file mode 100644
index 00000000..30ec4312
Binary files /dev/null and b/mixly/common/modules/mixly-modules/workers/common/tree-sitter/web-tree-sitter.wasm differ
diff --git a/mixly/common/modules/mixly-modules/workers/nodejs/node-file-watcher.js b/mixly/common/modules/mixly-modules/workers/nodejs/node-file-watcher.js
new file mode 100644
index 00000000..fdeafe49
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/nodejs/node-file-watcher.js
@@ -0,0 +1,66 @@
+const chokidar = require('chokidar');
+
+let watchedPath = {};
+
+const watch = function(inPath) {
+ if (watchedPath[watchedPath]) {
+ return;
+ }
+ watchedPath[inPath] = chokidar.watch(inPath, {
+ persistent: true,
+ depth: 0,
+ ignoreInitial: true
+ });
+
+ watchedPath[inPath].on('add', (actionPath, stats) => {
+ self.postMessage({
+ watcher: inPath,
+ event: 'add',
+ path: actionPath,
+ stats
+ });
+ });
+
+ watchedPath[inPath].on('addDir', (actionPath, stats) => {
+ self.postMessage({
+ watcher: inPath,
+ event: 'addDir',
+ path: actionPath,
+ stats
+ });
+ });
+
+ watchedPath[inPath].on('unlink', (actionPath, stats) => {
+ self.postMessage({
+ watcher: inPath,
+ event: 'unlink',
+ path: actionPath,
+ stats
+ });
+ });
+
+ watchedPath[inPath].on('unlinkDir', (actionPath, stats) => {
+ self.postMessage({
+ watcher: inPath,
+ event: 'unlinkDir',
+ path: actionPath,
+ stats
+ });
+ });
+}
+
+const unwatch = function(inPath) {
+ if (!watchedPath[inPath]) {
+ return;
+ }
+ watchedPath[inPath].close();
+ delete watchedPath[inPath];
+}
+
+self.addEventListener('message', function(event) {
+ if (event.data.func === 'watch') {
+ watch(...event.data.args);
+ } else if (event.data.func === 'unwatch') {
+ unwatch(...event.data.args);
+ }
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/nodejs/node-serial-worker.js b/mixly/common/modules/mixly-modules/workers/nodejs/node-serial-worker.js
new file mode 100644
index 00000000..1b377e8a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/nodejs/node-serial-worker.js
@@ -0,0 +1,91 @@
+importScripts('../common/serial-worker.js');
+const child_process = require('node:child_process');
+const serialport = require('serialport');
+
+const {
+ SerialPort,
+ ReadlineParser,
+ ByteLengthParser
+} = serialport;
+
+const portsOperator = {};
+
+class NodeSerialWorker extends SerialWorker {
+ #serialport_ = null;
+ #parserBytes_ = null;
+ constructor(port) {
+ super(port);
+ }
+
+ #addEventsListener_() {
+ this.#parserBytes_.on('data', (buffer) => {
+ this.onBuffer(buffer);
+ });
+
+ this.#serialport_.on('error', (error) => {
+ this.onError(error);
+ this.onClose(1);
+ });
+
+ this.#serialport_.on('open', () => {
+ this.onOpen();
+ });
+
+ this.#serialport_.on('close', () => {
+ this.onClose(1);
+ });
+ }
+
+ open() {
+ super.open();
+ this.#serialport_ = new SerialPort({
+ path: this.getPortName(),
+ baudRate: this.getBaudRate() - 0, // 波特率
+ dataBits: 8, // 数据位
+ parity: 'none', // 奇偶校验
+ stopBits: 1, // 停止位
+ flowControl: false,
+ autoOpen: false // 不自动打开
+ }, false);
+ this.#parserBytes_ = this.#serialport_.pipe(new ByteLengthParser({ length: 1 }));
+ this.#serialport_.open((error) => {
+ if (error) {
+ this.onError(error);
+ // this.onClose(1);
+ }
+ });
+ this.#addEventsListener_();
+ }
+
+ close() {
+ super.close();
+ if (this.isOpened()) {
+ try {
+ this.#serialport_.close();
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ }
+
+ onBuffer(buffer) {
+ super.onBuffer(buffer);
+ }
+}
+
+const create = (port) => {
+ if (!portsOperator[port]) {
+ portsOperator[port] = new NodeSerialWorker(port);
+ }
+ portsOperator[port].open();
+}
+
+self.addEventListener('message', function(event) {
+ console.log(event.data);
+ const { port, type } = event.data;
+ if (type === 'open') {
+ create(port);
+ } else if (type === 'close') {
+ portsOperator[port] && portsOperator[port].close();
+ }
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/web/file-system-access.js b/mixly/common/modules/mixly-modules/workers/web/file-system-access.js
new file mode 100644
index 00000000..b3641c0a
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/web/file-system-access.js
@@ -0,0 +1,132 @@
+importScripts('../../../web-modules/workerpool.min.js');
+importScripts('../../../web-modules/browserfs.min.js');
+
+let fs = BrowserFS.fs;
+
+const createPromise = function(func, ...args) {
+ return new Promise((resolve, reject) => {
+ func(...args, function() {
+ resolve([...arguments]);
+ });
+ });
+}
+
+const addFileSystemHandler = function(filesystem) {
+ return new Promise((resolve, reject) => {
+ BrowserFS.configure({
+ fs: "FileSystemAccess",
+ options: { handle: filesystem }
+ }, function (error) {
+ if (error) {
+ reject(error);
+ return;
+ }
+ fs = BrowserFS.fs;
+ resolve();
+ });
+ });
+}
+
+const rename = function(oldPath, newPath) {
+ return createPromise(fs.rename, oldPath, newPath);
+}
+
+const stat = function(p) {
+ return createPromise(fs.stat, p);
+}
+
+const open = function(p, flag, mode) {
+ return createPromise(fs.open, p, flag, mode);
+}
+
+const unlink = function(p) {
+ return createPromise(fs.unlink, p);
+}
+
+const rmdir = function(p) {
+ return createPromise(fs.rmdir, p);
+}
+
+const mkdir = function(p, mode) {
+ return createPromise(fs.mkdir, p, mode);
+}
+
+const readdir = function(p) {
+ return createPromise(fs.readdir, p);
+}
+
+const exists = function(p) {
+ return createPromise(fs.exists, p);
+}
+
+const realpath = function(p) {
+ return createPromise(fs.realpath, p);
+}
+
+const truncate = function(p, len) {
+ return createPromise(fs.truncate, p, len);
+}
+
+const readFile = function(fname, encoding, flag) {
+ return createPromise(fs.readFile, fname, encoding);
+}
+
+const writeFile = function(fname, data, encoding) {
+ return createPromise(fs.writeFile, fname, data, encoding);
+}
+
+const appendFile = function(fname, data, encoding) {
+ return createPromise(fs.appendFile, fname, data, encoding);
+}
+
+const chmod = function(p, mode) {
+ return createPromise(fs.chmod, p, mode);
+}
+
+const chown = function(p, new_uid, new_gid) {
+ return createPromise(fs.chown, p, new_uid, new_gid);
+}
+
+const utimes = function(p, atime, mtime) {
+ return createPromise(fs.utimes, p, atime, mtime);
+}
+
+const link = function(srcpath, dstpath) {
+ return createPromise(fs.link, srcpath, dstpath);
+}
+
+const symlink = function(srcpath, dstpath, type) {
+ return createPromise(fs.symlink, srcpath, dstpath, type);
+}
+
+const readlink = function(p) {
+ return createPromise(fs.readlink, p);
+}
+
+const syncClose = function(method, fd) {
+ return fs.syncClose(method, fd);
+}
+
+workerpool.worker({
+ addFileSystemHandler,
+ rename,
+ stat,
+ open,
+ unlink,
+ rmdir,
+ mkdir,
+ readdir,
+ exists,
+ realpath,
+ truncate,
+ readFile,
+ writeFile,
+ appendFile,
+ chmod,
+ chown,
+ utimes,
+ link,
+ symlink,
+ readlink,
+ syncClose
+});
\ No newline at end of file
diff --git a/mixly/common/modules/mixly-modules/workers/web/serial-worker.js b/mixly/common/modules/mixly-modules/workers/web/serial-worker.js
new file mode 100644
index 00000000..a9680b66
--- /dev/null
+++ b/mixly/common/modules/mixly-modules/workers/web/serial-worker.js
@@ -0,0 +1,123 @@
+// importScripts('../../web-modules/workerpool.min.js');
+const workerpool = require('../../web-modules/workerpool.min.js');
+
+const encoder = new TextEncoder();
+const decoder = new TextDecoder('utf-8');
+
+class SerialWorker {
+ constructor(serial) {
+ this.serial = serial;
+ this.receiveBuffer = [];
+ this.receiveStr = '';
+ this.bufferLength = 0;
+ const test = setInterval(() => {
+ const message = generateRandomString(5);
+ this.onData(encoder.encode(message));
+ }, 1000);
+ setTimeout(() => {
+ clearInterval(test);
+ }, 120 * 1000);
+ }
+
+ onOpen() {
+
+ }
+
+ onData(data) {
+ /* UTF-8编码方式
+ * ------------------------------------------------------------
+ * |1字节 0xxxxxxx |
+ * |2字节 110xxxxx 10xxxxxx |
+ * |3字节 1110xxxx 10xxxxxx 10xxxxxx |
+ * |4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
+ * |6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx|
+ * ------------------------------------------------------------
+ */
+ for (let i in data) {
+ if ((data[i] & 0x80) === 0x00) {
+ // 1字节
+ this.receiveBuffer = [];
+ this.bufferLength = 0;
+ this.receiveStr += String.fromCharCode(data[i]);
+ } else if ((data[i] & 0xc0) === 0x80) {
+ // 2字节以上的中间字节,10xxxxxx
+ // 如果没有起始头,则丢弃这个字节
+ if (!this.receiveBuffer.length) {
+ return;
+ }
+ // 如果不是2字节及以上的起始头,则丢弃这个字节
+ if (this.bufferLength < 2) {
+ return;
+ }
+ this.receiveBuffer.push(data[i]);
+ if (this.bufferLength === this.receiveBuffer.length) {
+ this.receiveStr += decoder.decode(new Uint8Array(this.receiveBuffer));
+ this.receiveBuffer = [];
+ }
+ } else {
+ // 2字节以上的起始头
+ if (this.receiveBuffer) {
+ this.receiveBuffer = [];
+ }
+ this.bufferLength = this.#getBufferLength(data[i]);
+ this.receiveBuffer.push(data[i]);
+ }
+ }
+ }
+
+ onError() {
+
+ }
+
+ onClose() {
+
+ }
+
+ #getBufferLength(data) {
+ let len = 2;
+ if ((data & 0xFC) === 0xFC) {
+ len = 6;
+ } else if ((data & 0xF8) === 0xF8) {
+ len = 5;
+ } else if ((data & 0xF0) === 0xF0) {
+ len = 4;
+ } else if ((data & 0xE0) === 0xE0) {
+ len = 3;
+ } else if ((data & 0xC0) === 0xC0) {
+ len = 2;
+ }
+ return len;
+ }
+}
+
+const createSerialWork = function(serial) {
+ return new Promise((resolve, reject) => {
+ console.log(serial)
+ const serialWork = new SerialWorker(serial);
+ const test = setInterval(() => {
+ const data = serialWork.receiveStr;
+ serialWork.receiveStr = '';
+ workerpool.workerEmit({
+ status: 'data',
+ data: data
+ });
+ }, 5000);
+ setTimeout(() => {
+ workerpool.workerEmit({
+ status: 'close'
+ });
+ resolve();
+ }, 120 * 1000);
+ });
+}
+
+function generateRandomString() {
+ return '1234';
+}
+
+
+workerpool.worker({
+ createSerialWork,
+ generateRandomString
+});
\ No newline at end of file
diff --git a/mixly/common/modules/web-modules/Blob.js b/mixly/common/modules/web-modules/Blob.js
new file mode 100644
index 00000000..030264a8
--- /dev/null
+++ b/mixly/common/modules/web-modules/Blob.js
@@ -0,0 +1,177 @@
+/* js/Blob.js
+ * A Blob implementation.
+ * 2013-01-23
+ *
+ * By Eli Grey, http://eligrey.com
+ * By Devin Samarin, https://github.com/eboyjr
+ * License: X11/MIT
+ * See LICENSE.md
+ */
+
+/*global self, unescape */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+ plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/js/Blob.js/blob/master/js/Blob.js */
+
+if (typeof Blob !== "function")
+var Blob = (function (view) {
+ "use strict";
+
+ var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || view.MSBlobBuilder || (function(view) {
+ var
+ get_class = function(object) {
+ return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
+ }
+ , FakeBlobBuilder = function BlobBuilder() {
+ this.data = [];
+ }
+ , FakeBlob = function Blob(data, type, encoding) {
+ this.data = data;
+ this.size = data.length;
+ this.type = type;
+ this.encoding = encoding;
+ }
+ , FBB_proto = FakeBlobBuilder.prototype
+ , FB_proto = FakeBlob.prototype
+ , FileReaderSync = view.FileReaderSync
+ , FileException = function(type) {
+ this.code = this[this.name = type];
+ }
+ , file_ex_codes = (
+ "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+ + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
+ ).split(" ")
+ , file_ex_code = file_ex_codes.length
+ , real_URL = view.URL || view.webkitURL || view
+ , real_create_object_URL = real_URL.createObjectURL
+ , real_revoke_object_URL = real_URL.revokeObjectURL
+ , URL = real_URL
+ , btoa = view.btoa
+ , atob = view.atob
+ , can_apply_typed_arrays = false
+ , can_apply_typed_arrays_test = function(pass) {
+ can_apply_typed_arrays = !pass;
+ }
+
+ , ArrayBuffer = view.ArrayBuffer
+ , Uint8Array = view.Uint8Array
+ ;
+ FakeBlob.fake = FB_proto.fake = true;
+ while (file_ex_code--) {
+ FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
+ }
+ try {
+ if (Uint8Array) {
+ can_apply_typed_arrays_test.apply(0, new Uint8Array(1));
+ }
+ } catch (ex) {}
+ if (!real_URL.createObjectURL) {
+ URL = view.URL = {};
+ }
+ URL.createObjectURL = function(blob) {
+ var
+ type = blob.type
+ , data_URI_header
+ ;
+ if (type === null) {
+ type = "application/octet-stream";
+ }
+ if (blob instanceof FakeBlob) {
+ data_URI_header = "data:" + type;
+ if (blob.encoding === "base64") {
+ return data_URI_header + ";base64," + blob.data;
+ } else if (blob.encoding === "URI") {
+ return data_URI_header + "," + decodeURIComponent(blob.data);
+ } if (btoa) {
+ return data_URI_header + ";base64," + btoa(blob.data);
+ } else {
+ return data_URI_header + "," + encodeURIComponent(blob.data);
+ }
+ } else if (real_create_object_URL) {
+ return real_create_object_URL.call(real_URL, blob);
+ }
+ };
+ URL.revokeObjectURL = function(object_URL) {
+ if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
+ real_revoke_object_URL.call(real_URL, object_URL);
+ }
+ };
+ FBB_proto.append = function(data/*, endings*/) {
+ var bb = this.data;
+ // decode data to a binary string
+ if (Uint8Array && data instanceof ArrayBuffer) {
+ if (can_apply_typed_arrays) {
+ bb.push(String.fromCharCode.apply(String, new Uint8Array(data)));
+ } else {
+ var
+ str = ""
+ , buf = new Uint8Array(data)
+ , i = 0
+ , buf_len = buf.length
+ ;
+ for (; i < buf_len; i++) {
+ str += String.fromCharCode(buf[i]);
+ }
+ }
+ } else if (get_class(data) === "Blob" || get_class(data) === "File") {
+ if (FileReaderSync) {
+ var fr = new FileReaderSync;
+ bb.push(fr.readAsBinaryString(data));
+ } else {
+ // async FileReader won't work as BlobBuilder is sync
+ throw new FileException("NOT_READABLE_ERR");
+ }
+ } else if (data instanceof FakeBlob) {
+ if (data.encoding === "base64" && atob) {
+ bb.push(atob(data.data));
+ } else if (data.encoding === "URI") {
+ bb.push(decodeURIComponent(data.data));
+ } else if (data.encoding === "raw") {
+ bb.push(data.data);
+ }
+ } else {
+ if (typeof data !== "string") {
+ data += ""; // convert unsupported types to strings
+ }
+ // decode UTF-16 to binary string
+ bb.push(unescape(encodeURIComponent(data)));
+ }
+ };
+ FBB_proto.getBlob = function(type) {
+ if (!arguments.length) {
+ type = null;
+ }
+ return new FakeBlob(this.data.join(""), type, "raw");
+ };
+ FBB_proto.toString = function() {
+ return "[object BlobBuilder]";
+ };
+ FB_proto.slice = function(start, end, type) {
+ var args = arguments.length;
+ if (args < 3) {
+ type = null;
+ }
+ return new FakeBlob(
+ this.data.slice(start, args > 1 ? end : this.data.length)
+ , type
+ , this.encoding
+ );
+ };
+ FB_proto.toString = function() {
+ return "[object Blob]";
+ };
+ return FakeBlobBuilder;
+ }(view));
+
+ return function Blob(blobParts, options) {
+ var type = options ? (options.type || "") : "";
+ var builder = new BlobBuilder();
+ if (blobParts) {
+ for (var i = 0, len = blobParts.length; i < len; i++) {
+ builder.append(blobParts[i]);
+ }
+ }
+ return builder.getBlob(type);
+ };
+}(self));
diff --git a/mixly/common/modules/web-modules/FileSaver.min.js b/mixly/common/modules/web-modules/FileSaver.min.js
new file mode 100644
index 00000000..b9f98def
--- /dev/null
+++ b/mixly/common/modules/web-modules/FileSaver.min.js
@@ -0,0 +1,2 @@
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
+var saveAs=saveAs||(function(h){"use strict";var r=h.document,l=function(){return h.URL||h.webkitURL||h},e=h.URL||h.webkitURL||h,n=r.createElementNS("http://www.w3.org/1999/xhtml","a"),g="download" in n,j=function(t){var s=r.createEvent("MouseEvents");s.initMouseEvent("click",true,false,h,0,0,0,0,0,false,false,false,false,0,null);return t.dispatchEvent(s)},o=h.webkitRequestFileSystem,p=h.requestFileSystem||o||h.mozRequestFileSystem,m=function(s){(h.setImmediate||h.setTimeout)(function(){throw s},0)},c="application/octet-stream",k=0,b=[],i=function(){var t=b.length;while(t--){var s=b[t];if(typeof s==="string"){e.revokeObjectURL(s)}else{s.remove()}}b.length=0},q=function(t,s,w){s=[].concat(s);var v=s.length;while(v--){var x=t["on"+s[v]];if(typeof x==="function"){try{x.call(t,w||t)}catch(u){m(u)}}}},f=function(t,u){var v=this,B=t.type,E=false,x,w,s=function(){var F=l().createObjectURL(t);b.push(F);return F},A=function(){q(v,"writestart progress write writeend".split(" "))},D=function(){if(E||!x){x=s(t)}w.location.href=x;v.readyState=v.DONE;A()},z=function(F){return function(){if(v.readyState!==v.DONE){return F.apply(this,arguments)}}},y={create:true,exclusive:false},C;v.readyState=v.INIT;if(!u){u="download"}if(g){x=s(t);n.href=x;n.download=u;if(j(n)){v.readyState=v.DONE;A();return}}if(h.chrome&&B&&B!==c){C=t.slice||t.webkitSlice;t=C.call(t,0,t.size,c);E=true}if(o&&u!=="download"){u+=".download"}if(B===c||o){w=h}else{w=h.open()}if(!p){D();return}k+=t.size;p(h.TEMPORARY,k,z(function(F){F.root.getDirectory("saved",y,z(function(G){var H=function(){G.getFile(u,y,z(function(I){I.createWriter(z(function(J){J.onwriteend=function(K){w.location.href=I.toURL();b.push(I);v.readyState=v.DONE;q(v,"writeend",K)};J.onerror=function(){var K=J.error;if(K.code!==K.ABORT_ERR){D()}};"writestart progress write abort".split(" ").forEach(function(K){J["on"+K]=v["on"+K]});J.write(t);v.abort=function(){J.abort();v.readyState=v.DONE};v.readyState=v.WRITING}),D)}),D)};G.getFile(u,{create:false},z(function(I){I.remove();H()}),z(function(I){if(I.code===I.NOT_FOUND_ERR){H()}else{D()}}))}),D)}),D)},d=f.prototype,a=function(s,t){return new f(s,t)};d.abort=function(){var s=this;s.readyState=s.DONE;q(s,"abort")};d.readyState=d.INIT=0;d.WRITING=1;d.DONE=2;d.error=d.onwritestart=d.onprogress=d.onwrite=d.onabort=d.onerror=d.onwriteend=null;h.addEventListener("unload",i,false);return a}(self));
\ No newline at end of file
diff --git a/mixly/common/modules/web-modules/ace/ace.js b/mixly/common/modules/web-modules/ace/ace.js
new file mode 100644
index 00000000..e30815c4
--- /dev/null
+++ b/mixly/common/modules/web-modules/ace/ace.js
@@ -0,0 +1,23 @@
+(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;un.length)t=n.length;t-=e.length;var r=n.indexOf(e,t);return r!==-1&&r===t}),String.prototype.repeat||r(String.prototype,"repeat",function(e){var t="",n=this;while(e>0){e&1&&(t+=n);if(e>>=1)n+=n}return t}),String.prototype.includes||r(String.prototype,"includes",function(e,t){return this.indexOf(e,t)!=-1}),Object.assign||(Object.assign=function(e){if(e===undefined||e===null)throw new TypeError("Cannot convert undefined or null to object");var t=Object(e);for(var n=1;n>>0,r=arguments[1],i=r>>0,s=i<0?Math.max(n+i,0):Math.min(i,n),o=arguments[2],u=o===undefined?n:o>>0,a=u<0?Math.max(n+u,0):Math.min(u,n);while(s0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n65535?2:1}}),ace.define("ace/lib/useragent",["require","exports","module"],function(e,t,n){"use strict";t.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};var r=typeof navigator=="object"?navigator:{},i=(/mac|win|linux/i.exec(r.platform)||["other"])[0].toLowerCase(),s=r.userAgent||"",o=r.appName||"";t.isWin=i=="win",t.isMac=i=="mac",t.isLinux=i=="linux",t.isIE=o=="Microsoft Internet Explorer"||o.indexOf("MSAppHost")>=0?parseFloat((s.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((s.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=s.match(/ Gecko\/\d+/),t.isOpera=typeof opera=="object"&&Object.prototype.toString.call(window["opera"])=="[object Opera]",t.isWebKit=parseFloat(s.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(s.split(" Chrome/")[1])||undefined,t.isSafari=parseFloat(s.split(" Safari/")[1])&&!t.isChrome||undefined,t.isEdge=parseFloat(s.split(" Edge/")[1])||undefined,t.isAIR=s.indexOf("AdobeAIR")>=0,t.isAndroid=s.indexOf("Android")>=0,t.isChromeOS=s.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(s)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIOS||t.isAndroid}),ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function u(){var e=o;o=null,e&&e.forEach(function(e){a(e[0],e[1])})}function a(e,n,r){if(typeof document=="undefined")return;if(o)if(r)u();else if(r===!1)return o.push([e,n]);if(s)return;var i=r;if(!r||!r.getRootNode)i=document;else{i=r.getRootNode();if(!i||i==r)i=document}var a=i.ownerDocument||i;if(n&&t.hasCssString(n,i))return null;n&&(e+="\n/*# sourceURL=ace/css/"+n+" */");var f=t.createElement("style");f.appendChild(a.createTextNode(e)),n&&(f.id=n),i==a&&(i=t.getDocumentHead(a)),i.insertBefore(f,i.firstChild)}var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function l(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e&&e.appendChild&&t&&t.appendChild(e),e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0,r.isChromeOS&&(t.HI_DPI=!1);if(typeof document!="undefined"){var f=document.createElement("div");t.HI_DPI&&f.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof f.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),f=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=u[t+"Path"];return o==null?o=u.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return u.$moduleUrls[e]=t};var a=function(t,n){if(t==="ace/theme/textmate"||t==="./theme/textmate")return n(null,e("./theme/textmate"));if(f)return f(t,n);console.error("loader is not configured")},f;t.setLoader=function(e){f=e},t.dynamicModules=Object.create(null),t.$loading={},t.$loaded={},t.loadModule=function(e,n){var r;if(Array.isArray(e))var s=e[0],o=e[1];else if(typeof e=="string")var o=e;var u=function(e){if(e&&!t.$loading[o])return n&&n(e);t.$loading[o]||(t.$loading[o]=[]),t.$loading[o].push(n);if(t.$loading[o].length>1)return;var r=function(){a(o,function(e,n){n&&(t.$loaded[o]=n),t._emit("load.module",{name:o,module:n});var r=t.$loading[o];t.$loading[o]=null,r.forEach(function(e){e&&e(n)})})};if(!t.get("packaged"))return r();i.loadScript(t.moduleUrl(o,s),r),l()};if(t.dynamicModules[o])t.dynamicModules[o]().then(function(e){e.default?u(e.default):u(e)});else{try{r=this.$require(o)}catch(f){}u(r||t.$loaded[o])}},t.$require=function(e){if(typeof n["require"]=="function"){var t="require";return n[t](e)}},t.setModuleLoader=function(e,n){t.dynamicModules[e]=n};var l=function(){!u.basePath&&!u.workerPath&&!u.modePath&&!u.themePath&&!Object.keys(u.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),l=function(){})};t.version="1.39.0"}),ace.define("ace/loader_build",["require","exports","module","ace/lib/fixoldbrowsers","ace/config"],function(e,t,n){"use strict";function s(t){if(!i||!i.document)return;r.set("packaged",t||e.packaged||n.packaged||i.define&&define.packaged);var s={},u="",a=document.currentScript||document._currentScript,f=a&&a.ownerDocument||document;a&&a.src&&(u=a.src.split(/[?#]/)[0].split("/").slice(0,-1).join("/")||"");var l=f.getElementsByTagName("script");for(var c=0;c ["+this.end.row+"/"+this.end.column+"]"},e.prototype.contains=function(e,t){return this.compare(e,t)==0},e.prototype.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},e.prototype.comparePoint=function(e){return this.compare(e.row,e.column)},e.prototype.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},e.prototype.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},e.prototype.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},e.prototype.isStart=function(e,t){return this.start.row==e&&this.start.column==t},e.prototype.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},e.prototype.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},e.prototype.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},e.prototype.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},e.prototype.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},e.prototype.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},e.prototype.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},e.prototype.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},e.prototype.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},e.prototype.clipRows=function(t,n){if(this.end.row>n)var r={row:n+1,column:0};else if(this.end.rown)var i={row:n+1,column:0};else if(this.start.row1?(u++,u>4&&(u=1)):u=1;if(i.isIE){var o=Math.abs(e.clientX-a)>5||Math.abs(e.clientY-f)>5;if(!l||o)u=1;l&&clearTimeout(l),l=setTimeout(function(){l=null},n[u-1]||600),u==1&&(a=e.clientX,f=e.clientY)}e._clicks=u,r[s]("mousedown",e);if(u>4)u=0;else if(u>1)return r[s](h[u],e)}var u=0,a,f,l,h={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){c(e,"mousedown",p,o)})},t.getModifierString=function(e){return r.KEY_MODS[p(e)]},t.addCommandKeyListener=function(e,n,r){var i=null;c(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=d(n,e,e.keyCode);return i=e.defaultPrevented,t},r),c(e,"keypress",function(e){i&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),i=null)},r),c(e,"keyup",function(e){s[e.keyCode]=null},r),s||(v(),c(window,"focus",v))};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var m=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+m++,i=function(s){s.data==r&&(t.stopPropagation(s),h(n,"message",i),e())};c(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/clipboard",["require","exports","module"],function(e,t,n){"use strict";var r;n.exports={lineMode:!1,pasteCancelled:function(){return r&&r>Date.now()-50?!0:r=!1},cancel:function(){r=Date.now()}}}),ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/config","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../config").nls,s=e("../lib/useragent"),o=e("../lib/dom"),u=e("../lib/lang"),a=e("../clipboard"),f=s.isChrome<18,l=s.isIE,c=s.isChrome>63,h=400,p=e("../lib/keys"),d=p.KEY_MODS,v=s.isIOS,m=v?/\s/:/\n/,g=s.isMobile,y;y=function(e,t){function Q(){T=!0,n.blur(),n.focus(),T=!1}function Y(e){e.keyCode==27&&n.value.lengthk&&N[s]=="\n")o=p.end;else if(rk&&N.slice(0,s).split("\n").length>2)o=p.down;else if(s>k&&N[s-1]==" ")o=p.right,u=d.option;else if(s>k||s==k&&k!=C&&r==s)o=p.right;r!==s&&(u|=d.shift);if(o){var a=t.onCommandKey({},u,o);if(!a&&t.commands){o=p.keyCodeToString(o);var f=t.commands.findKeyCommand(u,o);f&&t.execCommand(f)}C=r,k=s,H("")}};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=o.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck","false"),n.style.opacity="0",e.insertBefore(n,e.firstChild);var y=!1,b=!1,w=!1,E=!1,S="";g||(n.style.fontSize="1px");var x=!1,T=!1,N="",C=0,k=0,L=0,A=Number.MAX_SAFE_INTEGER,O=Number.MIN_SAFE_INTEGER,M=0;try{var _=document.activeElement===n}catch(D){}this.setNumberOfExtraLines=function(e){A=Number.MAX_SAFE_INTEGER,O=Number.MIN_SAFE_INTEGER;if(e<0){M=0;return}M=e},this.setAriaLabel=function(){var e="";t.$textInputAriaLabel&&(e+="".concat(t.$textInputAriaLabel,", "));if(t.session){var r=t.session.selection.cursor.row;e+=i("text-input.aria-label","Cursor at row $0",[r+1])}n.setAttribute("aria-label",e)},this.setAriaOptions=function(e){e.activeDescendant?(n.setAttribute("aria-haspopup","true"),n.setAttribute("aria-autocomplete",e.inline?"both":"list"),n.setAttribute("aria-activedescendant",e.activeDescendant)):(n.setAttribute("aria-haspopup","false"),n.setAttribute("aria-autocomplete","both"),n.removeAttribute("aria-activedescendant")),e.role&&n.setAttribute("role",e.role),e.setLabel&&(n.setAttribute("aria-roledescription",i("text-input.aria-roledescription","editor")),this.setAriaLabel())},this.setAriaOptions({role:"textbox"}),r.addListener(n,"blur",function(e){if(T)return;t.onBlur(e),_=!1},t),r.addListener(n,"focus",function(e){if(T)return;_=!0;if(s.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),s.isEdge?setTimeout(H):H()},t),this.$focusScroll=!1,this.focus=function(){this.setAriaOptions({setLabel:t.renderer.enableKeyboardAccessibility});if(S||c||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var r=n.getBoundingClientRect().top!=0}catch(i){return}var s=[];if(r){var o=n.parentElement;while(o&&o.nodeType==1)s.push(o),o.setAttribute("ace_nocontext","true"),!o.parentElement&&o.getRootNode?o=o.getRootNode().host:o=o.parentElement}n.focus({preventScroll:!0}),r&&s.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return _},t.on("beforeEndOperation",function(){var e=t.curOp,r=e&&e.command&&e.command.name;if(r=="insertstring")return;var i=r&&(e.docChanged||e.selectionChanged);w&&i&&(N=n.value="",K()),H()}),t.on("changeSelection",this.setAriaLabel);var P=function(e,n){var r=n;for(var i=1;i<=e-A&&i<2*M+1;i++)r+=t.session.getLine(e-i).length+1;return r},H=v?function(e){if(!_||y&&!e||E)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=N=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(C!=i||k!=s)&&n.setSelectionRange(i,s),C=i,k=s}:function(){if(w||E)return;if(!_&&!I)return;w=!0;var e=0,r=0,i="";if(t.session){var s=t.selection,o=s.getRange(),u=s.cursor.row;if(u===O+1)A=O+1,O=A+2*M;else if(u===A-1)O=A-1,A=O-2*M;else if(uO+1)A=u>M?u-M:0,O=u>M?u+M:2*M;var a=[];for(var f=A;f<=O;f++)a.push(t.session.getLine(f));i=a.join("\n"),e=P(o.start.row,o.start.column),r=P(o.end.row,o.end.column);if(o.start.rowO){var c=t.session.getLine(O+1);r=o.end.row>O+1?c.length:o.end.column,r+=i.length+1,i=i+"\n"+c}else g&&u>0&&(i="\n"+i,r+=1,e+=1);i.length>h&&(e=N.length&&e.value===N&&N&&e.selectionEnd!==k},j=function(e){if(w)return;y?y=!1:B(n)?(t.selectAll(),H()):g&&n.selectionStart!=C&&H()},F=null;this.setInputHandler=function(e){F=e},this.getInputHandler=function(){return F};var I=!1,q=function(e,r){I&&(I=!1);if(b)return H(),e&&t.onPaste(e),b=!1,"";var i=n.selectionStart,o=n.selectionEnd,u=C,a=N.length-k,f=e,l=e.length-i,c=e.length-o,h=0;while(u>0&&N[h]==e[h])h++,u--;f=f.slice(h),h=1;while(a>0&&N.length-h>C-1&&N[N.length-h]==e[e.length-h])h++,a--;l-=h-1,c-=h-1;var p=f.length-h+1;p<0&&(u=-p,p=0),f=f.slice(0,p);if(!r&&!f&&!l&&!u&&!a&&!c)return"";E=!0;var d=!1;return s.isAndroid&&f==". "&&(f=" ",d=!0),f&&!u&&!a&&!l&&!c||x?t.onTextInput(f):t.onTextInput(f,{extendLeft:u,extendRight:a,restoreStart:l,restoreEnd:c}),E=!1,N=e,C=i,k=o,L=c,d?"\n":f},R=function(e){if(w)return J();if(e&&e.inputType){if(e.inputType=="historyUndo")return t.execCommand("undo");if(e.inputType=="historyRedo")return t.execCommand("redo")}var r=n.value,i=q(r,!0);(r.length>h+100||m.test(i)||g&&C<1&&C==k)&&H()},U=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||f)return;var i=l||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return U(e,t,!0)}},z=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);U(e,s)?(v&&(H(s),y=s,setTimeout(function(){y=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(y=!0,n.value=s,n.select(),setTimeout(function(){y=!1,H(),i?t.onCut():t.onCopy()}))},W=function(e){z(e,!0)},X=function(e){z(e,!1)},V=function(e){var i=U(e);if(a.pasteCancelled())return;typeof i=="string"?(i&&t.onPaste(i,e),s.isIE&&setTimeout(H),r.preventDefault(e)):(n.value="",b=!0)};r.addCommandKeyListener(n,function(e,n,r){if(w)return;return t.onCommandKey(e,n,r)},t),r.addListener(n,"select",j,t),r.addListener(n,"input",R,t),r.addListener(n,"cut",W,t),r.addListener(n,"copy",X,t),r.addListener(n,"paste",V,t),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(s.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:X(e);break;case 86:V(e);break;case 88:W(e)}},t);var $=function(e){if(w||!t.onCompositionStart||t.$readOnly)return;w={};if(x)return;e.data&&(w.useTextareaForIME=!1),setTimeout(J,0),t._signal("compositionStart"),t.on("mousedown",Q);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,w.markerRange=r,w.selectionStart=C,t.onCompositionStart(w),w.useTextareaForIME?(N=n.value="",C=0,k=0):(n.msGetInputContext&&(w.context=n.msGetInputContext()),n.getInputContext&&(w.context=n.getInputContext()))},J=function(){if(!w||!t.onCompositionUpdate||t.$readOnly)return;if(x)return Q();if(w.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;q(e),w.markerRange&&(w.context&&(w.markerRange.start.column=w.selectionStart=w.context.compositionStartOffset),w.markerRange.end.column=w.markerRange.start.column+k-w.selectionStart+L)}},K=function(e){if(!t.onCompositionEnd||t.$readOnly)return;w=!1,t.onCompositionEnd(),t.off("mousedown",Q),e&&R()},G=u.delayedCall(J,50).schedule.bind(null,null);r.addListener(n,"compositionstart",$,t),r.addListener(n,"compositionupdate",J,t),r.addListener(n,"keyup",Y,t),r.addListener(n,"keydown",G,t),r.addListener(n,"compositionend",K,t),this.getElement=function(){return n},this.setCommandMode=function(e){x=e,n.readOnly=!1},this.setReadOnly=function(e){x||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){},this.onContextMenu=function(e){I=!0,H(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,i){S||(S=n.style.cssText),n.style.cssText=(i?"z-index:100000;":"")+(s.isIE?"opacity:0.1;":"")+"text-indent: -"+(C+k)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=o.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(a.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){o.translate(n,e.clientX-l-2,Math.min(e.clientY-f-2,c))};h(e);if(e.type!="mousedown")return;t.renderer.$isMousePressed=!0,clearTimeout(Z),s.isWin&&r.capture(t.container,h,et)},this.onContextMenuClose=et;var Z,tt=function(e){t.textInput.onContextMenu(e),et()};r.addListener(n,"mouseup",tt,t),r.addListener(n,"mousedown",function(e){e.preventDefault(),et()},t),r.addListener(t.renderer.scroller,"contextmenu",tt,t),r.addListener(n,"contextmenu",tt,t),v&&nt(e,t,n),this.destroy=function(){n.parentElement&&n.parentElement.removeChild(n)}},t.TextInput=y,t.$setUserAgentForTests=function(e,t){g=e,v=t}}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550,o=function(){function e(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}return e.prototype.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},e.prototype.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.setStyle("ace_selecting"),this.setState("select")},e.prototype.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},e.prototype.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},e.prototype.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting")},e.prototype.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},e.prototype.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},e.prototype.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},e.prototype.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},e.prototype.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedn.clientHeight;r||t.preventDefault()}}),ace.define("ace/tooltip",["require","exports","module","ace/lib/dom","ace/lib/event","ace/range","ace/lib/scroll"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=this&&this.__values||function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("./lib/dom"),o=e("./lib/event"),u=e("./range").Range,a=e("./lib/scroll").preventParentScroll,f="ace_tooltip",l=function(){function e(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}return e.prototype.$init=function(){return this.$element=s.createElement("div"),this.$element.className=f,this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},e.prototype.getElement=function(){return this.$element||this.$init()},e.prototype.setText=function(e){this.getElement().textContent=e},e.prototype.setHtml=function(e){this.getElement().innerHTML=e},e.prototype.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},e.prototype.setClassName=function(e){s.addCssClass(this.getElement(),e)},e.prototype.setTheme=function(e){this.$element.className=f+" "+(e.isDark?"ace_dark ":"")+(e.cssClass||"")},e.prototype.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},e.prototype.hide=function(e){this.isOpen&&(this.getElement().style.display="none",this.getElement().className=f,this.isOpen=!1)},e.prototype.getHeight=function(){return this.getElement().offsetHeight},e.prototype.getWidth=function(){return this.getElement().offsetWidth},e.prototype.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)},e}(),c=function(){function e(){this.popups=[]}return e.prototype.addPopup=function(e){this.popups.push(e),this.updatePopups()},e.prototype.removePopup=function(e){var t=this.popups.indexOf(e);t!==-1&&(this.popups.splice(t,1),this.updatePopups())},e.prototype.updatePopups=function(){var e,t,n,r;this.popups.sort(function(e,t){return t.priority-e.priority});var s=[];try{for(var o=i(this.popups),u=o.next();!u.done;u=o.next()){var a=u.value,f=!0;try{for(var l=(n=void 0,i(s)),c=l.next();!c.done;c=l.next()){var h=c.value;if(this.doPopupsOverlap(h,a)){f=!1;break}}}catch(p){n={error:p}}finally{try{c&&!c.done&&(r=l.return)&&r.call(l)}finally{if(n)throw n.error}}f?s.push(a):a.hide()}}catch(d){e={error:d}}finally{try{u&&!u.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}},e.prototype.doPopupsOverlap=function(e,t){var n=e.getElement().getBoundingClientRect(),r=t.getElement().getBoundingClientRect();return n.leftr.left&&n.topr.top},e}(),h=new c;t.popupManager=h,t.Tooltip=l;var p=function(e){function t(t){t===void 0&&(t=document.body);var n=e.call(this,t)||this;n.timeout=undefined,n.lastT=0,n.idleTime=350,n.lastEvent=undefined,n.onMouseOut=n.onMouseOut.bind(n),n.onMouseMove=n.onMouseMove.bind(n),n.waitForHover=n.waitForHover.bind(n),n.hide=n.hide.bind(n);var r=n.getElement();return r.style.whiteSpace="pre-wrap",r.style.pointerEvents="auto",r.addEventListener("mouseout",n.onMouseOut),r.tabIndex=-1,r.addEventListener("blur",function(){r.contains(document.activeElement)||this.hide()}.bind(n)),r.addEventListener("wheel",a),n}return r(t,e),t.prototype.addToEditor=function(e){e.on("mousemove",this.onMouseMove),e.on("mousedown",this.hide),e.renderer.getMouseEventTarget().addEventListener("mouseout",this.onMouseOut,!0)},t.prototype.removeFromEditor=function(e){e.off("mousemove",this.onMouseMove),e.off("mousedown",this.hide),e.renderer.getMouseEventTarget().removeEventListener("mouseout",this.onMouseOut,!0),this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},t.prototype.onMouseMove=function(e,t){this.lastEvent=e,this.lastT=Date.now();var n=t.$mouseHandler.isMousePressed;if(this.isOpen){var r=this.lastEvent&&this.lastEvent.getDocumentPosition();(!this.range||!this.range.contains(r.row,r.column)||n||this.isOutsideOfText(this.lastEvent))&&this.hide()}if(this.timeout||n)return;this.lastEvent=e,this.timeout=setTimeout(this.waitForHover,this.idleTime)},t.prototype.waitForHover=function(){this.timeout&&clearTimeout(this.timeout);var e=Date.now()-this.lastT;if(this.idleTime-e>10){this.timeout=setTimeout(this.waitForHover,this.idleTime-e);return}this.timeout=null,this.lastEvent&&!this.isOutsideOfText(this.lastEvent)&&this.$gatherData(this.lastEvent,this.lastEvent.editor)},t.prototype.isOutsideOfText=function(e){var t=e.editor,n=e.getDocumentPosition(),r=t.session.getLine(n.row);if(n.column==r.length){var i=t.renderer.pixelToScreenCoordinates(e.clientX,e.clientY),s=t.session.documentToScreenPosition(n.row,n.column);if(s.column!=i.column||s.row!=i.row)return!0}return!1},t.prototype.setDataProvider=function(e){this.$gatherData=e},t.prototype.showForRange=function(e,t,n,r){var i=10;if(r&&r!=this.lastEvent)return;if(this.isOpen&&document.activeElement==this.getElement())return;var s=e.renderer;this.isOpen||(h.addPopup(this),this.$registerCloseEvents(),this.setTheme(s.theme)),this.isOpen=!0,this.addMarker(t,e.session),this.range=u.fromPoints(t.start,t.end);var o=s.textToScreenCoordinates(t.start.row,t.start.column),a=s.scroller.getBoundingClientRect();o.pageXt.session.documentToScreenRow(a.row,a.column))return c()}r.showTooltip(i);if(!r.isOpen)return;t.on("mousewheel",c),t.on("changeSession",c),window.addEventListener("keydown",c,!0);if(e.$tooltipFollowsMouse)p(u);else{var h=u.getGutterRow(),d=n.$lines.get(h);if(d){var v=d.element.querySelector(".ace_gutter_annotation"),m=v.getBoundingClientRect(),g=r.getElement().style;g.left=m.right-f+"px",g.top=m.bottom-l+"px"}else p(u)}}function c(e){if(e&&e.type==="keydown"&&(e.ctrlKey||e.metaKey))return;if(e&&e.type==="mouseout"&&(!e.relatedTarget||r.getElement().contains(e.relatedTarget)))return;i&&(i=clearTimeout(i)),r.isOpen&&(r.hideTooltip(),t.off("mousewheel",c),t.off("changeSession",c),window.removeEventListener("keydown",c,!0))}function p(e){r.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,r=new h(t,!0);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var i,u;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(s.hasCssClass(n,"ace_fold-widget"))return c();r.isOpen&&e.$tooltipFollowsMouse&&p(t),u=t;if(i)return;i=setTimeout(function(){i=null,u&&!e.isMousePressed&&a()},50)}),o.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!r.isOpen)return;i=setTimeout(function(){i=null,c(e)},50)},t)}var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=this&&this.__values||function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("../lib/dom"),o=e("../lib/event"),u=e("../tooltip").Tooltip,a=e("../config").nls,f=5,l=3;t.GUTTER_TOOLTIP_LEFT_OFFSET=f,t.GUTTER_TOOLTIP_TOP_OFFSET=l,t.GutterHandler=c;var h=function(e){function t(t,n){n===void 0&&(n=!1);var r=e.call(this,t.container)||this;r.editor=t,r.visibleTooltipRow;var i=r.getElement();return i.setAttribute("role","tooltip"),i.style.pointerEvents="auto",n&&(r.onMouseOut=r.onMouseOut.bind(r),i.addEventListener("mouseout",r.onMouseOut)),r}return r(t,e),t.prototype.onMouseOut=function(e){if(!this.isOpen)return;if(!e.relatedTarget||this.getElement().contains(e.relatedTarget))return;if(e&&e.currentTarget.contains(e.relatedTarget))return;this.hideTooltip()},t.prototype.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),u.prototype.setPosition.call(this,e,t)},Object.defineProperty(t,"annotationLabels",{get:function(){return{error:{singular:a("gutter-tooltip.aria-label.error.singular","error"),plural:a("gutter-tooltip.aria-label.error.plural","errors")},security:{singular:a("gutter-tooltip.aria-label.security.singular","security finding"),plural:a("gutter-tooltip.aria-label.security.plural","security findings")},warning:{singular:a("gutter-tooltip.aria-label.warning.singular","warning"),plural:a("gutter-tooltip.aria-label.warning.plural","warnings")},info:{singular:a("gutter-tooltip.aria-label.info.singular","information message"),plural:a("gutter-tooltip.aria-label.info.plural","information messages")},hint:{singular:a("gutter-tooltip.aria-label.hint.singular","suggestion"),plural:a("gutter-tooltip.aria-label.hint.plural","suggestions")}}},enumerable:!1,configurable:!0}),t.prototype.showTooltip=function(e){var n,r=this.editor.renderer.$gutterLayer,i=r.$annotations[e],o;i?o={displayText:Array.from(i.displayText),type:Array.from(i.type)}:o={displayText:[],type:[]};var u=r.session.getFoldLine(e);if(u&&r.$showFoldedAnnotations){var a={error:[],security:[],warning:[],info:[],hint:[]},f={error:1,security:2,warning:3,info:4,hint:5},l;for(var c=e+1;c<=u.end.row;c++){if(!r.$annotations[c])continue;for(var h=0;ha?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.$resetCursorStyle(),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("div");n.style.cssText="top:-100px;position:absolute;z-index:2147483647;opacity:0.5",n.textContent="\u00a0";var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.on("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",t.container.appendChild(n),i.setDragImage&&i.setDragImage(n,0,0),setTimeout(function(){t.container.removeChild(n)}),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.$resetCursorStyle()}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e),t),i.addListener(c,"dragend",this.onDragEnd.bind(e),t),i.addListener(c,"dragenter",this.onDragEnter.bind(e),t),i.addListener(c,"dragover",this.onDragOver.bind(e),t),i.addListener(c,"dragleave",this.onDragLeave.bind(e),t),i.addListener(c,"drop",this.onDrop.bind(e),t);var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event","ace/lib/event","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./mouse_event").MouseEvent,i=e("../lib/event"),s=e("../lib/dom");t.addTouchListeners=function(e,t){function b(){var e=window.navigator&&window.navigator.clipboard,r=!1,i=function(){var n=t.getCopyText(),i=t.session.getUndoManager().hasUndo();y.replaceChild(s.buildDom(r?["span",!n&&o("selectall")&&["span",{"class":"ace_mobile-button",action:"selectall"},"Select All"],n&&o("copy")&&["span",{"class":"ace_mobile-button",action:"copy"},"Copy"],n&&o("cut")&&["span",{"class":"ace_mobile-button",action:"cut"},"Cut"],e&&o("paste")&&["span",{"class":"ace_mobile-button",action:"paste"},"Paste"],i&&o("undo")&&["span",{"class":"ace_mobile-button",action:"undo"},"Undo"],o("find")&&["span",{"class":"ace_mobile-button",action:"find"},"Find"],o("openCommandPalette")&&["span",{"class":"ace_mobile-button",action:"openCommandPalette"},"Palette"]]:["span"]),y.firstChild)},o=function(e){return t.commands.canExecute(e,t)},u=function(n){var s=n.target.getAttribute("action");if(s=="more"||!r)return r=!r,i();if(s=="paste")e.readText().then(function(e){t.execCommand(s,e)});else if(s){if(s=="cut"||s=="copy")e?e.writeText(t.getCopyText()):document.execCommand("copy");t.execCommand(s)}y.firstChild.style.display="none",r=!1,s!="openCommandPalette"&&t.focus()};y=s.buildDom(["div",{"class":"ace_mobile-menu",ontouchstart:function(e){n="menu",e.stopPropagation(),e.preventDefault(),t.textInput.focus()},ontouchend:function(e){e.stopPropagation(),e.preventDefault(),u(e)},onclick:u},["span"],["span",{"class":"ace_mobile-button",action:"more"},"..."]],t.container)}function w(){if(!t.getOption("enableMobileMenu")){y&&E();return}y||b();var e=t.selection.cursor,n=t.renderer.textToScreenCoordinates(e.row,e.column),r=t.renderer.textToScreenCoordinates(0,0).pageX,i=t.renderer.scrollLeft,s=t.container.getBoundingClientRect();y.style.top=n.pageY-s.top-3+"px",n.pageX-s.left=2?t.selection.getLineRange(p.row):t.session.getBracketRange(p);e&&!e.isEmpty()?t.selection.setRange(e):t.selection.selectWord(),n="wait"}function T(){h+=60,c=setInterval(function(){h--<=0&&(clearInterval(c),c=null),Math.abs(v)<.01&&(v=0),Math.abs(m)<.01&&(m=0),h<20&&(v=.9*v),h<20&&(m=.9*m);var e=t.session.getScrollTop();t.renderer.scrollBy(10*v,10*m),e==t.session.getScrollTop()&&(h=0)},10)}var n="scroll",o,u,a,f,l,c,h=0,p,d=0,v=0,m=0,g,y;i.addListener(e,"contextmenu",function(e){if(!g)return;var n=t.textInput.getElement();n.focus()},t),i.addListener(e,"touchstart",function(e){var i=e.touches;if(l||i.length>1){clearTimeout(l),l=null,a=-1,n="zoom";return}g=t.$mouseHandler.isMousePressed=!0;var s=t.renderer.layerConfig.lineHeight,c=t.renderer.layerConfig.lineHeight,y=e.timeStamp;f=y;var b=i[0],w=b.clientX,E=b.clientY;Math.abs(o-w)+Math.abs(u-E)>s&&(a=-1),o=e.clientX=w,u=e.clientY=E,v=m=0;var T=new r(e,t);p=T.getDocumentPosition();if(y-a<500&&i.length==1&&!h)d++,e.preventDefault(),e.button=0,x();else{d=0;var N=t.selection.cursor,C=t.selection.isEmpty()?N:t.selection.anchor,k=t.renderer.$cursorLayer.getPixelPosition(N,!0),L=t.renderer.$cursorLayer.getPixelPosition(C,!0),A=t.renderer.scroller.getBoundingClientRect(),O=t.renderer.layerConfig.offset,M=t.renderer.scrollLeft,_=function(e,t){return e/=c,t=t/s-.75,e*e+t*t};if(e.clientXP?"cursor":"anchor"),P<3.5?n="anchor":D<3.5?n="cursor":n="scroll",l=setTimeout(S,450)}a=y},t),i.addListener(e,"touchend",function(e){g=t.$mouseHandler.isMousePressed=!1,c&&clearInterval(c),n=="zoom"?(n="",h=0):l?(t.selection.moveToPosition(p),h=0,w()):n=="scroll"?(T(),E()):w(),clearTimeout(l),l=null},t),i.addListener(e,"touchmove",function(e){l&&(clearTimeout(l),l=null);var i=e.touches;if(i.length>1||n=="zoom")return;var s=i[0],a=o-s.clientX,c=u-s.clientY;if(n=="wait"){if(!(a*a+c*c>4))return e.preventDefault();n="cursor"}o=s.clientX,u=s.clientY,e.clientX=s.clientX,e.clientY=s.clientY;var h=e.timeStamp,p=h-f;f=h;if(n=="scroll"){var d=new r(e,t);d.speed=1,d.wheelX=a,d.wheelY=c,10*Math.abs(a)0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},e.prototype.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},e.prototype.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},e.prototype.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent},e}();t.BidiHandler=o}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(){function e(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})}return e.prototype.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},e.prototype.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},e.prototype.getCursor=function(){return this.lead.getPosition()},e.prototype.setAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},e.prototype.getAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},e.prototype.getSelectionLead=function(){return this.lead.getPosition()},e.prototype.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},e.prototype.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},e.prototype.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},e.prototype.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},e.prototype.setRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},e.prototype.$setSelection=function(e,t,n,r){if(this.$silent)return;var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},e.prototype.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},e.prototype.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},e.prototype.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},e.prototype.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},e.prototype.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},e.prototype.selectUp=function(){this.$moveSelection(this.moveCursorUp)},e.prototype.selectDown=function(){this.$moveSelection(this.moveCursorDown)},e.prototype.selectRight=function(){this.$moveSelection(this.moveCursorRight)},e.prototype.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},e.prototype.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},e.prototype.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},e.prototype.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},e.prototype.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},e.prototype.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},e.prototype.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},e.prototype.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},e.prototype.selectWord=function(){this.setSelectionRange(this.getWordRange())},e.prototype.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},e.prototype.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},e.prototype.selectLine=function(){this.setSelectionRange(this.getLineRange())},e.prototype.moveCursorUp=function(){this.moveCursorBy(-1,0)},e.prototype.moveCursorDown=function(){this.moveCursorBy(1,0)},e.prototype.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},e.prototype.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},e.prototype.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},e.prototype.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},e.prototype.moveCursorFileStart=function(){this.moveCursorTo(0,0)},e.prototype.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},e.prototype.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},e.prototype.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},e.prototype.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},e.prototype.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},e.prototype.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);if(e!=0&&this.session.lineWidgets&&this.session.lineWidgets[this.lead.row]){var i=this.session.lineWidgets[this.lead.row];e<0?e-=i.rowsAbove||0:e>0&&(e+=i.rowCount-(i.rowsAbove||0))}var s=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&s.row===this.lead.row&&s.column===this.lead.column,this.moveCursorTo(s.row,s.column+t,t===0)},e.prototype.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},e.prototype.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},e.prototype.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},e.prototype.detach=function(){this.lead.detach(),this.anchor.detach()},e.prototype.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},e.prototype.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},e.prototype.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},e.prototype.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},e.prototype.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},e.prototype.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0},e}();u.prototype.setSelectionAnchor=u.prototype.setAnchor,u.prototype.getSelectionAnchor=u.prototype.getAnchor,u.prototype.setSelectionRange=u.prototype.setRange,r.implement(u.prototype,s),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/lib/report_error"],function(e,t,n){"use strict";var r=e("./lib/report_error").reportError,i=2e3,s=function(){function e(e){this.splitRegex,this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}}return e.prototype.$setMaxTokenCount=function(e){i=e|0},e.prototype.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},e}();s.prototype.reportError=r,t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/deep_copy"],function(e,t,n){"use strict";var r=e("../lib/deep_copy").deepCopy,i;i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}},function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},e.prototype.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},e.prototype.getCurrentTokenRow=function(){return this.$row},e.prototype.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},e.prototype.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},e.prototype.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)},e}();t.TokenIterator=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","rparen","paren","punctuation.operator"],a=["text","paren.rparen","rparen","paren","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d;d=function(e){e=e||{},this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l),v=i.getTokenAt(u.row,u.column);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(v&&/(?:string)\.quasi|\.xml/.test(v.type)){var m=[/tag\-(?:open|name)/,/attribute\-name/];if(m.some(function(e){return e.test(v.type)})||/(string)\.quasi/.test(v.type)&&v.value[u.column-v.start-1]!=="$")return;return d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}}if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var g=a.substring(u.column,u.column+1);if(g=="}"){var y=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(y!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var b="";d.isMaybeInsertedClosing(u,a)&&(b=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var g=a.substring(u.column,u.column+1);if(g==="}"){var w=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!w)return null;var E=this.$getIndent(i.getLine(w.row))}else{if(!b){d.clearMaybeInsertedClosing();return}var E=this.$getIndent(a)}var S=E+i.getTabString();return{text:"\n"+S+"\n"+E+b,selection:[1,S.length,1,S.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(v),T=r.$mode.$pairQuotesAfter,N=T&&T[o]&&T[o].test(d);if(!N&&S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;var C=l[f.column-2];if(!(d!=o||C!=o&&!E.test(C)))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}}),e.closeDocComment!==!1&&this.add("doc comment end","insertion",function(e,t,n,r,i){if(e==="doc-start"&&(i==="\n"||i==="\r\n")&&n.selection.isEmpty()){var s=n.getCursorPosition();if(s.column===0)return;var o=r.doc.getLine(s.row),u=r.doc.getLine(s.row+1),a=r.getTokens(s.row),f=0;for(var l=0;l=s.column){if(f===s.column){if(!/\.doc/.test(c.type))return;if(/\*\//.test(c.value)){var h=a[l+1];if(!h||!/\.doc/.test(h.type))return}}var p=s.column-(f-c.value.length),d=c.value.indexOf("*/"),v=c.value.indexOf("/**",d>-1?d+2:0);if(v!==-1&&p>v&&p=d&&p<=v||!/\.doc/.test(c.type))return;break}}var m=this.$getIndent(o);if(/\s*\*/.test(u))return/^\s*\*/.test(o)?{text:i+m+"* ",selection:[1,2+m.length,1,2+m.length]}:{text:i+m+" * ",selection:[1,3+m.length,1,3+m.length]};if(/\/\*\*/.test(o.substring(0,s.column)))return{text:i+m+" * "+i+" "+m+"*/",selection:[1,4+m.length,1,4+m.length]}}})},d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){if(/[)}\]]/.test(e.session.getLine(n.row)[n.column]))return!0;var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"],u=function(e){(function(t){var n=o[e],r=t[n];t[o[e]]=function(){return this.$delegator(n,arguments,r)}})(a)},a=this;for(var t=0;tt[n].column&&n++,s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},e.prototype.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},e.prototype.$registerLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];return t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e},e.prototype.addLineWidget=function(e){this.$registerLineWidget(e),e.session=this.session;if(!this.editor)return e;var t=this.editor.renderer;e.html&&!e.el&&(e.el=r.createElement("div"),e.el.innerHTML=e.html),e.text&&!e.el&&(e.el=r.createElement("div"),e.el.textContent=e.text),e.el&&(r.addCssClass(e.el,"ace_lineWidgetContainer"),e.className&&r.addCssClass(e.el,e.className),e.el.style.position="absolute",e.el.style.zIndex="5",t.container.appendChild(e.el),e._inDocument=!0,e.coverGutter||(e.el.style.zIndex="3"),e.pixelHeight==null&&(e.pixelHeight=e.el.offsetHeight)),e.rowCount==null&&(e.rowCount=e.pixelHeight/t.layerConfig.lineHeight);var n=this.session.getFoldAt(e.row,0);e.$fold=n;if(n){var i=this.session.lineWidgets;e.row==n.end.row&&!i[n.start.row]?i[n.start.row]=e:e.hidden=!0}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,t),this.onWidgetChanged(e),e},e.prototype.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},e.prototype.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},e.prototype.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},e.prototype.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}},e}();t.LineWidgets=i}),ace.define("ace/apply_delta",["require","exports","module"],function(e,t,n){"use strict";function r(e,t){throw console.log("Invalid Delta:",e),"Invalid Delta: "+t}function i(e,t){return t.row>=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";function o(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var t=u(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(t.row,t.column,!0)},e.prototype.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},e.prototype.detach=function(){this.document.off("change",this.$onChange)},e.prototype.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},e.prototype.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n},e}();s.prototype.$insertRight=!1,r.implement(s.prototype,i),t.Anchor=s}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(){function e(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)}return e.prototype.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e||"")},e.prototype.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},e.prototype.createAnchor=function(e,t){return new u(this,e,t)},e.prototype.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},e.prototype.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},e.prototype.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},e.prototype.getNewLineMode=function(){return this.$newLineMode},e.prototype.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},e.prototype.getLine=function(e){return this.$lines[e]||""},e.prototype.getLines=function(e,t){return this.$lines.slice(e,t+1)},e.prototype.getAllLines=function(){return this.getLines(0,this.getLength())},e.prototype.getLength=function(){return this.$lines.length},e.prototype.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},e.prototype.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},e.prototype.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},e.prototype.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},e.prototype.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},e.prototype.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},e.prototype.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},e.prototype.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},e.prototype.clonePos=function(e){return{row:e.row,column:e.column}},e.prototype.pos=function(e,t){return{row:e,column:t}},e.prototype.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},e.prototype.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},e.prototype.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},e.prototype.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},e.prototype.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},e.prototype.$safeApplyDelta=function(e){var t=this.$lines.length;(e.action=="remove"&&e.start.row20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}}return e.prototype.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},e.prototype.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},e.prototype.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},e.prototype.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},e.prototype.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},e.prototype.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},e.prototype.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},e.prototype.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},e.prototype.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},e.prototype.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens},e.prototype.cleanup=function(){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.removeAllListeners()},e}();r.implement(s.prototype,i),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./range").Range,s=function(){function e(e,t,n){n===void 0&&(n="text"),this.setRegexp(e),this.clazz=t,this.type=n,this.docLen=0}return e.prototype.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},e.prototype.update=function(e,t,n,s){if(!this.regExp)return;var o=s.firstRow,u=s.lastRow,a={},f=n.$editor.$search,l=f.$isMultilineSearch(n.$editor.getLastSearchOptions());for(var c=o;c<=u;c++){var h=this.cache[c];if(h==null||n.getValue().length!=this.docLen){if(l){h=[];var p=f.$multiLineForward(n,this.regExp,c,u);if(p){var d=p.endRow<=u?p.endRow-1:u;d>c&&(c=d),h.push(new i(p.startRow,p.startCol,p.endRow,p.endCol))}h.length>this.MAX_RANGES&&(h=h.slice(0,this.MAX_RANGES))}else h=r.getMatchOffsets(n.getLine(c),this.regExp),h.length>this.MAX_RANGES&&(h=h.slice(0,this.MAX_RANGES)),h=h.map(function(e){return new i(c,e.offset,c,e.offset+e.length)});this.cache[c]=h.length?h:""}if(h.length===0)continue;for(var v=h.length;v--;){var m=h[v].toScreenRange(n),g=m.toString();if(a[g])continue;a[g]=!0,t.drawSingleLineMarker(e,m,this.clazz,s)}}this.docLen=n.getValue().length},e}();s.prototype.MAX_RANGES=500,t.SearchHighlight=s}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;ithis.$undoDepth-1&&this.$undoStack.splice(0,r-this.$undoDepth+1),this.$undoStack.push(this.lastDeltas),e.id=this.$rev=++this.$maxRev}if(e.action=="remove"||e.action=="insert")this.$lastDelta=e;this.lastDeltas.push(e)},e.prototype.addSelection=function(e,t){this.selections.push({value:e,rev:t||this.$rev})},e.prototype.startNewGroup=function(){return this.lastDeltas=null,this.$rev},e.prototype.markIgnored=function(e,t){t==null&&(t=this.$rev+1);var n=this.$undoStack;for(var r=n.length;r--;){var i=n[r][0];if(i.id<=e)break;i.id0},e.prototype.canRedo=function(){return this.$redoStack.length>0},e.prototype.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},e.prototype.isAtBookmark=function(){return this.$rev===this.mark},e.prototype.toJSON=function(){return{$redoStack:this.$redoStack,$undoStack:this.$undoStack}},e.prototype.fromJSON=function(e){this.reset(),this.$undoStack=e.$undoStack,this.$redoStack=e.$redoStack},e.prototype.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)},e}();r.prototype.hasUndo=r.prototype.canUndo,r.prototype.hasRedo=r.prototype.canRedo,r.prototype.isClean=r.prototype.isAtBookmark,r.prototype.markClean=r.prototype.bookmark;var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){function e(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}return e.prototype.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},e.prototype.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},e.prototype.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},e.prototype.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},e.prototype.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},e.prototype.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},e.prototype.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},e.prototype.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},e.prototype.containsPoint=function(e){return this.pointIndex(e)>=0},e.prototype.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},e.prototype.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column==t.column&&this.$bias<=0||(a.start.column+=l,a.start.row+=f));if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$bias<0)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column,c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),o.collapseChildren||p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;if(e==null)n=new r(0,0,this.getLength(),0),t==null&&(t=!0);else if(typeof e=="number")n=new r(e,0,e,this.getLine(e).length);else if("row"in e)n=r.fromPoints(e,e);else{if(Array.isArray(e))return i=[],e.forEach(function(e){i=i.concat(this.unfold(e))},this),i;n=e}i=this.getFoldsInRangeList(n);var s=i;while(i.length==1&&r.comparePoints(i[0].start,n.start)<0&&r.comparePoints(i[0].end,n.end)>0)this.expandFolds(i),i=this.getFoldsInRangeList(n);t!=0?this.removeFolds(i):this.expandFolds(i);if(s.length)return s},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tc)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn(),f}},this.foldAll=function(e,t,n,r){n==undefined&&(n=1e5);var i=this.foldWidgets;if(!i)return;t=t||this.getLength(),e=e||0;for(var s=e;s=e&&(s=o.end.row,o.collapseChildren=n,this.addFold("...",o))}},this.foldToLevel=function(e){this.foldAll();while(e-->0)this.unfold(null,!1)},this.foldAllComments=function(){var e=this;this.foldAll(null,null,null,function(t){var n=e.getTokens(t);for(var r=0;r=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t instanceof u&&(t=t.domEvent);var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator,u=e("../mouse/mouse_event").MouseEvent;t.Folding=a}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.getMatchingBracketRanges=function(e,t){var n=this.getLine(e.row),r=/([\(\[\{])|([\)\]\}])/,s=!t&&n.charAt(e.column-1),o=s&&s.match(r);o||(s=(t===undefined||t)&&n.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(r));if(!o)return null;var u=new i(e.row,e.column-1,e.row,e.column),a=o[1]?this.$findClosingBracket(o[1],e):this.$findOpeningBracket(o[2],e);if(!a)return[u];var f=new i(a.row,a.column,a.row,a.column+1);return[u,f]},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)").replace(/-close\b/,"-(close|open)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)").replace(/-open\b/,"-(close|open)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a "?r=!0:t.type.indexOf("tag-name")!==-1&&(n=!0));while(t&&!n);return t},this.$findClosingTag=function(e,t){var n,r=t.value,s=t.value,o=0,u=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);t=e.stepForward();var a=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length),f=!1;do{n=t;if(n.type.indexOf("tag-close")!==-1&&!f){var l=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);f=!0}t=e.stepForward();if(t){if(t.value===">"&&!f){var l=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);f=!0}if(t.type.indexOf("tag-name")!==-1){r=t.value;if(s===r)if(n.value==="<")o++;else if(n.value===""){o--;if(o<0){e.stepBackward();var c=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+2);t=e.stepForward();var h=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length);t.type.indexOf("tag-close")===-1&&(t=e.stepForward());if(!t||t.value!==">")return;var p=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}}else if(s===r&&t.value==="/>"){o--;if(o<0)var c=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+2),h=c,p=h,l=new i(a.end.row,a.end.column,a.end.row,a.end.column+1)}}}while(t&&o>=0);if(u&&l&&c&&p&&a&&h)return{openTag:new i(u.start.row,u.start.column,l.end.row,l.end.column),closeTag:new i(c.start.row,c.start.column,p.end.row,p.end.column),openTagName:a,closeTagName:h}},this.$findOpeningTag=function(e,t){var n=e.getCurrentToken(),r=t.value,s=0,o=e.getCurrentTokenRow(),u=e.getCurrentTokenColumn(),a=u+2,f=new i(o,u,o,a);e.stepForward();var l=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length);t.type.indexOf("tag-close")===-1&&(t=e.stepForward());if(!t||t.value!==">")return;var c=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);e.stepBackward(),e.stepBackward();do{t=n,o=e.getCurrentTokenRow(),u=e.getCurrentTokenColumn(),a=u+t.value.length,n=e.stepBackward();if(t)if(t.type.indexOf("tag-name")!==-1){if(r===t.value)if(n.value==="<"){s++;if(s>0){var h=new i(o,u,o,a),p=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);do t=e.stepForward();while(t&&t.value!==">");var d=new i(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}else n.value===""&&s--}else if(t.value==="/>"){var v=0,m=n;while(m){if(m.type.indexOf("tag-name")!==-1&&m.value===r){s--;break}if(m.value==="<")break;m=e.stepBackward(),v++}for(var g=0;g=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./bidihandler").BidiHandler,o=e("./config"),u=e("./lib/event_emitter").EventEmitter,a=e("./selection").Selection,f=e("./mode/text").Mode,l=e("./range").Range,c=e("./line_widgets").LineWidgets,h=e("./document").Document,p=e("./background_tokenizer").BackgroundTokenizer,d=e("./search_highlight").SearchHighlight,v=e("./undomanager").UndoManager,m=function(){function e(t,n){this.doc,this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$editor=null,this.prevOp={},this.$foldData=[],this.id="session"+ ++e.$uid,this.$foldData.toString=function(){return this.join("\n")},this.bgTokenizer=new p((new f).getTokenizer(),this);var r=this;this.bgTokenizer.on("update",function(e){r._signal("tokenizerUpdate",e)}),this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof t!="object"||!t.getLine)t=new h(t);this.setDocument(t),this.selection=new a(this),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.selection.on("changeCursor",this.$onSelectionChange),this.$bidiHandler=new s(this),o.resetOptions(this),this.setMode(n),o._signal("session",this),this.destroyed=!1,this.$initOperationListeners()}return e.prototype.$initOperationListeners=function(){var e=this;this.curOp=null,this.on("change",function(){e.curOp||(e.startOperation(),e.curOp.selectionBefore=e.$lastSel),e.curOp.docChanged=!0},!0),this.on("changeSelection",function(){e.curOp||(e.startOperation(),e.curOp.selectionBefore=e.$lastSel),e.curOp.selectionChanged=!0},!0),this.$operationResetTimer=i.delayedCall(this.endOperation.bind(this,!0))},e.prototype.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(e={}),this.$operationResetTimer.schedule(),this.curOp={command:e.command||{},args:e.args},this.curOp.selectionBefore=this.selection.toJSON(),this._signal("startOperation",e)},e.prototype.endOperation=function(e){if(this.curOp){if(e&&e.returnValue===!1){this.curOp=null,this._signal("endOperation",e);return}if(e==1&&this.curOp.command&&this.curOp.command.name=="mouse")return;var t=this.selection.toJSON();this.curOp.selectionAfter=t,this.$lastSel=this.selection.toJSON(),this.getUndoManager().addSelection(t),this._signal("beforeEndOperation"),this.prevOp=this.curOp,this.curOp=null,this._signal("endOperation",e)}},e.prototype.setDocument=function(e){this.doc&&this.doc.off("change",this.$onChange),this.doc=e,e.on("change",this.$onChange,!0),this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},e.prototype.getDocument=function(){return this.doc},Object.defineProperty(e.prototype,"widgetManager",{get:function(){var e=new c(this);return this.widgetManager=e,this.$editor&&e.attach(this.$editor),e},set:function(e){Object.defineProperty(this,"widgetManager",{writable:!0,enumerable:!0,configurable:!0,value:e})},enumerable:!1,configurable:!0}),e.prototype.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},e.prototype.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},e.prototype.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},e.prototype.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},e.prototype.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},e.prototype.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},e.prototype.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},e.prototype.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},e.prototype.setTabSize=function(e){this.setOption("tabSize",e)},e.prototype.getTabSize=function(){return this.$tabSize},e.prototype.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},e.prototype.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},e.prototype.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},e.prototype.setOverwrite=function(e){this.setOption("overwrite",e)},e.prototype.getOverwrite=function(){return this.$overwrite},e.prototype.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},e.prototype.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},e.prototype.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},e.prototype.getBreakpoints=function(){return this.$breakpoints},e.prototype.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},e.prototype.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},e.prototype.getLine=function(e){return this.doc.getLine(e)},e.prototype.getLines=function(e,t){return this.doc.getLines(e,t)},e.prototype.getLength=function(){return this.doc.getLength()},e.prototype.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},e.prototype.insert=function(e,t){return this.doc.insert(e,t)},e.prototype.remove=function(e){return this.doc.remove(e)},e.prototype.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},e.prototype.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},e.prototype.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},e.prototype.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},e.prototype.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},e.prototype.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},e.prototype.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},e.prototype.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},e.prototype.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},e.prototype.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},e.prototype.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},e.prototype.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},e.prototype.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},e.prototype.getUseWrapMode=function(){return this.$useWrapMode},e.prototype.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},e.prototype.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},e.prototype.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},e.prototype.getWrapLimit=function(){return this.$wrapLimit},e.prototype.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},e.prototype.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},e.prototype.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},e.prototype.$updateRowLengthCache=function(e,t){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},e.prototype.$updateWrapData=function(e,t){var n=this.doc.getAllLines(),r=this.getTabSize(),i=this.$wrapData,s=this.$wrapLimit,o,u,a=e;t=Math.min(t,n.length-1);while(a<=t)u=this.getFoldLine(a,u),u?(o=[],u.walk(function(e,t,r,i){var s;if(e!=null){s=this.$getDisplayTokens(e,o.length),s[0]=b;for(var u=1;ut-h){var p=s+t-h;if(e[p-1]>=S&&e[p]>=S){c(p);continue}if(e[p]==b||e[p]==w){for(p;p!=s-1;p--)if(e[p]==b)break;if(p>s){c(p);continue}p=s+t;for(p;p>2)),s-1);while(p>d&&e[p]d&&e[p]d&&e[p]==E)p--}else while(p>d&&e[p]d){c(++p);continue}p=s+t,e[p]==y&&p--,c(p-h)}return r},e.prototype.$getDisplayTokens=function(e,t){var n=[],r;t=t||0;for(var i=0;i39&&s<48||s>57&&s<64?n.push(E):s>=4352&&N(s)?n.push(g,y):n.push(g)}return n},e.prototype.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&N(r)?n+=2:n+=1;if(n>t)break}return[n,i]},e.prototype.getRowLength=function(e){var t=1;return this.lineWidgets&&(t+=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0),!this.$useWrapMode||!this.$wrapData[e]?t:this.$wrapData[e].length+t},e.prototype.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},e.prototype.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},e.prototype.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return this.lineWidgets&&this.lineWidgets[u]&&this.lineWidgets[u].rowsAbove&&(r+=this.lineWidgets[u].rowsAbove),{row:r,column:v+this.$getStringScreenWidth(d)[0]}},e.prototype.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},e.prototype.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},e.prototype.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},e.prototype.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},e.prototype.getPrecedingCharacter=function(){var e=this.selection.getCursor();if(e.column===0)return e.row===0?"":this.doc.getNewLineCharacter();var t=this.getLine(e.row);return t[e.column-1]},e.prototype.destroy=function(){this.destroyed||(this.bgTokenizer.setDocument(null),this.bgTokenizer.cleanup(),this.destroyed=!0),this.endOperation(),this.$stopWorker(),this.removeAllListeners(),this.doc&&this.doc.off("change",this.$onChange),this.selection&&(this.selection.off("changeCursor",this.$onSelectionChange),this.selection.off("changeSelection",this.$onSelectionChange)),this.selection.detach()},e}();m.$uid=0,m.prototype.$modes=o.$modes,m.prototype.getValue=m.prototype.toString,m.prototype.$defaultUndoManager={undo:function(){},redo:function(){},hasUndo:function(){},hasRedo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},m.prototype.$overwrite=!1,m.prototype.$mode=null,m.prototype.$modeId=null,m.prototype.$scrollTop=0,m.prototype.$scrollLeft=0,m.prototype.$wrapLimit=80,m.prototype.$useWrapMode=!1,m.prototype.$wrapLimitRange={min:null,max:null},m.prototype.lineWidgets=null,m.prototype.isFullWidth=N,r.implement(m.prototype,u);var g=1,y=2,b=3,w=4,E=9,S=10,x=11,T=12;e("./edit_session/folding").Folding.call(m.prototype),e("./edit_session/bracket_match").BracketMatch.call(m.prototype),o.defineOptions(m.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e),e>0&&this.$tabSize!==e&&(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=m}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function i(e,r){r===void 0&&(r=!0);var i=n&&t.$supportsUnicodeFlag?new RegExp("[\\p{L}\\p{N}_]","u"):new RegExp("\\w");if(i.test(e)||t.regExp)return n&&t.$supportsUnicodeFlag?r?"(?<=^|[^\\p{L}\\p{N}_])":"(?=[^\\p{L}\\p{N}_]|$)":"\\b";return""}var n=r.supportsLookbehind(),s=Array.from(e),o=s[0],u=s[s.length-1];return i(o)+e+i(u,!1)}function a(e,t,n){var r=null,i=0;while(i<=e.length){t.lastIndex=i;var s=t.exec(e);if(!s)break;var o=s.index+s[0].length;if(o>e.length-n)break;if(!r||o>r.index+r[0].length)r=s;i=s.index+1}return r}function f(e,t){var n=5e3,r={row:t,column:0},i=e.doc.positionToIndex(r),s=i+n,o=e.doc.indexToPosition(s),u=o.row;return u+1}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){function e(){this.$options={}}return e.prototype.set=function(e){return i.mixin(this.$options,e),this},e.prototype.getOptions=function(){return r.copyObject(this.$options)},e.prototype.setOptions=function(e){this.$options=e},e.prototype.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},e.prototype.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g,y=0;yy&&(y=w),o.push(new s(g.startRow,g.startCol,g.endRow,g.endCol))}}else{g=r.getMatchOffsets(i[y],u);for(var h=0;hx&&o[h].end.row==T)h--;o=o.slice(y,h+1);for(y=0,h=o.length;y=i){n+="\\";break}var o=e.charCodeAt(r);switch(o){case t.Backslash:n+="\\";break;case t.n:n+="\n";break;case t.t:n+=" "}continue}if(s===t.DollarSign){r++;if(r>=i){n+="$";break}var u=e.charCodeAt(r);if(u===t.DollarSign){n+="$$";continue}if(u===t.Digit0||u===t.Ampersand){n+="$&";continue}if(t.Digit1<=u&&u<=t.Digit9){n+="$"+e[r];continue}}n+=e[r]}return n||e},e.prototype.replace=function(e,t){var n=this.$options,r=this.$assembleRegExp(n);if(n.$isMultiLine)return t;if(!r)return;var i=this.$isMultilineSearch(n);i&&(e=e.replace(/\r\n|\r|\n/g,"\n"));var s=r.exec(e);if(!s||!i&&s[0].length!=e.length)return null;t=n.regExp?this.parseReplaceString(t):t.replace(/\$/g,"$$$$"),t=e.replace(r,t);if(n.preserveCase){t=t.split("");for(var o=Math.min(e.length,e.length);o--;){var u=e[o];u&&u.toLowerCase()!=u?t[o]=t[o].toUpperCase():t[o]=t[o].toLowerCase()}t=t.join("")}return t},e.prototype.$assembleRegExp=function(e,t){if(e.needle instanceof RegExp)return e.re=e.needle;var n=e.needle;if(!e.needle)return e.re=!1;e.regExp||(n=r.escapeRegExp(n));var i=e.caseSensitive?"gm":"gmi";try{new RegExp(n,"u"),e.$supportsUnicodeFlag=!0,i+="u"}catch(s){e.$supportsUnicodeFlag=!1}e.wholeWord&&(n=u(n,e)),e.$isMultiLine=!t&&/[\n\r]/.test(n);if(e.$isMultiLine)return e.re=this.$assembleMultilineRegExp(n,i);try{var o=new RegExp(n,i)}catch(s){o=!1}return e.re=o},e.prototype.$assembleMultilineRegExp=function(e,t){var n=e.replace(/\r\n|\r|\n/g,"$\n^").split("\n"),r=[];for(var i=0;ir)break;var a=e.getLine(o++);i=i==null?a:i+"\n"+a}var l=t.exec(i);t.lastIndex=0;if(l){var c=i.slice(0,l.index).split("\n"),h=l[0].split("\n"),p=n+c.length-1,d=c[c.length-1].length,v=p+h.length-1,m=h.length==1?d+h[0].length:h[h.length-1].length;return{startRow:p,startCol:d,endRow:v,endCol:m}}}return null},e.prototype.$multiLineBackward=function(e,t,n,r,i){var s,o=f(e,r),u=e.getLine(r).length-n;for(var l=r;l>=i;){for(var c=0;c=i;c++){var h=e.getLine(l--);s=s==null?h:h+"\n"+s}var p=a(s,t,u);if(p){var d=s.slice(0,p.index).split("\n"),v=p[0].split("\n"),m=l+d.length,g=d[d.length-1].length,y=m+v.length-1,b=v.length==1?g+v[0].length:v[v.length-1].length;return{startRow:m,startCol:g,endRow:y,endCol:b}}}return null},e.prototype.$matchIterator=function(e,t){var n=this.$assembleRegExp(t);if(!n)return!1;var i=this.$isMultilineSearch(t),s=this.$multiLineForward,o=this.$multiLineBackward,u=t.backwards==1,a=t.skipCurrent!=0,f=n.unicode,l=t.range,c=t.start;c||(c=l?l[u?"end":"start"]:e.selection.getRange()),c.start&&(c=c[a!=u?"end":"start"]);var h=l?l.start.row:0,p=l?l.end.row:e.getLength()-1;if(u)var d=function(e){var n=c.row;if(m(n,c.column,e))return;for(n--;n>=h;n--)if(m(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=p,h=c.row;n>=h;n--)if(m(n,Number.MAX_VALUE,e))return};else var d=function(e){var n=c.row;if(m(n,c.column,e))return;for(n+=1;n<=p;n++)if(m(n,0,e))return;if(t.wrap==0)return;for(n=h,p=c.row;n<=p;n++)if(m(n,0,e))return};if(t.$isMultiLine)var v=n.length,m=function(t,r,i){var s=u?t-v+1:t;if(s<0||s+v>e.getLength())return;var o=e.getLine(s),a=o.search(n[0]);if(!u&&ar)return;if(i(s,a,s+v-1,l))return!0};else if(u)var m=function(t,s,u){if(i){var a=o(e,n,s,t,h);if(!a)return!1;if(u(a.startRow,a.startCol,a.endRow,a.endCol))return!0}else{var l=e.getLine(t),c=[],p,d=0;n.lastIndex=0;while(p=n.exec(l)){var v=p[0].length;d=p.index;if(!v){if(d>=l.length)break;n.lastIndex=d+=r.skipEmptyMatch(l,d,f)}if(p.index+v>s)break;c.push(p.index,v)}for(var m=c.length-1;m>=0;m-=2){var g=c[m-1],v=c[m];if(u(t,g,t,g+v))return!0}}};else var m=function(t,o,u){n.lastIndex=o;if(i){var a=s(e,n,t,p);if(a){var l=a.endRow<=p?a.endRow-1:p;l>t&&(t=l)}if(!a)return!1;if(u(a.startRow,a.startCol,a.endRow,a.endCol))return!0}else{var c=e.getLine(t),h,d;while(d=n.exec(c)){var v=d[0].length;h=d.index;if(u(t,h,t,h+v))return!0;if(!v){n.lastIndex=h+=r.skipEmptyMatch(c,h,f);if(h>=c.length)return!1}}}};return{forEach:d}},e}();t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("../lib/keys"),s=e("../lib/useragent"),o=i.KEY_MODS,u=function(){function e(e,t){this.$init(e,t,!1)}return e.prototype.$init=function(e,t,n){this.platform=t||(s.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=n},e.prototype.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},e.prototype.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},e.prototype.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=o[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var s=this.parseKeys(e),u=o[s.hashId]+s.key;this._addCommandToBinding(r+u,t,n)},this)},e.prototype._addCommandToBinding=function(e,t,n){var r=this.commandKeyBinding,i;if(!t)delete r[e];else if(!r[e]||this.$singleCommand)r[e]=t;else{Array.isArray(r[e])?(i=r[e].indexOf(t))!=-1&&r[e].splice(i,1):r[e]=[r[e]],typeof n!="number"&&(n=a(t));var s=r[e];for(i=0;in)break}s.splice(i,0,t)}},e.prototype.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},e.prototype.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},e.prototype.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},e.prototype._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},e.prototype.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),r=i[n];if(i.FUNCTION_KEYS[r])n=i.FUNCTION_KEYS[r].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=i.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},e.prototype.findKeyCommand=function(e,t){var n=o[e]+t;return this.commandKeyBinding[n]},e.prototype.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=o[t]+n,s=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,s=this.commandKeyBinding[e.$keyChain]||s);if(s)if(s=="chainKeys"||s[s.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:s}},e.prototype.getStatusText=function(e,t){return t.$keyChain||""},e}(),f=function(e){function t(t,n){var r=e.call(this,t,n)||this;return r.$singleCommand=!0,r}return r(t,e),t}(u);f.call=function(e,t,n){u.prototype.$init.call(e,t,n,!0)},u.call=function(e,t,n){u.prototype.$init.call(e,t,n,!1)},t.HashHandler=f,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("../lib/oop"),s=e("../keyboard/hash_handler").MultiHashHandler,o=e("../lib/event_emitter").EventEmitter,u=function(e){function t(t,n){var r=e.call(this,n,t)||this;return r.byName=r.commands,r.setDefaultHandler("exec",function(e){return e.args?e.command.exec(e.editor,e.args,e.event,!1):e.command.exec(e.editor,{},e.event,!0)}),r}return r(t,e),t.prototype.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!this.canExecute(e,t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},t.prototype.canExecute=function(e,t){return typeof e=="string"&&(e=this.commands[e]),e?t&&t.$readOnly&&!e.readOnly?!1:this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t)?!1:!0:!1},t.prototype.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.off("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},t.prototype.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},t.prototype.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})},t}(s);i.implement(u.prototype,o),t.CommandManager=u}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",description:"Show settings menu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",description:"Go to next error",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",description:"Go to previous error",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",description:"Toggle fold widget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",description:"Toggle parent fold widget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldAllComments",description:"Fold all comments",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAllComments()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-\\|Ctrl-P","Command-\\"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-\\|Ctrl-Shift-P","Command-Shift-\\"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"autoindent",description:"Auto Indent",bindKey:o(null,null),exec:function(e){e.autoIndent()},scrollIntoView:"animate"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"openlink",bindKey:o("Ctrl+F3","F3"),exec:function(e){e.openLink()}},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o0||e+t=0&&this.$isFoldWidgetVisible(e-t))return e-t;if(e+t<=this.lines.getLength()-1&&this.$isFoldWidgetVisible(e+t))return e+t}return null},e.prototype.$findNearestAnnotation=function(e){if(this.$isAnnotationVisible(e))return e;var t=0;while(e-t>0||e+t=0&&this.$isAnnotationVisible(e-t))return e-t;if(e+t<=this.lines.getLength()-1&&this.$isAnnotationVisible(e+t))return e+t}return null},e.prototype.$focusFoldWidget=function(e){if(e==null)return;var t=this.$getFoldWidget(e);t.classList.add(this.editor.renderer.keyboardFocusClassName),t.focus()},e.prototype.$focusAnnotation=function(e){if(e==null)return;var t=this.$getAnnotation(e);t.classList.add(this.editor.renderer.keyboardFocusClassName),t.focus()},e.prototype.$blurFoldWidget=function(e){var t=this.$getFoldWidget(e);t.classList.remove(this.editor.renderer.keyboardFocusClassName),t.blur()},e.prototype.$blurAnnotation=function(e){var t=this.$getAnnotation(e);t.classList.remove(this.editor.renderer.keyboardFocusClassName),t.blur()},e.prototype.$moveFoldWidgetUp=function(){var e=this.activeRowIndex;while(e>0){e--;if(this.$isFoldWidgetVisible(e)){this.$blurFoldWidget(this.activeRowIndex),this.activeRowIndex=e,this.$focusFoldWidget(this.activeRowIndex);return}}return},e.prototype.$moveFoldWidgetDown=function(){var e=this.activeRowIndex;while(e0){e--;if(this.$isAnnotationVisible(e)){this.$blurAnnotation(this.activeRowIndex),this.activeRowIndex=e,this.$focusAnnotation(this.activeRowIndex);return}}return},e.prototype.$moveAnnotationDown=function(){var e=this.activeRowIndex;while(e=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/lang"),u=e("./lib/useragent"),a=e("./keyboard/textinput").TextInput,f=e("./mouse/mouse_handler").MouseHandler,l=e("./mouse/fold_handler").FoldHandler,c=e("./keyboard/keybinding").KeyBinding,h=e("./edit_session").EditSession,p=e("./search").Search,d=e("./range").Range,v=e("./lib/event_emitter").EventEmitter,m=e("./commands/command_manager").CommandManager,g=e("./commands/default_commands").commands,y=e("./config"),b=e("./token_iterator").TokenIterator,w=e("./keyboard/gutter_handler").GutterKeyboardHandler,E=e("./config").nls,S=e("./clipboard"),x=e("./lib/keys"),T=function(){function e(t,n,r){this.session,this.$toDestroy=[];var i=t.getContainerElement();this.container=i,this.renderer=t,this.id="editor"+ ++e.$uid,this.commands=new m(u.isMac?"mac":"win",g),typeof document=="object"&&(this.textInput=new a(t.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.$mouseHandler=new f(this),new l(this)),this.keyBinding=new c(this),this.$search=(new p).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on("exec",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=o.delayedCall(function(){this._signal("input",{}),this.session&&!this.session.destroyed&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on("change",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(n||r&&r.session||new h("")),y.resetOptions(this),r&&this.setOptions(r),y._signal("editor",this)}return e.prototype.$initOperationListeners=function(){this.commands.on("exec",this.startOperation.bind(this),!0),this.commands.on("afterExec",this.endOperation.bind(this),!0)},e.prototype.startOperation=function(e){this.session.startOperation(e)},e.prototype.endOperation=function(e){this.session.endOperation(e)},e.prototype.onStartOperation=function(e){this.curOp=this.session.curOp,this.curOp.scrollTop=this.renderer.scrollTop,this.prevOp=this.session.prevOp,e||(this.previousCommand=null)},e.prototype.onEndOperation=function(e){if(this.curOp&&this.session){if(e&&e.returnValue===!1){this.curOp=null;return}this._signal("beforeEndOperation");if(!this.curOp)return;var t=this.curOp.command,n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.$lastSel=this.session.selection.toJSON(),this.prevOp=this.curOp,this.curOp=null}},e.prototype.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},e.prototype.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;y.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},e.prototype.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},e.prototype.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange),this.session.off("startOperation",this.$onStartOperation),this.session.off("endOperation",this.$onEndOperation);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.$onStartOperation=this.onStartOperation.bind(this),this.session.on("startOperation",this.$onStartOperation),this.$onEndOperation=this.onEndOperation.bind(this),this.session.on("endOperation",this.$onEndOperation),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),t&&(t.$editor=null),e&&e._signal("changeEditor",{editor:this}),e&&(e.$editor=this),e&&!e.destroyed&&e.bgTokenizer.scheduleStart()},e.prototype.getSession=function(){return this.session},e.prototype.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},e.prototype.getValue=function(){return this.session.getValue()},e.prototype.getSelection=function(){return this.selection},e.prototype.resize=function(e){this.renderer.onResize(e)},e.prototype.setTheme=function(e,t){this.renderer.setTheme(e,t)},e.prototype.getTheme=function(){return this.renderer.getTheme()},e.prototype.setStyle=function(e){this.renderer.setStyle(e)},e.prototype.unsetStyle=function(e){this.renderer.unsetStyle(e)},e.prototype.getFontSize=function(){return this.getOption("fontSize")||s.computedStyle(this.container).fontSize},e.prototype.setFontSize=function(e){this.setOption("fontSize",e)},e.prototype.$highlightBrackets=function(){if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||t.destroyed)return;t.$bracketHighlight&&(t.$bracketHighlight.markerIds.forEach(function(e){t.removeMarker(e)}),t.$bracketHighlight=null);var n=e.getCursorPosition(),r=e.getKeyboardHandler(),i=r&&r.$getDirectionForHighlight&&r.$getDirectionForHighlight(e),s=t.getMatchingBracketRanges(n,i);if(!s){var o=new b(t,n.row,n.column),u=o.getCurrentToken();if(u&&/\b(?:tag-open|tag-name)/.test(u.type)){var a=t.getMatchingTags(n);a&&(s=[a.openTagName.isEmpty()?a.openTag:a.openTagName,a.closeTagName.isEmpty()?a.closeTag:a.closeTagName])}}!s&&t.$mode.getMatching&&(s=t.$mode.getMatching(e.session));if(!s){e.getHighlightIndentGuides()&&e.renderer.$textLayer.$highlightIndentGuide();return}var f="ace_bracket";Array.isArray(s)?s.length==1&&(f="ace_error_bracket"):s=[s],s.length==2&&(d.comparePoints(s[0].end,s[1].start)==0?s=[d.fromPoints(s[0].start,s[1].end)]:d.comparePoints(s[0].start,s[1].end)==0&&(s=[d.fromPoints(s[1].start,s[0].end)])),t.$bracketHighlight={ranges:s,markerIds:s.map(function(e){return t.addMarker(e,f,"text")})},e.getHighlightIndentGuides()&&e.renderer.$textLayer.$highlightIndentGuide()},50)},e.prototype.focus=function(){this.textInput.focus()},e.prototype.isFocused=function(){return this.textInput.isFocused()},e.prototype.blur=function(){this.textInput.blur()},e.prototype.onFocus=function(e){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus",e)},e.prototype.onBlur=function(e){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur",e)},e.prototype.$cursorChange=function(){this.renderer.updateCursor(),this.$highlightBrackets(),this.$updateHighlightActiveLine()},e.prototype.onDocumentChange=function(e){var t=this.session.$useWrapMode,n=e.start.row==e.end.row?e.end.row:Infinity;this.renderer.updateLines(e.start.row,n,t),this._signal("change",e),this.$cursorChange()},e.prototype.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},e.prototype.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},e.prototype.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},e.prototype.onCursorChange=function(){this.$cursorChange(),this._signal("changeSelection")},e.prototype.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!="line"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.theme&&this.renderer.theme.$selectionColorConflict&&!this.selection.isEmpty()&&(t=!1),this.renderer.$maxLines&&this.session.getLength()===1&&!(this.renderer.$minLines>1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new d(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},e.prototype.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},e.prototype.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},e.prototype.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},e.prototype.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},e.prototype.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},e.prototype.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},e.prototype.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},e.prototype.onChangeWrapLimit=function(){this.renderer.updateFull()},e.prototype.onChangeWrapMode=function(){this.renderer.onResize(!0)},e.prototype.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},e.prototype.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},e.prototype.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;iu.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e);n.insert(i,e),s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new d(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new d(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(this.$enableAutoIndent){if(n.getDocument().isNewLine(e)){var h=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},h)}c&&r.autoOutdent(l,n,i.row)}},e.prototype.autoIndent=function(){var e=this.session,t=e.getMode(),n=this.selection.isEmpty()?[new d(0,0,e.doc.getLength()-1,0)]:this.selection.getAllRanges(),r="",i="",s="",o=e.getTabString();for(var u=0;u0&&(r=e.getState(l-1),i=e.getLine(l-1),s=t.getNextLineIndent(r,i,o));var c=e.getLine(l),h=t.$getIndent(c);if(s!==h){if(h.length>0){var p=new d(l,0,l,h.length);e.remove(p)}s.length>0&&e.insert({row:l,column:0},s)}t.autoOutdent(r,e,l)}}},e.prototype.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},e.prototype.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,n.start.column<0&&(n.start.row--,n.start.column+=this.session.getLine(n.start.row).length+1),this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},e.prototype.onCommandKey=function(e,t,n){return this.keyBinding.onCommandKey(e,t,n)},e.prototype.setOverwrite=function(e){this.session.setOverwrite(e)},e.prototype.getOverwrite=function(){return this.session.getOverwrite()},e.prototype.toggleOverwrite=function(){this.session.toggleOverwrite()},e.prototype.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},e.prototype.getScrollSpeed=function(){return this.getOption("scrollSpeed")},e.prototype.setDragDelay=function(e){this.setOption("dragDelay",e)},e.prototype.getDragDelay=function(){return this.getOption("dragDelay")},e.prototype.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},e.prototype.getSelectionStyle=function(){return this.getOption("selectionStyle")},e.prototype.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},e.prototype.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},e.prototype.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},e.prototype.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},e.prototype.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},e.prototype.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},e.prototype.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},e.prototype.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},e.prototype.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},e.prototype.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},e.prototype.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},e.prototype.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},e.prototype.setHighlightIndentGuides=function(e){this.renderer.setHighlightIndentGuides(e)},e.prototype.getHighlightIndentGuides=function(){return this.renderer.getHighlightIndentGuides()},e.prototype.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},e.prototype.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},e.prototype.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},e.prototype.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},e.prototype.setReadOnly=function(e){this.setOption("readOnly",e)},e.prototype.getReadOnly=function(){return this.getOption("readOnly")},e.prototype.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},e.prototype.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},e.prototype.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},e.prototype.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},e.prototype.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},e.prototype.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},e.prototype.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},e.prototype.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},e.prototype.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},e.prototype.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},e.prototype.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},e.prototype.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},e.prototype.setGhostText=function(e,t){this.renderer.setGhostText(e,t)},e.prototype.removeGhostText=function(){this.renderer.removeGhostText()},e.prototype.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new d(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},e.prototype.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},e.prototype.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},e.prototype.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},e.prototype.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new d(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&s<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;h=a&&u<=f&&p.match(/((?:https?|ftp):\/\/[\S]+)/)){l=p.replace(/[\s:.,'";}\]]+$/,"");break}a=f}}catch(d){n={error:d}}finally{try{h&&!h.done&&(i=c.return)&&i.call(c)}finally{if(n)throw n.error}}return l},e.prototype.openLink=function(){var e=this.selection.getCursor(),t=this.findLinkAt(e.row,e.column);return t&&window.open(t,"_blank"),t!=null},e.prototype.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},e.prototype.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange(),r=e.isBackwards();if(n.isEmpty()){var i=n.start.row;t.duplicateLines(i,i)}else{var s=r?n.start:n.end,o=t.insert(s,t.getTextRange(n));n.start=s,n.end=o,e.setSelectionRange(n,r)}},e.prototype.moveLinesDown=function(){this.$moveLines(1,!1)},e.prototype.moveLinesUp=function(){this.$moveLines(-1,!1)},e.prototype.moveText=function(e,t,n){return this.session.moveText(e,t,n)},e.prototype.copyLinesUp=function(){this.$moveLines(-1,!0)},e.prototype.copyLinesDown=function(){this.$moveLines(1,!0)},e.prototype.$moveLines=function(e,t){var n,r,i=this.selection;if(!i.inMultiSelectMode||this.inVirtualSelectionMode){var s=i.toOrientedRange();n=this.$getSelectedRows(s),r=this.session.$moveLines(n.first,n.last,t?0:e),t&&e==-1&&(r=0),s.moveBy(r,0),i.fromOrientedRange(s)}else{var o=i.rangeList.ranges;i.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;var u=0,a=0,f=o.length;for(var l=0;lp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},e.prototype.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},e.prototype.onCompositionStart=function(e){this.renderer.showComposition(e)},e.prototype.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},e.prototype.onCompositionEnd=function(){this.renderer.hideComposition()},e.prototype.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},e.prototype.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},e.prototype.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},e.prototype.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},e.prototype.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},e.prototype.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},e.prototype.selectPageDown=function(){this.$moveByPage(1,!0)},e.prototype.selectPageUp=function(){this.$moveByPage(-1,!0)},e.prototype.gotoPageDown=function(){this.$moveByPage(1,!1)},e.prototype.gotoPageUp=function(){this.$moveByPage(-1,!1)},e.prototype.scrollPageDown=function(){this.$moveByPage(1)},e.prototype.scrollPageUp=function(){this.$moveByPage(-1)},e.prototype.scrollToRow=function(e){this.renderer.scrollToRow(e)},e.prototype.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},e.prototype.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},e.prototype.getCursorPosition=function(){return this.selection.getCursor()},e.prototype.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},e.prototype.getSelectionRange=function(){return this.selection.getRange()},e.prototype.selectAll=function(){this.selection.selectAll()},e.prototype.clearSelection=function(){this.selection.clearSelection()},e.prototype.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},e.prototype.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},e.prototype.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new b(this.session,n.row,n.column),i=r.getCurrentToken(),s=0;i&&i.type.indexOf("tag-name")!==-1&&(i=r.stepBackward());var o=i||r.stepForward();if(!o)return;var u,a=!1,f={},l=n.column-o.start,c,h={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(o.value.match(/[{}()\[\]]/g))for(;l1?f[o.value]++:i.value===""&&f[o.value]--,f[o.value]===-1&&(u="tag",a=!0));a||(i=o,s++,o=r.stepForward(),l=0)}while(o&&!a);if(!u)return;var p,v;if(u==="bracket"){p=this.session.getBracketRange(n);if(!p){p=new d(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+l-1,r.getCurrentTokenRow(),r.getCurrentTokenColumn()+l-1),v=p.start;if(t||v.row===n.row&&Math.abs(v.column-n.column)<2)p=this.session.getBracketRange(v)}}else if(u==="tag"){if(!o||o.type.indexOf("tag-name")===-1)return;p=new d(r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2,r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2);if(p.compare(n.row,n.column)===0){var m=this.session.getMatchingTags(n);m&&(m.openTag.contains(n.row,n.column)?(p=m.closeTag,v=p.start):(p=m.openTag,m.closeTag.start.row===n.row&&m.closeTag.start.column===n.column?v=p.end:v=p.start))}v=v||p.start}v=p&&p.cursor||v,v&&(e?p&&t?this.selection.setRange(p):p&&p.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(v.row,v.column):this.selection.moveTo(v.row,v.column))},e.prototype.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.exitMultiSelectMode&&this.exitMultiSelectMode(),this.moveCursorTo(e-1,t||0),this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},e.prototype.navigateTo=function(e,t){this.selection.moveTo(e,t)},e.prototype.navigateUp=function(e){if(this.selection.isMultiLine()&&!this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(-e||-1,0)},e.prototype.navigateDown=function(e){if(this.selection.isMultiLine()&&this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(e||1,0)},e.prototype.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},e.prototype.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},e.prototype.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},e.prototype.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},e.prototype.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},e.prototype.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},e.prototype.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},e.prototype.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},e.prototype.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end),r):r},e.prototype.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;var i=this.getSelectionRange();this.selection.moveTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},e.prototype.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},e.prototype.getLastSearchOptions=function(){return this.$search.getOptions()},e.prototype.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&i.mixin(t,e);var r=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(r)||this.$search.$options.needle,e||(r=this.session.getWordRange(r.start.row,r.start.column),e=this.session.getTextRange(r)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:r});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?r.start=r.end:r.end=r.start,this.selection.setRange(r)},e.prototype.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},e.prototype.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},e.prototype.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},e.prototype.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},e.prototype.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},e.prototype.destroy=function(){this.$toDestroy&&(this.$toDestroy.forEach(function(e){e.destroy()}),this.$toDestroy=null),this.$mouseHandler&&this.$mouseHandler.destroy(),this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy(),this._$emitInputEvent&&this._$emitInputEvent.cancel(),this.removeAllListeners()},e.prototype.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},e.prototype.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",s.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},e.prototype.prompt=function(e,t,n){var r=this;y.loadModule("ace/ext/prompt",function(i){i.prompt(r,e,t,n)})},e}();T.$uid=0,T.prototype.curOp=null,T.prototype.prevOp={},T.prototype.$mergeableCommands=["backspace","del","insertstring"],T.prototype.$toggleWordPairs=[["first","last"],["true","false"],["yes","no"],["width","height"],["top","bottom"],["right","left"],["on","off"],["x","y"],["get","set"],["max","min"],["horizontal","vertical"],["show","hide"],["add","remove"],["up","down"],["before","after"],["even","odd"],["in","out"],["inside","outside"],["next","previous"],["increase","decrease"],["attach","detach"],["&&","||"],["==","!="]],i.implement(T.prototype,v),y.defineOptions(T.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},enableAutoIndent:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?N.attach(this):N.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?N.attach(this):N.detach(this)}},placeholder:{set:function(e){this.$updatePlaceholder||(this.$updatePlaceholder=function(){var e=this.session&&(this.renderer.$composition||this.session.getLength()>1||this.session.getLine(0).length>0);if(e&&this.renderer.placeholderNode)this.renderer.off("afterRender",this.$updatePlaceholder),s.removeCssClass(this.container,"ace_hasPlaceholder"),this.renderer.placeholderNode.remove(),this.renderer.placeholderNode=null;else if(!e&&!this.renderer.placeholderNode){this.renderer.on("afterRender",this.$updatePlaceholder),s.addCssClass(this.container,"ace_hasPlaceholder");var t=s.createElement("div");t.className="ace_placeholder",t.textContent=this.$placeholder||"",this.renderer.placeholderNode=t,this.renderer.content.appendChild(this.renderer.placeholderNode)}else!e&&this.renderer.placeholderNode&&(this.renderer.placeholderNode.textContent=this.$placeholder||"")}.bind(this),this.on("input",this.$updatePlaceholder)),this.$updatePlaceholder()}},enableKeyboardAccessibility:{set:function(e){var t={name:"blurTextInput",description:"Set focus to the editor content div to allow tabbing through the page",bindKey:"Esc",exec:function(e){e.blur(),e.renderer.scroller.focus()},readOnly:!0},n=function(e){if(e.target==this.renderer.scroller&&e.keyCode===x.enter){e.preventDefault();var t=this.getCursorPosition().row;this.isRowVisible(t)||this.scrollToLine(t,!0,!0),this.focus()}},r;e?(this.renderer.enableKeyboardAccessibility=!0,this.renderer.keyboardFocusClassName="ace_keyboard-focus",this.textInput.getElement().setAttribute("tabindex",-1),this.textInput.setNumberOfExtraLines(u.isWin?3:0),this.renderer.scroller.setAttribute("tabindex",0),this.renderer.scroller.setAttribute("role","group"),this.renderer.scroller.setAttribute("aria-roledescription",E("editor.scroller.aria-roledescription","editor")),this.renderer.scroller.classList.add(this.renderer.keyboardFocusClassName),this.renderer.scroller.setAttribute("aria-label",E("editor.scroller.aria-label","Editor content, press Enter to start editing, press Escape to exit")),this.renderer.scroller.addEventListener("keyup",n.bind(this)),this.commands.addCommand(t),this.renderer.$gutter.setAttribute("tabindex",0),this.renderer.$gutter.setAttribute("aria-hidden",!1),this.renderer.$gutter.setAttribute("role","group"),this.renderer.$gutter.setAttribute("aria-roledescription",E("editor.gutter.aria-roledescription","editor gutter")),this.renderer.$gutter.setAttribute("aria-label",E("editor.gutter.aria-label","Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit")),this.renderer.$gutter.classList.add(this.renderer.keyboardFocusClassName),this.renderer.content.setAttribute("aria-hidden",!0),r||(r=new w(this)),r.addListener(),this.textInput.setAriaOptions({setLabel:!0})):(this.renderer.enableKeyboardAccessibility=!1,this.textInput.getElement().setAttribute("tabindex",0),this.textInput.setNumberOfExtraLines(0),this.renderer.scroller.setAttribute("tabindex",-1),this.renderer.scroller.removeAttribute("role"),this.renderer.scroller.removeAttribute("aria-roledescription"),this.renderer.scroller.classList.remove(this.renderer.keyboardFocusClassName),this.renderer.scroller.removeAttribute("aria-label"),this.renderer.scroller.removeEventListener("keyup",n.bind(this)),this.commands.removeCommand(t),this.renderer.content.removeAttribute("aria-hidden"),this.renderer.$gutter.setAttribute("tabindex",-1),this.renderer.$gutter.setAttribute("aria-hidden",!0),this.renderer.$gutter.removeAttribute("role"),this.renderer.$gutter.removeAttribute("aria-roledescription"),this.renderer.$gutter.removeAttribute("aria-label"),this.renderer.$gutter.classList.remove(this.renderer.keyboardFocusClassName),r&&r.removeListener())},initialValue:!1},textInputAriaLabel:{set:function(e){this.$textInputAriaLabel=e},initialValue:""},enableMobileMenu:{set:function(e){this.$enableMobileMenu=e},initialValue:!0},customScrollbar:"renderer",hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",highlightIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",useResizeObserver:"renderer",useSvgGutterIcons:"renderer",showFoldedAnnotations:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var N={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=T}),ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(){function e(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0}return e.prototype.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},e.prototype.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},e.prototype.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},e.prototype.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLineCount(e)},e.prototype.getLength=function(){return this.cells.length},e.prototype.get=function(e){return this.cells[e]},e.prototype.shift=function(){this.$cacheCell(this.cells.shift())},e.prototype.pop=function(){this.$cacheCell(this.cells.pop())},e.prototype.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,l),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},e.prototype.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},e.prototype.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},e.prototype.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},e.prototype.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},e.prototype.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,l);this.$renderCell(u,e,s,i),r.push(u),i++}return r},e.prototype.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],f=s.childNodes[1],l=s.childNodes[2],c=l.firstChild,h=o.$firstLineNumber,p=o.$breakpoints,d=o.$decorations,v=o.gutterRenderer||this.$renderer,m=this.$showFoldWidgets&&o.foldWidgets,g=n?n.start.row:Number.MAX_VALUE,y=t.lineHeight+"px",b=this.$useSvgGutterIcons?"ace_gutter-cell_svg-icons ":"ace_gutter-cell ",w=this.$useSvgGutterIcons?"ace_icon_svg":"ace_icon",E=(v?v.getText(o,i):i+h).toString();this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=g&&this.$cursorRow<=n.end.row)&&(b+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),p[i]&&(b+=p[i]),d[i]&&(b+=d[i]),this.$annotations[i]&&i!==g&&(b+=this.$annotations[i].className);if(m){var S=m[i];S==null&&(S=m[i]=o.getFoldWidget(i))}if(S){var x="ace_fold-widget ace_"+S,T=S=="start"&&i==g&&in.right-t.right)return"foldWidgets"},e}();f.prototype.$fixedWidth=!1,f.prototype.$highlightGutterLine=!0,f.prototype.$renderer="",f.prototype.$showLineNumbers=!0,f.prototype.$showFoldWidgets=!0,i.implement(f.prototype,o),t.Gutter=f}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";function o(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}var r=e("../range").Range,i=e("../lib/dom"),s=function(){function e(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)}return e.prototype.setPadding=function(e){this.$padding=e},e.prototype.setSession=function(e){this.session=e},e.prototype.setMarkers=function(e){this.markers=e},e.prototype.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},e.prototype.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),i,l==f?0:1,s)},e.prototype.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:"+s+"px;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:"+s+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},e.prototype.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},e.prototype.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+(e.width+(i||0))+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},e.prototype.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},e.prototype.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},e}();s.prototype.$padding=0,t.Marker=s}),ace.define("ace/layer/text_util",["require","exports","module"],function(e,t,n){var r=new Set(["text","rparen","lparen"]);t.isTextToken=function(e){return r.has(e)}}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter","ace/config","ace/layer/text_util"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=e("../config").nls,f=e("./text_util").isTextToken,l=function(){function e(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)}return e.prototype.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},e.prototype.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},e.prototype.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},e.prototype.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},e.prototype.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},e.prototype.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},e.prototype.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},e.prototype.setSession=function(e){this.session=e,e&&this.$computeTabString()},e.prototype.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,typeof e=="string"?(this.showSpaces=/tab/i.test(e),this.showTabs=/space/i.test(e),this.showEOL=/eol/i.test(e)):this.showSpaces=this.showTabs=this.showEOL=e,this.$computeTabString(),!0)},e.prototype.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},e.prototype.setHighlightIndentGuides=function(e){return this.$highlightIndentGuides===e?!1:(this.$highlightIndentGuides=e,e)},e.prototype.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1),f&&(c.style.top=this.$lines.computeLineTop(u,e,this.session)+"px");var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow)),this.$highlightIndentGuide()},e.prototype.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},e.prototype.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},e.prototype.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC\u2066\u2067\u2068\u202A\u202B\u202D\u202E\u202C\u2069\u2060\u2061\u2062\u2063\u2064\u206A\u206B\u206B\u206C\u206D\u206E\u206F]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),l,c=0;while(l=o.exec(r)){var h=l[1],p=l[2],d=l[3],v=l[4],m=l[5];if(!i.showSpaces&&p)continue;var g=c!=l.index?r.slice(c,l.index):"";c=l.index+l[0].length,g&&u.appendChild(this.dom.createTextNode(g,this.element));if(h){var y=i.session.getScreenTabSize(t+l.index);u.appendChild(i.$tabStrings[y].cloneNode(!0)),t+=y-1}else if(p)if(i.showSpaces){var b=this.dom.createElement("span");b.className="ace_invisible ace_invisible_space",b.textContent=s.stringRepeat(i.SPACE_CHAR,p.length),u.appendChild(b)}else u.appendChild(this.dom.createTextNode(p,this.element));else if(d){var b=this.dom.createElement("span");b.className="ace_invisible ace_invisible_space ace_invalid",b.textContent=s.stringRepeat(i.SPACE_CHAR,d.length),u.appendChild(b)}else if(v){t+=1;var b=this.dom.createElement("span");b.style.width=i.config.characterWidth*2+"px",b.className=i.showSpaces?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",b.textContent=i.showSpaces?i.SPACE_CHAR:v,u.appendChild(b)}else if(m){t+=1;var b=this.dom.createElement("span");b.style.width=i.config.characterWidth*2+"px",b.className="ace_cjk",b.textContent=m,u.appendChild(b)}}u.appendChild(this.dom.createTextNode(c?r.slice(c):r,this.element));if(!f(n.type)){var w="ace_"+n.type.replace(/\./g," ace_"),b=this.dom.createElement("span");n.type=="fold"&&(b.style.width=n.value.length*this.config.characterWidth+"px",b.setAttribute("title",a("inline-fold.closed.title","Unfold code"))),b.className=w,b.appendChild(u),e.appendChild(b)}else e.appendChild(u);return t+r.length},e.prototype.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;ss[o].start.row?this.$highlightIndentGuideMarker.dir=-1:this.$highlightIndentGuideMarker.dir=1;break}}if(!this.$highlightIndentGuideMarker.end&&e[t.row]!==""&&t.column===e[t.row].length){this.$highlightIndentGuideMarker.dir=1;for(var o=t.row+1;o0))return;r=e.element.childNodes[0]}var i=r.childNodes;if(i){var s=i[t-1];s&&s.classList&&s.classList.contains("ace_indent-guide")&&s.classList.add("ace_indent-guide-active")}}},e.prototype.$renderHighlightIndentGuide=function(){if(!this.$lines)return;var e=this.$lines.cells;this.$clearActiveIndentGuide();var t=this.$highlightIndentGuideMarker.indentLevel;if(t!==0)if(this.$highlightIndentGuideMarker.dir===1)for(var n=0;n=this.$highlightIndentGuideMarker.start+1){if(r.row>=this.$highlightIndentGuideMarker.end)break;this.$setIndentGuideActive(r,t)}}else for(var n=e.length-1;n>=0;n--){var r=e[n];if(this.$highlightIndentGuideMarker.end&&r.row=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}n[n.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,u,null,"",!0)},e.prototype.$renderSimpleLine=function(e,t){var n=0;for(var r=0;rthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,i,s);n=this.$renderToken(e,n,i,s)}},e.prototype.$renderOverflowMessage=function(e,t,n,r,i){n&&this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var s=this.dom.createElement("span");s.className="ace_inline_button ace_keyword ace_toggle_wrap",s.textContent=i?"":"",e.appendChild(s)},e.prototype.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showEOL&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},e.prototype.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},e.prototype.$useLineGroups=function(){return this.session.getUseWrapMode()},e}();l.prototype.EOF_CHAR="\u00b6",l.prototype.EOL_CHAR_LF="\u00ac",l.prototype.EOL_CHAR_CRLF="\u00a4",l.prototype.EOL_CHAR=l.prototype.EOL_CHAR_LF,l.prototype.TAB_CHAR="\u2014",l.prototype.SPACE_CHAR="\u00b7",l.prototype.$padding=0,l.prototype.MAX_LINE_LENGTH=1e4,l.prototype.showInvisibles=!1,l.prototype.showSpaces=!1,l.prototype.showTabs=!1,l.prototype.showEOL=!1,l.prototype.displayIndentGuides=!0,l.prototype.$highlightIndentGuides=!0,l.prototype.$tabStrings=[],l.prototype.destroy={},l.prototype.onChangeTabSize=l.prototype.$computeTabString,r.implement(l.prototype,u),t.Text=l}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(){function e(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)}return e.prototype.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},e.prototype.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";this.$isAnimating=!0,setTimeout(function(){this.$isAnimating&&r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},e.prototype.$stopCssAnimation=function(){this.$isAnimating=!1,r.removeCssClass(this.element,"ace_animate-blinking")},e.prototype.setPadding=function(e){this.$padding=e},e.prototype.setSession=function(e){this.session=e},e.prototype.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},e.prototype.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},e.prototype.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},e.prototype.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},e.prototype.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},e.prototype.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},e.prototype.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},e.prototype.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&(this.$isSmoothBlinking=!1,r.removeCssClass(this.element,"ace_smooth-blinking")),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&(this.$isSmoothBlinking=!0,setTimeout(function(){this.$isSmoothBlinking&&r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this)));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},e.prototype.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},e.prototype.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},e.prototype.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},e.prototype.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)},e}();i.prototype.$padding=0,i.prototype.drawCursor=null,t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/event"),u=e("./lib/event_emitter").EventEmitter,a=32768,f=function(){function e(e,t){this.element=s.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+t,this.inner=s.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent="\u00a0",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addListener(this.element,"scroll",this.onScroll.bind(this)),o.addListener(this.element,"mousedown",o.preventDefault)}return e.prototype.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1},e}();i.implement(f.prototype,u);var l=function(e){function t(t,n){var r=e.call(this,t,"-v")||this;return r.scrollTop=0,r.scrollHeight=0,n.$scrollbarWidth=r.width=s.scrollbarWidth(t.ownerDocument),r.inner.style.width=r.element.style.width=(r.width||15)+5+"px",r.$minWidth=0,r}return r(t,e),t.prototype.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},t.prototype.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},t.prototype.setHeight=function(e){this.element.style.height=e+"px"},t.prototype.setScrollHeight=function(e){this.scrollHeight=e,e>a?(this.coeff=a/e,e=a):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},t.prototype.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)},t}(f);l.prototype.setInnerHeight=l.prototype.setScrollHeight;var c=function(e){function t(t,n){var r=e.call(this,t,"-h")||this;return r.scrollLeft=0,r.height=n.$scrollbarWidth,r.inner.style.height=r.element.style.height=(r.height||15)+5+"px",r}return r(t,e),t.prototype.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},t.prototype.getHeight=function(){return this.isVisible?this.height:0},t.prototype.setWidth=function(e){this.element.style.width=e+"px"},t.prototype.setInnerWidth=function(e){this.inner.style.width=e+"px"},t.prototype.setScrollWidth=function(e){this.inner.style.width=e+"px"},t.prototype.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)},t}(f);t.ScrollBar=l,t.ScrollBarV=l,t.ScrollBarH=c,t.VScrollBar=l,t.HScrollBar=c}),ace.define("ace/scrollbar_custom",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=this&&this.__extends||function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){function r(){this.constructor=t}if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");e(t,n),t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}}(),i=e("./lib/oop"),s=e("./lib/dom"),o=e("./lib/event"),u=e("./lib/event_emitter").EventEmitter;s.importCssString(".ace_editor>.ace_sb-v div, .ace_editor>.ace_sb-h div{\n position: absolute;\n background: rgba(128, 128, 128, 0.6);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid #bbb;\n border-radius: 2px;\n z-index: 8;\n}\n.ace_editor>.ace_sb-v, .ace_editor>.ace_sb-h {\n position: absolute;\n z-index: 6;\n background: none;\n overflow: hidden!important;\n}\n.ace_editor>.ace_sb-v {\n z-index: 6;\n right: 0;\n top: 0;\n width: 12px;\n}\n.ace_editor>.ace_sb-v div {\n z-index: 8;\n right: 0;\n width: 100%;\n}\n.ace_editor>.ace_sb-h {\n bottom: 0;\n left: 0;\n height: 12px;\n}\n.ace_editor>.ace_sb-h div {\n bottom: 0;\n height: 100%;\n}\n.ace_editor>.ace_sb_grabbed {\n z-index: 8;\n background: #000;\n}","ace_scrollbar.css",!1);var a=function(){function e(e,t){this.element=s.createElement("div"),this.element.className="ace_sb"+t,this.inner=s.createElement("div"),this.inner.className="",this.element.appendChild(this.inner),this.VScrollWidth=12,this.HScrollHeight=12,e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,o.addMultiMouseDownListener(this.element,[500,300,300],this,"onMouseDown")}return e.prototype.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1},e}();i.implement(a.prototype,u);var f=function(e){function t(t,n){var r=e.call(this,t,"-v")||this;return r.scrollTop=0,r.scrollHeight=0,r.parent=t,r.width=r.VScrollWidth,r.renderer=n,r.inner.style.width=r.element.style.width=(r.width||15)+"px",r.$minWidth=0,r}return r(t,e),t.prototype.onMouseDown=function(e,t){if(e!=="mousedown")return;if(o.getButton(t)!==0||t.detail===2)return;if(t.target===this.inner){var n=this,r=t.clientY,i=function(e){r=e.clientY},s=function(){clearInterval(l)},u=t.clientY,a=this.thumbTop,f=function(){if(r===undefined)return;var e=n.scrollTopFromThumbTop(a+r-u);if(e===n.scrollTop)return;n._emit("scroll",{data:e})};o.capture(this.inner,i,s);var l=setInterval(f,20);return o.preventDefault(t)}var c=t.clientY-this.element.getBoundingClientRect().top-this.thumbHeight/2;return this._emit("scroll",{data:this.scrollTopFromThumbTop(c)}),o.preventDefault(t)},t.prototype.getHeight=function(){return this.height},t.prototype.scrollTopFromThumbTop=function(e){var t=e*(this.pageHeight-this.viewHeight)/(this.slideHeight-this.thumbHeight);return t>>=0,t<0?t=0:t>this.pageHeight-this.viewHeight&&(t=this.pageHeight-this.viewHeight),t},t.prototype.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},t.prototype.setHeight=function(e){this.height=Math.max(0,e),this.slideHeight=this.height,this.viewHeight=this.height,this.setScrollHeight(this.pageHeight,!0)},t.prototype.setScrollHeight=function(e,t){if(this.pageHeight===e&&!t)return;this.pageHeight=e,this.thumbHeight=this.slideHeight*this.viewHeight/this.pageHeight,this.thumbHeight>this.slideHeight&&(this.thumbHeight=this.slideHeight),this.thumbHeight<15&&(this.thumbHeight=15),this.inner.style.height=this.thumbHeight+"px",this.scrollTop>this.pageHeight-this.viewHeight&&(this.scrollTop=this.pageHeight-this.viewHeight,this.scrollTop<0&&(this.scrollTop=0),this._emit("scroll",{data:this.scrollTop}))},t.prototype.setScrollTop=function(e){this.scrollTop=e,e<0&&(e=0),this.thumbTop=e*(this.slideHeight-this.thumbHeight)/(this.pageHeight-this.viewHeight),this.inner.style.top=this.thumbTop+"px"},t}(a);f.prototype.setInnerHeight=f.prototype.setScrollHeight;var l=function(e){function t(t,n){var r=e.call(this,t,"-h")||this;return r.scrollLeft=0,r.scrollWidth=0,r.height=r.HScrollHeight,r.inner.style.height=r.element.style.height=(r.height||12)+"px",r.renderer=n,r}return r(t,e),t.prototype.onMouseDown=function(e,t){if(e!=="mousedown")return;if(o.getButton(t)!==0||t.detail===2)return;if(t.target===this.inner){var n=this,r=t.clientX,i=function(e){r=e.clientX},s=function(){clearInterval(l)},u=t.clientX,a=this.thumbLeft,f=function(){if(r===undefined)return;var e=n.scrollLeftFromThumbLeft(a+r-u);if(e===n.scrollLeft)return;n._emit("scroll",{data:e})};o.capture(this.inner,i,s);var l=setInterval(f,20);return o.preventDefault(t)}var c=t.clientX-this.element.getBoundingClientRect().left-this.thumbWidth/2;return this._emit("scroll",{data:this.scrollLeftFromThumbLeft(c)}),o.preventDefault(t)},t.prototype.getHeight=function(){return this.isVisible?this.height:0},t.prototype.scrollLeftFromThumbLeft=function(e){var t=e*(this.pageWidth-this.viewWidth)/(this.slideWidth-this.thumbWidth);return t>>=0,t<0?t=0:t>this.pageWidth-this.viewWidth&&(t=this.pageWidth-this.viewWidth),t},t.prototype.setWidth=function(e){this.width=Math.max(0,e),this.element.style.width=this.width+"px",this.slideWidth=this.width,this.viewWidth=this.width,this.setScrollWidth(this.pageWidth,!0)},t.prototype.setScrollWidth=function(e,t){if(this.pageWidth===e&&!t)return;this.pageWidth=e,this.thumbWidth=this.slideWidth*this.viewWidth/this.pageWidth,this.thumbWidth>this.slideWidth&&(this.thumbWidth=this.slideWidth),this.thumbWidth<15&&(this.thumbWidth=15),this.inner.style.width=this.thumbWidth+"px",this.scrollLeft>this.pageWidth-this.viewWidth&&(this.scrollLeft=this.pageWidth-this.viewWidth,this.scrollLeft<0&&(this.scrollLeft=0),this._emit("scroll",{data:this.scrollLeft}))},t.prototype.setScrollLeft=function(e){this.scrollLeft=e,e<0&&(e=0),this.thumbLeft=e*(this.slideWidth-this.thumbWidth)/(this.pageWidth-this.viewWidth),this.inner.style.left=this.thumbLeft+"px"},t}(a);l.prototype.setInnerWidth=l.prototype.setScrollWidth,t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(){function e(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}}return e.prototype.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},e.prototype.clear=function(e){var t=this.changes;return this.changes=0,t},e}();t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=512,l=typeof ResizeObserver=="function",c=200,h=function(){function e(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.textContent=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()}return e.prototype.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},e.prototype.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},e.prototype.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){e.checkForSizeChanges()}),this.$observer.observe(this.$measureNode)},e.prototype.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},e.prototype.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},e.prototype.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},e.prototype.$measureCharWidth=function(e){this.$main.textContent=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},e.prototype.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},e.prototype.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},e.prototype.$getZoom=function(e){return!e||!e.parentElement?1:(Number(window.getComputedStyle(e).zoom)||1)*this.$getZoom(e.parentElement)},e.prototype.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},e.prototype.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)},e}();h.prototype.$characterSize={width:0,height:0},r.implement(h.prototype,a),t.FontMetrics=h}),ace.define("ace/css/editor-css",["require","exports","module"],function(e,t,n){n.exports='\n.ace_br1 {border-top-left-radius : 3px;}\n.ace_br2 {border-top-right-radius : 3px;}\n.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}\n.ace_br4 {border-bottom-right-radius: 3px;}\n.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}\n.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}\n.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}\n.ace_br8 {border-bottom-left-radius : 3px;}\n.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}\n.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}\n.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\n\n\n.ace_editor {\n position: relative;\n overflow: hidden;\n padding: 0;\n font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'Source Code Pro\', \'source-code-pro\', monospace;\n direction: ltr;\n text-align: left;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n forced-color-adjust: none;\n}\n\n.ace_scroller {\n position: absolute;\n overflow: hidden;\n top: 0;\n bottom: 0;\n background-color: inherit;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n cursor: text;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n min-width: 100%;\n contain: style size layout;\n font-variant-ligatures: no-common-ligatures;\n}\n\n.ace_keyboard-focus:focus {\n box-shadow: inset 0 0 0 2px #5E9ED6;\n outline: none;\n}\n\n.ace_dragging .ace_scroller:before{\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n content: \'\';\n background: rgba(250, 250, 250, 0.01);\n z-index: 1000;\n}\n.ace_dragging.ace_dark .ace_scroller:before{\n background: rgba(0, 0, 0, 0.01);\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n width: auto;\n top: 0;\n bottom: 0;\n left: 0;\n cursor: default;\n z-index: 4;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n contain: style size layout;\n}\n\n.ace_gutter-active-line {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.ace_scroller.ace_scroll-left:after {\n content: "";\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;\n pointer-events: none;\n}\n\n.ace_gutter-cell, .ace_gutter-cell_svg-icons {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding-left: 19px;\n padding-right: 6px;\n background-repeat: no-repeat;\n}\n\n.ace_gutter-cell_svg-icons .ace_gutter_annotation {\n margin-left: -14px;\n float: left;\n}\n\n.ace_gutter-cell .ace_gutter_annotation {\n margin-left: -19px;\n float: left;\n}\n\n.ace_gutter-cell.ace_error, .ace_icon.ace_error, .ace_icon.ace_error_fold, .ace_gutter-cell.ace_security, .ace_icon.ace_security, .ace_icon.ace_security_fold {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning, .ace_icon.ace_warning, .ace_icon.ace_warning_fold {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info, .ace_icon.ace_info, .ace_gutter-cell.ace_hint, .ace_icon.ace_hint {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_dark .ace_gutter-cell.ace_info, .ace_dark .ace_icon.ace_info, .ace_dark .ace_gutter-cell.ace_hint, .ace_dark .ace_icon.ace_hint {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");\n}\n\n.ace_icon_svg.ace_error {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJyZWQiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KPGNpcmNsZSBmaWxsPSJub25lIiBjeD0iOCIgY3k9IjgiIHI9IjciIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGxpbmUgeDE9IjExIiB5MT0iNSIgeDI9IjUiIHkyPSIxMSIvPgo8bGluZSB4MT0iMTEiIHkxPSIxMSIgeDI9IjUiIHkyPSI1Ii8+CjwvZz4KPC9zdmc+");\n background-color: crimson;\n}\n.ace_icon_svg.ace_security {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8ZyBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iZGFya29yYW5nZSIgZmlsbD0ibm9uZSIgc2hhcGUtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iPgogICAgICAgIDxwYXRoIGNsYXNzPSJzdHJva2UtbGluZWpvaW4tcm91bmQiIGQ9Ik04IDE0LjgzMDdDOCAxNC44MzA3IDIgMTIuOTA0NyAyIDguMDg5OTJWMy4yNjU0OEM1LjMxIDMuMjY1NDggNy45ODk5OSAxLjM0OTE4IDcuOTg5OTkgMS4zNDkxOEM3Ljk4OTk5IDEuMzQ5MTggMTAuNjkgMy4yNjU0OCAxNCAzLjI2NTQ4VjguMDg5OTJDMTQgMTIuOTA0NyA4IDE0LjgzMDcgOCAxNC44MzA3WiIvPgogICAgICAgIDxwYXRoIGQ9Ik0yIDguMDg5OTJWMy4yNjU0OEM1LjMxIDMuMjY1NDggNy45ODk5OSAxLjM0OTE4IDcuOTg5OTkgMS4zNDkxOCIvPgogICAgICAgIDxwYXRoIGQ9Ik0xMy45OSA4LjA4OTkyVjMuMjY1NDhDMTAuNjggMy4yNjU0OCA4IDEuMzQ5MTggOCAxLjM0OTE4Ii8+CiAgICAgICAgPHBhdGggY2xhc3M9InN0cm9rZS1saW5lam9pbi1yb3VuZCIgZD0iTTggNFY5Ii8+CiAgICAgICAgPHBhdGggY2xhc3M9InN0cm9rZS1saW5lam9pbi1yb3VuZCIgZD0iTTggMTBWMTIiLz4KICAgIDwvZz4KPC9zdmc+");\n background-color: crimson;\n}\n.ace_icon_svg.ace_warning {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJkYXJrb3JhbmdlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+Cjxwb2x5Z29uIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiIHBvaW50cz0iOCAxIDE1IDE1IDEgMTUgOCAxIi8+CjxyZWN0IHg9IjgiIHk9IjEyIiB3aWR0aD0iMC4wMSIgaGVpZ2h0PSIwLjAxIi8+CjxsaW5lIHgxPSI4IiB5MT0iNiIgeDI9IjgiIHkyPSIxMCIvPgo8L2c+Cjwvc3ZnPg==");\n background-color: darkorange;\n}\n.ace_icon_svg.ace_info {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJibHVlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CjxjaXJjbGUgZmlsbD0ibm9uZSIgY3g9IjgiIGN5PSI4IiByPSI3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjggMTEgOCA4Ii8+Cjxwb2x5bGluZSBwb2ludHM9IjkgOCA2IDgiLz4KPGxpbmUgeDE9IjEwIiB5MT0iMTEiIHgyPSI2IiB5Mj0iMTEiLz4KPHJlY3QgeD0iOCIgeT0iNSIgd2lkdGg9IjAuMDEiIGhlaWdodD0iMC4wMSIvPgo8L2c+Cjwvc3ZnPg==");\n background-color: royalblue;\n}\n.ace_icon_svg.ace_hint {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8ZyBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0ic2lsdmVyIiBmaWxsPSJub25lIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CiAgICAgICAgPHBhdGggY2xhc3M9InN0cm9rZS1saW5lam9pbi1yb3VuZCIgZD0iTTYgMTRIMTAiLz4KICAgICAgICA8cGF0aCBkPSJNOCAxMUg5QzkgOS40NzAwMiAxMiA4LjU0MDAyIDEyIDUuNzYwMDJDMTIuMDIgNC40MDAwMiAxMS4zOSAzLjM2MDAyIDEwLjQzIDIuNjcwMDJDOSAxLjY0MDAyIDcuMDAwMDEgMS42NDAwMiA1LjU3MDAxIDIuNjcwMDJDNC42MTAwMSAzLjM2MDAyIDMuOTggNC40MDAwMiA0IDUuNzYwMDJDNCA4LjU0MDAyIDcuMDAwMDEgOS40NzAwMiA3LjAwMDAxIDExSDhaIi8+CiAgICA8L2c+Cjwvc3ZnPg==");\n background-color: silver;\n}\n\n.ace_icon_svg.ace_error_fold {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSI+CiAgPHBhdGggZD0ibSAxOC45Mjk4NTEsNy44Mjk4MDc2IGMgMC4xNDYzNTMsNi4zMzc0NjA0IC02LjMyMzE0Nyw3Ljc3Nzg0NDQgLTcuNDc3OTEyLDcuNzc3ODQ0NCAtMi4xMDcyNzI2LC0wLjEyODc1IDUuMTE3Njc4LDAuMzU2MjQ5IDUuMDUxNjk4LC03Ljg3MDA2MTggLTAuNjA0NjcyLC04LjAwMzk3MzQ5IC03LjA3NzI3MDYsLTcuNTYzMTE4OSAtNC44NTczLC03LjQzMDM5NTU2IDEuNjA2LC0wLjExNTE0MjI1IDYuODk3NDg1LDEuMjYyNTQ1OTYgNy4yODM1MTQsNy41MjI2MTI5NiB6IiBmaWxsPSJjcmltc29uIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibSA4LjExNDc1NjIsMi4wNTI5ODI4IGMgMy4zNDkxNjk4LDAgNi4wNjQxMzI4LDIuNjc2ODYyNyA2LjA2NDEzMjgsNS45Nzg5NTMgMCwzLjMwMjExMjIgLTIuNzE0OTYzLDUuOTc4OTIwMiAtNi4wNjQxMzI4LDUuOTc4OTIwMiAtMy4zNDkxNDczLDAgLTYuMDY0MTc3MiwtMi42NzY4MDggLTYuMDY0MTc3MiwtNS45Nzg5MjAyIDAuMDA1MzksLTMuMjk5ODg2MSAyLjcxNzI2NTYsLTUuOTczNjQwOCA2LjA2NDE3NzIsLTUuOTc4OTUzIHogbSAwLC0xLjczNTgyNzE5IGMgLTQuMzIxNDgzNiwwIC03LjgyNDc0MDM4LDMuNDU0MDE4NDkgLTcuODI0NzQwMzgsNy43MTQ3ODAxOSAwLDQuMjYwNzI4MiAzLjUwMzI1Njc4LDcuNzE0NzQ1MiA3LjgyNDc0MDM4LDcuNzE0NzQ1MiA0LjMyMTQ0OTgsMCA3LjgyNDY5OTgsLTMuNDU0MDE3IDcuODI0Njk5OCwtNy43MTQ3NDUyIDAsLTIuMDQ2MDkxNCAtMC44MjQzOTIsLTQuMDA4MzY3MiAtMi4yOTE3NTYsLTUuNDU1MTc0NiBDIDEyLjE4MDIyNSwxLjEyOTk2NDggMTAuMTkwMDEzLDAuMzE3MTU1NjEgOC4xMTQ3NTYyLDAuMzE3MTU1NjEgWiBNIDYuOTM3NDU2Myw4LjI0MDU5ODUgNC42NzE4Njg1LDEwLjQ4NTg1MiA2LjAwODY4MTQsMTEuODc2NzI4IDguMzE3MDAzNSw5LjYwMDc5MTEgMTAuNjI1MzM3LDExLjg3NjcyOCAxMS45NjIxMzgsMTAuNDg1ODUyIDkuNjk2NTUwOCw4LjI0MDU5ODUgMTEuOTYyMTM4LDYuMDA2ODA2NiAxMC41NzMyNDYsNC42Mzc0MzM1IDguMzE3MDAzNSw2Ljg3MzQyOTcgNi4wNjA3NjA3LDQuNjM3NDMzNSA0LjY3MTg2ODUsNi4wMDY4MDY2IFoiIGZpbGw9ImNyaW1zb24iIHN0cm9rZS13aWR0aD0iMiIvPgo8L3N2Zz4=");\n background-color: crimson;\n}\n.ace_icon_svg.ace_security_fold {\n -webkit-mask-image: url("data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTcgMTQiIGZpbGw9Im5vbmUiPgogICAgPHBhdGggZD0iTTEwLjAwMDEgMTMuNjk5MkMxMC4wMDAxIDEzLjY5OTIgMTEuOTI0MSAxMy40NzYzIDEzIDEyLjY5OTJDMTQuNDEzOSAxMS42NzgxIDE2IDEwLjUgMTYuMTI1MSA2LjgxMTI2VjIuNTg5ODdDMTYuMTI1MSAyLjU0NzY4IDE2LjEyMjEgMi41MDYxOSAxNi4xMTY0IDIuNDY1NTlWMS43MTQ4NUgxNS4yNDE0TDE1LjIzMDcgMS43MTQ4NEwxNC42MjUxIDEuNjk5MjJWNi44MTEyM0MxNC42MjUxIDguNTEwNjEgMTQuNjI1MSA5LjQ2NDYxIDEyLjc4MjQgMTEuNzIxQzEyLjE1ODYgMTIuNDg0OCAxMC4wMDAxIDEzLjY5OTIgMTAuMDAwMSAxMy42OTkyWiIgZmlsbD0iY3JpbXNvbiIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuMzM2MDkgMC4zNjc0NzVDNy4wMzIxNCAwLjE1MjY1MiA2LjYyNTQ4IDAuMTUzNjE0IDYuMzIyNTMgMC4zNjk5OTdMNi4zMDg2OSAwLjM3OTU1NEM2LjI5NTUzIDAuMzg4NTg4IDYuMjczODggMC40MDMyNjYgNi4yNDQxNyAwLjQyMjc4OUM2LjE4NDcxIDAuNDYxODYgNi4wOTMyMSAwLjUyMDE3MSA1Ljk3MzEzIDAuNTkxMzczQzUuNzMyNTEgMC43MzQwNTkgNS4zNzk5IDAuOTI2ODY0IDQuOTQyNzkgMS4xMjAwOUM0LjA2MTQ0IDEuNTA5NyAyLjg3NTQxIDEuODgzNzcgMS41ODk4NCAxLjg4Mzc3SDAuNzE0ODQ0VjIuNzU4NzdWNi45ODAxNUMwLjcxNDg0NCA5LjQ5Mzc0IDIuMjg4NjYgMTEuMTk3MyAzLjcwMjU0IDEyLjIxODVDNC40MTg0NSAxMi43MzU1IDUuMTI4NzQgMTMuMTA1MyA1LjY1NzMzIDEzLjM0NTdDNS45MjI4NCAxMy40NjY0IDYuMTQ1NjYgMTMuNTU1OSA2LjMwNDY1IDEzLjYxNjFDNi4zODQyMyAxMy42NDYyIDYuNDQ4MDUgMTMuNjY5IDYuNDkzNDkgMTMuNjg0OEM2LjUxNjIyIDEzLjY5MjcgNi41MzQzOCAxMy42OTg5IDYuNTQ3NjQgMTMuNzAzM0w2LjU2MzgyIDEzLjcwODdMNi41NjkwOCAxMy43MTA0TDYuNTcwOTkgMTMuNzExTDYuODM5ODQgMTMuNzUzM0w2LjU3MjQyIDEzLjcxMTVDNi43NDYzMyAxMy43NjczIDYuOTMzMzUgMTMuNzY3MyA3LjEwNzI3IDEzLjcxMTVMNy4xMDg3IDEzLjcxMUw3LjExMDYxIDEzLjcxMDRMNy4xMTU4NyAxMy43MDg3TDcuMTMyMDUgMTMuNzAzM0M3LjE0NTMxIDEzLjY5ODkgNy4xNjM0NiAxMy42OTI3IDcuMTg2MTkgMTMuNjg0OEM3LjIzMTY0IDEzLjY2OSA3LjI5NTQ2IDEzLjY0NjIgNy4zNzUwMyAxMy42MTYxQzcuNTM0MDMgMTMuNTU1OSA3Ljc1Njg1IDEzLjQ2NjQgOC4wMjIzNiAxMy4zNDU3QzguNTUwOTUgMTMuMTA1MyA5LjI2MTIzIDEyLjczNTUgOS45NzcxNSAxMi4yMTg1QzExLjM5MSAxMS4xOTczIDEyLjk2NDggOS40OTM3NyAxMi45NjQ4IDYuOTgwMThWMi43NTg4QzEyLjk2NDggMi43MTY2IDEyLjk2MTkgMi42NzUxMSAxMi45NTYxIDIuNjM0NTFWMS44ODM3N0gxMi4wODExQzEyLjA3NzUgMS44ODM3NyAxMi4wNzQgMS44ODM3NyAxMi4wNzA0IDEuODgzNzdDMTAuNzk3OSAxLjg4MDA0IDkuNjE5NjIgMS41MTEwMiA4LjczODk0IDEuMTI0ODZDOC43MzUzNCAxLjEyMzI3IDguNzMxNzQgMS4xMjE2OCA4LjcyODE0IDEuMTIwMDlDOC4yOTEwMyAwLjkyNjg2NCA3LjkzODQyIDAuNzM0MDU5IDcuNjk3NzkgMC41OTEzNzNDNy41Nzc3MiAwLjUyMDE3MSA3LjQ4NjIyIDAuNDYxODYgNy40MjY3NiAwLjQyMjc4OUM3LjM5NzA1IDAuNDAzMjY2IDcuMzc1MzkgMC4zODg1ODggNy4zNjIyNCAwLjM3OTU1NEw3LjM0ODk2IDAuMzcwMzVDNy4zNDg5NiAwLjM3MDM1IDcuMzQ4NDcgMC4zNzAwMiA3LjM0NTYzIDAuMzc0MDU0TDcuMzM3NzkgMC4zNjg2NTlMNy4zMzYwOSAwLjM2NzQ3NVpNOC4wMzQ3MSAyLjcyNjkxQzguODYwNCAzLjA5MDYzIDkuOTYwNjYgMy40NjMwOSAxMS4yMDYxIDMuNTg5MDdWNi45ODAxNUgxMS4yMTQ4QzExLjIxNDggOC42Nzk1MyAxMC4xNjM3IDkuOTI1MDcgOC45NTI1NCAxMC43OTk4QzguMzU1OTUgMTEuMjMwNiA3Ljc1Mzc0IDExLjU0NTQgNy4yOTc5NiAxMS43NTI3QzcuMTE2NzEgMTEuODM1MSA2Ljk2MDYyIDExLjg5OTYgNi44Mzk4NCAxMS45NDY5QzYuNzE5MDYgMTEuODk5NiA2LjU2Mjk3IDExLjgzNTEgNi4zODE3MyAxMS43NTI3QzUuOTI1OTUgMTEuNTQ1NCA1LjMyMzczIDExLjIzMDYgNC43MjcxNSAxMC43OTk4QzMuNTE2MDMgOS45MjUwNyAyLjQ2NDg0IDguNjc5NTUgMi40NjQ4NCA2Ljk4MDE4VjMuNTg5MDlDMy43MTczOCAzLjQ2MjM5IDQuODIzMDggMy4wODYzOSA1LjY1MDMzIDIuNzIwNzFDNi4xNDIyOCAyLjUwMzI0IDYuNTQ0ODUgMi4yODUzNyA2LjgzMjU0IDIuMTE2MjRDNy4xMjE4MSAyLjI4NTM1IDcuNTI3IDIuNTAzNTIgOC4wMjE5NiAyLjcyMTMxQzguMDI2MiAyLjcyMzE3IDguMDMwNDUgMi43MjUwNCA4LjAzNDcxIDIuNzI2OTFaTTUuOTY0ODQgMy40MDE0N1Y3Ljc3NjQ3SDcuNzE0ODRWMy40MDE0N0g1Ljk2NDg0Wk01Ljk2NDg0IDEwLjQwMTVWOC42NTE0N0g3LjcxNDg0VjEwLjQwMTVINS45NjQ4NFoiIGZpbGw9ImNyaW1zb24iIHN0cm9rZS13aWR0aD0iMiIvPgo8L3N2Zz4=");\n background-color: crimson;\n}\n.ace_icon_svg.ace_warning_fold {\n -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC43NzY5IDE0LjczMzdMOC42NTE5MiAyLjQ4MzY5QzguMzI5NDYgMS44Mzg3NyA3LjQwOTEzIDEuODM4NzcgNy4wODY2NyAyLjQ4MzY5TDAuOTYxNjY5IDE0LjczMzdDMC42NzA3NzUgMTUuMzE1NSAxLjA5MzgzIDE2IDEuNzQ0MjkgMTZIMTMuOTk0M0MxNC42NDQ4IDE2IDE1LjA2NzggMTUuMzE1NSAxNC43NzY5IDE0LjczMzdaTTMuMTYwMDcgMTQuMjVMNy44NjkyOSA0LjgzMTU2TDEyLjU3ODUgMTQuMjVIMy4xNjAwN1pNOC43NDQyOSAxMS42MjVWMTMuMzc1SDYuOTk0MjlWMTEuNjI1SDguNzQ0MjlaTTYuOTk0MjkgMTAuNzVWNy4yNUg4Ljc0NDI5VjEwLjc1SDYuOTk0MjlaIiBmaWxsPSIjRUM3MjExIi8+CjxwYXRoIGQ9Ik0xMS4xOTkxIDIuOTUyMzhDMTAuODgwOSAyLjMxNDY3IDEwLjM1MzcgMS44MDUyNiA5LjcwNTUgMS41MDlMMTEuMDQxIDEuMDY5NzhDMTEuNjg4MyAwLjk0OTgxNCAxMi4zMzcgMS4yNzI2MyAxMi42MzE3IDEuODYxNDFMMTcuNjEzNiAxMS44MTYxQzE4LjM1MjcgMTMuMjkyOSAxNy41OTM4IDE1LjA4MDQgMTYuMDE4IDE1LjU3NDVDMTYuNDA0NCAxNC40NTA3IDE2LjMyMzEgMTMuMjE4OCAxNS43OTI0IDEyLjE1NTVMMTEuMTk5MSAyLjk1MjM4WiIgZmlsbD0iI0VDNzIxMSIvPgo8L3N2Zz4=");\n background-color: darkorange;\n}\n\n.ace_scrollbar {\n contain: strict;\n position: absolute;\n right: 0;\n bottom: 0;\n z-index: 6;\n}\n\n.ace_scrollbar-inner {\n position: absolute;\n cursor: text;\n left: 0;\n top: 0;\n}\n\n.ace_scrollbar-v{\n overflow-x: hidden;\n overflow-y: scroll;\n top: 0;\n}\n\n.ace_scrollbar-h {\n overflow-x: scroll;\n overflow-y: hidden;\n left: 0;\n}\n\n.ace_print-margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_text-input {\n position: absolute;\n z-index: 0;\n width: 0.5em;\n height: 1em;\n opacity: 0;\n background: transparent;\n -moz-appearance: none;\n appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n font: inherit;\n padding: 0 1px;\n margin: 0 -1px;\n contain: strict;\n -ms-user-select: text;\n -moz-user-select: text;\n -webkit-user-select: text;\n user-select: text;\n /*with `pre-line` chrome inserts instead of space*/\n white-space: pre!important;\n}\n.ace_text-input.ace_composition {\n background: transparent;\n color: inherit;\n z-index: 1000;\n opacity: 1;\n}\n.ace_composition_placeholder { color: transparent }\n.ace_composition_marker { \n border-bottom: 1px solid;\n position: absolute;\n border-radius: 0;\n margin-top: 1px;\n}\n\n[ace_nocontext=true] {\n transform: none!important;\n filter: none!important;\n clip-path: none!important;\n mask : none!important;\n contain: none!important;\n perspective: none!important;\n mix-blend-mode: initial!important;\n z-index: auto;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n /* workaround for chrome bug https://github.com/ajaxorg/ace/issues/2312*/\n word-wrap: normal;\n white-space: pre;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter-layer {\n position: relative;\n width: auto;\n text-align: right;\n pointer-events: auto;\n height: 1000000px;\n contain: style size layout;\n}\n\n.ace_text-layer {\n font: inherit !important;\n position: absolute;\n height: 1000000px;\n width: 1000000px;\n contain: style size layout;\n}\n\n.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {\n contain: style size layout;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n}\n\n.ace_hidpi .ace_text-layer,\n.ace_hidpi .ace_gutter-layer,\n.ace_hidpi .ace_content,\n.ace_hidpi .ace_gutter {\n contain: strict;\n}\n.ace_hidpi .ace_text-layer > .ace_line, \n.ace_hidpi .ace_text-layer > .ace_line_group {\n contain: strict;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n box-sizing: border-box;\n border-left: 2px solid;\n /* workaround for smooth cursor repaintng whole screen in chrome */\n transform: translatez(0);\n}\n\n.ace_multiselect .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_slim-cursors .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_overwrite-cursors .ace_cursor {\n border-left-width: 0;\n border-bottom: 1px solid;\n}\n\n.ace_hidden-cursors .ace_cursor {\n opacity: 0.2;\n}\n\n.ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {\n opacity: 0;\n}\n\n.ace_smooth-blinking .ace_cursor {\n transition: opacity 0.18s;\n}\n\n.ace_animate-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: step-end;\n animation-name: blink-ace-animate;\n animation-iteration-count: infinite;\n}\n\n.ace_animate-blinking.ace_smooth-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: ease-in-out;\n animation-name: blink-ace-animate-smooth;\n}\n \n@keyframes blink-ace-animate {\n from, to { opacity: 1; }\n 60% { opacity: 0; }\n}\n\n@keyframes blink-ace-animate-smooth {\n from, to { opacity: 1; }\n 45% { opacity: 1; }\n 60% { opacity: 0; }\n 85% { opacity: 0; }\n}\n\n.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_error_bracket {\n position: absolute;\n border-bottom: 1px solid #DE5555;\n border-radius: 0;\n}\n\n.ace_marker-layer .ace_active-line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_marker-layer .ace_selected-word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n\n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image:\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n border-radius: 2px;\n\n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image:\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\n url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");\n}\n\n.ace_tooltip {\n background-color: #f5f5f5;\n border: 1px solid gray;\n border-radius: 1px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n color: black;\n max-width: 100%;\n padding: 3px 4px;\n position: fixed;\n z-index: 999999;\n box-sizing: border-box;\n cursor: default;\n white-space: pre-wrap;\n word-wrap: break-word;\n line-height: normal;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n pointer-events: none;\n overflow: auto;\n max-width: min(60em, 66vw);\n overscroll-behavior: contain;\n}\n.ace_tooltip pre {\n white-space: pre-wrap;\n}\n\n.ace_tooltip.ace_dark {\n background-color: #636363;\n color: #fff;\n}\n\n.ace_tooltip:focus {\n outline: 1px solid #5E9ED6;\n}\n\n.ace_icon {\n display: inline-block;\n width: 18px;\n vertical-align: top;\n}\n\n.ace_icon_svg {\n display: inline-block;\n width: 12px;\n vertical-align: top;\n -webkit-mask-repeat: no-repeat;\n -webkit-mask-size: 12px;\n -webkit-mask-position: center;\n}\n\n.ace_folding-enabled > .ace_gutter-cell, .ace_folding-enabled > .ace_gutter-cell_svg-icons {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n\n margin: 0 -12px 0 1px;\n display: none;\n width: 11px;\n vertical-align: top;\n\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");\n background-repeat: no-repeat;\n background-position: center;\n\n border-radius: 3px;\n \n border: 1px solid transparent;\n cursor: pointer;\n}\n\n.ace_folding-enabled .ace_fold-widget {\n display: inline-block; \n}\n\n.ace_fold-widget.ace_end {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");\n}\n\n.ace_fold-widget.ace_closed {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n/**\n * Dark version for fold widgets\n */\n.ace_dark .ace_fold-widget {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");\n}\n.ace_dark .ace_fold-widget.ace_end {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");\n}\n.ace_dark .ace_fold-widget.ace_closed {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");\n}\n.ace_dark .ace_fold-widget:hover {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n background-color: rgba(255, 255, 255, 0.1);\n}\n.ace_dark .ace_fold-widget:active {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n}\n\n.ace_inline_button {\n border: 1px solid lightgray;\n display: inline-block;\n margin: -1px 8px;\n padding: 0 5px;\n pointer-events: auto;\n cursor: pointer;\n}\n.ace_inline_button:hover {\n border-color: gray;\n background: rgba(200,200,200,0.2);\n display: inline-block;\n pointer-events: auto;\n}\n\n.ace_fold-widget.ace_invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n\n.ace_fade-fold-widgets .ace_fold-widget {\n transition: opacity 0.4s ease 0.05s;\n opacity: 0;\n}\n\n.ace_fade-fold-widgets:hover .ace_fold-widget {\n transition: opacity 0.05s ease 0.05s;\n opacity:1;\n}\n\n.ace_underline {\n text-decoration: underline;\n}\n\n.ace_bold {\n font-weight: bold;\n}\n\n.ace_nobold .ace_bold {\n font-weight: normal;\n}\n\n.ace_italic {\n font-style: italic;\n}\n\n\n.ace_error-marker {\n background-color: rgba(255, 0, 0,0.2);\n position: absolute;\n z-index: 9;\n}\n\n.ace_highlight-marker {\n background-color: rgba(255, 255, 0,0.2);\n position: absolute;\n z-index: 8;\n}\n\n.ace_mobile-menu {\n position: absolute;\n line-height: 1.5;\n border-radius: 4px;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n background: white;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #dcdcdc;\n color: black;\n}\n.ace_dark > .ace_mobile-menu {\n background: #333;\n color: #ccc;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #444;\n\n}\n.ace_mobile-button {\n padding: 2px;\n cursor: pointer;\n overflow: hidden;\n}\n.ace_mobile-button:hover {\n background-color: #eee;\n opacity:1;\n}\n.ace_mobile-button:active {\n background-color: #ddd;\n}\n\n.ace_placeholder {\n position: relative;\n font-family: arial;\n transform: scale(0.9);\n transform-origin: left;\n white-space: pre;\n opacity: 0.7;\n margin: 0 10px;\n z-index: 1;\n}\n\n.ace_ghost_text {\n opacity: 0.5;\n font-style: italic;\n}\n\n.ace_ghost_text_container > div {\n white-space: pre;\n}\n\n.ghost_text_line_wrapped::after {\n content: "\u21a9";\n position: absolute;\n}\n\n.ace_lineWidgetContainer.ace_ghost_text {\n margin: 0px 4px\n}\n\n.ace_screenreader-only {\n position:absolute;\n left:-10000px;\n top:auto;\n width:1px;\n height:1px;\n overflow:hidden;\n}\n\n.ace_hidden_token {\n display: none;\n}'}),ace.define("ace/layer/decorators",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event_emitter").EventEmitter,o=function(){function e(e,t){this.canvas=r.createElement("canvas"),this.renderer=t,this.pixelRatio=1,this.maxHeight=t.layerConfig.maxHeight,this.lineHeight=t.layerConfig.lineHeight,this.canvasHeight=e.parent.scrollHeight,this.heightRatio=this.canvasHeight/this.maxHeight,this.canvasWidth=e.width,this.minDecorationHeight=2*this.pixelRatio|0,this.halfMinDecorationHeight=this.minDecorationHeight/2|0,this.canvas.width=this.canvasWidth,this.canvas.height=this.canvasHeight,this.canvas.style.top="0px",this.canvas.style.right="0px",this.canvas.style.zIndex="7px",this.canvas.style.position="absolute",this.colors={},this.colors.dark={error:"rgba(255, 18, 18, 1)",warning:"rgba(18, 136, 18, 1)",info:"rgba(18, 18, 136, 1)"},this.colors.light={error:"rgb(255,51,51)",warning:"rgb(32,133,72)",info:"rgb(35,68,138)"},e.element.appendChild(this.canvas)}return e.prototype.$updateDecorators=function(e){function i(e,t){return e.priorityt.priority?1:0}var t=this.renderer.theme.isDark===!0?this.colors.dark:this.colors.light;if(e){this.maxHeight=e.maxHeight,this.lineHeight=e.lineHeight,this.canvasHeight=e.height;var n=(e.lastRow+1)*this.lineHeight;nthis.canvasHeight&&(v=this.canvasHeight-this.halfMinDecorationHeight),h=Math.round(v-this.halfMinDecorationHeight),p=Math.round(v+this.halfMinDecorationHeight)}r.fillStyle=t[s[a].type]||null,r.fillRect(0,c,this.canvasWidth,p-h)}}var m=this.renderer.session.selection.getCursor();if(m){var l=this.compensateFoldRows(m.row,u),c=Math.round((m.row-l)*this.lineHeight*this.heightRatio);r.fillStyle="rgba(0, 0, 0, 0.5)",r.fillRect(0,c,this.canvasWidth,2)}},e.prototype.compensateFoldRows=function(e,t){var n=0;if(t&&t.length>0)for(var r=0;rt[r].start.row&&e=t[r].end.row&&(n+=t[r].end.row-t[r].start.row);return n},e}();i.implement(o.prototype,s),t.Decorator=o}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/scrollbar_custom","ace/scrollbar_custom","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/css/editor-css","ace/layer/decorators","ace/lib/useragent","ace/layer/text_util"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/lang"),o=e("./config"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./scrollbar_custom").HScrollBar,d=e("./scrollbar_custom").VScrollBar,v=e("./renderloop").RenderLoop,m=e("./layer/font_metrics").FontMetrics,g=e("./lib/event_emitter").EventEmitter,y=e("./css/editor-css"),b=e("./layer/decorators").Decorator,w=e("./lib/useragent"),E=e("./layer/text_util").isTextToken;i.importCssString(y,"ace_editor.css",!1);var S=function(){function e(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),o.get("useStrictCSP")==null&&o.set("useStrictCSP",!1),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden","true"),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new m(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.on("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!w.isIOS,this.$loop=new v(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),this.$addResizeObserver(),o.resetOptions(this),o._signal("renderer",this)}return e.prototype.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin(),i.setStyle(this.scroller.style,"line-height",this.lineHeight+"px")},e.prototype.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},e.prototype.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},e.prototype.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},e.prototype.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},e.prototype.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},e.prototype.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},e.prototype.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},e.prototype.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},e.prototype.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),!r&&this.$maxLines&&this.lineHeight>1&&(!i.style.height||i.style.height=="0px")&&(i.style.height="1px",r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);this.$resizeTimer&&this.$resizeTimer.cancel();if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.$customScrollbar&&this.$updateCustomScrollbar(!0)},e.prototype.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.setHeight(o.scrollerHeight),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight()),this.scrollBarH.setWidth(o.scrollerWidth);if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},e.prototype.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},e.prototype.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},e.prototype.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},e.prototype.getAnimatedScroll=function(){return this.$animatedScroll},e.prototype.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},e.prototype.getShowInvisibles=function(){return this.getOption("showInvisibles")},e.prototype.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},e.prototype.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},e.prototype.getHighlightIndentGuides=function(){return this.getOption("highlightIndentGuides")},e.prototype.setHighlightIndentGuides=function(e){this.setOption("highlightIndentGuides",e)},e.prototype.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},e.prototype.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},e.prototype.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},e.prototype.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},e.prototype.getShowGutter=function(){return this.getOption("showGutter")},e.prototype.setShowGutter=function(e){return this.setOption("showGutter",e)},e.prototype.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},e.prototype.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},e.prototype.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},e.prototype.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},e.prototype.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},e.prototype.getContainerElement=function(){return this.container},e.prototype.getMouseEventTarget=function(){return this.scroller},e.prototype.getTextAreaContainer=function(){return this.container},e.prototype.$moveTextAreaToCursor=function(){if(this.$isMousePressed)return;var e=this.textarea.style,t=this.$composition;if(!this.$keepTextAreaAtCursor&&!t){i.translate(this.textarea,-100,0);return}var n=this.$cursorLayer.$pixelPos;if(!n)return;t&&t.markerRange&&(n=this.$cursorLayer.getPixelPosition(t.markerRange.start,!0));var r=this.layerConfig,s=n.top,o=n.left;s-=r.offset;var u=t&&t.useTextareaForIME||w.isMobile?this.lineHeight:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1,f=this.$size.height-u;if(!t)s+=this.lineHeight;else if(t.useTextareaForIME){var l=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(l)[0]}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,f))},e.prototype.getFirstVisibleRow=function(){return this.layerConfig.firstRow},e.prototype.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},e.prototype.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},e.prototype.getLastVisibleRow=function(){return this.layerConfig.lastRow},e.prototype.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},e.prototype.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},e.prototype.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},e.prototype.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},e.prototype.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},e.prototype.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},e.prototype.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},e.prototype.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},e.prototype.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},e.prototype.freeze=function(){this.$frozen=!0},e.prototype.unfreeze=function(){this.$frozen=!1},e.prototype.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender",e),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-Math.max(this.layerConfig.firstRow,0))*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller ":"ace_scroller ace_scroll-left ",this.enableKeyboardAccessibility&&(this.scroller.className+=this.keyboardFocusClassName));if(e&this.CHANGE_FULL){this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}if(e&this.CHANGE_SCROLL){this.$changedLines=null,e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}e&this.CHANGE_TEXT?(this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_LINES?((this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?(this.$showGutter&&this.$gutterLayer.update(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)):e&this.CHANGE_CURSOR&&(this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),this.$customScrollbar&&this.$scrollDecorator.$updateDecorators(n)),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender",e)},e.prototype.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},e.prototype.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&(this.$autosize(),n=t.height<=2*this.lineHeight);var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},e.prototype.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},e.prototype.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},e.prototype.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},e.prototype.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},e.prototype.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},e.prototype.updateBreakpoints=function(e){this._rows=e,this.$loop.schedule(this.CHANGE_GUTTER)},e.prototype.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},e.prototype.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},e.prototype.hideCursor=function(){this.$cursorLayer.hideCursor()},e.prototype.showCursor=function(){this.$cursorLayer.showCursor()},e.prototype.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},e.prototype.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0;this.$scrollAnimation&&(this.$stopAnimation=!0);var a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-u=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},e.prototype.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},e.prototype.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},e.prototype.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},e.prototype.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},e.prototype.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},e.prototype.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText),e.useTextareaForIME==undefined&&(e.useTextareaForIME=this.$useTextareaForIME),this.$useTextareaForIME?(i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},e.prototype.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},e.prototype.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText=this.$composition.cssText;var e=this.session.selection.cursor;this.removeExtraToken(e.row,e.column),this.$composition=null,this.$cursorLayer.element.style.display=""},e.prototype.setGhostText=function(e,t){var n=this.session.selection.cursor,r=t||{row:n.row,column:n.column};this.removeGhostText();var s=this.$calculateWrappedTextChunks(e,r);this.addToken(s[0].text,"ghost_text",r.row,r.column),this.$ghostText={text:e,position:{row:r.row,column:r.column}};var o=i.createElement("div");if(s.length>1){var u=this.hideTokensAfterPosition(r.row,r.column),a;s.slice(1).forEach(function(e){var t=i.createElement("div"),n=i.createElement("span");n.className="ace_ghost_text",e.wrapped&&(t.className="ghost_text_line_wrapped"),e.text.length===0&&(e.text=" "),n.appendChild(i.createTextNode(e.text)),t.appendChild(n),o.appendChild(t),a=t}),u.forEach(function(e){var t=i.createElement("span");E(e.type)||(t.className="ace_"+e.type.replace(/\./g," ace_")),t.appendChild(i.createTextNode(e.value)),a.appendChild(t)}),this.$ghostTextWidget={el:o,row:r.row,column:r.column,className:"ace_ghost_text_container"},this.session.widgetManager.addLineWidget(this.$ghostTextWidget);var f=this.$cursorLayer.getPixelPosition(r,!0),l=this.container,c=l.getBoundingClientRect().height,h=s.length*this.lineHeight,p=h0){var f=0;a.push(i[o].length);for(var l=0;l1||Math.abs(e.$size.height-r)>1?e.$resizeTimer.delay():e.$resizeTimer.cancel()}),this.$resizeObserver.observe(this.container)},e}();S.prototype.CHANGE_CURSOR=1,S.prototype.CHANGE_MARKER=2,S.prototype.CHANGE_GUTTER=4,S.prototype.CHANGE_SCROLL=8,S.prototype.CHANGE_LINES=16,S.prototype.CHANGE_TEXT=32,S.prototype.CHANGE_SIZE=64,S.prototype.CHANGE_MARKER_BACK=128,S.prototype.CHANGE_MARKER_FRONT=256,S.prototype.CHANGE_FULL=512,S.prototype.CHANGE_H_SCROLL=1024,S.prototype.$changes=0,S.prototype.$padding=null,S.prototype.$frozen=!1,S.prototype.STEPS=8,r.implement(S.prototype,g),o.defineOptions(S.prototype,"renderer",{useResizeObserver:{set:function(e){!e&&this.$resizeObserver?(this.$resizeObserver.disconnect(),this.$resizeTimer.cancel(),this.$resizeTimer=this.$resizeObserver=null):e&&!this.$resizeObserver&&this.$addResizeObserver()}},animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},useSvgGutterIcons:{set:function(e){this.$gutterLayer.$useSvgGutterIcons=e},initialValue:!1},showFoldedAnnotations:{set:function(e){this.$gutterLayer.$showFoldedAnnotations=e},initialValue:!1},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightIndentGuides:{set:function(e){this.$textLayer.setHighlightIndentGuides(e)==1?this.$textLayer.$highlightIndentGuide():this.$textLayer.$clearActiveIndentGuide(this.$textLayer.$lines.cells)},initialValue:!0},highlightGutterLine:{set:function(e){this.$gutterLayer.setHighlightGutterLine(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.$minLines<562949953421311||(this.$minLines=0),this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},customScrollbar:{set:function(e){this.$updateCustomScrollbar(e)},initialValue:!1},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0},hasCssTransforms:{},useTextareaForIME:{initialValue:!w.isMobile&&!w.isIE}}),t.VirtualRenderer=S}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";function u(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}function a(e){if(typeof Worker=="undefined")return{postMessage:function(){},terminate:function(){}};if(o.get("loadWorkerFromBlob")){var t=u(e),n=window.URL||window.webkitURL,r=n.createObjectURL(t);return new Worker(r)}return new Worker(e)}var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),f=function(e){e.postMessage||(e=this.$createWorkerFromOldConfig.apply(this,arguments)),this.$worker=e,this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.$createWorkerFromOldConfig=function(t,n,r,i,s){e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var u=this.$normalizePath;i=i||u(e.toUrl("ace/worker/worker.js",null,"_"));var f={};t.forEach(function(t){f[t]=u(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}return this.$worker=a(i),s&&this.send("importScripts",s),this.$worker.postMessage({init:!0,tlns:f,module:n,classname:r}),this.$worker},this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker.onerror=function(e){e.preventDefault()},this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{t.data&&t.data.err&&(t.data.err={message:t.data.err.message,stack:t.data.err.stack,code:t.data.err.code}),this.$worker&&this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener,!0)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(){function e(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate,!0),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)}return e.prototype.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},e.prototype.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},e.prototype.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},e.prototype.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},e.prototype.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},e.prototype.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},e.prototype.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.off("change",this.$onUpdate),this.session.selection.off("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},e.prototype.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1?e.multiSelect.joinSelections():e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"splitSelectionIntoLines",description:"Split into lines",exec:function(e){e.multiSelect.splitIntoLines()},readOnly:!0},{name:"alignCursors",description:"Align cursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",description:"Find all",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",description:"Single selection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),ace.define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}if(!e.textInput)return;var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()},e),u.addListener(t,"keyup",r,e),u.addListener(t,"blur",r,e)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){var e=this.ranges.length?this.ranges:[this.getRange()],t=[];for(var n=0;n1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),s