Changed the last embedPC() parameter from sStateFile to sParms, although for backward compatibility, if sParms is not a JSON object definition (ie, beginning with a brace), it continues to be treated as a state filename.

This change allows us to pass any number of additional properties to embedPC(), which in turn can override properties contained in the XML file; the only property overrides currently supported are 'state' (which overrides any 'state' property set in the XML's <computer> element) and 'automount' (which overrides any 'automount' property set in the XML's <fdc> element).  The goal is to make it easier to reuse XML machine definitions, by allowing commonly altered component attributes to be overridden.
This commit is contained in:
Jeff Parsons 2016-01-18 16:11:08 -08:00
commit 12b760f912
28 changed files with 2187 additions and 2059 deletions

View file

@ -157,7 +157,7 @@
</xsl:if>
<div class="{$APPCLASS}-container" style="{$border}{$style}">
<xsl:if test="$class != '' and $component != 'machine'">
<div class="{$APPCLASS}-{$class}-object" data-value="id:'{$id}',name:'{$name}'{$parms}"></div>
<div class="{$APPCLASS}-{$class}-object" data-value="{{id:'{$id}',name:'{$name}'{$parms}}}"></div>
</xsl:if>
<xsl:if test="control">
<div class="{$APPCLASS}-controls">
@ -270,27 +270,27 @@
</xsl:if>
<xsl:choose>
<xsl:when test="@type = 'button'">
<button class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{$type},{$binding}"><xsl:apply-templates/></button>
<button class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}"><xsl:apply-templates/></button>
</xsl:when>
<xsl:when test="@type = 'list'">
<select class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{$type},{$binding}">
<select class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}">
<xsl:apply-templates select="item" mode="component"/>
</select>
</xsl:when>
<xsl:when test="@type = 'text'">
<input class="{$APPCLASS}-binding" type="text" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}" value="" autocapitalize="off" autocorrect="off"/>
<input class="{$APPCLASS}-binding" type="text" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}" value="" autocapitalize="off" autocorrect="off"/>
</xsl:when>
<xsl:when test="@type = 'submit'">
<input class="{$APPCLASS}-binding" type="submit" style="{$border}{$fontsize}{$style}" data-value="{$type},{$binding}" value="{.}"/>
<input class="{$APPCLASS}-binding" type="submit" style="{$border}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}" value="{.}"/>
</xsl:when>
<xsl:when test="@type = 'textarea'">
<textarea class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}" readonly="readonly"></textarea>
<textarea class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}" readonly="readonly"></textarea>
</xsl:when>
<xsl:when test="@type = 'heading'">
<div><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'file'">
<form class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}">
<form class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}">
<fieldset class="{$APPCLASS}-fieldset">
<input type="file"/>
<input type="submit" value="Mount" disabled="true"/>
@ -304,7 +304,7 @@
<div style="clear:both"></div><br/>
</xsl:when>
<xsl:otherwise>
<div class="{$APPCLASS}-binding{$subclass}" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}"><xsl:apply-templates/></div>
<div class="{$APPCLASS}-binding{$subclass}" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}"><xsl:apply-templates/></div>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="@label">

View file

@ -82,7 +82,7 @@ if (NODE) {
* @param {Object} [parmsMachine]
* @param {boolean} [fSuspended]
*
* The Computer component has no required (parmsComputer) properties, but does
* The Computer component has no required (parmsComputer) properties, but it does
* support the following:
*
* autoPower: true to automatically power the computer (default), false to wait;
@ -102,6 +102,16 @@ if (NODE) {
*
* state: the path to JSON-encoded state file (see details regarding 'state' below)
*
* The parmsMachine object, if provided, may contain any of:
*
* url: the location of the machine XML file
*
* autoMount: if set, this should override any 'autoMount' property in the FDC's
* parmsFDC object.
*
* state: if set, this should override any 'state' property in the Computer's
* parmsComputer object.
*
* If a predefined state is supplied AND it's successfully loaded, then resume behavior
* defaults to '1' (ie, resume enabled without prompting).
*
@ -141,7 +151,9 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
this.sStateData = null;
this.fStateData = false; // remembers if sStateData was loaded
this.fServerState = false;
this.url = parmsMachine? parmsMachine['url'] : null;
this.parmsMachine = parmsMachine;
this.url = this.getMachineParm('url') || "";
/*
* Generate a random number x (where 0 <= x < 1), add 0.1 so that it's guaranteed to be
@ -230,7 +242,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
* localStorage (in other words, it prevents fAllowResume from being true, and forcing resume off).
*/
var fAllowResume;
var sState = Component.parmsURL && Component.parmsURL['state'] || (fAllowResume = true) && parmsComputer['state'];
var sState = Component.parmsURL && Component.parmsURL['state'] || this.getMachineParm('state') || (fAllowResume = true) && parmsComputer['state'];
if (sState) {
sStatePath = this.sStatePath = sState;
@ -318,6 +330,17 @@ Computer.prototype.getMachineID = function()
return this.sMachineID;
};
/**
* getMachineParm(sParm)
*
* @param {string} sParm
* @return {string|undefined}
*/
Computer.prototype.getMachineParm = function(sParm)
{
return this.parmsMachine && this.parmsMachine[sParm];
};
/**
* getUserID()
*

View file

@ -142,22 +142,11 @@ function FDC(parmsFDC) {
this['dmaWrite'] = this.dmaWrite;
this['dmaFormat'] = this.dmaFormat;
this.configMount = null;
if (parmsFDC['autoMount']) {
this.configMount = parmsFDC['autoMount'];
if (typeof this.configMount == "string") {
try {
/*
* The most likely source of any exception will be right here, where we're parsing
* the JSON-encoded diskette data.
*/
this.configMount = eval("(" + parmsFDC['autoMount'] + ")");
} catch (e) {
Component.error("FDC auto-mount error: " + e.message + " (" + parmsFDC['autoMount'] + ")");
this.configMount = null;
}
}
}
/*
* We record any 'autoMount' object now, but we no longer parse it until initBus(), because the Computer's
* getMachineParm() service may have an override for us.
*/
this.configMount = parmsFDC['autoMount'] || null;
/*
* The following array keeps track of every disk image we've ever mounted. Each entry in the
@ -533,6 +522,23 @@ FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
this.chipset = cmp.getMachineComponent("ChipSet");
this.configMount = this.cmp.getMachineParm('autoMount') || this.configMount;
if (this.configMount) {
if (typeof this.configMount == "string") {
try {
/*
* The most likely source of any exception will be right here, where we're parsing
* the JSON-encoded diskette data.
*/
this.configMount = eval("(" + this.configMount + ")");
} catch (e) {
Component.error("FDC auto-mount error: " + e.message + " (" + this.configMount + ")");
this.configMount = null;
}
}
}
/*
* If we didn't need auto-mount support, we could defer controller initialization until we received a powerUp() notification,
* at which point reset() would call initController(), or restore() would restore the controller; in that case, all we'd need

View file

@ -69,7 +69,12 @@
</xsl:with-param>
<xsl:with-param name="component" select="'machine'"/>
<xsl:with-param name="class"><xsl:value-of select="@class"/>js</xsl:with-param>
<xsl:with-param name="parms"><xsl:if test="@parms">,<xsl:value-of select="@parms"/></xsl:if></xsl:with-param>
<xsl:with-param name="parms">
<xsl:choose>
<xsl:when test="$machineState != ''">state:"<xsl:value-of select="$machineState"/>"</xsl:when>
<xsl:otherwise><xsl:value-of select="@parms"/></xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="url"><xsl:value-of select="@url"/></xsl:with-param>
</xsl:call-template>
</div>
@ -108,9 +113,9 @@
<xsl:otherwise><xsl:value-of select="$component"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="componentURL">
<xsl:variable name="machineParms">
<xsl:choose>
<xsl:when test="$component = 'machine'">url:'<xsl:value-of select="$url"/>'</xsl:when>
<xsl:when test="$component = 'machine'"><xsl:value-of select="$parms"/></xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
@ -192,7 +197,7 @@
<xsl:variable name="componentClass">
<xsl:value-of select="$APPCLASS"/><xsl:text>-</xsl:text><xsl:value-of select="$component"/><xsl:text> </xsl:text><xsl:value-of select="$APPCLASS"/><xsl:text>-component</xsl:text>
</xsl:variable>
<div id="{$id}" class="{$componentClass}" style="{$width}{$height}{$pos}{$left}{$top}{$padding}" data-value="{$componentURL}">
<div id="{$id}" class="{$componentClass}" style="{$width}{$height}{$pos}{$left}{$top}{$padding}" data-value="{$machineParms}">
<xsl:if test="$component = 'machine'">
<xsl:apply-templates select="name" mode="machine"/>
</xsl:if>
@ -207,7 +212,7 @@
<xsl:apply-templates select="menu" mode="component"/>
</xsl:if>
<xsl:if test="$class != '' and $component != 'machine'">
<div class="{$APPCLASS}-{$class}-object" data-value="id:'{$id}',name:'{$name}'{$comment}{$parms}"> </div>
<div class="{$APPCLASS}-{$class}-object" data-value="{{id:'{$id}',name:'{$name}'{$comment}{$parms}}}"> </div>
</xsl:if>
<xsl:if test="control">
<div class="{$APPCLASS}-controls">
@ -355,30 +360,30 @@
</xsl:if>
<xsl:choose>
<xsl:when test="@type = 'canvas'">
<canvas class="{$APPCLASS}-binding {$APPCLASS}-canvas" width="{@width}" height="{@height}" style="-webkit-user-select:none;{$border}{$fontsize}{$style}" data-value="{$type},{$binding}"><xsl:apply-templates/></canvas>
<canvas class="{$APPCLASS}-binding {$APPCLASS}-canvas" width="{@width}" height="{@height}" style="-webkit-user-select:none;{$border}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}"><xsl:apply-templates/></canvas>
</xsl:when>
<xsl:when test="@type = 'button'">
<button class="{$APPCLASS}-binding" style="-webkit-user-select:none;{$border}{$width}{$height}{$fontsize}{$style}" data-value="{$type},{$binding}"><xsl:apply-templates/></button>
<button class="{$APPCLASS}-binding" style="-webkit-user-select:none;{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}"><xsl:apply-templates/></button>
</xsl:when>
<xsl:when test="@type = 'list'">
<select class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{$type},{$binding}">
<select class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}">
<xsl:apply-templates select="disk|app|manifest" mode="component"/>
</select>
</xsl:when>
<xsl:when test="@type = 'text'">
<input class="{$APPCLASS}-binding" type="text" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}" value="{.}" autocapitalize="off" autocorrect="off"/>
<input class="{$APPCLASS}-binding" type="text" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}" value="{.}" autocapitalize="off" autocorrect="off"/>
</xsl:when>
<xsl:when test="@type = 'submit'">
<input class="{$APPCLASS}-binding" type="submit" style="{$border}{$fontsize}{$style}" data-value="{$type},{$binding}" value="{.}"/>
<input class="{$APPCLASS}-binding" type="submit" style="{$border}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}" value="{.}"/>
</xsl:when>
<xsl:when test="@type = 'textarea'">
<textarea class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}" readonly="readonly"> </textarea>
<textarea class="{$APPCLASS}-binding" style="{$border}{$width}{$height}{$style}" data-value="{{{$type},{$binding}}}" readonly="readonly"> </textarea>
</xsl:when>
<xsl:when test="@type = 'heading'">
<div><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'file'">
<form class="{$APPCLASS}-binding" data-value="{$type},{$binding}">
<form class="{$APPCLASS}-binding" data-value="{{{$type},{$binding}}}">
<fieldset class="{$APPCLASS}-fieldset">
<input type="file"/>
<input type="submit" value="Mount" disabled="true"/>
@ -386,7 +391,7 @@
</form>
</xsl:when>
<xsl:when test="@type = 'led'">
<div class="{$APPCLASS}-binding {$APPCLASS}-{@type}" data-value="{$type},{$binding}"><xsl:value-of select="."/></div>
<div class="{$APPCLASS}-binding {$APPCLASS}-{@type}" data-value="{{{$type},{$binding}}}"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'separator'">
<hr/>
@ -398,7 +403,7 @@
<div style="clear:both"> </div>
</xsl:when>
<xsl:otherwise>
<div class="{$APPCLASS}-binding{$subClass} {$APPCLASS}-{@type}" style="-webkit-user-select:none;{$border}{$width}{$height}{$fontsize}{$style}" data-value="{$type},{$binding}"><xsl:apply-templates/></div>
<div class="{$APPCLASS}-binding{$subClass} {$APPCLASS}-{@type}" style="-webkit-user-select:none;{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding}}}"><xsl:apply-templates/></div>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="@label">
@ -424,7 +429,7 @@
</xsl:if>
</xsl:if>
</xsl:variable>
<option value="{@path}" data-value="{$desc}"><xsl:if test="name"><xsl:value-of select="name"/></xsl:if><xsl:if test="not(name)"><xsl:value-of select="."/></xsl:if></option>
<option value="{@path}" data-value="{{{$desc}}}"><xsl:if test="name"><xsl:value-of select="name"/></xsl:if><xsl:if test="not(name)"><xsl:value-of select="."/></xsl:if></option>
</xsl:template>
<xsl:template match="app[@ref]" mode="component">
@ -447,7 +452,7 @@
<xsl:variable name="files">
<xsl:for-each select="file"><xsl:if test="position() = 1"><xsl:value-of select="$path"/></xsl:if><xsl:value-of select="@name"/><xsl:if test="position() != last()">;</xsl:if></xsl:for-each>
</xsl:variable>
<option value="{$files}" data-value="{$desc}"><xsl:value-of select="@name"/></option>
<option value="{$files}" data-value="{{{$desc}}}"><xsl:value-of select="@name"/></option>
</xsl:template>
<xsl:template match="manifest[@ref]" mode="component">
@ -485,7 +490,7 @@
</xsl:variable>
<!-- TODO: Think about incorporating the optional "desc" tag into the disk description (see /disks/pc/tools/microsoft/c/4.00/manifest.xml) -->
<xsl:if test="@href">
<option value="{@href}" data-value="{$link}"><xsl:value-of select="$name"/></option>
<option value="{@href}" data-value="{{{$link}}}"><xsl:value-of select="$name"/></option>
</xsl:if>
<xsl:if test="not(@href)">
<xsl:variable name="dir">
@ -494,7 +499,7 @@
<xsl:variable name="files">
<xsl:for-each select="file"><xsl:if test="position() = 1"><xsl:value-of select="$dir"/></xsl:if><xsl:value-of select="@dir"/><xsl:value-of select="."/><xsl:if test="position() != last()">;</xsl:if></xsl:for-each>
</xsl:variable>
<option value="{$files}" data-value="{$link}"><xsl:value-of select="$name"/></option>
<option value="{$files}" data-value="{{{$link}}}"><xsl:value-of select="$name"/></option>
</xsl:if>
</xsl:if>
</xsl:for-each>

View file

@ -456,7 +456,7 @@ Component.getComponentParms = function(element)
sParms = element.getAttribute("data-value");
if (sParms) {
try {
parms = eval("({" + sParms + "})"); // jshint ignore:line
parms = eval("(" + sParms + ")"); // jshint ignore:line
/*
* We can no longer invoke removeAttribute() because some components (eg, Panel) need
* to run their initXXX() code more than once, to avoid initialization-order dependencies.

View file

@ -56,7 +56,7 @@ var cMachines = 0;
var fAsync = true;
/**
* loadXML(sFile, idMachine, sStateFile, fResolve, display, done)
* loadXML(sFile, idMachine, sParms, fResolve, display, done)
*
* This is the preferred way to load all XML and XSL files. It uses loadResource()
* to load them as strings, which parseXML() can massage before parsing/transforming them.
@ -81,12 +81,12 @@ var fAsync = true;
*
* @param {string} sXMLFile
* @param {string|null|undefined} idMachine
* @param {string|null|undefined} sStateFile
* @param {string|null|undefined} sParms
* @param {boolean} fResolve is true to resolve any "ref" attributes
* @param {function(string)} display
* @param {function(string,Object)} done (string contains the unparsed XML string data, and Object contains a parsed XML object)
*/
function loadXML(sXMLFile, idMachine, sStateFile, fResolve, display, done)
function loadXML(sXMLFile, idMachine, sParms, fResolve, display, done)
{
var doneLoadXML = function(sURLName, sXML, nErrorCode) {
if (nErrorCode) {
@ -94,14 +94,14 @@ function loadXML(sXMLFile, idMachine, sStateFile, fResolve, display, done)
done(sXML, null);
return;
}
parseXML(sXML, sXMLFile, idMachine, sStateFile, fResolve, display, done);
parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done);
};
display("Loading " + sXMLFile + "...");
web.loadResource(sXMLFile, fAsync, null, null, doneLoadXML);
}
/**
* parseXML(sXML, sXMLFile, idMachine, sStateFile, fResolve, display, done)
* parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done)
*
* Generates an XML document from an XML string. This function also provides a work-around for XSLT's
* lack of support for the document() function (at least on some browsers), by replacing every reference
@ -110,12 +110,12 @@ function loadXML(sXMLFile, idMachine, sStateFile, fResolve, display, done)
* @param {string} sXML
* @param {string|null} sXMLFile
* @param {string|null|undefined} idMachine
* @param {string|null|undefined} sStateFile
* @param {string|null|undefined} sParms
* @param {boolean} fResolve is true to resolve any "ref" attributes; default is false
* @param {function(string)} display
* @param {function(string,Object)} done (string contains the unparsed XML string data, and Object contains a parsed XML object)
*/
function parseXML(sXML, sXMLFile, idMachine, sStateFile, fResolve, display, done)
function parseXML(sXML, sXMLFile, idMachine, sParms, fResolve, display, done)
{
var buildXML = function(sXML, sError) {
if (sError) {
@ -125,7 +125,20 @@ function parseXML(sXML, sXMLFile, idMachine, sStateFile, fResolve, display, done
if (idMachine) {
var sURL = sXMLFile;
if (sURL && sURL.indexOf('/') < 0) sURL = window.location.pathname + sURL;
sXML = sXML.replace(/(<machine[^>]*\sid=)(['"]).*?\2/, "$1$2" + idMachine + "$2" + (sStateFile? " state=$2" + sStateFile + "$2" : "") + (sURL? " url=$2" + sURL + "$2" : ""));
/*
* We embed the URL to 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 JavaScript.
*/
if (!sParms) {
sParms = '{';
} else if (sParms.substr(0, 1) == '{') {
sParms = sParms.slice(0, -1);
if (sParms.length > 1) sParms += ',';
} else { // backward compatibility: sParms is actually sStateFile
sParms = '{state:"' + sParms + '",';
}
sParms += 'url:"' + sURL + '"}';
sXML = sXML.replace(/(<machine[^>]*\sid=)(['"]).*?\2/, "$1$2" + idMachine + "$2" + (sParms? " parms='" + sParms + "'" : "") + (sURL? ' url="' + sURL + '"' : ''));
}
/*
* Non-COMPILED kludge to replace the version number template in the XSL file (which we assume we're reading,
@ -281,7 +294,7 @@ function resolveXML(sXML, display, done)
}
/**
* embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sStateFile)
* embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sParms)
*
* This allows to you embed a machine on a web page, by transforming the machine XML into HTML.
*
@ -290,10 +303,10 @@ function resolveXML(sXML, display, done)
* @param {string} idElement
* @param {string} sXMLFile
* @param {string} sXSLFile
* @param {string} [sStateFile]
* @param {string} [sParms]
* @return {boolean} true if successful, false if error
*/
function embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sStateFile)
function embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sParms)
{
var eMachine, eWarning, fSuccess = true;
@ -439,9 +452,9 @@ function embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sStateFile
}
};
if (sXMLFile.charAt(0) != '<') {
loadXML(sXMLFile, idElement, sStateFile, true, displayMessage, loadXSL);
loadXML(sXMLFile, idElement, sParms, true, displayMessage, loadXSL);
} else {
parseXML(sXMLFile, null, idElement, sStateFile, false, displayMessage, loadXSL);
parseXML(sXMLFile, null, idElement, sParms, false, displayMessage, loadXSL);
}
} else {
displayError("missing machine element: " + idElement);
@ -467,18 +480,18 @@ function embedC1P(idElement, sXMLFile, sXSLFile)
}
/**
* embedPC(idElement, sXMLFile, sXSLFile, sStateFile)
* embedPC(idElement, sXMLFile, sXSLFile, sParms)
*
* @param {string} idElement
* @param {string} sXMLFile
* @param {string} sXSLFile
* @param {string} [sStateFile]
* @param {string} [sParms]
* @return {boolean} true if successful, false if error
*/
function embedPC(idElement, sXMLFile, sXSLFile, sStateFile)
function embedPC(idElement, sXMLFile, sXSLFile, sParms)
{
if (fAsync) web.enablePageEvents(false);
return embedMachine("PCjs", APPVERSION, idElement, sXMLFile, sXSLFile, sStateFile);
return embedMachine("PCjs", APPVERSION, idElement, sXMLFile, sXSLFile, sParms);
}
/**