"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 = '