pxt-calliope/pxtsim.js
Matthias L. Jugel b028b1df5b Auto-push
2017-08-19 17:16:35 +02:00

6881 lines
297 KiB
JavaScript

var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var pxsim;
(function (pxsim) {
var GROUND_COLOR = "blue";
var POWER_COLOR = "red";
;
;
;
;
;
;
function isOnBreadboardBottom(location) {
var isBot = false;
if (typeof location !== "string" && location.type === "breadboard") {
var bbLoc = location;
var row = bbLoc.row;
isBot = 0 <= ["a", "b", "c", "d", "e"].indexOf(row);
}
return isBot;
}
var arrCount = function (a) { return a.reduce(function (p, n) { return p + (n ? 1 : 0); }, 0); };
var arrAny = function (a) { return arrCount(a) > 0; };
function computePowerUsage(wire) {
var ends = [wire.start, wire.end];
var endIsGround = ends.map(function (e) { return e === "ground"; });
var endIsThreeVolt = ends.map(function (e) { return e === "threeVolt"; });
var endIsBot = ends.map(function (e) { return isOnBreadboardBottom(e); });
var hasGround = arrAny(endIsGround);
var hasThreeVolt = arrAny(endIsThreeVolt);
var hasBot = arrAny(endIsBot);
return {
topGround: hasGround && !hasBot,
topThreeVolt: hasThreeVolt && !hasBot,
bottomGround: hasGround && hasBot,
bottomThreeVolt: hasThreeVolt && hasBot,
singleGround: hasGround,
singleThreeVolt: hasThreeVolt
};
}
function mergePowerUsage(powerUsages) {
var finalPowerUsage = powerUsages.reduce(function (p, n) { return ({
topGround: p.topGround || n.topGround,
topThreeVolt: p.topThreeVolt || n.topThreeVolt,
bottomGround: p.bottomGround || n.bottomGround,
bottomThreeVolt: p.bottomThreeVolt || n.bottomThreeVolt,
singleGround: n.singleGround ? p.singleGround === null : p.singleGround,
singleThreeVolt: n.singleThreeVolt ? p.singleThreeVolt === null : p.singleThreeVolt,
}); }, {
topGround: false,
topThreeVolt: false,
bottomGround: false,
bottomThreeVolt: false,
singleGround: null,
singleThreeVolt: null,
});
if (finalPowerUsage.singleGround)
finalPowerUsage.topGround = finalPowerUsage.bottomGround = false;
if (finalPowerUsage.singleThreeVolt)
finalPowerUsage.topThreeVolt = finalPowerUsage.bottomThreeVolt = false;
return finalPowerUsage;
}
function copyDoubleArray(a) {
return a.map(function (b) { return b.map(function (p) { return p; }); });
}
function merge2(a, b) {
var res = {};
for (var aKey in a)
res[aKey] = a[aKey];
for (var bKey in b)
res[bKey] = b[bKey];
return res;
}
function merge3(a, b, c) {
return merge2(merge2(a, b), c);
}
function readPin(arg) {
pxsim.U.assert(!!arg, "Invalid pin: " + arg);
var pin = /^([A-Z]\w+)Pin\.(P\d+)$/.exec(arg);
return pin ? pin[2] : undefined;
}
pxsim.readPin = readPin;
function mkReverseMap(map) {
var origKeys = [];
var origVals = [];
for (var key in map) {
origKeys.push(key);
origVals.push(map[key]);
}
var newMap = {};
for (var i = 0; i < origKeys.length; i++) {
var newKey = origVals[i];
var newVal = origKeys[i];
newMap[newKey] = newVal;
}
return newMap;
}
function isConnectedToBB(pin) {
return pin.orientation === "-Z" && pin.style === "male";
}
var Allocator = (function () {
function Allocator(opts) {
this.availablePowerPins = {
top: {
threeVolt: pxsim.mkRange(26, 51).map(function (n) { return { type: "breadboard", row: "+", col: "" + n }; }),
ground: pxsim.mkRange(26, 51).map(function (n) { return { type: "breadboard", row: "-", col: "" + n }; }),
},
bottom: {
threeVolt: pxsim.mkRange(1, 26).map(function (n) { return { type: "breadboard", row: "+", col: "" + n }; }),
ground: pxsim.mkRange(1, 26).map(function (n) { return { type: "breadboard", row: "-", col: "" + n }; }),
},
};
this.opts = opts;
}
Allocator.prototype.allocPartIRs = function (def, name, bbFit) {
var partIRs = [];
var mkIR = function (def, name, instPins, partParams) {
var pinIRs = [];
for (var i = 0; i < def.numberOfPins; i++) {
var pinDef = def.pinDefinitions[i];
var pinTarget = void 0;
if (typeof pinDef.target === "string") {
pinTarget = pinDef.target;
}
else {
var instIdx = pinDef.target.pinInstantiationIdx;
pxsim.U.assert(!!instPins && instPins[instIdx] !== undefined, "No pin found for PinInstantiationIdx: " + instIdx + ". (Is the part missing an ArguementRole or \"trackArgs=\" annotations?)");
pinTarget = instPins[instIdx];
}
var pinLoc = def.visual.pinLocations[i];
var adjustedY = bbFit.yOffset + pinLoc.y;
var relativeRowIdx = Math.round(adjustedY / def.visual.pinDistance);
var relativeYOffset = adjustedY - relativeRowIdx * def.visual.pinDistance;
var adjustedX = bbFit.xOffset + pinLoc.x;
var relativeColIdx = Math.round(adjustedX / def.visual.pinDistance);
var relativeXOffset = adjustedX - relativeColIdx * def.visual.pinDistance;
var pinBBFit = {
partRelativeRowIdx: relativeRowIdx,
partRelativeColIdx: relativeColIdx,
xOffset: relativeXOffset,
yOffset: relativeYOffset
};
pinIRs.push({
def: pinDef,
loc: pinLoc,
target: pinTarget,
bbFit: pinBBFit,
});
}
return {
name: name,
def: def,
pins: pinIRs,
partParams: partParams || {},
bbFit: bbFit
};
};
if (def.instantiation.kind === "singleton") {
partIRs.push(mkIR(def, name));
}
else if (def.instantiation.kind === "function") {
var fnAlloc_1 = def.instantiation;
var fnNm_1 = fnAlloc_1.fullyQualifiedName;
var callsitesTrackedArgs = this.opts.fnArgs[fnNm_1];
pxsim.U.assert(!!callsitesTrackedArgs && !!callsitesTrackedArgs.length, "Failed to read pin(s) from callsite for: " + fnNm_1);
callsitesTrackedArgs.forEach(function (fnArgsStr) {
var fnArgsSplit = fnArgsStr.split(",");
pxsim.U.assert(fnArgsSplit.length === fnAlloc_1.argumentRoles.length, "Mismatch between number of arguments at callsite (function name: " + fnNm_1 + ") vs number of argument roles in part definition (part: " + name + ").");
var instPins = [];
var paramArgs = {};
fnArgsSplit.forEach(function (arg, idx) {
var role = fnAlloc_1.argumentRoles[idx];
if (role.partParameter !== undefined) {
paramArgs[role.partParameter] = arg;
}
if (role.pinInstantiationIdx !== undefined) {
var instIdx = role.pinInstantiationIdx;
var pin = readPin(arg);
instPins[instIdx] = pin;
}
});
partIRs.push(mkIR(def, name, instPins, paramArgs));
});
}
return partIRs;
};
Allocator.prototype.computePartDimensions = function (def, name) {
var pinLocs = def.visual.pinLocations;
var pinDefs = def.pinDefinitions;
var numPins = def.numberOfPins;
pxsim.U.assert(pinLocs.length === numPins, "Mismatch between \"numberOfPins\" and length of \"visual.pinLocations\" for \"" + name + "\"");
pxsim.U.assert(pinDefs.length === numPins, "Mismatch between \"numberOfPins\" and length of \"pinDefinitions\" for \"" + name + "\"");
pxsim.U.assert(numPins > 0, "Part \"" + name + "\" has no pins");
var pins = pinLocs.map(function (loc, idx) { return merge3({ idx: idx }, loc, pinDefs[idx]); });
var bbPins = pins.filter(function (p) { return p.orientation === "-Z"; });
var hasBBPins = bbPins.length > 0;
var pinDist = def.visual.pinDistance;
var xOff;
var yOff;
var colCount;
var rowCount;
if (hasBBPins) {
var refPin = bbPins[0];
var refPinColIdx = Math.ceil(refPin.x / pinDist);
var refPinRowIdx = Math.ceil(refPin.y / pinDist);
xOff = refPinColIdx * pinDist - refPin.x;
yOff = refPinRowIdx * pinDist - refPin.y;
colCount = Math.ceil((xOff + def.visual.width) / pinDist) + 1;
rowCount = Math.ceil((yOff + def.visual.height) / pinDist) + 1;
}
else {
colCount = Math.ceil(def.visual.width / pinDist);
rowCount = Math.ceil(def.visual.height / pinDist);
xOff = colCount * pinDist - def.visual.width;
yOff = rowCount * pinDist - def.visual.height;
}
return {
xOffset: xOff,
yOffset: yOff,
rowCount: rowCount,
colCount: colCount
};
};
Allocator.prototype.allocColumns = function (colCounts) {
var partsCount = colCounts.length;
var totalColumnsCount = pxsim.visuals.BREADBOARD_MID_COLS; //TODO allow multiple breadboards
var totalSpaceNeeded = colCounts.map(function (d) { return d.colCount; }).reduce(function (p, n) { return p + n; }, 0);
var extraSpace = totalColumnsCount - totalSpaceNeeded;
if (extraSpace <= 0) {
console.log("Not enough breadboard space!");
}
var padding = Math.floor(extraSpace / (partsCount - 1 + 2));
var partSpacing = padding; //Math.floor(extraSpace/(partsCount-1));
var totalPartPadding = extraSpace - partSpacing * (partsCount - 1);
var leftPadding = Math.floor(totalPartPadding / 2);
var rightPadding = Math.ceil(totalPartPadding / 2);
var nextAvailableCol = 1 + leftPadding;
var partStartCol = colCounts.map(function (part) {
var col = nextAvailableCol;
nextAvailableCol += part.colCount + partSpacing;
return col;
});
return partStartCol;
};
Allocator.prototype.placeParts = function (parts) {
var totalRowsCount = pxsim.visuals.BREADBOARD_MID_ROWS + 2; // 10 letters + 2 for the middle gap
var startColumnIndices = this.allocColumns(parts.map(function (p) { return p.bbFit; }));
var startRowIndicies = parts.map(function (p) {
var extraRows = totalRowsCount - p.bbFit.rowCount;
var topPad = Math.floor(extraRows / 2);
var startIdx = topPad;
if (startIdx > 4)
startIdx = 4;
if (startIdx < 1)
startIdx = 1;
return startIdx;
});
var placements = parts.map(function (p, idx) {
var row = startRowIndicies[idx];
var col = startColumnIndices[idx];
return merge2({ startColumnIdx: col, startRowIdx: row }, p);
});
return placements;
};
Allocator.prototype.nextColor = function () {
if (!this.availableWireColors || this.availableWireColors.length <= 0) {
this.availableWireColors = pxsim.visuals.GPIO_WIRE_COLORS.map(function (c) { return c; });
}
return this.availableWireColors.pop();
};
Allocator.prototype.allocWireIRs = function (part) {
var _this = this;
var groupToColor = [];
var wires = part.pins.map(function (pin, pinIdx) {
var end = pin.target;
var start;
var colIdx = part.startColumnIdx + pin.bbFit.partRelativeColIdx;
var colName = pxsim.visuals.getColumnName(colIdx);
var pinRowIdx = part.startRowIdx + pin.bbFit.partRelativeRowIdx;
if (pinRowIdx >= 7)
pinRowIdx -= 2;
if (isConnectedToBB(pin.def)) {
//make a wire from bb top or bottom to target
var connectedToTop = pinRowIdx < 5;
var rowName = connectedToTop ? "j" : "a";
start = {
type: "breadboard",
row: rowName,
col: colName,
style: pin.def.style
};
}
else {
//make a wire directly from pin to target
var rowName = pxsim.visuals.getRowName(pinRowIdx);
start = {
type: "breadboard",
row: rowName,
col: colName,
xOffset: pin.bbFit.xOffset / part.def.visual.pinDistance,
yOffset: pin.bbFit.yOffset / part.def.visual.pinDistance,
style: pin.def.style
};
}
var color;
if (end === "ground") {
color = GROUND_COLOR;
}
else if (end === "threeVolt") {
color = POWER_COLOR;
}
else if (typeof pin.def.colorGroup === "number") {
if (groupToColor[pin.def.colorGroup]) {
color = groupToColor[pin.def.colorGroup];
}
else {
color = groupToColor[pin.def.colorGroup] = _this.nextColor();
}
}
else {
color = _this.nextColor();
}
return {
start: start,
end: end,
color: color,
pinIdx: pinIdx,
};
});
return merge2(part, { wires: wires });
};
Allocator.prototype.allocLocation = function (location, opts) {
var _this = this;
if (location === "ground" || location === "threeVolt") {
//special case if there is only a single ground or three volt pin in the whole build
if (location === "ground" && this.powerUsage.singleGround) {
var boardGroundPin = this.getBoardGroundPin();
return { type: "dalboard", pin: boardGroundPin };
}
else if (location === "threeVolt" && this.powerUsage.singleThreeVolt) {
var boardThreeVoltPin = this.getBoardThreeVoltPin();
return { type: "dalboard", pin: boardThreeVoltPin };
}
pxsim.U.assert(!!opts.referenceBBPin);
var nearestCoord = this.opts.getBBCoord(opts.referenceBBPin);
var firstTopAndBot = [
this.availablePowerPins.top.ground[0] || this.availablePowerPins.top.threeVolt[0],
this.availablePowerPins.bottom.ground[0] || this.availablePowerPins.bottom.threeVolt[0]
].map(function (loc) {
return _this.opts.getBBCoord(loc);
});
if (!firstTopAndBot[0] || !firstTopAndBot[1]) {
console.debug("No more available \"" + location + "\" locations!");
}
var nearTop = pxsim.visuals.findClosestCoordIdx(nearestCoord, firstTopAndBot) == 0;
var barPins = void 0;
if (nearTop) {
if (location === "ground") {
barPins = this.availablePowerPins.top.ground;
}
else if (location === "threeVolt") {
barPins = this.availablePowerPins.top.threeVolt;
}
}
else {
if (location === "ground") {
barPins = this.availablePowerPins.bottom.ground;
}
else if (location === "threeVolt") {
barPins = this.availablePowerPins.bottom.threeVolt;
}
}
var pinCoords = barPins.map(function (rowCol) {
return _this.opts.getBBCoord(rowCol);
});
var closestPinIdx = pxsim.visuals.findClosestCoordIdx(nearestCoord, pinCoords);
var pin = barPins[closestPinIdx];
if (nearTop) {
this.availablePowerPins.top.ground.splice(closestPinIdx, 1);
this.availablePowerPins.top.threeVolt.splice(closestPinIdx, 1);
}
else {
this.availablePowerPins.bottom.ground.splice(closestPinIdx, 1);
this.availablePowerPins.bottom.threeVolt.splice(closestPinIdx, 1);
}
return pin;
}
else if (location.type === "breadboard") {
return location;
}
else if (location === "MOSI" || location === "MISO" || location === "SCK") {
if (!this.opts.boardDef.spiPins)
console.debug("No SPI pin mappings found!");
var pin = this.opts.boardDef.spiPins[location];
return { type: "dalboard", pin: pin };
}
else if (location === "SDA" || location === "SCL") {
if (!this.opts.boardDef.i2cPins)
console.debug("No I2C pin mappings found!");
var pin = this.opts.boardDef.i2cPins[location];
return { type: "dalboard", pin: pin };
}
else {
//it must be a MicrobitPin
pxsim.U.assert(typeof location === "string", "Unknown location type: " + location);
var mbPin = location;
var boardPin = this.opts.boardDef.gpioPinMap[mbPin];
pxsim.U.assert(!!boardPin, "Unknown pin: " + location);
return { type: "dalboard", pin: boardPin };
}
};
Allocator.prototype.getBoardGroundPin = function () {
var boardGround = this.opts.boardDef.groundPins[0] || null;
if (!boardGround) {
console.log("No available ground pin on board!");
}
return boardGround;
};
Allocator.prototype.getBoardThreeVoltPin = function () {
var threeVoltPin = this.opts.boardDef.threeVoltPins[0] || null;
if (!threeVoltPin) {
console.log("No available 3.3V pin on board!");
}
return threeVoltPin;
};
Allocator.prototype.allocPowerWires = function (powerUsage) {
var boardGroundPin = this.getBoardGroundPin();
var threeVoltPin = this.getBoardThreeVoltPin();
var topLeft = { type: "breadboard", row: "-", col: "26" };
var botLeft = { type: "breadboard", row: "-", col: "1" };
var topRight = { type: "breadboard", row: "-", col: "50" };
var botRight = { type: "breadboard", row: "-", col: "25" };
var top, bot;
if (this.opts.boardDef.attachPowerOnRight) {
top = topRight;
bot = botRight;
}
else {
top = topLeft;
bot = botLeft;
}
var groundWires = [];
var threeVoltWires = [];
if (powerUsage.bottomGround && powerUsage.topGround) {
//bb top - <==> bb bot -
groundWires.push({
start: this.allocLocation("ground", { referenceBBPin: top }),
end: this.allocLocation("ground", { referenceBBPin: bot }),
color: GROUND_COLOR,
});
}
if (powerUsage.topGround) {
//board - <==> bb top -
groundWires.push({
start: this.allocLocation("ground", { referenceBBPin: top }),
end: { type: "dalboard", pin: boardGroundPin },
color: GROUND_COLOR,
});
}
else if (powerUsage.bottomGround) {
//board - <==> bb bot -
groundWires.push({
start: this.allocLocation("ground", { referenceBBPin: bot }),
end: { type: "dalboard", pin: boardGroundPin },
color: GROUND_COLOR,
});
}
if (powerUsage.bottomThreeVolt && powerUsage.bottomGround) {
//bb top + <==> bb bot +
threeVoltWires.push({
start: this.allocLocation("threeVolt", { referenceBBPin: top }),
end: this.allocLocation("threeVolt", { referenceBBPin: bot }),
color: POWER_COLOR,
});
}
if (powerUsage.topThreeVolt) {
//board + <==> bb top +
threeVoltWires.push({
start: this.allocLocation("threeVolt", { referenceBBPin: top }),
end: { type: "dalboard", pin: threeVoltPin },
color: POWER_COLOR,
});
}
else if (powerUsage.bottomThreeVolt) {
//board + <==> bb bot +
threeVoltWires.push({
start: this.allocLocation("threeVolt", { referenceBBPin: bot }),
end: { type: "dalboard", pin: threeVoltPin },
color: POWER_COLOR,
});
}
var assembly = [];
if (groundWires.length > 0)
assembly.push({ wireIndices: groundWires.map(function (w, i) { return i; }) });
var numGroundWires = groundWires.length;
if (threeVoltWires.length > 0)
assembly.push({ wireIndices: threeVoltWires.map(function (w, i) { return i + numGroundWires; }) });
return {
wires: groundWires.concat(threeVoltWires),
assembly: assembly
};
};
Allocator.prototype.allocWire = function (wireIR) {
var _this = this;
var ends = [wireIR.start, wireIR.end];
var endIsPower = ends.map(function (e) { return e === "ground" || e === "threeVolt"; });
//allocate non-power first so we know the nearest pin for the power end
var endInsts = ends.map(function (e, idx) { return !endIsPower[idx] ? _this.allocLocation(e, {}) : null; });
//allocate power pins closest to the other end of the wire
endInsts = endInsts.map(function (e, idx) {
if (e)
return e;
var locInst = endInsts[1 - idx]; // non-power end
var l = _this.allocLocation(ends[idx], {
referenceBBPin: locInst,
});
return l;
});
return { start: endInsts[0], end: endInsts[1], color: wireIR.color };
};
Allocator.prototype.allocPart = function (ir) {
var bbConnections = ir.pins
.filter(function (p) { return isConnectedToBB(p.def); })
.map(function (p) {
var rowIdx = ir.startRowIdx + p.bbFit.partRelativeRowIdx;
if (rowIdx >= 7)
rowIdx -= 2;
var rowName = pxsim.visuals.getRowName(rowIdx);
var colIdx = ir.startColumnIdx + p.bbFit.partRelativeColIdx;
var colName = pxsim.visuals.getColumnName(colIdx);
return {
type: "breadboard",
row: rowName,
col: colName,
};
});
var part = {
name: ir.name,
visual: ir.def.visual,
bbFit: ir.bbFit,
startColumnIdx: ir.startColumnIdx,
startRowIdx: ir.startRowIdx,
breadboardConnections: bbConnections,
params: ir.partParams,
simulationBehavior: ir.def.simulationBehavior
};
return part;
};
Allocator.prototype.allocAll = function () {
var _this = this;
var partNmAndDefs = this.opts.partsList
.map(function (partName) { return { name: partName, def: _this.opts.partDefs[partName] }; })
.filter(function (d) { return !!d.def; });
if (partNmAndDefs.length > 0) {
var partNmsList = partNmAndDefs.map(function (p) { return p.name; });
var partDefsList = partNmAndDefs.map(function (p) { return p.def; });
var dimensions_1 = partNmAndDefs.map(function (nmAndPart) { return _this.computePartDimensions(nmAndPart.def, nmAndPart.name); });
var partIRs_1 = [];
partNmAndDefs.forEach(function (nmAndDef, idx) {
var dims = dimensions_1[idx];
var irs = _this.allocPartIRs(nmAndDef.def, nmAndDef.name, dims);
partIRs_1 = partIRs_1.concat(irs);
});
var partPlacements = this.placeParts(partIRs_1);
var partsAndWireIRs = partPlacements.map(function (p) { return _this.allocWireIRs(p); });
var allWireIRs = partsAndWireIRs.map(function (p) { return p.wires; }).reduce(function (p, n) { return p.concat(n); }, []);
var allPowerUsage = allWireIRs.map(function (w) { return computePowerUsage(w); });
this.powerUsage = mergePowerUsage(allPowerUsage);
var basicWires = this.allocPowerWires(this.powerUsage);
var partsAndWires = partsAndWireIRs.map(function (irs, idx) {
var part = _this.allocPart(irs);
var wires = irs.wires.map(function (w) { return _this.allocWire(w); });
var pinIdxToWireIdx = [];
irs.wires.forEach(function (wIR, idx) {
pinIdxToWireIdx[wIR.pinIdx] = idx;
});
var assembly = irs.def.assembly.map(function (stepDef) {
return {
part: stepDef.part,
wireIndices: (stepDef.pinIndices || []).map(function (i) { return pinIdxToWireIdx[i]; })
};
});
return {
part: part,
wires: wires,
assembly: assembly
};
});
var all = [basicWires].concat(partsAndWires);
// hide breadboard if not used
var requiresBreadboard = all.some(function (r) {
return (r.part && r.part.breadboardConnections && r.part.breadboardConnections.length > 0)
|| r.wires && r.wires.some(function (w) { return (w.end.type == "breadboard" && w.end.style != "croc") || (w.start.type == "breadboard" && w.start.style != "croc"); });
});
return {
partsAndWires: all,
requiresBreadboard: requiresBreadboard
};
}
else {
return {
partsAndWires: []
};
}
};
return Allocator;
}());
function allocateDefinitions(opts) {
return new Allocator(opts).allocAll();
}
pxsim.allocateDefinitions = allocateDefinitions;
})(pxsim || (pxsim = {}));
/// <reference path="../localtypings/vscode-debug-protocol.d.ts" />
/**
* Heavily adapted from https://github.com/Microsoft/vscode-debugadapter-node
* and altered to run in a browser and communcate via JSON over a websocket
* rather than through stdin and stdout
*/
var pxsim;
(function (pxsim) {
var protocol;
(function (protocol) {
var Message = (function () {
function Message(type) {
this.seq = 0;
this.type = type;
}
return Message;
}());
protocol.Message = Message;
var Response = (function (_super) {
__extends(Response, _super);
function Response(request, message) {
_super.call(this, 'response');
this.request_seq = request.seq;
this.command = request.command;
if (message) {
this.success = false;
this.message = message;
}
else {
this.success = true;
}
}
return Response;
}(Message));
protocol.Response = Response;
var Event = (function (_super) {
__extends(Event, _super);
function Event(event, body) {
_super.call(this, 'event');
this.event = event;
if (body) {
this.body = body;
}
}
return Event;
}(Message));
protocol.Event = Event;
var Source = (function () {
function Source(name, path, id, origin, data) {
if (id === void 0) { id = 0; }
this.name = name;
this.path = path;
this.sourceReference = id;
if (origin) {
this.origin = origin;
}
if (data) {
this.adapterData = data;
}
}
return Source;
}());
protocol.Source = Source;
var Scope = (function () {
function Scope(name, reference, expensive) {
if (expensive === void 0) { expensive = false; }
this.name = name;
this.variablesReference = reference;
this.expensive = expensive;
}
return Scope;
}());
protocol.Scope = Scope;
var StackFrame = (function () {
function StackFrame(i, nm, src, ln, col) {
if (ln === void 0) { ln = 0; }
if (col === void 0) { col = 0; }
this.id = i;
this.source = src;
this.line = ln;
this.column = col;
this.name = nm;
}
return StackFrame;
}());
protocol.StackFrame = StackFrame;
var Thread = (function () {
function Thread(id, name) {
this.id = id;
if (name) {
this.name = name;
}
else {
this.name = 'Thread #' + id;
}
}
return Thread;
}());
protocol.Thread = Thread;
var Variable = (function () {
function Variable(name, value, ref, indexedVariables, namedVariables) {
if (ref === void 0) { ref = 0; }
this.name = name;
this.value = value;
this.variablesReference = ref;
if (typeof namedVariables === 'number') {
this.namedVariables = namedVariables;
}
if (typeof indexedVariables === 'number') {
this.indexedVariables = indexedVariables;
}
}
return Variable;
}());
protocol.Variable = Variable;
var Breakpoint = (function () {
function Breakpoint(verified, line, column, source) {
this.verified = verified;
var e = this;
if (typeof line === 'number') {
e.line = line;
}
if (typeof column === 'number') {
e.column = column;
}
if (source) {
e.source = source;
}
}
return Breakpoint;
}());
protocol.Breakpoint = Breakpoint;
var Module = (function () {
function Module(id, name) {
this.id = id;
this.name = name;
}
return Module;
}());
protocol.Module = Module;
var CompletionItem = (function () {
function CompletionItem(label, start, length) {
if (length === void 0) { length = 0; }
this.label = label;
this.start = start;
this.length = length;
}
return CompletionItem;
}());
protocol.CompletionItem = CompletionItem;
var StoppedEvent = (function (_super) {
__extends(StoppedEvent, _super);
function StoppedEvent(reason, threadId, exception_text) {
if (exception_text === void 0) { exception_text = null; }
_super.call(this, 'stopped');
this.body = {
reason: reason,
threadId: threadId
};
if (exception_text) {
var e = this;
e.body.text = exception_text;
}
}
return StoppedEvent;
}(Event));
protocol.StoppedEvent = StoppedEvent;
var ContinuedEvent = (function (_super) {
__extends(ContinuedEvent, _super);
function ContinuedEvent(threadId, allThreadsContinued) {
_super.call(this, 'continued');
this.body = {
threadId: threadId
};
if (typeof allThreadsContinued === 'boolean') {
this.body.allThreadsContinued = allThreadsContinued;
}
}
return ContinuedEvent;
}(Event));
protocol.ContinuedEvent = ContinuedEvent;
var InitializedEvent = (function (_super) {
__extends(InitializedEvent, _super);
function InitializedEvent() {
_super.call(this, 'initialized');
}
return InitializedEvent;
}(Event));
protocol.InitializedEvent = InitializedEvent;
var TerminatedEvent = (function (_super) {
__extends(TerminatedEvent, _super);
function TerminatedEvent(restart) {
_super.call(this, 'terminated');
if (typeof restart === 'boolean') {
var e = this;
e.body = {
restart: restart
};
}
}
return TerminatedEvent;
}(Event));
protocol.TerminatedEvent = TerminatedEvent;
var OutputEvent = (function (_super) {
__extends(OutputEvent, _super);
function OutputEvent(output, category, data) {
if (category === void 0) { category = 'console'; }
_super.call(this, 'output');
this.body = {
category: category,
output: output
};
if (data !== undefined) {
this.body.data = data;
}
}
return OutputEvent;
}(Event));
protocol.OutputEvent = OutputEvent;
var ThreadEvent = (function (_super) {
__extends(ThreadEvent, _super);
function ThreadEvent(reason, threadId) {
_super.call(this, 'thread');
this.body = {
reason: reason,
threadId: threadId
};
}
return ThreadEvent;
}(Event));
protocol.ThreadEvent = ThreadEvent;
var BreakpointEvent = (function (_super) {
__extends(BreakpointEvent, _super);
function BreakpointEvent(reason, breakpoint) {
_super.call(this, 'breakpoint');
this.body = {
reason: reason,
breakpoint: breakpoint
};
}
return BreakpointEvent;
}(Event));
protocol.BreakpointEvent = BreakpointEvent;
var ModuleEvent = (function (_super) {
__extends(ModuleEvent, _super);
function ModuleEvent(reason, module) {
_super.call(this, 'module');
this.body = {
reason: reason,
module: module
};
}
return ModuleEvent;
}(Event));
protocol.ModuleEvent = ModuleEvent;
var ProtocolServer = (function () {
function ProtocolServer() {
this._pendingRequests = {};
}
ProtocolServer.prototype.start = function (host) {
var _this = this;
this._sequence = 1;
this.host = host;
this.host.onData(function (msg) {
if (msg.type === 'request') {
_this.dispatchRequest(msg);
}
else if (msg.type === 'response') {
var response = msg;
var clb = _this._pendingRequests[response.seq];
if (clb) {
delete _this._pendingRequests[response.seq];
clb(response);
}
}
});
};
ProtocolServer.prototype.stop = function () {
if (this.host) {
this.host.close();
}
};
ProtocolServer.prototype.sendEvent = function (event) {
this.send('event', event);
};
ProtocolServer.prototype.sendResponse = function (response) {
if (response.seq > 0) {
console.error("attempt to send more than one response for command " + response.command);
}
else {
this.send('response', response);
}
};
ProtocolServer.prototype.sendRequest = function (command, args, timeout, cb) {
var _this = this;
var request = {
command: command
};
if (args && Object.keys(args).length > 0) {
request.arguments = args;
}
this.send('request', request);
if (cb) {
this._pendingRequests[request.seq] = cb;
var timer_1 = setTimeout(function () {
clearTimeout(timer_1);
var clb = _this._pendingRequests[request.seq];
if (clb) {
delete _this._pendingRequests[request.seq];
clb(new protocol.Response(request, 'timeout'));
}
}, timeout);
}
};
ProtocolServer.prototype.send = function (typ, message) {
message.type = typ;
message.seq = this._sequence++;
if (this.host) {
var json = JSON.stringify(message);
this.host.send(json);
}
};
// ---- protected ----------------------------------------------------------
ProtocolServer.prototype.dispatchRequest = function (request) {
};
return ProtocolServer;
}());
protocol.ProtocolServer = ProtocolServer;
var DebugSession = (function (_super) {
__extends(DebugSession, _super);
function DebugSession() {
_super.apply(this, arguments);
this._debuggerLinesStartAt1 = false;
this._debuggerColumnsStartAt1 = false;
this._clientLinesStartAt1 = true;
this._clientColumnsStartAt1 = true;
}
DebugSession.prototype.shutdown = function () {
};
DebugSession.prototype.dispatchRequest = function (request) {
var response = new protocol.Response(request);
try {
if (request.command === 'initialize') {
var args = request.arguments;
if (typeof args.linesStartAt1 === 'boolean') {
this._clientLinesStartAt1 = args.linesStartAt1;
}
if (typeof args.columnsStartAt1 === 'boolean') {
this._clientColumnsStartAt1 = args.columnsStartAt1;
}
if (args.pathFormat !== 'path') {
this.sendErrorResponse(response, 2018, 'debug adapter only supports native paths', null);
}
else {
var initializeResponse = response;
initializeResponse.body = {};
this.initializeRequest(initializeResponse, args);
}
}
else if (request.command === 'launch') {
this.launchRequest(response, request.arguments);
}
else if (request.command === 'attach') {
this.attachRequest(response, request.arguments);
}
else if (request.command === 'disconnect') {
this.disconnectRequest(response, request.arguments);
}
else if (request.command === 'setBreakpoints') {
this.setBreakPointsRequest(response, request.arguments);
}
else if (request.command === 'setFunctionBreakpoints') {
this.setFunctionBreakPointsRequest(response, request.arguments);
}
else if (request.command === 'setExceptionBreakpoints') {
this.setExceptionBreakPointsRequest(response, request.arguments);
}
else if (request.command === 'configurationDone') {
this.configurationDoneRequest(response, request.arguments);
}
else if (request.command === 'continue') {
this.continueRequest(response, request.arguments);
}
else if (request.command === 'next') {
this.nextRequest(response, request.arguments);
}
else if (request.command === 'stepIn') {
this.stepInRequest(response, request.arguments);
}
else if (request.command === 'stepOut') {
this.stepOutRequest(response, request.arguments);
}
else if (request.command === 'stepBack') {
this.stepBackRequest(response, request.arguments);
}
else if (request.command === 'restartFrame') {
this.restartFrameRequest(response, request.arguments);
}
else if (request.command === 'goto') {
this.gotoRequest(response, request.arguments);
}
else if (request.command === 'pause') {
this.pauseRequest(response, request.arguments);
}
else if (request.command === 'stackTrace') {
this.stackTraceRequest(response, request.arguments);
}
else if (request.command === 'scopes') {
this.scopesRequest(response, request.arguments);
}
else if (request.command === 'variables') {
this.variablesRequest(response, request.arguments);
}
else if (request.command === 'setVariable') {
this.setVariableRequest(response, request.arguments);
}
else if (request.command === 'source') {
this.sourceRequest(response, request.arguments);
}
else if (request.command === 'threads') {
this.threadsRequest(response);
}
else if (request.command === 'evaluate') {
this.evaluateRequest(response, request.arguments);
}
else if (request.command === 'stepInTargets') {
this.stepInTargetsRequest(response, request.arguments);
}
else if (request.command === 'gotoTargets') {
this.gotoTargetsRequest(response, request.arguments);
}
else if (request.command === 'completions') {
this.completionsRequest(response, request.arguments);
}
else {
this.customRequest(request.command, response, request.arguments);
}
}
catch (e) {
this.sendErrorResponse(response, 1104, '{_stack}', { _exception: e.message, _stack: e.stack });
}
};
DebugSession.prototype.initializeRequest = function (response, args) {
// This default debug adapter does not support conditional breakpoints.
response.body.supportsConditionalBreakpoints = false;
// This default debug adapter does not support hit conditional breakpoints.
response.body.supportsHitConditionalBreakpoints = false;
// This default debug adapter does not support function breakpoints.
response.body.supportsFunctionBreakpoints = false;
// This default debug adapter implements the 'configurationDone' request.
response.body.supportsConfigurationDoneRequest = true;
// This default debug adapter does not support hovers based on the 'evaluate' request.
response.body.supportsEvaluateForHovers = false;
// This default debug adapter does not support the 'stepBack' request.
response.body.supportsStepBack = false;
// This default debug adapter does not support the 'setVariable' request.
response.body.supportsSetVariable = false;
// This default debug adapter does not support the 'restartFrame' request.
response.body.supportsRestartFrame = false;
// This default debug adapter does not support the 'stepInTargetsRequest' request.
response.body.supportsStepInTargetsRequest = false;
// This default debug adapter does not support the 'gotoTargetsRequest' request.
response.body.supportsGotoTargetsRequest = false;
// This default debug adapter does not support the 'completionsRequest' request.
response.body.supportsCompletionsRequest = false;
this.sendResponse(response);
};
DebugSession.prototype.disconnectRequest = function (response, args) {
this.sendResponse(response);
this.shutdown();
};
DebugSession.prototype.launchRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.attachRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.setBreakPointsRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.setFunctionBreakPointsRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.setExceptionBreakPointsRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.configurationDoneRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.continueRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.nextRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.stepInRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.stepOutRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.stepBackRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.restartFrameRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.gotoRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.pauseRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.sourceRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.threadsRequest = function (response) {
this.sendResponse(response);
};
DebugSession.prototype.stackTraceRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.scopesRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.variablesRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.setVariableRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.evaluateRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.stepInTargetsRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.gotoTargetsRequest = function (response, args) {
this.sendResponse(response);
};
DebugSession.prototype.completionsRequest = function (response, args) {
this.sendResponse(response);
};
/**
* Override this hook to implement custom requests.
*/
DebugSession.prototype.customRequest = function (command, response, args) {
this.sendErrorResponse(response, 1014, 'unrecognized request', null);
};
DebugSession.prototype.sendErrorResponse = function (response, codeOrMessage, format, variables) {
var msg;
if (typeof codeOrMessage === 'number') {
msg = {
id: codeOrMessage,
format: format
};
if (variables) {
msg.variables = variables;
}
msg.showUser = true;
}
else {
msg = codeOrMessage;
}
response.success = false;
DebugSession.formatPII(msg.format, true, msg.variables);
if (!response.body) {
response.body = {};
}
response.body.error = msg;
this.sendResponse(response);
};
DebugSession.prototype.convertClientLineToDebugger = function (line) {
if (this._debuggerLinesStartAt1) {
return this._clientLinesStartAt1 ? line : line + 1;
}
return this._clientLinesStartAt1 ? line - 1 : line;
};
DebugSession.prototype.convertDebuggerLineToClient = function (line) {
if (this._debuggerLinesStartAt1) {
return this._clientLinesStartAt1 ? line : line - 1;
}
return this._clientLinesStartAt1 ? line + 1 : line;
};
DebugSession.prototype.convertClientColumnToDebugger = function (column) {
if (this._debuggerColumnsStartAt1) {
return this._clientColumnsStartAt1 ? column : column + 1;
}
return this._clientColumnsStartAt1 ? column - 1 : column;
};
DebugSession.prototype.convertDebuggerColumnToClient = function (column) {
if (this._debuggerColumnsStartAt1) {
return this._clientColumnsStartAt1 ? column : column - 1;
}
return this._clientColumnsStartAt1 ? column + 1 : column;
};
DebugSession.prototype.convertClientPathToDebugger = function (clientPath) {
if (this._clientPathsAreURIs != this._debuggerPathsAreURIs) {
if (this._clientPathsAreURIs) {
return DebugSession.uri2path(clientPath);
}
else {
return DebugSession.path2uri(clientPath);
}
}
return clientPath;
};
DebugSession.prototype.convertDebuggerPathToClient = function (debuggerPath) {
if (this._debuggerPathsAreURIs != this._clientPathsAreURIs) {
if (this._debuggerPathsAreURIs) {
return DebugSession.uri2path(debuggerPath);
}
else {
return DebugSession.path2uri(debuggerPath);
}
}
return debuggerPath;
};
DebugSession.path2uri = function (str) {
var pathName = str.replace(/\\/g, '/');
if (pathName[0] !== '/') {
pathName = '/' + pathName;
}
return encodeURI('file://' + pathName);
};
DebugSession.uri2path = function (url) {
return url;
//return Url.parse(url).pathname;
};
/*
* If argument starts with '_' it is OK to send its value to telemetry.
*/
DebugSession.formatPII = function (format, excludePII, args) {
return format.replace(DebugSession._formatPIIRegexp, function (match, paramName) {
if (excludePII && paramName.length > 0 && paramName[0] !== '_') {
return match;
}
return args[paramName] && args.hasOwnProperty(paramName) ?
args[paramName] :
match;
});
};
DebugSession._formatPIIRegexp = /{([^}]+)}/g;
return DebugSession;
}(ProtocolServer));
protocol.DebugSession = DebugSession;
})(protocol = pxsim.protocol || (pxsim.protocol = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var util;
(function (util) {
function injectPolyphils() {
// Polyfill for Uint8Array.slice for IE and Safari
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice
// TODO: Move this polyfill to a more appropriate file. It is left here for now because moving it causes a crash in IE; see PXT issue #1301.
if (!Uint8Array.prototype.slice) {
Object.defineProperty(Uint8Array.prototype, 'slice', {
value: Array.prototype.slice,
writable: true,
enumerable: true
});
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
if (!Uint8Array.prototype.fill) {
Object.defineProperty(Uint8Array.prototype, 'fill', {
writable: true,
enumerable: true,
value: function (value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
}
});
}
}
util.injectPolyphils = injectPolyphils;
var Lazy = (function () {
function Lazy(_func) {
this._func = _func;
this._evaluated = false;
}
Object.defineProperty(Lazy.prototype, "value", {
get: function () {
if (!this._evaluated) {
this._value = this._func();
this._evaluated = true;
}
return this._value;
},
enumerable: true,
configurable: true
});
return Lazy;
}());
util.Lazy = Lazy;
function getNormalizedParts(path) {
path = path.replace(/\\/g, "/");
var parts = [];
path.split("/").forEach(function (part) {
if (part === ".." && parts.length) {
parts.pop();
}
else if (part && part !== ".") {
parts.push(part);
}
});
return parts;
}
util.getNormalizedParts = getNormalizedParts;
function normalizePath(path) {
return getNormalizedParts(path).join("/");
}
util.normalizePath = normalizePath;
function relativePath(fromDir, toFile) {
var fParts = getNormalizedParts(fromDir);
var tParts = getNormalizedParts(toFile);
var i = 0;
while (fParts[i] === tParts[i]) {
i++;
if (i === fParts.length || i === tParts.length) {
break;
}
}
var fRemainder = fParts.slice(i);
var tRemainder = tParts.slice(i);
for (var i_1 = 0; i_1 < fRemainder.length; i_1++) {
tRemainder.unshift("..");
}
return tRemainder.join("/");
}
util.relativePath = relativePath;
function pathJoin() {
var paths = [];
for (var _i = 0; _i < arguments.length; _i++) {
paths[_i - 0] = arguments[_i];
}
var result = "";
paths.forEach(function (path) {
path.replace(/\\/g, "/");
if (path.lastIndexOf("/") === path.length - 1) {
path = path.slice(0, path.length - 1);
}
result += "/" + path;
});
return result;
}
util.pathJoin = pathJoin;
})(util = pxsim.util || (pxsim.util = {}));
})(pxsim || (pxsim = {}));
/// <reference path="./debugProtocol.ts" />
/// <reference path="./utils.ts" />
var pxsim;
(function (pxsim) {
function getWarningMessage(msg) {
var r = {
type: "debugger",
subtype: "warning",
breakpointIds: [],
message: msg
};
var s = pxsim.runtime.currFrame;
while (s != null) {
r.breakpointIds.push(s.lastBrkId);
s = s.parent;
}
return r;
}
pxsim.getWarningMessage = getWarningMessage;
var BreakpointMap = (function () {
function BreakpointMap(breakpoints) {
var _this = this;
this.fileMap = {};
this.idMap = {};
breakpoints.forEach(function (tuple) {
var id = tuple[0], bp = tuple[1];
if (!_this.fileMap[bp.source.path]) {
_this.fileMap[bp.source.path] = [];
}
_this.fileMap[bp.source.path].push(tuple);
_this.idMap[id] = bp;
});
for (var file in this.fileMap) {
var bps = this.fileMap[file];
// Sort the breakpoints to make finding the closest breakpoint to a
// given line easier later. Order first by start line and then from
// worst to best choice for each line.
this.fileMap[file] = bps.sort(function (_a, _b) {
var a = _a[1];
var b = _b[1];
if (a.line === b.line) {
if (b.endLine === a.endLine) {
return a.column - b.column;
}
// We want the closest breakpoint, so give preference to breakpoints
// that span fewer lines (i.e. breakpoints that are "tighter" around
// the line being searched for)
return b.endLine - a.endLine;
}
return a.line - b.line;
});
}
}
BreakpointMap.prototype.getById = function (id) {
return this.idMap[id];
};
BreakpointMap.prototype.verifyBreakpoint = function (path, breakpoint) {
var breakpoints = this.fileMap[path];
var best;
if (breakpoints) {
// Breakpoints are pre-sorted for each file. The last matching breakpoint
// in the list should be the best match
for (var _i = 0, breakpoints_1 = breakpoints; _i < breakpoints_1.length; _i++) {
var _a = breakpoints_1[_i], id = _a[0], bp = _a[1];
if (bp.line <= breakpoint.line && bp.endLine >= breakpoint.line) {
best = [id, bp];
}
}
}
if (best) {
best[1].verified = true;
return best;
}
return [-1, { verified: false }];
};
return BreakpointMap;
}());
pxsim.BreakpointMap = BreakpointMap;
function getBreakpointMsg(s, brkId) {
function valToJSON(v) {
switch (typeof v) {
case "string":
case "number":
case "boolean":
return v;
case "function":
return { text: "(function)" };
case "undefined":
return null;
case "object":
if (!v)
return null;
if (v instanceof pxsim.RefObject)
return { id: v.id };
return { text: "(object)" };
default:
throw new Error();
}
}
function frameVars(frame) {
var r = {};
for (var _i = 0, _a = Object.keys(frame); _i < _a.length; _i++) {
var k = _a[_i];
if (/___\d+$/.test(k)) {
r[k] = valToJSON(frame[k]);
}
}
return r;
}
var r = {
type: "debugger",
subtype: "breakpoint",
breakpointId: brkId,
globals: frameVars(pxsim.runtime.globals),
stackframes: []
};
while (s != null) {
var info = s.fn ? s.fn.info : null;
if (info)
r.stackframes.push({
locals: frameVars(s),
funcInfo: info,
breakpointId: s.lastBrkId
});
s = s.parent;
}
return r;
}
pxsim.getBreakpointMsg = getBreakpointMsg;
var SimDebugSession = (function (_super) {
__extends(SimDebugSession, _super);
function SimDebugSession(container) {
var _this = this;
_super.call(this);
var options = {
onDebuggerBreakpoint: function (b) { return _this.onDebuggerBreakpoint(b); },
onDebuggerWarning: function (w) { return _this.onDebuggerWarning(w); },
onDebuggerResume: function () { return _this.onDebuggerResume(); },
onStateChanged: function (s) { return _this.onStateChanged(s); }
};
this.driver = new pxsim.SimulatorDriver(container, options);
}
SimDebugSession.prototype.runCode = function (js, parts, fnArgs, breakpoints, board) {
this.breakpoints = breakpoints;
if (this.projectDir) {
this.fixBreakpoints();
}
this.sendEvent(new pxsim.protocol.InitializedEvent());
this.driver.run(js, { parts: parts, fnArgs: fnArgs, boardDefinition: board });
};
SimDebugSession.prototype.stopSimulator = function (unload) {
if (unload === void 0) { unload = false; }
this.driver.stop(unload);
};
SimDebugSession.prototype.initializeRequest = function (response, args) {
response.body.supportsConditionalBreakpoints = false;
response.body.supportsHitConditionalBreakpoints = false;
response.body.supportsFunctionBreakpoints = false;
response.body.supportsEvaluateForHovers = false;
response.body.supportsStepBack = false;
response.body.supportsSetVariable = false;
response.body.supportsRestartFrame = false;
response.body.supportsStepInTargetsRequest = false;
response.body.supportsGotoTargetsRequest = false;
response.body.supportsCompletionsRequest = false;
// This default debug adapter implements the 'configurationDone' request.
response.body.supportsConfigurationDoneRequest = true;
this.sendResponse(response);
};
SimDebugSession.prototype.disconnectRequest = function (response, args) {
this.sendResponse(response);
this.shutdown();
};
SimDebugSession.prototype.launchRequest = function (response, args) {
if (!this.projectDir) {
this.projectDir = pxsim.util.normalizePath(args.projectDir);
if (this.breakpoints) {
this.fixBreakpoints();
}
}
this.sendResponse(response);
};
SimDebugSession.prototype.setBreakPointsRequest = function (response, args) {
var _this = this;
response.body = { breakpoints: [] };
var ids = [];
args.breakpoints.forEach(function (requestedBp) {
if (_this.breakpoints) {
var _a = _this.breakpoints.verifyBreakpoint(pxsim.util.relativePath(_this.projectDir, args.source.path), requestedBp), id = _a[0], bp = _a[1];
response.body.breakpoints.push(bp);
if (bp.verified) {
ids.push(id);
}
}
else {
response.body.breakpoints.push({ verified: false });
}
});
this.driver.setBreakpoints(ids);
this.sendResponse(response);
};
SimDebugSession.prototype.continueRequest = function (response, args) {
this.driver.resume(pxsim.SimulatorDebuggerCommand.Resume);
this.sendResponse(response);
};
SimDebugSession.prototype.nextRequest = function (response, args) {
this.driver.resume(pxsim.SimulatorDebuggerCommand.StepOver);
this.sendResponse(response);
};
SimDebugSession.prototype.stepInRequest = function (response, args) {
this.driver.resume(pxsim.SimulatorDebuggerCommand.StepInto);
this.sendResponse(response);
};
SimDebugSession.prototype.stepOutRequest = function (response, args) {
this.driver.resume(pxsim.SimulatorDebuggerCommand.StepOut);
this.sendResponse(response);
};
SimDebugSession.prototype.pauseRequest = function (response, args) {
this.driver.resume(pxsim.SimulatorDebuggerCommand.Pause);
this.sendResponse(response);
};
SimDebugSession.prototype.threadsRequest = function (response) {
response.body = { threads: [{ id: SimDebugSession.THREAD_ID, name: "main" }] };
this.sendResponse(response);
};
SimDebugSession.prototype.stackTraceRequest = function (response, args) {
if (this.lastBreak) {
var frames_1 = this.state.getFrames();
response.body = { stackFrames: frames_1 };
}
this.sendResponse(response);
};
SimDebugSession.prototype.scopesRequest = function (response, args) {
if (this.state) {
response.body = { scopes: this.state.getScopes(args.frameId) };
}
this.sendResponse(response);
};
SimDebugSession.prototype.variablesRequest = function (response, args) {
if (this.state) {
response.body = { variables: this.state.getVariables(args.variablesReference) };
}
this.sendResponse(response);
};
SimDebugSession.prototype.onDebuggerBreakpoint = function (breakMsg) {
this.lastBreak = breakMsg;
this.state = new StoppedState(this.lastBreak, this.breakpoints, this.projectDir);
if (breakMsg.exceptionMessage) {
this.sendEvent(new pxsim.protocol.StoppedEvent("exception", SimDebugSession.THREAD_ID, breakMsg.exceptionMessage));
}
else {
this.sendEvent(new pxsim.protocol.StoppedEvent("breakpoint", SimDebugSession.THREAD_ID));
}
};
SimDebugSession.prototype.onDebuggerWarning = function (warnMsg) {
};
SimDebugSession.prototype.onDebuggerResume = function () {
this.sendEvent(new pxsim.protocol.ContinuedEvent(SimDebugSession.THREAD_ID, true));
};
SimDebugSession.prototype.onStateChanged = function (state) {
switch (state) {
case pxsim.SimulatorState.Paused:
// Sending a stopped event here would be redundant
break;
case pxsim.SimulatorState.Running:
this.sendEvent(new pxsim.protocol.ContinuedEvent(SimDebugSession.THREAD_ID, true));
break;
case pxsim.SimulatorState.Stopped:
this.sendEvent(new pxsim.protocol.TerminatedEvent());
break;
case pxsim.SimulatorState.Unloaded:
default:
}
};
SimDebugSession.prototype.fixBreakpoints = function () {
// Fix breakpoint locations from the debugger's format to the client's
for (var bpId in this.breakpoints.idMap) {
var bp = this.breakpoints.idMap[bpId];
bp.source.path = pxsim.util.pathJoin(this.projectDir, bp.source.path);
bp.line = this.convertDebuggerLineToClient(bp.line);
bp.endLine = this.convertDebuggerLineToClient(bp.endLine);
bp.column = this.convertDebuggerColumnToClient(bp.column);
bp.endColumn = this.convertDebuggerColumnToClient(bp.endColumn);
}
};
// We only have one thread
// TODO: We could theoretically visualize the individual fibers
SimDebugSession.THREAD_ID = 1;
return SimDebugSession;
}(pxsim.protocol.DebugSession));
pxsim.SimDebugSession = SimDebugSession;
/**
* Maintains the state at the current breakpoint and handles lazy
* queries for stack frames, scopes, variables, etc. The protocol
* expects requests to be made in the order:
* Frames -> Scopes -> Variables
*/
var StoppedState = (function () {
function StoppedState(_message, _map, _dir) {
this._message = _message;
this._map = _map;
this._dir = _dir;
this._currentId = 1;
this._frames = {};
this._vars = {};
var globalId = this.nextId();
this._vars[globalId] = this.getVariableValues(this._message.globals);
this._globalScope = {
name: "Globals",
variablesReference: globalId,
expensive: false
};
}
/**
* Get stack frames for current breakpoint.
*/
StoppedState.prototype.getFrames = function () {
var _this = this;
return this._message.stackframes.map(function (s, i) {
;
var bp = _this._map.getById(s.breakpointId);
if (bp) {
_this._frames[s.breakpointId] = s;
return {
id: s.breakpointId,
name: s.funcInfo ? s.funcInfo.functionName : (i === 0 ? "main" : "anonymous"),
line: bp.line,
column: bp.column,
endLine: bp.endLine,
endColumn: bp.endLine,
source: bp.source
};
}
return undefined;
}).filter(function (b) { return !!b; });
};
/**
* Returns scopes visible to the given stack frame.
*
* TODO: Currently, we only support locals and globals (no closures)
*/
StoppedState.prototype.getScopes = function (frameId) {
var frame = this._frames[frameId];
if (frame) {
var localId = this.nextId();
this._vars[localId] = this.getVariableValues(frame.locals);
return [{
name: "Locals",
variablesReference: localId,
expensive: false
}, this._globalScope];
}
return [this._globalScope];
};
/**
* Returns variable information (and object properties)
*/
StoppedState.prototype.getVariables = function (variablesReference) {
var lz = this._vars[variablesReference];
return (lz && lz.value) || [];
};
StoppedState.prototype.getVariableValues = function (v) {
var _this = this;
return new pxsim.util.Lazy(function () {
var result = [];
for (var name_1 in v) {
var value = v[name_1];
var vString = void 0;
var variablesReference = 0;
if (value === null) {
vString = "null";
}
else if (value === undefined) {
vString = "undefined";
}
else if (typeof value === "object") {
vString = "(object)";
variablesReference = _this.nextId();
// Variables should be requested lazily, so reference loops aren't an issue
_this._vars[variablesReference] = _this.getVariableValues(value);
}
else {
vString = value.toString();
}
// Remove the metadata from the name
var displayName = name_1.substr(0, name_1.lastIndexOf("___"));
result.push({
name: displayName,
value: vString,
variablesReference: variablesReference
});
}
return result;
});
};
StoppedState.prototype.nextId = function () {
return this._currentId++;
};
return StoppedState;
}());
})(pxsim || (pxsim = {}));
/// <reference path="../localtypings/pxtparts.d.ts"/>
var pxsim;
(function (pxsim) {
var Embed;
(function (Embed) {
function start() {
window.addEventListener("message", receiveMessage, false);
var frameid = window.location.hash.slice(1);
pxsim.Runtime.postMessage({ type: 'ready', frameid: frameid });
}
Embed.start = start;
function receiveMessage(event) {
var origin = event.origin; // || (<any>event).originalEvent.origin;
// TODO: test origins
var data = event.data || {};
var type = data.type || '';
if (!type)
return;
switch (type || '') {
case 'run':
run(data);
break;
case 'stop':
stop();
break;
case 'mute':
mute(data.mute);
break;
case 'custom':
if (pxsim.handleCustomMessage)
pxsim.handleCustomMessage(data);
break;
case 'pxteditor':
break; //handled elsewhere
case 'debugger':
if (runtime) {
runtime.handleDebuggerMsg(data);
}
break;
default:
queue(data);
break;
}
}
// TODO remove this; this should be using Runtime.runtime which gets
// set correctly depending on which runtime is currently running
var runtime;
function stop() {
if (runtime) {
runtime.kill();
if (runtime.board)
runtime.board.kill();
}
}
Embed.stop = stop;
function run(msg) {
stop();
if (msg.mute)
mute(msg.mute);
runtime = new pxsim.Runtime(msg.code);
runtime.id = msg.id;
runtime.board.initAsync(msg)
.done(function () {
runtime.run(function (v) {
pxsim.dumpLivePointers();
});
});
}
Embed.run = run;
function mute(mute) {
pxsim.AudioContextManager.mute(mute);
}
function queue(msg) {
if (!runtime || runtime.dead) {
return;
}
runtime.board.receiveMessage(msg);
}
})(Embed = pxsim.Embed || (pxsim.Embed = {}));
})(pxsim || (pxsim = {}));
pxsim.util.injectPolyphils();
if (typeof window !== 'undefined') {
window.addEventListener('load', function (ev) {
pxsim.Embed.start();
});
}
/// <reference path="../typings/globals/bluebird/index.d.ts"/>
var pxsim;
(function (pxsim) {
var instructions;
(function (instructions) {
var LOC_LBL_SIZE = 10;
var QUANT_LBL_SIZE = 30;
var QUANT_LBL = function (q) { return (q + "x"); };
var WIRE_QUANT_LBL_SIZE = 20;
var LBL_VERT_PAD = 3;
var LBL_RIGHT_PAD = 5;
var LBL_LEFT_PAD = 5;
var REQ_WIRE_HEIGHT = 45;
var REQ_CMP_HEIGHT = 55;
var REQ_CMP_SCALE = 0.5 * 3;
var ORIENTATION = "portrait";
var PPI = 96.0;
var PAGE_SCALAR = 0.95;
var _a = (ORIENTATION == "portrait" ? [PPI * 8.5 * PAGE_SCALAR, PPI * 11.0 * PAGE_SCALAR] : [PPI * 11.0 * PAGE_SCALAR, PPI * 8.5 * PAGE_SCALAR]), FULL_PAGE_WIDTH = _a[0], FULL_PAGE_HEIGHT = _a[1];
var PAGE_MARGIN = PPI * 0.45;
var PAGE_WIDTH = FULL_PAGE_WIDTH - PAGE_MARGIN * 2;
var PAGE_HEIGHT = FULL_PAGE_HEIGHT - PAGE_MARGIN * 2;
var BORDER_COLOR = "gray";
var BORDER_RADIUS = 5 * 4;
var BORDER_WIDTH = 2 * 2;
var _b = [1, 1], PANEL_ROWS = _b[0], PANEL_COLS = _b[1];
var PANEL_MARGIN = 20;
var PANEL_PADDING = 8 * 3;
var PANEL_WIDTH = PAGE_WIDTH / PANEL_COLS - (PANEL_MARGIN + PANEL_PADDING + BORDER_WIDTH) * PANEL_COLS;
var PANEL_HEIGHT = PAGE_HEIGHT / PANEL_ROWS - (PANEL_MARGIN + PANEL_PADDING + BORDER_WIDTH) * PANEL_ROWS;
var BOARD_WIDTH = 465;
var BOARD_LEFT = (PANEL_WIDTH - BOARD_WIDTH) / 2.0 + PANEL_PADDING;
var BOARD_BOT = PANEL_PADDING;
var NUM_BOX_SIZE = 120;
var NUM_FONT = 80;
var NUM_MARGIN = 10;
var FRONT_PAGE_BOARD_WIDTH = 400;
var PART_SCALAR = 2.3;
var PARTS_BOARD_SCALE = 0.17;
var PARTS_BB_SCALE = 0.25;
var PARTS_CMP_SCALE = 0.3;
var PARTS_WIRE_SCALE = 0.23;
var BACK_PAGE_BOARD_WIDTH = PANEL_WIDTH - PANEL_PADDING * 1.5;
var STYLE = "\n .instr-panel {\n margin: " + PANEL_MARGIN + "px;\n padding: " + PANEL_PADDING + "px;\n border-width: " + BORDER_WIDTH + "px;\n border-color: " + BORDER_COLOR + ";\n border-style: solid;\n border-radius: " + BORDER_RADIUS + "px;\n display: inline-block;\n width: " + PANEL_WIDTH + "px;\n height: " + PANEL_HEIGHT + "px;\n position: relative;\n overflow: hidden;\n page-break-inside: avoid;\n }\n .board-svg {\n margin: 0 auto;\n display: block;\n position: absolute;\n bottom: " + BOARD_BOT + "px;\n left: " + BOARD_LEFT + "px;\n }\n .panel-num-outer {\n position: absolute;\n left: " + -BORDER_WIDTH + "px;\n top: " + -BORDER_WIDTH + "px;\n width: " + NUM_BOX_SIZE + "px;\n height: " + NUM_BOX_SIZE + "px;\n border-width: " + BORDER_WIDTH + "px;\n border-style: solid;\n border-color: " + BORDER_COLOR + ";\n border-radius: " + BORDER_RADIUS + "px 0 " + BORDER_RADIUS + "px 0;\n }\n .panel-num {\n margin: " + NUM_MARGIN + "px 0;\n text-align: center;\n font-size: " + NUM_FONT + "px;\n }\n .cmp-div {\n display: inline-block;\n }\n .reqs-div {\n margin-left: " + (PANEL_PADDING + NUM_BOX_SIZE) + "px;\n margin-top: 5px;\n }\n .partslist-wire,\n .partslist-cmp {\n margin: 10px;\n }\n .partslist-wire {\n display: inline-block;\n }\n ";
function addClass(el, cls) {
//TODO move to library
if (el.classList)
el.classList.add(cls);
else if (el.className.indexOf(cls) < 0)
el.className += " " + cls;
}
function mkTxt(p, txt, size) {
var el = pxsim.svg.elt("text");
var x = p[0], y = p[1];
pxsim.svg.hydrate(el, { x: x, y: y, style: "font-size:" + size + "px;" });
el.textContent = txt;
return el;
}
function mkBoardImgSvg(def) {
var boardView = pxsim.visuals.mkBoardView({
visual: def
});
return boardView.getView();
}
function mkBBSvg() {
var bb = new pxsim.visuals.Breadboard({});
return bb.getSVGAndSize();
}
function wrapSvg(el, opts) {
//TODO: Refactor this function; it is too complicated. There is a lot of error-prone math being done
// to scale and place all elements which could be simplified with more forethought.
var svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
var dims = { l: 0, t: 0, w: 0, h: 0 };
var cmpSvgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svgEl.appendChild(cmpSvgEl);
cmpSvgEl.appendChild(el.el);
var cmpSvgAtts = {
"viewBox": el.x + " " + el.y + " " + el.w + " " + el.h,
"preserveAspectRatio": "xMidYMid",
};
dims.w = el.w;
dims.h = el.h;
var scale = function (scaler) {
dims.h *= scaler;
dims.w *= scaler;
cmpSvgAtts.width = dims.w;
cmpSvgAtts.height = dims.h;
};
if (opts.cmpScale) {
scale(opts.cmpScale);
}
if (opts.cmpWidth && opts.cmpWidth < dims.w) {
scale(opts.cmpWidth / dims.w);
}
else if (opts.cmpHeight && opts.cmpHeight < dims.h) {
scale(opts.cmpHeight / dims.h);
}
pxsim.svg.hydrate(cmpSvgEl, cmpSvgAtts);
var elDims = { l: dims.l, t: dims.t, w: dims.w, h: dims.h };
var updateL = function (newL) {
if (newL < dims.l) {
var extraW = dims.l - newL;
dims.l = newL;
dims.w += extraW;
}
};
var updateR = function (newR) {
var oldR = dims.l + dims.w;
if (oldR < newR) {
var extraW = newR - oldR;
dims.w += extraW;
}
};
var updateT = function (newT) {
if (newT < dims.t) {
var extraH = dims.t - newT;
dims.t = newT;
dims.h += extraH;
}
};
var updateB = function (newB) {
var oldB = dims.t + dims.h;
if (oldB < newB) {
var extraH = newB - oldB;
dims.h += extraH;
}
};
//labels
var _a = [-0.3, 0.3], xOff = _a[0], yOff = _a[1]; //HACK: these constants tweak the way "mkTxt" knows how to center the text
var txtAspectRatio = [1.4, 1.0];
if (opts && opts.top) {
var size = opts.topSize;
var txtW = size / txtAspectRatio[0];
var txtH = size / txtAspectRatio[1];
var _b = [elDims.l + elDims.w / 2, elDims.t - LBL_VERT_PAD - txtH / 2], cx = _b[0], y = _b[1];
var lbl = pxsim.visuals.mkTxt(cx, y, size, 0, opts.top, xOff, yOff);
pxsim.svg.addClass(lbl, "cmp-lbl");
svgEl.appendChild(lbl);
var len = txtW * opts.top.length;
updateT(y - txtH / 2);
updateL(cx - len / 2);
updateR(cx + len / 2);
}
if (opts && opts.bot) {
var size = opts.botSize;
var txtW = size / txtAspectRatio[0];
var txtH = size / txtAspectRatio[1];
var _c = [elDims.l + elDims.w / 2, elDims.t + elDims.h + LBL_VERT_PAD + txtH / 2], cx = _c[0], y = _c[1];
var lbl = pxsim.visuals.mkTxt(cx, y, size, 0, opts.bot, xOff, yOff);
pxsim.svg.addClass(lbl, "cmp-lbl");
svgEl.appendChild(lbl);
var len = txtW * opts.bot.length;
updateB(y + txtH / 2);
updateL(cx - len / 2);
updateR(cx + len / 2);
}
if (opts && opts.right) {
var size = opts.rightSize;
var txtW = size / txtAspectRatio[0];
var txtH = size / txtAspectRatio[1];
var len = txtW * opts.right.length;
var _d = [elDims.l + elDims.w + LBL_RIGHT_PAD + len / 2, elDims.t + elDims.h / 2], cx = _d[0], cy = _d[1];
var lbl = pxsim.visuals.mkTxt(cx, cy, size, 0, opts.right, xOff, yOff);
pxsim.svg.addClass(lbl, "cmp-lbl");
svgEl.appendChild(lbl);
updateT(cy - txtH / 2);
updateR(cx + len / 2);
updateB(cy + txtH / 2);
}
if (opts && opts.left) {
var size = opts.leftSize;
var txtW = size / txtAspectRatio[0];
var txtH = size / txtAspectRatio[1];
var len = txtW * opts.left.length;
var _e = [elDims.l - LBL_LEFT_PAD - len / 2, elDims.t + elDims.h / 2], cx = _e[0], cy = _e[1];
var lbl = pxsim.visuals.mkTxt(cx, cy, size, 0, opts.left, xOff, yOff);
pxsim.svg.addClass(lbl, "cmp-lbl");
svgEl.appendChild(lbl);
updateT(cy - txtH / 2);
updateL(cx - len / 2);
updateB(cy + txtH / 2);
}
var svgAtts = {
"viewBox": dims.l + " " + dims.t + " " + dims.w + " " + dims.h,
"width": dims.w * PART_SCALAR,
"height": dims.h * PART_SCALAR,
"preserveAspectRatio": "xMidYMid",
};
pxsim.svg.hydrate(svgEl, svgAtts);
var div = document.createElement("div");
div.appendChild(svgEl);
return div;
}
function mkCmpDiv(cmp, opts) {
var state = pxsim.runtime.board;
var el;
if (cmp == "wire") {
el = pxsim.visuals.mkWirePart([0, 0], opts.wireClr || "red", opts.crocClips);
}
else {
var partVis = cmp;
if (typeof partVis.builtIn == "string") {
var cnstr = state.builtinPartVisuals[partVis.builtIn];
el = cnstr([0, 0]);
}
else {
el = pxsim.visuals.mkGenericPartSVG(partVis);
}
}
return wrapSvg(el, opts);
}
function mkBoardProps(allocOpts) {
var allocRes = pxsim.allocateDefinitions(allocOpts);
var stepToWires = [];
var stepToCmps = [];
var stepOffset = 1;
allocRes.partsAndWires.forEach(function (cAndWs) {
var part = cAndWs.part;
var wires = cAndWs.wires;
cAndWs.assembly.forEach(function (step, idx) {
if (step.part && part)
stepToCmps[stepOffset + idx] = [part];
if (step.wireIndices && step.wireIndices.length > 0 && wires)
stepToWires[stepOffset + idx] = step.wireIndices.map(function (i) { return wires[i]; });
});
stepOffset += cAndWs.assembly.length;
});
var numSteps = stepOffset;
var lastStep = numSteps - 1;
var allCmps = allocRes.partsAndWires.map(function (r) { return r.part; }).filter(function (p) { return !!p; });
var allWires = allocRes.partsAndWires.map(function (r) { return r.wires || []; }).reduce(function (p, n) { return p.concat(n); }, []);
var colorToWires = {};
var allWireColors = [];
allWires.forEach(function (w) {
if (!colorToWires[w.color]) {
colorToWires[w.color] = [];
allWireColors.push(w.color);
}
colorToWires[w.color].push(w);
});
return {
boardDef: allocOpts.boardDef,
cmpDefs: allocOpts.partDefs,
fnArgs: allocOpts.fnArgs,
allAlloc: allocRes,
stepToWires: stepToWires,
stepToCmps: stepToCmps,
allWires: allWires,
allCmps: allCmps,
lastStep: lastStep,
colorToWires: colorToWires,
allWireColors: allWireColors,
};
}
function mkBlankBoardAndBreadboard(props, width, buildMode) {
if (buildMode === void 0) { buildMode = false; }
var state = pxsim.runtime.board;
var opts = {
state: state,
boardDef: props.boardDef,
forceBreadboardLayout: true,
forceBreadboardRender: props.allAlloc.requiresBreadboard,
partDefs: props.cmpDefs,
maxWidth: width + "px",
fnArgs: props.fnArgs,
wireframe: buildMode,
partsList: []
};
var boardHost = new pxsim.visuals.BoardHost(pxsim.visuals.mkBoardView({
visual: opts.boardDef.visual,
wireframe: opts.wireframe
}), opts);
var view = boardHost.getView();
pxsim.svg.addClass(view, "board-svg");
//set smiley
//HACK
// let img = board.board.displayCmp.image;
// img.set(1, 0, 255);
// img.set(3, 0, 255);
// img.set(0, 2, 255);
// img.set(1, 3, 255);
// img.set(2, 3, 255);
// img.set(3, 3, 255);
// img.set(4, 2, 255);
// board.updateState();
return boardHost;
}
function drawSteps(board, step, props) {
var view = board.getView();
if (step > 0) {
pxsim.svg.addClass(view, "grayed");
}
var _loop_1 = function(i) {
var cmps = props.stepToCmps[i];
if (cmps) {
cmps.forEach(function (partInst) {
var cmp = board.addPart(partInst);
//last step
if (i === step) {
//highlight locations pins
partInst.breadboardConnections.forEach(function (bbLoc) { return board.highlightBreadboardPin(bbLoc); });
pxsim.svg.addClass(cmp.element, "notgrayed");
}
});
}
var wires = props.stepToWires[i];
if (wires) {
wires.forEach(function (w) {
var wire = board.addWire(w);
//last step
if (i === step) {
//location highlights
if (w.start.type == "breadboard") {
var lbls = board.highlightBreadboardPin(w.start);
}
else {
board.highlightBoardPin(w.start.pin);
}
if (w.end.type == "breadboard") {
var lbls = board.highlightBreadboardPin(w.end);
}
else {
board.highlightBoardPin(w.end.pin);
}
//highlight wire
board.highlightWire(wire);
}
});
}
};
for (var i = 0; i <= step; i++) {
_loop_1(i);
}
}
function mkPanel() {
//panel
var panel = document.createElement("div");
addClass(panel, "instr-panel");
return panel;
}
function mkPartsPanel(props) {
var panel = mkPanel();
// board and breadboard
var boardImg = mkBoardImgSvg(props.boardDef.visual);
var board = wrapSvg(boardImg, { left: QUANT_LBL(1), leftSize: QUANT_LBL_SIZE, cmpScale: PARTS_BOARD_SCALE });
panel.appendChild(board);
var bbRaw = mkBBSvg();
var bb = wrapSvg(bbRaw, { left: QUANT_LBL(1), leftSize: QUANT_LBL_SIZE, cmpScale: PARTS_BB_SCALE });
panel.appendChild(bb);
// components
var cmps = props.allCmps;
cmps.forEach(function (c) {
var quant = 1;
// TODO: don't special case this
if (c.visual.builtIn === "buttonpair") {
quant = 2;
}
var cmp = mkCmpDiv(c.visual, {
left: QUANT_LBL(quant),
leftSize: QUANT_LBL_SIZE,
cmpScale: PARTS_CMP_SCALE,
});
addClass(cmp, "partslist-cmp");
panel.appendChild(cmp);
});
// wires
props.allWireColors.forEach(function (clr) {
var quant = props.colorToWires[clr].length;
var cmp = mkCmpDiv("wire", {
left: QUANT_LBL(quant),
leftSize: WIRE_QUANT_LBL_SIZE,
wireClr: clr,
cmpScale: PARTS_WIRE_SCALE,
crocClips: props.boardDef.useCrocClips
});
addClass(cmp, "partslist-wire");
panel.appendChild(cmp);
});
return panel;
}
function mkStepPanel(step, props) {
var panel = mkPanel();
//board
var board = mkBlankBoardAndBreadboard(props, BOARD_WIDTH, true);
drawSteps(board, step, props);
panel.appendChild(board.getView());
//number
var numDiv = document.createElement("div");
addClass(numDiv, "panel-num-outer");
addClass(numDiv, "noselect");
panel.appendChild(numDiv);
var num = document.createElement("div");
addClass(num, "panel-num");
num.textContent = (step + 1) + "";
numDiv.appendChild(num);
// add requirements
var reqsDiv = document.createElement("div");
addClass(reqsDiv, "reqs-div");
panel.appendChild(reqsDiv);
var wires = (props.stepToWires[step] || []);
var mkLabel = function (loc) {
if (loc.type === "breadboard") {
var _a = loc, row = _a.row, col = _a.col;
return "(" + row + "," + col + ")";
}
else
return loc.pin;
};
wires.forEach(function (w) {
var cmp = mkCmpDiv("wire", {
top: mkLabel(w.end),
topSize: LOC_LBL_SIZE,
bot: mkLabel(w.start),
botSize: LOC_LBL_SIZE,
wireClr: w.color,
cmpHeight: REQ_WIRE_HEIGHT,
crocClips: props.boardDef.useCrocClips
});
addClass(cmp, "cmp-div");
reqsDiv.appendChild(cmp);
});
var cmps = (props.stepToCmps[step] || []);
cmps.forEach(function (c) {
var locs;
if (c.visual.builtIn === "buttonpair") {
//TODO: don't special case this
locs = [c.breadboardConnections[0], c.breadboardConnections[2]];
}
else {
locs = [c.breadboardConnections[0]];
}
locs.forEach(function (l, i) {
var topLbl;
if (l) {
var row = l.row, col = l.col;
topLbl = "(" + row + "," + col + ")";
}
else {
topLbl = "";
}
var scale = REQ_CMP_SCALE;
if (c.visual.builtIn === "buttonpair")
scale *= 0.5; //TODO: don't special case
var cmp = mkCmpDiv(c.visual, {
top: topLbl,
topSize: LOC_LBL_SIZE,
cmpHeight: REQ_CMP_HEIGHT,
cmpScale: scale
});
addClass(cmp, "cmp-div");
reqsDiv.appendChild(cmp);
});
});
return panel;
}
function updateFrontPanel(props) {
var panel = document.getElementById("front-panel");
var board = mkBlankBoardAndBreadboard(props, FRONT_PAGE_BOARD_WIDTH, false);
board.addAll(props.allAlloc);
panel.appendChild(board.getView());
return [panel, props];
}
function mkFinalPanel(props) {
var panel = mkPanel();
addClass(panel, "back-panel");
var board = mkBlankBoardAndBreadboard(props, BACK_PAGE_BOARD_WIDTH, false);
board.addAll(props.allAlloc);
panel.appendChild(board.getView());
return panel;
}
function renderParts(options) {
var COMP_CODE = "";
pxsim.runtime = new pxsim.Runtime(COMP_CODE);
pxsim.runtime.board = null;
pxsim.initCurrentRuntime();
var style = document.createElement("style");
document.head.appendChild(style);
style.textContent += STYLE;
var cmpDefs = options.partDefinitions;
//props
var dummyBreadboard = new pxsim.visuals.Breadboard({});
var props = mkBoardProps({
boardDef: options.boardDef,
partDefs: cmpDefs,
partsList: options.parts,
fnArgs: options.fnArgs,
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
});
//front page
var frontPanel = updateFrontPanel(props);
//all required parts
var partsPanel = mkPartsPanel(props);
document.body.appendChild(partsPanel);
//steps
for (var s = 0; s <= props.lastStep; s++) {
var p = mkStepPanel(s, props);
document.body.appendChild(p);
}
//final
var finalPanel = mkFinalPanel(props);
document.body.appendChild(finalPanel);
}
instructions.renderParts = renderParts;
})(instructions = pxsim.instructions || (pxsim.instructions = {}));
})(pxsim || (pxsim = {}));
// APIs for language/runtime support (records, locals, function values)
var pxsim;
(function (pxsim) {
pxsim.quiet = false;
function check(cond) {
if (!cond) {
debugger;
throw new Error("sim: check failed");
}
}
pxsim.check = check;
var refObjId = 1;
var liveRefObjs = {};
var stringLiterals;
var stringRefCounts = {};
var refCounting = true;
function noRefCounting() {
refCounting = false;
}
pxsim.noRefCounting = noRefCounting;
var RefObject = (function () {
function RefObject() {
this.id = refObjId++;
this.refcnt = 1;
liveRefObjs[this.id + ""] = this;
}
RefObject.prototype.destroy = function () { };
RefObject.prototype.print = function () {
console.log("RefObject id:" + this.id + " refs:" + this.refcnt);
};
return RefObject;
}());
pxsim.RefObject = RefObject;
function noLeakTracking(r) {
delete liveRefObjs[r.id + ""];
}
pxsim.noLeakTracking = noLeakTracking;
var FnWrapper = (function () {
function FnWrapper(func, caps, a0, a1, a2, cb) {
this.func = func;
this.caps = caps;
this.a0 = a0;
this.a1 = a1;
this.a2 = a2;
this.cb = cb;
}
return FnWrapper;
}());
pxsim.FnWrapper = FnWrapper;
var RefRecord = (function (_super) {
__extends(RefRecord, _super);
function RefRecord() {
_super.apply(this, arguments);
this.fields = [];
}
RefRecord.prototype.destroy = function () {
var refmask = this.vtable.refmask;
for (var i = 0; i < refmask.length; ++i)
if (refmask[i])
decr(this.fields[i]);
this.fields = null;
this.vtable = null;
};
RefRecord.prototype.isRef = function (idx) {
check(0 <= idx && idx < this.fields.length);
return !!this.vtable.refmask[idx];
};
RefRecord.prototype.print = function () {
console.log("RefInstance id:" + this.id + " (" + this.vtable.name + ") len:" + this.fields.length);
};
return RefRecord;
}(RefObject));
pxsim.RefRecord = RefRecord;
var RefAction = (function (_super) {
__extends(RefAction, _super);
function RefAction() {
_super.apply(this, arguments);
this.fields = [];
}
RefAction.prototype.isRef = function (idx) {
check(0 <= idx && idx < this.fields.length);
return idx < this.reflen;
};
RefAction.prototype.ldclo = function (n) {
n >>= 2;
check(0 <= n && n < this.fields.length);
return this.fields[n];
};
RefAction.prototype.destroy = function () {
for (var i = 0; i < this.reflen; ++i)
decr(this.fields[i]);
this.fields = null;
this.func = null;
};
RefAction.prototype.print = function () {
console.log("RefAction id:" + this.id + " refs:" + this.refcnt + " len:" + this.fields.length);
};
return RefAction;
}(RefObject));
pxsim.RefAction = RefAction;
var pxtcore;
(function (pxtcore) {
function mkAction(reflen, len, fn) {
var r = new RefAction();
r.reflen = reflen;
r.func = fn;
for (var i = 0; i < len; ++i)
r.fields.push(null);
return r;
}
pxtcore.mkAction = mkAction;
function runAction3(a, a0, a1, a2) {
var cb = pxsim.getResume();
if (a instanceof RefAction) {
pxtrt.incr(a);
cb(new FnWrapper(a.func, a.fields, a0, a1, a2, function () {
pxtrt.decr(a);
}));
}
else {
// no-closure case
cb(new FnWrapper(a, null, a0, a1, a2, null));
}
}
pxtcore.runAction3 = runAction3;
function runAction2(a, a0, a1) {
runAction3(a, a0, a1, null);
}
pxtcore.runAction2 = runAction2;
function runAction1(a, v) {
runAction3(a, v, null, null);
}
pxtcore.runAction1 = runAction1;
function runAction0(a) {
runAction3(a, null, null, null);
}
pxtcore.runAction0 = runAction0;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
var RefLocal = (function (_super) {
__extends(RefLocal, _super);
function RefLocal() {
_super.apply(this, arguments);
this.v = 0;
}
RefLocal.prototype.print = function () {
console.log("RefLocal id:" + this.id + " refs:" + this.refcnt + " v:" + this.v);
};
return RefLocal;
}(RefObject));
pxsim.RefLocal = RefLocal;
var RefRefLocal = (function (_super) {
__extends(RefRefLocal, _super);
function RefRefLocal() {
_super.apply(this, arguments);
this.v = null;
}
RefRefLocal.prototype.destroy = function () {
decr(this.v);
};
RefRefLocal.prototype.print = function () {
console.log("RefRefLocal id:" + this.id + " refs:" + this.refcnt + " v:" + this.v);
};
return RefRefLocal;
}(RefObject));
pxsim.RefRefLocal = RefRefLocal;
var RefMap = (function (_super) {
__extends(RefMap, _super);
function RefMap() {
_super.apply(this, arguments);
this.vtable = 42;
this.data = [];
}
RefMap.prototype.findIdx = function (key) {
for (var i = 0; i < this.data.length; ++i) {
if (this.data[i].key >> 1 == key)
return i;
}
return -1;
};
RefMap.prototype.destroy = function () {
_super.prototype.destroy.call(this);
for (var i = 0; i < this.data.length; ++i) {
if (this.data[i].key & 1) {
decr(this.data[i].val);
}
this.data[i].val = 0;
}
this.data = [];
};
RefMap.prototype.print = function () {
console.log("RefMap id:" + this.id + " refs:" + this.refcnt + " size:" + this.data.length);
};
return RefMap;
}(RefObject));
pxsim.RefMap = RefMap;
function num(v) {
if (v === undefined)
return 0;
return v;
}
function ref(v) {
if (v === undefined)
return null;
return v;
}
function decr(v) {
if (!refCounting)
return;
if (v instanceof RefObject) {
var o = v;
check(o.refcnt > 0);
if (--o.refcnt == 0) {
delete liveRefObjs[o.id + ""];
o.destroy();
}
}
else if (typeof v == "string") {
if (stringLiterals && !stringLiterals.hasOwnProperty(v)) {
stringRefDelta(v, -1);
}
}
else if (!v) {
}
else if (typeof v == "function") {
}
else {
throw new Error("bad decr");
}
}
pxsim.decr = decr;
function setupStringLiterals(strings) {
// reset
liveRefObjs = {};
stringRefCounts = {};
// and set up strings
strings[""] = 1;
strings["true"] = 1;
strings["false"] = 1;
// comment out next line to disable string ref counting
stringLiterals = strings;
}
pxsim.setupStringLiterals = setupStringLiterals;
function stringRefDelta(s, n) {
if (!stringRefCounts.hasOwnProperty(s))
stringRefCounts[s] = 0;
var r = (stringRefCounts[s] += n);
if (r == 0)
delete stringRefCounts[s];
else
check(r > 0);
return r;
}
function initString(v) {
if (!v || !stringLiterals)
return v;
if (typeof v == "string" && !stringLiterals.hasOwnProperty(v))
stringRefDelta(v, 1);
return v;
}
pxsim.initString = initString;
function incr(v) {
if (!refCounting)
return v;
if (v instanceof RefObject) {
var o = v;
check(o.refcnt > 0);
o.refcnt++;
}
else if (stringLiterals && typeof v == "string" && !stringLiterals.hasOwnProperty(v)) {
var k = stringRefDelta(v, 1);
check(k > 1);
}
return v;
}
pxsim.incr = incr;
function dumpLivePointers() {
if (!refCounting)
return;
Object.keys(liveRefObjs).forEach(function (k) {
liveRefObjs[k].print();
});
Object.keys(stringRefCounts).forEach(function (k) {
var n = stringRefCounts[k];
console.log("Live String:", JSON.stringify(k), "refcnt=", n);
});
}
pxsim.dumpLivePointers = dumpLivePointers;
var pxtcore;
(function (pxtcore) {
pxtcore.incr = pxsim.incr;
pxtcore.decr = pxsim.decr;
function ptrOfLiteral(v) {
return v;
}
pxtcore.ptrOfLiteral = ptrOfLiteral;
function debugMemLeaks() {
dumpLivePointers();
}
pxtcore.debugMemLeaks = debugMemLeaks;
function allocate() {
pxsim.U.userError("allocate() called in simulator");
}
pxtcore.allocate = allocate;
function templateHash() {
return 0;
}
pxtcore.templateHash = templateHash;
function programHash() {
return 0;
}
pxtcore.programHash = programHash;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
var pxtrt;
(function (pxtrt) {
pxtrt.incr = pxsim.incr;
pxtrt.decr = pxsim.decr;
function toInt8(v) {
return ((v & 0xff) << 24) >> 24;
}
pxtrt.toInt8 = toInt8;
function toInt16(v) {
return ((v & 0xffff) << 16) >> 16;
}
pxtrt.toInt16 = toInt16;
function toInt32(v) {
return v | 0;
}
pxtrt.toInt32 = toInt32;
function toUInt8(v) {
return v & 0xff;
}
pxtrt.toUInt8 = toUInt8;
function toUInt16(v) {
return v & 0xffff;
}
pxtrt.toUInt16 = toUInt16;
function nullFix(v) {
if (v === null || v === undefined || v === false)
return 0;
if (v === true)
return 1;
return v;
}
pxtrt.nullFix = nullFix;
function nullCheck(v) {
if (!v)
pxsim.U.userError("Using null value.");
}
pxtrt.nullCheck = nullCheck;
function panic(code) {
pxsim.U.userError("PANIC! Code " + code);
}
pxtrt.panic = panic;
function stringToBool(s) {
pxtrt.decr(s);
return s ? 1 : 0;
}
pxtrt.stringToBool = stringToBool;
function ptrToBool(v) {
pxtrt.decr(v);
return v ? 1 : 0;
}
pxtrt.ptrToBool = ptrToBool;
function emptyToNull(s) {
if (s == "")
return 0;
return s;
}
pxtrt.emptyToNull = emptyToNull;
function ldfld(r, idx) {
nullCheck(r);
check(!r.isRef(idx));
var v = num(r.fields[idx]);
pxtrt.decr(r);
return v;
}
pxtrt.ldfld = ldfld;
function stfld(r, idx, v) {
nullCheck(r);
check(!r.isRef(idx));
r.fields[idx] = v;
pxtrt.decr(r);
}
pxtrt.stfld = stfld;
function ldfldRef(r, idx) {
nullCheck(r);
check(r.isRef(idx));
var v = pxtrt.incr(ref(r.fields[idx]));
pxtrt.decr(r);
return v;
}
pxtrt.ldfldRef = ldfldRef;
function stfldRef(r, idx, v) {
nullCheck(r);
check(r.isRef(idx));
pxtrt.decr(r.fields[idx]);
r.fields[idx] = v;
pxtrt.decr(r);
}
pxtrt.stfldRef = stfldRef;
function ldloc(r) {
return r.v;
}
pxtrt.ldloc = ldloc;
function ldlocRef(r) {
return pxtrt.incr(r.v);
}
pxtrt.ldlocRef = ldlocRef;
function stloc(r, v) {
r.v = v;
}
pxtrt.stloc = stloc;
function stlocRef(r, v) {
pxtrt.decr(r.v);
r.v = v;
}
pxtrt.stlocRef = stlocRef;
function mkloc() {
return new RefLocal();
}
pxtrt.mkloc = mkloc;
function mklocRef() {
return new RefRefLocal();
}
pxtrt.mklocRef = mklocRef;
// Store a captured local in a closure. It returns the action, so it can be chained.
function stclo(a, idx, v) {
check(0 <= idx && idx < a.fields.length);
check(a.fields[idx] === null);
//console.log(`STCLO [${idx}] = ${v}`)
a.fields[idx] = v;
return a;
}
pxtrt.stclo = stclo;
function runtimeWarning(msg) {
pxsim.Runtime.postMessage(pxsim.getWarningMessage(msg));
}
pxtrt.runtimeWarning = runtimeWarning;
function mkMap() {
return new RefMap();
}
pxtrt.mkMap = mkMap;
function mapGet(map, key) {
var i = map.findIdx(key);
if (i < 0) {
pxtrt.decr(map);
return 0;
}
var r = map.data[i].val;
pxtrt.decr(map);
return r;
}
pxtrt.mapGet = mapGet;
function mapGetRef(map, key) {
var i = map.findIdx(key);
if (i < 0) {
pxtrt.decr(map);
return 0;
}
var r = pxtrt.incr(map.data[i].val);
pxtrt.decr(map);
return r;
}
pxtrt.mapGetRef = mapGetRef;
function mapSet(map, key, val) {
var i = map.findIdx(key);
if (i < 0) {
map.data.push({
key: key << 1,
val: val
});
}
else {
if (map.data[i].key & 1) {
pxtrt.decr(map.data[i].val);
map.data[i].key = key << 1;
}
map.data[i].val = val;
}
pxtrt.decr(map);
}
pxtrt.mapSet = mapSet;
function mapSetRef(map, key, val) {
var i = map.findIdx(key);
if (i < 0) {
map.data.push({
key: (key << 1) | 1,
val: val
});
}
else {
if (map.data[i].key & 1) {
pxtrt.decr(map.data[i].val);
}
else {
map.data[i].key = (key << 1) | 1;
}
map.data[i].val = val;
}
pxtrt.decr(map);
}
pxtrt.mapSetRef = mapSetRef;
})(pxtrt = pxsim.pxtrt || (pxsim.pxtrt = {}));
var pxtcore;
(function (pxtcore) {
function mkClassInstance(vtable) {
check(!!vtable.methods);
check(!!vtable.refmask);
var r = new RefRecord();
r.vtable = vtable;
var len = vtable.refmask.length;
for (var i = 0; i < len; ++i)
r.fields.push(0);
return r;
}
pxtcore.mkClassInstance = mkClassInstance;
})(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {}));
var thread;
(function (thread) {
thread.panic = pxtrt.panic;
function pause(ms) {
var cb = pxsim.getResume();
setTimeout(function () { cb(); }, ms);
}
thread.pause = pause;
function runInBackground(a) {
pxsim.runtime.runFiberAsync(a).done();
}
thread.runInBackground = runInBackground;
function forever(a) {
function loop() {
pxsim.runtime.runFiberAsync(a)
.then(function () { return Promise.delay(20); })
.then(loop)
.done();
}
pxtrt.nullCheck(a);
incr(a);
loop();
}
thread.forever = forever;
})(thread = pxsim.thread || (pxsim.thread = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
// A ref-counted collection of either primitive or ref-counted objects (String, Image,
// user-defined record, another collection)
var RefCollection = (function (_super) {
__extends(RefCollection, _super);
//undefiend or null values need to be handled specially to support default values
//default values of boolean, string, number & object arrays are respectively, false, null, 0, null
//All of the default values are implemented by mapping undefined\null to zero.
// 1 - collection of refs (need decr)
// 2 - collection of strings (in fact we always have 3, never 2 alone)
function RefCollection(flags) {
_super.call(this);
this.flags = flags;
this.data = [];
}
RefCollection.prototype.destroy = function () {
var data = this.data;
if (this.flags & 1)
for (var i = 0; i < data.length; ++i) {
pxsim.decr(data[i]);
data[i] = 0;
}
this.data = [];
};
RefCollection.prototype.isValidIndex = function (x) {
return (x >= 0 && x < this.data.length);
};
RefCollection.prototype.push = function (x) {
this.data.push(x);
};
RefCollection.prototype.pop = function () {
var x = this.data.pop();
if (x != undefined) {
return 0;
}
return x;
};
RefCollection.prototype.getLength = function () {
return this.data.length;
};
RefCollection.prototype.setLength = function (x) {
this.data.length = x;
};
RefCollection.prototype.getAt = function (x) {
if (this.data[x] != undefined) {
return this.data[x];
}
return 0;
};
RefCollection.prototype.setAt = function (x, y) {
this.data[x] = y;
};
RefCollection.prototype.insertAt = function (x, y) {
this.data.splice(x, 0, y);
};
RefCollection.prototype.removeAt = function (x) {
var ret = this.data.splice(x, 1);
if (ret[0] == undefined) {
return 0;
}
return ret[0]; //return the deleted element.
};
RefCollection.prototype.indexOf = function (x, start) {
if (x != 0) {
return this.data.indexOf(x, start);
}
//As we treat undefined same as 0 which is default value for all the arrays, will need to search both.
var defaultValueIndex = this.data.indexOf(x, start);
var undefinedIndex = -1;
for (var i = start; i < this.data.length; i++) {
if (this.data[i] == undefined) {
undefinedIndex = i;
break;
}
}
if (defaultValueIndex < undefinedIndex || undefinedIndex == -1) {
return defaultValueIndex;
}
return undefinedIndex;
};
RefCollection.prototype.print = function () {
console.log("RefCollection id:" + this.id + " refs:" + this.refcnt + " len:" + this.data.length + " flags:" + this.flags + " d0:" + this.data[0]);
};
return RefCollection;
}(pxsim.RefObject));
pxsim.RefCollection = RefCollection;
var Array_;
(function (Array_) {
function mk(f) {
return new RefCollection(f);
}
Array_.mk = mk;
function length(c) {
return c.getLength();
}
Array_.length = length;
function setLength(c, x) {
c.setLength(x);
}
Array_.setLength = setLength;
function push(c, x) {
if (c.flags & 1)
pxsim.incr(x);
c.push(x);
}
Array_.push = push;
function pop(c, x) {
var ret = c.pop();
if (c.flags & 1)
pxsim.decr(ret);
return ret;
}
Array_.pop = pop;
function getAt(c, x) {
var tmp = c.getAt(x);
if (c.flags & 1)
pxsim.incr(tmp);
return tmp;
}
Array_.getAt = getAt;
function removeAt(c, x) {
if (!c.isValidIndex(x))
return;
if (c.flags & 1) {
pxsim.decr(c.getAt(x));
}
return c.removeAt(x);
}
Array_.removeAt = removeAt;
function insertAt(c, x, y) {
if (c.flags & 1)
pxsim.incr(y);
c.insertAt(x, y);
}
Array_.insertAt = insertAt;
function setAt(c, x, y) {
if (c.isValidIndex(x) && (c.flags & 1)) {
//if there is an existing element handle refcount
pxsim.decr(c.getAt(x));
pxsim.incr(y);
}
c.setAt(x, y);
}
Array_.setAt = setAt;
function indexOf(c, x, start) {
return c.indexOf(x, start);
}
Array_.indexOf = indexOf;
function removeElement(c, x) {
var idx = indexOf(c, x, 0);
if (idx >= 0) {
removeAt(c, idx);
return 1;
}
return 0;
}
Array_.removeElement = removeElement;
})(Array_ = pxsim.Array_ || (pxsim.Array_ = {}));
var Math_;
(function (Math_) {
function sqrt(n) {
return Math.sqrt(n) >>> 0;
}
Math_.sqrt = sqrt;
function pow(x, y) {
return Math.pow(x, y) >>> 0;
}
Math_.pow = pow;
function random(max) {
if (max < 1)
return 0;
var r = 0;
do {
r = Math.floor(Math.random() * max);
} while (r == max);
return r;
}
Math_.random = random;
})(Math_ = pxsim.Math_ || (pxsim.Math_ = {}));
// for explanations see:
// http://stackoverflow.com/questions/3428136/javascript-integer-math-incorrect-results (second answer)
// (but the code below doesn't come from there; I wrote it myself)
// TODO use Math.imul if available
function intMult(a, b) {
return (((a & 0xffff) * (b >>> 16) + (b & 0xffff) * (a >>> 16)) << 16) + ((a & 0xffff) * (b & 0xffff));
}
var Number_;
(function (Number_) {
function lt(x, y) { return x < y; }
Number_.lt = lt;
function le(x, y) { return x <= y; }
Number_.le = le;
function neq(x, y) { return !eq(x, y); }
Number_.neq = neq;
function eq(x, y) { return pxsim.pxtrt.nullFix(x) == pxsim.pxtrt.nullFix(y); }
Number_.eq = eq;
function gt(x, y) { return x > y; }
Number_.gt = gt;
function ge(x, y) { return x >= y; }
Number_.ge = ge;
function div(x, y) { return Math.floor(x / y) | 0; }
Number_.div = div;
function mod(x, y) { return x % y; }
Number_.mod = mod;
function toString(x) { return pxsim.initString(x + ""); }
Number_.toString = toString;
})(Number_ = pxsim.Number_ || (pxsim.Number_ = {}));
var thumb;
(function (thumb) {
function adds(x, y) { return (x + y) | 0; }
thumb.adds = adds;
function subs(x, y) { return (x - y) | 0; }
thumb.subs = subs;
function divs(x, y) { return Math.floor(x / y) | 0; }
thumb.divs = divs;
function muls(x, y) { return intMult(x, y); }
thumb.muls = muls;
function ands(x, y) { return x & y; }
thumb.ands = ands;
function orrs(x, y) { return x | y; }
thumb.orrs = orrs;
function eors(x, y) { return x ^ y; }
thumb.eors = eors;
function lsls(x, y) { return x << y; }
thumb.lsls = lsls;
function lsrs(x, y) { return x >>> y; }
thumb.lsrs = lsrs;
function asrs(x, y) { return x >> y; }
thumb.asrs = asrs;
function cmp_lt(x, y) { return x < y; }
thumb.cmp_lt = cmp_lt;
function cmp_le(x, y) { return x <= y; }
thumb.cmp_le = cmp_le;
function cmp_ne(x, y) { return !cmp_eq(x, y); }
thumb.cmp_ne = cmp_ne;
function cmp_eq(x, y) { return pxsim.pxtrt.nullFix(x) == pxsim.pxtrt.nullFix(y); }
thumb.cmp_eq = cmp_eq;
function cmp_gt(x, y) { return x > y; }
thumb.cmp_gt = cmp_gt;
function cmp_ge(x, y) { return x >= y; }
thumb.cmp_ge = cmp_ge;
function ignore(v) { return v; }
thumb.ignore = ignore;
})(thumb = pxsim.thumb || (pxsim.thumb = {}));
var String_;
(function (String_) {
function mkEmpty() {
return "";
}
String_.mkEmpty = mkEmpty;
function fromCharCode(code) {
return pxsim.initString(String.fromCharCode(code));
}
String_.fromCharCode = fromCharCode;
function toNumber(s) {
return parseInt(s);
}
String_.toNumber = toNumber;
// TODO check edge-conditions
function concat(a, b) {
return pxsim.initString(a + b);
}
String_.concat = concat;
function substring(s, i, j) {
return pxsim.initString(s.slice(i, i + j));
}
String_.substring = substring;
function equals(s1, s2) {
return s1 == s2;
}
String_.equals = equals;
function compare(s1, s2) {
if (s1 == s2)
return 0;
if (s1 < s2)
return -1;
return 1;
}
String_.compare = compare;
function length(s) {
return s.length;
}
String_.length = length;
function isEmpty(s) {
return s == null || s.length == 0;
}
String_.isEmpty = isEmpty;
function substr(s, start, length) {
return pxsim.initString(s.substr(start, length));
}
String_.substr = substr;
function inRange(s, i) { return 0 <= i && i < s.length; }
function charAt(s, i) {
return inRange(s, i) ? pxsim.initString(s.charAt(i)) : null;
}
String_.charAt = charAt;
function charCodeAt(s, i) {
return inRange(s, i) ? s.charCodeAt(i) : 0;
}
String_.charCodeAt = charCodeAt;
})(String_ = pxsim.String_ || (pxsim.String_ = {}));
var Boolean_;
(function (Boolean_) {
function toString(v) {
return v ? "true" : "false";
}
Boolean_.toString = toString;
function bang(v) {
return !v;
}
Boolean_.bang = bang;
})(Boolean_ = pxsim.Boolean_ || (pxsim.Boolean_ = {}));
var RefBuffer = (function (_super) {
__extends(RefBuffer, _super);
function RefBuffer(data) {
_super.call(this);
this.data = data;
}
RefBuffer.prototype.print = function () {
console.log("RefBuffer id:" + this.id + " refs:" + this.refcnt + " len:" + this.data.length + " d0:" + this.data[0]);
};
return RefBuffer;
}(pxsim.RefObject));
pxsim.RefBuffer = RefBuffer;
var BufferMethods;
(function (BufferMethods) {
// keep in sync with C++!
(function (NumberFormat) {
NumberFormat[NumberFormat["Int8LE"] = 1] = "Int8LE";
NumberFormat[NumberFormat["UInt8LE"] = 2] = "UInt8LE";
NumberFormat[NumberFormat["Int16LE"] = 3] = "Int16LE";
NumberFormat[NumberFormat["UInt16LE"] = 4] = "UInt16LE";
NumberFormat[NumberFormat["Int32LE"] = 5] = "Int32LE";
NumberFormat[NumberFormat["Int8BE"] = 6] = "Int8BE";
NumberFormat[NumberFormat["UInt8BE"] = 7] = "UInt8BE";
NumberFormat[NumberFormat["Int16BE"] = 8] = "Int16BE";
NumberFormat[NumberFormat["UInt16BE"] = 9] = "UInt16BE";
NumberFormat[NumberFormat["Int32BE"] = 10] = "Int32BE";
})(BufferMethods.NumberFormat || (BufferMethods.NumberFormat = {}));
var NumberFormat = BufferMethods.NumberFormat;
;
function fmtInfoCore(fmt) {
switch (fmt) {
case NumberFormat.Int8LE: return -1;
case NumberFormat.UInt8LE: return 1;
case NumberFormat.Int16LE: return -2;
case NumberFormat.UInt16LE: return 2;
case NumberFormat.Int32LE: return -4;
case NumberFormat.Int8BE: return -10;
case NumberFormat.UInt8BE: return 10;
case NumberFormat.Int16BE: return -20;
case NumberFormat.UInt16BE: return 20;
case NumberFormat.Int32BE: return -40;
default: throw pxsim.U.userError("bad format");
}
}
function fmtInfo(fmt) {
var size = fmtInfoCore(fmt);
var signed = false;
if (size < 0) {
signed = true;
size = -size;
}
var swap = false;
if (size >= 10) {
swap = true;
size /= 10;
}
return { size: size, signed: signed, swap: swap };
}
function getNumber(buf, fmt, offset) {
var inf = fmtInfo(fmt);
var r = 0;
for (var i = 0; i < inf.size; ++i) {
r <<= 8;
var off = inf.swap ? offset + i : offset + inf.size - i - 1;
r |= buf.data[off];
}
if (inf.signed) {
var missingBits = 32 - (inf.size * 8);
r = (r << missingBits) >> missingBits;
}
return r;
}
BufferMethods.getNumber = getNumber;
function setNumber(buf, fmt, offset, r) {
var inf = fmtInfo(fmt);
for (var i = 0; i < inf.size; ++i) {
var off = !inf.swap ? offset + i : offset + inf.size - i - 1;
buf.data[off] = (r & 0xff);
r >>= 8;
}
}
BufferMethods.setNumber = setNumber;
function createBuffer(size) {
return new RefBuffer(new Uint8Array(size));
}
BufferMethods.createBuffer = createBuffer;
function getBytes(buf) {
// not sure if this is any useful...
return buf.data;
}
BufferMethods.getBytes = getBytes;
function inRange(buf, off) {
return 0 <= off && off < buf.data.length;
}
function getByte(buf, off) {
if (inRange(buf, off))
return buf.data[off];
else
return 0;
}
BufferMethods.getByte = getByte;
function setByte(buf, off, v) {
if (inRange(buf, off))
buf.data[off] = v;
}
BufferMethods.setByte = setByte;
function length(buf) {
return buf.data.length;
}
BufferMethods.length = length;
function fill(buf, value, offset, length) {
if (offset === void 0) { offset = 0; }
if (length === void 0) { length = -1; }
if (offset < 0 || offset > buf.data.length)
return;
if (length < 0)
length = buf.data.length;
length = Math.min(length, buf.data.length - offset);
buf.data.fill(value, offset, offset + length);
}
BufferMethods.fill = fill;
function slice(buf, offset, length) {
offset = Math.min(buf.data.length, offset);
if (length < 0)
length = buf.data.length;
length = Math.min(length, buf.data.length - offset);
return new RefBuffer(buf.data.slice(offset, offset + length));
}
BufferMethods.slice = slice;
function memmove(dst, dstOff, src, srcOff, len) {
if (src.buffer === dst.buffer) {
memmove(dst, dstOff, src.slice(srcOff, srcOff + len), 0, len);
}
else {
for (var i = 0; i < len; ++i)
dst[dstOff + i] = src[srcOff + i];
}
}
var INT_MIN = -0x80000000;
function shift(buf, offset, start, len) {
if (len < 0)
len = buf.data.length - start;
if (start < 0 || start + len > buf.data.length || start + len < start
|| len == 0 || offset == 0 || offset == INT_MIN)
return;
if (len == 0 || offset == 0 || offset == INT_MIN)
return;
if (offset <= -len || offset >= len) {
fill(buf, 0);
return;
}
if (offset < 0) {
offset = -offset;
memmove(buf.data, start + offset, buf.data, start, len - offset);
buf.data.fill(0, start, start + offset);
}
else {
len = len - offset;
memmove(buf.data, start, buf.data, start + offset, len);
buf.data.fill(0, start + len, start + len + offset);
}
}
BufferMethods.shift = shift;
function rotate(buf, offset, start, len) {
if (len < 0)
len = buf.data.length - start;
if (start < 0 || start + len > buf.data.length || start + len < start
|| len == 0 || offset == 0 || offset == INT_MIN)
return;
if (offset < 0)
offset += len << 8; // try to make it positive
offset %= len;
if (offset < 0)
offset += len;
var data = buf.data;
var n_first = offset;
var first = 0;
var next = n_first;
var last = len;
while (first != next) {
var tmp = data[first + start];
data[first++ + start] = data[next + start];
data[next++ + start] = tmp;
if (next == last) {
next = n_first;
}
else if (first == n_first) {
n_first = next;
}
}
}
BufferMethods.rotate = rotate;
function write(buf, dstOffset, src, srcOffset, length) {
if (srcOffset === void 0) { srcOffset = 0; }
if (length === void 0) { length = -1; }
if (length < 0)
length = src.data.length;
if (srcOffset < 0 || dstOffset < 0 || dstOffset > buf.data.length)
return;
length = Math.min(src.data.length - srcOffset, buf.data.length - dstOffset);
if (length < 0)
return;
memmove(buf.data, dstOffset, src.data, srcOffset, length);
}
BufferMethods.write = write;
})(BufferMethods = pxsim.BufferMethods || (pxsim.BufferMethods = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var logs;
(function (logs) {
var TrendChartElement = (function () {
function TrendChartElement(log, className) {
this.log = log;
this.vpw = 80;
this.vph = 15;
this.log = log;
this.element = pxsim.svg.elt("svg");
pxsim.svg.hydrate(this.element, { class: className, viewBox: "0 0 " + this.vpw + " " + this.vph });
this.g = pxsim.svg.child(this.element, "g");
this.line = pxsim.svg.child(this.g, "polyline");
}
TrendChartElement.prototype.render = function () {
var _this = this;
var data = this.log.accvalues.slice(-25); // take last 10 entry
var margin = 2;
var times = data.map(function (d) { return d.t; });
var values = data.map(function (d) { return d.v; });
var maxt = Math.max.apply(null, times);
var mint = Math.min.apply(null, times);
var maxv = Math.max.apply(null, values);
var minv = Math.min.apply(null, values);
var h = (maxv - minv) || 10;
var w = (maxt - mint) || 10;
var points = data.map(function (d) { return ((d.t - mint) / w * _this.vpw + "," + (_this.vph - (d.v - minv) / h * (_this.vph - 2 * margin) - margin)); }).join(' ');
pxsim.svg.hydrate(this.line, { points: points });
};
return TrendChartElement;
}());
var LogViewElement = (function () {
function LogViewElement(props) {
var _this = this;
this.props = props;
this.shouldScroll = false;
this.entries = [];
this.serialBuffers = {};
this.dropSim = false; // drop simulator events
this.registerEvents();
this.registerChromeSerial();
this.element = document.createElement("div");
this.element.className = "ui segment hideempty logs";
if (this.props.onClick)
this.element.onclick = function () { return _this.props.onClick(_this.rows()); };
}
LogViewElement.prototype.setLabel = function (text, theme) {
if (this.labelElement && this.labelElement.innerText == text)
return;
if (this.labelElement) {
if (this.labelElement.parentElement)
this.labelElement.parentElement.removeChild(this.labelElement);
this.labelElement = undefined;
}
if (text) {
this.labelElement = document.createElement("a");
this.labelElement.className = "ui " + theme + " top right attached mini label";
this.labelElement.appendChild(document.createTextNode(text));
}
};
LogViewElement.prototype.hasTrends = function () {
return this.entries.some(function (entry) { return !!entry.chartElement; });
};
// creates a deep clone of the log entries
LogViewElement.prototype.rows = function () {
return this.entries.map(function (e) {
return {
id: e.id,
theme: e.theme,
variable: e.variable,
accvalues: e.accvalues ? e.accvalues.slice(0) : undefined,
time: e.time,
value: e.value,
source: e.source,
count: e.count
};
});
};
LogViewElement.prototype.streamPayload = function (startTime) {
// filter out data
var es = this.entries.filter(function (e) { return !!e.accvalues && e.time + e.accvalues[e.accvalues.length - 1].t >= startTime; });
if (es.length == 0)
return undefined;
var fields = { "timestamp": 1, "partition": 1 };
var rows = [];
function entryVariable(e) {
return /^\s*$/.test(e.variable) ? 'data' : e.variable;
}
// collect fields
es.forEach(function (e) {
var n = entryVariable(e);
if (!fields[n])
fields[n] = 1;
});
// collapse data and fill values
var fs = Object.keys(fields);
es.forEach(function (e) {
var n = entryVariable(e);
var ei = fs.indexOf(n);
e.accvalues
.filter(function (v) { return (e.time + v.t) >= startTime; })
.forEach(function (v) {
var row = [e.time + v.t, 0];
for (var i = 2; i < fs.length; ++i)
row.push(i == ei ? v.v : null);
rows.push(row);
});
});
return { fields: fs, values: rows };
};
LogViewElement.prototype.registerChromeSerial = function () {
var _this = this;
var buffers = {};
var chrome = window.chrome;
if (chrome && chrome.runtime) {
var port = chrome.runtime.connect("cihhkhnngbjlhahcfmhekmbnnjcjdbge", { name: "micro:bit" });
port.onMessage.addListener(function (msg) {
if (msg.type == "serial") {
if (!_this.dropSim) {
_this.clear();
_this.dropSim = true;
}
var buf = (buffers[msg.id] || "") + msg.data;
var i = buf.lastIndexOf("\n");
if (i >= 0) {
var msgb = buf.substring(0, i + 1);
msgb.split('\n').filter(function (line) { return !!line; }).forEach(function (line) { return _this.appendEntry('microbit' + msg.id, line, 'black'); });
buf = buf.slice(i + 1);
}
buffers[msg.id] = buf;
}
});
}
};
LogViewElement.prototype.registerEvents = function () {
var _this = this;
window.addEventListener('message', function (ev) {
var msg = ev.data;
switch (msg.type || '') {
case 'serial':
var smsg = msg;
if (_this.dropSim && smsg.sim) {
// drop simulated event since we are receiving real events
return;
}
else if (!_this.dropSim && !smsg.sim) {
// first non-simulator serial event, drop all previous events
_this.clear();
_this.dropSim = true;
}
var value = smsg.data || '';
var source = smsg.id || '?';
var theme = source.split('-')[0] || '';
if (!/^[a-z]+$/.test(theme))
theme = 'black';
var buffer = _this.serialBuffers[source] || '';
for (var i = 0; i < value.length; ++i) {
switch (value.charCodeAt(i)) {
case 10:
_this.appendEntry(source, buffer, theme);
buffer = '';
break;
case 13:
break;
default:
buffer += value[i];
break;
}
}
_this.serialBuffers[source] = buffer;
break;
}
}, false);
};
LogViewElement.prototype.appendEntry = function (source, value, theme) {
var _this = this;
if (this.labelElement && !this.labelElement.parentElement)
this.element.insertBefore(this.labelElement, this.element.firstElementChild);
var ens = this.entries;
while (ens.length > this.props.maxEntries) {
var po = ens.shift();
if (po.element && po.element.parentElement)
po.element.parentElement.removeChild(po.element);
}
// find the entry with same source
var last = undefined;
var m = /^\s*(([^:]+):)?\s*(-?\d+)/i.exec(value);
var variable = m ? (m[2] || ' ') : undefined;
var nvalue = m ? parseInt(m[3]) : null;
for (var i = ens.length - 1; i >= 0; --i) {
if (ens[i].source == source &&
((i == ens.length - 1 && ens[i].value == value) ||
(variable && ens[i].variable == variable))) {
last = ens[i];
break;
}
}
if (last) {
last.value = value;
if (last.accvalues) {
last.accvalues.push({
t: Date.now() - last.time,
v: nvalue
});
if (last.accvalues.length > this.props.maxAccValues)
last.accvalues.shift();
}
else if (!last.countElement) {
last.countElement = document.createElement("span");
last.countElement.className = 'ui log counter';
last.element.insertBefore(last.countElement, last.element.firstChild);
}
last.count++;
this.scheduleRender(last);
}
else {
var e_1 = {
id: LogViewElement.counter++,
theme: theme,
time: Date.now(),
value: value,
source: source,
count: 1,
dirty: true,
variable: variable,
accvalues: nvalue != null ? [{ t: 0, v: nvalue }] : undefined,
element: document.createElement("div"),
valueElement: document.createTextNode('')
};
e_1.element.className = "ui log " + e_1.theme;
var raiseTrends = false;
if (e_1.accvalues) {
e_1.accvaluesElement = document.createElement('span');
e_1.accvaluesElement.className = "ui log " + e_1.theme + " gauge";
e_1.chartElement = new TrendChartElement(e_1, "ui trend " + e_1.theme);
if (this.props.onTrendChartClick) {
e_1.chartElement.element.onclick = function () { return _this.props.onTrendChartClick(e_1); };
e_1.chartElement.element.className += " link";
}
e_1.element.appendChild(e_1.accvaluesElement);
e_1.element.appendChild(e_1.chartElement.element);
raiseTrends = true;
}
e_1.element.appendChild(e_1.valueElement);
ens.push(e_1);
this.element.appendChild(e_1.element);
this.scheduleRender(e_1);
if (raiseTrends && this.props.onTrendChartChanged)
this.props.onTrendChartChanged();
}
};
LogViewElement.prototype.scheduleRender = function (e) {
var _this = this;
e.dirty = true;
if (!this.renderFiberId)
this.renderFiberId = setTimeout(function () { return _this.render(); }, 50);
};
LogViewElement.prototype.clear = function () {
this.entries = [];
if (this.labelElement && this.labelElement.parentElement)
this.labelElement.parentElement.removeChild(this.labelElement);
this.element.innerHTML = '';
this.serialBuffers = {};
this.dropSim = false;
if (this.props.onTrendChartChanged)
this.props.onTrendChartChanged();
};
LogViewElement.prototype.render = function () {
this.entries.forEach(function (entry) {
if (!entry.dirty)
return;
if (entry.countElement)
entry.countElement.innerText = entry.count.toString();
if (entry.accvaluesElement)
entry.accvaluesElement.innerText = entry.value;
if (entry.chartElement)
entry.chartElement.render();
entry.valueElement.textContent = entry.accvalues ? '' : entry.value;
entry.dirty = false;
});
this.renderFiberId = 0;
};
LogViewElement.counter = 0;
return LogViewElement;
}());
logs.LogViewElement = LogViewElement;
function entriesToCSV(entries) {
// first log all data entries to CSV
var dataEntries = [];
var rows = entries.length;
entries.forEach(function (e) {
if (e.accvalues && e.accvalues.length > 0) {
dataEntries.push(e);
rows = Math.max(e.accvalues.length, rows);
}
});
var csv = '';
// name columns
csv += dataEntries.map(function (entry) { return (entry.theme + " time, " + entry.theme + " " + (entry.variable.trim() || "data")); })
.concat(['log time', 'log source', 'log message'])
.join(', ');
csv += '\n';
var _loop_2 = function(i) {
var cols = [];
dataEntries.forEach(function (entry) {
var t0 = entry.accvalues[0].t;
if (i < entry.accvalues.length) {
cols.push(((entry.accvalues[i].t - t0) / 1000).toString());
cols.push(entry.accvalues[i].v.toString());
}
else {
cols.push(' ');
cols.push(' ');
}
});
if (i < entries.length) {
var t0 = entries[0].time;
cols.push(((entries[i].time - t0) / 1000).toString());
cols.push(entries[i].source);
cols.push(entries[i].value);
}
csv += cols.join(', ') + '\n';
};
for (var i = 0; i < rows; ++i) {
_loop_2(i);
}
return csv;
}
logs.entriesToCSV = entriesToCSV;
function entryToCSV(entry) {
var t0 = entry.accvalues.length > 0 ? entry.accvalues[0].t : 0;
var csv = (entry.theme + " time, " + (entry.variable.trim() || "data") + "\n")
+ entry.accvalues.map(function (v) { return ((v.t - t0) / 1000) + ", " + v.v; }).join('\n');
return csv;
}
logs.entryToCSV = entryToCSV;
})(logs = pxsim.logs || (pxsim.logs = {}));
})(pxsim || (pxsim = {}));
/// <reference path="../typings/globals/bluebird/index.d.ts"/>
/// <reference path="../localtypings/pxtparts.d.ts"/>
var pxsim;
(function (pxsim) {
var U;
(function (U) {
function addClass(el, cls) {
if (el.classList)
el.classList.add(cls);
else if (el.className.indexOf(cls) < 0)
el.className += ' ' + cls;
}
U.addClass = addClass;
function removeClass(el, cls) {
if (el.classList)
el.classList.remove(cls);
else
el.className = el.className.replace(cls, '').replace(/\s{2,}/, ' ');
}
U.removeClass = removeClass;
function assert(cond, msg) {
if (msg === void 0) { msg = "Assertion failed"; }
if (!cond) {
debugger;
throw new Error(msg);
}
}
U.assert = assert;
function repeatMap(n, fn) {
n = n || 0;
var r = [];
for (var i = 0; i < n; ++i)
r.push(fn(i));
return r;
}
U.repeatMap = repeatMap;
function userError(msg) {
var e = new Error(msg);
e.isUserError = true;
throw e;
}
U.userError = userError;
function now() {
return Date.now();
}
U.now = now;
function nextTick(f) {
Promise._async._schedule(f);
}
U.nextTick = nextTick;
})(U = pxsim.U || (pxsim.U = {}));
function getResume() { return pxsim.runtime.getResume(); }
pxsim.getResume = getResume;
var BaseBoard = (function () {
function BaseBoard() {
this.serialOutBuffer = '';
}
BaseBoard.prototype.updateView = function () { };
BaseBoard.prototype.receiveMessage = function (msg) { };
BaseBoard.prototype.initAsync = function (msg) {
this.runOptions = msg;
return Promise.resolve();
};
BaseBoard.prototype.kill = function () { };
BaseBoard.prototype.writeSerial = function (s) {
if (!s)
return;
for (var i = 0; i < s.length; ++i) {
var c = s[i];
switch (c) {
case '\n':
Runtime.postMessage({
type: 'serial',
data: this.serialOutBuffer + '\n',
id: pxsim.runtime.id,
sim: true
});
this.serialOutBuffer = '';
break;
case '\r': continue;
default: this.serialOutBuffer += c;
}
}
};
return BaseBoard;
}());
pxsim.BaseBoard = BaseBoard;
var CoreBoard = (function (_super) {
__extends(CoreBoard, _super);
function CoreBoard() {
var _this = this;
_super.call(this);
this.id = "b" + pxsim.Math_.random(2147483647);
this.bus = new pxsim.EventBus(pxsim.runtime);
// updates
this.updateSubscribers = [];
this.updateView = function () {
_this.updateSubscribers.forEach(function (sub) { return sub(); });
};
this.builtinParts = {};
this.builtinVisuals = {};
this.builtinPartVisuals = {};
}
CoreBoard.prototype.kill = function () {
_super.prototype.kill.call(this);
pxsim.AudioContextManager.stop();
};
return CoreBoard;
}(BaseBoard));
pxsim.CoreBoard = CoreBoard;
var BareBoard = (function (_super) {
__extends(BareBoard, _super);
function BareBoard() {
_super.apply(this, arguments);
}
return BareBoard;
}(BaseBoard));
function initBareRuntime() {
pxsim.runtime.board = new BareBoard();
var myRT = pxsim;
myRT.basic = {
pause: pxsim.thread.pause,
showNumber: function (n) {
var cb = getResume();
console.log("SHOW NUMBER:", n);
U.nextTick(cb);
}
};
myRT.serial = {
writeString: function (s) { return pxsim.runtime.board.writeSerial(s); },
};
myRT.pins = {
createBuffer: pxsim.BufferMethods.createBuffer,
};
myRT.control = {
inBackground: pxsim.thread.runInBackground
};
}
pxsim.initBareRuntime = initBareRuntime;
var EventQueue = (function () {
function EventQueue(runtime) {
this.runtime = runtime;
this.max = 5;
this.events = [];
}
EventQueue.prototype.push = function (e) {
if (!this.handler || this.events.length > this.max)
return;
this.events.push(e);
// if this is the first event pushed - start processing
if (this.events.length == 1)
this.poke();
};
EventQueue.prototype.poke = function () {
var _this = this;
var top = this.events.shift();
this.runtime.runFiberAsync(this.handler, top)
.done(function () {
// we're done processing the current event, if there is still something left to do, do it
if (_this.events.length > 0)
_this.poke();
});
};
Object.defineProperty(EventQueue.prototype, "handler", {
get: function () {
return this.mHandler;
},
set: function (a) {
if (this.mHandler) {
pxsim.pxtcore.decr(this.mHandler);
}
this.mHandler = a;
if (this.mHandler) {
pxsim.pxtcore.incr(this.mHandler);
}
},
enumerable: true,
configurable: true
});
return EventQueue;
}());
pxsim.EventQueue = EventQueue;
// overriden at loadtime by specific implementation
pxsim.initCurrentRuntime = undefined;
pxsim.handleCustomMessage = undefined;
var Runtime = (function () {
function Runtime(code) {
var _this = this;
this.numGlobals = 1000;
this.dead = false;
this.running = false;
this.startTime = 0;
this.globals = {};
this.numDisplayUpdates = 0;
U.assert(!!pxsim.initCurrentRuntime);
var yieldMaxSteps = 100;
// These variables are used by the generated code as well
// ---
var entryPoint;
var pxtrt = pxsim.pxtrt;
var breakpoints = null;
var breakAlways = false;
var globals = this.globals;
var yieldSteps = yieldMaxSteps;
// ---
var currResume;
var dbgResume;
var breakFrame = null; // for step-over
var lastYield = Date.now();
var __this = this;
function oops(msg) {
throw new Error("sim error: " + msg);
}
// referenced from eval()ed code
function doNothing(s) {
s.pc = -1;
return leave(s, s.parent.retval);
}
function maybeYield(s, pc, r0) {
yieldSteps = yieldMaxSteps;
var now = Date.now();
if (now - lastYield >= 20) {
lastYield = now;
s.pc = pc;
s.r0 = r0;
var cont = function () {
if (__this.dead)
return;
U.assert(s.pc == pc);
return loop(s);
};
//U.nextTick(cont)
setTimeout(cont, 5);
return true;
}
return false;
}
function setupDebugger(numBreakpoints) {
breakpoints = new Uint8Array(numBreakpoints);
breakAlways = true;
}
function isBreakFrame(s) {
if (!breakFrame)
return true; // nothing specified
for (var p = breakFrame; p; p = p.parent) {
if (p == s)
return true;
}
return false;
}
function breakpoint(s, retPC, brkId, r0) {
U.assert(!dbgResume);
s.pc = retPC;
s.r0 = r0;
Runtime.postMessage(pxsim.getBreakpointMsg(s, brkId));
dbgResume = function (m) {
dbgResume = null;
if (__this.dead)
return;
pxsim.runtime = __this;
U.assert(s.pc == retPC);
breakAlways = false;
breakFrame = null;
switch (m.subtype) {
case "resume":
break;
case "stepover":
breakAlways = true;
breakFrame = s;
break;
case "stepinto":
breakAlways = true;
break;
case "stepout":
breakAlways = true;
breakFrame = s.parent || s;
break;
}
return loop(s);
};
return null;
}
function handleDebuggerMsg(msg) {
switch (msg.subtype) {
case "config":
var cfg = msg;
if (cfg.setBreakpoints) {
breakpoints.fill(0);
for (var _i = 0, _a = cfg.setBreakpoints; _i < _a.length; _i++) {
var n = _a[_i];
breakpoints[n] = 1;
}
}
break;
case "pause":
breakAlways = true;
breakFrame = null;
break;
case "resume":
case "stepover":
case "stepinto":
case "stepout":
if (dbgResume)
dbgResume(msg);
break;
}
}
function loop(p) {
if (__this.dead) {
console.log("Runtime terminated");
return;
}
try {
pxsim.runtime = __this;
while (!!p) {
__this.currFrame = p;
__this.currFrame.overwrittenPC = false;
p = p.fn(p);
__this.maybeUpdateDisplay();
if (__this.currFrame.overwrittenPC)
p = __this.currFrame;
}
}
catch (e) {
if (__this.errorHandler)
__this.errorHandler(e);
else {
console.error("Simulator crashed, no error handler", e.stack);
var msg = pxsim.getBreakpointMsg(p, p.lastBrkId);
msg.exceptionMessage = e.message;
msg.exceptionStack = e.stack;
Runtime.postMessage(msg);
if (__this.postError)
__this.postError(e);
}
}
}
function actionCall(s, cb) {
if (cb)
s.finalCallback = cb;
s.depth = s.parent.depth + 1;
if (s.depth > 1000) {
U.userError("Stack overflow");
}
s.pc = 0;
return s;
}
function leave(s, v) {
s.parent.retval = v;
if (s.finalCallback)
s.finalCallback(v);
return s.parent;
}
function setupTop(cb) {
var s = setupTopCore(cb);
setupResume(s, 0);
return s;
}
function setupTopCore(cb) {
var frame = {
parent: null,
pc: 0,
depth: 0,
fn: function () {
if (cb)
cb(frame.retval);
return null;
}
};
return frame;
}
function topCall(fn, cb) {
U.assert(!!__this.board);
U.assert(!__this.running);
__this.setRunning(true);
var topFrame = setupTopCore(cb);
var frame = {
parent: topFrame,
fn: fn,
depth: 0,
pc: 0
};
loop(actionCall(frame));
}
function checkResumeConsumed() {
if (currResume)
oops("getResume() not called");
}
function setupResume(s, retPC) {
currResume = buildResume(s, retPC);
}
function buildResume(s, retPC) {
if (currResume)
oops("already has resume");
s.pc = retPC;
return function (v) {
if (__this.dead)
return;
pxsim.runtime = __this;
U.assert(s.pc == retPC);
// TODO should loop() be called here using U.nextTick?
// This matters if the simulator function calls cb()
// synchronously.
if (v instanceof pxsim.FnWrapper) {
var w = v;
var frame = {
parent: s,
fn: w.func,
lambdaArgs: [w.a0, w.a1, w.a2],
pc: 0,
caps: w.caps,
depth: s.depth + 1,
finalCallback: w.cb,
};
return loop(actionCall(frame));
}
s.retval = v;
return loop(s);
};
}
// tslint:disable-next-line
eval(code);
this.run = function (cb) { return topCall(entryPoint, cb); };
this.getResume = function () {
if (!currResume)
oops("noresume");
var r = currResume;
currResume = null;
return r;
};
this.setupTop = setupTop;
this.handleDebuggerMsg = handleDebuggerMsg;
this.entry = entryPoint;
this.overwriteResume = function (retPC) {
currResume = null;
if (retPC >= 0)
_this.currFrame.pc = retPC;
_this.currFrame.overwrittenPC = true;
};
pxsim.runtime = this;
pxsim.initCurrentRuntime();
}
Runtime.prototype.runningTime = function () {
return U.now() - this.startTime;
};
Runtime.prototype.runFiberAsync = function (a, arg0, arg1, arg2) {
var _this = this;
pxsim.incr(a);
return new Promise(function (resolve, reject) {
return U.nextTick(function () {
pxsim.runtime = _this;
_this.setupTop(resolve);
pxsim.pxtcore.runAction3(a, arg0, arg1, arg2);
pxsim.decr(a); // if it's still running, action.run() has taken care of incrementing the counter
});
});
};
Runtime.postMessage = function (data) {
if (!data)
return;
// TODO: origins
if (typeof window !== 'undefined' && window.parent && window.parent.postMessage) {
window.parent.postMessage(data, "*");
}
if (Runtime.messagePosted)
Runtime.messagePosted(data);
};
Runtime.prototype.kill = function () {
this.dead = true;
// TODO fix this
this.setRunning(false);
};
Runtime.prototype.updateDisplay = function () {
this.board.updateView();
};
Runtime.prototype.queueDisplayUpdate = function () {
this.numDisplayUpdates++;
};
Runtime.prototype.maybeUpdateDisplay = function () {
if (this.numDisplayUpdates) {
this.numDisplayUpdates = 0;
this.updateDisplay();
}
};
Runtime.prototype.setRunning = function (r) {
if (this.running != r) {
this.running = r;
if (this.running) {
this.startTime = U.now();
Runtime.postMessage({ type: 'status', runtimeid: this.id, state: 'running' });
}
else {
Runtime.postMessage({ type: 'status', runtimeid: this.id, state: 'killed' });
}
if (this.stateChanged)
this.stateChanged();
}
};
return Runtime;
}());
pxsim.Runtime = Runtime;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
(function (SimulatorState) {
SimulatorState[SimulatorState["Unloaded"] = 0] = "Unloaded";
SimulatorState[SimulatorState["Stopped"] = 1] = "Stopped";
SimulatorState[SimulatorState["Running"] = 2] = "Running";
SimulatorState[SimulatorState["Paused"] = 3] = "Paused";
})(pxsim.SimulatorState || (pxsim.SimulatorState = {}));
var SimulatorState = pxsim.SimulatorState;
(function (SimulatorDebuggerCommand) {
SimulatorDebuggerCommand[SimulatorDebuggerCommand["StepInto"] = 0] = "StepInto";
SimulatorDebuggerCommand[SimulatorDebuggerCommand["StepOver"] = 1] = "StepOver";
SimulatorDebuggerCommand[SimulatorDebuggerCommand["StepOut"] = 2] = "StepOut";
SimulatorDebuggerCommand[SimulatorDebuggerCommand["Resume"] = 3] = "Resume";
SimulatorDebuggerCommand[SimulatorDebuggerCommand["Pause"] = 4] = "Pause";
})(pxsim.SimulatorDebuggerCommand || (pxsim.SimulatorDebuggerCommand = {}));
var SimulatorDebuggerCommand = pxsim.SimulatorDebuggerCommand;
var SimulatorDriver = (function () {
function SimulatorDriver(container, options) {
if (options === void 0) { options = {}; }
this.container = container;
this.options = options;
this.themes = ["blue", "red", "green", "yellow"];
this.runId = '';
this.nextFrameId = 0;
this.frameCounter = 0;
this.runOptions = {};
this.state = SimulatorState.Unloaded;
this.frameCleanupTimeout = 0;
}
SimulatorDriver.prototype.setHwDebugger = function (hw) {
if (hw) {
// TODO set some visual on the simulator frame
// in future the simulator frame could reflect changes in the hardware
this.hwdbg = hw;
this.setState(SimulatorState.Running);
this.container.style.opacity = "0.3";
}
else {
delete this.container.style.opacity;
this.hwdbg = null;
this.setState(SimulatorState.Running);
this.stop();
}
};
SimulatorDriver.prototype.handleHwDebuggerMsg = function (msg) {
if (!this.hwdbg)
return;
this.handleMessage(msg);
};
SimulatorDriver.prototype.setThemes = function (themes) {
pxsim.U.assert(themes && themes.length > 0);
this.themes = themes;
};
SimulatorDriver.prototype.setState = function (state) {
if (this.state != state) {
this.state = state;
if (this.options.onStateChanged)
this.options.onStateChanged(this.state);
}
};
SimulatorDriver.prototype.postMessage = function (msg, source) {
if (this.hwdbg) {
this.hwdbg.postMessage(msg);
return;
}
// dispatch to all iframe besides self
var frames = this.container.getElementsByTagName("iframe");
if (source && (msg.type === 'eventbus' || msg.type == 'radiopacket')) {
if (frames.length < 2) {
this.container.appendChild(this.createFrame());
frames = this.container.getElementsByTagName("iframe");
}
else if (frames[1].dataset['runid'] != this.runId) {
this.startFrame(frames[1]);
}
}
for (var i = 0; i < frames.length; ++i) {
var frame = frames[i];
if (source && frame.contentWindow == source)
continue;
frame.contentWindow.postMessage(msg, "*");
}
};
SimulatorDriver.prototype.createFrame = function () {
var wrapper = document.createElement("div");
wrapper.className = 'simframe';
var frame = document.createElement('iframe');
frame.id = 'sim-frame-' + this.nextId();
frame.allowFullscreen = true;
frame.setAttribute('sandbox', 'allow-same-origin allow-scripts');
frame.sandbox.value = "allow-scripts allow-same-origin";
var simUrl = this.options.simUrl || (window.pxtConfig || {}).simUrl || "/sim/simulator.html";
if (this.runOptions.aspectRatio)
wrapper.style.paddingBottom = (100 / this.runOptions.aspectRatio) + "%";
frame.src = simUrl + '#' + frame.id;
frame.frameBorder = "0";
frame.dataset['runid'] = this.runId;
wrapper.appendChild(frame);
return wrapper;
};
SimulatorDriver.prototype.stop = function (unload) {
if (unload === void 0) { unload = false; }
this.postMessage({ type: 'stop' });
this.setState(SimulatorState.Stopped);
if (unload)
this.unload();
else {
var frames_2 = this.container.getElementsByTagName("iframe");
for (var i = 0; i < frames_2.length; ++i) {
var frame = frames_2[i];
if (!/grayscale/.test(frame.className))
pxsim.U.addClass(frame, "grayscale");
}
this.scheduleFrameCleanup();
}
};
SimulatorDriver.prototype.unload = function () {
this.cancelFrameCleanup();
this.container.innerHTML = '';
this.setState(SimulatorState.Unloaded);
};
SimulatorDriver.prototype.mute = function (mute) {
this.postMessage({ type: 'mute', mute: mute });
};
SimulatorDriver.prototype.cancelFrameCleanup = function () {
if (this.frameCleanupTimeout) {
clearTimeout(this.frameCleanupTimeout);
this.frameCleanupTimeout = 0;
}
};
SimulatorDriver.prototype.scheduleFrameCleanup = function () {
var _this = this;
this.cancelFrameCleanup();
this.frameCleanupTimeout = setTimeout(function () {
_this.frameCleanupTimeout = 0;
_this.cleanupFrames();
}, 5000);
};
SimulatorDriver.prototype.applyAspectRatio = function () {
var frames = this.container.getElementsByTagName("iframe");
for (var i = 0; i < frames.length; ++i) {
frames[i].parentElement.style.paddingBottom =
(100 / this.runOptions.aspectRatio) + "%";
}
};
SimulatorDriver.prototype.cleanupFrames = function () {
// drop unused extras frames after 5 seconds
var frames = this.container.getElementsByTagName("iframe");
for (var i = 1; i < frames.length; ++i) {
var frame = frames[i];
if (this.state == SimulatorState.Stopped
|| frame.dataset['runid'] != this.runId) {
if (this.options.removeElement)
this.options.removeElement(frame.parentElement);
else
frame.parentElement.remove();
}
}
};
SimulatorDriver.prototype.hide = function (completeHandler) {
if (!this.options.removeElement)
return;
var frames = this.container.getElementsByTagName("iframe");
for (var i = 0; i < frames.length; ++i) {
var frame = frames[i];
this.options.removeElement(frame.parentElement, completeHandler);
}
// Execute the complete handler if there are no frames in sim view
if (frames.length == 0 && completeHandler) {
completeHandler();
}
};
SimulatorDriver.prototype.unhide = function () {
if (!this.options.unhideElement)
return;
var frames = this.container.getElementsByTagName("iframe");
for (var i = 0; i < frames.length; ++i) {
var frame = frames[i];
this.options.unhideElement(frame.parentElement);
}
};
SimulatorDriver.prototype.run = function (js, opts) {
if (opts === void 0) { opts = {}; }
this.runOptions = opts;
this.runId = this.nextId();
this.addEventListeners();
// store information
this.currentRuntime = {
type: 'run',
boardDefinition: opts.boardDefinition,
parts: opts.parts,
fnArgs: opts.fnArgs,
code: js,
partDefinitions: opts.partDefinitions,
mute: opts.mute,
};
this.applyAspectRatio();
this.scheduleFrameCleanup();
// first frame
var frame = this.container.querySelector("iframe");
// lazy allocate iframe
if (!frame) {
var wrapper = this.createFrame();
this.container.appendChild(wrapper);
frame = wrapper.firstElementChild;
}
else
this.startFrame(frame);
this.setState(SimulatorState.Running);
};
SimulatorDriver.prototype.startFrame = function (frame) {
var msg = JSON.parse(JSON.stringify(this.currentRuntime));
var mc = '';
var m = /player=([A-Za-z0-9]+)/i.exec(window.location.href);
if (m)
mc = m[1];
msg.frameCounter = ++this.frameCounter;
msg.options = {
theme: this.themes[this.nextFrameId++ % this.themes.length],
player: mc
};
msg.id = msg.options.theme + "-" + this.nextId();
frame.dataset['runid'] = this.runId;
frame.contentWindow.postMessage(msg, "*");
pxsim.U.removeClass(frame, "grayscale");
};
SimulatorDriver.prototype.removeEventListeners = function () {
if (this.listener) {
window.removeEventListener('message', this.listener, false);
this.listener = undefined;
}
};
SimulatorDriver.prototype.handleMessage = function (msg, source) {
switch (msg.type || '') {
case 'ready':
var frameid = msg.frameid;
var frame = document.getElementById(frameid);
if (frame) {
this.startFrame(frame);
if (this.options.revealElement)
this.options.revealElement(frame);
}
break;
case 'simulator':
this.handleSimulatorCommand(msg);
break; //handled elsewhere
case 'serial': break; //handled elsewhere
case 'pxteditor':
case 'custom':
break; //handled elsewhere
case 'debugger':
this.handleDebuggerMessage(msg);
break;
default:
if (msg.type == 'radiopacket') {
// assign rssi noisy?
msg.rssi = 10;
}
this.postMessage(msg, source);
break;
}
};
SimulatorDriver.prototype.addEventListeners = function () {
var _this = this;
if (!this.listener) {
this.listener = function (ev) {
if (_this.hwdbg)
return;
_this.handleMessage(ev.data, ev.source);
};
window.addEventListener('message', this.listener, false);
}
};
SimulatorDriver.prototype.resume = function (c) {
var msg;
switch (c) {
case SimulatorDebuggerCommand.Resume:
msg = 'resume';
this.setState(SimulatorState.Running);
break;
case SimulatorDebuggerCommand.StepInto:
msg = 'stepinto';
this.setState(SimulatorState.Running);
break;
case SimulatorDebuggerCommand.StepOut:
msg = 'stepout';
this.setState(SimulatorState.Running);
break;
case SimulatorDebuggerCommand.StepOver:
msg = 'stepover';
this.setState(SimulatorState.Running);
break;
case SimulatorDebuggerCommand.Pause:
msg = 'pause';
break;
default:
console.debug('unknown command');
return;
}
this.postMessage({ type: 'debugger', subtype: msg });
};
SimulatorDriver.prototype.setBreakpoints = function (breakPoints) {
this.postDebuggerMessage("config", { setBreakpoints: breakPoints });
};
SimulatorDriver.prototype.handleSimulatorCommand = function (msg) {
if (this.options.onSimulatorCommand)
this.options.onSimulatorCommand(msg);
};
SimulatorDriver.prototype.handleDebuggerMessage = function (msg) {
console.log("DBG-MSG", msg.subtype, msg);
switch (msg.subtype) {
case "warning":
if (this.options.onDebuggerWarning)
this.options.onDebuggerWarning(msg);
break;
case "breakpoint":
var brk = msg;
if (this.state == SimulatorState.Running) {
if (brk.exceptionMessage)
this.stop();
else
this.setState(SimulatorState.Paused);
if (this.options.onDebuggerBreakpoint)
this.options.onDebuggerBreakpoint(brk);
}
else {
console.error("debugger: trying to pause from " + this.state);
}
break;
}
};
SimulatorDriver.prototype.postDebuggerMessage = function (subtype, data) {
if (data === void 0) { data = {}; }
var msg = JSON.parse(JSON.stringify(data));
msg.type = "debugger";
msg.subtype = subtype;
this.postMessage(msg);
};
SimulatorDriver.prototype.nextId = function () {
return this.nextFrameId++ + (Math.random() + '' + Math.random()).replace(/[^\d]/, '');
};
return SimulatorDriver;
}());
pxsim.SimulatorDriver = SimulatorDriver;
})(pxsim || (pxsim = {}));
/// <reference path="../typings/globals/bluebird/index.d.ts"/>
var pxsim;
(function (pxsim) {
;
;
function mkRange(a, b) {
var res = [];
for (; a < b; a++)
res.push(a);
return res;
}
pxsim.mkRange = mkRange;
function parseQueryString() {
var qs = window.location.search.substring(1);
var getQsVal = function (key) { return decodeURIComponent((qs.split(key + "=")[1] || "").split("&")[0] || ""); }; //.replace(/\+/g, " ");
return getQsVal;
}
pxsim.parseQueryString = parseQueryString;
var EventBus = (function () {
function EventBus(runtime) {
this.runtime = runtime;
this.queues = {};
}
EventBus.prototype.listen = function (id, evid, handler) {
var k = id + ":" + evid;
var queue = this.queues[k];
if (!queue)
queue = this.queues[k] = new pxsim.EventQueue(this.runtime);
queue.handler = handler;
};
EventBus.prototype.queue = function (id, evid, value) {
if (value === void 0) { value = 0; }
var k = id + ":" + evid;
var queue = this.queues[k];
if (queue)
queue.push(value);
};
return EventBus;
}());
pxsim.EventBus = EventBus;
var AnimationQueue = (function () {
function AnimationQueue(runtime) {
var _this = this;
this.runtime = runtime;
this.queue = [];
this.process = function () {
var top = _this.queue[0];
if (!top)
return;
if (_this.runtime.dead)
return;
runtime = _this.runtime;
var res = top.frame();
runtime.queueDisplayUpdate();
runtime.maybeUpdateDisplay();
if (res === false) {
_this.queue.shift();
// if there is already something in the queue, start processing
if (_this.queue[0]) {
_this.queue[0].setTimeoutHandle = setTimeout(_this.process, _this.queue[0].interval);
}
// this may push additional stuff
top.whenDone(false);
}
else {
top.setTimeoutHandle = setTimeout(_this.process, top.interval);
}
};
}
AnimationQueue.prototype.cancelAll = function () {
var q = this.queue;
this.queue = [];
for (var _i = 0, q_1 = q; _i < q_1.length; _i++) {
var a = q_1[_i];
a.whenDone(true);
if (a.setTimeoutHandle) {
clearTimeout(a.setTimeoutHandle);
}
}
};
AnimationQueue.prototype.cancelCurrent = function () {
var top = this.queue[0];
if (top) {
this.queue.shift();
top.whenDone(true);
if (top.setTimeoutHandle) {
clearTimeout(top.setTimeoutHandle);
}
}
};
AnimationQueue.prototype.enqueue = function (anim) {
if (!anim.whenDone)
anim.whenDone = function () { };
this.queue.push(anim);
// we start processing when the queue goes from 0 to 1
if (this.queue.length == 1)
this.process();
};
AnimationQueue.prototype.executeAsync = function (anim) {
var _this = this;
pxsim.U.assert(!anim.whenDone);
return new Promise(function (resolve, reject) {
anim.whenDone = resolve;
_this.enqueue(anim);
});
};
return AnimationQueue;
}());
pxsim.AnimationQueue = AnimationQueue;
var AudioContextManager;
(function (AudioContextManager) {
var _frequency = 0;
var _context; // AudioContext
var _vco; // OscillatorNode;
var _vca; // GainNode;
var _mute = false; //mute audio
function context() {
if (!_context)
_context = freshContext();
return _context;
}
function freshContext() {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
if (window.AudioContext) {
try {
// this call my crash.
// SyntaxError: audio resources unavailable for AudioContext construction
return new window.AudioContext();
}
catch (e) { }
}
return undefined;
}
function mute(mute) {
_mute = mute;
stop();
}
AudioContextManager.mute = mute;
function stop() {
if (_vca)
_vca.gain.value = 0;
_frequency = 0;
}
AudioContextManager.stop = stop;
function frequency() {
return _frequency;
}
AudioContextManager.frequency = frequency;
function tone(frequency, gain) {
if (_mute)
return;
if (frequency <= 0)
return;
_frequency = frequency;
var ctx = context();
if (!ctx)
return;
if (_vco) {
_vco.stop();
_vco.disconnect();
_vco = undefined;
}
gain = Math.max(0, Math.min(1, gain));
try {
_vco = ctx.createOscillator();
_vca = ctx.createGain();
_vco.type = 'triangle';
_vco.connect(_vca);
_vca.connect(ctx.destination);
_vca.gain.value = gain;
_vco.start(0);
}
catch (e) {
_vco = undefined;
_vca = undefined;
return;
}
_vco.frequency.value = frequency;
_vca.gain.value = gain;
}
AudioContextManager.tone = tone;
})(AudioContextManager = pxsim.AudioContextManager || (pxsim.AudioContextManager = {}));
pxsim.pointerEvents = typeof window != "undefined" && !!window.PointerEvent ? {
up: "pointerup",
down: "pointerdown",
move: "pointermove",
leave: "pointerleave"
} : {
up: "mouseup",
down: "mousedown",
move: "mousemove",
leave: "mouseleave"
};
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
function translateEl(el, xy) {
//TODO append translation instead of replacing the full transform
pxsim.svg.hydrate(el, { transform: "translate(" + xy[0] + " " + xy[1] + ")" });
}
visuals.translateEl = translateEl;
function composeSVG(opts) {
var _a = [opts.el1, opts.el2], a = _a[0], b = _a[1];
pxsim.U.assert(a.x == 0 && a.y == 0 && b.x == 0 && b.y == 0, "el1 and el2 x,y offsets not supported");
var setXY = function (e, x, y) { return pxsim.svg.hydrate(e, { x: x, y: y }); };
var setWH = function (e, w, h) {
if (w)
pxsim.svg.hydrate(e, { width: w });
if (h)
pxsim.svg.hydrate(e, { height: h });
};
var setWHpx = function (e, w, h) { return pxsim.svg.hydrate(e, { width: w + "px", height: h + "px" }); };
var scaleUnit = opts.scaleUnit2;
var aScalar = opts.scaleUnit2 / opts.scaleUnit1;
var bScalar = 1.0;
var aw = a.w * aScalar;
var ah = a.h * aScalar;
setWHpx(a.el, aw, ah);
var bw = b.w * bScalar;
var bh = b.h * bScalar;
setWHpx(b.el, bw, bh);
var _b = opts.margin, mt = _b[0], mr = _b[1], mb = _b[2], ml = _b[3];
var mm = opts.middleMargin;
var innerW = Math.max(aw, bw);
var ax = mr + (innerW - aw) / 2.0;
var ay = mt;
setXY(a.el, ax, ay);
var bx = mr + (innerW - bw) / 2.0;
var by = ay + ah + mm;
setXY(b.el, bx, by);
var edges = [ay, ay + ah, by, by + bh];
var w = mr + innerW + ml;
var h = mt + ah + mm + bh + mb;
var host = pxsim.svg.elt("svg", {
"version": "1.0",
"viewBox": "0 0 " + w + " " + h,
"class": "sim-bb",
});
setWH(host, opts.maxWidth, opts.maxHeight);
setXY(host, 0, 0);
var under = pxsim.svg.child(host, "g");
host.appendChild(a.el);
host.appendChild(b.el);
var over = pxsim.svg.child(host, "g");
var toHostCoord1 = function (xy) {
var x = xy[0], y = xy[1];
return [x * aScalar + ax, y * aScalar + ay];
};
var toHostCoord2 = function (xy) {
var x = xy[0], y = xy[1];
return [x * bScalar + bx, y * bScalar + by];
};
return {
under: under,
over: over,
host: host,
edges: edges,
scaleUnit: scaleUnit,
toHostCoord1: toHostCoord1,
toHostCoord2: toHostCoord2,
};
}
visuals.composeSVG = composeSVG;
function mkScaleFn(originUnit, targetUnit) {
return function (n) { return n * (targetUnit / originUnit); };
}
visuals.mkScaleFn = mkScaleFn;
function mkImageSVG(opts) {
var scaleFn = mkScaleFn(opts.imageUnitDist, opts.targetUnitDist);
var w = scaleFn(opts.width);
var h = scaleFn(opts.height);
var img = pxsim.svg.elt("image", {
width: w,
height: h
});
var href = img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', "" + opts.image);
return { el: img, w: w, h: h, x: 0, y: 0 };
}
visuals.mkImageSVG = mkImageSVG;
function findDistSqrd(a, b) {
var x = a[0] - b[0];
var y = a[1] - b[1];
return x * x + y * y;
}
visuals.findDistSqrd = findDistSqrd;
function findClosestCoordIdx(a, bs) {
var dists = bs.map(function (b) { return findDistSqrd(a, b); });
var minIdx = dists.reduce(function (prevIdx, currDist, currIdx, arr) {
return currDist < arr[prevIdx] ? currIdx : prevIdx;
}, 0);
return minIdx;
}
visuals.findClosestCoordIdx = findClosestCoordIdx;
function mkTxt(cx, cy, size, rot, txt, txtXOffFactor, txtYOffFactor) {
var el = pxsim.svg.elt("text");
//HACK: these constants (txtXOffFactor, txtYOffFactor) tweak the way this algorithm knows how to center the text
txtXOffFactor = txtXOffFactor || -0.33333;
txtYOffFactor = txtYOffFactor || 0.3;
var xOff = txtXOffFactor * size * txt.length;
var yOff = txtYOffFactor * size;
pxsim.svg.hydrate(el, {
style: "font-size:" + size + "px;",
transform: "translate(" + cx + " " + cy + ") rotate(" + rot + ") translate(" + xOff + " " + yOff + ")"
});
pxsim.svg.addClass(el, "noselect");
el.textContent = txt;
return el;
}
visuals.mkTxt = mkTxt;
visuals.GPIO_WIRE_COLORS = ["pink", "orange", "yellow", "green", "purple"];
visuals.WIRE_COLOR_MAP = {
black: "#514f4d",
white: "#fcfdfc",
gray: "#acabab",
purple: "#a772a1",
blue: "#01a6e8",
green: "#3cce73",
yellow: "#ece600",
orange: "#fdb262",
red: "#f44f43",
brown: "#c89764",
pink: "#ff80fa"
};
function mapWireColor(clr) {
return visuals.WIRE_COLOR_MAP[clr] || clr;
}
visuals.mapWireColor = mapWireColor;
;
visuals.PIN_DIST = 15;
//expects rgb from 0,255, gives h in [0,360], s in [0, 100], l in [0, 100]
function rgbToHsl(rgb) {
var r = rgb[0], g = rgb[1], b = rgb[2];
var _a = [r / 255, g / 255, b / 255], r$ = _a[0], g$ = _a[1], b$ = _a[2];
var cMin = Math.min(r$, g$, b$);
var cMax = Math.max(r$, g$, b$);
var cDelta = cMax - cMin;
var h, s, l;
var maxAndMin = cMax + cMin;
//lum
l = (maxAndMin / 2) * 100;
if (cDelta === 0)
s = h = 0;
else {
//hue
if (cMax === r$)
h = 60 * (((g$ - b$) / cDelta) % 6);
else if (cMax === g$)
h = 60 * (((b$ - r$) / cDelta) + 2);
else if (cMax === b$)
h = 60 * (((r$ - g$) / cDelta) + 4);
//sat
if (l > 50)
s = 100 * (cDelta / (2 - maxAndMin));
else
s = 100 * (cDelta / maxAndMin);
}
return [Math.floor(h), Math.floor(s), Math.floor(l)];
}
visuals.rgbToHsl = rgbToHsl;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var svg;
(function (svg_1) {
function parseString(xml) {
return new DOMParser().parseFromString(xml, "image/svg+xml").querySelector("svg");
}
svg_1.parseString = parseString;
function toDataUri(xml) {
return 'data:image/svg+xml,' + encodeURI(xml);
}
svg_1.toDataUri = toDataUri;
var pt;
function cursorPoint(pt, svg, evt) {
pt.x = evt.clientX;
pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
svg_1.cursorPoint = cursorPoint;
function rotateElement(el, originX, originY, degrees) {
el.setAttribute('transform', "translate(" + originX + "," + originY + ") rotate(" + (degrees + 90) + ") translate(" + -originX + "," + -originY + ")");
}
svg_1.rotateElement = rotateElement;
function addClass(el, cls) {
if (el.classList)
el.classList.add(cls);
else if (el.className.baseVal.indexOf(cls) < 0)
el.className.baseVal += ' ' + cls;
}
svg_1.addClass = addClass;
function removeClass(el, cls) {
if (el.classList)
el.classList.remove(cls);
else
el.className.baseVal = el.className.baseVal.replace(cls, '').replace(/\s{2,}/, ' ');
}
svg_1.removeClass = removeClass;
function hydrate(el, props) {
for (var k in props) {
if (k == "title") {
svg.title(el, props[k]);
}
else
el.setAttributeNS(null, k, props[k]);
}
}
svg_1.hydrate = hydrate;
function elt(name, props) {
var el = document.createElementNS("http://www.w3.org/2000/svg", name);
if (props)
svg.hydrate(el, props);
return el;
}
svg_1.elt = elt;
function child(parent, name, props) {
var el = svg.elt(name, props);
parent.appendChild(el);
return el;
}
svg_1.child = child;
function mkPath(cls, data, title) {
var p = { class: cls, d: data };
if (title)
p["title"] = title;
var el = svg.elt("path");
svg.hydrate(el, p);
return el;
}
svg_1.mkPath = mkPath;
function path(parent, cls, data, title) {
var el = mkPath(cls, data, title);
parent.appendChild(el);
return el;
}
svg_1.path = path;
function fill(el, c) {
el.style.fill = c;
}
svg_1.fill = fill;
function filter(el, c) {
el.style.filter = c;
}
svg_1.filter = filter;
function fills(els, c) {
els.forEach(function (el) { return el.style.fill = c; });
}
svg_1.fills = fills;
function buttonEvents(el, move, start, stop) {
var captured = false;
el.addEventListener('mousedown', function (ev) {
captured = true;
if (start)
start(ev);
return true;
});
el.addEventListener('mousemove', function (ev) {
if (captured) {
move(ev);
ev.preventDefault();
return false;
}
return true;
});
el.addEventListener('mouseup', function (ev) {
captured = false;
if (stop)
stop(ev);
});
el.addEventListener('mouseleave', function (ev) {
captured = false;
if (stop)
stop(ev);
});
}
svg_1.buttonEvents = buttonEvents;
function mkLinearGradient(id) {
var gradient = svg.elt("linearGradient");
svg.hydrate(gradient, { id: id, x1: "0%", y1: "0%", x2: "0%", y2: "100%" });
var stop1 = svg.child(gradient, "stop", { offset: "0%" });
var stop2 = svg.child(gradient, "stop", { offset: "100%" });
var stop3 = svg.child(gradient, "stop", { offset: "100%" });
var stop4 = svg.child(gradient, "stop", { offset: "100%" });
return gradient;
}
svg_1.mkLinearGradient = mkLinearGradient;
function linearGradient(defs, id) {
var lg = mkLinearGradient(id);
defs.appendChild(lg);
return lg;
}
svg_1.linearGradient = linearGradient;
function setGradientColors(lg, start, end) {
if (!lg)
return;
lg.childNodes[0].style.stopColor = start;
lg.childNodes[1].style.stopColor = start;
lg.childNodes[2].style.stopColor = end;
lg.childNodes[3].style.stopColor = end;
}
svg_1.setGradientColors = setGradientColors;
function setGradientValue(lg, percent) {
if (lg.childNodes[1].getAttribute("offset") != percent) {
lg.childNodes[1].setAttribute("offset", percent);
lg.childNodes[2].setAttribute("offset", percent);
}
}
svg_1.setGradientValue = setGradientValue;
function animate(el, cls) {
svg.addClass(el, cls);
var p = el.parentElement;
if (p) {
p.removeChild(el);
p.appendChild(el);
}
}
svg_1.animate = animate;
function mkTitle(txt) {
var t = svg.elt("title");
t.textContent = txt;
return t;
}
svg_1.mkTitle = mkTitle;
function title(el, txt) {
var t = mkTitle(txt);
el.appendChild(t);
return t;
}
svg_1.title = title;
function toHtmlColor(c) {
var b = c & 0xFF;
var g = (c >> 8) & 0xFF;
var r = (c >> 16) & 0xFF;
var a = (c >> 24) & 0xFF / 255;
return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
}
svg_1.toHtmlColor = toHtmlColor;
})(svg = pxsim.svg || (pxsim.svg = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var Button = (function () {
function Button(id) {
this.id = id;
}
return Button;
}());
pxsim.Button = Button;
var ButtonPairState = (function () {
function ButtonPairState(props) {
this.props = props;
this.usesButtonAB = false;
this.aBtn = new Button(this.props.ID_BUTTON_A);
this.bBtn = new Button(this.props.ID_BUTTON_B);
this.abBtn = new Button(this.props.ID_BUTTON_AB);
this.abBtn.virtual = true;
}
return ButtonPairState;
}());
pxsim.ButtonPairState = ButtonPairState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var CompassState = (function () {
function CompassState() {
this.usesHeading = false;
this.heading = 90;
}
return CompassState;
}());
pxsim.CompassState = CompassState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
(function (PinFlags) {
PinFlags[PinFlags["Unused"] = 0] = "Unused";
PinFlags[PinFlags["Digital"] = 1] = "Digital";
PinFlags[PinFlags["Analog"] = 2] = "Analog";
PinFlags[PinFlags["Input"] = 4] = "Input";
PinFlags[PinFlags["Output"] = 8] = "Output";
PinFlags[PinFlags["Touch"] = 16] = "Touch";
})(pxsim.PinFlags || (pxsim.PinFlags = {}));
var PinFlags = pxsim.PinFlags;
var Pin = (function () {
function Pin(id) {
this.id = id;
this.touched = false;
this.value = 0;
this.period = 0;
this.servoAngle = 0;
this.mode = PinFlags.Unused;
this.pitch = false;
this.pull = 0; // PullDown
}
Pin.prototype.digitalReadPin = function () {
this.mode = PinFlags.Digital | PinFlags.Input;
return this.value > 100 ? 1 : 0;
};
Pin.prototype.digitalWritePin = function (value) {
this.mode = PinFlags.Digital | PinFlags.Output;
this.value = value > 0 ? 200 : 0;
pxsim.runtime.queueDisplayUpdate();
};
Pin.prototype.setPull = function (pull) {
this.pull = pull;
};
Pin.prototype.analogReadPin = function () {
this.mode = PinFlags.Analog | PinFlags.Input;
return this.value || 0;
};
Pin.prototype.analogWritePin = function (value) {
this.mode = PinFlags.Analog | PinFlags.Output;
this.value = Math.max(0, Math.min(1023, value));
pxsim.runtime.queueDisplayUpdate();
};
Pin.prototype.analogSetPeriod = function (micros) {
this.mode = PinFlags.Analog | PinFlags.Output;
this.period = micros;
pxsim.runtime.queueDisplayUpdate();
};
Pin.prototype.servoWritePin = function (value) {
this.analogSetPeriod(20000);
this.servoAngle = Math.max(0, Math.min(180, value));
pxsim.runtime.queueDisplayUpdate();
};
Pin.prototype.servoSetPulse = function (pinId, micros) {
// TODO
};
Pin.prototype.isTouched = function () {
this.mode = PinFlags.Touch | PinFlags.Analog | PinFlags.Input;
return this.touched;
};
return Pin;
}());
pxsim.Pin = Pin;
var EdgeConnectorState = (function () {
function EdgeConnectorState(props) {
this.props = props;
this.pins = props.pins.map(function (id) { return id != undefined ? new Pin(id) : null; });
}
EdgeConnectorState.prototype.getPin = function (id) {
return this.pins.filter(function (p) { return p && p.id == id; })[0] || null;
};
return EdgeConnectorState;
}());
pxsim.EdgeConnectorState = EdgeConnectorState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var FileSystemState = (function () {
function FileSystemState() {
this.files = {};
}
FileSystemState.prototype.append = function (file, content) {
this.files[file] = (this.files[file] || "") + content;
};
FileSystemState.prototype.remove = function (file) {
delete this.files[file];
};
return FileSystemState;
}());
pxsim.FileSystemState = FileSystemState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var LightSensorState = (function () {
function LightSensorState() {
this.usesLightLevel = false;
this.lightLevel = 128;
}
return LightSensorState;
}());
pxsim.LightSensorState = LightSensorState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
(function (NeoPixelMode) {
NeoPixelMode[NeoPixelMode["RGB"] = 0] = "RGB";
NeoPixelMode[NeoPixelMode["RGBW"] = 1] = "RGBW";
})(pxsim.NeoPixelMode || (pxsim.NeoPixelMode = {}));
var NeoPixelMode = pxsim.NeoPixelMode;
;
var NeoPixelState = (function () {
function NeoPixelState() {
this.buffers = {};
this.colors = {};
this.dirty = {};
}
NeoPixelState.prototype.updateBuffer = function (buffer, pin) {
this.buffers[pin] = buffer;
this.dirty[pin] = true;
};
NeoPixelState.prototype.getColors = function (pin, mode) {
var outColors = this.colors[pin] || (this.colors[pin] = []);
if (this.dirty[pin]) {
var buf = this.buffers[pin] || (this.buffers[pin] = []);
this.readNeoPixelBuffer(buf, outColors, mode);
this.dirty[pin] = false;
}
return outColors;
};
NeoPixelState.prototype.readNeoPixelBuffer = function (inBuffer, outColors, mode) {
var buf = inBuffer;
var stride = mode === NeoPixelMode.RGBW ? 4 : 3;
var pixelCount = Math.floor(buf.length / stride);
for (var i = 0; i < pixelCount; i++) {
// NOTE: for whatever reason, NeoPixels pack GRB not RGB
var r = buf[i * stride + 1];
var g = buf[i * stride + 0];
var b = buf[i * stride + 2];
var w = 0;
if (stride === 4)
w = buf[i * stride + 3];
outColors[i] = [r, g, b, w];
}
};
return NeoPixelState;
}());
pxsim.NeoPixelState = NeoPixelState;
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
visuals.mkBoardView = function (opts) {
var boardVis = opts.visual;
return new visuals.GenericBoardSvg({
visualDef: boardVis,
wireframe: opts.wireframe,
});
};
var BoardHost = (function () {
function BoardHost(view, opts) {
var _this = this;
this.parts = [];
this.boardView = view;
this.state = opts.state;
var activeComponents = opts.partsList;
this.useCrocClips = opts.boardDef.useCrocClips;
var useBreadboard = 0 < activeComponents.length || opts.forceBreadboardLayout;
if (useBreadboard) {
this.breadboard = new visuals.Breadboard({
wireframe: opts.wireframe,
});
var bMarg = opts.boardDef.marginWhenBreadboarding || [0, 0, 40, 0];
var composition = visuals.composeSVG({
el1: this.boardView.getView(),
scaleUnit1: this.boardView.getPinDist(),
el2: this.breadboard.getSVGAndSize(),
scaleUnit2: this.breadboard.getPinDist(),
margin: [bMarg[0], bMarg[1], 20, bMarg[3]],
middleMargin: bMarg[2],
maxWidth: opts.maxWidth,
maxHeight: opts.maxHeight,
});
var under = composition.under;
var over = composition.over;
this.view = composition.host;
var edges = composition.edges;
this.fromMBCoord = composition.toHostCoord1;
this.fromBBCoord = composition.toHostCoord2;
var pinDist = composition.scaleUnit;
this.partGroup = over;
this.partOverGroup = pxsim.svg.child(this.view, "g");
this.style = pxsim.svg.child(this.view, "style", {});
this.defs = pxsim.svg.child(this.view, "defs", {});
this.wireFactory = new visuals.WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
var allocRes = pxsim.allocateDefinitions({
boardDef: opts.boardDef,
partDefs: opts.partDefs,
fnArgs: opts.fnArgs,
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
partsList: activeComponents,
});
this.addAll(allocRes);
if (!allocRes.requiresBreadboard && !opts.forceBreadboardRender)
this.breadboard.hide();
}
else {
var el = this.boardView.getView().el;
this.view = el;
this.partGroup = pxsim.svg.child(this.view, "g");
this.partOverGroup = pxsim.svg.child(this.view, "g");
if (opts.maxWidth)
pxsim.svg.hydrate(this.view, { width: opts.maxWidth });
if (opts.maxHeight)
pxsim.svg.hydrate(this.view, { height: opts.maxHeight });
}
this.state.updateSubscribers.push(function () { return _this.updateState(); });
}
BoardHost.prototype.highlightBoardPin = function (pinNm) {
this.boardView.highlightPin(pinNm);
};
BoardHost.prototype.highlightBreadboardPin = function (rowCol) {
this.breadboard.highlightLoc(rowCol);
};
BoardHost.prototype.highlightWire = function (wire) {
//TODO: move to wiring.ts
//underboard wires
wire.wires.forEach(function (e) {
pxsim.svg.addClass(e, "highlight");
e.style["visibility"] = "visible";
});
//un greyed out
pxsim.svg.addClass(wire.endG, "highlight");
};
BoardHost.prototype.getView = function () {
return this.view;
};
BoardHost.prototype.updateState = function () {
this.parts.forEach(function (c) { return c.updateState(); });
};
BoardHost.prototype.getBBCoord = function (rowCol) {
var bbCoord = this.breadboard.getCoord(rowCol);
return this.fromBBCoord(bbCoord);
};
BoardHost.prototype.getPinCoord = function (pin) {
var boardCoord = this.boardView.getCoord(pin);
pxsim.U.assert(!!boardCoord, "Unable to find coord for pin: " + pin);
return this.fromMBCoord(boardCoord);
};
BoardHost.prototype.getLocCoord = function (loc) {
var coord;
if (loc.type === "breadboard") {
var rowCol = loc;
coord = this.getBBCoord(rowCol);
}
else {
var pinNm = loc.pin;
coord = this.getPinCoord(pinNm);
}
if (!coord) {
console.error("Unknown location: " + name);
return [0, 0];
}
return coord;
};
BoardHost.prototype.addPart = function (partInst) {
var _this = this;
var part = null;
var colOffset = 0;
if (partInst.simulationBehavior) {
//TODO: seperate simulation behavior from builtin visual
var builtinBehavior = partInst.simulationBehavior;
var cnstr = this.state.builtinVisuals[builtinBehavior];
var stateFn = this.state.builtinParts[builtinBehavior];
part = cnstr();
part.init(this.state.bus, stateFn, this.view, partInst.params);
}
else {
var vis = partInst.visual;
part = new visuals.GenericPart(vis);
}
this.parts.push(part);
this.partGroup.appendChild(part.element);
if (part.overElement)
this.partOverGroup.appendChild(part.overElement);
if (part.defs)
part.defs.forEach(function (d) { return _this.defs.appendChild(d); });
this.style.textContent += part.style || "";
var colIdx = partInst.startColumnIdx;
var rowIdx = partInst.startRowIdx;
var row = visuals.getRowName(rowIdx);
var col = visuals.getColumnName(colIdx);
var xOffset = partInst.bbFit.xOffset / partInst.visual.pinDistance;
var yOffset = partInst.bbFit.yOffset / partInst.visual.pinDistance;
var rowCol = {
type: "breadboard",
row: row,
col: col,
xOffset: xOffset,
yOffset: yOffset
};
var coord = this.getBBCoord(rowCol);
part.moveToCoord(coord);
var getCmpClass = function (type) { return ("sim-" + type + "-cmp"); };
var cls = getCmpClass(partInst.name);
pxsim.svg.addClass(part.element, cls);
pxsim.svg.addClass(part.element, "sim-cmp");
part.updateTheme();
part.updateState();
return part;
};
BoardHost.prototype.addWire = function (inst) {
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
};
BoardHost.prototype.addAll = function (allocRes) {
var _this = this;
allocRes.partsAndWires.forEach(function (pAndWs) {
var part = pAndWs.part;
if (part)
_this.addPart(part);
var wires = pAndWs.wires;
if (wires)
wires.forEach(function (w) { return _this.addWire(w); });
});
};
return BoardHost;
}());
visuals.BoardHost = BoardHost;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
// The distance between the center of two pins. This is the constant on which everything else is based.
var PIN_DIST = 15;
// CSS styling for the breadboard
var BLUE = "#1AA5D7";
var RED = "#DD4BA0";
var BREADBOARD_CSS = "\n /* bread board */\n .sim-bb-background {\n fill:#E0E0E0;\n }\n .sim-bb-pin {\n fill:#999;\n }\n .sim-bb-pin-hover {\n visibility: hidden;\n pointer-events: all;\n stroke-width: " + PIN_DIST / 2 + "px;\n stroke: transparent;\n fill: #777;\n }\n .sim-bb-pin-hover:hover {\n visibility: visible;\n fill:#444;\n }\n .sim-bb-group-wire {\n stroke: #999;\n stroke-width: " + PIN_DIST / 4 + "px;\n visibility: hidden;\n }\n .sim-bb-pin-group {\n pointer-events: all;\n }\n .sim-bb-label,\n .sim-bb-label-hover {\n font-family:\"Lucida Console\", Monaco, monospace;\n fill:#555;\n pointer-events: all;\n stroke-width: 0;\n cursor: default;\n }\n .sim-bb-label-hover {\n visibility: hidden;\n fill:#000;\n font-weight: bold;\n }\n .sim-bb-bar {\n stroke-width: 0;\n }\n .sim-bb-blue {\n fill:" + BLUE + ";\n stroke:" + BLUE + "\n }\n .sim-bb-red {\n fill:" + RED + ";\n stroke:" + RED + ";\n }\n .sim-bb-pin-group:hover .sim-bb-pin-hover,\n .sim-bb-pin-group:hover .sim-bb-group-wire,\n .sim-bb-pin-group:hover .sim-bb-label-hover {\n visibility: visible;\n }\n .sim-bb-pin-group:hover .sim-bb-label {\n visibility: hidden;\n }\n /* outline mode */\n .sim-bb-outline .sim-bb-background {\n stroke-width: " + PIN_DIST / 7 + "px;\n fill: #FFF;\n stroke: #000;\n }\n .sim-bb-outline .sim-bb-mid-channel {\n fill: #FFF;\n stroke: #888;\n stroke-width: 2px;\n }\n /* grayed out */\n .grayed .sim-bb-background {\n stroke-width: " + PIN_DIST / 5 + "px;\n }\n .grayed .sim-bb-red,\n .grayed .sim-bb-blue {\n fill: #BBB;\n }\n .grayed .sim-bb-bar {\n fill: #FFF;\n }\n .grayed .sim-bb-pin {\n fill: #000;\n stroke: #FFF;\n stroke-width: 3px;\n }\n .grayed .sim-bb-label {\n fill: none;\n }\n .grayed .sim-bb-background {\n stroke-width: " + PIN_DIST / 2 + "px;\n stroke: #555;\n }\n .grayed .sim-bb-group-wire {\n stroke: #DDD;\n }\n .grayed .sim-bb-channel {\n visibility: hidden;\n }\n /* highlighted */\n .sim-bb-label.highlight {\n visibility: hidden;\n }\n .sim-bb-label-hover.highlight {\n visibility: visible;\n }\n .sim-bb-blue.highlight {\n fill:" + BLUE + ";\n }\n .sim-bb-red.highlight {\n fill:" + RED + ";\n }\n .sim-bb-bar.highlight {\n stroke-width: 0px;\n }\n ";
// Pin rows and coluns
visuals.BREADBOARD_MID_ROWS = 10;
visuals.BREADBOARD_MID_COLS = 30;
var MID_ROW_GAPS = [4, 4];
var MID_ROW_AND_GAPS = visuals.BREADBOARD_MID_ROWS + MID_ROW_GAPS.length;
var BAR_ROWS = 2;
var BAR_COLS = 25;
var POWER_ROWS = BAR_ROWS * 2;
var POWER_COLS = BAR_COLS * 2;
var BAR_COL_GAPS = [4, 9, 14, 19];
var BAR_COL_AND_GAPS = BAR_COLS + BAR_COL_GAPS.length;
// Essential dimensions
var WIDTH = PIN_DIST * (visuals.BREADBOARD_MID_COLS + 3);
var HEIGHT = PIN_DIST * (MID_ROW_AND_GAPS + POWER_ROWS + 5.5);
var MID_RATIO = 2.0 / 3.0;
var BAR_RATIO = (1.0 - MID_RATIO) * 0.5;
var MID_HEIGHT = HEIGHT * MID_RATIO;
var BAR_HEIGHT = HEIGHT * BAR_RATIO;
// Pin grids
var MID_GRID_WIDTH = (visuals.BREADBOARD_MID_COLS - 1) * PIN_DIST;
var MID_GRID_HEIGHT = (MID_ROW_AND_GAPS - 1) * PIN_DIST;
var MID_GRID_X = (WIDTH - MID_GRID_WIDTH) / 2.0;
var MID_GRID_Y = BAR_HEIGHT + (MID_HEIGHT - MID_GRID_HEIGHT) / 2.0;
var BAR_GRID_HEIGHT = (BAR_ROWS - 1) * PIN_DIST;
var BAR_GRID_WIDTH = (BAR_COL_AND_GAPS - 1) * PIN_DIST;
var BAR_TOP_GRID_X = (WIDTH - BAR_GRID_WIDTH) / 2.0;
var BAR_TOP_GRID_Y = (BAR_HEIGHT - BAR_GRID_HEIGHT) / 2.0;
var BAR_BOT_GRID_X = BAR_TOP_GRID_X;
var BAR_BOT_GRID_Y = BAR_TOP_GRID_Y + BAR_HEIGHT + MID_HEIGHT;
// Individual pins
var PIN_HOVER_SCALAR = 1.3;
var PIN_WIDTH = PIN_DIST / 2.5;
var PIN_ROUNDING = PIN_DIST / 7.5;
// Labels
var PIN_LBL_SIZE = PIN_DIST * 0.7;
var PIN_LBL_HOVER_SCALAR = 1.3;
var PLUS_LBL_SIZE = PIN_DIST * 1.7;
var MINUS_LBL_SIZE = PIN_DIST * 2;
var POWER_LBL_OFFSET = PIN_DIST * 0.8;
var MINUS_LBL_EXTRA_OFFSET = PIN_DIST * 0.07;
var LBL_ROTATION = -90;
// Channels
var CHANNEL_HEIGHT = PIN_DIST * 1.0;
var SMALL_CHANNEL_HEIGHT = PIN_DIST * 0.05;
// Background
var BACKGROUND_ROUNDING = PIN_DIST * 0.3;
// Row and column helpers
var alphabet = "abcdefghij".split("").reverse();
function getColumnName(colIdx) { return "" + (colIdx + 1); }
visuals.getColumnName = getColumnName;
;
function getRowName(rowIdx) { return alphabet[rowIdx]; }
visuals.getRowName = getRowName;
;
;
;
function mkGrid(opts) {
var xOff = opts.xOffset || 0;
var yOff = opts.yOffset || 0;
var allPins = [];
var grid = pxsim.svg.elt("g");
var colIdxOffset = opts.colStartIdx || 0;
var rowIdxOffset = opts.rowStartIdx || 0;
var copyArr = function (arr) { return arr ? arr.slice(0, arr.length) : []; };
var removeAll = function (arr, e) {
var res = 0;
var idx;
while (0 <= (idx = arr.indexOf(e))) {
arr.splice(idx, 1);
res += 1;
}
return res;
};
var rowGaps = 0;
var rowIdxsWithGap = copyArr(opts.rowIdxsWithGap);
var _loop_3 = function(i) {
var colGaps = 0;
var colIdxsWithGap = copyArr(opts.colIdxsWithGap);
var cy = yOff + i * opts.pinDist + rowGaps * opts.pinDist;
var rowIdx = i + rowIdxOffset;
var _loop_4 = function(j) {
var cx = xOff + j * opts.pinDist + colGaps * opts.pinDist;
var colIdx = j + colIdxOffset;
var addEl = function (pin) {
var pinX = cx - pin.w * 0.5;
var pinY = cy - pin.h * 0.5;
pxsim.svg.hydrate(pin.el, { x: pinX, y: pinY });
grid.appendChild(pin.el);
return pin.el;
};
var el = addEl(opts.mkPin());
var hoverEl = addEl(opts.mkHoverPin());
var row = opts.getRowName(rowIdx);
var col = opts.getColName(colIdx);
var group = opts.getGroupName ? opts.getGroupName(rowIdx, colIdx) : null;
var gridPin = { el: el, hoverEl: hoverEl, cx: cx, cy: cy, row: row, col: col, group: group };
allPins.push(gridPin);
//column gaps
colGaps += removeAll(colIdxsWithGap, colIdx);
};
for (var j = 0; j < opts.colCount; j++) {
_loop_4(j);
}
//row gaps
rowGaps += removeAll(rowIdxsWithGap, rowIdx);
};
for (var i = 0; i < opts.rowCount; i++) {
_loop_3(i);
}
return { g: grid, allPins: allPins };
}
visuals.mkGrid = mkGrid;
function mkBBPin() {
var el = pxsim.svg.elt("rect");
var width = PIN_WIDTH;
pxsim.svg.hydrate(el, {
class: "sim-bb-pin",
rx: PIN_ROUNDING,
ry: PIN_ROUNDING,
width: width,
height: width
});
return { el: el, w: width, h: width, x: 0, y: 0 };
}
function mkBBHoverPin() {
var el = pxsim.svg.elt("rect");
var width = PIN_WIDTH * PIN_HOVER_SCALAR;
pxsim.svg.hydrate(el, {
class: "sim-bb-pin-hover",
rx: PIN_ROUNDING,
ry: PIN_ROUNDING,
width: width,
height: width,
});
return { el: el, w: width, h: width, x: 0, y: 0 };
}
;
function mkBBLabel(cx, cy, size, rotation, txt, group, extraClasses) {
//lbl
var el = visuals.mkTxt(cx, cy, size, rotation, txt);
pxsim.svg.addClass(el, "sim-bb-label");
if (extraClasses)
extraClasses.forEach(function (c) { return pxsim.svg.addClass(el, c); });
//hover lbl
var hoverEl = visuals.mkTxt(cx, cy, size * PIN_LBL_HOVER_SCALAR, rotation, txt);
pxsim.svg.addClass(hoverEl, "sim-bb-label-hover");
if (extraClasses)
extraClasses.forEach(function (c) { return pxsim.svg.addClass(hoverEl, c); });
var lbl = { el: el, hoverEl: hoverEl, txt: txt, group: group };
return lbl;
}
;
var Breadboard = (function () {
function Breadboard(opts) {
//truth
this.allPins = [];
this.allLabels = [];
this.allPowerBars = [];
//quick lookup caches
this.rowColToPin = {};
this.rowColToLbls = {};
this.buildDom();
if (opts.wireframe)
pxsim.svg.addClass(this.bb, "sim-bb-outline");
}
Breadboard.prototype.hide = function () {
this.bb.style.display = 'none';
};
Breadboard.prototype.updateLocation = function (x, y) {
pxsim.svg.hydrate(this.bb, {
x: x + "px",
y: y + "px",
});
};
Breadboard.prototype.getPin = function (row, col) {
var colToPin = this.rowColToPin[row];
if (!colToPin)
return null;
var pin = colToPin[col];
if (!pin)
return null;
return pin;
};
Breadboard.prototype.getCoord = function (rowCol) {
var row = rowCol.row, col = rowCol.col, xOffset = rowCol.xOffset, yOffset = rowCol.yOffset;
var pin = this.getPin(row, col);
if (!pin)
return null;
var xOff = (xOffset || 0) * PIN_DIST;
var yOff = (yOffset || 0) * PIN_DIST;
return [pin.cx + xOff, pin.cy + yOff];
};
Breadboard.prototype.getPinDist = function () {
return PIN_DIST;
};
Breadboard.prototype.buildDom = function () {
var _this = this;
this.bb = pxsim.svg.elt("svg", {
"version": "1.0",
"viewBox": "0 0 " + WIDTH + " " + HEIGHT,
"class": "sim-bb",
"width": WIDTH + "px",
"height": HEIGHT + "px",
});
this.styleEl = pxsim.svg.child(this.bb, "style", {});
this.styleEl.textContent += BREADBOARD_CSS;
this.defs = pxsim.svg.child(this.bb, "defs", {});
//background
pxsim.svg.child(this.bb, "rect", { class: "sim-bb-background", width: WIDTH, height: HEIGHT, rx: BACKGROUND_ROUNDING, ry: BACKGROUND_ROUNDING });
//mid channel
var channelGid = "sim-bb-channel-grad";
var channelGrad = pxsim.svg.elt("linearGradient");
pxsim.svg.hydrate(channelGrad, { id: channelGid, x1: "0%", y1: "0%", x2: "0%", y2: "100%" });
this.defs.appendChild(channelGrad);
var channelDark = "#AAA";
var channelLight = "#CCC";
var stop1 = pxsim.svg.child(channelGrad, "stop", { offset: "0%", style: "stop-color: " + channelDark + ";" });
var stop2 = pxsim.svg.child(channelGrad, "stop", { offset: "20%", style: "stop-color: " + channelLight + ";" });
var stop3 = pxsim.svg.child(channelGrad, "stop", { offset: "80%", style: "stop-color: " + channelLight + ";" });
var stop4 = pxsim.svg.child(channelGrad, "stop", { offset: "100%", style: "stop-color: " + channelDark + ";" });
var mkChannel = function (cy, h, cls) {
var channel = pxsim.svg.child(_this.bb, "rect", { class: "sim-bb-channel " + (cls || ""), y: cy - h / 2, width: WIDTH, height: h });
channel.setAttribute("fill", "url(#" + channelGid + ")");
return channel;
};
mkChannel(BAR_HEIGHT + MID_HEIGHT / 2, CHANNEL_HEIGHT, "sim-bb-mid-channel");
mkChannel(BAR_HEIGHT, SMALL_CHANNEL_HEIGHT, "sim-bb-sml-channel");
mkChannel(BAR_HEIGHT + MID_HEIGHT, SMALL_CHANNEL_HEIGHT), "sim-bb-sml-channel";
//-----pins
var getMidTopOrBot = function (rowIdx) { return rowIdx < visuals.BREADBOARD_MID_ROWS / 2.0 ? "b" : "t"; };
var getBarTopOrBot = function (colIdx) { return colIdx < POWER_COLS / 2.0 ? "b" : "t"; };
var getMidGroupName = function (rowIdx, colIdx) {
var botOrTop = getMidTopOrBot(rowIdx);
var colNm = getColumnName(colIdx);
return "" + botOrTop + colNm;
};
var getBarRowName = function (rowIdx) { return rowIdx === 0 ? "-" : "+"; };
var getBarGroupName = function (rowIdx, colIdx) {
var botOrTop = getBarTopOrBot(colIdx);
var rowName = getBarRowName(rowIdx);
return "" + rowName + botOrTop;
};
//mid grid
var midGridRes = mkGrid({
xOffset: MID_GRID_X,
yOffset: MID_GRID_Y,
rowCount: visuals.BREADBOARD_MID_ROWS,
colCount: visuals.BREADBOARD_MID_COLS,
pinDist: PIN_DIST,
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getRowName,
getColName: getColumnName,
getGroupName: getMidGroupName,
rowIdxsWithGap: MID_ROW_GAPS,
});
var midGridG = midGridRes.g;
this.allPins = this.allPins.concat(midGridRes.allPins);
//bot bar
var botBarGridRes = mkGrid({
xOffset: BAR_BOT_GRID_X,
yOffset: BAR_BOT_GRID_Y,
rowCount: BAR_ROWS,
colCount: BAR_COLS,
pinDist: PIN_DIST,
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColumnName,
getGroupName: getBarGroupName,
colIdxsWithGap: BAR_COL_GAPS,
});
var botBarGridG = botBarGridRes.g;
this.allPins = this.allPins.concat(botBarGridRes.allPins);
//top bar
var topBarGridRes = mkGrid({
xOffset: BAR_TOP_GRID_X,
yOffset: BAR_TOP_GRID_Y,
rowCount: BAR_ROWS,
colCount: BAR_COLS,
colStartIdx: BAR_COLS,
pinDist: PIN_DIST,
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColumnName,
getGroupName: getBarGroupName,
colIdxsWithGap: BAR_COL_GAPS.map(function (g) { return g + BAR_COLS; }),
});
var topBarGridG = topBarGridRes.g;
this.allPins = this.allPins.concat(topBarGridRes.allPins);
//tooltip
this.allPins.forEach(function (pin) {
var el = pin.el, row = pin.row, col = pin.col, hoverEl = pin.hoverEl;
var title = "(" + row + "," + col + ")";
pxsim.svg.hydrate(el, { title: title });
pxsim.svg.hydrate(hoverEl, { title: title });
});
//catalog pins
this.allPins.forEach(function (pin) {
var colToPin = _this.rowColToPin[pin.row];
if (!colToPin)
colToPin = _this.rowColToPin[pin.row] = {};
colToPin[pin.col] = pin;
});
//-----labels
var mkBBLabelAtPin = function (row, col, xOffset, yOffset, txt, group) {
var size = PIN_LBL_SIZE;
var rotation = LBL_ROTATION;
var loc = _this.getCoord({ type: "breadboard", row: row, col: col });
var cx = loc[0], cy = loc[1];
var t = mkBBLabel(cx + xOffset, cy + yOffset, size, rotation, txt, group);
return t;
};
//columns
for (var colIdx = 0; colIdx < visuals.BREADBOARD_MID_COLS; colIdx++) {
var colNm = getColumnName(colIdx);
//top
var rowTIdx = 0;
var rowTNm = getRowName(rowTIdx);
var groupT = getMidGroupName(rowTIdx, colIdx);
var lblT = mkBBLabelAtPin(rowTNm, colNm, 0, -PIN_DIST, colNm, groupT);
this.allLabels.push(lblT);
//bottom
var rowBIdx = visuals.BREADBOARD_MID_ROWS - 1;
var rowBNm = getRowName(rowBIdx);
var groupB = getMidGroupName(rowBIdx, colIdx);
var lblB = mkBBLabelAtPin(rowBNm, colNm, 0, +PIN_DIST, colNm, groupB);
this.allLabels.push(lblB);
}
//rows
for (var rowIdx = 0; rowIdx < visuals.BREADBOARD_MID_ROWS; rowIdx++) {
var rowNm = getRowName(rowIdx);
//top
var colTIdx = 0;
var colTNm = getColumnName(colTIdx);
var lblT = mkBBLabelAtPin(rowNm, colTNm, -PIN_DIST, 0, rowNm);
this.allLabels.push(lblT);
//top
var colBIdx = visuals.BREADBOARD_MID_COLS - 1;
var colBNm = getColumnName(colBIdx);
var lblB = mkBBLabelAtPin(rowNm, colBNm, +PIN_DIST, 0, rowNm);
this.allLabels.push(lblB);
}
//+- labels
var botPowerLabels = [
//BL
mkBBLabel(0 + POWER_LBL_OFFSET + MINUS_LBL_EXTRA_OFFSET, BAR_HEIGHT + MID_HEIGHT + POWER_LBL_OFFSET, MINUS_LBL_SIZE, LBL_ROTATION, "-", getBarGroupName(0, 0), ["sim-bb-blue"]),
mkBBLabel(0 + POWER_LBL_OFFSET, BAR_HEIGHT + MID_HEIGHT + BAR_HEIGHT - POWER_LBL_OFFSET, PLUS_LBL_SIZE, LBL_ROTATION, "+", getBarGroupName(1, 0), ["sim-bb-red"]),
//BR
mkBBLabel(WIDTH - POWER_LBL_OFFSET + MINUS_LBL_EXTRA_OFFSET, BAR_HEIGHT + MID_HEIGHT + POWER_LBL_OFFSET, MINUS_LBL_SIZE, LBL_ROTATION, "-", getBarGroupName(0, BAR_COLS - 1), ["sim-bb-blue"]),
mkBBLabel(WIDTH - POWER_LBL_OFFSET, BAR_HEIGHT + MID_HEIGHT + BAR_HEIGHT - POWER_LBL_OFFSET, PLUS_LBL_SIZE, LBL_ROTATION, "+", getBarGroupName(1, BAR_COLS - 1), ["sim-bb-red"]),
];
this.allLabels = this.allLabels.concat(botPowerLabels);
var topPowerLabels = [
//TL
mkBBLabel(0 + POWER_LBL_OFFSET + MINUS_LBL_EXTRA_OFFSET, 0 + POWER_LBL_OFFSET, MINUS_LBL_SIZE, LBL_ROTATION, "-", getBarGroupName(0, BAR_COLS), ["sim-bb-blue"]),
mkBBLabel(0 + POWER_LBL_OFFSET, BAR_HEIGHT - POWER_LBL_OFFSET, PLUS_LBL_SIZE, LBL_ROTATION, "+", getBarGroupName(1, BAR_COLS), ["sim-bb-red"]),
//TR
mkBBLabel(WIDTH - POWER_LBL_OFFSET + MINUS_LBL_EXTRA_OFFSET, 0 + POWER_LBL_OFFSET, MINUS_LBL_SIZE, LBL_ROTATION, "-", getBarGroupName(0, POWER_COLS - 1), ["sim-bb-blue"]),
mkBBLabel(WIDTH - POWER_LBL_OFFSET, BAR_HEIGHT - POWER_LBL_OFFSET, PLUS_LBL_SIZE, LBL_ROTATION, "+", getBarGroupName(1, POWER_COLS - 1), ["sim-bb-red"]),
];
this.allLabels = this.allLabels.concat(topPowerLabels);
//catalog lbls
var lblNmToLbls = {};
this.allLabels.forEach(function (lbl) {
var el = lbl.el, txt = lbl.txt;
var lbls = lblNmToLbls[txt] = lblNmToLbls[txt] || [];
lbls.push(lbl);
});
var isPowerPin = function (pin) { return pin.row === "-" || pin.row === "+"; };
this.allPins.forEach(function (pin) {
var row = pin.row, col = pin.col, group = pin.group;
var colToLbls = _this.rowColToLbls[row] || (_this.rowColToLbls[row] = {});
var lbls = colToLbls[col] || (colToLbls[col] = []);
if (isPowerPin(pin)) {
//power pins
var isBot = Number(col) <= BAR_COLS;
if (isBot)
botPowerLabels.filter(function (l) { return l.group == pin.group; }).forEach(function (l) { return lbls.push(l); });
else
topPowerLabels.filter(function (l) { return l.group == pin.group; }).forEach(function (l) { return lbls.push(l); });
}
else {
//mid pins
var rowLbls = lblNmToLbls[row];
rowLbls.forEach(function (l) { return lbls.push(l); });
var colLbls = lblNmToLbls[col];
colLbls.forEach(function (l) { return lbls.push(l); });
}
});
//-----blue & red lines
var lnLen = BAR_GRID_WIDTH + PIN_DIST * 1.5;
var lnThickness = PIN_DIST / 5.0;
var lnYOff = PIN_DIST * 0.6;
var lnXOff = (lnLen - BAR_GRID_WIDTH) / 2.0;
var mkPowerLine = function (x, y, group, cls) {
var ln = pxsim.svg.elt("rect");
pxsim.svg.hydrate(ln, {
class: "sim-bb-bar " + cls,
x: x,
y: y - lnThickness / 2.0,
width: lnLen,
height: lnThickness });
var bar = { el: ln, group: group };
return bar;
};
var barLines = [
//top
mkPowerLine(BAR_BOT_GRID_X - lnXOff, BAR_BOT_GRID_Y - lnYOff, getBarGroupName(0, POWER_COLS - 1), "sim-bb-blue"),
mkPowerLine(BAR_BOT_GRID_X - lnXOff, BAR_BOT_GRID_Y + PIN_DIST + lnYOff, getBarGroupName(1, POWER_COLS - 1), "sim-bb-red"),
//bot
mkPowerLine(BAR_TOP_GRID_X - lnXOff, BAR_TOP_GRID_Y - lnYOff, getBarGroupName(0, 0), "sim-bb-blue"),
mkPowerLine(BAR_TOP_GRID_X - lnXOff, BAR_TOP_GRID_Y + PIN_DIST + lnYOff, getBarGroupName(1, 0), "sim-bb-red"),
];
this.allPowerBars = this.allPowerBars.concat(barLines);
//attach power bars
this.allPowerBars.forEach(function (b) { return _this.bb.appendChild(b.el); });
//-----electrically connected groups
//make groups
var allGrpNms = this.allPins.map(function (p) { return p.group; }).filter(function (g, i, a) { return a.indexOf(g) == i; });
var groups = allGrpNms.map(function (grpNm) {
var g = pxsim.svg.elt("g");
return g;
});
groups.forEach(function (g) { return pxsim.svg.addClass(g, "sim-bb-pin-group"); });
groups.forEach(function (g, i) { return pxsim.svg.addClass(g, "group-" + allGrpNms[i]); });
var grpNmToGroup = {};
allGrpNms.forEach(function (g, i) { return grpNmToGroup[g] = groups[i]; });
//group pins and add connecting wire
var grpNmToPins = {};
this.allPins.forEach(function (p, i) {
var g = p.group;
var pins = grpNmToPins[g] || (grpNmToPins[g] = []);
pins.push(p);
});
//connecting wire
allGrpNms.forEach(function (grpNm) {
var pins = grpNmToPins[grpNm];
var _a = [pins.map(function (p) { return p.cx; }), pins.map(function (p) { return p.cy; })], xs = _a[0], ys = _a[1];
var minFn = function (arr) { return arr.reduce(function (a, b) { return a < b ? a : b; }); };
var maxFn = function (arr) { return arr.reduce(function (a, b) { return a > b ? a : b; }); };
var _b = [minFn(xs), maxFn(xs), minFn(ys), maxFn(ys)], minX = _b[0], maxX = _b[1], minY = _b[2], maxY = _b[3];
var wire = pxsim.svg.elt("rect");
var width = Math.max(maxX - minX, 0.0001 /*rects with no width aren't displayed*/);
var height = Math.max(maxY - minY, 0.0001);
pxsim.svg.hydrate(wire, { x: minX, y: minY, width: width, height: height });
pxsim.svg.addClass(wire, "sim-bb-group-wire");
var g = grpNmToGroup[grpNm];
g.appendChild(wire);
});
//group pins
this.allPins.forEach(function (p) {
var g = grpNmToGroup[p.group];
g.appendChild(p.el);
g.appendChild(p.hoverEl);
});
//group lbls
var miscLblGroup = pxsim.svg.elt("g");
pxsim.svg.hydrate(miscLblGroup, { class: "sim-bb-group-misc" });
groups.push(miscLblGroup);
this.allLabels.forEach(function (l) {
if (l.group) {
var g = grpNmToGroup[l.group];
g.appendChild(l.el);
g.appendChild(l.hoverEl);
}
else {
miscLblGroup.appendChild(l.el);
miscLblGroup.appendChild(l.hoverEl);
}
});
//attach to bb
groups.forEach(function (g) { return _this.bb.appendChild(g); }); //attach to breadboard
};
Breadboard.prototype.getSVGAndSize = function () {
return { el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT };
};
Breadboard.prototype.highlightLoc = function (rowCol) {
var row = rowCol.row, col = rowCol.col;
var pin = this.rowColToPin[row][col];
var cx = pin.cx, cy = pin.cy;
var lbls = this.rowColToLbls[row][col];
var highlightLbl = function (lbl) {
pxsim.svg.addClass(lbl.el, "highlight");
pxsim.svg.addClass(lbl.hoverEl, "highlight");
};
lbls.forEach(highlightLbl);
};
return Breadboard;
}());
visuals.Breadboard = Breadboard;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
/// <reference path="../../typings/globals/bluebird/index.d.ts"/>
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
function mkBtnSvg(xy) {
var _a = ["sim-button", "sim-button-outer"], innerCls = _a[0], outerCls = _a[1];
var tabSize = visuals.PIN_DIST / 2.5;
var pegR = visuals.PIN_DIST / 5;
var btnR = visuals.PIN_DIST * .8;
var pegMargin = visuals.PIN_DIST / 8;
var plateR = visuals.PIN_DIST / 12;
var pegOffset = pegMargin + pegR;
var x = xy[0], y = xy[1];
var left = x - tabSize / 2;
var top = y - tabSize / 2;
var plateH = 3 * visuals.PIN_DIST - tabSize;
var plateW = 2 * visuals.PIN_DIST + tabSize;
var plateL = left;
var plateT = top + tabSize;
var btnCX = plateL + plateW / 2;
var btnCY = plateT + plateH / 2;
var btng = pxsim.svg.elt("g");
//tabs
var mkTab = function (x, y) {
pxsim.svg.child(btng, "rect", { class: "sim-button-tab", x: x, y: y, width: tabSize, height: tabSize });
};
mkTab(left, top);
mkTab(left + 2 * visuals.PIN_DIST, top);
mkTab(left, top + 3 * visuals.PIN_DIST);
mkTab(left + 2 * visuals.PIN_DIST, top + 3 * visuals.PIN_DIST);
//plate
pxsim.svg.child(btng, "rect", { class: outerCls, x: plateL, y: plateT, rx: plateR, ry: plateR, width: plateW, height: plateH });
//pegs
var mkPeg = function (x, y) {
pxsim.svg.child(btng, "circle", { class: "sim-button-nut", cx: x, cy: y, r: pegR });
};
mkPeg(plateL + pegOffset, plateT + pegOffset);
mkPeg(plateL + plateW - pegOffset, plateT + pegOffset);
mkPeg(plateL + pegOffset, plateT + plateH - pegOffset);
mkPeg(plateL + plateW - pegOffset, plateT + plateH - pegOffset);
//inner btn
var innerBtn = pxsim.svg.child(btng, "circle", { class: innerCls, cx: btnCX, cy: btnCY, r: btnR });
//return
return { el: btng, y: top, x: left, w: plateW, h: plateH + 2 * tabSize };
}
visuals.mkBtnSvg = mkBtnSvg;
visuals.BUTTON_PAIR_STYLE = "\n .sim-button {\n pointer-events: none;\n fill: #000;\n }\n .sim-button-outer:active ~ .sim-button,\n .sim-button-virtual:active {\n fill: #FFA500;\n }\n .sim-button-outer {\n cursor: pointer;\n fill: #979797;\n }\n .sim-button-outer:hover {\n stroke:gray;\n stroke-width: " + visuals.PIN_DIST / 5 + "px;\n }\n .sim-button-nut {\n fill:#000;\n pointer-events:none;\n }\n .sim-button-nut:hover {\n stroke:" + visuals.PIN_DIST / 15 + "px solid #704A4A;\n }\n .sim-button-tab {\n fill:#FFF;\n pointer-events:none;\n }\n .sim-button-virtual {\n cursor: pointer;\n fill: rgba(255, 255, 255, 0.6);\n stroke: rgba(255, 255, 255, 1);\n stroke-width: " + visuals.PIN_DIST / 5 + "px;\n }\n .sim-button-virtual:hover {\n stroke: rgba(128, 128, 128, 1);\n }\n .sim-text-virtual {\n fill: #000;\n pointer-events:none;\n }\n ";
var ButtonPairView = (function () {
function ButtonPairView() {
this.style = visuals.BUTTON_PAIR_STYLE;
}
ButtonPairView.prototype.init = function (bus, state) {
this.state = state;
this.bus = bus;
this.defs = [];
this.element = this.mkBtns();
this.updateState();
this.attachEvents();
};
ButtonPairView.prototype.moveToCoord = function (xy) {
var btnWidth = visuals.PIN_DIST * 3;
var x = xy[0], y = xy[1];
visuals.translateEl(this.aBtn, [x, y]);
visuals.translateEl(this.bBtn, [x + btnWidth, y]);
visuals.translateEl(this.abBtn, [x + visuals.PIN_DIST * 1.5, y + visuals.PIN_DIST * 4]);
};
ButtonPairView.prototype.updateState = function () {
var stateBtns = [this.state.aBtn, this.state.bBtn, this.state.abBtn];
var svgBtns = [this.aBtn, this.bBtn, this.abBtn];
if (this.state.usesButtonAB && this.abBtn.style.visibility != "visible") {
this.abBtn.style.visibility = "visible";
}
};
ButtonPairView.prototype.updateTheme = function () { };
ButtonPairView.prototype.mkBtns = function () {
this.aBtn = mkBtnSvg([0, 0]).el;
this.bBtn = mkBtnSvg([0, 0]).el;
var mkVirtualBtn = function () {
var numPins = 2;
var w = visuals.PIN_DIST * 2.8;
var offset = (w - (numPins * visuals.PIN_DIST)) / 2;
var corner = visuals.PIN_DIST / 2;
var cx = 0 - offset + w / 2;
var cy = cx;
var txtSize = visuals.PIN_DIST * 1.3;
var x = -offset;
var y = -offset;
var txtXOff = visuals.PIN_DIST / 7;
var txtYOff = visuals.PIN_DIST / 10;
var btng = pxsim.svg.elt("g");
var btn = pxsim.svg.child(btng, "rect", { class: "sim-button-virtual", x: x, y: y, rx: corner, ry: corner, width: w, height: w });
var btnTxt = visuals.mkTxt(cx + txtXOff, cy + txtYOff, txtSize, 0, "A+B");
pxsim.svg.addClass(btnTxt, "sim-text");
pxsim.svg.addClass(btnTxt, "sim-text-virtual");
btng.appendChild(btnTxt);
return btng;
};
this.abBtn = mkVirtualBtn();
this.abBtn.style.visibility = "hidden";
var el = pxsim.svg.elt("g");
pxsim.svg.addClass(el, "sim-buttonpair");
el.appendChild(this.aBtn);
el.appendChild(this.bBtn);
el.appendChild(this.abBtn);
return el;
};
ButtonPairView.prototype.attachEvents = function () {
var _this = this;
var btnStates = [this.state.aBtn, this.state.bBtn];
var btnSvgs = [this.aBtn, this.bBtn];
btnSvgs.forEach(function (btn, index) {
btn.addEventListener(pxsim.pointerEvents.down, function (ev) {
btnStates[index].pressed = true;
});
btn.addEventListener(pxsim.pointerEvents.leave, function (ev) {
btnStates[index].pressed = false;
});
btn.addEventListener(pxsim.pointerEvents.up, function (ev) {
btnStates[index].pressed = false;
_this.bus.queue(btnStates[index].id, _this.state.props.BUTTON_EVT_UP);
_this.bus.queue(btnStates[index].id, _this.state.props.BUTTON_EVT_CLICK);
});
});
var updateBtns = function (s) {
btnStates.forEach(function (b) { return b.pressed = s; });
};
this.abBtn.addEventListener(pxsim.pointerEvents.down, function (ev) {
updateBtns(true);
});
this.abBtn.addEventListener(pxsim.pointerEvents.leave, function (ev) {
updateBtns(false);
});
this.abBtn.addEventListener(pxsim.pointerEvents.up, function (ev) {
updateBtns(false);
_this.bus.queue(_this.state.abBtn.id, _this.state.props.BUTTON_EVT_UP);
_this.bus.queue(_this.state.abBtn.id, _this.state.props.BUTTON_EVT_CLICK);
});
};
return ButtonPairView;
}());
visuals.ButtonPairView = ButtonPairView;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
/// <reference path="../../typings/globals/bluebird/index.d.ts"/>
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
visuals.BOARD_SYTLE = "\n .noselect {\n -webkit-touch-callout: none; /* iOS Safari */\n -webkit-user-select: none; /* Chrome/Safari/Opera */\n -khtml-user-select: none; /* Konqueror */\n -moz-user-select: none; /* Firefox */\n -ms-user-select: none; /* Internet Explorer/Edge */\n user-select: none; /* Non-prefixed version, currently\n not supported by any browser */\n }\n\n .sim-board-pin {\n fill:#999;\n stroke:#000;\n stroke-width:" + visuals.PIN_DIST / 3.0 + "px;\n }\n .sim-board-pin-lbl {\n fill: #333;\n }\n .gray-cover {\n fill:#FFF;\n opacity: 0.3;\n stroke-width:0;\n visibility: hidden;\n }\n .sim-board-pin-hover {\n visibility: hidden;\n pointer-events: all;\n stroke-width:" + visuals.PIN_DIST / 6.0 + "px;\n }\n .sim-board-pin-hover:hover {\n visibility: visible;\n }\n .sim-board-pin-lbl {\n visibility: hidden;\n }\n .sim-board-outline .sim-board-pin-lbl {\n visibility: visible;\n }\n .sim-board-pin-lbl {\n fill: #555;\n }\n .sim-board-pin-lbl-hover {\n fill: red;\n }\n .sim-board-outline .sim-board-pin-lbl-hover {\n fill: black;\n }\n .sim-board-pin-lbl,\n .sim-board-pin-lbl-hover {\n font-family:\"Lucida Console\", Monaco, monospace;\n pointer-events: all;\n stroke-width: 0;\n }\n .sim-board-pin-lbl-hover {\n visibility: hidden;\n }\n .sim-board-outline .sim-board-pin-hover:hover + .sim-board-pin-lbl,\n .sim-board-pin-lbl.highlight {\n visibility: hidden;\n }\n .sim-board-outline .sim-board-pin-hover:hover + * + .sim-board-pin-lbl-hover,\n .sim-board-pin-lbl-hover.highlight {\n visibility: visible;\n }\n /* Graying out */\n .grayed .sim-board-pin-lbl:not(.highlight) {\n fill: #AAA;\n }\n .grayed .sim-board-pin:not(.highlight) {\n fill:#BBB;\n stroke:#777;\n }\n .grayed .gray-cover {\n visibility: inherit;\n }\n .grayed .sim-cmp:not(.notgrayed) {\n opacity: 0.3;\n }\n /* Highlighting */\n .sim-board-pin-lbl.highlight {\n fill: #000;\n font-weight: bold;\n }\n .sim-board-pin.highlight {\n fill:#999;\n stroke:#000;\n }\n ";
var PIN_LBL_SIZE = visuals.PIN_DIST * 0.7;
var PIN_LBL_HOVER_SIZE = PIN_LBL_SIZE * 1.5;
var SQUARE_PIN_WIDTH = visuals.PIN_DIST * 0.66666;
var SQUARE_PIN_HOVER_WIDTH = visuals.PIN_DIST * 0.66666 + visuals.PIN_DIST / 3.0;
var nextBoardId = 0;
var GenericBoardSvg = (function () {
function GenericBoardSvg(props) {
var _this = this;
this.props = props;
// pins & labels
//(truth)
this.allPins = [];
this.allLabels = [];
//(cache)
this.pinNmToLbl = {};
this.pinNmToPin = {};
//TODO: handle wireframe mode
this.id = nextBoardId++;
var visDef = props.visualDef;
var imgHref = props.wireframe ? visDef.outlineImage : visDef.image;
var boardImgAndSize = visuals.mkImageSVG({
image: imgHref,
width: visDef.width,
height: visDef.height,
imageUnitDist: visDef.pinDist,
targetUnitDist: visuals.PIN_DIST
});
var scaleFn = visuals.mkScaleFn(visDef.pinDist, visuals.PIN_DIST);
this.width = boardImgAndSize.w;
this.height = boardImgAndSize.h;
var img = boardImgAndSize.el;
this.element = pxsim.svg.elt("svg");
pxsim.svg.hydrate(this.element, {
"version": "1.0",
"viewBox": "0 0 " + this.width + " " + this.height,
"class": "sim sim-board-id-" + this.id,
"x": "0px",
"y": "0px"
});
if (props.wireframe)
pxsim.svg.addClass(this.element, "sim-board-outline");
this.style = pxsim.svg.child(this.element, "style", {});
this.style.textContent += visuals.BOARD_SYTLE;
this.defs = pxsim.svg.child(this.element, "defs", {});
this.g = pxsim.svg.elt("g");
this.element.appendChild(this.g);
// main board
this.g.appendChild(img);
this.background = img;
pxsim.svg.hydrate(img, { class: "sim-board" });
var backgroundCover = this.mkGrayCover(0, 0, this.width, this.height);
this.g.appendChild(backgroundCover);
// ----- pins
var mkSquarePin = function () {
var el = pxsim.svg.elt("rect");
var width = SQUARE_PIN_WIDTH;
pxsim.svg.hydrate(el, {
class: "sim-board-pin",
width: width,
height: width,
});
return { el: el, w: width, h: width, x: 0, y: 0 };
};
var mkSquareHoverPin = function () {
var el = pxsim.svg.elt("rect");
var width = SQUARE_PIN_HOVER_WIDTH;
pxsim.svg.hydrate(el, {
class: "sim-board-pin-hover",
width: width,
height: width
});
return { el: el, w: width, h: width, x: 0, y: 0 };
};
var mkPinBlockGrid = function (pinBlock, blockIdx) {
var xOffset = scaleFn(pinBlock.x) + visuals.PIN_DIST / 2.0;
var yOffset = scaleFn(pinBlock.y) + visuals.PIN_DIST / 2.0;
var rowCount = 1;
var colCount = pinBlock.labels.length;
var getColName = function (colIdx) { return pinBlock.labels[colIdx]; };
var getRowName = function () { return ("" + (blockIdx + 1)); };
var getGroupName = function () { return pinBlock.labels.join(" "); };
var gridRes = visuals.mkGrid({
xOffset: xOffset,
yOffset: yOffset,
rowCount: rowCount,
colCount: colCount,
pinDist: visuals.PIN_DIST,
mkPin: mkSquarePin,
mkHoverPin: mkSquareHoverPin,
getRowName: getRowName,
getColName: getColName,
getGroupName: getGroupName,
});
var pins = gridRes.allPins;
var pinsG = gridRes.g;
pxsim.svg.addClass(gridRes.g, "sim-board-pin-group");
return gridRes;
};
var pinBlocks = visDef.pinBlocks.map(mkPinBlockGrid);
var pinToBlockDef = [];
pinBlocks.forEach(function (blk, blkIdx) { return blk.allPins.forEach(function (p, pIdx) {
_this.allPins.push(p);
pinToBlockDef.push(visDef.pinBlocks[blkIdx]);
}); });
//tooltip
this.allPins.forEach(function (p) {
var tooltip = p.col;
pxsim.svg.hydrate(p.el, { title: tooltip });
pxsim.svg.hydrate(p.hoverEl, { title: tooltip });
});
//catalog pins
this.allPins.forEach(function (p) {
_this.pinNmToPin[p.col] = p;
});
// ----- labels
var mkLabelTxtEl = function (pinX, pinY, size, txt, pos) {
//TODO: extract constants
var lblY;
var lblX;
if (pos === "below") {
var lblLen = size * 0.25 * txt.length;
lblX = pinX;
lblY = pinY + 12 + lblLen;
}
else {
var lblLen = size * 0.32 * txt.length;
lblX = pinX;
lblY = pinY - 11 - lblLen;
}
var el = visuals.mkTxt(lblX, lblY, size, -90, txt);
return el;
};
var mkLabel = function (pinX, pinY, txt, pos) {
var el = mkLabelTxtEl(pinX, pinY, PIN_LBL_SIZE, txt, pos);
pxsim.svg.addClass(el, "sim-board-pin-lbl");
var hoverEl = mkLabelTxtEl(pinX, pinY, PIN_LBL_HOVER_SIZE, txt, pos);
pxsim.svg.addClass(hoverEl, "sim-board-pin-lbl-hover");
var label = { el: el, hoverEl: hoverEl, txt: txt };
return label;
};
this.allLabels = this.allPins.map(function (p, pIdx) {
var blk = pinToBlockDef[pIdx];
return mkLabel(p.cx, p.cy, p.col, blk.labelPosition);
});
//catalog labels
this.allPins.forEach(function (pin, pinIdx) {
var lbl = _this.allLabels[pinIdx];
_this.pinNmToLbl[pin.col] = lbl;
});
//attach pins & labels
this.allPins.forEach(function (p, idx) {
var lbl = _this.allLabels[idx];
//pins and labels must be adjacent for hover CSS
_this.g.appendChild(p.el);
_this.g.appendChild(p.hoverEl);
_this.g.appendChild(lbl.el);
_this.g.appendChild(lbl.hoverEl);
});
}
GenericBoardSvg.prototype.getCoord = function (pinNm) {
var pin = this.pinNmToPin[pinNm];
if (!pin)
return null;
return [pin.cx, pin.cy];
};
GenericBoardSvg.prototype.mkGrayCover = function (x, y, w, h) {
var rect = pxsim.svg.elt("rect");
pxsim.svg.hydrate(rect, { x: x, y: y, width: w, height: h, class: "gray-cover" });
return rect;
};
GenericBoardSvg.prototype.getView = function () {
return { el: this.element, w: this.width, h: this.height, x: 0, y: 0 };
};
GenericBoardSvg.prototype.getPinDist = function () {
return visuals.PIN_DIST;
};
GenericBoardSvg.prototype.highlightPin = function (pinNm) {
var lbl = this.pinNmToLbl[pinNm];
var pin = this.pinNmToPin[pinNm];
if (lbl && pin) {
pxsim.svg.addClass(lbl.el, "highlight");
pxsim.svg.addClass(lbl.hoverEl, "highlight");
pxsim.svg.addClass(pin.el, "highlight");
pxsim.svg.addClass(pin.hoverEl, "highlight");
}
};
return GenericBoardSvg;
}());
visuals.GenericBoardSvg = GenericBoardSvg;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
function mkGenericPartSVG(partVisual) {
var imgAndSize = visuals.mkImageSVG({
image: partVisual.image,
width: partVisual.width,
height: partVisual.height,
imageUnitDist: partVisual.pinDistance,
targetUnitDist: visuals.PIN_DIST
});
return imgAndSize;
}
visuals.mkGenericPartSVG = mkGenericPartSVG;
var GenericPart = (function () {
function GenericPart(partVisual) {
this.style = "";
this.defs = [];
var imgAndSize = mkGenericPartSVG(partVisual);
var img = imgAndSize.el;
this.element = pxsim.svg.elt("g");
this.element.appendChild(img);
}
GenericPart.prototype.moveToCoord = function (xy) {
visuals.translateEl(this.element, xy);
};
//unused
GenericPart.prototype.init = function (bus, state, svgEl) { };
GenericPart.prototype.updateState = function () { };
GenericPart.prototype.updateTheme = function () { };
return GenericPart;
}());
visuals.GenericPart = GenericPart;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
function mkMicroServoPart(xy) {
if (xy === void 0) { xy = [0, 0]; }
// TODO
return { el: null, y: 0, x: 0, w: 0, h: 0 };
}
visuals.mkMicroServoPart = mkMicroServoPart;
var MicroServoView = (function () {
function MicroServoView() {
this.style = "";
this.overElement = undefined;
this.defs = [];
this.currentAngle = 0;
this.targetAngle = 0;
this.lastAngleTime = 0;
}
MicroServoView.prototype.init = function (bus, state, svgEl, otherParams) {
this.state = state;
this.pin = this.state.props.servos[pxsim.readPin(otherParams["name"] || otherParams["pin"])];
this.bus = bus;
this.defs = [];
this.initDom();
this.updateState();
};
MicroServoView.prototype.initDom = function () {
this.element = pxsim.svg.parseString("\n<svg xmlns=\"http://www.w3.org/2000/svg\" id=\"svg2\" width=\"112.188\" height=\"299.674\">\n <g id=\"layer1\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"scale(0.8)\">\n <path id=\"path8212\" fill=\"#0061ff\" stroke-width=\"6.6\" d=\"M.378 44.61v255.064h112.188V44.61H.378z\"/>\n <path id=\"crankbase\" fill=\"#00f\" stroke-width=\"6.6\" d=\"M56.57 88.047C25.328 88.047 0 113.373 0 144.615c.02 22.352 11.807 42.596 32.238 51.66.03 3.318.095 5.24.088 7.938 0 13.947 11.307 25.254 25.254 25.254 13.947 0 25.254-11.307 25.254-25.254-.006-2.986-.415-5.442-.32-8.746 19.487-9.45 30.606-29.195 30.625-50.852 0-31.24-25.33-56.568-56.57-56.568z\"/>\n <path id=\"lowertip\" fill=\"#00a2ff\" stroke-width=\"2\" d=\"M.476 260.78v38.894h53.82v-10.486a6.82 6.566 0 0 1-4.545-6.182 6.82 6.566 0 0 1 6.82-6.566 6.82 6.566 0 0 1 6.82 6.566 6.82 6.566 0 0 1-4.545 6.182v10.486h53.82V260.78H.475z\"/>\n <path id=\"uppertip\" fill=\"#00a2ff\" stroke-width=\"2\" d=\"M112.566 83.503V44.61h-53.82v10.487a6.82 6.566 0 0 1 4.544 6.18 6.82 6.566 0 0 1-6.818 6.568 6.82 6.566 0 0 1-6.82-6.567 6.82 6.566 0 0 1 4.546-6.18V44.61H.378v38.893h112.188z\"/>\n <path id=\"VCC\" fill=\"red\" stroke-width=\"2\" d=\"M53.72 21.93h5.504v22.627H53.72z\"/>\n <path id=\"LOGIC\" fill=\"#fc0\" stroke-width=\"2\" d=\"M47.3 21.93h5.503v22.627H47.3z\"/>\n <path id=\"GND\" fill=\"#a02c2c\" stroke-width=\"2\" d=\"M60.14 21.93h5.505v22.627H60.14z\"/>\n <path id=\"connector\" stroke-width=\"2\" d=\"M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z\"/>\n <g id=\"crank\" transform=\"translate(0 -752.688)\">\n <path id=\"arm\" fill=\"#ececec\" stroke=\"#000\" stroke-width=\"1.372\" d=\"M47.767 880.88c-4.447 1.162-8.412 8.278-8.412 18.492s3.77 18.312 8.412 18.494c8.024.314 78.496 5.06 78.51-16.952.012-22.013-74.377-21.117-78.51-20.035z\"/>\n <circle id=\"path8216\" cx=\"56.661\" cy=\"899.475\" r=\"8.972\" fill=\"gray\" stroke-width=\"2\"/>\n </g>\n </g>\n</svg> \n ").firstElementChild;
this.crankEl = this.element.querySelector("#crank");
this.crankTransform = this.crankEl.getAttribute("transform");
};
MicroServoView.prototype.moveToCoord = function (xy) {
var x = xy[0], y = xy[1];
visuals.translateEl(this.element, [x, y]);
};
MicroServoView.prototype.updateState = function () {
this.targetAngle = this.state.getPin(this.pin).servoAngle;
if (this.targetAngle != this.currentAngle) {
var now = pxsim.U.now();
var cx = 56.661;
var cy = 899.475;
var speed = 300; // 0.1s/60 degree
var dt = Math.min(now - this.lastAngleTime, 50) / 1000;
var delta = this.targetAngle - this.currentAngle;
this.currentAngle += Math.min(Math.abs(delta), speed * dt) * (delta > 0 ? 1 : -1);
this.crankEl.setAttribute("transform", this.crankTransform
+ (" rotate(" + this.currentAngle + ", " + cx + ", " + cy + ")"));
this.lastAngleTime = now;
setTimeout(function () { return pxsim.runtime.updateDisplay(); }, 20);
}
};
MicroServoView.prototype.updateTheme = function () {
};
return MicroServoView;
}());
visuals.MicroServoView = MicroServoView;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));
var pxsim;
(function (pxsim) {
var visuals;
(function (visuals) {
var WIRE_WIDTH = visuals.PIN_DIST / 2.5;
var BB_WIRE_SMOOTH = 0.7;
var INSTR_WIRE_SMOOTH = 0.8;
var WIRE_PART_CURVE_OFF = 15;
var WIRE_PART_LENGTH = 100;
visuals.WIRES_CSS = "\n .sim-bb-wire {\n fill:none;\n stroke-linecap: round;\n stroke-width:" + WIRE_WIDTH + "px;\n pointer-events: none;\n }\n .sim-bb-wire-end {\n stroke:#333;\n fill:#333;\n }\n .sim-bb-wire-bare-end {\n fill: #ccc;\n }\n .sim-bb-wire-hover {\n stroke-width: " + WIRE_WIDTH + "px;\n visibility: hidden;\n stroke-dasharray: " + visuals.PIN_DIST / 10.0 + "," + visuals.PIN_DIST / 1.5 + ";\n /*stroke-opacity: 0.4;*/\n }\n .grayed .sim-bb-wire-ends-g:not(.highlight) .sim-bb-wire-end {\n stroke: #777;\n fill: #777;\n }\n .grayed .sim-bb-wire:not(.highlight) {\n stroke: #CCC;\n }\n .sim-bb-wire-ends-g:hover .sim-bb-wire-end {\n stroke: red;\n fill: red;\n }\n .sim-bb-wire-ends-g:hover .sim-bb-wire-bare-end {\n stroke: #FFF;\n fill: #FFF;\n }\n ";
function cssEncodeColor(color) {
//HACK/TODO: do real CSS encoding.
return color
.replace(/\#/g, "-")
.replace(/\(/g, "-")
.replace(/\)/g, "-")
.replace(/\,/g, "-")
.replace(/\./g, "-")
.replace(/\s/g, "");
}
(function (WireEndStyle) {
WireEndStyle[WireEndStyle["BBJumper"] = 0] = "BBJumper";
WireEndStyle[WireEndStyle["OpenJumper"] = 1] = "OpenJumper";
WireEndStyle[WireEndStyle["Croc"] = 2] = "Croc";
})(visuals.WireEndStyle || (visuals.WireEndStyle = {}));
var WireEndStyle = visuals.WireEndStyle;
function mkWirePart(cp, clr, croc) {
if (croc === void 0) { croc = false; }
var g = pxsim.svg.elt("g");
var cx = cp[0], cy = cp[1];
var offset = WIRE_PART_CURVE_OFF;
var p1 = [cx - offset, cy - WIRE_PART_LENGTH / 2];
var p2 = [cx + offset, cy + WIRE_PART_LENGTH / 2];
clr = visuals.mapWireColor(clr);
var e1;
if (croc)
e1 = mkCrocEnd(p1, true, clr);
else
e1 = mkOpenJumperEnd(p1, true, clr);
var s = mkWirePartSeg(p1, p2, clr);
var e2 = mkOpenJumperEnd(p2, false, clr);
g.appendChild(s.el);
g.appendChild(e1.el);
g.appendChild(e2.el);
var l = Math.min(e1.x, e2.x);
var r = Math.max(e1.x + e1.w, e2.x + e2.w);
var t = Math.min(e1.y, e2.y);
var b = Math.max(e1.y + e1.h, e2.y + e2.h);
return { el: g, x: l, y: t, w: r - l, h: b - t };
}
visuals.mkWirePart = mkWirePart;
function mkCurvedWireSeg(p1, p2, smooth, clrClass) {
var coordStr = function (xy) { return xy[0] + ", " + xy[1]; };
var x1 = p1[0], y1 = p1[1];
var x2 = p2[0], y2 = p2[1];
var yLen = (y2 - y1);
var c1 = [x1, y1 + yLen * smooth];
var c2 = [x2, y2 - yLen * smooth];
var w = pxsim.svg.mkPath("sim-bb-wire", "M" + coordStr(p1) + " C" + coordStr(c1) + " " + coordStr(c2) + " " + coordStr(p2));
pxsim.svg.addClass(w, "wire-stroke-" + clrClass);
return w;
}
function mkWirePartSeg(p1, p2, clr) {
//TODO: merge with mkCurvedWireSeg
var coordStr = function (xy) { return xy[0] + ", " + xy[1]; };
var x1 = p1[0], y1 = p1[1];
var x2 = p2[0], y2 = p2[1];
var yLen = (y2 - y1);
var c1 = [x1, y1 + yLen * .8];
var c2 = [x2, y2 - yLen * .8];
var e = pxsim.svg.mkPath("sim-bb-wire", "M" + coordStr(p1) + " C" + coordStr(c1) + " " + coordStr(c2) + " " + coordStr(p2));
e.style["stroke"] = clr;
return { el: e, x: Math.min(x1, x2), y: Math.min(y1, y2), w: Math.abs(x1 - x2), h: Math.abs(y1 - y2) };
}
function mkWireSeg(p1, p2, clrClass) {
var coordStr = function (xy) { return xy[0] + ", " + xy[1]; };
var w = pxsim.svg.mkPath("sim-bb-wire", "M" + coordStr(p1) + " L" + coordStr(p2));
pxsim.svg.addClass(w, "wire-stroke-" + clrClass);
return w;
}
function mkBBJumperEnd(p, clrClass) {
var endW = visuals.PIN_DIST / 4;
var w = pxsim.svg.elt("circle");
var x = p[0];
var y = p[1];
var r = WIRE_WIDTH / 2 + endW / 2;
pxsim.svg.hydrate(w, { cx: x, cy: y, r: r, class: "sim-bb-wire-end" });
pxsim.svg.addClass(w, "wire-fill-" + clrClass);
w.style["stroke-width"] = endW + "px";
return w;
}
function mkOpenJumperEnd(p, top, clr) {
var k = visuals.PIN_DIST * 0.24;
var plasticLength = k * 10;
var plasticWidth = k * 2;
var metalLength = k * 6;
var metalWidth = k;
var strokeWidth = visuals.PIN_DIST / 4.0;
var cx = p[0], cy = p[1];
var o = top ? -1 : 1;
var g = pxsim.svg.elt("g");
var el = pxsim.svg.elt("rect");
var h1 = plasticLength;
var w1 = plasticWidth;
var x1 = cx - w1 / 2;
var y1 = cy - (h1 / 2);
pxsim.svg.hydrate(el, { x: x1, y: y1, width: w1, height: h1, rx: 0.5, ry: 0.5, class: "sim-bb-wire-end" });
el.style["stroke-width"] = strokeWidth + "px";
var el2 = pxsim.svg.elt("rect");
var h2 = metalLength;
var w2 = metalWidth;
var cy2 = cy + o * (h1 / 2 + h2 / 2);
var x2 = cx - w2 / 2;
var y2 = cy2 - (h2 / 2);
pxsim.svg.hydrate(el2, { x: x2, y: y2, width: w2, height: h2, class: "sim-bb-wire-bare-end" });
el2.style["fill"] = "#bbb";
g.appendChild(el2);
g.appendChild(el);
return { el: g, x: x1 - strokeWidth, y: Math.min(y1, y2), w: w1 + strokeWidth * 2, h: h1 + h2 };
}
function mkSmallMBPinEnd(p, top, clr) {
//HACK
//TODO: merge with mkOpenJumperEnd()
var k = visuals.PIN_DIST * 0.24;
var plasticLength = k * 4;
var plasticWidth = k * 1.2;
var metalLength = k * 10;
var metalWidth = k;
var strokeWidth = visuals.PIN_DIST / 4.0;
var cx = p[0], cy = p[1];
var yOffset = 10;
var o = top ? -1 : 1;
var g = pxsim.svg.elt("g");
var el = pxsim.svg.elt("rect");
var h1 = plasticLength;
var w1 = plasticWidth;
var x1 = cx - w1 / 2;
var y1 = cy + yOffset - (h1 / 2);
pxsim.svg.hydrate(el, { x: x1, y: y1, width: w1, height: h1, rx: 0.5, ry: 0.5, class: "sim-bb-wire-end" });
el.style["stroke-width"] = strokeWidth + "px";
var el2 = pxsim.svg.elt("rect");
var h2 = metalLength;
var w2 = metalWidth;
var cy2 = cy + yOffset + o * (h1 / 2 + h2 / 2);
var x2 = cx - w2 / 2;
var y2 = cy2 - (h2 / 2);
pxsim.svg.hydrate(el2, { x: x2, y: y2, width: w2, height: h2, class: "sim-bb-wire-bare-end" });
el2.style["fill"] = "#bbb";
g.appendChild(el2);
g.appendChild(el);
return { el: g, x: x1 - strokeWidth, y: Math.min(y1, y2), w: w1 + strokeWidth * 2, h: h1 + h2 };
}
function mkCrocEnd(p, top, clr) {
//TODO: merge with mkOpenJumperEnd()
var k = visuals.PIN_DIST * 0.24;
var plasticWidth = k * 4;
var plasticLength = k * 10.0;
var metalWidth = k * 3.5;
var metalHeight = k * 3.5;
var pointScalar = .15;
var baseScalar = .3;
var taperScalar = .7;
var strokeWidth = visuals.PIN_DIST / 4.0;
var cx = p[0], cy = p[1];
var o = top ? -1 : 1;
var g = pxsim.svg.elt("g");
var el = pxsim.svg.elt("polygon");
var h1 = plasticLength;
var w1 = plasticWidth;
var x1 = cx - w1 / 2;
var y1 = cy - (h1 / 2);
var mkPnt = function (xy) { return (xy[0] + "," + xy[1]); };
var mkPnts = function () {
var xys = [];
for (var _i = 0; _i < arguments.length; _i++) {
xys[_i - 0] = arguments[_i];
}
return xys.map(function (xy) { return mkPnt(xy); }).join(" ");
};
var topScalar = top ? pointScalar : baseScalar;
var midScalar = top ? taperScalar : (1 - taperScalar);
var botScalar = top ? baseScalar : pointScalar;
pxsim.svg.hydrate(el, {
points: mkPnts([x1 + w1 * topScalar, y1], //TL
[x1 + w1 * (1 - topScalar), y1], //TR
[x1 + w1, y1 + h1 * midScalar], //MR
[x1 + w1 * (1 - botScalar), y1 + h1], //BR
[x1 + w1 * botScalar, y1 + h1], //BL
[x1, y1 + h1 * midScalar]) //ML
});
pxsim.svg.hydrate(el, { rx: 0.5, ry: 0.5, class: "sim-bb-wire-end" });
el.style["stroke-width"] = strokeWidth + "px";
var el2 = pxsim.svg.elt("rect");
var h2 = metalWidth;
var w2 = metalHeight;
var cy2 = cy + o * (h1 / 2 + h2 / 2);
var x2 = cx - w2 / 2;
var y2 = cy2 - (h2 / 2);
pxsim.svg.hydrate(el2, { x: x2, y: y2, width: w2, height: h2, class: "sim-bb-wire-bare-end" });
g.appendChild(el2);
g.appendChild(el);
return { el: g, x: x1 - strokeWidth, y: Math.min(y1, y2), w: w1 + strokeWidth * 2, h: h1 + h2 };
}
//TODO: make this stupid class obsolete
var WireFactory = (function () {
function WireFactory(underboard, overboard, boardEdges, styleEl, getLocCoord) {
this.nextWireId = 0;
this.styleEl = styleEl;
this.styleEl.textContent += visuals.WIRES_CSS;
this.underboard = underboard;
this.overboard = overboard;
this.boardEdges = boardEdges;
this.getLocCoord = getLocCoord;
}
WireFactory.prototype.indexOfMin = function (vs) {
var minIdx = 0;
var min = vs[0];
for (var i = 1; i < vs.length; i++) {
if (vs[i] < min) {
min = vs[i];
minIdx = i;
}
}
return minIdx;
};
WireFactory.prototype.closestEdgeIdx = function (p) {
var dists = this.boardEdges.map(function (e) { return Math.abs(p[1] - e); });
var edgeIdx = this.indexOfMin(dists);
return edgeIdx;
};
WireFactory.prototype.closestEdge = function (p) {
return this.boardEdges[this.closestEdgeIdx(p)];
};
WireFactory.prototype.drawWire = function (pin1, pin2, color) {
var _this = this;
var wires = [];
var g = pxsim.svg.child(this.overboard, "g", { class: "sim-bb-wire-group" });
var closestPointOffBoard = function (p) {
var offset = visuals.PIN_DIST / 2;
var e = _this.closestEdge(p);
var y;
if (e - p[1] < 0)
y = e - offset;
else
y = e + offset;
return [p[0], y];
};
var wireId = this.nextWireId++;
var clrClass = cssEncodeColor(color);
var end1 = mkBBJumperEnd(pin1, clrClass);
var end2 = mkBBJumperEnd(pin2, clrClass);
var endG = pxsim.svg.child(g, "g", { class: "sim-bb-wire-ends-g" });
endG.appendChild(end1);
endG.appendChild(end2);
var edgeIdx1 = this.closestEdgeIdx(pin1);
var edgeIdx2 = this.closestEdgeIdx(pin2);
if (edgeIdx1 == edgeIdx2) {
var seg = mkWireSeg(pin1, pin2, clrClass);
g.appendChild(seg);
wires.push(seg);
}
else {
var offP1 = closestPointOffBoard(pin1);
var offP2 = closestPointOffBoard(pin2);
var offSeg1 = mkWireSeg(pin1, offP1, clrClass);
var offSeg2 = mkWireSeg(pin2, offP2, clrClass);
var midSeg = void 0;
var midSegHover = void 0;
var isBetweenMiddleTwoEdges = (edgeIdx1 == 1 || edgeIdx1 == 2) && (edgeIdx2 == 1 || edgeIdx2 == 2);
if (isBetweenMiddleTwoEdges) {
midSeg = mkCurvedWireSeg(offP1, offP2, BB_WIRE_SMOOTH, clrClass);
midSegHover = mkCurvedWireSeg(offP1, offP2, BB_WIRE_SMOOTH, clrClass);
}
else {
midSeg = mkWireSeg(offP1, offP2, clrClass);
midSegHover = mkWireSeg(offP1, offP2, clrClass);
}
pxsim.svg.addClass(midSegHover, "sim-bb-wire-hover");
g.appendChild(offSeg1);
wires.push(offSeg1);
g.appendChild(offSeg2);
wires.push(offSeg2);
this.underboard.appendChild(midSeg);
wires.push(midSeg);
g.appendChild(midSegHover);
wires.push(midSegHover);
//set hover mechanism
var wireIdClass_1 = "sim-bb-wire-id-" + wireId;
var setId = function (e) { return pxsim.svg.addClass(e, wireIdClass_1); };
setId(endG);
setId(midSegHover);
this.styleEl.textContent += "\n ." + wireIdClass_1 + ":hover ~ ." + wireIdClass_1 + ".sim-bb-wire-hover {\n visibility: visible;\n }";
}
// wire colors
var colorCSS = "\n .wire-stroke-" + clrClass + " {\n stroke: " + visuals.mapWireColor(color) + ";\n }\n .wire-fill-" + clrClass + " {\n fill: " + visuals.mapWireColor(color) + ";\n }\n ";
this.styleEl.textContent += colorCSS;
return { endG: endG, end1: end1, end2: end2, wires: wires };
};
WireFactory.prototype.drawWireWithCrocs = function (pin1, pin2, color, smallPin) {
var _this = this;
if (smallPin === void 0) { smallPin = false; }
//TODO: merge with drawWire()
var PIN_Y_OFF = 40;
var CROC_Y_OFF = -17;
var wires = [];
var g = pxsim.svg.child(this.overboard, "g", { class: "sim-bb-wire-group" });
var closestPointOffBoard = function (p) {
var offset = visuals.PIN_DIST / 2;
var e = _this.closestEdge(p);
var y;
if (e - p[1] < 0)
y = e - offset;
else
y = e + offset;
return [p[0], y];
};
var wireId = this.nextWireId++;
var clrClass = cssEncodeColor(color);
var end1 = mkBBJumperEnd(pin1, clrClass);
var pin2orig = pin2;
var x2 = pin2[0], y2 = pin2[1];
pin2 = [x2, y2 + PIN_Y_OFF]; //HACK
x2 = pin2[0], y2 = pin2[1];
var endCoord2 = [x2, y2 + CROC_Y_OFF];
var end2AndSize;
if (smallPin)
end2AndSize = mkSmallMBPinEnd(endCoord2, true, color);
else
end2AndSize = mkCrocEnd(endCoord2, true, color);
var end2 = end2AndSize.el;
var endG = pxsim.svg.child(g, "g", { class: "sim-bb-wire-ends-g" });
endG.appendChild(end1);
//endG.appendChild(end2);
var edgeIdx1 = this.closestEdgeIdx(pin1);
var edgeIdx2 = this.closestEdgeIdx(pin2orig);
if (edgeIdx1 == edgeIdx2) {
var seg = mkWireSeg(pin1, pin2, clrClass);
g.appendChild(seg);
wires.push(seg);
}
else {
var offP1 = closestPointOffBoard(pin1);
//let offP2 = closestPointOffBoard(pin2orig);
var offSeg1 = mkWireSeg(pin1, offP1, clrClass);
//let offSeg2 = mkWireSeg(pin2, offP2, clrClass);
var midSeg = void 0;
var midSegHover = void 0;
var isBetweenMiddleTwoEdges = (edgeIdx1 == 1 || edgeIdx1 == 2) && (edgeIdx2 == 1 || edgeIdx2 == 2);
if (isBetweenMiddleTwoEdges) {
midSeg = mkCurvedWireSeg(offP1, pin2, BB_WIRE_SMOOTH, clrClass);
midSegHover = mkCurvedWireSeg(offP1, pin2, BB_WIRE_SMOOTH, clrClass);
}
else {
midSeg = mkWireSeg(offP1, pin2, clrClass);
midSegHover = mkWireSeg(offP1, pin2, clrClass);
}
pxsim.svg.addClass(midSegHover, "sim-bb-wire-hover");
g.appendChild(offSeg1);
wires.push(offSeg1);
// g.appendChild(offSeg2);
// wires.push(offSeg2);
this.underboard.appendChild(midSeg);
wires.push(midSeg);
//g.appendChild(midSegHover);
//wires.push(midSegHover);
//set hover mechanism
var wireIdClass_2 = "sim-bb-wire-id-" + wireId;
var setId = function (e) { return pxsim.svg.addClass(e, wireIdClass_2); };
setId(endG);
setId(midSegHover);
this.styleEl.textContent += "\n ." + wireIdClass_2 + ":hover ~ ." + wireIdClass_2 + ".sim-bb-wire-hover {\n visibility: visible;\n }";
}
endG.appendChild(end2); //HACK
// wire colors
var colorCSS = "\n .wire-stroke-" + clrClass + " {\n stroke: " + visuals.mapWireColor(color) + ";\n }\n .wire-fill-" + clrClass + " {\n fill: " + visuals.mapWireColor(color) + ";\n }\n ";
this.styleEl.textContent += colorCSS;
return { endG: endG, end1: end1, end2: end2, wires: wires };
};
WireFactory.prototype.addWire = function (start, end, color, withCrocs) {
if (withCrocs === void 0) { withCrocs = false; }
var startLoc = this.getLocCoord(start);
var endLoc = this.getLocCoord(end);
var wireEls;
if (withCrocs && end.type == "dalboard") {
var boardPin = end.pin;
if (boardPin == "P0" || boardPin == "P1" || boardPin == "P2" || boardPin == "GND" || boardPin == "+3v3") {
//HACK
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color);
}
else {
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color, true);
}
}
else {
wireEls = this.drawWire(startLoc, endLoc, color);
}
return wireEls;
};
return WireFactory;
}());
visuals.WireFactory = WireFactory;
})(visuals = pxsim.visuals || (pxsim.visuals = {}));
})(pxsim || (pxsim = {}));