1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-11 23:52:46 +00:00
pkimpel.retro-220/webUI/B220SystemConfig.js

572 lines
20 KiB
JavaScript

/***********************************************************************
* retro-220/webUI B220SystemConfig.js
************************************************************************
* Copyright (c) 2017, Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* Burroughs 220 Emulator System Configuration management object.
*
* Defines the system configuration used internally by the emulator and the
* methods used to manage that configuration data.
*
************************************************************************
* 2017-01-01 P.Kimpel
* Original version, from retro-205 webUI/D205SystemConfig.js.
***********************************************************************/
"use strict";
/**************************************/
function B220SystemConfig() {
/* Constructor for the SystemConfig configuration management object */
var s;
this.flushTimer = 0; // timer token for flushing configuration to localStorage
this.window = null; // configuration UI window object
this.alertWin = window; // current base window for alert/confirm/prompt
// Load or create the system configuration data
s = localStorage.getItem(this.configStorageName);
if (!s) {
this.createConfigData();
} else {
this.loadConfigData(s);
}
}
/**************************************/
B220SystemConfig.prototype.configStorageName = "retro-220-Config";
B220SystemConfig.prototype.configVersion = 1;
B220SystemConfig.prototype.flushDelay = 60000; // flush timer setting, ms
B220SystemConfig.defaultConfig = {
version: this.configVersion,
memorySize: 5000, // 11-digit words
ControlConsole: {
PCS1SW: 0, // Program Control Switches 1-0
PCS2SW: 0,
PCS3SW: 0,
PCS4SW: 0,
PCS5SW: 0,
PCW6SW: 0,
PCS7SW: 0,
PCS8SW: 0,
PCS9SW: 0,
PCS0SW: 0,
SONSW: 0, // S-register on
SUNITSSW: 0, // S-register units
STOCSW: 0, // S-to-C stop
STOPSW: 0}, // S-to-P stop
ConsoleInput: {
units: [
null, // unit[0] not used
{type: "PTRA", unitMask: 0x002, remote: 0, speed: 0},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"}
]},
ConsoleOutput: {
units: [
{type: "TTYA", unitMask: 0x001, remote: 1, format: 0, zeroSuppress: 0, mapMemory: 0, printerSpeed: 0,
columns: 72, tabs: "9,17,25,33,41,49,57,65,73,81"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"}
]},
Cardatron: {
hasCardatron: true,
units: [
null, // unit[0] not used
{type: "CR1", formatSelect: 0, formatCol: 1},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "LP2", algolGlyphs: true, greenBar: true, zeroSuppressCols: ""},
{type: "CP1", algolGlyphs: true, greenBar: false, zeroSuppressCols: ""}
]},
MagTape: {
hasMagTape: true,
units: [
null, // unit[0] not used
{type: "MTA", designate: 0},
{type: "MTB", designate: 1},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"},
{type: "NONE"}
]}
};
/**************************************/
B220SystemConfig.prototype.$$ = function $$(id) {
return this.doc.getElementById(id);
}
/**************************************/
B220SystemConfig.prototype.createConfigData = function createConfigData() {
/* Creates and initializes a new configuration data object and stores it in
localStorage. If former state preference objects exist, these are merged into
the new data object and then deleted */
var pref;
var prefs;
var s;
this.configData = B220SystemConfig.defaultConfig;
this.flushHandler();
};
/**************************************/
B220SystemConfig.prototype.loadConfigData = function loadConfigData(jsonConfig) {
/* Attempts to parse the JSON configuration data string and store it in
this.configData. If the parse is unsuccessful, recreates the default configuration.
Applies any necessary updates to older configurations */
try {
this.configData = JSON.parse(jsonConfig);
} catch (e) {
alert("Could not parse system configuration data:\n" +
e.message + "\nReinitializing configuration");
this.createConfigData();
}
// Apply updates
if (this.getNode("Cardatron.hasCardatron") === undefined) {
this.putNode("Cardatron.hasCardatron", true);
}
if (this.getNode("MagTape.hasMagTape") === undefined) {
this.putNode("MagTape.hasMagTape", true);
}
};
/**************************************/
B220SystemConfig.prototype.flushHandler = function flushHandler() {
/* Callback function for the flush timer. Stores the configuration data */
this.flushTimer = 0;
localStorage.setItem(this.configStorageName, JSON.stringify(this.configData));
};
/*************************************/
B220SystemConfig.prototype.flush = function flush() {
/* If the current configuration data object has been modified, stores it to
localStorage and resets the flush timer */
if (this.flushTimer) {
clearCallback(this.flushTimer);
this.flushHandler();
}
};
/**************************************/
B220SystemConfig.prototype.getNode = function getNode(nodeName, index) {
/* Retrieves a specified node of the configuration data object tree.
"nodeName" specifies the node using dotted-path format. A blank name
retrieves the entire tree. If the "index" parameter is specified, the final
node in the path is assumed to be an array, and "index" used to return
that element of the array. If a node does not exist, returns undefined */
var name;
var names;
var node = this.configData;
var top;
var x;
name = nodeName.trim();
if (name.length > 0) {
names = name.split(".");
top = names.length;
for (x=0; x<top; ++x) {
name = names[x];
if (name in node) {
node = node[name];
} else {
node = undefined;
break; // out of for loop
}
} // for x
}
if (index === undefined) {
return node;
} else {
return node[index];
}
};
/**************************************/
B220SystemConfig.prototype.putNode = function putNode(nodeName, data, index) {
/* Creates or replace a specified node of the configuration data object tree.
"nodeName" specifies the node using dotted.path format. A blank name
results in nothing being set. If a node does not exist, it and any necessary
parent nodes are created. If the "index" parameter is specified, the final
node in the path is assumed to be an array, and "index" is used to access
that element of the array. Setting the value of a node starts a timer (if it
is not already started). When that timer expires, the configuration data will
be flushed to the localStorage object. This delayed storage is done so that
several configuration changes into short order can be grouped in one flush */
var name;
var names;
var node = this.configData;
var top;
var x;
name = nodeName.trim();
if (name.length > 0) {
names = name.split(".");
top = names.length-1;
for (x=0; x<top; ++x) {
name = names[x];
if (name in node) {
node = node[name];
} else {
node = node[name] = {};
}
} // for x
if (index === undefined) {
node[names[top]] = data;
} else {
node[names[top]][index] = data;
}
if (!this.flushTimer) {
this.flushTimer = setCallback("CONFIG", this, this.flushTimeout, this.flushHandler);
}
}
};
/***********************************************************************
* System Configuration UI Support *
***********************************************************************/
/**************************************/
B220SystemConfig.prototype.setListValue = function setListValue(id, value) {
/* Sets the selection of the <select> list with the specified "id" to the
entry with the specified "value". If no such value exists, the list
selection is not changed */
var e = this.$$(id);
var opt;
var x;
if (e && e.tagName == "SELECT") {
opt = e.options;
for (x=0; x<opt.length; ++x) {
if (opt[x].value == value) {
e.selectedIndex = x;
break; // out of for loop
}
} // for x
}
};
/**************************************/
B220SystemConfig.prototype.loadConfigDialog = function loadConfigDialog() {
/* Loads the configuration UI window with the settings from this.configData */
var cd = this.configData; // local configuration reference
var mask; // unit mask bits
var prefix; // unit id prefix
var unit; // unit configuration object
var x; // unit index
var y; // secondary index
// System Properties
this.setListValue("SystemMemorySize", cd.memorySize.toString());
// Console Input units
if (!cd.ConsoleInput) {
cd.ConsoleInput = B220SystemConfig.defaultConfig.ConsoleInput;
}
for (x=1; x<=10; ++x) {
unit = cd.ConsoleInput.units[x];
prefix = "ConsoleIn" + x;
this.setListValue(prefix + "Type", unit.type);
mask = 0x001;
for (y=1; y<=10; ++y) {
mask <<= 1;
this.$$(prefix + "_" + y).checked = (unit.unitMask & mask ? true : false);
} // for y
} // for x
// Console Output units
if (!cd.ConsoleOutput) {
cd.ConsoleOutput = B220SystemConfig.defaultConfig.ConsoleOutput;
}
for (x=0; x<=10; ++x) {
unit = cd.ConsoleOutput.units[x];
prefix = "ConsoleOut" + x;
this.setListValue(prefix + "Type", unit.type);
mask = 0x001;
this.$$(prefix + "_SPO").checked = (unit.unitMask & mask ? true : false);
for (y=1; y<=10; ++y) {
mask <<= 1;
this.$$(prefix + "_" + y).checked = (unit.unitMask & mask ? true : false);
} // for y
this.setListValue(prefix + "Format", unit.format);
this.$$(prefix + "Columns").textContent = (unit.columns ? unit.columns : 72);
this.$$(prefix + "Tabs").textContent = (unit.tabs ? unit.tabs : "");
} // for x
// Cardatron units
if (!cd.Cardatron) {
cd.Cardatron = B220SystemConfig.defaultConfig.Cardatron;
}
for (x=1; x<=7; ++x) {
unit = cd.Cardatron.units[x];
prefix = "Cardatron" + x;
this.setListValue(prefix + "Type", unit.type);
switch (unit.type.substring(0, 2)) {
case "LP":
this.$$(prefix + "AlgolGlyphs").checked = unit.algolGlyphs;
this.$$(prefix + "Greenbar").checked = unit.greenBar;
this.$$(prefix + "ZeroSuppressCols").textContent = unit.zeroSuppressCols || "";
break;
case "CP":
this.$$(prefix + "AlgolGlyphs").checked = unit.algolGlyphs;
this.$$(prefix + "Greenbar").checked = false;
this.$$(prefix + "ZeroSuppressCols").textContent = unit.zeroSuppressCols || "";
break;
case "CR":
default:
this.$$(prefix + "AlgolGlyphs").checked = false;
this.$$(prefix + "Greenbar").checked = false;
this.$$(prefix + "ZeroSuppressCols").textContent = "";
break;
} // switch unit.type
} // for x
// Magnetic Tape units
if (!cd.MagTape) {
cd.MagTape = B220SystemConfig.defaultConfig.MagTape;
}
for (x=1; x<=10; ++x) {
unit = cd.MagTape.units[x];
prefix = "MagTape" + x;
this.setListValue(prefix + "Type", unit.type);
this.$$(prefix + "Designate").selectedIndex = unit.designate-1;
} // for x
this.$$("MessageArea").textContent = "220 System Configuration loaded.";
this.window.focus();
};
/**************************************/
B220SystemConfig.prototype.saveConfigDialog = function saveConfigDialog() {
/* Saves the configuration UI window settings to this.configData and flushes
the updated configuration to localStorage */
var cd = this.configData; // local configuration reference
var e; // local element reference
var mask; // unit mask
var prefix; // unit id prefix
var unit; // unit configuration object
var x; // unit index
var y; // secondary index
function getNumber(id, caption, min, max) {
var n;
var text = this.$$(id).value;
n = parseInt(text, 10);
if (isNaN(n)) {
alert(caption + " must be numeric");
} else if (n < min || n > max) {
alert(caption + " must be in the range (" + min + ", " + max + ")");
n = Number.NaN;
}
return n;
}
// System Properties
e = this.$$("SystemMemorySize");
x = parseInt(e.options[e.selectedIndex].value, 10);
cd.memorySize = (isNaN(x) ? 5000 : x);
// Console Input units
for (x=1; x<=10; ++x) {
unit = cd.ConsoleInput.units[x];
prefix = "ConsoleIn" + x;
e = this.$$(prefix + "Type");
unit.type = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
mask = 0x001;
unit.unitMask = 0;
for (y=1; y<=10; ++y) {
mask <<= 1;
if (this.$$(prefix + "_" + y).checked) {
unit.unitMask |= mask;
}
} // for y
if (unit.type != "NONE") {
unit.remote = (unit.remote || 0);
unit.speed = (unit.speed || 0);
}
} // for x
// Console Output units
for (x=0; x<=10; ++x) {
unit = cd.ConsoleOutput.units[x];
prefix = "ConsoleOut" + x;
e = this.$$(prefix + "Type");
unit.type = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
mask = 0x001;
unit.unitMask = 0;
if (this.$$(prefix + "_SPO").checked) {
unit.unitMask |= mask;
}
for (y=1; y<=10; ++y) {
mask <<= 1;
if (this.$$(prefix + "_" + y).checked) {
unit.unitMask |= mask;
}
} // for y
if (unit.type != "NONE") {
unit.remote = (unit.remote || 0);
unit.zeroSuppress = (unit.zeroSuppress || 0);
unit.mapMemory = (unit.mapMemory || 0);
unit.printerSpeed = (unit.printerSpeed || 0);
e = this.$$(prefix + "Format");
unit.format = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
unit.columns = (unit.columns ? unit.columns : 72);
unit.tabs = (unit.tabs ? unit.tabs : "1,9,17,25,33,41,49,57,65,73");
}
} // for x
// Cardatron units
cd.Cardatron.hasCardatron = false;
for (x=1; x<=7; ++x) {
unit = cd.Cardatron.units[x];
prefix = "Cardatron" + x;
e = this.$$(prefix + "Type");
unit.type = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
switch (unit.type.substring(0, 2)) {
case "LP":
cd.Cardatron.hasCardatron = true;
unit.algolGlyphs = this.$$(prefix + "AlgolGlyphs").checked;
unit.greenBar = this.$$(prefix + "Greenbar").checked;
unit.zeroSuppressCols = (unit.zeroSuppressCols || "");
break;
case "CP":
cd.Cardatron.hasCardatron = true;
unit.algolGlyphs = this.$$(prefix + "AlgolGlyphs").checked;
unit.greenBar = false;
unit.zeroSuppressCols = (unit.zeroSuppressCols || "");
break;
case "CR":
cd.Cardatron.hasCardatron = true;
unit.formatSelect = (unit.formatSelect || 0);
unit.formatCol = (unit.formatCol === undefined ? 1 : unit.formatCol)
// no break
default:
unit.algolGlyphs = false;
unit.greenBar = false;
break;
} // switch unit.type
} // for x
// Magnetic Tape units
cd.MagTape.hasMagTape = false;
for (x=1; x<=10; ++x) {
unit = cd.MagTape.units[x];
prefix = "MagTape" + x;
e = this.$$(prefix + "Type");
unit.type = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value);
unit.designate = this.$$(prefix + "Designate").selectedIndex+1;
if (unit.type != "NONE") {
cd.MagTape.hasMagTape = true;
}
} // for x
this.flushHandler(); // store the configuration
this.$$("MessageArea").textContent = "220 System Configuration updated.";
this.window.close();
};
/**************************************/
B220SystemConfig.prototype.closeConfigUI = function closeConfigUI() {
/* Closes the system configuration update dialog */
this.alertWin = window; // revert alerts to the global window
window.focus();
if (this.window) {
if (!this.window.closed) {
this.window.close();
}
this.window = null;
}
}
/**************************************/
B220SystemConfig.prototype.openConfigUI = function openConfigUI() {
/* Opens the system configuration update dialog and displays the current
system configuration */
function configUI_Load(ev) {
this.doc = ev.target;
this.window = this.doc.defaultView;
this.window.moveTo(screen.availWidth-this.window.outerWidth-40,
(screen.availHeight-this.window.outerHeight)/2);
this.window.focus();
this.alertWin = this.window;
this.$$("SaveBtn").addEventListener("click",
this.saveConfigDialog.bind(this), false);
this.$$("CancelBtn").addEventListener("click",
function(ev) {this.window.close()}.bind(this), false);
this.$$("DefaultsBtn").addEventListener("click",
function(ev) {
this.createConfigData();
this.loadConfigDialog();
}.bind(this), false);
this.window.addEventListener("unload",
this.closeConfigUI.bind(this), false);
this.loadConfigDialog();
}
this.doc = null;
this.window = null;
B220Util.openPopup(window, "../webUI/B220SystemConfig.html", this.configStorageName,
"location=no,scrollbars,resizable,width=800,height=800",
this, configUI_Load);
};