A machine's screen can now be used to display initialization (diagnostic) messages, up until the Computer component is ready to power/start the CPU, at which point control of the screen reverts to the Video component

This commit is contained in:
Jeff Parsons 2017-06-28 16:24:33 -07:00 • committed by Jeff Parsons
commit ea82dfec93
35 changed files with 6152 additions and 5539 deletions

View file

@ -1302,7 +1302,7 @@ class Web {
}
/**
* getResource(sURL, dataPost, fAsync, done)
* getResource(sURL, dataPost, fAsync, done, progress)
*
* Request the specified resource (sURL), and once the request is complete, notify done().
*
@ -1325,9 +1325,10 @@ class Web {
* @param {string|Object|null} [dataPost] for a POST request (default is a GET request)
* @param {boolean} [fAsync] is true for an asynchronous request
* @param {function(string,string,number)} [done]
* @param {function(number)} [progress]
* @return {Array|null} Array containing [sResource, nErrorCode], or null if no response yet
*/
static getResource(sURL, dataPost, fAsync = false, done)
static getResource(sURL, dataPost, fAsync = false, done, progress)
{
var nErrorCode = 0, sResource = null, response = null;
@ -1355,33 +1356,38 @@ class Web {
if (fAsync) {
xmlHTTP.onreadystatechange = function()
{
if (xmlHTTP.readyState === 4) {
/*
* The following line was recommended for WebKit, as a work-around to prevent the handler firing multiple
* times when debugging. Unfortunately, that's not the only XMLHttpRequest problem that occurs when
* debugging, so I think the WebKit problem is deeper than that. When we have multiple XMLHttpRequests
* pending, any debugging activity means most of them simply get dropped on floor, so what may actually be
* happening are mis-notifications rather than redundant notifications.
*
* xmlHTTP.onreadystatechange = undefined;
*/
sResource = xmlHTTP.responseText;
/*
* The normal "success" case is an HTTP status code of 200, but when testing with files loaded
* from the local file system (ie, when using the "file:" protocol), we have to be a bit more "flexible".
*/
if (xmlHTTP.status == 200 || !xmlHTTP.status && sResource.length && Web.getHostProtocol() == "file:") {
if (MAXDEBUG) Web.log("xmlHTTP.onreadystatechange(" + sURL + "): returned " + sResource.length + " bytes");
}
else {
nErrorCode = xmlHTTP.status || -1;
Web.log("xmlHTTP.onreadystatechange(" + sURL + "): error code " + nErrorCode);
}
if (done) done(sURL, sResource, nErrorCode);
if (xmlHTTP.readyState !== 4) {
if (progress) progress(1);
return;
}
/*
* The following line was recommended for WebKit, as a work-around to prevent the handler firing multiple
* times when debugging. Unfortunately, that's not the only XMLHttpRequest problem that occurs when
* debugging, so I think the WebKit problem is deeper than that. When we have multiple XMLHttpRequests
* pending, any debugging activity means most of them simply get dropped on floor, so what may actually be
* happening are mis-notifications rather than redundant notifications.
*
* xmlHTTP.onreadystatechange = undefined;
*/
sResource = xmlHTTP.responseText;
/*
* The normal "success" case is an HTTP status code of 200, but when testing with files loaded
* from the local file system (ie, when using the "file:" protocol), we have to be a bit more "flexible".
*/
if (xmlHTTP.status == 200 || !xmlHTTP.status && sResource.length && Web.getHostProtocol() == "file:") {
if (MAXDEBUG) Web.log("xmlHTTP.onreadystatechange(" + sURL + "): returned " + sResource.length + " bytes");
}
else {
nErrorCode = xmlHTTP.status || -1;
Web.log("xmlHTTP.onreadystatechange(" + sURL + "): error code " + nErrorCode);
}
if (progress) progress(2);
if (done) done(sURL, sResource, nErrorCode);
};
}
if (progress) progress(0);
if (dataPost && typeof dataPost == "object") {
var sDataPost = "";
for (var p in dataPost) {
@ -2258,6 +2264,7 @@ class Component {
ready: false,
busy: false,
busyCancel: false,
initDone: false,
powered: false,
unloading: false,
error: false
@ -2267,9 +2274,6 @@ class Component {
this.clearError();
this.bitsMessage = bitsMessage || 0;
/** @type {Object|null} controlPrint is the HTML control, if any, that we can print to */
this.controlPrint = null;
this.cmp = null;
this.bus = null;
this.cpu = null;
@ -2371,7 +2375,7 @@ class Component {
}
sElapsed = (Component.getTime() - Component.msStart) + "ms: ";
}
if (window && window.console) console.log(sElapsed + sMsg.replace(/\n/g, " "));
if (window && window.console) console.log(sElapsed + sMsg.replace(/\n/g, ' '));
}
}
}
@ -2426,13 +2430,15 @@ class Component {
* @param {string} s is the message text
* @param {boolean} [fPrintOnly]
* @param {string} [id] is the caller's ID, if any
* @return {boolean}
*/
static notice(s, fPrintOnly, id)
{
if (!COMPILED) {
Component.println(s, "notice", id);
Component.println(s, Component.TYPE.NOTICE, id);
}
if (!fPrintOnly) Component.alertUser((id? (id + ": ") : "") + s);
return true;
}
/**
@ -2443,7 +2449,7 @@ class Component {
static warning(s)
{
if (!COMPILED) {
Component.println(s, "warning");
Component.println(s, Component.TYPE.WARNING);
}
Component.alertUser(s);
}
@ -2456,7 +2462,7 @@ class Component {
static error(s)
{
if (!COMPILED) {
Component.println(s, "error");
Component.println(s, Component.TYPE.ERROR);
}
Component.alertUser(s);
}
@ -2506,6 +2512,116 @@ class Component {
return sResponse;
}
/**
* Component.appendControl(control, sText)
*
* @param {Object} control
* @param {string} sText
*/
static appendControl(control, sText)
{
control.value += sText;
/*
* Prevent the <textarea> from getting too large; otherwise, printing becomes slower and slower.
*/
if (COMPILED) {
sText = control.value;
if (sText.length > 8192) control.value = sText.substr(sText.length - 4096);
}
control.scrollTop = control.scrollHeight;
}
/**
* Component.replaceControl(control, sSearch, sReplace)
*
* @param {Object} control
* @param {string} sSearch
* @param {string} sReplace
*/
static replaceControl(control, sSearch, sReplace)
{
/*
* Prevent the <textarea> from getting too large; otherwise, printing becomes slower and slower.
*/
var sText = control.value;
var i = sText.lastIndexOf(sSearch);
if (i < 0) {
sText += sSearch + '\n';
} else {
sText = sText.substr(0, i) + sReplace + sText.substr(i + sSearch.length);
}
if (COMPILED && sText.length > 8192) sText = sText.substr(sText.length - 4096);
control.value = sText;
control.scrollTop = control.scrollHeight;
}
/**
* Component.bindExternalControl(component, sControl, sBinding, sType)
*
* @param {Component} component
* @param {string} sControl
* @param {string} sBinding
* @param {string} [sType] is the external component type
*/
static bindExternalControl(component, sControl, sBinding, sType)
{
if (sControl) {
if (sType === undefined) sType = "Panel";
var target = Component.getComponentByType(sType, component.id);
if (target) {
var eBinding = target.bindings[sControl];
if (eBinding) {
component.setBinding(null, sBinding, eBinding);
}
}
}
}
/**
* Component.bindComponentControls(component, element, sAppClass)
*
* @param {Component} component
* @param {HTMLElement} element from the DOM
* @param {string} sAppClass
*/
static bindComponentControls(component, element, sAppClass)
{
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 !== 1 /* document.ELEMENT_NODE */) {
continue;
}
var sClass = control.getAttribute("class");
if (!sClass) continue;
var aClasses = sClass.split(" ");
for (var iClass = 0; iClass < aClasses.length; iClass++) {
var parms;
sClass = aClasses[iClass];
switch (sClass) {
case sAppClass + "-binding":
parms = Component.getComponentParms(control);
if (parms && parms['binding']) {
component.setBinding(parms['type'], parms['binding'], control, parms['value']);
} else if (!parms || parms['type'] != "description") {
Component.log("Component '" + component.toString() + "' missing binding" + (parms? " for " + parms['type'] : ""), "warning");
}
iClass = aClasses.length;
break;
default:
// if (DEBUG) Component.log("Component.bindComponentControls(" + component.toString() + "): unrecognized control class \"" + sClass + "\"", "warning");
break;
}
}
}
}
}
/**
* Component.getComponents(idRelated)
*
@ -2640,73 +2756,6 @@ class Component {
return parms;
}
/**
* Component.bindExternalControl(component, sControl, sBinding, sType)
*
* @param {Component} component
* @param {string} sControl
* @param {string} sBinding
* @param {string} [sType] is the external component type
*/
static bindExternalControl(component, sControl, sBinding, sType)
{
if (sControl) {
if (sType === undefined) sType = "Panel";
var target = Component.getComponentByType(sType, component.id);
if (target) {
var eBinding = target.bindings[sControl];
if (eBinding) {
component.setBinding(null, sBinding, eBinding);
}
}
}
}
/**
* Component.bindComponentControls(component, element, sAppClass)
*
* @param {Component} component
* @param {HTMLElement} element from the DOM
* @param {string} sAppClass
*/
static bindComponentControls(component, element, sAppClass)
{
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 !== 1 /* document.ELEMENT_NODE */) {
continue;
}
var sClass = control.getAttribute("class");
if (!sClass) continue;
var aClasses = sClass.split(" ");
for (var iClass = 0; iClass < aClasses.length; iClass++) {
var parms;
sClass = aClasses[iClass];
switch (sClass) {
case sAppClass + "-binding":
parms = Component.getComponentParms(control);
if (parms && parms['binding']) {
component.setBinding(parms['type'], parms['binding'], control, parms['value']);
} else if (!parms || parms['type'] != "description") {
Component.log("Component '" + component.toString() + "' missing binding" + (parms? " for " + parms['type'] : ""), "warning");
}
iClass = aClasses.length;
break;
default:
// if (DEBUG) Component.log("Component.bindComponentControls(" + component.toString() + "): unrecognized control class \"" + sClass + "\"", "warning");
break;
}
}
}
}
}
/**
* Component.getElementsByClass(element, sClass, sObjClass)
*
@ -3007,7 +3056,7 @@ class Component {
*
* @this {Component}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "reset")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, 'print')
* @param {HTMLElement} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
@ -3015,11 +3064,11 @@ class Component {
setBinding(sHTMLType, sBinding, control, sValue)
{
switch (sBinding) {
case "clear":
case 'clear':
if (!this.bindings[sBinding]) {
this.bindings[sBinding] = control;
control.onclick = (function(component) {
return function clearPanel() {
return function clearControl() {
if (component.bindings['print']) {
component.bindings['print'].value = "";
}
@ -3027,36 +3076,12 @@ class Component {
}(this));
}
return true;
case "print":
case 'print':
if (!this.bindings[sBinding]) {
this.bindings[sBinding] = control;
/*
* HACK: Save this particular HTML element so that the Debugger can access it, too
*/
this.controlPrint = control;
/*
* This was added for Firefox (Safari automatically clears the <textarea> on a page reload,
* but Firefox does not).
*/
control.value = "";
this.println = (function(control) {
return function printPanel(s, type) {
s = (type !== undefined? (type + ": ") : "") + (s || "");
/*
* Prevent the <textarea> from getting too large; otherwise, printing becomes slower and slower.
*/
if (COMPILED) {
if (control.value.length > 8192) {
control.value = control.value.substr(control.value.length - 4096);
}
}
control.value += s + "\n";
control.scrollTop = control.scrollHeight;
if (!COMPILED && window && window.console) console.log(s);
};
}(control));
var controlTextArea = /** @type {HTMLTextAreaElement} */ (control);
this.bindings[sBinding] = controlTextArea;
/**
* Override this.notice() with a replacement function that eliminates the Component.alertUser() call
* Override this.notice() with a replacement function that eliminates the Component.alertUser() call.
*
* @this {Component}
* @param {string} s
@ -3064,10 +3089,28 @@ class Component {
* @param {string} [id]
* @return {boolean}
*/
this.notice = function noticePanel(s, fPrintOnly, id) {
this.notice = function noticeControl(s, fPrintOnly, id) {
this.println(s, this.idComponent);
return true;
};
/*
* This was added for Firefox (Safari will clear the <textarea> on a page reload, but Firefox does not).
*/
controlTextArea.value = "";
this.print = function(control) {
return function printControl(s) {
Component.appendControl(control, s);
};
}(controlTextArea);
this.println = function(component, control) {
return function printlnControl(s, type, id) {
if (DEBUG || type != Component.TYPE.PROGRESS) {
s = (type != null? (type + ": ") : "") + (s || "");
component.print(s + '\n');
}
if (!COMPILED && window && window.console) Component.println(s, type, id);
};
}(this, controlTextArea);
}
return true;
default:
@ -3146,7 +3189,28 @@ class Component {
}
/**
* println(s, type)
* print(s)
*
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
*
* Components using this.print() should wait until after their constructor has run to display any messages, because
* if a Control Panel has been loaded, its override will not take effect until its own constructor has run.
*
* @this {Component}
* @param {string} s
*/
print(s)
{
var i = s.lastIndexOf('\n');
if (i >= 0) {
Component.println(s.substr(0, i));
s = s.substr(i + 1);
}
Component.printBuffer += s;
}
/**
* println(s, type, id)
*
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
*
@ -3160,7 +3224,8 @@ class Component {
*/
println(s, type, id)
{
Component.println(s, type, id || this.id);
Component.println(Component.printBuffer + s, type, id || this.id);
Component.printBuffer = "";
}
/**
@ -3173,11 +3238,11 @@ class Component {
*/
status(s)
{
this.println(this.idComponent + ": " + s);
this.println(this.type + ": " + s);
}
/**
* notice(s, fPrintOnly)
* notice(s, fPrintOnly, id)
*
* notice() is like println() but implies a need for user notification, so we alert() as well; however, if this.println()
* is overridden, this.notice will be replaced with a similar override, on the assumption that the override is taking care
@ -3437,6 +3502,18 @@ class Component {
}
}
/*
* These are the standard TYPE values you can pass as the second argument to println(); in reality,
* you can pass anything you want, because they are just tacked onto the message as a prefix, with the
* exception of PROGRESS, which will suppress the message unless we're in DEBUG or INIT mode.
*/
Component.TYPE = {
NOTICE: "notice",
WARNING: "warning",
ERROR: "error",
PROGRESS: "progress"
};
/*
* Every component created on the current page is recorded in this array (see Component.add()),
* enabling any component to locate another component by ID (see Component.getComponentByID())
@ -3469,6 +3546,7 @@ Component.globalCommands = {
Component.componentCommands = {
'select': Component.scriptSelect
};
Component.printBuffer = "";
/*
* The following polyfills provide ES5 functionality that's missing in older browsers (eg, IE8),
@ -13688,14 +13766,15 @@ class C1PComputer extends Component {
/*
* Iterate through all the other components and update their print methods if the Control Panel has provided overrides.
*/
if (panel.controlPrint) {
var controlPrint = panel.bindings['print'];
if (controlPrint) {
var aComponents = Component.getComponents(parmsComputer['id']);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component == panel) continue;
component.notice = panel.notice;
component.print = panel.print;
component.println = panel.println;
component.controlPrint = panel.controlPrint;
}
}
}