Added support for "clickOnce" script controls

This commit is contained in:
Jeff Parsons 2017-02-07 17:57:02 -08:00 committed by Jeff Parsons
commit d190bc0c9c
7 changed files with 136 additions and 29 deletions

View file

@ -1301,7 +1301,13 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
if (!aParms) continue;
sReplacement = "";
sMachineID = findParm(aParms, 'machine');
var sControl = findParm(aParms, 'type') || 'button';
var sSingle = "false,";
var sControl = findParm(aParms, 'type');
if (sControl == "clickOnce") {
sControl = "";
sSingle = "true,";
}
sControl = sControl || "button";
var sControlType = sControl == 'button'? ' type="button"' : '';
if (this.aMachineDefs[sMachineID]) {
var sCommand = findParm(aParms, 'command');
@ -1312,7 +1318,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
} else if (sValue) {
sValue = sValue.replace(/"/g, """);
}
sReplacement = '<' + sControl + sControlType + ' onclick="commandMachine(';
sReplacement = '<' + sControl + sControlType + ' onclick="commandMachine(this,' + sSingle;
sReplacement += "'" + sMachineID + "',";
sReplacement += "'" + findParm(aParms, 'component') + "',";
sReplacement += "'" + sCommand + "',";

View file

@ -162,7 +162,7 @@ class PanelPDP11 extends Component {
'TEST': [0, 0, true, false, this.processLEDTest]
};
for (var i = 0; i < 22; i++) {
this.switches['S'+i] = [0, 0, false, false, this.processSwitchReg, i];
this.switches['S'+i] = [0, 0, false, false, this.processSRSwitch, i];
}
/** @type {ComputerPDP11} */
@ -179,14 +179,23 @@ class PanelPDP11 extends Component {
/*
* The 'hold' and 'toggle' exports, which map to holdSwitch() and toggleSwitch(), both press and release
* the specified switch, but processCommands() considers a 'hold' function to be asynchronous, which means it
* will be passed a callback function that can be used to implement a delay. toggleSwitch() doesn't support
* a delay, so it should only be used with toggles (eg, 'ENABLE'), not momentary switches (eg, 'TEST').
* the specified switch, but processCommands() considers a 'hold' function to be asynchronous, which means
* that holdSwitch() will be passed a callback function that can be used to implement a delay between the
* press and the release, whereas toggleSwitch() will not.
*
* holdSwitch() only makes sense for momentary switches (eg, 'TEST'), where a visual delay might be nice.
* If the switch isn't momentary, or no delay is desired, then use toggleSwitch(); it will be more efficient.
*
* Finally, for switches that are toggles (eg, 'ENABLE'), you can use setSwitch() to set it to a specific
* state: zero for "off" and non-zero for "on". setSwitch() also supports meta-switches like "SR", using
* the entire value to set a series of switches at once; the value is assumed to be octal unless overridden
* by a prefix (eg, "0x") or suffix (eg, ".").
*/
this['exports'] = {
'hold': this.holdSwitch,
'toggle': this.toggleSwitch,
'reset': this.resetSwitches
'reset': this.resetSwitches,
'set': this.setSwitch
};
this.setReady();
@ -255,7 +264,7 @@ class PanelPDP11 extends Component {
*/
setSR(value)
{
this.setSwitches(value);
this.setSRSwitches(value);
}
/**
@ -631,6 +640,28 @@ class PanelPDP11 extends Component {
return true;
}
/**
* setSwitch(sBinding, sValue)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {string} sValue
* @return {boolean}
*/
setSwitch(sBinding, sValue)
{
if (sBinding == "SR") {
return this.setSRSwitches(Str.parseInt(sValue, 8))
}
var sw = this.switches[sBinding];
if (sw) {
sw[1] = +sValue? 1 : 0;
this.displaySwitch(sBinding, sw[1]);
return true;
}
return false;
}
/**
* toggleSwitch(sBinding)
*
@ -736,12 +767,16 @@ class PanelPDP11 extends Component {
processStart(value, index)
{
if (!value && !this.cpu.isRunning()) {
this.cpu.setPC(this.regAddr);
/*
* TODO: Verify what the PDP-11/70 Handbook means when it says that when the 'START' switch
* is depressed, "the computer system will be cleared." I take it to mean that it performs
* the equivalent of a RESET instruction.
*/
this.cpu.resetCPU();
/*
* The PDP-11/70 Handbook goes on to say: "If the system needs to be initialized but execution
* is not wanted, the START switch should be depressed while the HALT/ENABLE switch is in the HALT
@ -955,18 +990,18 @@ class PanelPDP11 extends Component {
/*
* This is another one of my "innovations": when you're done testing the LEDs, all the switches reset as well.
*/
this.setSwitches(0);
this.setSRSwitches(0);
}
}
/**
* processSwitchReg(value, index)
* processSRSwitch(value, index)
*
* @this {PanelPDP11}
* @param {number} value (normally 0 or 1, but we only depend on it being zero or non-zero)
* @param {number} index
*/
processSwitchReg(value, index)
processSRSwitch(value, index)
{
if (value) {
this.regSwitches |= 1 << index;
@ -1066,31 +1101,37 @@ class PanelPDP11 extends Component {
}
/**
* hasSwitches(value)
* hasSRSwitches(value)
*
* @this {PanelPDP11}
* @return {boolean}
*/
hasSwitches()
hasSRSwitches()
{
return this.bindings[PanelPDP11.SWITCH.S0] !== undefined;
}
/**
* setSwitches(value)
* setSRSwitches(value)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number|undefined} value
* @return {boolean}
*/
setSwitches(value)
setSRSwitches(value)
{
if (this.hasSwitches()) {
this.regSwitches = value;
if (this.hasSRSwitches() && !isNaN(value)) {
this.regSwitches = value | 0;
for (var i = 0; i < 22; i++) {
this.switches['S'+i][1] = (value & (1 << i))? 1 : 0;
this.switches['S'+i][1] = (this.regSwitches & (1 << i))? 1 : 0;
}
/*
* This (re)displays ALL switches, not merely the SR switches, but that's OK.
*/
this.displaySwitches();
return true;
}
return false;
}
/**

View file

@ -623,10 +623,10 @@ class Component {
*
* This is a simple parser that breaks sScript into an array of commands, where each command
* is an array of tokens, where tokens are sequences of characters separated by any of: tab, space,
* carriage-return (CR), line-feed (LF), semi-colon, single-quote, or double-quote; if a quote is
* carriage-return (CR), line-feed (LF), semicolon, single-quote, or double-quote; if a quote is
* used, all characters up to the next matching quote become part of the token, allowing any of the
* other separators to be part of the token. CR, LF and semi-colon also serve to terminate a command,
* with semi-colon being preferred, because it's 1) more visible, and 2) essential when the entire
* other separators to be part of the token. CR, LF and semicolon also serve to terminate a command,
* with semicolon being preferred, because it's 1) more visible, and 2) essential when the entire
* script is a multi-line string where all CR/LF were replaced by spaces (which is what Jekyll does,
* and since we can't change Jekyll, it's what our own MarkDown Front Matter parser does as well;
* see convertMD() in markout.js, where the aCommandDefs array is built).
@ -783,7 +783,7 @@ class Component {
}
if (!fSuccess) {
Component.alertUser("Script error (" + sCommand + ")");
Component.alertUser("Script error: " + sCommand + (fnCommand? " failed" : " unrecognized"));
break;
}

View file

@ -88,7 +88,7 @@ function addStickyMachine(idMachine, sPosition)
}
/**
* commandMachine(idMachine, sComponent, sCommand, sValue)
* commandMachine(control, fSingle, idMachine, sComponent, sCommand, sValue)
*
* Note that this script is not compiled into any of the machines, since "sticky machines" are feature of the PCjs
* website rather than of the machines. And since the machines are compiled, all their code and data is completely
@ -103,14 +103,19 @@ function addStickyMachine(idMachine, sPosition)
* component is found, then its 'exports' table is checked for an entry matching the specified command string, and if
* an entry is found, then the corresponding function is called with the specified data.
*
* @param {Object} control
* @param {boolean} fSingle
* @param {string} idMachine
* @param {string} sComponent
* @param {string} sCommand
* @param {string} [sValue]
* @return {boolean}
*/
function commandMachine(idMachine, sComponent, sCommand, sValue)
function commandMachine(control, fSingle, idMachine, sComponent, sCommand, sValue)
{
if (fSingle) {
control.disabled = true;
}
if (sCommand == "script" && window.processMachineScript) {
return window.processMachineScript(idMachine, sComponent, sValue);
}