8bitworkshop/src/ui.js

1434 lines
39 KiB
JavaScript
Raw Normal View History

"use strict";
2016-12-16 01:21:51 +00:00
// catch errors
function installErrorHandler() {
if (typeof window.onerror == "object") {
window.onerror = function (msgevent, url, line, col, error) {
console.log(msgevent, url, line, col);
console.log(error);
if (window.location.host.endsWith('8bitworkshop.com')) {
ga('send', 'exception', {
'exDescription': msgevent + " " + url + " " + " " + line + ":" + col + ", " + error,
'exFatal': true
});
}
alert(msgevent+"");
};
}
}
function uninstallErrorHandler() {
window.onerror = null;
}
function gotoNewLocation() {
uninstallErrorHandler();
window.location = "?" + $.param(qs);
}
// make sure VCS doesn't start
2017-11-21 21:36:38 +00:00
if (window.Javatari) Javatari.AUTO_START = false;
2017-01-13 02:21:35 +00:00
// 8bitworkshop IDE user interface
var PRESETS; // presets array
var platform_id;
var platform; // platform object
var toolbar = $("#controls_top");
var SourceFile = function(lines, text) {
lines = lines || [];
this.lines = lines;
this.text = text;
this.offset2line = {};
this.line2offset = {};
for (var info of lines) {
if (info.offset >= 0) {
this.offset2line[info.offset] = info.line;
this.line2offset[info.line] = info.offset;
}
}
this.findLineForOffset = function(PC) {
if (this.offset2line) {
for (var i=0; i<16; i++) {
var line = this.offset2line[PC];
if (line >= 0) {
return line;
}
PC--;
}
}
return 0;
}
2017-04-22 17:33:20 +00:00
this.lineCount = function() { return this.line2offset.length; }
}
var CodeProject = function(worker, platform_id, platform, store) {
var self = this;
self.callbackResendFiles = function() { }; // TODO?
self.callbackBuildResult = function(result) { };
self.callbackBuildStatus = function(busy) { };
var pendingWorkerMessages = 0;
var tools_preloaded = {};
function preloadWorker(path) {
var tool = platform.getToolForFilename(path);
if (tool && !tools_preloaded[tool]) {
worker.postMessage({preload:tool, platform:platform_id});
tools_preloaded[tool] = true;
}
}
function loadFileDependencies(text, callback) {
var filenames = [];
if (platform_id == 'verilog') {
var re = /^(`include|[.]include)\s+"(.+?)"/gm;
var m;
while (m = re.exec(text)) {
filenames.push(m[2]);
}
}
var result = [];
function loadNextDependency() {
var fn = filenames.shift();
if (!fn) {
callback(result);
} else {
store.getItem(fn, function(err, value) {
result.push({
filename:fn,
prefix:platform_id,
text:value // might be null, that's ok
});
loadNextDependency();
});
}
}
loadNextDependency(); // load first dependency
}
function okToSend() {
return pendingWorkerMessages++ == 0;
}
function updateFileInStore(path, text) {
// protect against accidential whole-file deletion
if (text.trim().length) {
// TODO? (originalFileID != path || text != originalText)) {
store.setItem(path, text);
}
}
self.updateFile = function(path, text, isBinary) {
updateFileInStore(path, text); // TODO: isBinary
preloadWorker(path);
if (okToSend()) {
self.callbackBuildStatus(true);
loadFileDependencies(text, function(depends) {
worker.postMessage({
code:text,
dependencies:depends,
platform:platform_id,
tool:platform.getToolForFilename(path)
});
});
}
};
function processBuildResult(data) {
if (data.listings) {
for (var lstname in data.listings) {
var lst = data.listings[lstname];
if (lst.lines)
lst.sourcefile = new SourceFile(lst.lines);
if (lst.asmlines)
lst.assemblyfile = new SourceFile(lst.asmlines, lst.text);
}
}
}
worker.onmessage = function(e) {
if (pendingWorkerMessages > 1) {
self.callbackResendFiles(); // TODO: we should handle this internally
pendingWorkerMessages = 0;
} else {
pendingWorkerMessages = 0;
}
self.callbackBuildStatus(false);
if (e.data && !e.data.unchanged) {
processBuildResult(e.data);
self.callbackBuildResult(e.data);
}
};
// TODO: parse output, listings, files, etc
}
var current_project;
var TOOL_TO_SOURCE_STYLE = {
'dasm': '6502',
'acme': '6502',
'cc65': 'text/x-csrc',
'ca65': '6502',
'z80asm': 'z80',
'sdasz80': 'z80',
'sdcc': 'text/x-csrc',
2017-11-11 19:45:32 +00:00
'verilator': 'verilog',
2018-03-02 05:15:33 +00:00
'jsasm': 'z80'
}
function newWorker() {
return new Worker("./src/worker/workermain.js");
}
2018-06-28 04:57:06 +00:00
var disasmview = CodeMirror(document.getElementById('disassembly'), {
mode: 'z80',
theme: 'cobalt',
tabSize: 8,
readOnly: true,
styleActiveLine: true
});
var userPaused;
var editor;
var current_output;
var current_preset_entry;
var current_file_id;
var assemblyfile;
var sourcefile;
var symbolmap;
var addr2symbol;
var compparams;
2016-12-16 01:21:51 +00:00
var trace_pending_at_pc;
var store;
//scrollProfileView(disasmview);
2017-01-08 15:51:19 +00:00
var currentDebugLine;
var lastDebugInfo;
var lastDebugState;
2017-04-19 01:18:53 +00:00
var memorylist;
2017-04-22 17:33:20 +00:00
2018-06-28 04:57:06 +00:00
function deleteEditor() {
if (editor) {
$("#editor").empty();
editor = null;
}
}
function newEditor(mode) {
2018-06-28 04:57:06 +00:00
deleteEditor();
2018-03-02 05:15:33 +00:00
var isAsm = mode=='6502' || mode =='z80' || mode=='verilog' || mode=='gas'; // TODO
editor = CodeMirror(document.getElementById('editor'), {
theme: 'mbo',
lineNumbers: true,
matchBrackets: true,
tabSize: 8,
2017-02-20 23:50:29 +00:00
indentAuto: true,
gutters: isAsm ? ["CodeMirror-linenumbers", "gutter-offset", "gutter-bytes", "gutter-clock", "gutter-info"]
: ["CodeMirror-linenumbers", "gutter-offset", "gutter-info"],
});
2017-02-20 23:50:29 +00:00
var timer;
editor.on('changes', function(ed, changeobj) {
2017-02-20 23:50:29 +00:00
clearTimeout(timer);
timer = setTimeout(function() {
setCode(editor.getValue());
2017-02-20 23:50:29 +00:00
}, 200);
});
editor.on('cursorActivity', function(ed) {
var start = editor.getCursor(true);
var end = editor.getCursor(false);
if (start.line == end.line && start.ch < end.ch) {
var name = editor.getSelection();
inspectVariable(editor, name);
} else {
inspectVariable(editor);
}
});
//scrollProfileView(editor);
editor.setOption("mode", mode);
}
2016-12-16 01:21:51 +00:00
function inspectVariable(ed, name) {
var val;
if (platform.inspect) {
platform.inspect(name);
}
}
2016-12-16 01:21:51 +00:00
function getCurrentPresetTitle() {
if (!current_preset_entry)
2016-12-16 01:21:51 +00:00
return "ROM";
else
return current_preset_entry.title || current_preset_entry.name || "ROM";
2016-12-16 01:21:51 +00:00
}
function setLastPreset(id) {
if (platform_id != 'base_z80') { // TODO
localStorage.setItem("__lastplatform", platform_id);
localStorage.setItem("__lastid_"+platform_id, id);
}
2016-12-16 01:21:51 +00:00
}
function loadCode(text, fileid) {
current_project = new CodeProject(newWorker(), platform_id, platform, store);
current_project.callbackResendFiles = function() {
setCode(editor.getValue()); // TODO
};
current_project.callbackBuildResult = function(result) {
setCompileOutput(result);
};
current_project.callbackBuildStatus = function(busy) {
if (busy) {
toolbar.addClass("is-busy");
} else {
toolbar.removeClass("is-busy");
}
$('#compile_spinner').css('visibility', busy ? 'visible' : 'hidden');
};
var tool = platform.getToolForFilename(fileid);
newEditor(tool && TOOL_TO_SOURCE_STYLE[tool]);
editor.setValue(text); // calls setCode()
editor.clearHistory();
2016-12-16 01:21:51 +00:00
current_output = null;
setLastPreset(fileid);
2016-12-16 01:21:51 +00:00
}
function loadFile(fileid, filename, preset) {
current_file_id = fileid;
current_preset_entry = preset;
2018-06-26 06:56:36 +00:00
store.getItem(fileid, function(err, text) {
if (err) console.log(err);
if (!text) text = '';
if (text) {
loadCode(text, fileid);
} else if (!text && preset) {
2018-06-28 04:57:06 +00:00
if (platform_id == 'vcs' && filename.indexOf('.') <= 0)
filename += ".a"; // legacy stuff
console.log("Loading preset", fileid, filename, preset);
2018-06-26 06:56:36 +00:00
if (text.length == 0) {
console.log("Fetching", filename);
$.get( filename, function( text ) {
console.log("GET",text.length,'bytes');
loadCode(text, fileid);
}, 'text')
.fail(function() {
alert("Could not load preset " + fileid);
loadCode("", fileid);
});
}
} else {
var ext = platform.getToolForFilename(fileid);
$.get( "presets/"+platform_id+"/skeleton."+ext, function( text ) {
loadCode(text, fileid);
}, 'text')
.fail(function() {
2018-06-26 06:56:36 +00:00
alert("Could not load skeleton for " + platform_id + "/" + ext);
loadCode("", fileid);
});
2016-12-16 01:21:51 +00:00
}
2018-06-26 06:56:36 +00:00
});
2016-12-16 01:21:51 +00:00
}
2018-06-28 04:57:06 +00:00
// can pass integer or string id
2016-12-16 01:21:51 +00:00
function loadPreset(preset_id) {
2018-06-28 04:57:06 +00:00
var index = parseInt(preset_id+""); // might fail -1
2016-12-16 01:21:51 +00:00
for (var i=0; i<PRESETS.length; i++)
if (PRESETS[i].id == preset_id)
index = i;
index = (index + PRESETS.length) % PRESETS.length;
if (index >= 0) {
// load the preset
loadFile(preset_id, "presets/" + platform_id + "/" + PRESETS[index].id, PRESETS[index]);
2016-12-16 01:21:51 +00:00
} else {
// no preset found? load local
loadFile(preset_id, "local/" + platform_id + "/" + preset_id);
2016-12-16 01:21:51 +00:00
}
}
2018-06-28 04:57:06 +00:00
function reloadPresetNamed(id) {
qs['platform'] = platform_id;
qs['file'] = id;
gotoNewLocation();
2016-12-16 01:21:51 +00:00
}
function _createNewFile(e) {
var filename = prompt("Create New File", "newfile" + platform.getDefaultExtension());
2016-12-16 01:21:51 +00:00
if (filename && filename.length) {
if (filename.indexOf(".") < 0) {
2017-04-20 00:55:13 +00:00
filename += platform.getDefaultExtension();
2016-12-16 01:21:51 +00:00
}
qs['file'] = "local/" + filename;
gotoNewLocation();
2016-12-16 01:21:51 +00:00
}
2016-12-30 23:51:15 +00:00
return true;
2016-12-16 01:21:51 +00:00
}
2018-06-26 23:57:03 +00:00
function _uploadNewFile(e) {
$("#uploadFileElem").click();
}
function handleFileUpload(files) {
console.log(files);
var index = 0;
function uploadNextFile() {
var f = files[index++];
if (!f) {
console.log("Done uploading");
gotoNewLocation();
} else {
var path = "local/" + f.name;
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
store.setItem(path, data, function(err, result) {
if (err)
console.log(err);
else {
console.log("Uploaded " + path + " " + data.length + " bytes");
if (index == 1)
qs['file'] = path;
uploadNextFile();
}
});
}
reader.readAsText(f);
}
}
if (files) uploadNextFile();
}
2017-02-02 19:11:52 +00:00
function getCurrentFilename() {
var toks = current_file_id.split("/");
2017-02-02 19:11:52 +00:00
return toks[toks.length-1];
}
2016-12-16 01:21:51 +00:00
function _shareFile(e) {
if (current_output == null) {
2016-12-30 23:51:15 +00:00
alert("Please fix errors before sharing.");
return true;
}
2017-01-25 17:30:05 +00:00
var github = new Octokat();
var files = {};
2016-12-16 01:21:51 +00:00
var text = editor.getValue();
2017-02-02 19:11:52 +00:00
files[getCurrentFilename()] = {"content": text};
2017-01-25 17:30:05 +00:00
var gistdata = {
"description": '8bitworkshop.com {"platform":"' + platform_id + '"}',
"public": true,
"files": files
};
var gist = github.gists.create(gistdata).done(function(val) {
var url = "http://8bitworkshop.com/?sharekey=" + val.id;
window.prompt("Copy link to clipboard (Ctrl+C, Enter)", url);
}).fail(function(err) {
alert("Error sharing file: " + err.message);
2016-12-16 01:21:51 +00:00
});
2016-12-30 23:51:15 +00:00
return true;
2016-12-16 01:21:51 +00:00
}
function _resetPreset(e) {
if (!current_preset_entry) {
2017-01-03 01:42:15 +00:00
alert("Can only reset built-in file examples.")
} else if (confirm("Reset '" + current_preset_entry.name + "' to default?")) {
2016-12-16 01:21:51 +00:00
qs['reset'] = '1';
gotoNewLocation();
2016-12-16 01:21:51 +00:00
}
2016-12-30 23:51:15 +00:00
return true;
2016-12-16 01:21:51 +00:00
}
2017-02-02 19:11:52 +00:00
function _downloadROMImage(e) {
if (current_output == null) {
alert("Please fix errors before downloading ROM.");
return true;
}
var blob = new Blob([current_output], {type: "application/octet-stream"});
saveAs(blob, getCurrentFilename()+".rom");
}
function _downloadSourceFile(e) {
var blob = new Blob([editor.getValue()], {type: "text/plain;charset=utf-8"});
saveAs(blob, getCurrentFilename());
}
2016-12-16 01:21:51 +00:00
function populateExamples(sel) {
2018-06-26 06:56:36 +00:00
// make sure to use callback so it follows other sections
store.length(function(err, len) {
sel.append($("<option />").text("--------- Examples ---------").attr('disabled',true));
for (var i=0; i<PRESETS.length; i++) {
var preset = PRESETS[i];
var name = preset.chapter ? (preset.chapter + ". " + preset.name) : preset.name;
sel.append($("<option />").val(preset.id).text(name).attr('selected',preset.id==current_file_id));
2018-06-26 06:56:36 +00:00
}
});
2016-12-16 01:21:51 +00:00
}
2018-06-26 06:56:36 +00:00
function populateFiles(sel, category, prefix) {
store.keys(function(err, keys) {
var foundSelected = false;
var numFound = 0;
if (!keys) keys = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key.startsWith(prefix)) {
if (numFound++ == 0)
sel.append($("<option />").text("------- " + category + " -------").attr('disabled',true));
var name = key.substring(prefix.length);
sel.append($("<option />").val(key).text(name).attr('selected',key==current_file_id));
if (key == current_file_id) foundSelected = true;
2018-06-26 06:56:36 +00:00
}
}
if (!foundSelected && current_file_id && current_file_id.startsWith(prefix)) {
var name = current_file_id.slice(prefix.length);
2018-06-26 06:56:36 +00:00
var key = prefix + name;
sel.append($("<option />").val(key).text(name).attr('selected',true));
}
});
2016-12-16 01:21:51 +00:00
}
function updateSelector() {
var sel = $("#preset_select").empty();
if (platform_id != 'base_z80') { // TODO
populateFiles(sel, "Local Files", "local/");
populateFiles(sel, "Shared", "shared/");
}
2016-12-16 01:21:51 +00:00
populateExamples(sel);
// set click handlers
sel.off('change').change(function(e) {
2018-06-28 04:57:06 +00:00
reloadPresetNamed($(this).val());
2016-12-16 01:21:51 +00:00
});
}
function setCode(text) {
current_project.updateFile(current_file_id, text, false);
2016-12-16 01:21:51 +00:00
}
function setCompileOutput(data) {
2017-05-04 15:54:56 +00:00
// TODO: kills current selection
// TODO: use current_project
// choose first listing (TODO:support multiple source files)
sourcefile = null;
assemblyfile = null;
if (data.listings) {
var lst;
for (var lstname in data.listings) {
lst = data.listings[lstname];
break;
}
if (lst) {
sourcefile = lst.sourcefile;
assemblyfile = lst.assemblyfile;
}
}
if (!sourcefile) sourcefile = new SourceFile();
symbolmap = data.symbolmap;
addr2symbol = invertMap(symbolmap);
addr2symbol[0x10000] = '__END__'; // TODO?
compparams = data.params;
2016-12-16 01:21:51 +00:00
// errors?
var lines2errmsg = [];
function addErrorMarker(line, msg) {
var div = document.createElement("div");
div.setAttribute("class", "tooltipbox tooltiperror");
div.style.color = '#ff3333'; // TODO
div.appendChild(document.createTextNode("\u24cd"));
var tooltip = document.createElement("span");
tooltip.setAttribute("class", "tooltiptext");
if (lines2errmsg[line])
msg = lines2errmsg[line] + "\n" + msg;
tooltip.appendChild(document.createTextNode(msg));
lines2errmsg[line] = msg;
div.appendChild(tooltip);
editor.setGutterMarker(line, "gutter-info", div);
}
2018-06-25 23:47:40 +00:00
if (data.errors && data.errors.length > 0) {
// TODO: move cursor to error line if offscreen?
2017-01-06 14:49:07 +00:00
toolbar.addClass("has-errors");
2016-12-16 01:21:51 +00:00
editor.clearGutter("gutter-info");
2017-02-20 23:50:29 +00:00
var numLines = editor.lineCount();
for (info of data.errors) {
2017-02-20 23:50:29 +00:00
var line = info.line-1;
if (line < 0 || line >= numLines) line = numLines-1;
addErrorMarker(line, info.msg);
2016-12-16 01:21:51 +00:00
}
current_output = null;
} else {
// load ROM
// TODO: don't have to compare anymore; worker does it
var rom = data.output;
var rom_changed = false;
if (rom && rom.code)
rom_changed = !current_output || rom.code != current_output.code;
else if (rom)
rom_changed = !arrayCompare(rom, current_output);
2016-12-16 01:21:51 +00:00
if (rom_changed) {
try {
//console.log("Loading ROM length", rom.length);
2016-12-31 16:05:22 +00:00
platform.loadROM(getCurrentPresetTitle(), rom);
if (!userPaused) resume();
2016-12-16 01:21:51 +00:00
current_output = rom;
//resetProfiler();
toolbar.removeClass("has-errors");
2016-12-16 01:21:51 +00:00
} catch (e) {
console.log(e); // TODO: show error
toolbar.addClass("has-errors");
addErrorMarker(0, e+"");
2016-12-16 01:21:51 +00:00
current_output = null;
}
2018-03-02 05:15:33 +00:00
} else if (rom.program_rom_variable) { //TODO: a little wonky...
platform.loadROM(rom.program_rom_variable, rom.program_rom);
rom_changed = true;
2016-12-16 01:21:51 +00:00
}
if (rom_changed || trace_pending_at_pc) {
// update editor annotations
2017-01-17 21:50:57 +00:00
// TODO: do incrementally for performance
2016-12-16 01:21:51 +00:00
editor.clearGutter("gutter-info");
editor.clearGutter("gutter-bytes");
editor.clearGutter("gutter-offset");
editor.clearGutter("gutter-clock");
// TODO: support multiple files
var lstlines = sourcefile.lines || [];
for (var info of lstlines) {
if (info.offset >= 0) {
var textel = document.createTextNode(hex(info.offset,4));
2016-12-16 01:21:51 +00:00
editor.setGutterMarker(info.line-1, "gutter-offset", textel);
}
if (info.insns) {
var insnstr = info.insns.length > 9 ? ("...") : info.insns;
2016-12-16 01:21:51 +00:00
var textel = document.createTextNode(insnstr);
editor.setGutterMarker(info.line-1, "gutter-bytes", textel);
if (info.iscode) {
var opcode = parseInt(info.insns.split()[0], 16);
if (platform.getOpcodeMetadata) {
var meta = platform.getOpcodeMetadata(opcode, info.offset);
var clockstr = meta.minCycles+"";
var textel = document.createTextNode(clockstr);
editor.setGutterMarker(info.line-1, "gutter-clock", textel);
}
2016-12-16 01:21:51 +00:00
}
}
}
}
updateDisassembly();
2016-12-16 01:21:51 +00:00
if (trace_pending_at_pc) {
showLoopTimingForPC(trace_pending_at_pc);
}
}
trace_pending_at_pc = null;
}
function setCurrentLine(line) {
2018-06-24 17:39:08 +00:00
function addCurrentMarker(line) {
var div = document.createElement("div");
div.style.color = '#66ffff'; // TODO
div.appendChild(document.createTextNode("\u25b6"));
editor.setGutterMarker(line, "gutter-info", div);
}
clearCurrentLine();
if (line>0) {
addCurrentMarker(line-1);
editor.setSelection({line:line,ch:0}, {line:line-1,ch:0}, {scroll:true});
currentDebugLine = line;
}
2016-12-16 01:21:51 +00:00
}
2018-06-24 17:39:08 +00:00
function clearCurrentLine() {
if (currentDebugLine) {
editor.clearGutter("gutter-info");
editor.setSelection(editor.getCursor()); // TODO??
currentDebugLine = 0;
}
}
2016-12-16 01:21:51 +00:00
function showMemory(state) {
var s = state && platform.cpuStateToLongString && platform.cpuStateToLongString(state.c);
if (s) {
2017-01-20 03:42:58 +00:00
if (platform.getRasterPosition) {
var pos = platform.getRasterPosition();
s += "H:" + pos.x + " V:" + pos.y + "\n"; // TODO: padding
}
if (platform.ramStateToLongString) {
s += platform.ramStateToLongString(state);
2016-12-16 01:21:51 +00:00
}
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
$("#mem_info").show().html(hs);
lastDebugInfo = s;
} else {
$("#mem_info").hide();
lastDebugInfo = null;
}
}
function setupBreakpoint() {
// TODO
2016-12-31 16:05:22 +00:00
platform.setupDebug(function(state) {
2017-01-06 14:49:07 +00:00
lastDebugState = state;
2017-11-24 19:14:22 +00:00
if (state.c) {
var PC = state.c.PC;
var line = sourcefile.findLineForOffset(PC);
if (line >= 0) {
console.log("BREAKPOINT", hex(PC), line);
setCurrentLine(line);
} else {
console.log("BREAKPOINT", hex(PC));
// TODO: switch to disasm
}
2016-12-16 01:21:51 +00:00
}
showMemory(state);
2017-01-06 14:49:07 +00:00
updateDisassembly();
2016-12-31 16:05:22 +00:00
});
2016-12-16 01:21:51 +00:00
}
2017-11-16 17:08:06 +00:00
function _pause() {
2016-12-31 16:05:22 +00:00
if (platform.isRunning()) {
platform.pause();
console.log("Paused");
2016-12-16 01:21:51 +00:00
}
2017-11-16 17:08:06 +00:00
$("#dbg_pause").addClass("btn_stopped");
$("#dbg_go").removeClass("btn_active");
2016-12-16 01:21:51 +00:00
}
2017-11-16 17:08:06 +00:00
function pause() {
2016-12-16 01:21:51 +00:00
clearBreakpoint();
2017-11-16 17:08:06 +00:00
_pause();
userPaused = true;
2017-11-16 17:08:06 +00:00
}
function _resume() {
2016-12-31 16:05:22 +00:00
if (! platform.isRunning()) {
platform.resume();
console.log("Resumed");
2016-12-16 01:21:51 +00:00
}
2017-11-16 17:08:06 +00:00
$("#dbg_pause").removeClass("btn_stopped");
$("#dbg_go").addClass("btn_active");
}
function resume() {
clearBreakpoint();
2018-06-24 17:39:08 +00:00
if (! platform.isRunning() ) {
clearCurrentLine();
}
2017-11-16 17:08:06 +00:00
_resume();
userPaused = false;
2016-12-16 01:21:51 +00:00
}
function singleStep() {
setupBreakpoint();
2016-12-31 16:05:22 +00:00
platform.step();
2016-12-16 01:21:51 +00:00
}
2017-11-24 19:14:22 +00:00
function singleFrameStep() {
setupBreakpoint();
platform.runToVsync();
}
2016-12-16 01:21:51 +00:00
function getCurrentLine() {
return editor.getCursor().line+1;
}
2017-02-18 22:50:51 +00:00
function getDisasmViewPC() {
var line = disasmview.getCursor().line;
2017-02-25 15:57:39 +00:00
if (line >= 0) {
2017-02-18 22:50:51 +00:00
var toks = disasmview.getLine(line).split(/\s+/);
2017-02-25 15:57:39 +00:00
if (toks && toks[0].length == 4) {
2017-02-18 22:50:51 +00:00
return parseInt(toks[0], 16);
}
}
}
2017-02-05 04:19:54 +00:00
function getCurrentPC() {
var line = getCurrentLine();
2017-02-25 15:57:39 +00:00
while (line >= 0) {
// TODO: what if in disassembler?
2017-02-25 15:57:39 +00:00
var pc = sourcefile.line2offset[line];
if (pc >= 0) return pc;
line--;
2017-02-05 04:19:54 +00:00
}
2017-02-25 15:57:39 +00:00
return getDisasmViewPC();
2017-02-05 04:19:54 +00:00
}
function runToCursor() {
setupBreakpoint();
var pc = getCurrentPC();
if (pc >= 0) {
2017-02-05 04:19:54 +00:00
console.log("Run to", pc.toString(16));
if (platform.runToPC) {
platform.runToPC(pc);
} else {
platform.runEval(function(c) {
return c.PC == pc;
});
}
2016-12-16 01:21:51 +00:00
}
}
function runUntilReturn() {
setupBreakpoint();
platform.runUntilReturn();
}
function runStepBackwards() {
setupBreakpoint();
platform.stepBack();
}
2016-12-16 01:21:51 +00:00
function clearBreakpoint() {
2017-01-06 14:49:07 +00:00
lastDebugState = null;
2017-11-11 19:45:32 +00:00
if (platform.clearDebug) platform.clearDebug();
2016-12-16 01:21:51 +00:00
showMemory();
}
function jumpToLine(ed, i) {
var t = ed.charCoords({line: i, ch: 0}, "local").top;
var middleHeight = ed.getScrollerElement().offsetHeight / 2;
ed.scrollTo(null, t - middleHeight - 5);
}
2017-04-22 17:33:20 +00:00
function getVisibleSourceFile() {
var div = $("#disassembly");
return div.is(':visible') ? assemblyfile : sourcefile;
}
2017-01-06 14:49:07 +00:00
function updateDisassembly() {
var div = $("#disassembly");
if (div.is(':visible')) {
var state = lastDebugState || platform.saveState();
var pc = state.c ? state.c.PC : 0;
// do we have an assembly listing?
if (assemblyfile && assemblyfile.text) {
var asmtext = assemblyfile.text;
if (platform_id == 'base_z80') { // TODO
asmtext = asmtext.replace(/[ ]+\d+\s+;.+\n/g, '');
asmtext = asmtext.replace(/[ ]+\d+\s+.area .+\n/g, '');
}
disasmview.setValue(asmtext);
2017-02-18 22:50:51 +00:00
var findPC = platform.getDebugCallback() ? pc : getCurrentPC();
if (findPC) {
var lineno = assemblyfile.findLineForOffset(findPC);
if (lineno) {
// set cursor while debugging
2017-02-18 22:50:51 +00:00
if (platform.getDebugCallback()) disasmview.setCursor(lineno-1, 0);
jumpToLine(disasmview, lineno-1);
return; // success, don't disassemble in next step
}
}
}
// fall through to platform disassembler?
if (platform.disassemble) {
var curline = 0;
var selline = 0;
// TODO: not perfect disassembler
function disassemble(start, end) {
if (start < 0) start = 0;
if (end > 0xffff) end = 0xffff;
// TODO: use pc2visits
var a = start;
var s = "";
while (a < end) {
var disasm = platform.disassemble(a, platform.readAddress);
var srclinenum = sourcefile.offset2line[a];
if (srclinenum) {
var srcline = editor.getLine(srclinenum-1);
if (srcline && srcline.trim().length) {
s += "; " + srclinenum + ":\t" + srcline + "\n";
curline++;
}
2017-01-08 15:51:19 +00:00
}
var bytes = "";
for (var i=0; i<disasm.nbytes; i++)
bytes += hex(platform.readAddress(a+i));
while (bytes.length < 14)
bytes += ' ';
var dline = hex(parseInt(a)) + "\t" + bytes + "\t" + disasm.line + "\n";
s += dline;
if (a == pc) selline = curline;
curline++;
a += disasm.nbytes || 1;
2017-01-08 15:51:19 +00:00
}
return s;
2017-01-08 15:51:19 +00:00
}
var text = disassemble(pc-96, pc) + disassemble(pc, pc+96);
disasmview.setValue(text);
disasmview.setCursor(selline, 0);
jumpToLine(disasmview, selline);
2018-02-15 00:26:40 +00:00
} else if (current_output && current_output.code) {
// show verilog javascript
disasmview.setValue(current_output.code);
2017-01-08 15:51:19 +00:00
}
2017-01-06 14:49:07 +00:00
}
}
function toggleDisassembly() {
$("#disassembly").toggle();
$("#editor").toggle();
updateDisassembly();
//if (profilelist) createProfileWindow();
2017-01-06 14:49:07 +00:00
}
2016-12-16 01:21:51 +00:00
function resetAndDebug() {
if (platform.setupDebug && platform.readAddress) { // TODO??
2017-11-11 19:45:32 +00:00
clearBreakpoint();
2017-11-16 17:08:06 +00:00
_resume();
2017-11-11 19:45:32 +00:00
platform.reset();
setupBreakpoint();
2018-06-22 06:24:52 +00:00
if (platform.runEval)
platform.runEval(function(c) { return true; }); // break immediately
else
; // TODO???
2017-11-11 19:45:32 +00:00
} else {
platform.reset();
}
2017-01-03 01:42:15 +00:00
}
2017-01-06 16:57:28 +00:00
var lastBreakExpr = "c.PC = 0x6000";
function _breakExpression() {
var exprs = window.prompt("Enter break expression", lastBreakExpr);
if (exprs) {
var fn = new Function('c', 'return (' + exprs + ');');
setupBreakpoint();
platform.runEval(fn);
lastBreakExpr = exprs;
}
}
function getSymbolAtAddress(a) {
if (addr2symbol[a]) return addr2symbol[a];
var i=0;
while (--a >= 0) {
i++;
if (addr2symbol[a]) return addr2symbol[a] + '+' + i;
}
return '';
}
2017-04-19 01:18:53 +00:00
function updateDebugWindows() {
if (platform.isRunning()) {
updateMemoryWindow();
//updateProfileWindow();
2017-04-19 01:18:53 +00:00
}
setTimeout(updateDebugWindows, 200);
}
function updateMemoryWindow() {
2017-04-26 01:14:17 +00:00
if (memorylist) {
2017-04-19 01:18:53 +00:00
$("#memoryview").find('[data-index]').each(function(i,e) {
var div = $(e);
var row = div.attr('data-index');
2017-04-19 01:18:53 +00:00
var oldtext = div.text();
var newtext = getMemoryLineAt(row);
2017-04-19 01:18:53 +00:00
if (oldtext != newtext)
div.text(newtext);
});
}
}
function getMemoryLineAt(row) {
var offset = row * 16;
var n1 = 0;
var n2 = 16;
var sym;
if (getDumpLines()) {
var dl = dumplines[row];
if (dl) {
offset = dl.a & 0xfff0;
n1 = dl.a - offset;
n2 = n1 + dl.l;
sym = dl.s;
} else {
return '.';
}
}
2017-04-19 01:18:53 +00:00
var s = hex(offset,4) + ' ';
for (var i=0; i<n1; i++) s += ' ';
if (n1 > 8) s += ' ';
for (var i=n1; i<n2; i++) {
var read = platform.readAddress(offset+i);
2017-04-19 01:18:53 +00:00
if (i==8) s += ' ';
s += ' ' + (read>=0?hex(read,2):'??');
}
for (var i=n2; i<16; i++) s += ' ';
if (sym) s += ' ' + sym;
2017-04-19 01:18:53 +00:00
return s;
}
2017-04-22 17:33:20 +00:00
function getVisibleEditorLineHeight() {
return $(".CodeMirror-line:visible").first().height();
2017-04-19 01:18:53 +00:00
}
function getDumpLineAt(line) {
var d = dumplines[line];
if (d) {
return d.a + " " + d.s;
}
}
var IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true};
function getDumpLines() {
if (!dumplines && addr2symbol) {
dumplines = [];
var ofs = 0;
var sym;
for (var nextofs in addr2symbol) {
nextofs |= 0;
var nextsym = addr2symbol[nextofs];
if (sym) {
if (IGNORE_SYMS[sym]) {
ofs = nextofs;
} else {
while (ofs < nextofs) {
var ofs2 = (ofs + 16) & 0xffff0;
if (ofs2 > nextofs) ofs2 = nextofs;
//if (ofs < 1000) console.log(ofs, ofs2, nextofs, sym);
dumplines.push({a:ofs, l:ofs2-ofs, s:sym});
ofs = ofs2;
}
}
}
sym = nextsym;
}
}
return dumplines;
}
function getMemorySegment(a) {
if (!compparams) return 'unknown';
if (a >= compparams.data_start && a < compparams.data_start+compparams.data_size) {
if (platform.getSP && a >= platform.getSP() - 15)
return 'stack';
else
return 'data';
}
else if (a >= compparams.code_start && a < compparams.code_start+compparams.code_size)
return 'code';
else
return 'unknown';
}
function findMemoryWindowLine(a) {
for (var i=0; i<dumplines.length; i++)
if (dumplines[i].a >= a)
return i;
}
2017-04-19 01:18:53 +00:00
function showMemoryWindow() {
2017-04-26 01:14:17 +00:00
memorylist = new VirtualList({
2017-04-19 01:18:53 +00:00
w:$("#emulator").width(),
h:$("#emulator").height(),
2017-04-22 17:33:20 +00:00
itemHeight: getVisibleEditorLineHeight(),
2017-04-19 01:18:53 +00:00
totalRows: 0x1000,
generatorFn: function(row) {
var s = getMemoryLineAt(row);
2017-04-19 01:18:53 +00:00
var div = document.createElement("div");
if (dumplines) {
var dlr = dumplines[row];
if (dlr) div.classList.add('seg_' + getMemorySegment(dumplines[row].a));
}
2017-04-19 01:18:53 +00:00
div.appendChild(document.createTextNode(s));
return div;
}
});
2017-04-26 01:14:17 +00:00
$("#memoryview").empty().append(memorylist.container);
2017-04-19 01:18:53 +00:00
updateMemoryWindow();
if (compparams && dumplines)
2017-04-26 01:14:17 +00:00
memorylist.scrollToItem(findMemoryWindowLine(compparams.data_start));
2017-04-19 01:18:53 +00:00
}
function toggleMemoryWindow() {
//if ($("#profileview").is(':visible')) toggleProfileWindow();
2017-04-19 01:18:53 +00:00
if ($("#memoryview").is(':visible')) {
2017-04-26 01:14:17 +00:00
memorylist = null;
2017-04-19 01:18:53 +00:00
$("#emulator").show();
$("#memoryview").hide();
} else {
showMemoryWindow();
$("#emulator").hide();
$("#memoryview").show();
}
}
2017-05-04 15:54:56 +00:00
function handleWindowMessage(e) {
2017-11-21 21:36:38 +00:00
//console.log("window message", e.data);
2017-05-04 15:54:56 +00:00
if (e.data.bytes) {
editor.replaceSelection(e.data.bytestr);
}
if (e.data.close) {
2017-05-10 01:46:45 +00:00
$("#pixeditback").hide();
2017-05-04 15:54:56 +00:00
}
}
function openBitmapEditorWithParams(fmt, bytestr, palfmt, palstr) {
2017-05-10 01:46:45 +00:00
$("#pixeditback").show();
2017-05-04 15:54:56 +00:00
pixeditframe.contentWindow.postMessage({fmt:fmt, bytestr:bytestr, palfmt:palfmt, palstr:palstr}, '*');
}
2017-05-04 23:25:41 +00:00
function lookBackwardsForJSONComment(line, req) {
var re = /[/;][*;]([{].+[}])[*;][/;]/;
2017-05-04 15:54:56 +00:00
while (--line >= 0) {
var s = editor.getLine(line);
var m = re.exec(s);
if (m) {
var jsontxt = m[1].replace(/([A-Za-z]+):/g, '"$1":'); // fix lenient JSON
var obj = JSON.parse(jsontxt);
2017-05-04 23:25:41 +00:00
if (obj[req]) {
var start = {obj:obj, line:line, ch:s.indexOf(m[0])+m[0].length};
var line0 = line;
var pos0 = start.ch;
2017-05-04 23:25:41 +00:00
line--;
while (++line < editor.lineCount()) {
var l = editor.getLine(line);
var endsection;
if (platform_id == 'verilog')
endsection = l.indexOf('end') >= pos0;
else
endsection = l.indexOf(';') >= pos0;
if (endsection) {
2017-05-04 23:25:41 +00:00
var end = {line:line, ch:editor.getLine(line).length};
return {obj:obj, start:start, end:end};
}
pos0 = 0;
2017-05-04 15:54:56 +00:00
}
2017-05-04 23:25:41 +00:00
line = line0;
2017-05-04 15:54:56 +00:00
}
}
}
}
function openBitmapEditorAtCursor() {
if ($("#pixeditback").is(":visible")) {
$("#pixeditback").hide(250);
return;
}
var data = lookBackwardsForJSONComment(getCurrentLine(), 'w');
if (data && data.obj && data.obj.w>0 && data.obj.h>0) {
2017-05-04 23:25:41 +00:00
var paldata = lookBackwardsForJSONComment(data.start.line-1, 'pal');
2017-05-04 15:54:56 +00:00
var palbytestr;
if (paldata) {
palbytestr = editor.getRange(paldata.start, paldata.end);
paldata = paldata.obj;
}
editor.setSelection(data.end, data.start);
openBitmapEditorWithParams(data.obj, editor.getSelection(), paldata, palbytestr);
} else {
alert("To edit graphics, move cursor to a constant array preceded by a comment in the format:\n\n/*{w:,h:,bpp:,count:...}*/\n\n(See code examples)");
2017-05-04 15:54:56 +00:00
}
}
2017-05-20 19:13:23 +00:00
function _recordVideo() {
var canvas = $("#emulator").find("canvas")[0];
if (!canvas) {
alert("Could not find canvas element to record video!");
return;
}
var rotate = 0;
if (canvas.style && canvas.style.transform) {
if (canvas.style.transform.indexOf("rotate(-90deg)") >= 0)
rotate = -1;
else if (canvas.style.transform.indexOf("rotate(90deg)") >= 0)
rotate = 1;
}
var gif = new GIF({
workerScript: 'gif.js/dist/gif.worker.js',
workers: 4,
quality: 10,
rotate: rotate
});
2017-05-20 19:13:23 +00:00
var img = $('#videoPreviewImage');
//img.attr('src', 'https://articulate-heroes.s3.amazonaws.com/uploads/rte/kgrtehja_DancingBannana.gif');
gif.on('finished', function(blob) {
img.attr('src', URL.createObjectURL(blob));
$("#pleaseWaitModal").modal('hide');
2017-11-16 17:08:06 +00:00
_resume();
2017-05-20 19:13:23 +00:00
$("#videoPreviewModal").modal('show');
});
var intervalMsec = 17;
var maxFrames = 500;
var nframes = 0;
console.log("Recording video", canvas);
var f = function() {
if (nframes++ > maxFrames) {
console.log("Rendering video");
$("#pleaseWaitModal").modal('show');
2017-11-16 17:08:06 +00:00
_pause();
2017-05-20 19:13:23 +00:00
gif.render();
} else {
gif.addFrame(canvas, {delay: intervalMsec, copy: true});
2017-05-20 19:13:23 +00:00
setTimeout(f, intervalMsec);
}
};
f();
}
2018-02-26 23:18:23 +00:00
function setFrameRateUI(fps) {
platform.setFrameRate(fps);
if (fps > 0.01)
$("#fps_label").text(fps.toFixed(2));
else
$("#fps_label").text("1/"+Math.round(1/fps));
}
function _slowerFrameRate() {
var fps = platform.getFrameRate();
fps = fps/2;
if (fps > 0.00001) setFrameRateUI(fps);
}
function _fasterFrameRate() {
var fps = platform.getFrameRate();
fps = Math.min(60, fps*2);
setFrameRateUI(fps);
}
function _slowestFrameRate() {
setFrameRateUI(60/65536);
}
function _fastestFrameRate() {
setFrameRateUI(60);
}
2016-12-16 01:21:51 +00:00
function setupDebugControls(){
$("#dbg_reset").click(resetAndDebug);
$("#dbg_pause").click(pause);
$("#dbg_go").click(resume);
2017-11-24 19:14:22 +00:00
if (platform.step)
2017-11-11 19:45:32 +00:00
$("#dbg_step").click(singleStep).show();
2017-11-24 19:14:22 +00:00
else
2017-11-11 19:45:32 +00:00
$("#dbg_step").hide();
2017-11-24 19:14:22 +00:00
if (platform.runToVsync)
$("#dbg_tovsync").click(singleFrameStep).show();
else
$("#dbg_tovsync").hide();
if ((platform.runEval || platform.runToPC) && platform_id != 'verilog')
2017-11-24 19:14:22 +00:00
$("#dbg_toline").click(runToCursor).show();
else
2017-11-11 19:45:32 +00:00
$("#dbg_toline").hide();
2017-11-24 19:14:22 +00:00
if (platform.runUntilReturn)
$("#dbg_stepout").click(runUntilReturn).show();
else
2017-11-11 19:45:32 +00:00
$("#dbg_stepout").hide();
2017-11-24 19:14:22 +00:00
if (platform.stepBack)
$("#dbg_stepback").click(runStepBackwards).show();
else
2017-11-11 19:45:32 +00:00
$("#dbg_stepback").hide();
2017-11-24 19:14:22 +00:00
2017-11-20 01:32:58 +00:00
if (window.traceTiming) {
$("#dbg_timing").click(traceTiming).show();
2017-11-24 19:14:22 +00:00
} else if (platform.readAddress) {
2017-04-19 01:18:53 +00:00
$("#dbg_memory").click(toggleMemoryWindow).show();
}
/*if (platform.getProbe) {
2017-04-19 01:18:53 +00:00
$("#dbg_profile").click(toggleProfileWindow).show();
}*/
if (platform.saveState) { // TODO: only show if listing or disasm available
$("#dbg_disasm").click(toggleDisassembly).show();
}
2017-01-06 14:49:07 +00:00
$("#disassembly").hide();
2017-05-04 15:54:56 +00:00
$("#dbg_bitmap").click(openBitmapEditorAtCursor);
2016-12-30 23:51:15 +00:00
$(".dropdown-menu").collapse({toggle: false});
$("#item_new_file").click(_createNewFile);
2018-06-26 23:57:03 +00:00
$("#item_upload_file").click(_uploadNewFile);
2016-12-30 23:51:15 +00:00
$("#item_share_file").click(_shareFile);
$("#item_reset_file").click(_resetPreset);
2017-11-24 19:14:22 +00:00
if (platform.runEval)
$("#item_debug_expr").click(_breakExpression).show();
else
$("#item_debug_expr").hide();
2017-02-02 19:11:52 +00:00
$("#item_download_rom").click(_downloadROMImage);
$("#item_download_file").click(_downloadSourceFile);
2017-05-20 19:13:23 +00:00
$("#item_record_video").click(_recordVideo);
2018-02-26 23:18:23 +00:00
if (platform.setFrameRate && platform.getFrameRate) {
$("#speed_bar").show();
$("#dbg_slower").click(_slowerFrameRate);
$("#dbg_faster").click(_fasterFrameRate);
$("#dbg_slowest").click(_slowestFrameRate);
$("#dbg_fastest").click(_fastestFrameRate);
2018-02-26 23:18:23 +00:00
}
2017-04-19 01:18:53 +00:00
updateDebugWindows();
2016-12-16 01:21:51 +00:00
}
2016-12-18 20:59:31 +00:00
function showWelcomeMessage() {
2017-05-01 11:37:48 +00:00
if (!localStorage.getItem("8bitworkshop.hello")) {
2016-12-30 23:51:15 +00:00
// Instance the tour
2017-04-20 00:55:13 +00:00
var is_vcs = platform_id == 'vcs';
2016-12-30 23:51:15 +00:00
var tour = new Tour({
autoscroll:false,
2016-12-30 23:51:15 +00:00
//storage:false,
steps: [
{
element: "#editor",
title: "Welcome to 8bitworkshop!",
2017-04-20 00:55:13 +00:00
content: is_vcs ? "Type your 6502 assembly code into the editor, and it'll be assembled in real-time. All changes are saved to browser local storage."
: "Type your C source code into the editor, and it'll be compiled in real-time. All changes are saved to browser local storage."
2016-12-30 23:51:15 +00:00
},
{
element: "#emulator",
placement: 'left',
2017-04-20 00:55:13 +00:00
title: "Emulator",
content: "This is an emulator for the \"" + platform_id + "\" platform. We'll load your compiled code into the emulator whenever you make changes."
2016-12-30 23:51:15 +00:00
},
{
element: "#preset_select",
title: "File Selector",
content: "Pick a code example from the book, or access your own files and files shared by others."
},
{
element: "#debug_bar",
placement: 'bottom',
title: "Debug Tools",
2017-04-20 00:55:13 +00:00
content: "Use these buttons to set breakpoints, single step through code, pause/resume, and use debugging tools."
2016-12-30 23:51:15 +00:00
},
{
element: "#dropdownMenuButton",
title: "Main Menu",
2017-04-20 00:55:13 +00:00
content: "Click the menu to switch between platforms, create new files, or share your work with others."
2016-12-30 23:51:15 +00:00
},
]});
tour.init();
setTimeout(function() { tour.start(); }, 2000);
2016-12-18 20:59:31 +00:00
}
}
2016-12-16 01:21:51 +00:00
///////////////////////////////////////////////////
var qs = (function (a) {
if (!a || a == "")
return {};
var b = {};
for (var i = 0; i < a.length; ++i) {
var p = a[i].split('=', 2);
if (p.length == 1)
b[p[0]] = "";
else
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
return b;
})(window.location.search.substr(1).split('&'));
2017-01-25 17:30:05 +00:00
function initPlatform() {
store = createNewPersistentStore(platform_id);
2017-01-25 17:30:05 +00:00
}
2017-04-26 19:44:55 +00:00
function showBookLink() {
if (platform_id == 'vcs')
$("#booklink_vcs").show();
else if (platform_id == 'mw8080bw' || platform_id == 'vicdual' || platform_id == 'galaxian-scramble' || platform_id == 'vector-z80color' || platform_id == 'williams-z80')
2017-04-26 19:44:55 +00:00
$("#booklink_arcade").show();
}
function addPageFocusHandlers() {
2017-05-02 13:09:53 +00:00
var hidden = false;
document.addEventListener("visibilitychange", function() {
2017-05-02 13:09:53 +00:00
if (document.visibilityState == 'hidden' && platform.isRunning()) {
2017-11-16 17:08:06 +00:00
_pause();
2017-05-02 13:09:53 +00:00
hidden = true;
} else if (document.visibilityState == 'visible' && hidden) {
2017-11-16 17:08:06 +00:00
_resume();
2017-05-02 13:09:53 +00:00
hidden = false;
}
});
$(window).on("focus", function() {
if (hidden) {
2017-11-16 17:08:06 +00:00
_resume();
2017-05-02 13:09:53 +00:00
hidden = false;
}
});
$(window).on("blur", function() {
if (platform.isRunning()) {
2017-11-16 17:08:06 +00:00
_pause();
2017-05-02 13:09:53 +00:00
hidden = true;
}
});
}
2017-01-14 16:14:25 +00:00
function startPlatform() {
2017-01-25 17:30:05 +00:00
initPlatform();
2017-01-29 21:06:05 +00:00
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
2017-01-14 16:14:25 +00:00
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
PRESETS = platform.getPresets();
if (qs['file']) {
// start platform and load file
platform.start();
setupDebugControls();
2017-01-14 16:14:25 +00:00
loadPreset(qs['file']);
updateSelector();
2017-04-26 19:44:55 +00:00
showBookLink();
addPageFocusHandlers();
return true;
2017-01-14 16:14:25 +00:00
} else {
// try to load last file (redirect)
2017-01-14 16:14:25 +00:00
var lastid = localStorage.getItem("__lastid_"+platform_id) || localStorage.getItem("__lastid");
localStorage.removeItem("__lastid");
2018-06-28 04:57:06 +00:00
reloadPresetNamed(lastid || PRESETS[0].id);
return false;
2017-01-14 16:14:25 +00:00
}
}
2017-01-25 17:30:05 +00:00
function loadSharedFile(sharekey) {
var github = new Octokat();
var gist = github.gists(sharekey);
gist.fetch().done(function(val) {
var filename;
for (filename in val.files) { break; }
var newid = 'shared/' + filename;
var json = JSON.parse(val.description.slice(val.description.indexOf(' ')+1));
console.log("Fetched " + newid, json);
platform_id = json['platform'];
initPlatform();
current_project.updateFile(newid, val.files[filename].content);
2017-01-25 17:30:05 +00:00
qs['file'] = newid;
qs['platform'] = platform_id;
delete qs['sharekey'];
gotoNewLocation();
}).fail(function(err) {
alert("Error loading share file: " + err.message);
});
return true;
}
2017-01-03 01:42:15 +00:00
// start
2017-01-14 16:14:25 +00:00
function startUI(loadplatform) {
installErrorHandler();
2017-05-04 15:54:56 +00:00
window.addEventListener("message", handleWindowMessage, false);
2017-01-25 17:30:05 +00:00
// add default platform?
platform_id = qs['platform'] || localStorage.getItem("__lastplatform");
if (!platform_id) {
platform_id = qs['platform'] = "vcs";
}
2017-01-14 16:14:25 +00:00
// parse query string
// is this a share URL?
if (qs['sharekey']) {
2017-01-25 17:30:05 +00:00
loadSharedFile(qs['sharekey']);
} else {
2017-01-14 16:14:25 +00:00
// reset file?
if (qs['file'] && qs['reset']) {
2017-01-27 02:59:34 +00:00
initPlatform();
2018-06-26 06:56:36 +00:00
store.removeItem(qs['file']);
2017-01-14 16:14:25 +00:00
qs['reset'] = '';
gotoNewLocation();
2017-01-14 16:14:25 +00:00
} else {
// load and start platform object
if (loadplatform) {
2017-01-29 21:06:05 +00:00
var scriptfn = 'src/platform/' + platform_id.split('-')[0] + '.js';
var script = document.createElement('script');
script.onload = function() {
2017-01-14 16:14:25 +00:00
console.log("loaded platform", platform_id);
startPlatform();
showWelcomeMessage();
};
script.src = scriptfn;
document.getElementsByTagName('head')[0].appendChild(script);
} else {
2017-01-14 16:14:25 +00:00
startPlatform();
showWelcomeMessage();
}
2017-01-14 16:14:25 +00:00
}
2016-12-16 01:21:51 +00:00
}
}