Save machine finally works

This commit is contained in:
Jeff Parsons 2016-02-17 13:40:55 -08:00
commit 0e3de68273
11 changed files with 1573 additions and 1505 deletions

View file

@ -252,13 +252,13 @@ Component.addMachine = function(idMachine)
* Component.addMachineResource(idMachine, sName, data)
*
* @param {string} idMachine
* @param {string} sName (name of the resource)
* @param {string|null} sName (name of the resource)
* @param {*} data
*/
Component.addMachineResource = function(idMachine, sName, data)
{
Component.assert(Component.machines[idMachine]);
if (Component.machines[idMachine]) {
if (Component.machines[idMachine] && sName) {
Component.assert(Component.machines[idMachine][sName] === undefined);
Component.machines[idMachine][sName] = data;
}

View file

@ -123,8 +123,17 @@ function parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done)
return;
}
if (idMachine) {
/*
* A more sensible place to record the machine XML would be embedMachine(), like we do for the
* XSL file, but since we're about to modify the original machine XML, it's best to record it now.
*/
Component.addMachineResource(idMachine, sXMLFile, sXML);
var sURL = sXMLFile;
if (sURL && sURL.indexOf('/') < 0) sURL = window.location.pathname + sURL;
if (sURL && sURL.indexOf('/') < 0 && window.location.pathname.slice(-1) == '/') {
sURL = window.location.pathname + sURL;
}
/*
* We embed the URL of the XML file both as a separate "xml" attribute for easy access from the
* XSL file, and as part of the "parms" attribute for easy access from machines (see getMachineParm()).
@ -361,6 +370,23 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
try {
eMachine = document.getElementById(idMachine);
if (eMachine) {
/*
* If we have a 'css' resource, add it to the page first.
*/
var css;
if (typeof resources == "object" && (css = resources['css'])) {
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
}
var sAppClass = sName.toLowerCase(); // eg, "pcjs" or "c1pjs"
if (!sXSLFile) {
/*
@ -381,7 +407,6 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
displayError(sXML);
return;
}
Component.addMachineResource(idMachine, sXMLFile, sXML);
/*
* Non-COMPILED kludge to extract the version number from the stylesheet path in the machine XML file;
* we don't need this code in COMPILED (non-DEBUG) releases, because APPVERSION is hard-coded into them.
@ -395,6 +420,9 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
displayError(sXSL);
return;
}
/*
* Record the XSL file, in case someone wants to save the entire machine later.
*/
Component.addMachineResource(idMachine, sXSLFile, sXSL);
/*
* The <machine> template in components.xsl now generates a "machine div" that makes

View file

@ -55,13 +55,13 @@ function savePC(idMachine, sPCJSFile)
var sState = cmp.powerOff(true);
var sParms = cmp.saveMachineParms();
if (!sPCJSFile) {
if (MAXDEBUG) {
if (DEBUG) {
sPCJSFile = "/tmp/pcjs/" + (XMLVERSION || APPVERSION) + "/pc.js"
} else {
sPCJSFile = "/versions/pcjs/" + (XMLVERSION || APPVERSION) + "/pc" + (dbg? "-dbg" : "") + ".js";
}
}
web.loadResource(sPCJSFile, true, null, null, downloadPC, [idMachine, sParms, sState, cmp, dbg]);
web.loadResource(sPCJSFile, true, null, null, downloadCSS, [idMachine, str.getBaseName(sPCJSFile, true), sParms, sState]);
return true;
}
web.alertUser("Unable to identify machine '" + idMachine + "'");
@ -69,17 +69,50 @@ function savePC(idMachine, sPCJSFile)
}
/**
* downloadPC(sURL, sPCJS, nErrorCode, aMachineInfo)
* downloadCSS(sURL, sPCJS, nErrorCode, aMachineInfo)
*
* @param {string} sURL
* @param {string} sPCJS
* @param {number} nErrorCode
* @param {string} aMachineInfo ([0] = idMachine, [1] = sParms, [2] = sState, [3] = Computer component, [4] = Debugger component, if any)
* @param {Array} aMachineInfo ([0] = idMachine, [1] = sScript, [2] = sParms, [3] = sState)
*/
function downloadPC(sURL, sPCJS, nErrorCode, aMachineInfo)
function downloadCSS(sURL, sPCJS, nErrorCode, aMachineInfo)
{
var matchScript;
var idMachine = aMachineInfo[0], sXMLFile, sXSLFile;
if (!nErrorCode && sPCJS) {
aMachineInfo.push(sPCJS);
var res = Component.getMachineResources(aMachineInfo[0]);
var sCSSFile = null;
for (var sName in res) {
if (str.endsWith(sName, "components.xsl")) {
sCSSFile = sName.replace(".xsl", ".css");
break;
}
}
if (!sCSSFile) {
/*
* This is probably a bad idea (ie, allowing downloadPC() to proceed with our stylesheet)...
*/
downloadPC(sURL, null, 0, aMachineInfo);
} else {
web.loadResource(sCSSFile, true, null, null, downloadPC, aMachineInfo);
}
return;
}
web.alertUser("Error (" + nErrorCode + ") requesting " + sURL);
}
/**
* downloadPC(sURL, sCSS, nErrorCode, aMachineInfo)
*
* @param {string} sURL
* @param {string|null} sCSS
* @param {number} nErrorCode
* @param {Array} aMachineInfo ([0] = idMachine, [1] = sScript, [2] = sParms, [3] = sState, [4] = sPCJS)
*/
function downloadPC(sURL, sCSS, nErrorCode, aMachineInfo)
{
var matchScript, sXMLFile, sXSLFile;
var idMachine = aMachineInfo[0], sScript = aMachineInfo[1], sPCJS = aMachineInfo[4];
/*
* sPCJS is supposed to contain the entire PCjs script, which has been wrapped with:
@ -101,103 +134,101 @@ function downloadPC(sURL, sPCJS, nErrorCode, aMachineInfo)
* Note that the "resources" variable has been added to our externs.js, to prevent it from being renamed
* by the Closure Compiler.
*/
if (sPCJS) {
matchScript = sPCJS.match(/^(\s*\(function\(\)\{)([\s\S]*)(}\)\(\);\s*)$/);
if (!matchScript) {
/*
* If the match failed, we assume that a DEBUG (uncompiled) script is being used,
* so we'll provide a fake match that should work with whatever script was provided.
*/
if (DEBUG) {
matchScript = [sPCJS, "", sPCJS, ""];
} else {
sPCJS = "";
}
matchScript = sPCJS.match(/^(\s*\(function\(\)\{)([\s\S]*)(}\)\(\);\s*)$/);
if (!matchScript) {
/*
* If the match failed, we assume that a DEBUG (uncompiled) script is being used,
* so we'll provide a fake match that should work with whatever script was provided.
*/
if (DEBUG) {
matchScript = [sPCJS, "", sPCJS, ""];
} else {
sPCJS = "";
}
}
if (sPCJS) {
var resOld = Component.getMachineResources(idMachine), resNew = {};
for (var sName in resOld) {
var data = resOld[sName];
var sExt = str.getExtension(sName);
if (sExt == "xml") {
/*
* Look through this resource for <disk> entries whose paths do not appear as one of the
* other machine resources, and remove those entries.
*/
var matchDisk, reDisk = /[ \t]*<disk [^>]*path=(['"])(.*?)\1.*?<\/disk>\n?/g;
while (matchDisk = reDisk.exec(resOld[sName])) {
var path = matchDisk[2];
if (path) {
if (resOld[path]) {
Component.log("recording disk: '" + path + "'");
} else {
data = data.replace(matchDisk[0], "");
}
var resOld = Component.getMachineResources(idMachine), resNew = {};
for (var sName in resOld) {
var data = resOld[sName];
var sExt = str.getExtension(sName);
if (sExt == "xml") {
/*
* Look through this resource for <disk> entries whose paths do not appear as one of the
* other machine resources, and remove those entries.
*/
var matchDisk, reDisk = /[ \t]*<disk [^>]*path=(['"])(.*?)\1.*?<\/disk>\n?/g;
while (matchDisk = reDisk.exec(resOld[sName])) {
var path = matchDisk[2];
if (path) {
if (resOld[path]) {
Component.log("recording disk: '" + path + "'");
} else {
data = data.replace(matchDisk[0], "");
}
}
sXMLFile = sName = str.getBaseName(sName);
}
else if (sExt == "xsl") {
sXSLFile = sName = str.getBaseName(sName);
}
Component.log("saving resource: '" + sName + "' (" + data.length + " bytes)");
resNew[sName] = data;
sXMLFile = sName = str.getBaseName(sName);
}
if (aMachineInfo[1]) {
var sParms = resNew[sName = 'parms'] = aMachineInfo[1];
Component.log("saving resource: '" + sName + "' (" + sParms.length + " bytes)");
else if (sExt == "xsl") {
sXSLFile = sName = str.getBaseName(sName);
}
if (aMachineInfo[2]) {
var sState = resNew[sName = 'state'] = aMachineInfo[2];
Component.log("saving resource: '" + sName + "' (" + sState.length + " bytes)");
}
if (sXMLFile && sXSLFile) {
var sResources = JSON.stringify(resNew);
var sScriptFile = str.getBaseName(sURL, true);
sPCJS = matchScript[1] + "var resources=" + sResources + ";" + matchScript[2] + matchScript[3];
Component.log("saving machine: '" + idMachine + "' (" + sPCJS.length + " bytes)");
var uri;
if (MAXDEBUG) {
sPCJS = sPCJS.replace(/[\u00A0-\u2666]/g, function(c) {
return '&#' + c.charCodeAt(0) + ';';
});
uri = "data:application/javascript;base64," + btoa(sPCJS);
} else {
uri = "data:application/javascript," + (web.isUserAgent("Firefox")? encodeURIComponent(sPCJS) : encodeURI(sPCJS));
}
var link = document.createElement('a');
if (typeof link.download == 'string') {
link.href = uri;
link.download = sScriptFile + ".json";
document.body.appendChild(link); // Firefox requires the link to be in the body (?)
link.click();
document.body.removeChild(link);
} else {
window.open(uri);
}
var sAlert = 'Check your Downloads folder for "' + sScriptFile + '.json", ';
sAlert += 'copy it to your web server as "' + sScriptFile + '.js", and then add the following to your web page:\n\n';
sAlert += '<div id="' + idMachine + '"></div>\n';
sAlert += '...\n';
sAlert += '<script type="text/javascript" src="' + sScriptFile + '.js"></script>\n';
sAlert += '<script type="text/javascript">embedPC("' + idMachine + '","' + sXMLFile + '","' + sXSLFile + '");</script>\n\n';
sAlert += 'The machine should appear where the <div> is located.';
web.alertUser(sAlert);
} else {
web.alertUser("Missing XML/XSL resources");
}
} else {
web.alertUser("Unabled to download script");
Component.log("saving resource: '" + sName + "' (" + data.length + " bytes)");
resNew[sName] = data;
}
if (sCSS) {
resNew[sName = 'css'] = sCSS;
Component.log("saving resource: '" + sName + "' (" + sCSS.length + " bytes)");
}
if (aMachineInfo[2]) {
var sParms = resNew[sName = 'parms'] = aMachineInfo[2];
Component.log("saving resource: '" + sName + "' (" + sParms.length + " bytes)");
}
if (aMachineInfo[3]) {
var sState = resNew[sName = 'state'] = aMachineInfo[3];
Component.log("saving resource: '" + sName + "' (" + sState.length + " bytes)");
}
if (sXMLFile && sXSLFile) {
var sResources = JSON.stringify(resNew);
sPCJS = matchScript[1] + "var resources=" + sResources + ";" + matchScript[2] + matchScript[3];
Component.log("saving machine: '" + idMachine + "' (" + sPCJS.length + " bytes)");
var uri;
if (MAXDEBUG) {
sPCJS = sPCJS.replace(/[\u00A0-\u2666]/g, function(c) {
return '&#' + c.charCodeAt(0) + ';';
});
uri = "data:application/javascript;base64," + btoa(sPCJS);
} else {
uri = "data:application/javascript," + (web.isUserAgent("Firefox")? encodeURIComponent(sPCJS) : encodeURI(sPCJS));
}
var link = document.createElement('a');
if (typeof link.download == 'string' && sPCJS.length < 2000000) {
link.href = uri;
link.download = sScript + ".json";
document.body.appendChild(link); // Firefox requires the link to be in the body (?)
link.click();
document.body.removeChild(link);
} else {
window.open(uri);
}
var sAlert = 'Check your Downloads folder for "' + sScript + '.json", ';
sAlert += 'copy it to your web server as "' + sScript + '.js", and then add the following to your web page:\n\n';
sAlert += '<div id="' + idMachine + '"></div>\n';
sAlert += '...\n';
sAlert += '<script type="text/javascript" src="' + sScript + '.js"></script>\n';
sAlert += '<script type="text/javascript">embedPC("' + idMachine + '","' + sXMLFile + '","' + sXSLFile + '");</script>\n\n';
sAlert += 'The machine should appear where the <div> is located.';
web.alertUser(sAlert);
return;
}
web.alertUser("Missing XML/XSL resources");
}
/**