"use strict"; // 8bitworkshop IDE user interface var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.highlightSearch = exports.reloadWorkspaceFile = exports.emulationHalted = exports.getSaveState = exports.getTestOutput = exports.setTestInput = exports.startUI = exports.getPlatformAndRepo = exports.setupSplits = exports.haltEmulation = exports.setFrameRateUI = exports.clearBreakpoint = exports.runToPC = exports.setupBreakpoint = exports.lastDebugState = exports.compparams = exports.projectWindows = exports.current_project = exports.platform = exports.repo_id = exports.store_id = exports.platform_id = exports.qs = void 0; const localforage = __importStar(require("localforage")); const project_1 = require("./project"); const windows_1 = require("./windows"); const baseplatform_1 = require("../common/baseplatform"); const emu_1 = require("../common/emu"); const toolbar_1 = require("./toolbar"); const util_1 = require("../common/util"); const recorder_1 = require("../common/recorder"); const services_1 = require("./services"); const Split = require("split.js"); const _index_1 = require("../platform/_index"); const editors_1 = require("./views/editors"); const debugviews_1 = require("./views/debugviews"); const asseteditor_1 = require("./views/asseteditor"); const baseviews_1 = require("./views/baseviews"); const treeviews_1 = require("./views/treeviews"); const file_saver_1 = require("file-saver"); const DOMPurify = require("dompurify"); const CommodoreTape_1 = require("../common/audio/CommodoreTape"); exports.qs = (0, util_1.decodeQueryString)(window.location.search || '?'); const isElectron = (0, util_1.parseBool)(exports.qs.electron); const isEmbed = (0, util_1.parseBool)(exports.qs.embed); /// GLOBALS (TODO: remove) var PRESETS; // presets array var platform_name; // platform name (after setPlatformUI) var toolbar = $("#controls_top"); var uitoolbar; var stateRecorder; var userPaused; // did user explicitly pause? var current_output; // current ROM (or other object) var current_preset; // current preset object (if selected) var store; // persistent store var lastDebugInfo; // last debug info (CPU text) var debugCategory; // current debug category var debugTickPaused = false; var recorderActive = false; var lastViewClicked = null; var lastDebugCommand = null; var errorWasRuntime = false; var lastBreakExpr = "c.PC == 0x6000"; // TODO: codemirror multiplex support? // TODO: move to views.ts? const TOOL_TO_SOURCE_STYLE = { 'dasm': '6502', 'acme': '6502', 'cc65': 'text/x-csrc', 'ca65': '6502', 'nesasm': '6502', 'z80asm': 'z80', 'sdasz80': 'z80', 'sdcc': 'text/x-csrc', 'verilator': 'verilog', 'jsasm': 'z80', 'zmac': 'z80', 'bataribasic': 'bataribasic', 'markdown': 'markdown', 'js': 'javascript', 'xasm6809': 'z80', 'cmoc': 'text/x-csrc', 'yasm': 'gas', 'smlrc': 'text/x-csrc', 'inform6': 'inform6', 'fastbasic': 'fastbasic', 'basic': 'basic', 'silice': 'verilog', 'wiz': 'text/x-wiz', 'vasmarm': 'vasm', 'armips': 'vasm', 'ecs': 'ecs', 'remote:llvm-mos': 'text/x-csrc', }; const TOOL_TO_HELPURL = { 'dasm': 'https://raw.githubusercontent.com/sehugg/dasm/master/doc/dasm.txt', 'cc65': 'https://cc65.github.io/doc/cc65.html', 'ca65': 'https://cc65.github.io/doc/ca65.html', 'sdcc': 'http://sdcc.sourceforge.net/doc/sdccman.pdf', 'verilator': 'https://www.veripool.org/ftp/verilator_doc.pdf', 'fastbasic': 'https://github.com/dmsc/fastbasic/blob/master/manual.md', 'bataribasic': "help/bataribasic/manual.html", 'wiz': "https://github.com/wiz-lang/wiz/blob/master/readme.md#wiz", 'silice': "https://github.com/sylefeb/Silice", 'zmac': "https://raw.githubusercontent.com/sehugg/zmac/master/doc.txt", 'cmoc': "http://perso.b2b2c.ca/~sarrazip/dev/cmoc.html", 'remote:llvm-mos': 'https://llvm-mos.org/wiki/Welcome', }; function gaEvent(category, action, label, value) { if (window['ga']) ga('send', 'event', category, action, label, value); } function alertError(s) { setWaitDialog(false); bootbox.alert({ title: ' Alert', message: DOMPurify.sanitize(s) }); } function alertInfo(s) { setWaitDialog(false); bootbox.alert(DOMPurify.sanitize(s)); } function fatalError(s) { alertError(s); throw new Error(s); } function newWorker() { // TODO: return new Worker("https://8bitworkshop.com.s3-website-us-east-1.amazonaws.com/dev/gen/worker/bundle.js"); return new Worker("./gen/worker/bundle.js"); } const hasLocalStorage = function () { try { const key = "__some_random_key_you_are_not_going_to_use__"; localStorage.setItem(key, key); var has = localStorage.getItem(key) == key; localStorage.removeItem(key); return has; } catch (e) { return false; } }(); // wrapper for localstorage class UserPrefs { setLastPreset(id) { if (hasLocalStorage && !isEmbed) { if (exports.repo_id && exports.platform_id && !isElectron) localStorage.setItem("__lastrepo_" + exports.platform_id, exports.repo_id); else localStorage.removeItem("__lastrepo_" + exports.platform_id); localStorage.setItem("__lastplatform", exports.platform_id); localStorage.setItem("__lastid_" + exports.store_id, id); } } unsetLastPreset() { if (hasLocalStorage && !isEmbed) { delete exports.qs.file; localStorage.removeItem("__lastid_" + exports.store_id); } } getLastPreset() { return hasLocalStorage && !isEmbed && localStorage.getItem("__lastid_" + exports.store_id); } getLastPlatformID() { return hasLocalStorage && !isEmbed && localStorage.getItem("__lastplatform"); } getLastRepoID(platform) { return hasLocalStorage && !isEmbed && platform && localStorage.getItem("__lastrepo_" + platform); } shouldCompleteTour() { return hasLocalStorage && !isEmbed && !localStorage.getItem("8bitworkshop.hello"); } completedTour() { if (hasLocalStorage && !isEmbed) localStorage.setItem("8bitworkshop.hello", "true"); } } var userPrefs = new UserPrefs(); // https://developers.google.com/web/updates/2016/06/persistent-storage function requestPersistPermission(interactive, failureonly) { if (navigator.storage && navigator.storage.persist) { navigator.storage.persist().then(persistent => { console.log("requestPersistPermission =", persistent); if (persistent) { interactive && !failureonly && alertInfo("Your browser says it will persist your local file edits, but you may want to back up your work anyway."); } else { interactive && alertError("Your browser refused to expand the peristent storage quota. Your edits may not be preserved after closing the page."); } }); } else { interactive && alertError("Your browser may not persist edits after closing the page. Try a different browser."); } } function getCurrentPresetTitle() { if (!current_preset) return exports.current_project.mainPath || "ROM"; else return current_preset.title || current_preset.name || exports.current_project.mainPath || "ROM"; } async function newFilesystem() { var basefs = new project_1.WebPresetsFileSystem(exports.platform_id); if (isElectron) { console.log('using electron with local filesystem', alternateLocalFilesystem); return new project_1.OverlayFilesystem(basefs, alternateLocalFilesystem); } else if (exports.qs.localfs != null) { return new project_1.OverlayFilesystem(basefs, await getLocalFilesystem(exports.qs.localfs)); } else { return new project_1.OverlayFilesystem(basefs, new project_1.LocalForageFilesystem(store)); } } async function initProject() { var filesystem = await newFilesystem(); exports.current_project = new project_1.CodeProject(newWorker(), exports.platform_id, exports.platform, filesystem); exports.current_project.remoteTool = exports.qs.tool || null; exports.projectWindows = new windows_1.ProjectWindows($("#workspace")[0], exports.current_project); exports.current_project.callbackBuildResult = (result) => { setCompileOutput(result); }; exports.current_project.callbackBuildStatus = (busy) => { setBusyStatus(busy); }; } function setBusyStatus(busy) { if (busy) { toolbar.addClass("is-busy"); } else { toolbar.removeClass("is-busy"); } $('#compile_spinner').css('visibility', busy ? 'visible' : 'hidden'); } function newDropdownListItem(id, text) { var li = document.createElement("li"); var a = document.createElement("a"); a.setAttribute("class", "dropdown-item"); a.setAttribute("href", "#"); a.setAttribute("data-wndid", id); if (id == exports.projectWindows.getActiveID()) $(a).addClass("dropdown-item-checked"); a.appendChild(document.createTextNode(text)); li.appendChild(a); return { li, a }; } function refreshWindowList() { var ul = $("#windowMenuList").empty(); var separate = false; function addWindowItem(id, name, createfn) { if (separate) { ul.append(document.createElement("hr")); separate = false; } let { li, a } = newDropdownListItem(id, name); ul.append(li); if (createfn) { var onopen = (id, wnd) => { ul.find('a').removeClass("dropdown-item-checked"); $(a).addClass("dropdown-item-checked"); }; exports.projectWindows.setCreateFunc(id, createfn); exports.projectWindows.setShowFunc(id, onopen); $(a).click((e) => { exports.projectWindows.createOrShow(id); lastViewClicked = id; }); } } function loadEditor(path) { var tool = exports.platform.getToolForFilename(path); // hack because .h files can be DASM or CC65 if (tool == 'dasm' && path.endsWith(".h") && getCurrentMainFilename().endsWith(".c")) { tool = 'cc65'; } var mode = tool && TOOL_TO_SOURCE_STYLE[tool]; return new editors_1.SourceEditor(path, mode); } function addEditorItem(id) { addWindowItem(id, (0, util_1.getFilenameForPath)(id), () => { var data = exports.current_project.getFile(id); if (typeof data === 'string') return loadEditor(id); else if (data instanceof Uint8Array) return new debugviews_1.BinaryFileView(id, data); }); } // add main file editor addEditorItem(exports.current_project.mainPath); // add other source files exports.current_project.iterateFiles((id, text) => { if (text && id != exports.current_project.mainPath) { addEditorItem(id); } }); // add listings separate = true; var listings = exports.current_project.getListings(); if (listings) { for (var lstfn in listings) { var lst = listings[lstfn]; // add listing if source/assembly file exists and has text if ((lst.assemblyfile && lst.assemblyfile.text) || (lst.sourcefile && lst.sourcefile.text) || lst.text) { addWindowItem(lstfn, (0, util_1.getFilenameForPath)(lstfn), (path) => { return new editors_1.ListingView(path); }); } } } // add other tools separate = true; if (exports.platform.disassemble && exports.platform.saveState) { addWindowItem("#disasm", "Disassembly", () => { return new editors_1.DisassemblerView(); }); } if (exports.platform.readAddress) { addWindowItem("#memory", "Memory Browser", () => { return new debugviews_1.MemoryView(); }); } if (exports.current_project.segments && exports.current_project.segments.length) { addWindowItem("#memmap", "Memory Map", () => { return new debugviews_1.MemoryMapView(); }); } if (exports.platform.readVRAMAddress) { addWindowItem("#memvram", "VRAM Browser", () => { return new debugviews_1.VRAMMemoryView(); }); } if (exports.platform.startProbing) { addWindowItem("#memheatmap", "Memory Probe", () => { return new debugviews_1.AddressHeatMapView(); }); // TODO: only if raster addWindowItem("#crtheatmap", "CRT Probe", () => { //return new RasterPCHeatMapView(); return new debugviews_1.RasterStackMapView(); }); addWindowItem("#probelog", "Probe Log", () => { return new debugviews_1.ProbeLogView(); }); addWindowItem("#scanlineio", "Scanline I/O", () => { return new debugviews_1.ScanlineIOView(); }); addWindowItem("#symbolprobe", "Symbol Profiler", () => { return new debugviews_1.ProbeSymbolView(); }); addWindowItem("#callstack", "Call Stack", () => { return new treeviews_1.CallStackView(); }); /* addWindowItem("#framecalls", "Frame Profiler", () => { return new FrameCallsView(); }); */ } if (exports.platform.getDebugTree) { addWindowItem("#debugview", "Debug Tree", () => { return new treeviews_1.DebugBrowserView(); }); } addWindowItem('#asseteditor', 'Asset Editor', () => { return new asseteditor_1.AssetEditorView(); }); } function highlightLines(path, hispec) { if (hispec) { var toks = exports.qs.highlight.split(','); var start = parseInt(toks[0]) - 1; var end = parseInt(toks[1]) - 1; var editor = exports.projectWindows.createOrShow(path); editor.highlightLines(start, end); } } function loadMainWindow(preset_id) { // we need this to build create functions for the editor refreshWindowList(); // show main file exports.projectWindows.createOrShow(preset_id); // build project exports.current_project.setMainFile(preset_id); // highlighting? highlightLines(preset_id, exports.qs.highlight); } async function loadProject(preset_id) { // set current file ID // TODO: this is done twice (mainPath and mainpath!) exports.current_project.mainPath = preset_id; userPrefs.setLastPreset(preset_id); // load files from storage or web URLs var result = await exports.current_project.loadFiles([preset_id]); measureTimeLoad = new Date(); // for timing calc. if (result && result.length) { // file found; continue loadMainWindow(preset_id); } else { var skel = await getSkeletonFile(preset_id); exports.current_project.filedata[preset_id] = skel || "\n"; loadMainWindow(preset_id); // don't alert if we selected "new file" if (!exports.qs.newfile) { alertInfo("Could not find file \"" + preset_id + "\". Loading default file."); } else { requestPersistPermission(true, true); } delete exports.qs.newfile; replaceURLState(); } } function reloadProject(id) { // leave repository == '/' if (id == '/') { exports.qs = { repo: '/' }; } else if (id.indexOf('://') >= 0) { var urlparse = (0, services_1.parseGithubURL)(id); if (urlparse) { exports.qs = { repo: urlparse.repopath }; } } else { exports.qs.platform = exports.platform_id; exports.qs.file = id; } gotoNewLocation(); } async function getSkeletonFile(fileid) { var ext = exports.platform.getToolForFilename(fileid); try { return await $.get("presets/" + (0, util_1.getBasePlatform)(exports.platform_id) + "/skeleton." + ext, 'text'); } catch (e) { alertError("Could not load skeleton for " + exports.platform_id + "/" + ext + "; using blank file"); } } function checkEnteredFilename(fn) { if (fn.indexOf(" ") >= 0) { alertError("No spaces in filenames, please."); return false; } return true; } function _createNewFile(e) { // TODO: support spaces bootbox.prompt({ title: "Enter the name of your new main source file.", placeholder: "newfile" + exports.platform.getDefaultExtension(), callback: (filename) => { if (filename && filename.trim().length > 0) { if (!checkEnteredFilename(filename)) return; if (filename.indexOf(".") < 0) { filename += exports.platform.getDefaultExtension(); } var path = filename; gaEvent('workspace', 'file', 'new'); exports.qs.newfile = '1'; reloadProject(path); } } }); return true; } function _uploadNewFile(e) { const uploadFileElem = $(``); const file = uploadFileElem[0]; uploadFileElem.change((e) => { handleFileUpload(file.files); }); uploadFileElem.click(); } // called from index.html function handleFileUpload(files) { console.log(files); var index = 0; function uploadNextFile() { var f = files[index++]; if (!f) { console.log("Done uploading", index); if (index > 2) { alertInfo("Files uploaded."); setTimeout(updateSelector, 1000); // TODO: wait for files to upload } else { exports.qs.file = files[0].name; bootbox.confirm({ message: "Open '" + DOMPurify.sanitize(exports.qs.file) + "' as main project file?", buttons: { confirm: { label: "Open As New Project" }, cancel: { label: "Include/Link With Project Later" }, }, callback: (result) => { if (result) gotoNewLocation(); else setTimeout(updateSelector, 1000); // TODO: wait for files to upload } }); } gaEvent('workspace', 'file', 'upload'); } else { var path = f.name; var reader = new FileReader(); reader.onload = function (e) { var arrbuf = e.target.result; var data = new Uint8Array(arrbuf); // convert to UTF8, unless it's a binary file if ((0, util_1.isProbablyBinary)(path, data)) { //gotoMainFile = false; } else { data = (0, util_1.byteArrayToUTF8)(data).replace('\r\n', '\n'); // convert CRLF to LF } // store in local forage exports.projectWindows.updateFile(path, data); console.log("Uploaded " + path + " " + data.length + " bytes"); uploadNextFile(); }; reader.readAsArrayBuffer(f); // read as binary } } if (files) uploadNextFile(); } async function _openLocalDirectory(e) { var pickerfn = window['showDirectoryPicker']; if (!pickerfn) { alertError(`This browser can't open local files on your computer, yet. Try Chrome.`); } var dirHandle = await pickerfn(); var repoid = dirHandle.name; var storekey = '__localfs__' + repoid; var fsdata = { handle: dirHandle, }; var lstore = localforage.createInstance({ name: storekey, version: 2.0 }); await lstore.setItem(storekey, fsdata); exports.qs = { localfs: repoid }; gotoNewLocation(true); } async function promptUser(message) { return new Promise((resolve, reject) => { bootbox.prompt(DOMPurify.sanitize(message), (result) => { resolve(result); }); }); } async function getLocalFilesystem(repoid) { const options = { mode: 'readwrite' }; var storekey = '__localfs__' + repoid; var lstore = localforage.createInstance({ name: storekey, version: 2.0 }); var fsdata = await lstore.getItem(storekey); var dirHandle = fsdata.handle; console.log(fsdata, dirHandle); var granted = await dirHandle.queryPermission(options); console.log(granted); if (granted !== 'granted') { await promptUser(`Request permissions to access filesystem?`); granted = await dirHandle.requestPermission(options); } if (granted !== 'granted') { alertError(`Could not get permission to access filesystem.`); return; } return { getFileData: async (path) => { console.log('getFileData', path); let fileHandle = await dirHandle.getFileHandle(path, { create: false }); console.log('getFileData', fileHandle); let file = await fileHandle.getFile(); console.log('getFileData', file); let contents = await ((0, util_1.isProbablyBinary)(path) ? file.binary() : file.text()); console.log(fileHandle, file, contents); return contents; }, setFileData: async (path, data) => { //let vh = await dirHandle.getFileHandle(path, { create: true }); } }; } function getCurrentMainFilename() { return (0, util_1.getFilenameForPath)(exports.current_project.mainPath); } function getCurrentEditorFilename() { return (0, util_1.getFilenameForPath)(exports.projectWindows.getActiveID()); } // GITHUB stuff (TODO: move) var githubService; function getCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } async function getGithubService() { if (!githubService) { // load github API client await (0, util_1.loadScript)('lib/octokat.js'); // load firebase await (0, util_1.loadScript)('https://www.gstatic.com/firebasejs/8.8.1/firebase-app.js'); await (0, util_1.loadScript)('https://www.gstatic.com/firebasejs/8.8.1/firebase-auth.js'); await (0, util_1.loadScript)('https://8bitworkshop.com/config.js'); // get github API key from cookie // TODO: move to service? var ghkey = getCookie('__github_key'); githubService = new services_1.GithubService(Octokat, ghkey, store, exports.current_project); console.log("loaded github service"); } return githubService; } function getBoundGithubURL() { var toks = (exports.repo_id || '').split('/'); if (toks.length != 2) { alertError("You are not in a GitHub repository. Choose one from the pulldown, or Import or Publish one."); return null; } return 'https://github.com/' + toks[0] + '/' + toks[1]; } async function importProjectFromGithub(githuburl, replaceURL) { var sess; var urlparse = (0, services_1.parseGithubURL)(githuburl); if (!urlparse) { alertError('Could not parse Github URL.'); return; } // redirect to repo if exists var existing = (0, services_1.getRepos)()[urlparse.repopath]; if (existing && !confirm("You've already imported " + urlparse.repopath + " -- do you want to replace all local files?")) { return; } // create new store for imported repository setWaitDialog(true); var newstore = (0, project_1.createNewPersistentStore)(urlparse.repopath); // import into new store setWaitProgress(0.25); var gh = await getGithubService(); return gh.import(githuburl).then((sess1) => { sess = sess1; setWaitProgress(0.75); return gh.pull(githuburl, newstore); }).then((sess2) => { // TODO: only first session has mainPath? // reload repo exports.qs = { repo: sess.repopath }; // file:sess.mainPath, platform:sess.platform_id}; setWaitDialog(false); gaEvent('sync', 'import', githuburl); gotoNewLocation(replaceURL); }).catch((e) => { setWaitDialog(false); console.log(e); alertError("Could not import " + githuburl + "." + e); }); } async function _loginToGithub(e) { var gh = await getGithubService(); gh.login().then(() => { alertInfo("You are signed in to Github."); }).catch((e) => { alertError("Could not sign in." + e); }); } async function _logoutOfGithub(e) { var gh = await getGithubService(); gh.logout().then(() => { alertInfo("You are logged out of Github."); }); } function _importProjectFromGithub(e) { var modal = $("#importGithubModal"); var btn = $("#importGithubButton"); modal.modal('show'); btn.off('click').on('click', () => { var githuburl = $("#importGithubURL").val() + ""; modal.modal('hide'); importProjectFromGithub(githuburl, false); }); } function _publishProjectToGithub(e) { if (exports.repo_id) { if (!confirm("This project (" + exports.current_project.mainPath + ") is already bound to a Github repository. Do you want to re-publish to a new repository? (You can instead choose 'Push Changes' to update files in the existing repository.)")) return; } var modal = $("#publishGithubModal"); var btn = $("#publishGithubButton"); $("#githubRepoName").val((0, util_1.getFilenamePrefix)((0, util_1.getFilenameForPath)(exports.current_project.mainPath))); modal.modal('show'); btn.off('click').on('click', async () => { var name = $("#githubRepoName").val() + ""; var desc = $("#githubRepoDesc").val() + ""; var priv = $("#githubRepoPrivate").val() == 'private'; var license = $("#githubRepoLicense").val() + ""; var sess; if (!name) { alertError("You did not enter a project name."); return; } modal.modal('hide'); setWaitDialog(true); var gh = await getGithubService(); gh.login().then(() => { setWaitProgress(0.25); return gh.publish(name, desc, license, priv); }).then((_sess) => { sess = _sess; setWaitProgress(0.5); exports.repo_id = exports.qs.repo = sess.repopath; return pushChangesToGithub('initial import from 8bitworkshop.com'); }).then(() => { gaEvent('sync', 'publish', priv ? "" : name); importProjectFromGithub(sess.url, false); }).catch((e) => { setWaitDialog(false); console.log(e); alertError("Could not publish GitHub repository: " + e); }); }); } function _pushProjectToGithub(e) { var ghurl = getBoundGithubURL(); if (!ghurl) return; var modal = $("#pushGithubModal"); var btn = $("#pushGithubButton"); modal.modal('show'); btn.off('click').on('click', () => { var commitMsg = $("#githubCommitMsg").val() + ""; modal.modal('hide'); pushChangesToGithub(commitMsg); }); } function _pullProjectFromGithub(e) { var ghurl = getBoundGithubURL(); if (!ghurl) return; bootbox.confirm("Pull from repository and replace all local files? Any changes you've made will be overwritten.", async (ok) => { if (ok) { setWaitDialog(true); var gh = await getGithubService(); gh.pull(ghurl).then((sess) => { setWaitDialog(false); exports.projectWindows.updateAllOpenWindows(store); }); } }); } function confirmCommit(sess) { return new Promise((resolve, reject) => { var files = sess.commit.files; console.log(files); // anything changed? if (files.length == 0) { setWaitDialog(false); alertInfo("No files changed."); return; } // build commit confirm message var msg = ""; for (var f of files) { msg += DOMPurify.sanitize(f.filename) + ": " + f.status; if (f.additions || f.deletions || f.changes) { msg += " (" + f.additions + " additions, " + f.deletions + " deletions, " + f.changes + " changes)"; } ; msg += "
"; } // show dialog, continue when yes bootbox.confirm(msg, (ok) => { if (ok) { resolve(sess); } else { setWaitDialog(false); } }); }); } async function pushChangesToGithub(message) { var ghurl = getBoundGithubURL(); if (!ghurl) return; // build file list for push var files = []; for (var path in exports.current_project.filedata) { var newpath = exports.current_project.stripLocalPath(path); var data = exports.current_project.filedata[path]; if (newpath && data) { files.push({ path: newpath, data: data }); } } // include built ROM file in bin/[mainfile].rom if (current_output instanceof Uint8Array) { let binpath = "bin/" + getCurrentMainFilename() + ".rom"; files.push({ path: binpath, data: current_output }); } // push files setWaitDialog(true); var gh = await getGithubService(); return gh.login().then(() => { setWaitProgress(0.5); return gh.commit(ghurl, message, files); }).then((sess) => { return confirmCommit(sess); }).then((sess) => { return gh.push(sess); }).then((sess) => { setWaitDialog(false); alertInfo("Pushed files to " + ghurl); return sess; }).catch((e) => { setWaitDialog(false); console.log(e); alertError("Could not push GitHub repository: " + e); }); } function _removeRepository() { var ghurl = getBoundGithubURL(); if (!ghurl) return; bootbox.prompt("

Are you sure you want to delete this repository (" + DOMPurify.sanitize(ghurl) + ") from browser storage?

All changes since last commit will be lost.

Type DELETE to proceed.

", (yes) => { if (yes.trim().toUpperCase() == "DELETE") { removeRepository(); } }); } async function removeRepository() { var ghurl = getBoundGithubURL(); setWaitDialog(true); let gh = await getGithubService(); let sess = await gh.getGithubSession(ghurl); gh.bind(sess, false); // delete all keys in (repo) storage await store.keys().then((keys) => { return Promise.all(keys.map((key) => { return store.removeItem(key); })); }); setWaitDialog(false); // leave repository exports.qs = { repo: '/' }; gotoNewLocation(); } function _shareEmbedLink(e) { if (current_output == null) { alertError("Please fix errors before sharing."); return true; } if (!(current_output instanceof Uint8Array)) { alertError("Can't share a Verilog executable yet. (It's not actually a ROM...)"); return true; } loadClipboardLibrary(); (0, util_1.loadScript)('lib/liblzg.js').then(() => { // TODO: Module is bad var name (conflicts with MAME) var lzgrom = (0, util_1.compressLZG)(window['Module'], Array.from(current_output)); window['Module'] = null; // so we load it again next time var lzgb64 = btoa((0, util_1.byteArrayToString)(lzgrom)); var embed = { p: exports.platform_id, //n: current_project.mainPath, r: lzgb64 }; var linkqs = $.param(embed); var fulllink = get8bitworkshopLink(linkqs, 'player.html'); var iframelink = '