Added support for keyboard lock LEDs

This commit is contained in:
Jeff Parsons 2014-11-14 18:08:45 -08:00 • committed by jeffpar
commit 501649f67b
37 changed files with 1970 additions and 1829 deletions

View file

@ -10,6 +10,9 @@
<video id="videoMDA" screenwidth="720" screenheight="350" scale="true" charset="/devices/pc/video/ibm-mda-cga.json" pos="center" padding="8px">
<menu>
<title>Monochrome Display</title>
<control type="led" class="output" label="Scroll" binding="scroll-lock" pos="right" padleft="8px"/>
<control type="led" class="output" label="Num" binding="num-lock" pos="right" padleft="8px"/>
<control type="led" class="output" label="Caps" binding="caps-lock" pos="right" padleft="8px"/>
</menu>
</video>
<keyboard ref="/devices/pc/keyboard/us83.xml"/>

View file

@ -9,6 +9,9 @@
<video id="videoMDA" screenwidth="720" screenheight="350" scale="true" charset="/devices/pc/video/ibm-mda-cga.json" padding="8px">
<menu>
<title>Monochrome Display</title>
<control type="led" class="output" label="Scroll" binding="scroll-lock" pos="right" padleft="8px"/>
<control type="led" class="output" label="Num" binding="num-lock" pos="right" padleft="8px"/>
<control type="led" class="output" label="Caps" binding="caps-lock" pos="right" padleft="8px"/>
</menu>
</video>
<cpu id="cpu8088" model="8088" autostart="true" pos="left" padleft="8px" padbottom="8px">
@ -22,6 +25,7 @@
<control type="list" class="input" binding="listDrives"/>
<control type="list" class="input" binding="listDisks">
<disk path="">None</disk>
<manifest ref="/disks/pc/dos/ibm/2.00/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/diags/ibm/2.20/manifest.xml" disk="*"/>
</control>
<control type="button" class="input" binding="loadDrive">Load Drive</control>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
Installation Notes
---
Boot from disk "N1" (must not be write-protected).
Serial number: sco005312
activation key: thmjvbqz
Note: this Xenix version can't run with a VGA card.
You can use a Hercules monochrome card or a CGA/EGA card.
Manifest Details
---
[Manifest](manifest.xml) update using:
find -L private -name "*" -type f -exec node ../../../../../modules/diskdump/bin/diskdump --format=json --output=. --manifest --title="SCO Xenix 8086 Operating System v2.1.3" --disk={} \;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.16.0/manifest.xsl"?>
<manifest type="software">
<title>SCO Xenix 8086 Operating System v2.1.3</title>
<disk id="disk01" size="368640" chs="40:2:9" img="private/B1" href="/disks/pc/xenix/8086/2.1.3/B1.json" md5="f14b42cc487c43892a98a7429f406b2c" md5json="ddcbf13e539aaa77ec311b8a390b919a">
<name>B1</name>
</disk>
<disk id="disk02" size="368640" chs="40:2:9" img="private/B2" href="/disks/pc/xenix/8086/2.1.3/B2.json" md5="d43f006038c66cf442e1fb0b7d94c889" md5json="9f1b830c15442ac7cbf56cda585f2d69">
<name>B2</name>
</disk>
<disk id="disk03" size="368640" chs="40:2:9" img="private/B3" href="/disks/pc/xenix/8086/2.1.3/B3.json" md5="7a0e593bb15eaa0fa56a7ef9f72e0b8d" md5json="2406f520bdb52fd7b498b7db68aef6b3">
<name>B3</name>
</disk>
<disk id="disk04" size="368640" chs="40:2:9" img="private/GAMES" href="/disks/pc/xenix/8086/2.1.3/GAMES.json" md5="0be7c5213dc1d540c4019e130f533835" md5json="2dac3fffe51f3200f3b8f2a5cebcf5a2">
<name>GAMES</name>
</disk>
<disk id="disk05" size="368640" chs="40:2:9" img="private/N1" href="/disks/pc/xenix/8086/2.1.3/N1.json" md5="f3abb0fbee6a60d925cbd81a31e9f2a5" md5json="d84c47d7fa2e14f00f76dabed23a3b58">
<name>N1</name>
</disk>
<disk id="disk06" size="368640" chs="40:2:9" img="private/N2" href="/disks/pc/xenix/8086/2.1.3/N2.json" md5="f3c8234732431e0fb29fea3c70f01be2" md5json="38b9a234911757176e437abff2173d02">
<name>N2</name>
</disk>
<disk id="disk07" size="368640" chs="40:2:9" img="private/N3" href="/disks/pc/xenix/8086/2.1.3/N3.json" md5="fb94524d93934e05615d29d618e426bf" md5json="49537e4c3601cb95e807b763fca2eb59">
<name>N3</name>
</disk>
<disk id="disk08" size="368640" chs="40:2:9" img="private/N4" href="/disks/pc/xenix/8086/2.1.3/N4.json" md5="93b380d3bfe3d720f4b14f1078119125" md5json="3e5e568e3eecf7c3673a68442065ed85">
<name>N4</name>
</disk>
<disk id="disk09" size="368640" chs="40:2:9" img="private/N5" href="/disks/pc/xenix/8086/2.1.3/N5.json" md5="5f9361267fea2565561e187896d0b437" md5json="74e4ff69686cc45932424d02e9f5e5eb">
<name>N5</name>
</disk>
<disk id="disk10" size="368640" chs="40:2:9" img="private/N6" href="/disks/pc/xenix/8086/2.1.3/N6.json" md5="fa649506a8d03d6b620a543b947f7b4a" md5json="7e7fbf7288b432c377653a5e1b2ceb8b">
<name>N6</name>
</disk>
<disk id="disk11" size="368640" chs="40:2:9" img="private/X1" href="/disks/pc/xenix/8086/2.1.3/X1.json" md5="0d456f3ff755fb0b3c9244abd6e946ed" md5json="dfcee09880e36afff1c579dc4d35c3c6">
<name>X1</name>
</disk>
<disk id="disk12" size="368640" chs="40:2:9" img="private/X2" href="/disks/pc/xenix/8086/2.1.3/X2.json" md5="62c2c7de8af9b742c552e118049f1f53" md5json="c3b05fb2f8ffc91e3c70ebfdd07dc8fd">
<name>X2</name>
</disk>
<disk id="disk13" size="368640" chs="40:2:9" img="private/X3" href="/disks/pc/xenix/8086/2.1.3/X3.json" md5="3f113fbade21054c5405ce89ada06c7a" md5json="57299f97b269ad62b5d1c6e73afa811d">
<name>X3</name>
</disk>
<disk id="disk14" size="368640" chs="40:2:9" img="private/X4" href="/disks/pc/xenix/8086/2.1.3/X4.json" md5="f14e3828cc4dc55d5c6b3c538f1532b1" md5json="2ec909508308505b1af7cf8e37598be6">
<name>X4</name>
</disk>
<disk id="disk15" size="368640" chs="40:2:9" img="private/X5" href="/disks/pc/xenix/8086/2.1.3/X5.json" md5="19e99431c8a44d78d50b12e524861eb9" md5json="e3bf413459e789e70979114ff3626944">
<name>X5</name>
</disk>
<disk id="disk16" size="368640" chs="40:2:9" img="private/X6" href="/disks/pc/xenix/8086/2.1.3/X6.json" md5="228b4b09421a46931d8976b5dd2b0bb9" md5json="970d62a77b268a532300c15bd6be7150">
<name>X6</name>
</disk>
</manifest>

View file

@ -94,6 +94,16 @@
line-height: 34px;
background-color: #ffffff;
}
.pcjs-led {
float: left;
width: 8px;
height: 8px;
margin: 4px;
border: 1px solid black;
text-align: center;
line-height: 19px;
background-color: #000000;
}
.pcjs-reference {
float: left;
font-size: x-small;

View file

@ -377,6 +377,9 @@
</fieldset>
</form>
</xsl:when>
<xsl:when test="@type = 'led'">
<div class="{$APPCLASS}-{@class} {$APPCLASS}-{@type}" data-value="{$type},{$binding}"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'separator'">
<hr/>
</xsl:when>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -150,7 +150,7 @@ function ChipSet(parmsChipSet)
Component.call(this, "ChipSet", parmsChipSet, ChipSet);
this.model = parmsChipSet['model'];
this.model = (this.model !== undefined? parseInt(this.model, 10) : ChipSet.MODEL_5150);
this.model = (this.model? parseInt(this.model, 10) : ChipSet.MODEL_5150);
/*
* SW1 describes the number of floppy drives, the amount of base memory, the primary monitor type,
@ -173,7 +173,7 @@ function ChipSet(parmsChipSet)
} else {
this.aFloppyDrives = [360, 360];
var aFloppyDrives = parmsChipSet['floppies'];
if (aFloppyDrives && aFloppyDrives.length !== undefined) this.aFloppyDrives = aFloppyDrives;
if (aFloppyDrives && aFloppyDrives.length) this.aFloppyDrives = aFloppyDrives;
var nDrives = this.aFloppyDrives.length;
if (nDrives) {
this.sw1Init |= ChipSet.PPI_SW.FDRIVE.IPL;
@ -1030,19 +1030,23 @@ ChipSet.prototype.reset = function(fHard)
* DMA Controller initialization
*/
this.aDMACs = new Array(this.cDMACs);
for (i = 0; i < this.cDMACs; i++) this.initDMAController(i);
for (i = 0; i < this.cDMACs; i++) {
this.initDMAController(i);
}
/*
* PIC initialization
*/
this.aPICs = new Array(this.cPICs);
this.initPIC(ChipSet.PIC0.INDEX, ChipSet.PIC0.PORT_LO);
if (this.cPICs > 1) this.initPIC(ChipSet.PIC1.INDEX, ChipSet.PIC1.PORT_LO);
if (this.cPICs > 1) {
this.initPIC(ChipSet.PIC1.INDEX, ChipSet.PIC1.PORT_LO);
}
/*
* Timer initialization
*/
this.bTimerCtrl = undefined; // tracks writes to port 0x43
this.bTimerCtrl = null; // tracks writes to port 0x43
this.aTimers = new Array(3);
for (i = 0; i < this.aTimers.length; i++) {
this.initTimer(i);
@ -1051,10 +1055,10 @@ ChipSet.prototype.reset = function(fHard)
/*
* PPI and other misc ports
*/
this.bPPIA = undefined; // tracks writes to port 0x60, in case PPI_CTRL.A_IN is not set
this.bPPIB = undefined; // tracks writes to port 0x61, in case PPI_CTRL.B_IN is not set
this.bPPIC = undefined; // tracks writes to port 0x62, in case PPI_CTRL.C_IN_LO or PPI_CTRL.C_IN_HI is not set
this.bPPICtrl = undefined; // tracks writes to port 0x63 (eg, 0x99); read-only
this.bPPIA = null; // tracks writes to port 0x60, in case PPI_CTRL.A_IN is not set
this.bPPIB = null; // tracks writes to port 0x61, in case PPI_CTRL.B_IN is not set
this.bPPIC = null; // tracks writes to port 0x62, in case PPI_CTRL.C_IN_LO or PPI_CTRL.C_IN_HI is not set
this.bPPICtrl = null; // tracks writes to port 0x63 (eg, 0x99); read-only
this.bNMI = ChipSet.NMI.DISABLE;// tracks writes to the NMI Mask Register
/*
@ -1522,20 +1526,17 @@ ChipSet.prototype.restore = function(data)
this.sw2 = a[3];
a = data[1];
this.aDMACs = new Array(this.cDMACs);
for (i = 0; i < this.cDMACs; i++) {
this.initDMAController(i, a.length == 1? a[0][i] : a);
}
a = data[2];
this.aPICs = new Array(this.cPICs);
for (i = 0; i < this.cPICs; i++) {
this.initPIC(i, i === 0? ChipSet.PIC0.PORT_LO : ChipSet.PIC1.PORT_LO, a[0][i]);
}
a = data[3];
this.bTimerCtrl = a[0];
this.aTimers = new Array(3);
for (i = 0; i < this.aTimers.length; i++) {
this.initTimer(i, a[1][i]);
}
@ -1591,9 +1592,12 @@ ChipSet.aDMAControllerInit = [0, null, null, 0, new Array(4)];
ChipSet.prototype.initDMAController = function(iDMAC, aState)
{
var controller = this.aDMACs[iDMAC];
if (!controller) controller = {
if (!controller) {
Component.assert(!aState);
controller = {
aChannels: new Array(4)
};
}
var a = aState && aState.length == 5? aState : ChipSet.aDMAControllerInit;
controller.bStatus = a[0];
controller.bCmd = a[1];
@ -1618,19 +1622,17 @@ ChipSet.aDMAChannelInit = [true, [0,0], [0,0], [0,0], [0,0]];
*/
ChipSet.prototype.initDMAChannel = function(controller, iChannel, aState)
{
var a = ChipSet.aDMAChannelInit;
var channel = controller.aChannels[iChannel];
if (!channel) {
Component.assert(!aState);
channel = {
masked: true,
addrInit: [0,0],
countInit: [0,0],
addrCurrent: [0,0],
countCurrent: [0,0]
};
} else {
if (aState && aState.length == 8) a = aState;
}
var a = aState && aState.length == 8? aState : ChipSet.aDMAChannelInit;
channel.masked = a[0];
channel.addrInit[0] = a[1][0]; channel.addrInit[1] = a[1][1];
channel.countInit[0] = a[2][0]; channel.countInit[1] = a[2][1];
@ -1639,7 +1641,6 @@ ChipSet.prototype.initDMAChannel = function(controller, iChannel, aState)
channel.mode = a[5];
channel.bPage = a[6];
// a[7] is deprecated
}
channel.controller = controller;
channel.iChannel = iChannel;
this.initDMAFunction(channel, a[8], a[9]);
@ -1650,8 +1651,8 @@ ChipSet.prototype.initDMAChannel = function(controller, iChannel, aState)
* initDMAFunction(channel)
*
* @param {Object} channel
* @param {Component|string|undefined} component
* @param {string|undefined} sFunction
* @param {Component|string} [component]
* @param {string} [sFunction]
* @param {Object} [obj]
* @return {*}
*/
@ -1723,7 +1724,7 @@ ChipSet.prototype.saveDMAChannels = function(controller)
ChipSet.aPICInit = [0, new Array(4)];
/**
* initPIC(iPIC, aState)
* initPIC(iPIC, port, aState)
*
* @this {ChipSet}
* @param {number} iPIC
@ -1733,12 +1734,16 @@ ChipSet.aPICInit = [0, new Array(4)];
ChipSet.prototype.initPIC = function(iPIC, port, aState)
{
var pic = this.aPICs[iPIC];
if (!pic) pic = {};
if (!pic) {
pic = {
aICW: [null,null,null,null]
};
}
var a = aState && aState.length == 8? aState : ChipSet.aPICInit;
pic.port = port;
pic.nIRQBase = iPIC << 3;
pic.nDelay = a[0];
pic.aICW = a[1];
pic.aICW[0] = a[1][0]; pic.aICW[1] = a[1][1]; pic.aICW[2] = a[1][2]; pic.aICW[3] = a[1][3];
pic.nICW = a[2];
pic.bIMR = a[3];
pic.bIRR = a[4];
@ -1785,12 +1790,14 @@ ChipSet.aTimerInit = [[0,0], [0,0], [0,0], [0,0]];
ChipSet.prototype.initTimer = function(iTimer, aState)
{
var timer = this.aTimers[iTimer];
if (!timer) timer = {
if (!timer) {
timer = {
countInit: [0,0],
countStart: [0,0],
countCurrent: [0,0],
countLatched: [0,0]
};
}
var a = aState && aState.length == 13? aState : ChipSet.aTimerInit;
timer.countInit[0] = a[0][0]; timer.countInit[1] = a[0][1];
timer.countStart[0] = a[1][0]; timer.countStart[1] = a[1][1];
@ -1842,7 +1849,7 @@ ChipSet.prototype.saveTimers = function()
* getSWMemorySize(fInit)
*
* @this {ChipSet}
* @param {boolean|undefined} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @param {boolean} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @return {number} number of Kb of specified memory (NOT necessarily the same as installed memory; see RAM component)
*/
ChipSet.prototype.getSWMemorySize = function(fInit)
@ -1856,7 +1863,7 @@ ChipSet.prototype.getSWMemorySize = function(fInit)
* getSWFloppyDrives(fInit)
*
* @this {ChipSet}
* @param {boolean|undefined} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @param {boolean} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @return {number} number of floppy drives specified by SW1 (range is 0 to 4)
*/
ChipSet.prototype.getSWFloppyDrives = function(fInit)
@ -1923,7 +1930,7 @@ ChipSet.prototype.getSWFloppyDriveSize = function(iDrive)
* getSWVideoMonitor(fInit)
*
* @this {ChipSet}
* @param {boolean|undefined} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @param {boolean} [fInit] is true for init switch value(s) only, current value(s) otherwise
* @return {number} one of ChipSet.MONITOR.*
*/
ChipSet.prototype.getSWVideoMonitor = function(fInit)
@ -1956,7 +1963,7 @@ ChipSet.prototype.addSwitches = function(s, control, n, v, oTips)
var aeCells = Component.getElementsByClass(control, sCellClass);
var sTip = null;
for (i = 0; i < aeCells.length; i++) {
if (oTips !== undefined && oTips[i] !== undefined) {
if (oTips != null && oTips[i] != null) {
sTip = oTips[i];
}
if (sTip) aeCells[i].setAttribute("title", sTip);
@ -1992,7 +1999,7 @@ ChipSet.prototype.getSwitch = function(control)
*
* @this {ChipSet}
* @param {Object} control is an HTML control DOM object
* @param {boolean} f is true if the switch represented by e should be "on", false if "off"
* @param {boolean} f is true if the switch represented by control should be "on", false if "off"
*/
ChipSet.prototype.setSwitch = function(control, f)
{
@ -2047,12 +2054,12 @@ ChipSet.prototype.updateSwitchDesc = function()
2: "Color",
3: "Monochrome"
};
if (controlDesc !== undefined) {
if (controlDesc != null) {
var sHTML = "";
sHTML += this.getSWMemorySize(true) + "Kb";
sHTML += ", " + asMonitorTypes[this.getSWVideoMonitor(true)] + " Monitor";
sHTML += ", " + this.getSWFloppyDrives(true) + " Floppy Drives";
if (this.sw1 !== undefined && this.sw1 != this.sw1Init || this.sw2 !== undefined && this.sw2 != this.sw2Init)
if (this.sw1 != null && this.sw1 != this.sw1Init || this.sw2 != null && this.sw2 != this.sw2Init)
sHTML += " (Reset required)";
controlDesc.innerHTML = sHTML;
}
@ -2092,7 +2099,7 @@ ChipSet.prototype.dumpTimer = function()
var timer = this.aTimers[iTimer];
var sDump = "TIMER" + iTimer + ":";
var count = 0;
if (timer.countBytes !== undefined) {
if (timer.countBytes != null) {
for (var i = 0; i <= timer.countBytes; i++) {
count |= (timer.countCurrent[i] << (i * 8));
}
@ -2735,7 +2742,7 @@ ChipSet.prototype.inPICLo = function(iPIC, addrFrom)
{
var b = 0;
var pic = this.aPICs[iPIC];
if (pic.bOCW3 !== undefined) {
if (pic.bOCW3 != null) {
var bReadReg = pic.bOCW3 & ChipSet.PIC_LO.OCW3_READ_CMD;
switch (bReadReg) {
case ChipSet.PIC_LO.OCW3_READ_IRR:
@ -3425,7 +3432,7 @@ ChipSet.prototype.resetTimerIndex = function(iTimer)
* 0: Time-of-Day interrupt (~18.2 interrupts/second)
* 1: DMA refresh
* 2: Sound/Cassette
* @param {boolean|undefined} [fCycleReset] is true if a cycle-count reset is about to occur
* @param {boolean} [fCycleReset] is true if a cycle-count reset is about to occur
* @return {Object} timer
*/
ChipSet.prototype.updateTimer = function(iTimer, fCycleReset)
@ -3593,7 +3600,7 @@ ChipSet.prototype.updateTimer = function(iTimer, fCycleReset)
* updateAllTimers(fCycleReset)
*
* @this {ChipSet}
* @param {boolean|undefined} [fCycleReset] is true if a cycle-count reset is about to occur
* @param {boolean} [fCycleReset] is true if a cycle-count reset is about to occur
*/
ChipSet.prototype.updateAllTimers = function(fCycleReset)
{
@ -4483,7 +4490,7 @@ ChipSet.prototype.setSpeaker = function(fOn)
* @this {ChipSet}
* @param {string} sMessage is any caller-defined message string
* @param {number} [bitsMessage] is one or more Debugger MESSAGE_* category flag(s)
* @param {number|undefined} [nIRQ] if the message is associated with a particular IRQ #
* @param {number} [nIRQ] if the message is associated with a particular IRQ #
*
* This is a combination of the Debugger's messageEnabled() and message() functions, for convenience.
*

View file

@ -172,6 +172,12 @@ function FDC(parmsFDC) {
*/
this.aDiskHistory = [];
/*
* If setBinding() sees a binding for loading local disks, it will set this flag, and then initBus()
* can intelligently update the "listDisks" control accordingly.
*/
this.fLocalDisks = false;
/*
* The remainder of FDC initialization now takes place in our initBus() handler, largely because we
* want initController() to have access to the ChipSet component, so that it can query switches and/or CMOS
@ -419,9 +425,6 @@ FDC.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
case "listDisks":
this.bindings[sBinding] = control;
this.addDiskette("Local Disk", "?");
this.addDiskette("Remote Disk", "??");
control.onchange = function onChangeListDisks(event) {
var controlDesc = fdc.bindings["descDisk"];
var controlOption = control.options[control.selectedIndex];
@ -475,6 +478,7 @@ FDC.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
* Check for availability of FileReader
*/
if (window && 'FileReader' in window) {
this.fLocalDisks = true;
this.bindings[sBinding] = control;
/*
@ -540,6 +544,11 @@ FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addPortInputTable(this, FDC.aPortInput);
bus.addPortOutputTable(this, FDC.aPortOutput);
if (this.fLocalDisks) {
this.addDiskette("Local Disk", "?");
}
this.addDiskette("Remote Disk", "??");
if (!this.autoMount()) this.setReady();
};
@ -1190,7 +1199,7 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
}
if (sDiskettePath == "?") {
this.notice("Use 'Load' to load local disks");
this.notice('Use "Choose File" and "Load" to select and load a local disk.');
return;
}

View file

@ -173,6 +173,8 @@ Keyboard.KEYCODE = {
/* 0x79 */ F10: 121,
/* 0x7A */ F11: 122,
/* 0x7B */ F12: 123,
/* 0x90 */ NUM_LOCK: 144,
/* 0x91 */ SCROLL_LOCK:145,
//
// ONDOWN is a bias we add to browser keyCodes that we want to handle on "down" rather than "press".
//
@ -208,12 +210,12 @@ Keyboard.LOCATION = {
};
/**
* These internal "shift key" states are stored in bitsShift, as well as in aKeyCodes; note that the
* right-hand versions of selected shift bits are shifted 1 bit right.
* These internal "shift key" states are used to indicate the current simulated shift/modifier key states
* in shiftStates; note that right-hand shift bits match the corresponding left-hand bit shifted 1 bit right.
*
* @enum {number}
*/
Keyboard.STATE = {
Keyboard.STATES = {
RSHIFT: 0x0001,
SHIFT: 0x0002,
RCTRL: 0x0004, // 101-key keyboard only
@ -223,21 +225,34 @@ Keyboard.STATE = {
COMMAND: 0x0040, // 101-key keyboard only (TODO: Treat this like the 'Windows' key)
INSERT: 0x0080, // TODO: Placeholder
CAPS_LOCK: 0x0100,
NUM_LOCK: 0x0200, // TODO: Placeholder
SCROLL_LOCK: 0x0400, // TODO: Placeholder
SIMULATE: 0x003f // STATE.RSHIFT | STATE.SHIFT | STATE.RCTRL | STATE.CTRL | STATE.RALT | STATE.ALT
NUM_LOCK: 0x0200,
SCROLL_LOCK: 0x0400,
LOCK: 0x0700, // STATES.CAPS_LOCK | STATES.NUM_LOCK | STATES.SCROLL_LOCK
SIMULATE: 0x003f // STATES.RSHIFT | STATES.SHIFT | STATES.RCTRL | STATES.CTRL | STATES.RALT | STATES.ALT
};
/**
* Maps the KEYCODE of a "shift key" to its corresponding (default) STATE bit above
* Maps KEYCODES of shift/modifier keys to their corresponding (default) STATES bit above
*
* @enum {number}
*/
Keyboard.STATEKEYS = {
16: Keyboard.STATE.SHIFT,
17: Keyboard.STATE.CTRL,
18: Keyboard.STATE.ALT,
20: Keyboard.STATE.CAPS_LOCK
Keyboard.KEYSTATES = {};
Keyboard.KEYSTATES[Keyboard.KEYCODE.SHIFT] = Keyboard.STATES.SHIFT;
Keyboard.KEYSTATES[Keyboard.KEYCODE.CTRL] = Keyboard.STATES.CTRL;
Keyboard.KEYSTATES[Keyboard.KEYCODE.ALT] = Keyboard.STATES.ALT;
Keyboard.KEYSTATES[Keyboard.KEYCODE.CAPS_LOCK] = Keyboard.STATES.CAPS_LOCK;
Keyboard.KEYSTATES[Keyboard.KEYCODE.NUM_LOCK] = Keyboard.STATES.NUM_LOCK;
Keyboard.KEYSTATES[Keyboard.KEYCODE.SCROLL_LOCK]= Keyboard.STATES.SCROLL_LOCK;
/**
* Maps "soft-key" definitions of shift/modifier keys to their corresponding (default) STATES bit above
*
* @enum {number}
*/
Keyboard.LEDSTATES = {
'caps-lock': Keyboard.STATES.CAPS_LOCK,
'num-lock': Keyboard.STATES.NUM_LOCK,
'scroll-lock': Keyboard.STATES.SCROLL_LOCK
};
/**
@ -257,10 +272,6 @@ Keyboard.STATEKEYS = {
Keyboard.aButtonCodes = {
'tab': Keyboard.KEYCODE.TAB + Keyboard.KEYCODE.ONDOWN,
'esc': Keyboard.KEYCODE.ESC + Keyboard.KEYCODE.ONDOWN,
'right-shift': Keyboard.KEYCODE.SHIFT + Keyboard.KEYCODE.ONDOWN + Keyboard.KEYCODE.ONRIGHT,
'shift': Keyboard.KEYCODE.SHIFT + Keyboard.KEYCODE.ONDOWN,
'ctrl': Keyboard.KEYCODE.CTRL + Keyboard.KEYCODE.ONDOWN,
'alt': Keyboard.KEYCODE.ALT + Keyboard.KEYCODE.ONDOWN,
'caps-lock': Keyboard.KEYCODE.CAPS_LOCK + Keyboard.KEYCODE.ONDOWN,
'f1': Keyboard.KEYCODE.F1 + Keyboard.KEYCODE.ONDOWN,
'f2': Keyboard.KEYCODE.F2 + Keyboard.KEYCODE.ONDOWN,
@ -546,8 +557,8 @@ Keyboard.aSoftCodes = {
* The other problem (which is more of a problem with keyboards like the C1P than any IBM keyboards) is
* that the shift/modifier state for a character on the "source" keyboard may not match the shift/modifier
* state for the same character on the "target" keyboard. And since this code is inherited from C1Pjs,
* we've inherited the same solution: keySimulateUpOrDown() has the ability to "undo" any states in bitsShift
* that conflict with the state(s) required for the character in question.
* we've inherited the same solution: keySimulateUpOrDown() has the ability to "undo" any states in
* shiftStates that conflict with the state(s) required for the character in question.
*
* @enum {number}
*/
@ -667,7 +678,9 @@ Keyboard.aKeyCodes[Keyboard.KEYCODE.F7 + Keyboard.KEYCODE.ONDOWN] = Keyboard
Keyboard.aKeyCodes[Keyboard.KEYCODE.F8 + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.F8;
Keyboard.aKeyCodes[Keyboard.KEYCODE.F9 + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.F9;
Keyboard.aKeyCodes[Keyboard.KEYCODE.F10 + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.F10;
// TODO: 0x45 ('num-lock'), 0x46 ('scroll-lock'), 0x4a ('num-sub') and 0x4e ('num-add')
Keyboard.aKeyCodes[Keyboard.KEYCODE.NUM_LOCK + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.NUM_LOCK;
Keyboard.aKeyCodes[Keyboard.KEYCODE.SCROLL_LOCK + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.SCROLL_LOCK;
// TODO: 0x4a ('num-sub') and 0x4e ('num-add')
Keyboard.aKeyCodes[Keyboard.KEYCODE.HOME + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.NUM_HOME;
Keyboard.aKeyCodes[Keyboard.KEYCODE.UP + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.NUM_UP;
Keyboard.aKeyCodes[Keyboard.KEYCODE.PGUP + Keyboard.KEYCODE.ONDOWN] = Keyboard.SCANCODE.NUM_PGUP;
@ -788,6 +801,7 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
*/
var kbd = this;
var id = sHTMLType + '-' + sBinding;
if (this.bindings[id] === undefined) {
switch (sBinding) {
case "kbd":
@ -802,6 +816,28 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
return kbd.keyUpDown(event, false);
};
return true;
case "caps-lock":
this.bindings[id] = control;
control.onclick = function onClickCapsLock(event) {
return kbd.toggleCapsLock(event);
};
return true;
case "num-lock":
this.bindings[id] = control;
control.onclick = function onClickNumLock(event) {
return kbd.toggleNumLock(event);
};
return true;
case "scroll-lock":
this.bindings[id] = control;
control.onclick = function onClickScrollLock(event) {
return kbd.toggleScrollLock(event);
};
return true;
default:
if (Keyboard.aButtonCodes[sBinding] !== undefined && sHTMLType == "button") {
this.bindings[id] = control;
@ -809,6 +845,7 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
return function onClickKeyboard(event) {
if (DEBUG) kbd.messageDebugger(sKey + " clicked", Debugger.MESSAGE.KEYS);
if (kbd.cpu) kbd.cpu.setFocus();
kbd.checkShiftState(keyCode);
return !kbd.keySimulatePress(keyCode);
};
}(this, sBinding, Keyboard.aButtonCodes[sBinding]);
@ -841,28 +878,28 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
};
/**
* findBinding(bKey, t, fDown)
* findBinding(bKey, sType, fDown)
*
* @this {Keyboard}
* @param {number} bKey
* @param {string} t is the type of control (eg, "button" or "key")
* @param {string} sType is the type of control (eg, "button" or "key")
* @param {boolean} [fDown] is true if the key is going down, false if up, or undefined if unchanged
* @return {Object} is the HTML control DOM object (eg, HTMLButtonElement), or undefined if no such control exists
*/
Keyboard.prototype.findBinding = function(bKey, t, fDown)
Keyboard.prototype.findBinding = function(bKey, sType, fDown)
{
var e;
for (var s in Keyboard.aSoftCodes) {
if (Keyboard.aSoftCodes[s] == bKey) {
var id = t + '-' + s;
e = this.bindings[id];
if (e && fDown !== undefined) {
this.setSoftKeyState(e, fDown);
var control;
for (var sBinding in Keyboard.aSoftCodes) {
if (Keyboard.aSoftCodes[sBinding] == bKey) {
var id = sType + '-' + sBinding;
control = this.bindings[id];
if (control && fDown !== undefined) {
this.setSoftKeyState(control, fDown);
}
break;
}
}
return e;
return control;
};
/**
@ -1108,11 +1145,11 @@ Keyboard.prototype.reset = function()
this.initState();
/*
* The physical (not virtual) state of various shift/lock keys.
* The current (assumed) physical state of the various shift/lock keys.
*
* TODO: Determine how (or whether) we can query the browser's initial shift/lock key states.
*/
this.bitsShift = 0;
this.shiftStates = 0;
/*
* New scan codes are "pushed" onto abScanBuffer and then "shifted" off.
@ -1344,6 +1381,100 @@ Keyboard.prototype.injectKeysFromBuffer = function(msDelay)
}
};
/**
* setLED(control, f)
*
* @this {Keyboard}
* @param {Object} control is an HTML control DOM object
* @param {boolean} f is true if the LED represented by control should be "on", false if "off"
*/
Keyboard.prototype.setLED = function(control, f)
{
control.style.backgroundColor = (f? "#00ff00" : "#000000");
};
/**
* updateLEDs(bitState)
*
* Updates any and all shift-related LEDs with the corresponding state in shiftStates.
*
* @this {Keyboard}
* @param {number} [bitState] is the bit in shiftStates that may have changed, if known; undefined if not
*/
Keyboard.prototype.updateLEDs = function(bitState)
{
var control;
for (var sBinding in Keyboard.LEDSTATES) {
var id = "led-" + sBinding;
var bitLED = Keyboard.LEDSTATES[sBinding];
if ((!bitState || bitState == bitLED) && (control = this.bindings[id])) {
this.setLED(control, !!(this.shiftStates & bitLED));
}
}
};
/**
* toggleCapsLock(event)
*
* @this {Keyboard}
* @param {Object} event
*/
Keyboard.prototype.toggleCapsLock = function(event)
{
if (this.cpu) this.cpu.setFocus();
this.keySimulatePress(Keyboard.KEYCODE.CAPS_LOCK + Keyboard.KEYCODE.ONDOWN, true);
};
/**
* toggleNumLock(event)
*
* @this {Keyboard}
* @param {Object} event
*/
Keyboard.prototype.toggleNumLock = function(event)
{
if (this.cpu) this.cpu.setFocus();
this.keySimulatePress(Keyboard.KEYCODE.NUM_LOCK + Keyboard.KEYCODE.ONDOWN, true);
};
/**
* toggleScrollLock(event)
*
* @this {Keyboard}
* @param {Object} event
*/
Keyboard.prototype.toggleScrollLock = function(event)
{
if (this.cpu) this.cpu.setFocus();
this.keySimulatePress(Keyboard.KEYCODE.SCROLL_LOCK + Keyboard.KEYCODE.ONDOWN, true);
};
/**
* checkShiftState(keyCode, fDown)
*
* @this {Keyboard}
* @param {number} keyCode (should already include any ONDOWN and/or ONRIGHT modifiers)
* @param {boolean} [fDown] is true for down, false for up, undefined for TBD
*/
Keyboard.prototype.checkShiftState = function(keyCode, fDown)
{
if (Keyboard.aKeyCodes[keyCode]) {
var bCode = Math.floor(keyCode % 1000);
var fRight = (Math.floor(keyCode / 1000) & 2);
var bitState = Keyboard.KEYSTATES[bCode] || 0;
if (bitState) {
if (fRight) bitState >>= 1;
if (bitState & Keyboard.STATES.LOCK) {
fDown = !(this.shiftStates & bitState);
}
this.shiftStates &= ~bitState;
if (fDown) this.shiftStates |= bitState;
this.updateLEDs(bitState);
}
}
};
/**
* keyUpDown(event, fDown)
*
@ -1365,20 +1496,26 @@ Keyboard.prototype.keyUpDown = function(event, fDown)
if (Keyboard.aKeyCodes[keyCode + Keyboard.KEYCODE.ONDOWN]) {
keyCodeSim += Keyboard.KEYCODE.ONDOWN;
var bShift = Keyboard.STATEKEYS[keyCode] || 0;
var bitState = Keyboard.KEYSTATES[keyCode] || 0;
if (bShift) {
if (bitState) {
if (event.location == Keyboard.LOCATION.RIGHT) {
bShift >>= 1;
bitState >>= 1;
keyCodeSim += Keyboard.KEYCODE.ONRIGHT;
}
this.bitsShift &= ~bShift;
if (fDown) this.bitsShift |= bShift;
if (keyCode == Keyboard.KEYCODE.CAPS_LOCK) {
this.shiftStates &= ~bitState;
if (fDown) this.shiftStates |= bitState;
this.updateLEDs(bitState);
if (keyCode == Keyboard.KEYCODE.CAPS_LOCK || keyCode == Keyboard.KEYCODE.NUM_LOCK || keyCode == Keyboard.KEYCODE.SCROLL_LOCK) {
/*
* FYI, CAPS_LOCK generates a "down" event ONLY when getting locked, and an "up" event ONLY
* FYI, "lock" keys generate a "down" event ONLY when getting locked, and an "up" event ONLY
* when getting unlocked--which is exactly what I want, even though that may seem a little
* counter-intuitive (since the key itself actually went down AND up for each event).
* counter-intuitive.
*
* However, since the key DID actually go down AND up on each event, we must make sure both make
* and break scan codes are delivered; keySimulatePress() will take care of that.
*/
fPass = this.keySimulatePress(keyCodeSim);
} else {
@ -1389,7 +1526,7 @@ Keyboard.prototype.keyUpDown = function(event, fDown)
/*
* HACK for simulating Ctrl-Break using Ctrl-Del (Mac) / Ctrl-Backspace (Windows)
*/
if (keyCode == Keyboard.KEYCODE.BS && (this.bitsShift & (Keyboard.STATE.CTRL|Keyboard.STATE.ALT)) == Keyboard.STATE.CTRL) {
if (keyCode == Keyboard.KEYCODE.BS && (this.shiftStates & (Keyboard.STATES.CTRL|Keyboard.STATES.ALT)) == Keyboard.STATES.CTRL) {
keyCodeSim = Keyboard.KEYCODE.FAKE_CTRLBREAK;
}
/*
@ -1427,8 +1564,8 @@ Keyboard.prototype.keyUpDown = function(event, fDown)
/*
* Avoid interfering with useful Browser key commands, like COMMAND-Q, COMMAND-T, etc.
*/
this.bitsShift &= ~Keyboard.STATE.COMMAND;
if (fDown) this.bitsShift |= Keyboard.STATE.COMMAND;
this.shiftStates &= ~Keyboard.STATES.COMMAND;
if (fDown) this.shiftStates |= Keyboard.STATES.COMMAND;
fAutoClear = false;
fPass = true;
}
@ -1476,7 +1613,7 @@ Keyboard.prototype.keyUpDown = function(event, fDown)
* above that explicitly clear fAutoClear -- such as the COMMAND key itself -- are exceptions
* to the rule).
*/
this.bitsShift &= ~Keyboard.STATE.COMMAND;
this.shiftStates &= ~Keyboard.STATES.COMMAND;
/*
* I don't reliably get keyDown/keyUp events for all keys on all devices, but for those devices that
* I DO, it seems like a good idea to cancel any pending key "up" simulation on receipt of the actual
@ -1529,11 +1666,11 @@ Keyboard.prototype.keyPress = function(event)
*/
fPass = false;
} else {
if (this.bitsShift & Keyboard.STATE.COMMAND) {
this.bitsShift &= ~Keyboard.STATE.COMMAND;
if (this.shiftStates & Keyboard.STATES.COMMAND) {
this.shiftStates &= ~Keyboard.STATES.COMMAND;
} else {
// if (DEBUG && event.altKey) this.messageDebugger("ALT press: keyCode " + keyCode, Debugger.MESSAGE.KEYS);
if (this.bitsShift & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) {
if (this.shiftStates & (Keyboard.STATES.CTRL | Keyboard.STATES.ALT)) {
fPass = false;
} else {
if (this.fEscapeDisabled && keyCode == Keyboard.ASCII['`']) keyCode = Keyboard.KEYCODE.ESC;
@ -1547,14 +1684,15 @@ Keyboard.prototype.keyPress = function(event)
};
/**
* keySimulatePress(keyCode)
* keySimulatePress(keyCode, fCheckShift, fQuickRelease)
*
* @this {Keyboard}
* @param {number} keyCode
* @param {boolean} [fCheckShift]
* @param {boolean} [fQuickRelease] is true to simulate the press and release immediately
* @return {boolean} true if successfully simulated, false if unrecognized/unsupported key
*/
Keyboard.prototype.keySimulatePress = function(keyCode, fQuickRelease)
Keyboard.prototype.keySimulatePress = function(keyCode, fCheckShift, fQuickRelease)
{
var fSimulated = false;
@ -1563,6 +1701,8 @@ Keyboard.prototype.keySimulatePress = function(keyCode, fQuickRelease)
*/
this.autoClear(keyCode);
if (fCheckShift) this.checkShiftState(keyCode);
if (this.keySimulateUpOrDown(keyCode, true, Keyboard.SIMCODE.KEYPRESS)) {
/*
* If fQuickRelease is set, we switch to an alternate approach, which is to immediately queue a
@ -1627,7 +1767,7 @@ Keyboard.prototype.keySimulateUpOrDown = function(keyCode, fDown, simCode)
* whenever both CTRL and ALT are pressed as well, so that it's easier to simulate that old favorite: CTRL-ALT-DEL
*/
if (wCode == Keyboard.SCANCODE.BS) {
if ((this.bitsShift & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) == (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) {
if ((this.shiftStates & (Keyboard.STATES.CTRL | Keyboard.STATES.ALT)) == (Keyboard.STATES.CTRL | Keyboard.STATES.ALT)) {
wCode = Keyboard.SCANCODE.NUM_DEL;
}
}
@ -1651,17 +1791,17 @@ Keyboard.prototype.keySimulateUpOrDown = function(keyCode, fDown, simCode)
continue;
}
if (bScan == Keyboard.SCANCODE.SHIFT) {
if (!(this.bitsShift & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT))) {
if (!(this.bitsShift & Keyboard.STATE.CAPS_LOCK) || !fAlpha) {
if (!(this.shiftStates & (Keyboard.STATES.SHIFT | Keyboard.STATES.RSHIFT))) {
if (!(this.shiftStates & Keyboard.STATES.CAPS_LOCK) || !fAlpha) {
bShift = bScan;
}
}
} else if (bScan == Keyboard.SCANCODE.CTRL) {
if (!(this.bitsShift & (Keyboard.STATE.CTRL | Keyboard.STATE.RCTRL))) {
if (!(this.shiftStates & (Keyboard.STATES.CTRL | Keyboard.STATES.RCTRL))) {
bShift = bScan;
}
} else if (bScan == Keyboard.SCANCODE.ALT) {
if (!(this.bitsShift & (Keyboard.STATE.ALT | Keyboard.STATE.RALT))) {
if (!(this.shiftStates & (Keyboard.STATES.ALT | Keyboard.STATES.RALT))) {
bShift = bScan;
}
} else {

View file

@ -87,8 +87,9 @@ if (typeof module !== 'undefined') {
* or setPixel().
*
* Thanks to the CPU's new block-based memory manager that allows us to sparse-allocate memory
* in 4K increments, updateScreen() can also ask the CPU for the "dirty" state of all the blocks
* underlying the video buffer, bypassing the update completely if the buffer is still clean.
* (in 4Kb increments on 20-bit buses, 16Kb increments on 24-bit buses), updateScreen()
* can also ask the CPU for the "dirty" state of all the blocks underlying the video buffer,
* bypassing the update completely if the buffer is still clean.
*
* Unfortunately, that optimization is defeated if our count of active blink elements is non-zero,
* because we must rescan the entire buffer to locate and redraw them all; I'm assuming for now
@ -499,8 +500,8 @@ Video.MODES.UNKNOWN = 0xFF;
* Supported Fonts
*
* Once we've finished loading the standard 8K font file, aFonts[] should contain one or more of the
* fonts listed below. For the standard MDA/CGA font ROM, the first (MDA) font resides in the first 4K,
* and the second and third (CGA) fonts reside in the two 2K halves of the second 4K.
* fonts listed below. For the standard MDA/CGA font ROM, the first (MDA) font resides in the first 4Kb,
* and the second and third (CGA) fonts reside in the two 2K halves of the second 4Kb.
*
* It may seem odd that the cell size for FONT_CGAD is *larger* than the cell size for FONT_CGA,
* since 40-column mode is actually lower resolution, but since we don't shrink the window when we shrink
@ -1018,7 +1019,7 @@ Card.MISC.IO_SELECT = 0x01; // 0 sets CRT ports to 0x3Bn, 1 sets CRT
Card.MISC.ENABLE_RAM = 0x02; // 0 disables video RAM, 1 enables
Card.MISC.CLK_SELECT = 0x0C; // 0x0: 14Mhz I/O clock, 0x4: 16Mhz on-board clock, 0x8: external clock, 0xC: unused
Card.MISC.DISABLE_DRV = 0x10; // 0 activates internal video drivers, 1 activates feature connector direct drive outputs
Card.MISC.PAGE_ODD_EVEN = 0x20; // 0 selects the low 64K page of video RAM for text modes, 1 selects the high page
Card.MISC.PAGE_ODD_EVEN = 0x20; // 0 selects the low 64Kb page of video RAM for text modes, 1 selects the high page
Card.MISC.HORZ_POLARITY = 0x40; // 0 selects positive horizontal retrace
Card.MISC.VERT_POLARITY = 0x80; // 0 selects positive vertical retrace
@ -1981,6 +1982,9 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
this.kbd = cmp.getComponentByType("Keyboard");
if (this.kbd && this.canvasScreen) {
for (var s in this.bindings) {
if (s.indexOf("lock") > 0) this.kbd.setBinding("input", "led", s, this.bindings[s]);
}
this.kbd.setBinding("input", this.textareaScreen? "textarea" : "canvas", "kbd", this.textareaScreen || this.canvasScreen);
}
@ -2008,10 +2012,17 @@ Video.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
var video = this;
var canvas, lockPointer;
if (!this.bindings[sBinding]) {
/*
* We now save every binding that comes in, so that if there are bindings for "caps-lock' and the like,
* we can forward them to the Keyboard.
*/
this.bindings[sBinding] = control;
switch (sBinding) {
case "lockPointer":
this.bindings[sBinding] = control;
this.sLockMessage = control.innerHTML;
if (this.canvasScreen && this.canvasScreen.lockPointer) {
control.onclick = function onClickLockPointer() {
@ -2025,7 +2036,6 @@ Video.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
return true;
case "refresh":
this.bindings[sBinding] = control;
control.onclick = function onClickRefresh() {
if (DEBUG) video.messageDebugger("refreshScreen()");
video.updateScreen(true);
@ -2035,6 +2045,7 @@ Video.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
default:
break;
}
}
return false;
};

View file

@ -99,6 +99,16 @@
line-height: 34px; /* the equivalent of "vertical-align: middle" for single-line elements */
background-color: #ffffff;
}
.pcjs-led {
float: left;
width: 8px;
height: 8px;
margin: 4px;
border: 1px solid black;
text-align: center;
line-height: 19px; /* the equivalent of "vertical-align: middle" for single-line elements */
background-color: #000000;
}
.pcjs-reference {
float: left;
font-size: x-small;

View file

@ -378,6 +378,9 @@
</fieldset>
</form>
</xsl:when>
<xsl:when test="@type = 'led'">
<div class="{$APPCLASS}-{@class} {$APPCLASS}-{@type}" data-value="{$type},{$binding}"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'separator'">
<hr/>
</xsl:when>

View file

@ -509,21 +509,21 @@ if (window && !window.document.ELEMENT_NODE) window.document.ELEMENT_NODE = 1;
*/
Component.bindComponentControls = function(component, element, sAppClass)
{
var iControl, aeControls;
aeControls = Component.getElementsByClass(element.parentNode, sAppClass + "-control");
for (iControl = 0; iControl < aeControls.length; iControl++) {
var iNode, aeChildNodes;
aeChildNodes = aeControls[iControl].childNodes;
for (iNode = 0; iNode < aeChildNodes.length; iNode++) {
var aeControls = Component.getElementsByClass(element.parentNode, sAppClass + "-control");
for (var iControl = 0; iControl < aeControls.length; iControl++) {
var aeChildNodes = aeControls[iControl].childNodes;
for (var iNode = 0; iNode < aeChildNodes.length; iNode++) {
var control = aeChildNodes[iNode];
if (control.nodeType !== window.document.ELEMENT_NODE) {
continue;
}
var sClass = control.getAttribute("class");
if (!sClass) continue;
var iClass, aClasses;
aClasses = sClass.split(" ");
for (iClass = 0; iClass < aClasses.length; iClass++) {
var aClasses = sClass.split(" ");
for (var iClass = 0; iClass < aClasses.length; iClass++) {
var parms;
sClass = aClasses[iClass];
switch (sClass) {
@ -535,7 +535,7 @@ Component.bindComponentControls = function(component, element, sAppClass)
} else {
Component.log('Component.bindComponentControls("' + component.toString() + '"): missing binding' + (parms? ' for ' + parms['type'] : ''), "warning");
}
aClasses = [];
iClass = aClasses.length;
break;
default:
// Component.log("Component.bindComponentControls(" + component.toString() + "): unrecognized control class \"" + sClass + "\"", "warning");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -94,6 +94,16 @@
line-height: 34px;
background-color: #ffffff;
}
.pcjs-led {
float: left;
width: 8px;
height: 8px;
margin: 4px;
border: 1px solid black;
text-align: center;
line-height: 19px;
background-color: #000000;
}
.pcjs-reference {
float: left;
font-size: x-small;

View file

@ -377,6 +377,9 @@
</fieldset>
</form>
</xsl:when>
<xsl:when test="@type = 'led'">
<div class="{$APPCLASS}-{@class} {$APPCLASS}-{@type}" data-value="{$type},{$binding}"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'separator'">
<hr/>
</xsl:when>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long