把前端从CircuitVerse中拆了出来

This commit is contained in:
zhbaor 2022-01-23 17:33:42 +08:00
commit 5bf1284599
2182 changed files with 189323 additions and 0 deletions

201
simulator/src/Verilog2CV.js Normal file
View file

@ -0,0 +1,201 @@
import { newCircuit, switchCircuit, changeCircuitName} from './circuit'
import SubCircuit from './subcircuit';
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/mode/verilog/verilog.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/hint/anyword-hint.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/display/autorefresh.js';
import { showError, showMessage } from './utils';
import { showProperties } from './ux';
var editor;
var verilogMode = false;
export function createVerilogCircuit() {
newCircuit(undefined, undefined, true, true);
verilogModeSet(true);
}
export function saveVerilogCode() {
var code = editor.getValue();
globalScope.verilogMetadata.code = code;
generateVerilogCircuit(code);
}
export function resetVerilogCode() {
editor.setValue(globalScope.verilogMetadata.code);
}
export function hasVerilogCodeChanges() {
return editor.getValue() != globalScope.verilogMetadata.code;
}
export function verilogModeGet() {
return verilogMode;
}
export function verilogModeSet(mode) {
if(mode == verilogMode) return;
verilogMode = mode;
if(mode) {
$('#code-window').show();
$('.elementPanel').hide();
$('.timing-diagram-panel').hide();
$('.quick-btn').hide();
$('#verilogEditorPanel').show();
if (!embed) {
simulationArea.lastSelected = globalScope.root;
showProperties(undefined);
showProperties(simulationArea.lastSelected);
}
resetVerilogCode();
}
else {
$('#code-window').hide();
$('.elementPanel').show();
$('.timing-diagram-panel').show();
$('.quick-btn').show();
$('#verilogEditorPanel').hide();
}
}
import yosysTypeMap from './VerilogClasses';
class verilogSubCircuit {
constructor(circuit) {
this.circuit = circuit;
}
getPort(portName) {
var numInputs = this.circuit.inputNodes.length;
var numOutputs = this.circuit.outputNodes.length
for(var i = 0; i < numInputs; i++) {
if(this.circuit.data.Input[i].label == portName){
return this.circuit.inputNodes[i];
}
}
for(var i = 0; i < numOutputs; i++) {
if(this.circuit.data.Output[i].label == portName) {
return this.circuit.outputNodes[i];
}
}
}
}
export function YosysJSON2CV(JSON, parentScope = globalScope, name = "verilogCircuit", subCircuitScope = {}, root = false){
var parentID = (parentScope.id);
var subScope;
if(root) {
subScope = parentScope;
}
else {
subScope = newCircuit(name, undefined, true, false);
}
var circuitDevices = {};
for (var subCircuitName in JSON.subcircuits) {
var scope = YosysJSON2CV(JSON.subcircuits[subCircuitName], subScope, subCircuitName, subCircuitScope);
subCircuitScope[subCircuitName] = scope.id;
}
for (var device in JSON.devices) {
var deviceType = JSON.devices[device].type;
if(deviceType == "Subcircuit") {
var subCircuitName = JSON.devices[device].celltype;
circuitDevices[device] = new verilogSubCircuit(new SubCircuit(500, 500, undefined, subCircuitScope[subCircuitName]));
}
else {
circuitDevices[device] = new yosysTypeMap[deviceType](JSON.devices[device]);
}
}
for (var connection in JSON.connectors) {
var fromId = JSON.connectors[connection]["from"]["id"];
var fromPort = JSON.connectors[connection]["from"]["port"];
var toId = JSON.connectors[connection]["to"]["id"];
var toPort = JSON.connectors[connection]["to"]["port"];
var fromObj = circuitDevices[fromId];
var toObj = circuitDevices[toId];
var fromPortNode = fromObj.getPort(fromPort);
var toPortNode = toObj.getPort(toPort);
fromPortNode.connect(toPortNode);
}
if (!root) {
switchCircuit(parentID);
return subScope;
}
}
export default function generateVerilogCircuit(verilogCode, scope = globalScope) {
const url='/simulator/verilogcv';
var params = {"code": verilogCode};
$.ajax({
url: url,
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
},
data: params,
success: function(response) {
var circuitData = response;
scope.initialize();
for(var id in scope.verilogMetadata.subCircuitScopeIds)
delete scopeList[id];
scope.verilogMetadata.subCircuitScopeIds = [];
scope.verilogMetadata.code = verilogCode;
var subCircuitScope = {};
YosysJSON2CV(circuitData, globalScope, "verilogCircuit", subCircuitScope, true);
changeCircuitName(circuitData.name);
showMessage('Verilog Circuit Successfully Created');
$('#verilogOutput').empty();
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
var errorCode = XMLHttpRequest.status;
if (errorCode == 500) {
showError("Could not connect to Yosys");
}
else {
showError("There is some issue with the code");
var errorMessage = XMLHttpRequest.responseJSON;
$('#verilogOutput').html(errorMessage.message);
}
},
failure: function(err) {
}
});
}
export function setupCodeMirrorEnvironment() {
var myTextarea = document.getElementById("codeTextArea");
CodeMirror.commands.autocomplete = function(cm) {
cm.showHint({hint: CodeMirror.hint.anyword});
}
editor = CodeMirror.fromTextArea(myTextarea, {
mode: "verilog",
autoRefresh:true,
styleActiveLine: true,
lineNumbers: true,
autoCloseBrackets: true,
smartIndent: true,
indentWithTabs: true,
extraKeys: {"Ctrl-Space": "autocomplete"}
});
editor.setValue("// Write Some Verilog Code Here!")
setTimeout(function() {
editor.refresh();
},1);
}

File diff suppressed because it is too large Load diff

218
simulator/src/app.js Executable file
View file

@ -0,0 +1,218 @@
import { setup } from './setup';
import Array from './arrayHelpers';
document.addEventListener('DOMContentLoaded', () => {
setup();
var js = {
"devices": {
"dev0": {
"type": "Input",
"net": "clk",
"order": 0,
"bits": 1
},
"dev1": {
"type": "Input",
"net": "addr",
"order": 1,
"bits": 4
},
"dev2": {
"type": "Output",
"net": "data",
"order": 2,
"bits": 5
},
"dev3": {
"type": "Input",
"net": "addr2",
"order": 3,
"bits": 4
},
"dev4": {
"type": "Output",
"net": "data2",
"order": 4,
"bits": 5
},
"dev5": {
"type": "Input",
"net": "wraddr",
"order": 5,
"bits": 4
},
"dev6": {
"type": "Input",
"net": "wrdata",
"order": 6,
"bits": 5
},
"dev7": {
"type": "Input",
"net": "wraddr2",
"order": 7,
"bits": 4
},
"dev8": {
"type": "Input",
"net": "wrdata2",
"order": 8,
"bits": 5
},
"dev9": {
"label": "mem",
"type": "Memory",
"bits": 5,
"abits": 4,
"words": 16,
"offset": 0,
"rdports": [
{},
{
"clock_polarity": true
}
],
"wrports": [
{
"clock_polarity": true
},
{
"clock_polarity": true
}
],
"memdata": [
13,
"00001",
3,
"11111"
]
}
},
"connectors": [
{
"to": {
"id": "dev9",
"port": "rd1clk"
},
"from": {
"id": "dev0",
"port": "out"
},
"name": "clk"
},
{
"to": {
"id": "dev9",
"port": "wr0clk"
},
"from": {
"id": "dev0",
"port": "out"
},
"name": "clk"
},
{
"to": {
"id": "dev9",
"port": "wr1clk"
},
"from": {
"id": "dev0",
"port": "out"
},
"name": "clk"
},
{
"to": {
"id": "dev9",
"port": "rd0addr"
},
"from": {
"id": "dev1",
"port": "out"
},
"name": "addr"
},
{
"to": {
"id": "dev2",
"port": "in"
},
"from": {
"id": "dev9",
"port": "rd0data"
},
"name": "data"
},
{
"to": {
"id": "dev9",
"port": "rd1addr"
},
"from": {
"id": "dev3",
"port": "out"
},
"name": "addr2"
},
{
"to": {
"id": "dev4",
"port": "in"
},
"from": {
"id": "dev9",
"port": "rd1data"
},
"name": "data2"
},
{
"to": {
"id": "dev9",
"port": "wr0addr"
},
"from": {
"id": "dev5",
"port": "out"
},
"name": "wraddr"
},
{
"to": {
"id": "dev9",
"port": "wr0data"
},
"from": {
"id": "dev6",
"port": "out"
},
"name": "wrdata"
},
{
"to": {
"id": "dev9",
"port": "wr1addr"
},
"from": {
"id": "dev7",
"port": "out"
},
"name": "wraddr2"
},
{
"to": {
"id": "dev9",
"port": "wr1data"
},
"from": {
"id": "dev8",
"port": "out"
},
"name": "wrdata2"
}
],
"subcircuits": {}
};
});
window.Array = Array;

34
simulator/src/arrayHelpers.js Executable file
View file

@ -0,0 +1,34 @@
/* eslint-disable func-names */
/* eslint-disable no-global-assign */
/* eslint-disable no-extend-native */
export default Array = window.Array;
Object.defineProperty(Array.prototype, "clean", {
value: function (deleteValue) {
for (var i = 0; i < this.length; i++) {
if (this[i] === deleteValue) {
this.splice(i, 1);
i--;
}
}
return this;
},
enumerable: false
});
Object.defineProperty(Array.prototype, "extend", {
value: function (otherArray) {
/* you should include a test to check whether other_array really is an array */
otherArray.forEach(function (v) {
this.push(v);
}, this);
},
enumerable: false
});
Object.defineProperty(Array.prototype, "contains", {
value: function (value) {
return this.indexOf(value) > -1;
},
enumerable: false
});

17
simulator/src/backgroundArea.js Executable file
View file

@ -0,0 +1,17 @@
import { dots } from './canvasApi';
var backgroundArea;
export default backgroundArea = {
canvas: document.getElementById('backgroundArea'),
setup() {
this.canvas = document.getElementById('backgroundArea');
this.canvas.width = width;
this.canvas.height = height;
this.context = this.canvas.getContext('2d');
dots(true, false);
},
clear() {
if (!this.context) return;
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
};

1218
simulator/src/canvas2svg.js Normal file

File diff suppressed because it is too large Load diff

449
simulator/src/canvasApi.js Executable file
View file

@ -0,0 +1,449 @@
/* eslint-disable no-param-reassign */
import backgroundArea from './backgroundArea';
import simulationArea from './simulationArea';
import miniMapArea, { removeMiniMap, updatelastMinimapShown } from './minimap';
import { colors } from './themer/themer';
var unit = 10;
export function findDimensions(scope = globalScope) {
var totalObjects = 0;
simulationArea.minWidth = undefined;
simulationArea.maxWidth = undefined;
simulationArea.minHeight = undefined;
simulationArea.maxHeight = undefined;
for (var i = 0; i < updateOrder.length; i++) {
if (updateOrder[i] !== 'wires') {
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
totalObjects += 1;
var obj = scope[updateOrder[i]][j];
if (totalObjects === 1) {
simulationArea.minWidth = obj.absX();
simulationArea.minHeight = obj.absY();
simulationArea.maxWidth = obj.absX();
simulationArea.maxHeight = obj.absY();
}
if (obj.objectType !== 'Node') {
if (obj.y - obj.upDimensionY < simulationArea.minHeight) { simulationArea.minHeight = obj.y - obj.upDimensionY; }
if (obj.y + obj.downDimensionY > simulationArea.maxHeight) { simulationArea.maxHeight = obj.y + obj.downDimensionY; }
if (obj.x - obj.leftDimensionX < simulationArea.minWidth) { simulationArea.minWidth = obj.x - obj.leftDimensionX; }
if (obj.x + obj.rightDimensionX > simulationArea.maxWidth) { simulationArea.maxWidth = obj.x + obj.rightDimensionX; }
} else {
if (obj.absY() < simulationArea.minHeight) { simulationArea.minHeight = obj.absY(); }
if (obj.absY() > simulationArea.maxHeight) { simulationArea.maxHeight = obj.absY(); }
if (obj.absX() < simulationArea.minWidth) { simulationArea.minWidth = obj.absX(); }
if (obj.absX() > simulationArea.maxWidth) { simulationArea.maxWidth = obj.absX(); }
}
}
}
}
simulationArea.objectList = updateOrder;
}
// Function used to change the zoom level wrt to a point
// fn to change scale (zoom) - It also shifts origin so that the position
// of the object in focus doesn't change
export function changeScale(delta, xx, yy, method = 1) {
// method = 3/2 - Zoom wrt center of screen
// method = 1 - Zoom wrt position of mouse
// Otherwise zoom wrt to selected object
if (method === 3) {
xx = (width / 2 - globalScope.ox) / globalScope.scale;
yy = (height / 2 - globalScope.oy) / globalScope.scale;
} else if (xx === undefined || yy === undefined || xx === 'zoomButton' || yy === 'zoomButton') {
if (simulationArea.lastSelected && simulationArea.lastSelected.objectType !== 'Wire') { // selected object
xx = simulationArea.lastSelected.x;
yy = simulationArea.lastSelected.y;
} else { // mouse location
// eslint-disable-next-line no-lonely-if
if (method === 1) {
xx = simulationArea.mouseX;
yy = simulationArea.mouseY;
} else if (method === 2) {
xx = (width / 2 - globalScope.ox) / globalScope.scale;
yy = (height / 2 - globalScope.oy) / globalScope.scale;
}
}
}
var oldScale = globalScope.scale;
globalScope.scale = Math.max(0.5, Math.min(4 * DPR, globalScope.scale + delta));
globalScope.scale = Math.round(globalScope.scale * 10) / 10;
globalScope.ox -= Math.round(xx * (globalScope.scale - oldScale)); // Shift accordingly, so that we zoom wrt to the selected point
globalScope.oy -= Math.round(yy * (globalScope.scale - oldScale));
// dots(true,false);
// MiniMap
if (!embed && !lightMode) {
findDimensions(globalScope);
miniMapArea.setup();
$('#miniMap').show();
updatelastMinimapShown();
$('#miniMap').show();
setTimeout(removeMiniMap, 2000);
}
}
// fn to draw Dots on screen
// the function is called only when the zoom level or size of screen changes.
// Otherwise for normal panning, the canvas itself is moved to give the illusion of movement
export function dots(dots = true, transparentBackground = false, force = false) {
var scale = unit * globalScope.scale;
var ox = globalScope.ox % scale; // offset
var oy = globalScope.oy % scale; // offset
document.getElementById('backgroundArea').style.left = (ox - scale) / DPR;
document.getElementById('backgroundArea').style.top = (oy - scale) / DPR;
if (globalScope.scale === simulationArea.prevScale && !force) return;
if (!backgroundArea.context) return;
simulationArea.prevScale = globalScope.scale;
var canvasWidth = backgroundArea.canvas.width; // max X distance
var canvasHeight = backgroundArea.canvas.height; // max Y distance
var ctx = backgroundArea.context;
ctx.beginPath();
backgroundArea.clear();
ctx.strokeStyle = colors["canvas_stroke"];
ctx.lineWidth = 1;
if (!transparentBackground) {
ctx.fillStyle = colors["canvas_fill"];
ctx.rect(0, 0, canvasWidth, canvasHeight);
ctx.fill();
}
if (!embed) {
var correction = 0.5 * (ctx.lineWidth % 2);
for (var i = 0; i < canvasWidth; i += scale) {
ctx.moveTo(Math.round(i + correction) - correction, 0);
ctx.lineTo(Math.round(i + correction) - correction, canvasHeight);
}
for (var j = 0; j < canvasHeight; j += scale) {
ctx.moveTo(0, Math.round(j + correction) - correction);
ctx.lineTo(canvasWidth, Math.round(j + correction) - correction);
}
ctx.stroke();
}
// Old Code
// function drawPixel(x, y, r, g, b, a) {
// var index = (x + y * canvasWidth) * 4;
// canvasData.data[index + 0] = r;
// canvasData.data[index + 1] = g;
// canvasData.data[index + 2] = b;
// canvasData.data[index + 3] = a;
// }
// if (dots) {
// var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
//
//
//
// for (var i = 0 + ox; i < canvasWidth; i += scale)
// for (var j = 0 + oy; j < canvasHeight; j += scale)
// drawPixel(i, j, 0, 0, 0, 255);
// ctx.putImageData(canvasData, 0, 0);
// }
}
// Helper canvas API starts here
// All canvas functions are wrt to a center point (xx,yy),
// direction is used to abstract rotation of everything by a certain angle
// Possible values for direction = "RIGHT" (default), "LEFT", "UP", "DOWN"
export function bezierCurveTo(x1, y1, x2, y2, x3, y3, xx, yy, dir) {
[x1, y1] = rotate(x1, y1, dir);
[x2, y2] = rotate(x2, y2, dir);
[x3, y3] = rotate(x3, y3, dir);
var { ox } = globalScope;
var { oy } = globalScope;
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
x3 *= globalScope.scale;
y3 *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
var ctx = simulationArea.context;
ctx.bezierCurveTo(Math.round(xx + ox + x1), Math.round(yy + oy + y1), Math.round(xx + ox + x2), Math.round(yy + oy + y2), Math.round(xx + ox + x3), Math.round(yy + oy + y3));
}
export function moveTo(ctx, x1, y1, xx, yy, dir, bypass = false) {
var correction = 0.5 * (ctx.lineWidth % 2);
let newX;
let newY;
[newX, newY] = rotate(x1, y1, dir);
newX *= globalScope.scale;
newY *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
if (bypass) { ctx.moveTo(xx + globalScope.ox + newX, yy + globalScope.oy + newY); } else { ctx.moveTo(Math.round(xx + globalScope.ox + newX - correction) + correction, Math.round(yy + globalScope.oy + newY - correction) + correction); }
}
export function lineTo(ctx, x1, y1, xx, yy, dir) {
let newX;
let newY;
var correction = 0.5 * (ctx.lineWidth % 2);
[newX, newY] = rotate(x1, y1, dir);
newX *= globalScope.scale;
newY *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.lineTo(Math.round(xx + globalScope.ox + newX - correction) + correction, Math.round(yy + globalScope.oy + newY - correction) + correction);
}
export function arc(ctx, sx, sy, radius, start, stop, xx, yy, dir) {
// ox-x of origin, xx- x of element , sx - shift in x from element
let Sx; let Sy; let newStart; let newStop; let counterClock;
var correction = 0.5 * (ctx.lineWidth % 2);
[Sx, Sy] = rotate(sx, sy, dir);
Sx *= globalScope.scale;
Sy *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
radius *= globalScope.scale;
[newStart, newStop, counterClock] = rotateAngle(start, stop, dir);
ctx.arc(Math.round(xx + globalScope.ox + Sx + correction) - correction, Math.round(yy + globalScope.oy + Sy + correction) - correction, Math.round(radius), newStart, newStop, counterClock);
}
export function arc2(ctx, sx, sy, radius, start, stop, xx, yy, dir) {
// ox-x of origin, xx- x of element , sx - shift in x from element
let Sx; let Sy; let newStart; let newStop; let counterClock;
var correction = 0.5 * (ctx.lineWidth % 2);
[Sx, Sy] = rotate(sx, sy, dir);
Sx *= globalScope.scale;
Sy *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
radius *= globalScope.scale;
[newStart, newStop, counterClock] = rotateAngle(start, stop, dir);
var pi = 0;
if (counterClock) { pi = Math.PI; }
ctx.arc(Math.round(xx + globalScope.ox + Sx + correction) - correction, Math.round(yy + globalScope.oy + Sy + correction) - correction, Math.round(radius), newStart + pi, newStop + pi);
}
export function drawCircle2(ctx, sx, sy, radius, xx, yy, dir) { // ox-x of origin, xx- x of element , sx - shift in x from element
let Sx;
let Sy;
[Sx, Sy] = rotate(sx, sy, dir);
Sx *= globalScope.scale;
Sy *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
radius *= globalScope.scale;
ctx.arc(Math.round(xx + globalScope.ox + Sx), Math.round(yy + globalScope.oy + Sy), Math.round(radius), 0, 2 * Math.PI);
}
export function rect(ctx, x1, y1, x2, y2) {
var correction = 0.5 * (ctx.lineWidth % 2);
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
ctx.rect(Math.round(globalScope.ox + x1 - correction) + correction, Math.round(globalScope.oy + y1 - correction) + correction, Math.round(x2), Math.round(y2));
}
export function drawImage(ctx,img, x1, y1, w_canvas, h_canvas) {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x1 += globalScope.ox;
y1 += globalScope.oy;
w_canvas *= globalScope.scale;
h_canvas *= globalScope.scale;
ctx.drawImage(img, x1, y1, w_canvas, h_canvas);
}
export function rect2(ctx, x1, y1, x2, y2, xx, yy, dir = 'RIGHT') {
var correction = 0.5 * (ctx.lineWidth % 2);
[x1, y1] = rotate(x1, y1, dir);
[x2, y2] = rotate(x2, y2, dir);
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.rect(Math.round(globalScope.ox + xx + x1 - correction) + correction, Math.round(globalScope.oy + yy + y1 - correction) + correction, Math.round(x2), Math.round(y2));
}
export function rotate(x1, y1, dir) {
if (dir === 'LEFT') { return [-x1, y1]; }
if (dir === 'DOWN') { return [y1, x1]; }
if (dir === 'UP') { return [y1, -x1]; }
return [x1, y1];
}
export function correctWidth(w) {
return Math.max(1, Math.round(w * globalScope.scale));
}
function rotateAngle(start, stop, dir) {
if (dir === 'LEFT') { return [start, stop, true]; }
if (dir === 'DOWN') { return [start - Math.PI / 2, stop - Math.PI / 2, true]; }
if (dir === 'UP') { return [start - Math.PI / 2, stop - Math.PI / 2, false]; }
return [start, stop, false];
}
export function drawLine(ctx, x1, y1, x2, y2, color, width) {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineCap = 'round';
ctx.lineWidth = correctWidth(width);//* globalScope.scale;
var correction = 0.5 * (ctx.lineWidth % 2);
var hCorrection = 0;
var vCorrection = 0;
if (y1 === y2) vCorrection = correction;
if (x1 === x2) hCorrection = correction;
ctx.moveTo(Math.round(x1 + globalScope.ox + hCorrection) - hCorrection, Math.round(y1 + globalScope.oy + vCorrection) - vCorrection);
ctx.lineTo(Math.round(x2 + globalScope.ox + hCorrection) - hCorrection, Math.round(y2 + globalScope.oy + vCorrection) - vCorrection);
ctx.stroke();
}
// Checks if string color is a valid color using a hack
export function validColor(color) {
var $div = $('<div>');
$div.css('border', `1px solid ${color}`);
return ($div.css('border-color') !== '');
}
// Helper function to color "RED" to RGBA
export function colorToRGBA(color) {
var cvs;
var ctx;
cvs = document.createElement('canvas');
cvs.height = 1;
cvs.width = 1;
ctx = cvs.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
return ctx.getImageData(0, 0, 1, 1).data;
}
export function drawCircle(ctx, x1, y1, r, color) {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(Math.round(x1 + globalScope.ox), Math.round(y1 + globalScope.oy), Math.round(r * globalScope.scale), 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
}
// To show message like values, node name etc
export function canvasMessage(ctx, str, x1, y1, fontSize = 10) {
if (!str || !str.length) return;
ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway`;
ctx.textAlign = 'center';
var width = ctx.measureText(str).width / globalScope.scale + 8;
var height = 13;
ctx.strokeStyle = 'black';
ctx.lineWidth = correctWidth(1);
ctx.fillStyle = 'yellow';
ctx.save();
ctx.beginPath();
rect(ctx, x1 - width / 2, y1 - height / 2 - 3, width, height);
ctx.shadowColor = '#999';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.stroke();
ctx.fill();
ctx.restore();
x1 *= globalScope.scale;
y1 *= globalScope.scale;
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillText(str, Math.round(x1 + globalScope.ox), Math.round(y1 + globalScope.oy));
ctx.fill();
}
export function fillText(ctx, str, x1, y1, fontSize = 20) {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway`;
ctx.fillText(str, Math.round(x1 + globalScope.ox), Math.round(y1 + globalScope.oy));
}
export function fillText2(ctx, str, x1, y1, xx, yy, dir) {
var angle = {
RIGHT: 0,
LEFT: 0,
DOWN: Math.PI / 2,
UP: -Math.PI / 2,
};
x1 *= globalScope.scale;
y1 *= globalScope.scale;
[x1, y1] = rotate(x1, y1, dir);
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.font = `${Math.round(14 * globalScope.scale)}px Raleway`;
ctx.save();
ctx.translate(Math.round(xx + x1 + globalScope.ox), Math.round(yy + y1 + globalScope.oy));
ctx.rotate(angle[dir]);
ctx.textAlign = 'center';
ctx.fillText(str, 0, Math.round(4 * globalScope.scale) * (1 - 0 * (+(dir === 'DOWN'))));
ctx.restore();
}
export function fillText4(ctx, str, x1, y1, xx, yy, dir, fontSize = 14, textAlign = 'center') {
var angle = {
RIGHT: 0,
LEFT: 0,
DOWN: Math.PI / 2,
UP: -Math.PI / 2,
};
x1 *= globalScope.scale;
y1 *= globalScope.scale;
[x1, y1] = rotate(x1, y1, dir);
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway`;
// ctx.font = 20+"px Raleway";
ctx.textAlign = textAlign;
ctx.fillText(str, xx + x1 + globalScope.ox, yy + y1 + globalScope.oy + Math.round(fontSize / 3 * globalScope.scale));
}
export function fillText3(ctx, str, x1, y1, xx = 0, yy = 0, fontSize = 14, font = 'Raleway', textAlign = 'center') {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.font = `${Math.round(fontSize * globalScope.scale)}px ${font}`;
ctx.textAlign = textAlign;
ctx.fillText(str, Math.round(xx + x1 + globalScope.ox), Math.round(yy + y1 + globalScope.oy));
}
export const oppositeDirection = {
RIGHT: 'LEFT',
LEFT: 'RIGHT',
DOWN: 'UP',
UP: 'DOWN',
};
export const fixDirection = {
right: 'LEFT',
left: 'RIGHT',
down: 'UP',
up: 'DOWN',
LEFT: 'LEFT',
RIGHT: 'RIGHT',
UP: 'UP',
DOWN: 'DOWN',
};

362
simulator/src/circuit.js Executable file
View file

@ -0,0 +1,362 @@
/* eslint-disable import/no-cycle */
/* eslint-disable no-bitwise */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-restricted-globals */
/* eslint-disable consistent-return */
/* eslint-disable func-names */
/* eslint-disable array-callback-return */
/* eslint-disable no-use-before-define */
/* eslint-disable no-param-reassign */
/* eslint-disable no-alert */
import CircuitElement from './circuitElement';
import plotArea from './plotArea';
import simulationArea, { changeClockTime } from './simulationArea';
import {
stripTags,
uniq,
showMessage,
showError,
truncateString
} from './utils';
import { findDimensions, dots } from './canvasApi';
import { updateRestrictedElementsList } from './restrictedElementDiv';
import { scheduleBackup } from './data/backupCircuit';
import { showProperties } from './ux';
import {
scheduleUpdate, updateSimulationSet,
updateCanvasSet, updateSubcircuitSet,
forceResetNodesSet, changeLightMode
} from './engine';
import { toggleLayoutMode, layoutModeGet } from './layoutMode';
import { setProjectName, getProjectName } from './data/save';
import { changeClockEnable } from './sequential';
import { changeInputSize } from './modules';
import { verilogModeGet, verilogModeSet } from './Verilog2CV';
export const circuitProperty = {
toggleLayoutMode, setProjectName, changeCircuitName, changeClockTime, deleteCurrentCircuit, changeClockEnable, changeInputSize, changeLightMode,
};
export var scopeList = {};
export function resetScopeList() {
scopeList = {};
}
/**
* Function used to change the current focusedCircuit
* Disables layoutMode if enabled
* Changes UI tab etc
* Sets flags to make updates, resets most of the things
* @param {string} id - identifier for circuit
* @category circuit
*/
export function switchCircuit(id) {
if (layoutModeGet()) { toggleLayoutMode(); }
if (verilogModeGet()) { verilogModeSet(false);}
// globalScope.fixLayout();
scheduleBackup();
if (id === globalScope.id) return;
$(`.circuits`).removeClass('current');
simulationArea.lastSelected = undefined;
simulationArea.multipleObjectSelections = [];
simulationArea.copyList = [];
globalScope = scopeList[id];
if (globalScope.verilogMetadata.isVerilogCircuit) {
verilogModeSet(true);
}
if (globalScope.isVisible()) {
$(`#${id}`).addClass('current');
}
updateSimulationSet(true);
updateSubcircuitSet(true);
forceResetNodesSet(true);
dots(false);
simulationArea.lastSelected = globalScope.root;
if (!embed) {
showProperties(simulationArea.lastSelected);
plotArea.reset();
}
updateCanvasSet(true);
scheduleUpdate();
// to update the restricted elements information
updateRestrictedElementsList();
}
/**
* Deletes the current circuit
* Ensures that at least one circuit is there
* Ensures that no circuit depends on the current circuit
* Switched to a random circuit
* @category circuit
*/
function deleteCurrentCircuit(scopeId = globalScope.id) {
const scope = scopeList[scopeId];
if (Object.keys(scopeList).length <= 1) {
showError('At least 2 circuits need to be there in order to delete a circuit.');
return;
}
let dependencies = '';
for (id in scopeList) {
if (id != scope.id && scopeList[id].checkDependency(scope.id)) {
if (dependencies === '') {
dependencies = scopeList[id].name;
} else {
dependencies += `, ${scopeList[id].name}`;
}
}
}
if (dependencies) {
dependencies = `\nThe following circuits are depending on '${scope.name}': ${dependencies}\nDelete subcircuits of ${scope.name} before trying to delete ${scope.name}`;
alert(dependencies);
return;
}
const confirmation = confirm(`Are you sure want to close: ${scope.name}\nThis cannot be undone.`);
if (confirmation) {
if (scope.verilogMetadata.isVerilogCircuit) {
scope.initialize();
for(var id in scope.verilogMetadata.subCircuitScopeIds)
delete scopeList[id];
}
$(`#${scope.id}`).remove();
delete scopeList[scope.id];
switchCircuit(Object.keys(scopeList)[0]);
showMessage('Circuit was successfully closed');
} else { showMessage('Circuit was not closed'); }
}
/**
* Function to create new circuit
* Function creates button in tab, creates scope and switches to this circuit
* @param {string} name - name of the new circuit
* @param {string} id - identifier for circuit
* @category circuit
*/
export function newCircuit(name, id, isVerilog = false, isVerilogMain = false) {
if (layoutModeGet()) { toggleLayoutMode(); }
if (verilogModeGet()) { verilogModeSet(false);}
name = name || prompt('Enter circuit name:','Untitled-Circuit');
name = stripTags(name);
if (!name) return;
const scope = new Scope(name);
if (id) scope.id = id;
scopeList[scope.id] = scope;
if(isVerilog) {
scope.verilogMetadata.isVerilogCircuit = true;
scope.verilogMetadata.isMainCircuit = isVerilogMain;
}
globalScope = scope;
$('.circuits').removeClass('current');
if (!isVerilog || isVerilogMain) {
if(embed) {
var html = `<div style='' class='circuits toolbarButton current' draggable='true' id='${scope.id}'><span class='circuitName noSelect'>${truncateString(name, 18)}</span></div>`;
$('#tabsBar').append(html);
$("#tabsBar").addClass('embed-tabs');
}
else {
var html = `<div style='' class='circuits toolbarButton current' draggable='true' id='${scope.id}'><span class='circuitName noSelect'>${truncateString(name, 18)}</span><span class ='tabsCloseButton' id='${scope.id}' >x</span></div>`;
$('#tabsBar').children().last().before(html);
}
// Remove listeners
$('.circuits').off('click');
$('.circuitName').off('click');
$('.tabsCloseButton').off('click');
// Add listeners
$('.circuits').on('click',function () {
switchCircuit(this.id);
});
$('.circuitName').on('click',(e) => {
simulationArea.lastSelected = globalScope.root;
setTimeout(() => {
document.getElementById('circname').select();
}, 100);
});
$('.tabsCloseButton').on('click',function (e) {
e.stopPropagation();
deleteCurrentCircuit(this.id);
});
if (!embed) {
showProperties(scope.root);
}
dots(false);
}
return scope;
}
/**
* Used to change name of a circuit
* @param {string} name - new name
* @param {string} id - id of the circuit
* @category circuit
*/
export function changeCircuitName(name, id = globalScope.id) {
name = name || 'Untitled';
name = stripTags(name);
$(`#${id} .circuitName`).html(`${truncateString(name, 18)}`);
scopeList[id].name = name;
}
/**
* Class representing a Scope
* @class
* @param {string} name - name of the circuit
* @param {number=} id - a random id for the circuit
* @category circuit
*/
export default class Scope {
constructor(name = 'localScope', id = undefined) {
this.restrictedCircuitElementsUsed = [];
this.id = id || Math.floor((Math.random() * 100000000000) + 1);
this.CircuitElement = [];
this.name = name;
// root object for referring to main canvas - intermediate node uses this
this.root = new CircuitElement(0, 0, this, 'RIGHT', 1);
this.backups = [];
// maintaining a state (history) for redo function
this.history = [];
this.timeStamp = new Date().getTime();
this.verilogMetadata = {
isVerilogCircuit: false,
isMainCircuit: false,
code: "// Write Some Verilog Code Here!",
subCircuitScopeIds: []
}
this.ox = 0;
this.oy = 0;
this.scale = DPR;
this.stack = [];
this.initialize();
// Setting default layout
this.layout = { // default position
width: 100,
height: 40,
title_x: 50,
title_y: 13,
titleEnabled: true,
};
}
isVisible() {
if(!this.verilogMetadata.isVerilogCircuit)return true;
return this.verilogMetadata.isMainCircuit;
}
initialize() {
this.tunnelList = {};
this.pending = [];
this.nodes = []; // intermediate nodes only
this.allNodes = [];
this.wires = [];
// Creating arrays for other module elements
for (let i = 0; i < moduleList.length; i++) {
this[moduleList[i]] = [];
}
}
/**
* Resets all nodes recursively
*/
reset() {
for (let i = 0; i < this.allNodes.length; i++) { this.allNodes[i].reset(); }
for (let i = 0; i < this.Splitter.length; i++) {
this.Splitter[i].reset();
}
for (let i = 0; i < this.SubCircuit.length; i++) {
this.SubCircuit[i].reset();
}
}
/**
* Adds all inputs to simulationQueue
*/
addInputs() {
for (let i = 0; i < inputList.length; i++) {
for (var j = 0; j < this[inputList[i]].length; j++) {
simulationArea.simulationQueue.add(this[inputList[i]][j], 0);
}
}
for (let i = 0; i < this.SubCircuit.length; i++) { this.SubCircuit[i].addInputs(); }
}
/**
* Ticks clocks recursively -- needs to be deprecated and synchronize all clocks with a global clock
*/
clockTick() {
for (let i = 0; i < this.Clock.length; i++) { this.Clock[i].toggleState(); } // tick clock!
for (let i = 0; i < this.SubCircuit.length; i++) { this.SubCircuit[i].localScope.clockTick(); } // tick clock!
}
/**
* Checks if this circuit contains directly or indirectly scope with id
* Recursive nature
*/
checkDependency(id) {
if (id === this.id) return true;
for (let i = 0; i < this.SubCircuit.length; i++) { if (this.SubCircuit[i].id === id) return true; }
for (let i = 0; i < this.SubCircuit.length; i++) { if (scopeList[this.SubCircuit[i].id].checkDependency(id)) return true; }
return false;
}
/**
* Get dependency list - list of all circuits, this circuit depends on
*/
getDependencies() {
var list = [];
for (let i = 0; i < this.SubCircuit.length; i++) {
list.push(this.SubCircuit[i].id);
list.extend(scopeList[this.SubCircuit[i].id].getDependencies());
}
return uniq(list);
}
/**
* helper function to reduce layout size
*/
fixLayout() {
var maxY = 20;
for (let i = 0; i < this.Input.length; i++) { maxY = Math.max(this.Input[i].layoutProperties.y, maxY); }
for (let i = 0; i < this.Output.length; i++) { maxY = Math.max(this.Output[i].layoutProperties.y, maxY); }
if (maxY !== this.layout.height) { this.layout.height = maxY + 10; }
}
/**
* Function which centers the circuit to the correct zoom level
*/
centerFocus(zoomIn = true) {
if (layoutModeGet()) return;
findDimensions(this);
var ytoolbarOffset = embed ? 0 : 60 * DPR; // Some part ofcanvas is hidden behind the toolbar
var minX = simulationArea.minWidth || 0;
var minY = simulationArea.minHeight || 0;
var maxX = simulationArea.maxWidth || 0;
var maxY = simulationArea.maxHeight || 0;
var reqWidth = maxX - minX + 75 * DPR;
var reqHeight = maxY - minY + 75 * DPR;
this.scale = Math.min(width / reqWidth, (height - ytoolbarOffset) / reqHeight);
if (!zoomIn) { this.scale = Math.min(this.scale, DPR); }
this.scale = Math.max(this.scale, DPR / 10);
this.ox = (-minX) * this.scale + (width - (maxX - minX) * this.scale) / 2;
this.oy = (-minY) * this.scale + (height - ytoolbarOffset - (maxY - minY) * this.scale) / 2;
}
}

884
simulator/src/circuitElement.js Executable file
View file

@ -0,0 +1,884 @@
/* eslint-disable no-multi-assign */
/* eslint-disable no-bitwise */
import { scheduleUpdate } from './engine';
import simulationArea from './simulationArea';
import {
fixDirection, fillText, correctWidth, rect2, oppositeDirection,
} from './canvasApi';
import { colors } from './themer/themer';
import { layoutModeGet, tempBuffer } from './layoutMode';
import { fillSubcircuitElements } from './ux';
import { generateNodeName } from './verilogHelpers';
/**
* Base class for circuit elements.
* @param {number} x - x coordinate of the element
* @param {number} y - y coordinate of the element
* @param {Scope} scope - The circuit on which circuit element is being drawn
* @param {string} dir - The direction of circuit element
* @param {number} bitWidth - the number of bits per node.
* @category circuitElement
*/
export default class CircuitElement {
constructor(x, y, scope, dir, bitWidth) {
// Data member initializations
this.x = x;
this.y = y;
this.hover = false;
if (this.x === undefined || this.y === undefined) {
this.x = simulationArea.mouseX;
this.y = simulationArea.mouseY;
this.newElement = true;
this.hover = true;
}
this.deleteNodesWhenDeleted = true; // FOR NOW - TO CHECK LATER
this.nodeList = [];
this.clicked = false;
this.oldx = x;
this.oldy = y;
// The following attributes help in setting the touch area bound. They are the distances from the center.
// Note they are all positive distances from center. They will automatically be rotated when direction is changed.
// To stop the rotation when direction is changed, check overrideDirectionRotation attribute.
this.leftDimensionX = 10;
this.rightDimensionX = 10;
this.upDimensionY = 10;
this.downDimensionY = 10;
this.label = '';
this.scope = scope;
this.baseSetup();
this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) || 1;
this.direction = dir;
this.directionFixed = false;
this.labelDirectionFixed = false;
this.labelDirection = oppositeDirection[dir];
this.orientationFixed = true;
this.fixedBitWidth = false;
scheduleUpdate();
this.queueProperties = {
inQueue: false,
time: undefined,
index: undefined,
};
if (this.canShowInSubcircuit) {
this.subcircuitMetadata = {
showInSubcircuit: false, // if canShowInSubcircuit == true, showInSubcircuit determines wheter the user has added the element in the subcircuit
showLabelInSubcircuit: true, // determines whether the label of the element is to be showin the subcircuit
labelDirection: this.labelDirection, // determines the direction of the label of the element in the subcircuit
// coordinates of the element in the subcircuit relative to the subcircuit
x : 0,
y : 0
}
}
}
/**
* Function to flip bits
* @param {number} val - the value of flipped bits
* @returns {number} - The number of flipped bits
*/
flipBits(val) {
return ((~val >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth);
}
/**
* Function to get absolute value of x coordinate of the element
* @param {number} x - value of x coordinate of the element
* @return {number} - absolute value of x
*/
absX() {
return this.x;
}
/**
* Function to get absolute value of y coordinate of the element
* @param {number} y - value of y coordinate of the element
* @return {number} - absolute value of y
*/
absY() {
return this.y;
}
/**
* adds the element to scopeList
*/
baseSetup() {
this.scope[this.objectType].push(this);
}
/**
* Function to copy the circuit element obj to a new circuit element
* @param {CircuitElement} obj - element to be copied from
*/
copyFrom(obj) {
var properties = ['label', 'labelDirection'];
for (let i = 0; i < properties.length; i++) {
if (obj[properties[i]] !== undefined) { this[properties[i]] = obj[properties[i]]; }
}
}
/** Methods to be Implemented for derivedClass
* saveObject(); //To generate JSON-safe data that can be loaded
* customDraw(); //This is to draw the custom design of the circuit(Optional)
* resolve(); // To execute digital logic(Optional)
* override isResolvable(); // custom logic for checking if module is ready
* override newDirection(dir) //To implement custom direction logic(Optional)
* newOrientation(dir) //To implement custom orientation logic(Optional)
*/
// Method definitions
/**
* Function to update the scope when a new element is added.
* @param {Scope} scope - the circuit in which we add element
*/
updateScope(scope) {
this.scope = scope;
for (let i = 0; i < this.nodeList.length; i++) { this.nodeList[i].scope = scope; }
}
/**
* To generate JSON-safe data that can be loaded
* @memberof CircuitElement
* @return {JSON} - the data to be saved
*/
saveObject() {
var data = {
x: this.x,
y: this.y,
objectType: this.objectType,
label: this.label,
direction: this.direction,
labelDirection: this.labelDirection,
propagationDelay: this.propagationDelay,
customData: this.customSave(),
};
if(this.canShowInSubcircuit) data.subcircuitMetadata = this.subcircuitMetadata;
return data;
}
/**
* Always overriden
* @memberof CircuitElement
* @return {JSON} - the data to be saved
*/
// eslint-disable-next-line class-methods-use-this
customSave() {
return {
values: {},
nodes: {},
constructorParamaters: [],
};
}
/**
* check hover over the element
* @return {boolean}
*/
checkHover() {
if (simulationArea.mouseDown) return;
for (let i = 0; i < this.nodeList.length; i++) {
this.nodeList[i].checkHover();
}
if (!simulationArea.mouseDown) {
if (simulationArea.hover === this) {
this.hover = this.isHover();
if (!this.hover) simulationArea.hover = undefined;
} else if (!simulationArea.hover) {
this.hover = this.isHover();
if (this.hover) simulationArea.hover = this;
} else {
this.hover = false;
}
}
}
/**
* This sets the width and height of the element if its rectangular
* and the reference point is at the center of the object.
* width and height define the X and Y distance from the center.
* Effectively HALF the actual width and height.
* NOT OVERRIDABLE
* @param {number} w - width
* @param {number} h - height
*/
setDimensions(width, height) {
this.leftDimensionX = this.rightDimensionX = width;
this.downDimensionY = this.upDimensionY = height;
}
/**
* @memberof CircuitElement
* @param {number} w -width
*/
setWidth(width) {
this.leftDimensionX = this.rightDimensionX = width;
}
/**
* @param {number} h -height
*/
setHeight(height) {
this.downDimensionY = this.upDimensionY = height;
}
/**
* Helper Function to drag element to a new position
*/
startDragging() {
if(!layoutModeGet()){
this.oldx = this.x;
this.oldy = this.y;
}
else{
this.oldx = this.subcircuitMetadata.x;
this.oldy = this.subcircuitMetadata.y;
}
}
/**
* Helper Function to drag element to a new position
* @memberof CircuitElement
*/
drag() {
if(!layoutModeGet()){
this.x = this.oldx + simulationArea.mouseX - simulationArea.mouseDownX;
this.y = this.oldy + simulationArea.mouseY - simulationArea.mouseDownY;
}
else{
this.subcircuitMetadata.x = this.oldx + simulationArea.mouseX - simulationArea.mouseDownX;
this.subcircuitMetadata.y = this.oldy + simulationArea.mouseY - simulationArea.mouseDownY;
}
}
/**
* The update method is used to change the parameters of the object on mouse click and hover.
* Return Value: true if state has changed else false
* NOT OVERRIDABLE
*/
update() {
if (layoutModeGet()) {
return this.layoutUpdate();
}
let update = false;
update |= this.newElement;
if (this.newElement) {
if (this.centerElement) {
this.x = Math.round((simulationArea.mouseX - (this.rightDimensionX - this.leftDimensionX) / 2) / 10) * 10;
this.y = Math.round((simulationArea.mouseY - (this.downDimensionY - this.upDimensionY) / 2) / 10) * 10;
} else {
this.x = simulationArea.mouseX;
this.y = simulationArea.mouseY;
}
if (simulationArea.mouseDown) {
this.newElement = false;
simulationArea.lastSelected = this;
} else return update;
}
for (let i = 0; i < this.nodeList.length; i++) {
update |= this.nodeList[i].update();
}
if (!simulationArea.hover || simulationArea.hover === this) { this.hover = this.isHover(); }
if (!simulationArea.mouseDown) this.hover = false;
if ((this.clicked || !simulationArea.hover) && this.isHover()) {
this.hover = true;
simulationArea.hover = this;
} else if (!simulationArea.mouseDown && this.hover && this.isHover() === false) {
if (this.hover) simulationArea.hover = undefined;
this.hover = false;
}
if (simulationArea.mouseDown && (this.clicked)) {
this.drag();
if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
for (let i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
simulationArea.multipleObjectSelections[i].drag();
}
}
update |= true;
} else if (simulationArea.mouseDown && !simulationArea.selected) {
this.startDragging();
if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
for (let i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
simulationArea.multipleObjectSelections[i].startDragging();
}
}
simulationArea.selected = this.clicked = this.hover;
update |= this.clicked;
} else {
if (this.clicked) simulationArea.selected = false;
this.clicked = false;
this.wasClicked = false;
// If this is SubCircuit, then call releaseClick to recursively release clicks on each subcircuit object
if(this.objectType == "SubCircuit") this.releaseClick();
}
if (simulationArea.mouseDown && !this.wasClicked) {
if (this.clicked) {
this.wasClicked = true;
if (this.click) this.click();
if (simulationArea.shiftDown) {
simulationArea.lastSelected = undefined;
if (simulationArea.multipleObjectSelections.contains(this)) {
simulationArea.multipleObjectSelections.clean(this);
} else {
simulationArea.multipleObjectSelections.push(this);
}
} else {
simulationArea.lastSelected = this;
}
}
}
return update;
}
/**
* Used to update the state of the elements inside the subcircuit in layout mode
* Return Value: true if the state has changed, false otherwise
**/
layoutUpdate() {
var update = false;
update |= this.newElement;
if (this.newElement) {
this.subcircuitMetadata.x = simulationArea.mouseX;
this.subcircuitMetadata.y = simulationArea.mouseY;
if (simulationArea.mouseDown) {
this.newElement = false;
simulationArea.lastSelected = this;
} else return;
}
if (!simulationArea.hover || simulationArea.hover == this)
this.hover = this.isHover();
if ((this.clicked || !simulationArea.hover) && this.isHover()) {
this.hover = true;
simulationArea.hover = this;
} else if (!simulationArea.mouseDown && this.hover && this.isHover() == false) {
if (this.hover) simulationArea.hover = undefined;
this.hover = false;
}
if (simulationArea.mouseDown && (this.clicked)) {
this.drag();
update |= true;
} else if (simulationArea.mouseDown && !simulationArea.selected) {
this.startDragging();
simulationArea.selected = this.clicked = this.hover;
update |= this.clicked;
} else {
if (this.clicked) simulationArea.selected = false;
this.clicked = false;
this.wasClicked = false;
}
if (simulationArea.mouseDown && !this.wasClicked) {
if (this.clicked) {
this.wasClicked = true;
simulationArea.lastSelected = this;
}
}
if (!this.clicked && !this.newElement) {
let x = this.subcircuitMetadata.x;
let y = this.subcircuitMetadata.y;
let yy = tempBuffer.layout.height;
let xx = tempBuffer.layout.width;
let rX = this.layoutProperties.rightDimensionX;
let lX = this.layoutProperties.leftDimensionX;
let uY = this.layoutProperties.upDimensionY;
let dY = this.layoutProperties.downDimensionY;
if (lX <= x && x + rX <= xx && y >= uY && y + dY <= yy)
return;
this.subcircuitMetadata.showInSubcircuit = false;
fillSubcircuitElements();
}
return update;
}
/**
* Helper Function to correct the direction of element
*/
fixDirection() {
this.direction = fixDirection[this.direction] || this.direction;
this.labelDirection = fixDirection[this.labelDirection] || this.labelDirection;
}
/**
* The isHover method is used to check if the mouse is hovering over the object.
* Return Value: true if mouse is hovering over object else false
* NOT OVERRIDABLE
*/
isHover() {
var mX = simulationArea.mouseXf - this.x;
var mY = this.y - simulationArea.mouseYf;
var rX = this.rightDimensionX;
var lX = this.leftDimensionX;
var uY = this.upDimensionY;
var dY = this.downDimensionY;
if (layoutModeGet()) {
var mX = simulationArea.mouseXf - this.subcircuitMetadata.x;
var mY = this.subcircuitMetadata.y - simulationArea.mouseYf;
var rX = this.layoutProperties.rightDimensionX;
var lX = this.layoutProperties.leftDimensionX;
var uY = this.layoutProperties.upDimensionY;
var dY = this.layoutProperties.downDimensionY;
}
if (!this.directionFixed && !this.overrideDirectionRotation) {
if (this.direction === 'LEFT') {
lX = this.rightDimensionX;
rX = this.leftDimensionX;
} else if (this.direction === 'DOWN') {
lX = this.downDimensionY;
rX = this.upDimensionY;
uY = this.leftDimensionX;
dY = this.rightDimensionX;
} else if (this.direction === 'UP') {
lX = this.downDimensionY;
rX = this.upDimensionY;
dY = this.leftDimensionX;
uY = this.rightDimensionX;
}
}
return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY;
}
isSubcircuitHover(xoffset = 0, yoffset = 0) {
var mX = simulationArea.mouseXf - this.subcircuitMetadata.x - xoffset;
var mY = yoffset + this.subcircuitMetadata.y - simulationArea.mouseYf;
var rX = this.layoutProperties.rightDimensionX;
var lX = this.layoutProperties.leftDimensionX;
var uY = this.layoutProperties.upDimensionY;
var dY = this.layoutProperties.downDimensionY;
return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY;
}
/**
* Helper Function to set label of an element.
* @memberof CircuitElement
* @param {string} label - the label for element
*/
setLabel(label) {
this.label = label || '';
}
/**
* Method that draws the outline of the module and calls draw function on module Nodes.
* NOT OVERRIDABLE
*/
draw() {
//
var ctx = simulationArea.context;
this.checkHover();
if (this.x * this.scope.scale + this.scope.ox < -this.rightDimensionX * this.scope.scale - 0 || this.x * this.scope.scale + this.scope.ox > width + this.leftDimensionX * this.scope.scale + 0 || this.y * this.scope.scale + this.scope.oy < -this.downDimensionY * this.scope.scale - 0 || this.y * this.scope.scale + this.scope.oy > height + 0 + this.upDimensionY * this.scope.scale) return;
// Draws rectangle and highlights
if (this.rectangleObject) {
ctx.strokeStyle = colors['stroke'];
ctx.fillStyle = colors['fill'];
ctx.lineWidth = correctWidth(3);
ctx.beginPath();
rect2(ctx, -this.leftDimensionX, -this.upDimensionY, this.leftDimensionX + this.rightDimensionX, this.upDimensionY + this.downDimensionY, this.x, this.y, [this.direction, 'RIGHT'][+this.directionFixed]);
if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected === this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = colors["hover_select"];
ctx.fill();
ctx.stroke();
}
if (this.label !== '') {
var rX = this.rightDimensionX;
var lX = this.leftDimensionX;
var uY = this.upDimensionY;
var dY = this.downDimensionY;
if (!this.directionFixed) {
if (this.direction === 'LEFT') {
lX = this.rightDimensionX;
rX = this.leftDimensionX;
} else if (this.direction === 'DOWN') {
lX = this.downDimensionY;
rX = this.upDimensionY;
uY = this.leftDimensionX;
dY = this.rightDimensionX;
} else if (this.direction === 'UP') {
lX = this.downDimensionY;
rX = this.upDimensionY;
dY = this.leftDimensionX;
uY = this.rightDimensionX;
}
}
if (this.labelDirection === 'LEFT') {
ctx.beginPath();
ctx.textAlign = 'right';
ctx.fillStyle = colors['text'];
fillText(ctx, this.label, this.x - lX - 10, this.y + 5, 14);
ctx.fill();
} else if (this.labelDirection === 'RIGHT') {
ctx.beginPath();
ctx.textAlign = 'left';
ctx.fillStyle = colors['text'];
fillText(ctx, this.label, this.x + rX + 10, this.y + 5, 14);
ctx.fill();
} else if (this.labelDirection === 'UP') {
ctx.beginPath();
ctx.textAlign = 'center';
ctx.fillStyle = colors['text'];
fillText(ctx, this.label, this.x, this.y + 5 - uY - 10, 14);
ctx.fill();
} else if (this.labelDirection === 'DOWN') {
ctx.beginPath();
ctx.textAlign = 'center';
ctx.fillStyle = colors['text'];
fillText(ctx, this.label, this.x, this.y + 5 + dY + 10, 14);
ctx.fill();
}
}
// calls the custom circuit design
if (this.customDraw) { this.customDraw(); }
// draws nodes - Moved to renderCanvas
// for (let i = 0; i < this.nodeList.length; i++)
// this.nodeList[i].draw();
}
/**
Draws element in layout mode (inside the subcircuit)
@param {number} xOffset - x position of the subcircuit
@param {number} yOffset - y position of the subcircuit
Called by subcirucit.js/customDraw() - for drawing as a part of another circuit
and layoutMode.js/renderLayout() - for drawing in layoutMode
**/
drawLayoutMode(xOffset = 0, yOffset = 0){
var ctx = simulationArea.context;
if(layoutModeGet()) {
this.checkHover();
}
if (this.subcircuitMetadata.x * this.scope.scale + this.scope.ox < -this.layoutProperties.rightDimensionX * this.scope.scale || this.subcircuitMetadata.x * this.scope.scale + this.scope.ox > width + this.layoutProperties.leftDimensionX * this.scope.scale || this.subcircuitMetadata.y * this.scope.scale + this.scope.oy < -this.layoutProperties.downDimensionY * this.scope.scale || this.subcircuitMetadata.y * this.scope.scale + this.scope.oy > height + this.layoutProperties.upDimensionY * this.scope.scale) return;
if (this.subcircuitMetadata.showLabelInSubcircuit) {
var rX = this.layoutProperties.rightDimensionX;
var lX = this.layoutProperties.leftDimensionX;
var uY = this.layoutProperties.upDimensionY;
var dY = this.layoutProperties.downDimensionY;
// this.subcircuitMetadata.labelDirection
if (this.subcircuitMetadata.labelDirection == "LEFT") {
ctx.beginPath();
ctx.textAlign = "right";
ctx.fillStyle = "black";
fillText(ctx, this.label, this.subcircuitMetadata.x + xOffset - lX - 10, this.subcircuitMetadata.y + yOffset + 5, 10);
ctx.fill();
} else if (this.subcircuitMetadata.labelDirection == "RIGHT") {
ctx.beginPath();
ctx.textAlign = "left";
ctx.fillStyle = "black";
fillText(ctx, this.label, this.subcircuitMetadata.x + xOffset + rX + 10, this.subcircuitMetadata.y + yOffset + 5, 10);
ctx.fill();
} else if (this.subcircuitMetadata.labelDirection == "UP") {
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
fillText(ctx, this.label, this.subcircuitMetadata.x + xOffset, this.subcircuitMetadata.y + yOffset + 5 - uY - 10, 10);
ctx.fill();
} else if (this.subcircuitMetadata.labelDirection == "DOWN") {
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
fillText(ctx, this.label, this.subcircuitMetadata.x + xOffset, this.subcircuitMetadata.y + yOffset + 5 + dY + 10, 10);
ctx.fill();
}
}
// calls the subcircuitDraw function in the element to draw it to canvas
this.subcircuitDraw(xOffset, yOffset);
}
// method to delete object
// OVERRIDE WITH CAUTION
delete() {
simulationArea.lastSelected = undefined;
this.scope[this.objectType].clean(this); // CHECK IF THIS IS VALID
if (this.deleteNodesWhenDeleted) { this.deleteNodes(); } else {
for (let i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].connections.length) { this.nodeList[i].converToIntermediate(); } else { this.nodeList[i].delete(); }
}
}
this.deleted = true;
}
/**
* method to delete object
* OVERRIDE WITH CAUTION
* @memberof CircuitElement
*/
cleanDelete() {
this.deleteNodesWhenDeleted = true;
this.delete();
}
/**
* Helper Function to delete the element and all the node attached to it.
*/
deleteNodes() {
for (let i = 0; i < this.nodeList.length; i++) { this.nodeList[i].delete(); }
}
/**
* method to change direction
* OVERRIDE WITH CAUTION
* @param {string} dir - new direction
*/
newDirection(dir) {
if (this.direction === dir) return;
// Leave this for now
if (this.directionFixed && this.orientationFixed) return;
if (this.directionFixed) {
this.newOrientation(dir);
return; // Should it return ?
}
// if (obj.direction === undefined) return;
this.direction = dir;
for (let i = 0; i < this.nodeList.length; i++) {
this.nodeList[i].refresh();
}
}
/**
* Helper Function to change label direction of the element.
* @memberof CircuitElement
* @param {string} dir - new direction
*/
newLabelDirection(dir) {
if(layoutModeGet()) this.subcircuitMetadata.labelDirection = dir;
else this.labelDirection = dir;
}
/**
* Method to check if object can be resolved
* OVERRIDE if necessary
* @return {boolean}
*/
isResolvable() {
if (this.alwaysResolve) return true;
for (let i = 0; i < this.nodeList.length; i++) { if (this.nodeList[i].type === 0 && this.nodeList[i].value === undefined) return false; }
return true;
}
/**
* Method to change object Bitwidth
* OVERRIDE if necessary
* @param {number} bitWidth - new bitwidth
*/
newBitWidth(bitWidth) {
if (this.fixedBitWidth) return;
if (this.bitWidth === undefined) return;
if (this.bitWidth < 1) return;
this.bitWidth = bitWidth;
for (let i = 0; i < this.nodeList.length; i++) { this.nodeList[i].bitWidth = bitWidth; }
}
/**
* Method to change object delay
* OVERRIDE if necessary
* @param {number} delay - new delay
*/
changePropagationDelay(delay) {
if (this.propagationDelayFixed) return;
if (delay === undefined) return;
if (delay === '') return;
var tmpDelay = parseInt(delay, 10);
if (tmpDelay < 0) return;
this.propagationDelay = tmpDelay;
}
/**
* Dummy resolve function
* OVERRIDE if necessary
*/
resolve() {
}
/**
* Helper Function to process verilog
*/
processVerilog(){
// Output count used to sanitize output
var output_total = 0;
for (var i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].type == NODE_OUTPUT && this.nodeList[i].connections.length > 0)
output_total++;
}
var output_count = 0;
for (var i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].type == NODE_OUTPUT) {
if (this.objectType != "Input" && this.objectType != "Clock" && this.nodeList[i].connections.length > 0) {
this.nodeList[i].verilogLabel =
generateNodeName(this.nodeList[i], output_count, output_total);
if (!this.scope.verilogWireList[this.nodeList[i].bitWidth].contains(this.nodeList[i].verilogLabel))
this.scope.verilogWireList[this.nodeList[i].bitWidth].push(this.nodeList[i].verilogLabel);
output_count++;
}
this.scope.stack.push(this.nodeList[i]);
}
}
}
/**
* Helper Function to check if verilog resolvable
* @return {boolean}
*/
isVerilogResolvable() {
var backupValues = [];
for (let i = 0; i < this.nodeList.length; i++) {
backupValues.push(this.nodeList[i].value);
this.nodeList[i].value = undefined;
}
for (let i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].verilogLabel) {
this.nodeList[i].value = 1;
}
}
var res = this.isResolvable();
for (let i = 0; i < this.nodeList.length; i++) {
this.nodeList[i].value = backupValues[i];
}
return res;
}
/**
* Helper Function to remove proporgation.
*/
removePropagation() {
for (let i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].type === NODE_OUTPUT) {
if (this.nodeList[i].value !== undefined) {
this.nodeList[i].value = undefined;
simulationArea.simulationQueue.add(this.nodeList[i]);
}
}
}
}
/**
* Helper Function to name the verilog.
* @return {string}
*/
verilogName() {
return this.verilogType || this.objectType;
}
verilogBaseType() {
return this.verilogName();
}
verilogParametrizedType() {
var type = this.verilogBaseType();
// Suffix bitwidth for multi-bit inputs
// Example: DflipFlop #(2) DflipFlop_0
if (this.bitWidth != undefined && this.bitWidth > 1)
type += " #(" + this.bitWidth + ")";
return type
}
/**
* Helper Function to generate verilog
* @return {JSON}
*/
generateVerilog() {
// Example: and and_1(_out, _out, _Q[0]);
var inputs = [];
var outputs = [];
for (var i = 0; i < this.nodeList.length; i++) {
if (this.nodeList[i].type == NODE_INPUT) {
inputs.push(this.nodeList[i]);
} else {
if (this.nodeList[i].connections.length > 0)
outputs.push(this.nodeList[i]);
else
outputs.push(""); // Don't create a wire
}
}
var list = outputs.concat(inputs);
var res = this.verilogParametrizedType();
var moduleParams = list.map(x => x.verilogLabel).join(", ");
res += ` ${this.verilogLabel}(${moduleParams});`;
return res;
}
/**
* Toggles the visibility of the labels of subcircuit elements. Called by event handlers in ux.js
**/
toggleLabelInLayoutMode(){
this.subcircuitMetadata.showLabelInSubcircuit = !this.subcircuitMetadata.showLabelInSubcircuit;
}
}
CircuitElement.prototype.alwaysResolve = false;
CircuitElement.prototype.propagationDelay = 10;
CircuitElement.prototype.tooltip = undefined;
CircuitElement.prototype.propagationDelayFixed = false;
CircuitElement.prototype.rectangleObject = true;
CircuitElement.prototype.objectType = 'CircuitElement';
CircuitElement.prototype.canShowInSubcircuit = false; // determines whether the element is supported to be shown inside a subcircuit
CircuitElement.prototype.subcircuitMetadata = {}; // stores the coordinates and stuff for the elements in the subcircuit
CircuitElement.prototype.layoutProperties = {
rightDimensionX : 5,
leftDimensionX : 5,
upDimensionY : 5,
downDimensionY: 5
};
CircuitElement.prototype.subcircuitMutableProperties = {
"label": {
name: "label: ",
type: "text",
func: "setLabel"
},
"show label": {
name: "show label ",
type: "checkbox",
func: "toggleLabelInLayoutMode"
}
};

View file

@ -0,0 +1,450 @@
/* eslint-disable import/no-cycle */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
import Node from './node';
import { scheduleBackup } from './data/backupCircuit';
import BooleanMinimize from './quinMcCluskey';
import Input from './modules/Input';
import ConstantVal from './modules/ConstantVal';
import Output from './modules/Output';
import AndGate from './modules/AndGate';
import OrGate from './modules/OrGate';
import NotGate from './modules/NotGate';
import { stripTags } from './utils';
var inputSample = 5;
var dataSample = [['01---', '11110', '01---', '00000'], ['01110', '1-1-1', '----0'], ['01---', '11110', '01110', '1-1-1', '0---0'], ['----1']];
var sampleInputListNames = ['A', 'B'];
var sampleOutputListNames = ['X'];
/**
* The prompt for combinational analysis
* @param {Scope=} - the circuit in which we want combinational analysis
* @category combinationalAnalysis
*/
export function createCombinationalAnalysisPrompt(scope = globalScope) {
scheduleBackup();
$('#combinationalAnalysis').empty();
$('#combinationalAnalysis').append("<p>Enter Input names separated by commas: <input id='inputNameList' type='text' placeHolder='eg. In A, In B'></p>");
$('#combinationalAnalysis').append("<p>Enter Output names separated by commas: <input id='outputNameList' type='text' placeHolder='eg. Out X, Out Y'></p>");
$('#combinationalAnalysis').append("<p style='text-align:center;'>OR</p>");
$('#combinationalAnalysis').append("<p>Enter Boolean Function: <input class='truth_table_input' autofocus id='booleanExpression' placeholder='Example: (AB)' type='text'></p>");
$('#combinationalAnalysis').append("<label class='cb-checkbox'>I need a decimal column.<input id='decimalColumnBox' type='checkbox'></label>");
$('#combinationalAnalysis').dialog({
resizable:false,
width: 'auto',
buttons: [
{
style: 'padding: 5px',
text: 'Next',
click() {
var inputList = stripTags($("#inputNameList").val()).split(',');
var outputList = stripTags($("#outputNameList").val()).split(',');
var booleanExpression = $('#booleanExpression').val();
inputList = inputList.map((x) => x.trim());
inputList = inputList.filter((e) => e);
outputList = outputList.map((x) => x.trim());
outputList = outputList.filter((e) => e);
booleanExpression = booleanExpression.replace(/ /g, '');
booleanExpression = booleanExpression.toUpperCase();
var booleanInputVariables = [];
for (var i = 0; i < booleanExpression.length; i++) {
if ((booleanExpression[i] >= 'A' && booleanExpression[i] <= 'Z')) {
if (booleanExpression.indexOf(booleanExpression[i]) == i) {
booleanInputVariables.push(booleanExpression[i]);
}
}
}
booleanInputVariables.sort();
if (inputList.length > 0 && outputList.length > 0 && booleanInputVariables.length == 0) {
$(this).dialog('close');
createBooleanPrompt(inputList, outputList, null, scope);
}
else if (booleanInputVariables.length > 0 && inputList.length == 0 && outputList.length == 0) {
$(this).dialog('close');
var output = solveBooleanFunction(booleanInputVariables, booleanExpression);
if(output != null) {
createBooleanPrompt(booleanInputVariables, booleanExpression, output, scope);
}
}
else if ((inputList.length == 0 || outputList.length == 0) && booleanInputVariables == 0) {
alert('Enter Input / Output Variable(s) OR Boolean Function!');
}
else {
alert('Use Either Combinational Analysis Or Boolean Function To Generate Circuit!');
}
},
},
],
});
$("#combinationalAnalysis").checkBo();
}
/**
* This funciton hashes the output array and makes required JSON using
* a BooleanMinimize class defined in Quin_Mcluskey.js var s which will
* be output table is also initialied here
* @param {Array} inputListNames - labels of input nodes
* @param {Array} outputListNames - labels of output nodes
* @param {Scope=} scope - h circuit
* @category combinationalAnalysis
*/
function createBooleanPrompt(inputListNames, outputListNames, output, scope = globalScope) {
var inputListNames = inputListNames || (prompt('Enter inputs separated by commas').split(','));
var outputListNames = outputListNames || (prompt('Enter outputs separated by commas').split(','));
var outputListNamesInteger = [];
if(output == null) {
for (var i = 0; i < outputListNames.length; i++) { outputListNamesInteger[i] = 7 * i + 13; }// assigning an integer to the value, 7*i + 13 is random
} else {
outputListNamesInteger = [13];
}
var s = '<table class="content-table">';
s += '<tbody style="display:block; max-height:70vh; overflow-y:scroll" >';
s += '<tr>';
if ($('#decimalColumnBox').is(':checked')) { s += '<th>' + 'dec' + '</th>'; }
for (var i = 0; i < inputListNames.length; i++) { s += `<th>${inputListNames[i]}</th>`; }
if (output == null) { for (var i = 0; i < outputListNames.length; i++) { s += `<th>${outputListNames[i]}</th>`; } }
else { s += `<th>${outputListNames}</th>`; }
s += '</tr>';
var matrix = [];
for (var i = 0; i < inputListNames.length; i++) {
matrix[i] = new Array((1 << inputListNames.length));
}
for (var i = 0; i < inputListNames.length; i++) {
for (var j = 0; j < (1 << inputListNames.length); j++) {
matrix[i][j] = (+((j & (1 << (inputListNames.length - i - 1))) != 0));
}
}
for (var j = 0; j < (1 << inputListNames.length); j++) {
s += '<tr>';
if ($('#decimalColumnBox').is(':checked')) { s += `<td>${j}</td>`; }
for (var i = 0; i < inputListNames.length; i++) {
s += `<td>${matrix[i][j]}</td>`;
}
for (var i = 0; i < outputListNamesInteger.length; i++) {
if (output == null) {
s += `<td class ="output ${outputListNamesInteger[i]}" id="${j}">` + 'x' + '</td>';
// using hash values as they'll be used in the generateBooleanTableData function
}
}
if (output != null) {
s += `<td class="${outputListNamesInteger[0]}" id="${j}">` + `${output[j]}` + '</td>';
}
s += '</tr>';
}
s += '</tbody>';
s += '</table>';
$('#combinationalAnalysis').empty();
$('#combinationalAnalysis').append(s);
$('#combinationalAnalysis').dialog({
resizable: false,
width: 'auto',
buttons: [
{
style: 'padding: 6px',
text: 'Generate Circuit',
click() {
$(this).dialog('close');
var data = generateBooleanTableData(outputListNamesInteger);
// passing the hash values to avoid spaces being passed which is causing a problem
var minimizedCircuit = [];
let inputCount = inputListNames.length;
for (const output in data) {
let oneCount = data[output][1].length; // Number of ones
let zeroCount = data[output][0].length; // Number of zeroes
if(oneCount == 0) {
// Hardcode to 0 as output
minimizedCircuit.push(['-'.repeat(inputCount) + '0']);
}
else if(zeroCount == 0) {
// Hardcode to 1 as output
minimizedCircuit.push(['-'.repeat(inputCount) + '1']);
}
else {
// Perform KMap like minimzation
const temp = new BooleanMinimize(
inputListNames.length,
data[output][1].map(Number),
data[output].x.map(Number),
);
minimizedCircuit.push(temp.result);
}
}
if (output == null) {
drawCombinationalAnalysis(minimizedCircuit, inputListNames, outputListNames, scope);
}
else {
drawCombinationalAnalysis(minimizedCircuit, inputListNames, [`${outputListNames}`], scope);
}
},
},
{
style: 'padding: 6px',
text: 'Print Truth Table',
click() {
var sTable = document.getElementById('combinationalAnalysis').innerHTML;
var style = '<style> table {font: 20px Calibri;} table, th, td {border: solid 1px #DDD;border-collapse: collapse;} padding: 2px 3px;text-align: center;} </style>';
var win = window.open('', '', 'height=700,width=700');
var htmlBody = `
<html><head>\
<title>Boolean Logic Table</title>\
${style}\
</head>\
<body>\
<center>${sTable}</center>\
</body></html>
`;
win.document.write(htmlBody);
win.document.close();
win.print();
},
},
],
});
$('.output').on('click',function () {
var v = $(this).html();
if (v == 0)v = $(this).html(1);
else if (v == 1)v = $(this).html('x');
else if (v == 'x')v = $(this).html(0);
});
}
function generateBooleanTableData(outputListNames) {
var data = {};
for (var i = 0; i < outputListNames.length; i++) {
data[outputListNames[i]] = {
x: [],
1: [],
0: [],
};
var rows = $(`.${outputListNames[i]}`);
for (let j = 0; j < rows.length; j++) {
data[outputListNames[i]][rows[j].innerHTML].push(rows[j].id);
}
}
return data;
}
function drawCombinationalAnalysis(combinationalData, inputList, outputListNames, scope = globalScope) {
var inputCount = inputList.length;
var maxTerms = 0;
for (var i = 0; i < combinationalData.length; i++) { maxTerms = Math.max(maxTerms, combinationalData[i].length); }
var startPosX = 200;
var startPosY = 200;
var currentPosY = 300;
var andPosX = startPosX + inputCount * 40 + 40 + 40;
var orPosX = andPosX + Math.floor(maxTerms / 2) * 10 + 80;
var outputPosX = orPosX + 60;
var inputObjects = [];
var logixNodes = [];
// Appending constant input to the end of inputObjects
for (var i = 0; i <= inputCount; i++) {
if(i < inputCount) {
// Regular Input
inputObjects.push(new Input(startPosX + i * 40, startPosY, scope, 'DOWN', 1));
inputObjects[i].setLabel(inputList[i]);
}
else {
// Constant Input
inputObjects.push(new ConstantVal(startPosX + i * 40, startPosY, scope, 'DOWN', 1, '1'));
inputObjects[i].setLabel('_C_');
}
inputObjects[i].newLabelDirection('UP');
var v1 = new Node(startPosX + i * 40, startPosY + 20, 2, scope.root);
inputObjects[i].output1.connect(v1);
var v2 = new Node(startPosX + i * 40 + 20, startPosY + 20, 2, scope.root);
v1.connect(v2);
var notG = new NotGate(startPosX + i * 40 + 20, startPosY + 40, scope, 'DOWN', 1);
notG.inp1.connect(v2);
logixNodes.push(v1);
logixNodes.push(notG.output1);
}
function countTerm(s) {
var c = 0;
for (var i = 0; i < s.length; i++) { if (s[i] !== '-')c++; }
return c;
}
for (var i = 0; i < combinationalData.length; i++) {
var andGateNodes = [];
for (var j = 0; j < combinationalData[i].length; j++) {
var c = countTerm(combinationalData[i][j]);
if (c > 1) {
var andGate = new AndGate(andPosX, currentPosY, scope, 'RIGHT', c, 1);
andGateNodes.push(andGate.output1);
var misses = 0;
for (var k = 0; k < combinationalData[i][j].length; k++) {
if (combinationalData[i][j][k] == '-') { misses++; continue; }
var index = 2 * k + (combinationalData[i][j][k] == 0);
var v = new Node(logixNodes[index].absX(), andGate.inp[k - misses].absY(), 2, scope.root);
logixNodes[index].connect(v);
logixNodes[index] = v;
v.connect(andGate.inp[k - misses]);
}
} else {
for (var k = 0; k < combinationalData[i][j].length; k++) {
if (combinationalData[i][j][k] == '-') continue;
var index = 2 * k + (combinationalData[i][j][k] == 0);
var andGateSubstituteNode = new Node(andPosX, currentPosY, 2, scope.root);
var v = new Node(logixNodes[index].absX(), andGateSubstituteNode.absY(), 2, scope.root);
logixNodes[index].connect(v);
logixNodes[index] = v;
v.connect(andGateSubstituteNode);
andGateNodes.push(andGateSubstituteNode);
}
}
currentPosY += c * 10 + 30;
}
var andGateCount = andGateNodes.length;
var midWay = Math.floor(andGateCount / 2);
var orGatePosY = (andGateNodes[midWay].absY() + andGateNodes[Math.floor((andGateCount - 1) / 2)].absY()) / 2;
if (orGatePosY % 10 == 5) { orGatePosY += 5; } // To make or gate fall in grid
if (andGateCount > 1) {
var o = new OrGate(orPosX, orGatePosY, scope, 'RIGHT', andGateCount, 1);
if (andGateCount % 2 == 1)andGateNodes[midWay].connect(o.inp[midWay]);
for (var j = 0; j < midWay; j++) {
var v = new Node(andPosX + 30 + (midWay - j) * 10, andGateNodes[j].absY(), 2, scope.root);
v.connect(andGateNodes[j]);
var v2 = new Node(andPosX + 30 + (midWay - j) * 10, o.inp[j].absY(), 2, scope.root);
v2.connect(v);
o.inp[j].connect(v2);
var v = new Node(andPosX + 30 + (midWay - j) * 10, andGateNodes[andGateCount - j - 1].absY(), 2, scope.root);
v.connect(andGateNodes[andGateCount - j - 1]);
var v2 = new Node(andPosX + 30 + (midWay - j) * 10, o.inp[andGateCount - j - 1].absY(), 2, scope.root);
v2.connect(v);
o.inp[andGateCount - j - 1].connect(v2);
}
var out = new Output(outputPosX, o.y, scope, 'LEFT', 1);
out.inp1.connect(o.output1);
} else {
var out = new Output(outputPosX, andGateNodes[0].absY(), scope, 'LEFT', 1);
out.inp1.connect(andGateNodes[0]);
}
out.setLabel(outputListNames[i]);
out.newLabelDirection('RIGHT');
}
for (var i = 0; i < logixNodes.length; i++) {
if (logixNodes[i].absY() != currentPosY) {
var v = new Node(logixNodes[i].absX(), currentPosY, 2, scope.root);
logixNodes[i].connect(v);
}
}
globalScope.centerFocus();
}
/**
* This function solves passed boolean expression and returns
* output array which contains solution of the truth table
* of given boolean expression
* @param {Array} inputListNames - labels for input nodes
* @param {String} booleanExpression - boolean expression which is to be solved
*/
function solveBooleanFunction(inputListNames, booleanExpression) {
let i;
let j;
let output = [];
if (booleanExpression.match(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01+'() ]/g) != null) {
alert('One of the characters is not allowed.');
return;
}
if (inputListNames.length > 8) {
alert('You can only have 8 variables at a time.');
return;
}
var s = '<table class="content-table">';
s += '<tbody style="display:block; max-height:70vh; overflow-y:scroll" >';
s += '<tr>';
if ($('#decimalColumnBox').is(':checked')) { s += '<th>' + 'dec' + '</th>'; }
for (i = 0; i < inputListNames.length; i++) { s += `<th>${inputListNames[i]}</th>`; }
s += `<th>${booleanExpression}</th>`;
s += '</tr>';
var matrix = [];
for (i = 0; i < inputListNames.length; i++) {
matrix[i] = new Array((inputListNames.length));
}
for (i = 0; i < inputListNames.length; i++) {
for (j = 0; j < (1 << inputListNames.length); j++) {
matrix[i][j] = (+((j & (1 << (inputListNames.length - i - 1))) != 0));
}
}
// generate equivalent expression by replacing input vars with possible combinations of o and 1
for (i = 0; i < (2 ** inputListNames.length); i++) {
const data = [];
for (j = 0; j < inputListNames.length; j++) {
data[j] = Math.floor(i / Math.pow(2, inputListNames.length - j - 1)) % 2;
}
let equation = booleanExpression;
for (j = 0; j < inputListNames.length; j++) {
equation = equation.replace(new RegExp(inputListNames[j], 'g'), data[j]);
}
output[i] = solve(equation);
}
for (j = 0; j < (1 << inputListNames.length); j++) {
s += '<tr>';
if ($('#decimalColumnBox').is(':checked')) { s += `<td>${j}</td>`; }
for (i = 0; i < inputListNames.length; i++) {
s += `<td>${matrix[i][j]}</td>`;
}
s += `<td class="13" id="${j}">` + `${output[j]}` + '</td>';
s += '</tr>';
}
s += '</tbody>';
s += '</table>';
// generates solution for the truth table of booleanexpression
function solve(equation) {
while (equation.indexOf("(") != -1) {
const start = equation.lastIndexOf("(");
const end = equation.indexOf(")", start);
if (start != -1) {
equation = equation.substring(0, start)
+ solve(equation.substring(start + 1, end))
+ equation.substring(end + 1);
}
}
equation = equation.replace(/''/g, '');
equation = equation.replace(/0'/g, '1');
equation = equation.replace(/1'/g, '0');
for (let i = 0; i < equation.length - 1; i++) {
if ((equation[i] == '0' || equation[i] == '1') && (equation[i + 1] == '0' || equation[i + 1] == '1')) {
equation = equation.substring(0, i + 1) + '*' + equation.substring(i + 1, equation.length);
}
}
try {
const safeEval = eval;
const answer = safeEval(equation);
if (answer == 0) {
return 0;
}
if (answer > 0) {
return 1;
}
return '';
} catch (e) {
return '';
}
}
return output;
}

View file

@ -0,0 +1,22 @@
//
// Color configuration
//
$primary: $charcoal; //base color theme
$input: $silver; //input border (mainly bottom)
$text-primary: $white; //global font color
$text-secondary: $silver; //placeholder text
$sim-primary: $white; //simulator bg
$sim-secondary: $very-light-grey; //simulator gridlines
$dialog-primary: $primary; //dialog boxes bg color
$dialog-secondary: $grey; //dialog boxes box border color
$button-primary: $medium-sea-green; //btn primary
$button-secondary: $roman; //delete button
$error-primary: $carousel-pink; //error text color
$box-shadow: $grey-shadow; //box shadow
$border-primary: $white; //border color
$border-secondary: $suva-grey; //dropdown border color
$border-tertiary: $silver; //input box border
$bg-primary--nav: $suva-grey; //nav-bar bg color
$bg-secondary--nav: $primary; //active nav-bar menu bg color
$bg-tertiary--nav: $gainsboro; //inactive nav-bar menu bg color

View file

View file

@ -0,0 +1,18 @@
//
// Color Variables
//
$charcoal: #454545;
$silver: #bbbbbb;
$gainsboro: #dddddd;
$white: #ffffff;
$grey : #7d7d7d;
$very-light-grey: #cacaca;
$suva-grey: #8b8b8b;
$medium-sea-green:#42b983;
$roman: #dc5656;
$grey-shadow: #4545457f;
$fire-brick: #ac3522;
$carousel-pink: #f8d7da;

View file

View file

View file

View file

View file

View file

@ -0,0 +1,12 @@
//
// base/default rule set here
// no class or ID selectors
//
html {
box-sizing: border-box;
}
* *:before *:after {
box-sizing: inherit;
}

View file

@ -0,0 +1,51 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
/* File modified - uncomment below */
/*
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
*/
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View file

@ -0,0 +1,323 @@
//old ui ruleset starts here
.deleteOfflineProject {
float: right;
cursor: pointer;
padding: 2px;
}
.pointerCursor {
cursor: pointer;
}
.defaultCursor {
cursor: default;
}
#container {
display: table;
width: 100%;
height: 100%;
}
#container > div {
display: table-row;
height: 0;
}
#container > div.fill {
height: auto;
}
/* END OF MODULES */
#restrictedDiv {
position: absolute;
top: 10px;
margin-left: 10px;
width: 560px;
z-index: 100;
}
#restrictedElementsDiv {
position: absolute;
top: 90px;
right: 10px;
z-index: 100;
width: 200px;
}
#MessageDiv {
position: absolute;
left: 30px;
bottom: 30px;
z-index: 110;
}
.errorMessage {
height: auto;
width: 100%;
padding: 2px;
margin: 2px;
border: 1px solid red;
border-radius: 3px;
background-color: #fee;
font-size: 15px;
}
.normalMessage {
height: auto;
width: 100%; padding: 2px;
margin: 2px;
border: 1px solid green;
border-radius: 3px;
background-color: #99ff33;
font-size: 15px;
}
#canvasArea {
display: block;
position: relative;
width: 100%;
background-color: red;
}
.simulation {
position: relative;
width: auto;
height: 100%;
overflow: hidden;
background-color: "white";
}
.left {
float: left;
}
.right {
float: right;
}
.objectPropertyAttributeChecked.btn {
width: 100%;
margin-bottom: 5px;
}
/* For loading screen - pace.js */
.pace {
-webkit-pointer-events: none;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
position: fixed;
width: 100vw;
height: 100vh;
background-color: #fff;
z-index: 100000;
}
.pace-inactive {
display: none;
}
#Help {
visibility: hidden;
/* Hidden by default. Visible on click */
min-width: 250px;
/* Set a default minimum width */
margin-left: -125px;
/* Divide value of min-width by 2 */
background-color: #333;
/* Black background color */
color: #fff;
/* White text color */
text-align: center;
/* Centered text */
border-radius: 2px;
/* Rounded borders */
padding: 16px;
/* Padding */
position: fixed;
/* Sit on top of the screen */
z-index: 1;
/* Add a z-index if needed */
right: 50px;
/* Center the snackbar */
bottom: 50px;
/* 30px from the bottom */
opacity: 0;
}
#Help.show {
visibility: visible;
/* Show the snackbar */
opacity: 1;
-webkit-transition-delay: 0.5s;
/* Safari */
transition-delay: 0.5s;
-webkit-transition-duration: 0.3s;
/* Safari */
transition-duration: 0.3s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-webkit-keyframes fadeout {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes fadeout {
from {
bottom: 0px;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}
.pace .pace-progress {
background: #29d;
position: fixed;
z-index: 2000;
top: 0;
right: 100%;
width: 50%;
height: 5px;
}
/* LOADING ICON CSS STARTS*/
.sk-folding-cube {
margin: 20px auto;
width: 40px;
height: 40px;
position: relative;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg);
}
.sk-folding-cube .sk-cube {
float: left;
width: 50%;
height: 50%;
position: relative;
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1);
}
.sk-folding-cube .sk-cube:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #09f;
-webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
animation: sk-foldCubeAngle 2.4s infinite linear both;
-webkit-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
transform-origin: 100% 100%;
}
.sk-folding-cube .sk-cube2 {
-webkit-transform: scale(1.1) rotateZ(90deg);
transform: scale(1.1) rotateZ(90deg);
}
.sk-folding-cube .sk-cube3 {
-webkit-transform: scale(1.1) rotateZ(180deg);
transform: scale(1.1) rotateZ(180deg);
}
.sk-folding-cube .sk-cube4 {
-webkit-transform: scale(1.1) rotateZ(270deg);
transform: scale(1.1) rotateZ(270deg);
}
.sk-folding-cube .sk-cube2:before {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
}
.sk-folding-cube .sk-cube3:before {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}
.sk-folding-cube .sk-cube4:before {
-webkit-animation-delay: 0.9s;
animation-delay: 0.9s;
}
@-webkit-keyframes sk-foldCubeAngle {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
@keyframes sk-foldCubeAngle {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
/* LOADING ICON CSS ENDS*/

969
simulator/src/css/UX.css Normal file
View file

@ -0,0 +1,969 @@
.deleteOfflineProject {
float: right;
cursor: pointer;
padding: 2px;
}
#contextMenu {
width: 150px;
visibility: hidden;
box-shadow: 0px 2px 7px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(0, 0, 0, 0.2);
position: fixed;
z-index: 100;
background: #fff;
opacity: 0;
top: 100;
left: 100;
cursor: pointer;
color: #000;
padding-bottom: 4px;
padding-top: 4px;
transition: opacity 0.2s ease-in-out;
user-select: none;
}
#contextMenu ul {
margin: 0;
padding: 0;
font: 16px sans-serif;
}
#contextMenu ul li {
list-style: none;
padding: 8px;
padding-left: 20px;
}
#contextMenu ul li a {
text-decoration: none;
color: #000 !important;
}
#contextMenu ul li:hover {
background: rgba(0, 0, 0, 0.1);
}
button:focus {
outline: 0;
}
.side {
height: 100%;
background-color: #333;
padding: 3px;
color: #fff;
border-side: 1px solid #0099ff;
border-bottom: 40px solid #0099ff;
padding: 0.5em;
}
.option {
display: block;
background-color: black;
border: 1px solid #005cb3;
color: #0099ff;
padding: 5px;
width: 200px;
margin: 3px;
word-wrap:break-word;
overflow-x:hidden;
}
.pannel-heading {
background-color: #f5f5f5;
}
#layoutDialog {
position: absolute;
right: 100px;
top: 100px;
z-index: 101;
width: 200px;
height: 230px;
border: 1px solid grey;
border-radius: 2px;
background-color: white;
overflow-x: hidden;
}
.projectName {
/*margin:3px;*/
color: #0099ff;
margin: 0 auto;
text-align: center;
font-size: 1.4em;
position: static;
left: 50%;
display: block;
width: 500px;
text-align: center;
margin-left: -250px;
}
.inline {
width: auto;
padding-right: 20px;
display: inline-block;
}
.option:hover {
border-color: #0099ff;
}
input[type="radio"]:checked ~ label {
color: #0dff92;
}
.option input[type="radio"] {
margin-right: 5px;
/*position: absolute;*/
visibility: hidden;
}
.option input[type="radio"]:checked {
/*position: absolute;*/
visibility: visible;
}
.zoomButton:focus {
outline: 0;
}
.zoomButton {
padding: 5px;
opacity: 0.3;
}
.zoomButton:hover {
/*height:20px;
width:20px;*/
opacity: 0.8;
transition: opacity 0.2s;
}
.ui-accordion-header-icon.ui-icon {
/*background-image: url("./ui-icons_white_256x240.png");*/
}
.noSelect {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
-o-user-select: none;
}
.pointerCursor {
cursor: pointer;
}
.defaultCursor {
cursor: default;
}
#container {
display: table;
width: 100%;
height: 100%;
}
#container > div {
display: table-row;
height: 0;
}
#container > div.fill {
height: auto;
}
#modules-header {
margin-bottom: 0.5em;
font-size: 1.3em;
text-transform: uppercase;
font-family: Arial, Helvetica, sans-serif;
color: #0099ff;
text-align: center;
padding-top: 0.3em;
}
.panel {
padding: 0em;
background-color: #333;
margin: 0;
border-radius: 0;
margin-bottom: 0em;
border: 1px solid #0099ff;
}
.ui-accordion-header {
background-color: #333;
color: #fff;
border: 1px solid #0099ff;
border-radius: 0;
margin: 0em;
padding: 0em;
outline: none;
}
.ui-accordion-header.ui-accordion-header-active.ui-state-active {
background-color: #0099ff;
outline: none;
margin-bottom: 0;
}
.ui-accordion-header.ui-state-hover {
background-color: #0066cc;
outline: none;
/*margin-bottom: 0;*/
}
/* MODULES */
.moduleProperty {
display: none;
background-color: #333;
color: #fff;
/*padding-bottom: 2em;*/
margin-top: 1em;
}
#moduleProperty-inner {
border: 1px solid #0099ff;
padding: 1em;
/*margin-bottom: 1em;*/
}
#moduleProperty-toolTip {
padding: 10px;
/*font-size: 1.1em;*/
color: #0099ff;
}
#moduleProperty-title {
text-transform: uppercase;
font-size: 1.3em;
color: #0099ff;
margin-bottom: 0.55em;
text-align: center;
}
#moduleProperty-header {
font-size: 1.1em;
text-transform: uppercase;
margin-bottom: 0.5em;
}
#moduleProperty-inner > p {
margin: 0;
margin-top: 0.2em;
}
input,
select {
padding: 0.25rem;
}
.moduleProperty input,
.moduleProperty select {
background-color: #333;
border: none;
border-bottom: 2px solid #ccc;
}
.moduleProperty input:active,
.moduleProperty input:focus,
.moduleProperty select:active,
.moduleProperty select:focus {
border-bottom: 2px solid #0099ff;
}
.navbar.navbar-default {
margin: 0px;
border-radius: 0px;
border: 0;
padding: 0px;
min-height: 0px;
border-bottom: 1px solid #0099ff;
}
.navbar-brand {
padding: 7px 15px;
height: auto;
}
/* END OF MODULES */
#tabsBar {
width: 100%;
/*height: 3em;*/
margin-left: 20px;
background-color: #000;
}
#tabsBar div {
display: inline-block;
/*padding-left: 0.5em;*/
margin: 0.1em;
color: #fff;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
position: relative;
}
#tabsBar .circuits {
color: #fff;
text-align: center;
background-color: #00284d;
padding-left: 0.5em;
padding-right: 1.5em;
border: 1px solid #005cb3;
border-radius: 0.1em;
}
#tabsBar .circuits:hover {
background-color: #00ace6;
/*border: 1px solid #0099ff;*/
transition-duration: 100ms;
}
#tabsBar .current {
/*background-color: #0086b3;*/
background-color: #004280;
border: 1px solid #0099ff;
}
.tabsCloseButton:hover{
color:#fff;
font-family: 'Gill Sans', sans-serif;
margin-left: 1em;
opacity: 0.5;
}
.tabsCloseButton {
color:#111;
font-family: 'Gill Sans', sans-serif;
margin-left: 1em;
opacity: 1.0;
position: absolute;
top: 5px;
right: 3;
}
th,
td {
padding-left: 15px;
padding-right: 15px;
text-align: left;
border: 1px solid #0099ff;
color: white;
}
#booleanTable {
width: 200px;
}
table {
border-collapse: collapse;
-webkit-user-select: none;
/* Chrome/Safari */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* IE10+ */
/* Rules below not implemented in browsers yet */
-o-user-select: none;
user-select: none;
}
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#restrictedDiv {
position: absolute;
top: 10px;
margin-left: 10px;
width: 560px;
z-index: 100;
}
#restrictedElementsDiv {
position: absolute;
top: 90px;
right: 10px;
z-index: 100;
width: 200px;
}
#MessageDiv {
position: absolute;
margin-left: 30px;
bottom: 100px;
/*height:auto;*/
/*width:60%;*/
/*padding: 2px;*/
/*border: 3px solid red;*/
/*border-radius: 6px;*/
/*background-color: #fcc;*/
z-index: 10;
}
.errorMessage {
/*position: absolute;*/
/*margin-left: 30%;*/
/*bottom: 1px;*/
height: auto;
width: 100%;
/*margin-bottom: 10px;*/
padding: 2px;
margin: 2px;
border: 1px solid red;
border-radius: 3px;
background-color: #fee;
font-size: 15px;
/*z-index: 10;*/
}
.normalMessage {
/*position: absolute;*/
/*margin-left: 30%;*/
/*bottom: 150px;*/
height: auto;
width: 100%;
/*margin-bottom: 10px;*/
padding: 2px;
margin: 2px;
border: 1px solid green;
border-radius: 3px;
background-color: #99ff33;
font-size: 15px;
/*z-index: 10;*/
}
#canvasArea {
display: block;
position: relative;
/*height: 100%;*/
width: 100%;
background-color: red;
}
.simulation {
position: relative;
width: auto;
height: 100%;
overflow: hidden;
background-color: "white";
}
.switch {
position: relative;
display: inline-block;
width: 43px;
height: 17px;
margin-bottom: 0px;
}
.switch input {
display: none;
}
.slider {
position: absolute;
cursor: pointer;
top: 2;
left: 0;
right: 0;
bottom: -2;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 17px;
width: 17px;
left: 0px;
bottom: 0px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: #2196f3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196f3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/* Slider for white background */
.slider2 {
position: absolute;
cursor: pointer;
top: 2;
left: 0;
right: 0;
bottom: -2;
/* border: 1px solid black; */
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider2:before {
position: absolute;
content: "";
height: 17px;
width: 17px;
left: 0px;
bottom: 0px;
background-color: #aaa;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider2 {
background-color: #2196f3;
}
input:focus + .slider2 {
box-shadow: 0 0 1px #2196f3;
}
input:checked + .slider2:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider2.round {
border-radius: 34px;
}
.slider2.round:before {
border-radius: 50%;
}
#miniMap {
position: fixed;
z-index: 2;
bottom: 20px;
right: 40px;
/*height:150px;
width: 25%;*/
overflow-y: scroll;
background-color: black;
/*border:1px solid #aaa;*/
opacity: 0.97;
box-shadow: 0px 0px 15px #888888;
overflow: hidden;
/*transition: opacity .25s ease-in-out;*/
}
#plot {
position: fixed;
z-index: 1;
bottom: 0;
right: 0;
/*display: block;*/
/*height: 0px;*/
/*width: 100%;*/
overflow-y: scroll;
background-color: #eee;
/*background-blend-mode: color;*/
}
.left {
float: left;
}
.right {
float: right;
}
.icon {
position: relative;
height: 70px;
width: 70px;
/*margin: 1px;*/
margin-bottom: 5px;
margin-left: 3px;
display: inline-block;
background-color: white;
border-radius: 4px;
/*border-color: #0099ff;*/
border: 2px solid #0099ff;
text-align: center;
font-size: 8px;
padding: 5px;
}
img {
display: none;
}
div.icon img {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
/*margin: auto;*/
width: 100%;
/*height:100%;*/
display: inline-block;
}
.img__description {
position: absolute;
/*top: 0;*/
bottom: -16;
text-align:center;
left: 0;
right: 0;
background-color: #0099ff;
color: white;
font-size: 8px;
/*background: rgba(29, 106, 154, 0.72);
color: #fff;*/
visibility: hidden;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
opacity: 0;
/* transition effect. not necessary */
transition: opacity 0.2s, visibility 0.2s;
}
.icon:hover .img__description {
visibility: visible;
opacity: 1;
}
.icon:hover {
/*background-color: #cce5ff;*/
/*border-color: blue;*/
margin-bottom: 1px;
height: 74px;
background-color: #f5f5f5;
transition: height 0.2s margin 0.2s;
}
.objectPropertyAttributeChecked.btn {
width: 100%;
margin-bottom: 5px;
}
/* For loading screen - pace.js */
.pace {
-webkit-pointer-events: none;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
width: 100%;
height: 100%;
background-color: #fff;
}
.pace-inactive {
display: none;
}
#Help {
visibility: hidden;
/* Hidden by default. Visible on click */
min-width: 250px;
/* Set a default minimum width */
margin-left: -125px;
/* Divide value of min-width by 2 */
background-color: #333;
/* Black background color */
color: #fff;
/* White text color */
text-align: center;
/* Centered text */
border-radius: 2px;
/* Rounded borders */
padding: 16px;
/* Padding */
position: fixed;
/* Sit on top of the screen */
z-index: 1;
/* Add a z-index if needed */
right: 50px;
/* Center the snackbar */
bottom: 50px;
/* 30px from the bottom */
opacity: 0;
}
#Help.show {
visibility: visible;
/* Show the snackbar */
opacity: 1;
-webkit-transition-delay: 0.5s;
/* Safari */
transition-delay: 0.5s;
-webkit-transition-duration: 0.3s;
/* Safari */
transition-duration: 0.3s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-webkit-keyframes fadeout {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes fadeout {
from {
bottom: 0px;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}
.pace .pace-progress {
background: #29d;
position: fixed;
z-index: 2000;
top: 0;
right: 100%;
width: 50%;
height: 5px;
}
/* dropdown-menu styles */
.dropdown-menu {
background-color: black;
border: 1px solid #09f;
border-top: none;
}
.navbar-nav > li > a {
padding: 7px 15px;
}
.dropdown-menu > li > a {
color: #939393 !important;
padding: 3px 14px;
}
.dropdown-menu > li > a:hover {
color: #4db8ff !important;
background-color: black;
}
.ui-dialog {
background: #222;
}
.ui-dialog p {
color: #9d9d9d;
}
.ui-widget-header {
border: 1px solid #0099ff;
color: #0099ff;
}
.ui-dialog-buttonpane {
background-color: black;
}
.ui-dialog-titlebar {
background-color: black;
}
.ui-dialog-titlebar-close {
background-image: url("../img/cross.png");
position: absolute;
right: 0.3em;
top: 50%;
width: 21px;
margin: -10px 0 0 0;
padding: 1px;
height: 20px;
}
.ui-icon-close {
background-position: -80px -128px;
}
.ui-dialog .ui-dialog-buttonpane button {
background-color: #004280;
border: 1px solid #09f;
color: white;
}
.navbar .nav.pull-right {
float: right;
margin-right: 10px;
/*set margin this way in your custom stylesheet*/
}
/* LOADING ICON CSS STARTS*/
.sk-folding-cube {
margin: 20px auto;
width: 40px;
height: 40px;
position: relative;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg);
}
.sk-folding-cube .sk-cube {
float: left;
width: 50%;
height: 50%;
position: relative;
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1);
}
.sk-folding-cube .sk-cube:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #09f;
-webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
animation: sk-foldCubeAngle 2.4s infinite linear both;
-webkit-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
transform-origin: 100% 100%;
}
.sk-folding-cube .sk-cube2 {
-webkit-transform: scale(1.1) rotateZ(90deg);
transform: scale(1.1) rotateZ(90deg);
}
.sk-folding-cube .sk-cube3 {
-webkit-transform: scale(1.1) rotateZ(180deg);
transform: scale(1.1) rotateZ(180deg);
}
.sk-folding-cube .sk-cube4 {
-webkit-transform: scale(1.1) rotateZ(270deg);
transform: scale(1.1) rotateZ(270deg);
}
.sk-folding-cube .sk-cube2:before {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
}
.sk-folding-cube .sk-cube3:before {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}
.sk-folding-cube .sk-cube4:before {
-webkit-animation-delay: 0.9s;
animation-delay: 0.9s;
}
@-webkit-keyframes sk-foldCubeAngle {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
@keyframes sk-foldCubeAngle {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
/* LOADING ICON CSS ENDS*/

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Group_305" width="30" height="30" data-name="Group 305" viewBox="0 0 30 30"><g id="Rectangle_1072" fill="none" stroke="#f1f9ff" stroke-width="2" data-name="Rectangle 1072"><rect width="30" height="30" stroke="none" rx="10"/><rect width="28" height="28" x="1" y="1" fill="none" rx="9"/></g><path id="Path_36" fill="#fff" d="M4.7,6.1,0,1.4,1.4,0,4.7,3.3,8,0,9.4,1.4Z" data-name="Path 36" transform="translate(10 11.655)"/></svg>

After

Width:  |  Height:  |  Size: 470 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Group_306" width="30" height="30" data-name="Group 306" viewBox="0 0 30 30"><g id="Rectangle_1072" fill="none" stroke="#f1f9ff" stroke-width="2" data-name="Rectangle 1072"><rect width="30" height="30" stroke="none" rx="10"/><rect width="28" height="28" x="1" y="1" fill="none" rx="9"/></g><path id="Path_36" fill="#fff" d="M4.7,6.1,0,1.4,1.4,0,4.7,3.3,8,0,9.4,1.4Z" data-name="Path 36" transform="translate(18.1 9.655) rotate(90)"/></svg>

After

Width:  |  Height:  |  Size: 482 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18.142" height="18.142" viewBox="0 0 18.142 18.142"><g id="icon" transform="translate(9.071 2) rotate(45)"><path id="line" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M0,0,10,10"/><path id="line-2" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M0,10,10,0" data-name="line"/></g></svg>

After

Width:  |  Height:  |  Size: 473 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16.142" height="16.142" viewBox="0 0 16.142 16.142"><g id="icon" transform="translate(8.071 1) rotate(45)"><path id="line" fill="#fff" stroke="#fff" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M0,0"/><path id="line-2" fill="#fff" stroke="#fff" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M0,10,10,0" data-name="line"/></g></svg>

After

Width:  |  Height:  |  Size: 421 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Group_307" width="30" height="30" data-name="Group 307" viewBox="0 0 30 30"><g id="Rectangle_1072" fill="none" stroke="#f1f9ff" stroke-width="2" data-name="Rectangle 1072"><rect width="30" height="30" stroke="none" rx="10"/><rect width="28" height="28" x="1" y="1" fill="none" rx="9"/></g><path id="Path_36" fill="#fff" d="M4.7,6.1,0,1.4,1.4,0,4.7,3.3,8,0,9.4,1.4Z" data-name="Path 36" transform="translate(12 19.055) rotate(-90)"/></svg>

After

Width:  |  Height:  |  Size: 482 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Group_304" width="30" height="30" data-name="Group 304" viewBox="0 0 30 30"><g id="Rectangle_1072" fill="none" stroke="#f1f9ff" stroke-width="2" data-name="Rectangle 1072"><rect width="30" height="30" stroke="none" rx="10"/><rect width="28" height="28" x="1" y="1" fill="none" rx="9"/></g><path id="Path_36" fill="#fff" d="M4.7,6.1,0,1.4,1.4,0,4.7,3.3,8,0,9.4,1.4Z" data-name="Path 36" transform="translate(19.4 17.755) rotate(180)"/></svg>

After

Width:  |  Height:  |  Size: 484 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="277.523" height="222.018" viewBox="0 0 277.523 222.018"><defs><style>.cls-1{fill:#ddd}</style></defs><path id="ic_delete_sweep_24px" d="M182.39,170.514h55.5v27.752h-55.5Zm0-111.009h97.133V87.257H182.39Zm0,55.5h83.257v27.752H182.39ZM15.876,198.266a27.834,27.834,0,0,0,27.752,27.752h83.257a27.834,27.834,0,0,0,27.752-27.752V59.5H15.876ZM168.514,17.876H126.885L113.009,4H57.5L43.628,17.876H2V45.628H168.514Z" class="cls-1" transform="translate(-2 -4)"/></svg>

After

Width:  |  Height:  |  Size: 503 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.324" height="241.324" preserveAspectRatio="xMidYMid meet" viewBox="0 0 241.324 241.324"><defs><style>.cls-1{fill:#ddd}</style></defs><path id="ic_photo_library_24px" d="M243.324,170.927V26.132A24.2,24.2,0,0,0,219.192,2H74.4A24.2,24.2,0,0,0,50.265,26.132V170.927A24.2,24.2,0,0,0,74.4,195.059H219.192A24.2,24.2,0,0,0,243.324,170.927ZM110.6,122.662l24.494,32.7L170.927,110.6l48.265,60.331H74.4ZM2,50.265V219.192a24.2,24.2,0,0,0,24.132,24.132H195.059V219.192H26.132V50.265Z" class="cls-1" transform="translate(-2 -2)"/></svg>

After

Width:  |  Height:  |  Size: 572 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.324" height="241.324" viewBox="0 0 241.324 241.324"><path id="path" fill="#DDD" fill-rule="evenodd" d="M5,194.612H56.712v51.712H91.187V160.137H5v34.475Zm51.712-137.9H5V91.187H91.187V5H56.712V56.712ZM160.137,246.324h34.475V194.612h51.712V160.137H160.137v86.187ZM194.612,56.712V5H160.137V91.187h86.187V56.712Z" transform="translate(-5 -5)"/></svg>

After

Width:  |  Height:  |  Size: 396 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="301.655" height="241.324" viewBox="0 0 301.655 241.324"><defs><style>.cls-1{fill:#ddd}</style></defs><path id="ic_create_new_folder_24px" d="M273.489,34.165H152.827L122.662,4h-90.5A29.934,29.934,0,0,0,2.151,34.165L2,215.158a30.061,30.061,0,0,0,30.165,30.165H273.489a30.061,30.061,0,0,0,30.165-30.165V64.331A30.061,30.061,0,0,0,273.489,34.165ZM258.407,154.827H213.158v45.248H182.993V154.827H137.745V124.662h45.248V79.414h30.165v45.248h45.248Z" class="cls-1" transform="translate(-2 -4)"/></svg>

After

Width:  |  Height:  |  Size: 540 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.324" height="241.324" viewBox="0 0 241.324 241.324"><path id="path" fill="#DDD" fill-rule="evenodd" d="M124.662,4C58.3,4,4,58.3,4,124.662S58.3,245.324,124.662,245.324c55.806,0,102.563-39.215,120.662-90.5H215.159c-18.1,34.69-51.281,60.331-90.5,60.331-49.773,0-90.5-40.723-90.5-90.5s40.723-90.5,90.5-90.5c25.641,0,46.757,10.558,60.331,30.165l-45.248,45.248H245.324V4L215.159,34.166C188.01,19.083,157.844,4,124.662,4Z" transform="translate(-4 -4)"/></svg>

After

Width:  |  Height:  |  Size: 503 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="344.749" height="241.324" viewBox="0 0 344.749 241.324"><path id="Path" fill="#DDD" d="M275.8,103.425a68.95,68.95,0,0,1,0,137.9H189.612V162.346l19.328,19.33A17.238,17.238,0,0,0,233.319,157.3L185.451,109.43a17.239,17.239,0,0,0-26.156,0L111.431,157.3a17.237,17.237,0,0,0,24.377,24.377l19.329-19.328v78.977H86.187a86.193,86.193,0,0,1-11.624-171.6,103.442,103.442,0,0,1,201.235,33.7Z"/></svg>

After

Width:  |  Height:  |  Size: 435 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.324" height="241.324" viewBox="0 0 241.324 241.324"><defs><style>.cls-1{fill:#ddd}</style></defs><path id="ic_save_24px" d="M190.7,3H29.814A26.806,26.806,0,0,0,3,29.814v187.7a26.806,26.806,0,0,0,26.814,26.814h187.7a26.893,26.893,0,0,0,26.814-26.814V56.628ZM123.662,217.51a40.221,40.221,0,1,1,40.221-40.221A40.167,40.167,0,0,1,123.662,217.51ZM163.883,83.441H29.814V29.814H163.883Z" class="cls-1" transform="translate(-3 -3)"/></svg>

After

Width:  |  Height:  |  Size: 482 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="241.324" height="241.324" viewBox="0 0 241.324 241.324"><path id="path" fill="#DDD" fill-rule="evenodd" d="M124.662,4c66.364,0,120.662,54.3,120.662,120.662s-54.3,120.662-120.662,120.662C68.856,245.324,22.1,206.109,4,154.827H34.165c18.1,34.69,51.281,60.331,90.5,60.331,49.773,0,90.5-40.723,90.5-90.5s-40.723-90.5-90.5-90.5c-25.641,0-46.756,10.558-60.331,30.165l45.248,45.248H4V4L34.165,34.166C61.314,19.083,91.48,4,124.662,4Z" transform="translate(-4 -4)"/></svg>

After

Width:  |  Height:  |  Size: 509 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="96.65" height="49.325" viewBox="0 0 96.65 49.325"><g id="chevron-down" transform="translate(1.414 1.414)"><path id="chevron-down-2" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="5" d="M6,9,52.911,55.911,99.821,9" data-name="chevron-down" transform="translate(-6 -9)"/></g></svg>

After

Width:  |  Height:  |  Size: 366 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="ic-actions-close-simple" width="24" height="24" viewBox="0 0 24 24"><rect id="Rectangle_32" width="24" height="24" fill="none" data-name="Rectangle 32"/><g id="Layer" transform="translate(-0.029)"><path id="Path_76" fill="#fff" d="M4.93,19.07a1,1,0,0,1,0-1.41L17.66,4.93a1,1,0,1,1,1.41,1.41L6.34,19.07a1,1,0,0,1-1.41,0Z" data-name="Path 76"/><path id="Path_77" fill="#fff" d="M4.93,4.93a1,1,0,0,1,1.41,0L19.07,17.66a1,1,0,1,1-1.41,1.41L4.93,6.34A1,1,0,0,1,4.93,4.93Z" data-name="Path 77"/></g></svg>

After

Width:  |  Height:  |  Size: 543 B

View file

@ -0,0 +1,6 @@
.navbar,
.navbar-search-active,
.footer-empty-div,
.footer-container-fluid {
display: none;
}

249
simulator/src/css/embed.css Normal file
View file

@ -0,0 +1,249 @@
.switch {
position: relative;
display: inline-block;
width: 43px;
height: 17px;
margin-bottom: 0px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 2;
left: 0;
right: 0;
bottom: -2;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 17px;
width: 17px;
left: 0px;
bottom: 0px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
#clockProperty{
padding: 10px;
}
#clockPropertyHeader{
border-radius: 3px;
}
#clockProperty{
padding: 10px;
border-radius: 6px;
opacity: 0.1;
transition: .4s;
height: 109px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
#clockProperty:hover{
opacity: 1;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
/* background-color: #2196F3; */
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
#tabsBar .circuits {
color: #000;
text-align: center;
padding-left: 0.5em;
padding-right: 0.5em;
border: 1px solid #111;
display: inline-block;
}
button:focus {outline:0;}
#tabsBar .circuits:hover {
background-color: lightgray;
/*border: 1px solid #0099ff;*/
transition-duration: 100ms
}
#tabsBar .current {
transition-duration: 100ms
}
.noSelect {
-moz-user-select: none; -webkit-user-select: none; -ms-user-select:none; user-select:none;-o-user-select:none;
}
.pointerCursor {
cursor: pointer;
}
.defaultCursor {
cursor: default;
}
.simulation{
position: relative;
width:auto;
height:100%;
overflow:hidden;
background-color: "white";
}
#elementName {
position:absolute;
left: 6px; bottom: 6px;
background-color: white;
z-index:101;
color: black;
padding: 1px;
border: 0.5px solid black;
display: none;
}
/* LOADING ICON CSS STARTS*/
.sk-folding-cube {
margin: 20px auto;
width: 40px;
height: 40px;
position: relative;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg);
}
.sk-folding-cube .sk-cube {
float: left;
width: 50%;
height: 50%;
position: relative;
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1);
}
.sk-folding-cube .sk-cube:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #09f;
-webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
animation: sk-foldCubeAngle 2.4s infinite linear both;
-webkit-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
transform-origin: 100% 100%;
}
.sk-folding-cube .sk-cube2 {
-webkit-transform: scale(1.1) rotateZ(90deg);
transform: scale(1.1) rotateZ(90deg);
}
.sk-folding-cube .sk-cube3 {
-webkit-transform: scale(1.1) rotateZ(180deg);
transform: scale(1.1) rotateZ(180deg);
}
.sk-folding-cube .sk-cube4 {
-webkit-transform: scale(1.1) rotateZ(270deg);
transform: scale(1.1) rotateZ(270deg);
}
.sk-folding-cube .sk-cube2:before {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
}
.sk-folding-cube .sk-cube3:before {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}
.sk-folding-cube .sk-cube4:before {
-webkit-animation-delay: 0.9s;
animation-delay: 0.9s;
}
@-webkit-keyframes sk-foldCubeAngle {
0%, 10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
} 25%, 75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
} 90%, 100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
@keyframes sk-foldCubeAngle {
0%, 10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
opacity: 0;
} 25%, 75% {
-webkit-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
opacity: 1;
} 90%, 100% {
-webkit-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
opacity: 0;
}
}
/* LOADING ICON CSS ENDS*/
#restrictedElementsDiv {
position: absolute;
bottom: 10;
right: 2;
z-index: 100;
width: 215px;
font-size: 14px;
background-color: gainsboro;
border: 1px solid #0D3349;
opacity: .1;
padding: 3px 5px;
}
#restrictedElementsDiv:hover {
opacity: 1;
transition: .4s;
}
.zoom-wrapper {
position: absolute;
bottom: 1px;
right: 0px;
font-size: 10px;
z-index: 100;
}
.zoom-wrapper button {
opacity: .3;
}
.zoom-wrapper button:hover {
opacity: 1;
}
.embed-fullscreen-btn {
border-radius: 20px;
}

View file

@ -0,0 +1,24 @@
.error-code {
color: #42b983;
font-family: 'CircuitBoredNF';
font-size: 142px;
}
.help-text-main {
color: #1c1c1c;
font-family: 'Segoe UI';
font-size: 22px;
font-weight: 400;
margin: 0;
}
.return {
color: #42b983;
display: inline-block;
font-family: 'Segoe UI';
font-weight: 700;
}
.return:hover {
color: #42b983;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,386 @@
/*
* checkBo lightweight jQuery plugin v0.1.4 by @ElmahdiMahmoud
* Licensed under the MIT license - https://github.com/elmahdim/checkbo/blob/master/LICENSE
*
* Custom checkbox and radio
* Author URL: elmahdim.com
*/
.cb-checkbox .cb-inner,
.cb-checkbox i {
width: 18px;
height: 18px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
.cb-checkbox.cb-sm .cb-inner,
.cb-checkbox.cb-sm i {
width: 14px;
height: 14px;
}
.cb-checkbox.cb-md .cb-inner,
.cb-checkbox.cb-md i {
width: 24px;
height: 24px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.cb-checkbox.cb-lg .cb-inner,
.cb-checkbox.cb-lg i {
width: 30px;
height: 30px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
}
.cb-radio .cb-inner {
width: 18px;
height: 18px;
}
.cb-radio.cb-sm .cb-inner {
width: 14px;
height: 14px;
}
.cb-radio.cb-md .cb-inner {
width: 24px;
height: 24px;
}
.cb-radio.cb-lg .cb-inner {
width: 30px;
height: 30px;
}
.cb-checkbox,
.cb-radio {
padding: 3px 0;
color: inherit;
cursor: pointer;
overflow: hidden;
font-size: inherit;
font-weight: 400;
display: inline-block;
line-height: 18px;
}
.cb-checkbox input[type="checkbox"],
.cb-radio input[type="radio"],
.cb-switcher input[type="checkbox"],
.cb-switcher input[type="radio"] {
display: none;
}
.cb-checkbox.disabled,
.cb-checkbox.disabled *,
.cb-radio.disabled,
.cb-radio.disabled *,
.cb-switcher.disabled,
.cb-switcher.disabled * {
cursor: default;
}
.cb-checkbox.disabled,
.cb-checkbox.disabled .cb-inner {
color: #ddd;
}
.cb-checkbox.disabled:hover .cb-inner {
border-color: #ddd;
}
.cb-checkbox.disabled.checked .cb-inner {
background-color: #ddd;
border-color: #ddd;
}
.cb-radio.disabled {
color: #ddd;
}
.cb-radio.disabled .cb-inner {
border-color: #ddd;
}
.cb-radio.disabled i {
background-color: transparent;
}
.cb-radio.disabled.checked .cb-inner {
border-color: #ddd;
}
.cb-radio.disabled.checked .cb-inner i {
background-color: #ddd;
}
.cb-radio.disabled:hover .cb-inner {
border-color: #ddd;
}
.cb-checkbox .cb-inner {
float: left;
overflow: hidden;
margin: 0 5px 0 0;
position: relative;
background: transparent;
display: inline-block;
border: 1px solid #d6d6d6;
/* -moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
-webkit-transition: all 0.5s ease;
transition: all 0.5s ease; */
}
.cb-checkbox i {
top: 1px;
left: 2px;
display: block;
position: absolute;
}
.cb-checkbox i:after,
.cb-checkbox i:before {
height: 0;
width: 2px;
content: "";
display: block;
position: absolute;
background-color: #fff;
/* -moz-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
-webkit-transition: all 0.2s ease;
transition: all 0.2s ease; */
}
.cb-checkbox i:before {
top: 0;
left: 0;
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.cb-checkbox i:after {
left: 7px;
bottom: 5px;
/* -moz-transition-delay: 0.3s;
-o-transition-delay: 0.3s;
-webkit-transition-delay: 0.3s;
transition-delay: 0.3s; */
-moz-transform: rotate(30deg);
-ms-transform: rotate(30deg);
-webkit-transform: rotate(30deg);
transform: rotate(30deg);
}
.cb-radio .cb-inner {
float: left;
overflow: hidden;
margin: 0 5px 0 0;
position: relative;
display: inline-block;
border: 1px solid #d7d7d7;
background-color: transparent;
-moz-border-radius: 100%;
-webkit-border-radius: 100%;
border-radius: 100%;
-moz-transition: all 0.1s ease;
-o-transition: all 0.1s ease;
-webkit-transition: all 0.1s ease;
transition: all 0.1s ease;
}
.cb-radio i {
top: 50%;
left: 50%;
width: 6px;
height: 6px;
margin-top: -3px;
margin-left: -3px;
position: absolute;
background-color: transparent;
-moz-border-radius: 100%;
-webkit-border-radius: 100%;
border-radius: 100%;
-moz-transform: scale(0.05, 5);
-ms-transform: scale(0.05, 5);
-webkit-transform: scale(0.05, 5);
transform: scale(0.05, 5);
-moz-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
-webkit-transition: all 0.2s ease;
transition: all 0.2s ease;
}
.cb-checkbox.cb-sm,
.cb-radio.cb-sm {
line-height: 14px;
}
.cb-checkbox.cb-md,
.cb-radio.cb-md {
line-height: 24px;
}
.cb-checkbox.cb-lg,
.cb-radio.cb-lg {
line-height: 30px;
}
.cb-checkbox.cb-sm i:before {
top: 4px;
left: 1px;
}
.cb-checkbox.cb-sm i:after {
left: 5px;
}
.cb-checkbox.cb-md i:before {
top: 10px;
left: 5px;
}
.cb-checkbox.cb-md i:after {
bottom: 6px;
left: 11px;
}
.cb-checkbox.checked .cb-inner {
border-color: transparent;
background-color: transparent;
}
.cb-checkbox.checked.cb-sm i:before {
top: 4px;
left: 1px;
}
.cb-checkbox.checked.cb-sm i:after {
height: 9px;
}
.cb-checkbox.checked.cb-md i:before {
top: 10px;
left: 4px;
height: 8px;
}
.cb-checkbox.checked.cb-md i:after {
bottom: 6px;
left: 11px;
height: 16px;
}
.cb-checkbox.checked.cb-lg i:before {
top: 11px;
left: 6px;
height: 12px;
}
.cb-checkbox.checked.cb-lg i:after {
left: 14px;
bottom: 7px;
height: 20px;
}
.cb-checkbox.checked i:before {
top: 6px;
left: 2px;
height: 6px;
}
.cb-checkbox.checked i:after {
height: 12px;
}
.cb-radio.checked .cb-inner {
background: #fff;
box-shadow: 0 0 3px #efefef;
}
.cb-radio.checked i {
-moz-transform: scale(1.1, 1.1);
-ms-transform: scale(1.1, 1.1);
-webkit-transform: scale(1.1, 1.1);
transform: scale(1.1, 1.1);
background-color: transparent;
}
.cb-checkbox:hover .cb-inner,
.cb-radio:hover .cb-inner {
border-color: white;
}
.cb-switcher {
display: inline-block;
border: 1px solid #eee;
background-color: #fff;
width: 95px;
height: 35px;
position: relative;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 20px;
-moz-transition: background 0.4s ease;
-o-transition: background 0.4s ease;
-webkit-transition: background 0.4s ease;
transition: background 0.4s ease;
}
.cb-switcher,
.cb-switcher * {
cursor: pointer;
}
.cb-switcher ::-moz-selection {
background-color: transparent;
}
.cb-switcher ::selection {
background-color: transparent;
}
.cb-switcher .cb-state {
z-index: 1;
text-align: center;
font-size: 12px;
}
.cb-switcher .cb-state,
.cb-switcher:before {
width: 34px;
height: 34px;
line-height: 34px;
position: absolute;
left: 0;
top: -1px;
-moz-border-radius: 100%;
-webkit-border-radius: 100%;
border-radius: 100%;
-moz-transition: all 0.4s ease;
-o-transition: all 0.4s ease;
-webkit-transition: all 0.4s ease;
transition: all 0.4s ease;
}
.cb-switcher:before {
content: "";
background-color: #eee;
-moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
}
.cb-switcher.checked {
background-color: transparent;
}
.cb-switcher.checked .cb-state,
.cb-switcher.checked:before {
left: 60px;
color: transparent;
}
.cb-switcher.checked:before {
background-color: #fff;
-moz-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1);
}
.cb-switcher.checked .inner-switcher:before {
border-top-color: transparent;
}
.cb-switcher.checked .inner-switcher:after {
border-bottom-color: transparent;
}
.cb-switcher .inner-switcher:after,
.cb-switcher .inner-switcher:before {
content: "";
position: absolute;
left: 50%;
width: 0;
height: 0;
z-index: 2;
margin-left: -20px;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
-moz-transition: border 0.4s ease;
-o-transition: border 0.4s ease;
-webkit-transition: border 0.4s ease;
transition: border 0.4s ease;
}
.cb-switcher .inner-switcher:before {
border-top: 17px solid #fff;
top: 0;
}
.cb-switcher .inner-switcher:after {
border-bottom: 17px solid #fff;
bottom: 0;
}
.cb-state {
color: #ccc;
display: inline-block;
}
.cb-switcher-group .cb-state {
position: relative;
top: 7px;
}
.is-hidden {
display: none !important;
visibility: hidden !important;
}

View file

@ -0,0 +1,20 @@
.display--none {
display: none;
}
.circuit-element-category {
border-bottom: 1px solid #026e57;
font-weight: 500;
margin: 20px 0 5px;
padding-bottom: 5px;
}
.restricted-elements-list {
margin: 10px 0 25px;
}
.form-check-label {
font-size: 16px;
margin-bottom: 6px;
margin-top: 6px;
}

View file

@ -0,0 +1,146 @@
.ui-dialog[aria-describedby="customShortcutDialog"] {
min-width: 680px;
}
#edit {
display: none;
position: absolute;
width: 490px;
height: 150px;
background: #2d302e;
border-radius: 5px;
z-index: 10000;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: none;
}
#edit > span {
margin-top: 10px;
position: absolute;
text-align: center;
width: 100%;
}
#pressedKeys {
text-align: center;
position: absolute;
top: 50%;
width: 100%;
}
#warning {
position: absolute;
bottom: 5px;
width: 100%;
text-align: center;
font-size: 14px;
color: #dc5656;
}
#customShortcutDialog {
display: none;
align-items: center;
color: white;
flex-direction: column;
max-height: 430px !important;
overflow: hidden !important;
}
#closeDialog {
font-size: 25px;
color: white;
transform: rotate(45deg);
position: absolute;
top: 0%;
right: 2%;
cursor: pointer;
user-select: none;
-moz-user-select: none;
}
#dialogTitle {
position: absolute;
top: 2%;
left: 3%;
user-select: none;
-moz-user-select: none;
}
#heading {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
width: 100%;
height: 35px;
user-select: none;
padding-bottom: 10px;
}
#heading > span {
padding: 0 20px;
}
#preference {
width: 100%;
overflow-y: auto;
}
#preference div {
display: inline-flex;
justify-content: space-between;
align-items: center;
height: 35px;
cursor: pointer;
user-select: none;
-moz-user-select: none;
width: 100%;
padding-left: 5px;
padding-right: 7px;
}
#preference div:not("#edit-icon") > span {
padding: 0 30px;
}
#preference > div:hover {
background-color: #7474743f;
}
#preference > div:hover span {
visibility: visible;
}
#edit-icon {
background: url(../img/edit_icon.png) no-repeat;
background-size: 15px 15px;
display: inline-block;
visibility: hidden;
width: 15px;
height: 15px;
}
@keyframes shake {
10%,
90% {
transform: translate(-50.5%, -50%);
}
20%,
80% {
transform: translate(-49.5%, -50%);
}
30%,
50%,
70% {
transform: translate(-50.5%, -50%);
}
40%,
60% {
transform: translate(-49.5%, -50%);
}
}

1
simulator/src/css/typeahead.min.css vendored Normal file

File diff suppressed because one or more lines are too long

41
simulator/src/data.js Executable file
View file

@ -0,0 +1,41 @@
import { fullView, deleteSelected } from './ux';
import { createSubCircuitPrompt } from './subcircuit';
import save from './data/save';
import load from './data/load';
import createSaveAsImgPrompt from './data/saveImage'
import { clearProject, newProject, saveOffline, openOffline, recoverProject } from './data/project'
import { newCircuit } from './circuit'
import { createCombinationalAnalysisPrompt } from './combinationalAnalysis';
import { colorThemes } from "./themer/themer";
import { showTourGuide } from './tutorials';
import {createVerilogCircuit, saveVerilogCode, resetVerilogCode} from './Verilog2CV';
import { generateVerilog } from './verilog';
import { bitConverterDialog } from './utils';
const logixFunction = {};
logixFunction.save = save;
logixFunction.load = load;
logixFunction.createSaveAsImgPrompt = createSaveAsImgPrompt;
logixFunction.clearProject = clearProject;
logixFunction.newProject = newProject;
logixFunction.saveOffline = saveOffline;
logixFunction.newCircuit = newCircuit;
logixFunction.createOpenLocalPrompt = openOffline;
logixFunction.recoverProject = recoverProject;
logixFunction.createSubCircuitPrompt = createSubCircuitPrompt;
logixFunction.createCombinationalAnalysisPrompt = createCombinationalAnalysisPrompt;
logixFunction.fullViewOption = fullView;
logixFunction.colorThemes = colorThemes;
logixFunction.showTourGuide = showTourGuideHelper;
logixFunction.deleteSelected = deleteSelected;
logixFunction.newVerilogModule = createVerilogCircuit;
logixFunction.saveVerilogCode = saveVerilogCode;
logixFunction.resetVerilogCode = resetVerilogCode;
logixFunction.generateVerilog = generateVerilog;
logixFunction.bitconverter = bitConverterDialog;
export default logixFunction;
// Hack to restart tour guide
function showTourGuideHelper() {
setTimeout(()=> {showTourGuide();}, 100);
}

View file

@ -0,0 +1,67 @@
import { projectSavedSet } from './project';
/* eslint-disable no-param-reassign */
function extract(obj) {
return obj.saveObject();
}
// Check if there is anything to backup - to be deprecated
/**
* Check if backup is available
* @param {Scope} scope
* @return {boolean}
* @category data
*/
export function checkIfBackup(scope) {
for (let i = 0; i < updateOrder.length; i++) { if (scope[updateOrder[i]].length) return true; }
return false;
}
export function backUp(scope = globalScope) {
// Disconnection of subcircuits are needed because these are the connections between nodes
// in current scope and those in the subcircuit's scope
for (let i = 0; i < scope.SubCircuit.length; i++) { scope.SubCircuit[i].removeConnections(); }
var data = {};
// Storing layout
data.layout = scope.layout;
// Storing Verilog Properties
data.verilogMetadata = scope.verilogMetadata;
// Storing all nodes
data.allNodes = scope.allNodes.map(extract);
// Storing other details
data.id = scope.id;
data.name = scope.name;
// Storing details of all module objects
for (let i = 0; i < moduleList.length; i++) {
if (scope[moduleList[i]].length) { data[moduleList[i]] = scope[moduleList[i]].map(extract); }
}
// Adding restricted circuit elements used in the save data
data.restrictedCircuitElementsUsed = scope.restrictedCircuitElementsUsed;
// Storing intermediate nodes (nodes in wires)
data.nodes = [];
for (let i = 0; i < scope.nodes.length; i++) { data.nodes.push(scope.allNodes.indexOf(scope.nodes[i])); }
// Restoring the connections
for (let i = 0; i < scope.SubCircuit.length; i++) { scope.SubCircuit[i].makeConnections(); }
return data;
}
export function scheduleBackup(scope = globalScope) {
var backup = JSON.stringify(backUp(scope));
if (scope.backups.length === 0 || scope.backups[scope.backups.length - 1] !== backup) {
scope.backups.push(backup);
scope.history = [];
scope.timeStamp = new Date().getTime();
projectSavedSet(false);
}
return backup;
}

235
simulator/src/data/load.js Executable file
View file

@ -0,0 +1,235 @@
import { resetScopeList, newCircuit, switchCircuit } from '../circuit';
import { setProjectName } from './save';
import {
scheduleUpdate, update, updateSimulationSet, updateCanvasSet, gridUpdateSet
} from '../engine';
import { updateRestrictedElementsInScope } from '../restrictedElementDiv';
import simulationArea from '../simulationArea';
import { loadSubCircuit } from '../subcircuit';
import { scheduleBackup } from './backupCircuit';
import { showProperties } from '../ux';
import { constructNodeConnections, loadNode, replace } from '../node';
import { generateId } from '../utils';
import modules from '../modules';
import { oppositeDirection } from '../canvasApi';
import plotArea from '../plotArea';
/**
* Backward compatibility - needs to be deprecated
* @param {CircuitElement} obj - the object to be rectified
* @category data
*/
function rectifyObjectType(obj) {
const rectify = {
FlipFlop: 'DflipFlop',
Ram: 'Rom',
};
return rectify[obj] || obj;
}
/**
* Function to load CircuitElements
* @param {JSON} data - JSOn data
* @param {Scope} scope - circuit in which we want to load modules
* @category data
*/
function loadModule(data, scope) {
// Create circuit element
var obj = new modules[rectifyObjectType(data.objectType)](data.x, data.y, scope, ...data.customData.constructorParamaters || []);
// Sets directions
obj.label = data.label;
obj.labelDirection = data.labelDirection || oppositeDirection[fixDirection[obj.direction]];
// Sets delay
obj.propagationDelay = data.propagationDelay || obj.propagationDelay;
obj.fixDirection();
// Restore other values
if (data.customData.values) {
for (var prop in data.customData.values) {
obj[prop] = data.customData.values[prop];
}
}
// Replace new nodes with the correct old nodes (with connections)
if (data.customData.nodes) {
for (const node in data.customData.nodes) {
const n = data.customData.nodes[node];
if (n instanceof Array) {
for (let i = 0; i < n.length; i++) {
obj[node][i] = replace(obj[node][i], n[i]);
}
} else {
obj[node] = replace(obj[node], n);
}
}
}
if(data.subcircuitMetadata)
obj.subcircuitMetadata = data["subcircuitMetadata"];
}
/**
* This function shouldn't ideally exist. But temporary fix
* for some issues while loading nodes.
* @category data
*/
function removeBugNodes(scope = globalScope) {
let x = scope.allNodes.length;
for (let i = 0; i < x; i++) {
if (scope.allNodes[i].type !== 2 && scope.allNodes[i].parent.objectType === 'CircuitElement') { scope.allNodes[i].delete(); }
if (scope.allNodes.length !== x) {
i = 0;
x = scope.allNodes.length;
}
}
}
/**
* Function to load a full circuit
* @param {Scope} scope
* @param {JSON} data
* @category data
*/
export function loadScope(scope, data) {
const ML = moduleList.slice(); // Module List copy
scope.restrictedCircuitElementsUsed = data.restrictedCircuitElementsUsed;
// Load all nodes
data.allNodes.map((x) => loadNode(x, scope));
// Make all connections
for (let i = 0; i < data.allNodes.length; i++) { constructNodeConnections(scope.allNodes[i], data.allNodes[i]); }
// Load all modules
for (let i = 0; i < ML.length; i++) {
if (data[ML[i]]) {
if (ML[i] === 'SubCircuit') {
// Load subcircuits differently
for (let j = 0; j < data[ML[i]].length; j++) { loadSubCircuit(data[ML[i]][j], scope); }
} else {
// Load everything else similarly
for (let j = 0; j < data[ML[i]].length; j++) {
loadModule(data[ML[i]][j], scope);
}
}
}
}
// Update wires according
scope.wires.map((x) => {
x.updateData(scope);
});
removeBugNodes(scope); // To be deprecated
// If Verilog Circuit Metadata exists, then restore
if (data.verilogMetadata) {
scope.verilogMetadata = data.verilogMetadata;
}
// If layout exists, then restore
if (data.layout) {
scope.layout = data.layout;
} else {
// Else generate new layout according to how it would have been otherwise (backward compatibility)
scope.layout = {};
scope.layout.width = 100;
scope.layout.height = Math.max(scope.Input.length, scope.Output.length) * 20 + 20;
scope.layout.title_x = 50;
scope.layout.title_y = 13;
for (let i = 0; i < scope.Input.length; i++) {
scope.Input[i].layoutProperties = {
x: 0,
y: scope.layout.height / 2 - scope.Input.length * 10 + 20 * i + 10,
id: generateId(),
};
}
for (let i = 0; i < scope.Output.length; i++) {
scope.Output[i].layoutProperties = {
x: scope.layout.width,
y: scope.layout.height / 2 - scope.Output.length * 10 + 20 * i + 10,
id: generateId(),
};
}
}
// Backward compatibility
if (scope.layout.titleEnabled === undefined) { scope.layout.titleEnabled = true; }
}
// Function to load project from data
/**
* loads a saved project
* @param {JSON} data - the json data of the
* @category data
* @exports load
*/
export default function load(data) {
// If project is new and no data is there, then just set project name
if (!data) {
setProjectName(__projectName);
return;
}
var { projectId } = data;
setProjectName(data.name);
globalScope = undefined;
resetScopeList(); // Remove default scope
$('.circuits').remove(); // Delete default scope
// Load all according to the dependency order
for (let i = 0; i < data.scopes.length; i++) {
var isVerilogCircuit = false;
var isMainCircuit = false;
if(data.scopes[i].verilogMetadata) {
isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit;
isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit;
}
// Create new circuit
const scope = newCircuit(data.scopes[i].name || 'Untitled', data.scopes[i].id, isVerilogCircuit, isMainCircuit);
// Load circuit data
loadScope(scope, data.scopes[i]);
// Focus circuit
globalScope = scope;
// Center circuit
if (embed) { globalScope.centerFocus(true); } else { globalScope.centerFocus(false); }
// update and backup circuit once
update(globalScope, true);
// Updating restricted element list initially on loading
updateRestrictedElementsInScope();
scheduleBackup();
}
// Restore clock
simulationArea.changeClockTime(data.timePeriod || 500);
simulationArea.clockEnabled = data.clockEnabled === undefined ? true : data.clockEnabled;
if (!embed) { showProperties(simulationArea.lastSelected); }
// Reorder tabs according to the saved order
if (data.orderedTabs) {
var unorderedTabs = $('.circuits').detach();
var plusButton = $('#tabsBar').children().detach();
for (const tab of data.orderedTabs) { $('#tabsBar').append(unorderedTabs.filter(`#${tab}`)); }
$('#tabsBar').append(plusButton);
}
// Switch to last focussedCircuit
if (data.focussedCircuit)
switchCircuit(data.focussedCircuit);
updateSimulationSet(true);
updateCanvasSet(true);
gridUpdateSet(true);
// Reset Timing
if(!embed)
plotArea.reset();
scheduleUpdate(1);
}

140
simulator/src/data/project.js Executable file
View file

@ -0,0 +1,140 @@
/* eslint-disable guard-for-in */
/* eslint-disable no-bitwise */
/* eslint-disable import/no-cycle */
/* eslint-disable no-restricted-globals */
/* eslint-disable no-alert */
import { resetScopeList, scopeList, newCircuit } from '../circuit';
import { showMessage, showError, generateId } from '../utils';
import { checkIfBackup } from './backupCircuit';
import {generateSaveData, getProjectName, setProjectName} from './save';
import load from './load';
/**
* Helper function to recover unsaved data
* @category data
*/
export function recoverProject() {
if (localStorage.getItem('recover')) {
var data = JSON.parse(localStorage.getItem('recover'));
if (confirm(`Would you like to recover: ${data.name}`)) {
load(data);
}
localStorage.removeItem('recover');
} else {
showError('No recover project found');
}
}
/**
* Prompt to restore from localStorage
* @category data
*/
export function openOffline() {
$('#openProjectDialog').empty();
const projectList = JSON.parse(localStorage.getItem('projectList'));
let flag = true;
for (id in projectList) {
flag = false;
$('#openProjectDialog').append(`<label class="option custom-radio"><input type="radio" name="projectId" value="${id}" />${projectList[id]}<span></span><i class="fa fa-trash deleteOfflineProject" onclick="deleteOfflineProject('${id}')"></i></label>`);
}
if (flag) $('#openProjectDialog').append('<p>Looks like no circuit has been saved yet. Create a new one and save it!</p>');
$('#openProjectDialog').dialog({
resizable:false,
width: 'auto',
buttons: !flag ? [{
text: 'Open Project',
click() {
if (!$('input[name=projectId]:checked').val()) return;
load(JSON.parse(localStorage.getItem($('input[name=projectId]:checked').val())));
window.projectId = $('input[name=projectId]:checked').val();
$(this).dialog('close');
},
}] : [],
});
}
/**
* Flag for project saved or not
* @type {boolean}
* @category data
*/
var projectSaved = true;
export function projectSavedSet(param) {
projectSaved = param;
}
/**
* Helper function to store to localStorage -- needs to be deprecated/removed
* @category data
*/
export function saveOffline() {
const data = generateSaveData();
localStorage.setItem(projectId, data);
const temp = JSON.parse(localStorage.getItem('projectList')) || {};
temp[projectId] = getProjectName();
localStorage.setItem('projectList', JSON.stringify(temp));
showMessage(`We have saved your project: ${getProjectName()} in your browser's localStorage`);
}
/**
* Checks if any circuit has unsaved data
* @category data
*/
function checkToSave() {
let saveFlag = false;
// eslint-disable-next-line no-restricted-syntax
for (id in scopeList) {
saveFlag |= checkIfBackup(scopeList[id]);
}
return saveFlag;
}
/**
* Prompt user to save data if unsaved
* @category data
*/
window.onbeforeunload = function () {
if (projectSaved || embed) return;
if (!checkToSave()) return;
alert('You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?');
const data = generateSaveData('Untitled');
localStorage.setItem('recover', data);
// eslint-disable-next-line consistent-return
return 'Are u sure u want to leave? Any unsaved changes may not be recoverable';
};
/**
* Function to clear project
* @category data
*/
export function clearProject() {
if (confirm('Would you like to clear the project?')) {
globalScope = undefined;
resetScopeList();
$('.circuits').remove();
newCircuit('main');
showMessage('Your project is as good as new!');
}
}
/**
Function used to start a new project while prompting confirmation from the user
* @param {boolean} verify - flag to verify a new project
* @category data
*/
export function newProject(verify) {
if (verify || projectSaved || !checkToSave() || confirm('What you like to start a new project? Any unsaved changes will be lost.')) {
clearProject();
localStorage.removeItem('recover');
window.location = '/simulator';
setProjectName(undefined);
projectId = generateId();
showMessage('New Project has been created!');
}
}

View file

@ -0,0 +1,46 @@
/* eslint-disable import/no-cycle */
/**
* Function to restore copy from backup
* @param {Scope=} scope - The circuit on which redo is called
* @category data
*/
import { layoutModeGet } from '../layoutMode';
import Scope, { scopeList } from '../circuit';
import { loadScope } from './load';
import { updateRestrictedElementsInScope } from '../restrictedElementDiv';
import { forceResetNodesSet } from '../engine';
/**
* Function called to generate a prompt to save an image
* @param {Scope=} - the circuit in which we want to call redo
* @category data
* @exports redo
*/
export default function redo(scope = globalScope) {
if (layoutModeGet()) return;
if (scope.history.length === 0) return;
const backupOx = globalScope.ox;
const backupOy = globalScope.oy;
const backupScale = globalScope.scale;
globalScope.ox = 0;
globalScope.oy = 0;
const tempScope = new Scope(scope.name);
loading = true;
const redoData = scope.history.pop();
scope.backups.push(redoData);
loadScope(tempScope, JSON.parse(redoData));
tempScope.backups = scope.backups;
tempScope.history = scope.history;
tempScope.id = scope.id;
tempScope.name = scope.name;
scopeList[scope.id] = tempScope;
globalScope = tempScope;
globalScope.ox = backupOx;
globalScope.oy = backupOy;
globalScope.scale = backupScale;
loading = false;
forceResetNodesSet(true);
// Updated restricted elements
updateRestrictedElementsInScope();
}
// for html file

409
simulator/src/data/save.js Executable file
View file

@ -0,0 +1,409 @@
import { scopeList } from '../circuit';
import { resetup } from '../setup';
import { update } from '../engine';
import { stripTags, showMessage } from '../utils';
import { backUp } from './backupCircuit';
import simulationArea from '../simulationArea';
import backgroundArea from '../backgroundArea';
import { findDimensions } from '../canvasApi';
import { projectSavedSet } from './project';
import { colors } from '../themer/themer';
import {layoutModeGet, toggleLayoutMode} from '../layoutMode';
import {verilogModeGet} from '../Verilog2CV';
import domtoimage from 'dom-to-image';
import C2S from '../../vendor/canvas2svg';
var projectName = undefined;
/**
* Function to set the name of project.
* @param {string} name - name for project
* @category data
*/
export function setProjectName(name) {
if(name == undefined) {
$('#projectName').html('Untitled');
return;
}
name = stripTags(name);
projectName = name;
$('#projectName').html(name);
}
/**
* Function to set the name of project.
* @param {string} name - name for project
* @category data
*/
export function getProjectName() {
return projectName;
}
/**
* Helper function to save canvas as image based on image type
* @param {string} name -name of the circuit
* @param {string} imgType - image type ex: png,jpg etc.
* @category data
*/
function downloadAsImg(name, imgType) {
const gh = simulationArea.canvas.toDataURL(`image/${imgType}`);
const anchor = document.createElement('a');
anchor.href = gh;
anchor.download = `${name}.${imgType}`;
anchor.click();
}
/**
* Returns the order of tabs in the project
*/
export function getTabsOrder() {
var tabs = $("#tabsBar").children().not('button');
var order = [];
for (let i = 0; i < tabs.length; i++) {
order.push(tabs[i].id);
}
return order
}
/**
* Generates JSON of the entire project
* @param {string} name - the name of project
* @return {JSON}
* @category data
*/
export function generateSaveData(name) {
data = {};
// Prompts for name, defaults to Untitled
name = getProjectName() || name || prompt('Enter Project Name:') || 'Untitled';
data.name = stripTags(name);
setProjectName(data.name);
// Save project details
data.timePeriod = simulationArea.timePeriod;
data.clockEnabled = simulationArea.clockEnabled;
data.projectId = projectId;
data.focussedCircuit = globalScope.id;
data.orderedTabs = getTabsOrder();
// Project Circuits, each scope is one circuit
data.scopes = [];
const dependencyList = {};
const completed = {};
// Getting list of dependencies for each circuit
for (id in scopeList) { dependencyList[id] = scopeList[id].getDependencies(); }
// Helper function to save Scope
// Recursively saves inner subcircuits first, before saving parent circuits
function saveScope(id) {
if (completed[id]) return;
for (let i = 0; i < dependencyList[id].length; i++) {
// Save inner subcircuits
saveScope(dependencyList[id][i]);
}
completed[id] = true;
update(scopeList[id], true); // For any pending integrity checks on subcircuits
data.scopes.push(backUp(scopeList[id]));
}
// Save all circuits
for (let id in scopeList) { saveScope(id); }
// convert to text
data = JSON.stringify(data);
return data;
}
// Helper function to download text
function download(filename, text) {
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
} else {
pom.click();
}
}
/**
* Function to generate image for the circuit
* @param {string} imgType - ex: png,jpg etc.
* @param {string} view - view type ex: full
* @param {boolean} transparent - tranparent bg or not
* @param {number} resolution - resolution of the image
* @param {boolean=} down - will download if true
* @category data
*/
export function generateImage(imgType, view, transparent, resolution, down = true) {
// Backup all data
const backUpOx = globalScope.ox;
const backUpOy = globalScope.oy;
const backUpWidth = width;
const backUpHeight = height;
const backUpScale = globalScope.scale;
const backUpContextBackground = backgroundArea.context;
const backUpContextSimulation = simulationArea.context;
backgroundArea.context = simulationArea.context;
globalScope.ox *= 1 / backUpScale;
globalScope.oy *= 1 / backUpScale;
// If SVG, create SVG context - using canvas2svg here
if (imgType === 'svg') {
simulationArea.context = new C2S(width, height);
resolution = 1;
} else if (imgType !== 'png') {
transparent = false;
}
globalScope.scale = resolution;
const scope = globalScope;
// Focus circuit
var flag = 1;
if (flag) {
if (view === 'full') {
findDimensions();
const minX = simulationArea.minWidth;
const minY = simulationArea.minHeight;
const maxX = simulationArea.maxWidth;
const maxY = simulationArea.maxHeight;
width = (maxX - minX + 100) * resolution;
height = (maxY - minY + 100) * resolution;
globalScope.ox = (-minX + 50) * resolution;
globalScope.oy = (-minY + 50) * resolution;
} else {
globalScope.ox *= resolution;
globalScope.oy *= resolution;
width = (width * resolution) / backUpScale;
height = (height * resolution) / backUpScale;
}
}
globalScope.ox = Math.round(globalScope.ox);
globalScope.oy = Math.round(globalScope.oy);
simulationArea.canvas.width = width;
simulationArea.canvas.height = height;
backgroundArea.canvas.width = width;
backgroundArea.canvas.height = height;
backgroundArea.context = simulationArea.context;
simulationArea.clear();
// Background
if (!transparent) {
simulationArea.context.fillStyle = colors["canvas_fill"];
simulationArea.context.rect(0, 0, width, height);
simulationArea.context.fill();
}
// Draw circuits, why is it updateOrder and not renderOrder?
for (let i = 0; i < renderOrder.length; i++) {
for (let j = 0; j < scope[renderOrder[i]].length; j++) { scope[renderOrder[i]][j].draw(); }
}
let returnData;
// If circuit is to be downloaded, download, other wise return dataURL
if (down) {
if (imgType === 'svg') {
const mySerializedSVG = simulationArea.context.getSerializedSvg(); // true here, if you need to convert named to numbered entities.
download(`${globalScope.name}.svg`, mySerializedSVG);
} else {
downloadAsImg(globalScope.name, imgType);
}
} else {
returnData = simulationArea.canvas.toDataURL(`image/${imgType}`);
}
// Restore everything
width = backUpWidth;
height = backUpHeight;
simulationArea.canvas.width = width;
simulationArea.canvas.height = height;
backgroundArea.canvas.width = width;
backgroundArea.canvas.height = height;
globalScope.scale = backUpScale;
backgroundArea.context = backUpContextBackground;
simulationArea.context = backUpContextSimulation;
globalScope.ox = backUpOx;
globalScope.oy = backUpOy;
resetup();
if (!down) return returnData;
}
async function crop(dataURL, w, h) {
//get empty second canvas
var myCanvas = document.createElement("CANVAS");
myCanvas.width = w;
myCanvas.height = h;
var myContext = myCanvas.getContext('2d');
var myImage;
var img = new Image();
return new Promise (function (resolved, rejected) {
img.src = dataURL;
img.onload = () => {
myContext.drawImage(img, 0, 0, w, h,0,0, w ,h);
myContext.save();
//create a new data URL
myImage = myCanvas.toDataURL('image/jpeg');
resolved(myImage);}
})
}
/**
* Function that is used to save image for display in the website
* @return {JSON}
* @category data
*/
async function generateImageForOnline() {
// Verilog Mode -> Different logic
// Fix aspect ratio to 1.6
// Ensure image is approximately 700 x 440
var ratio = 1.6
if(verilogModeGet()) {
var node = document.getElementsByClassName('CodeMirror')[0];
// var node = document.getElementsByClassName('CodeMirror')[0];
var prevHeight = $(node).css('height');
var prevWidth = $(node).css('width');
var baseWidth = 500;
var baseHeight = Math.round(baseWidth / ratio);
$(node).css('height', baseHeight);
$(node).css('width', baseWidth);
var data = await domtoimage.toJpeg(node);
$(node).css('width', prevWidth);
$(node).css('height', prevHeight);
data = await crop(data, baseWidth, baseHeight);
return data;
}
simulationArea.lastSelected = undefined; // Unselect any selections
// Fix aspect ratio to 1.6
if (width > height * ratio) {
height = width / ratio;
} else {
width = height * 1.6;
}
// Center circuits
globalScope.centerFocus();
// Ensure image is approximately 700 x 440
const resolution = Math.min(700 / (simulationArea.maxWidth - simulationArea.minWidth), 440 / (simulationArea.maxHeight - simulationArea.minHeight));
data = generateImage('jpeg', 'current', false, resolution, false);
// Restores Focus
globalScope.centerFocus(false);
return data;
}
/**
* Function called when you save acircuit online
* @category data
* @exports save
*/
export default async function save() {
if(layoutModeGet())
toggleLayoutMode();
projectSavedSet(true);
$('.loadingIcon').fadeIn();
const data = generateSaveData();
const projectName = getProjectName();
var imageData = await generateImageForOnline();
if (!userSignedIn) {
// user not signed in, save locally temporarily and force user to sign in
localStorage.setItem('recover_login', data);
// Asking user whether they want to login.
if (confirm('You have to login to save the project, you will be redirected to the login page.')) window.location.href = '/users/sign_in';
else $('.loadingIcon').fadeOut();
// eslint-disable-next-line camelcase
} else if (__logix_project_id == "0") {
// Create new project - this part needs to be improved and optimised
const form = $('<form/>', {
action: '/simulator/create_data',
method: 'post',
});
form.append(
$('<input>', {
type: 'hidden',
name: 'authenticity_token',
value: $('meta[name="csrf-token"]').attr('content'),
}),
);
form.append(
$('<input>', {
type: 'text',
name: 'data',
value: data,
}),
);
form.append(
$('<input>', {
type: 'text',
name: 'image',
value: imageData,
}),
);
form.append(
$('<input>', {
type: 'text',
name: 'name',
value: projectName,
}),
);
$('body').append(form);
form.submit();
} else {
// updates project - this part needs to be improved and optimised
$.ajax({
url: '/simulator/update_data',
type: 'POST',
contentType: 'application/json',
beforeSend(xhr) {
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
},
data: JSON.stringify({
data,
id: __logix_project_id,
image: imageData,
name: projectName,
}),
success(response) {
showMessage(`We have saved your project: ${projectName} in our servers.`);
$('.loadingIcon').fadeOut();
localStorage.removeItem('recover');
},
failure(err) {
showMessage("There was an error, we couldn't save to our servers");
$('.loadingIcon').fadeOut();
},
});
}
// Restore everything
resetup();
}

49
simulator/src/data/saveImage.js Executable file
View file

@ -0,0 +1,49 @@
/**
* Helper function to show prompt to save image
* Options - resolution, image type, view
* @param {Scope=} scope - useless though
* @category data
*/
import { generateImage } from './save';
/**
* Function called to generate a prompt to save an image
* @category data
* @param {Scope=} - circuit whose image we want
* @exports createSaveAsImgPrompt
*/
export default function createSaveAsImgPrompt(scope = globalScope) {
$('#saveImageDialog').dialog({
resizable:false,
width: 'auto',
buttons: [{
text: 'Render Circuit Image',
click() {
generateImage($('input[name=imgType]:checked').val(), $('input[name=view]:checked').val(), $('input[name=transparent]:checked').val(), $('input[name=resolution]:checked').val());
$(this).dialog('close');
},
class: "render-btn",
}],
});
$('input[name=imgType]').change(() => {
$('input[name=resolution]').prop('disabled', false);
$('input[name=transparent]').prop('disabled', false);
const imgType = $('input[name=imgType]:checked').val();
imgType == 'svg'? $('.btn-group-toggle, .download-dialog-section-3').addClass('disable') : $('.btn-group-toggle, .download-dialog-section-3, .cb-inner').removeClass('disable');
if (imgType === 'svg') {
$('input[name=resolution][value=1]').trigger('click');
$('input[name=view][value="full"]').trigger('click');
$('input[name=resolution]').prop('disabled', true);
$('input[name=view]').prop('disabled', true);
} else if (imgType !== 'png') {
$('input[name=transparent]').attr('checked', false);
$('input[name=transparent]').prop('disabled', true);
$('input[name=view]').prop('disabled', false);
$('.cb-inner').addClass('disable');
} else {
$('input[name=view]').prop("disabled", false);
$('.cb-inner').removeClass('disable');
}
});
}

46
simulator/src/data/undo.js Executable file
View file

@ -0,0 +1,46 @@
/* eslint-disable import/no-cycle */
/**
* Function to restore copy from backup
* @param {Scope=} scope - The circuit on which undo is called
* @category data
*/
import { layoutModeGet } from '../layoutMode';
import Scope, { scopeList } from '../circuit';
import { loadScope } from './load';
import { updateRestrictedElementsInScope } from '../restrictedElementDiv';
import { forceResetNodesSet } from '../engine';
/**
* Function called to generate a prompt to save an image
* @param {Scope=} - the circuit in which we want to call undo
* @category data
* @exports undo
*/
export default function undo(scope = globalScope) {
if (layoutModeGet()) return;
if (scope.backups.length < 2) return;
const backupOx = globalScope.ox;
const backupOy = globalScope.oy;
const backupScale = globalScope.scale;
globalScope.ox = 0;
globalScope.oy = 0;
const tempScope = new Scope(scope.name);
loading = true;
const undoData = scope.backups.pop();
scope.history.push(undoData);
scope.backups.length !== 0 && loadScope(tempScope, JSON.parse(scope.backups[scope.backups.length - 1]));
tempScope.backups = scope.backups;
tempScope.history = scope.history;
tempScope.id = scope.id;
tempScope.name = scope.name;
scopeList[scope.id] = tempScope;
globalScope = tempScope;
globalScope.ox = backupOx;
globalScope.oy = backupOy;
globalScope.scale = backupScale;
loading = false;
forceResetNodesSet(true);
// Updated restricted elements
updateRestrictedElementsInScope();
}
// for html file

View file

@ -0,0 +1,70 @@
## Circuit2Verilog Module
**Primary Contributors:**
1. James H - J Yeh, Ph.D.
2. Satvik Ramaprasad
## Introduction
This is an experimental module that generates Verilog netlist (structural
Verilog) given the circuit. Currently, the module generates fully functional
Verilog code for basic circuits. For a complex circuit, additional (manual) work
may need to be done in order to make it work. We are continuously improving this
module to work with more and more complex circuits.
# Algorithm
The basic algorithm is fairly straightforward. We have the circuit graph in
memory. We just need to convert this graph into Verilog netlist. It is done by
performing a DFS on the circuit graph. The DFS involves the following steps
1. Creating Verilog wires as and when required
2. Connecting Verilog wires in element instantiations
## Some background information
The different sub-circuits form a DAG (Directed Acyclic Graph) or dependency
graph. Each sub-circuit itself (called scope internally) is actually a (cyclic)
graph on its own. Therefore the Verilog generation is done in a 2 step DFS
approach. The first DFS is performed on the dependency graph. The second DFS is
done on an individual sub-circuit (scope).
## Code/Algorithm workflow
1. `exportVerilog()` - entry point
2. `exportVerilogScope()` - DFS(1) on Sub Circuits Dependency Graph
1. Set Verilog Labels for all elements
2. `generateHeader()` - Generates Module Header
3. `generateOutputList()` - Output Output List
4. `generateInputList()` - Generates Input List
5. `processGraph()` - DFS(2) on individual subcircuit/scope
1. DFS starts from inputs
2. Calls `processVerilog()` on all circuit elements (graph nodes) - resolves label names and adds neighbors to DFS stack.
3. Calls `generateVerilog()` on all circuit elements to get the final Verilog.
6. Generate Wire initializations
## Functions
**Verilog Module Functions:**
1. `verilog.exportVerilog()` - Entry point
1. `verilog.exportVerilogScope()` - Recursive DFS function on subcircuit graph
1. `verilog.processGraph()` - Iterative DFS function on subcircuit scope
1. `verilog.resetLabels()` - Resets labels in scope
1. `verilog.setLabels()` - Sets labels in scope
1. `verilog.generateHeader()` - Generates Verilog Module Header
1. `verilog.generateInputList()` - Generates Verilog Module Input List
1. `verilog.generateOutputList()` - Generates Verilog Module Output List
1. `sanitizeLabel()` - Sanitizes label for node/wire
1. `verilog.generateNodeName()` - Helper function to resolve node/wire name
**CircuitElement Functions:**
These functions can be overridden by derived classes.
1. `CircuitElement.prototype.processVerilog()` - Graph algorithm to resolve Verilog wire labels
1. `CircuitElement.prototype.verilogName()` - Generate Verilog name
1. `CircuitElement.prototype.generateVerilog()` - Generate final Verilog code
1. `CircuitElement.prototype.verilogType` - Verilog type name
1. `CircuitElement.moduleVerilog` - Custom module Verilog for elements

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 180 KiB

84
simulator/src/embed.js Normal file
View file

@ -0,0 +1,84 @@
/* eslint-disable import/no-cycle */
// Helper functions for when circuit is embedded
import { scopeList, circuitProperty } from './circuit';
import simulationArea from './simulationArea';
import {
scheduleUpdate, wireToBeCheckedSet, updateCanvasSet, gridUpdateSet,
} from './engine';
import { prevPropertyObjGet, prevPropertyObjSet } from './ux';
import { ZoomIn, ZoomOut} from './listeners';
circuitProperty.toggleFullScreen = toggleFullScreen;
$(document).ready(() => {
// Clock features
$("#clockProperty").append(
"<input type='button' class='objectPropertyAttributeEmbed custom-btn--secondary embed-fullscreen-btn' name='toggleFullScreen' value='Full Screen'> </input>"
);
$("#clockProperty").append(
`<div>Time: <input class='objectPropertyAttributeEmbed' min='50' type='number' style='width:48px' step='10' name='changeClockTime' value='${simulationArea.timePeriod}'></div>`
);
$("#clockProperty").append(
`<div>Clock: <label class='switch'> <input type='checkbox' ${
["", "checked"][simulationArea.clockEnabled + 0]
} class='objectPropertyAttributeEmbedChecked' name='changeClockEnable' > <span class='slider'></span> </label><div>`
);
// Following codes need to be removed
$('.objectPropertyAttributeEmbed').on('change keyup paste click', function () {
scheduleUpdate();
updateCanvasSet(true);
wireToBeCheckedSet(1);
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet() ; } else { circuitProperty[this.name](this.value); }
});
// Following codes need to be removed
$('.objectPropertyAttributeEmbedChecked').on('change keyup paste click', function () {
scheduleUpdate();
updateCanvasSet(true);
wireToBeCheckedSet(1);
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet(); } else { circuitProperty[this.name](this.checked); }
});
$('#zoom-in-embed').on('click', () => ZoomIn());
$('#zoom-out-embed').on('click', () => ZoomOut());
});
// Full screen toggle helper function
function toggleFullScreen(value) {
if (!getfullscreenelement()) {
GoInFullscreen(document.documentElement);
} else {
GoOutFullscreen();
}
}
// Center focus accordingly
function exitHandler() {
setTimeout(() => {
Object.keys(scopeList).forEach((id) => {
scopeList[id].centerFocus(true);
});
gridUpdateSet(true);
scheduleUpdate();
}, 100);
}
function GoInFullscreen(element) {
if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); }
}
function GoOutFullscreen() {
if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); }
}
function getfullscreenelement() {
return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
}
// Full screen Listeners
if (document.addEventListener) {
document.addEventListener('webkitfullscreenchange', exitHandler, false);
document.addEventListener('mozfullscreenchange', exitHandler, false);
document.addEventListener('fullscreenchange', exitHandler, false);
document.addEventListener('MSFullscreenChange', exitHandler, false);
}

View file

@ -0,0 +1,205 @@
/* eslint-disable import/no-cycle */
// Listeners when circuit is embedded
// Refer listeners.js
import simulationArea from './simulationArea';
import {
scheduleUpdate, update, updateSelectionsAndPane,
wireToBeCheckedSet, updatePositionSet, updateSimulationSet,
updateCanvasSet, gridUpdateSet, errorDetectedSet,
} from './engine';
import { changeScale } from './canvasApi';
import { copy, paste } from './events';
import { ZoomIn, ZoomOut} from './listeners';
var unit = 10;
export default function startListeners() {
window.addEventListener('keyup', (e) => {
scheduleUpdate(1);
if (e.keyCode == 16) {
simulationArea.shiftDown = false;
}
if (e.key == 'Meta' || e.key == 'Control') {
simulationArea.controlDown = false;
}
});
document.getElementById('simulationArea').addEventListener('mousedown', (e) => {
errorDetectedSet(false);
updateSimulationSet(true);
updatePositionSet(true);
updateCanvasSet(true);
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseDownX = Math.round(((simulationArea.mouseDownRawX - globalScope.ox) / globalScope.scale) / unit) * unit;
simulationArea.mouseDownY = Math.round(((simulationArea.mouseDownRawY - globalScope.oy) / globalScope.scale) / unit) * unit;
simulationArea.mouseDown = true;
simulationArea.oldx = globalScope.ox;
simulationArea.oldy = globalScope.oy;
e.preventDefault();
scheduleUpdate(1);
});
document.getElementById('simulationArea').addEventListener('mousemove', () => {
var ele = document.getElementById('elementName');
if (globalScope && simulationArea && simulationArea.objectList) {
var { objectList } = simulationArea;
objectList = objectList.filter((val) => val !== 'wires');
for (var i = 0; i < objectList.length; i++) {
for (var j = 0; j < globalScope[objectList[i]].length; j++) {
if (globalScope[objectList[i]][j].isHover()) {
ele.style.display = 'block';
if (objectList[i] === 'SubCircuit') {
ele.innerHTML = `Subcircuit: ${globalScope.SubCircuit[j].data.name}`;
} else {
ele.innerHTML = `CircuitElement: ${objectList[i]}`;
}
return;
}
}
}
}
ele.style.display = 'none';
document.getElementById('elementName').innerHTML = '';
});
window.addEventListener('mousemove', (e) => {
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseXf = (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale;
simulationArea.mouseYf = (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale;
simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit;
simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit;
updateCanvasSet(true);
if (simulationArea.lastSelected == globalScope.root) {
updateCanvasSet(true);
var fn;
fn = function () {
updateSelectionsAndPane();
};
scheduleUpdate(0, 20, fn);
} else {
scheduleUpdate(0, 200);
}
});
window.addEventListener('keydown', (e) => {
errorDetectedSet(false);
updateSimulationSet(true);
updatePositionSet(true);
// zoom in (+)
if (e.key == 'Meta' || e.key == 'Control') {
simulationArea.controlDown = true;
}
if (simulationArea.controlDown && (e.keyCode == 187 || e.KeyCode == 171)) {
e.preventDefault();
ZoomIn();
}
// zoom out (-)
if (simulationArea.controlDown && (e.keyCode == 189 || e.Keycode == 173)) {
e.preventDefault();
ZoomOut();
}
if (simulationArea.mouseRawX < 0 || simulationArea.mouseRawY < 0 || simulationArea.mouseRawX > width || simulationArea.mouseRawY > height) return;
scheduleUpdate(1);
updateCanvasSet(true);
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown) {
if (e.key.toString().length == 1 || e.key.toString() == 'Backspace') {
simulationArea.lastSelected.keyDown(e.key.toString());
return;
}
}
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown2) {
if (e.key.toString().length == 1) {
simulationArea.lastSelected.keyDown2(e.key.toString());
return;
}
}
// if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown3) {
// if (e.key.toString() != "Backspace" && e.key.toString() != "Delete") {
// simulationArea.lastSelected.keyDown3(e.key.toString());
// return;
// }
// }
if (e.key == 'T' || e.key == 't') {
simulationArea.changeClockTime(prompt('Enter Time:'));
}
});
document.getElementById('simulationArea').addEventListener('dblclick', (e) => {
scheduleUpdate(2);
if (simulationArea.lastSelected && simulationArea.lastSelected.dblclick !== undefined) {
simulationArea.lastSelected.dblclick();
}
});
window.addEventListener('mouseup', (e) => {
simulationArea.mouseDown = false;
errorDetectedSet(false);
updateSimulationSet(true);
updatePositionSet(true);
updateCanvasSet(true);
gridUpdateSet(true);
wireToBeCheckedSet(1);
scheduleUpdate(1);
});
window.addEventListener('mousedown', function (e) {
this.focus();
});
document.getElementById('simulationArea').addEventListener('mousewheel', MouseScroll);
document.getElementById('simulationArea').addEventListener('DOMMouseScroll', MouseScroll);
function MouseScroll(event) {
updateCanvasSet(true);
event.preventDefault();
var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail;
var scrolledUp = deltaY < 0;
var scrolledDown = deltaY > 0;
if (event.ctrlKey) {
if (scrolledUp && globalScope.scale > 0.5 * DPR) {
changeScale(-0.1 * DPR);
}
if (scrolledDown && globalScope.scale < 4 * DPR) {
changeScale(0.1 * DPR);
}
} else {
if (scrolledUp && globalScope.scale < 4 * DPR) {
changeScale(0.1 * DPR);
}
if (scrolledDown && globalScope.scale > 0.5 * DPR) {
changeScale(-0.1 * DPR);
}
}
updateCanvasSet(true);
gridUpdateSet(true);
update(); // Schedule update not working, this is INEFFICENT
}
}
var isIe = (navigator.userAgent.toLowerCase().indexOf('msie') != -1
|| navigator.userAgent.toLowerCase().indexOf('trident') != -1);

515
simulator/src/engine.js Executable file
View file

@ -0,0 +1,515 @@
/* eslint-disable import/no-cycle */
/* eslint-disable no-use-before-define */
/* eslint-disable no-continue */
/* eslint-disable no-param-reassign */
/* eslint-disable no-bitwise */
import { layoutModeGet, layoutUpdate } from './layoutMode';
import plotArea from './plotArea';
import simulationArea from './simulationArea';
import {
dots, canvasMessage, findDimensions, rect2,
} from './canvasApi';
import { showProperties, prevPropertyObjGet } from './ux';
import { showError } from './utils';
import miniMapArea from './minimap';
import { resetup } from './setup';
import { verilogModeGet } from './Verilog2CV';
/**
* Core of the simulation and rendering algorithm.
*/
/**
* @type {number} engine
* @category engine
*/
var wireToBeChecked = 0;
/**
* Used to set wireChecked boolean which updates wires in UI if true (or 1). 2 if some problem and it is handled.
* @param {number} param - value of wirechecked
* @category engine
*/
export function wireToBeCheckedSet(param) {
wireToBeChecked = param;
}
/**
* scheduleUpdate() will be called if true
* @type {boolean}
* @category engine
*/
var willBeUpdated = false;
/**
* used to set willBeUpdated variable
* @type {boolean}
* @category engine
* @category engine
*/
export function willBeUpdatedSet(param) {
willBeUpdated = param;
}
/**
* true if we have an element selected and
* is used when we are paning the grid.
* @type {boolean}
* @category engine
*/
var objectSelection = false;
/**
* used to set the value of object selection,
* @param {boolean} param
* @category engine
*/
export function objectSelectionSet(param) {
objectSelection = param;
}
/**
* Flag for updating position
* @type {boolean}
* @category engine
*/
var updatePosition = true;
/**
* used to set the value of updatePosition.
* @param {boolean} param
* @category engine
*/
export function updatePositionSet(param) {
updatePosition = param;
}
/**
* Flag for updating simulation
* @type {boolean}
* @category engine
*/
var updateSimulation = true;
/**
* used to set the value of updateSimulation.
* @param {boolean} param
* @category engine
*/
export function updateSimulationSet(param) {
updateSimulation = param;
}
/**
* Flag for rendering
* @type {boolean}
* @category engine
*/
var updateCanvas = true;
/**
* used to set the value of updateCanvas.
* @param {boolean} param
* @category engine
*/
export function updateCanvasSet(param) {
updateCanvas = param;
}
/**
* Flag for updating grid
* @type {boolean}
* @category engine
*/
var gridUpdate = true;
/**
* used to set gridUpdate
* @param {boolean} param
* @category engine
*/
export function gridUpdateSet(param) {
gridUpdate = param;
}
/**
* used to get gridUpdate
* @return {boolean}
* @category engine
*/
export function gridUpdateGet() {
return gridUpdate;
}
/**
* Flag for updating grid
* @type {boolean}
* @category engine
*/
var forceResetNodes = true;
/**
* used to set forceResetNodes
* @param {boolean} param
* @category engine
*/
export function forceResetNodesSet(param) {
forceResetNodes = param;
}
/**
* Flag for updating grid
* @type {boolean}
* @category engine
*/
var errorDetected = false;
/**
* used to set errorDetected
* @param {boolean} param
* @category engine
*/
export function errorDetectedSet(param) {
errorDetected = param;
}
/**
* used to set errorDetected
* @returns {boolean} errorDetected
* @category engine
*/
export function errorDetectedGet() {
return errorDetected;
}
/**
* details of where and what canvas message has to be shown.
* @type {Object}
* @property {number} x - x cordinate of message
* @property {number} y - x cordinate of message
* @property {number} string - the message
* @category engine
*/
export var canvasMessageData = {
x: undefined,
y: undefined,
string: undefined,
};
/**
* Flag for updating subCircuits
* @type {boolean}
* @category engine
*/
var updateSubcircuit = true;
/**
* used to set updateSubcircuit
* @param {boolean} param
* @category engine
*/
export function updateSubcircuitSet(param) {
if (updateSubcircuit != param) {
updateSubcircuit = param;
return true;
}
updateSubcircuit = param;
return false;
}
/**
* turn light mode on
* @param {boolean} val -- new value for light mode
* @category engine
*/
export function changeLightMode(val) {
if (!val && lightMode) {
lightMode = false;
DPR = window.devicePixelRatio || 1;
globalScope.scale *= DPR;
} else if (val && !lightMode) {
lightMode = true;
globalScope.scale /= DPR;
DPR = 1
$('#miniMap').fadeOut('fast');
}
resetup();
}
/**
* Function to render Canvas according th renderupdate order
* @param {Scope} scope - The circuit whose canvas we want to render
* @category engine
*/
export function renderCanvas(scope) {
if (layoutModeGet() || verilogModeGet()) { // Different Algorithm
return;
}
var ctx = simulationArea.context;
// Reset canvas
simulationArea.clear();
// Update Grid
if (gridUpdate) {
gridUpdateSet(false);
dots();
}
canvasMessageData = {
x: undefined,
y: undefined,
string: undefined,
}; // Globally set in draw fn ()
// Render objects
for (let i = 0; i < renderOrder.length; i++) {
for (var j = 0; j < scope[renderOrder[i]].length; j++) { scope[renderOrder[i]][j].draw(); }
}
// Show any message
if (canvasMessageData.string !== undefined) {
canvasMessage(ctx, canvasMessageData.string, canvasMessageData.x, canvasMessageData.y);
}
// If multiple object selections are going on, show selected area
if (objectSelection) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.fillStyle = 'rgba(0,0,0,0.1)';
rect2(ctx, simulationArea.mouseDownX, simulationArea.mouseDownY, simulationArea.mouseX - simulationArea.mouseDownX, simulationArea.mouseY - simulationArea.mouseDownY, 0, 0, 'RIGHT');
ctx.stroke();
ctx.fill();
}
if (simulationArea.hover !== undefined) {
simulationArea.canvas.style.cursor = 'pointer';
} else if (simulationArea.mouseDown) {
simulationArea.canvas.style.cursor = 'grabbing';
} else {
simulationArea.canvas.style.cursor = 'default';
}
}
/**
* Function to move multiple objects and panes window
* deselected using dblclick right now (PR open for esc key)
* @param {Scope=} scope - the circuit in which we are selecting stuff
* @category engine
*/
export function updateSelectionsAndPane(scope = globalScope) {
if (!simulationArea.selected && simulationArea.mouseDown) {
simulationArea.selected = true;
simulationArea.lastSelected = scope.root;
simulationArea.hover = scope.root;
// Selecting multiple objects
if (simulationArea.shiftDown) {
objectSelectionSet(true);
} else if (!embed) {
findDimensions(scope);
miniMapArea.setup();
$('#miniMap').show();
}
} else if (simulationArea.lastSelected === scope.root && simulationArea.mouseDown) {
// pane canvas to give an idea of grid moving
if (!objectSelection) {
globalScope.ox = (simulationArea.mouseRawX - simulationArea.mouseDownRawX) + simulationArea.oldx;
globalScope.oy = (simulationArea.mouseRawY - simulationArea.mouseDownRawY) + simulationArea.oldy;
globalScope.ox = Math.round(globalScope.ox);
globalScope.oy = Math.round(globalScope.oy);
gridUpdateSet(true);
if (!embed && !lightMode) miniMapArea.setup();
} else {
// idea: kind of empty
}
} else if (simulationArea.lastSelected === scope.root) {
/*
Select multiple objects by adding them to the array
simulationArea.multipleObjectSelections when we select
using shift + mouse movement to select an area but
not shift + click
*/
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
if (objectSelection) {
objectSelectionSet(false);
var x1 = simulationArea.mouseDownX;
var x2 = simulationArea.mouseX;
var y1 = simulationArea.mouseDownY;
var y2 = simulationArea.mouseY;
// Sort those four points to make a selection pane
if (x1 > x2) {
const temp = x1;
x1 = x2;
x2 = temp;
}
if (y1 > y2) {
const temp = y1;
y1 = y2;
y2 = temp;
}
// Select the objects, push them into a list
for (let i = 0; i < updateOrder.length; i++) {
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
var obj = scope[updateOrder[i]][j];
if (simulationArea.multipleObjectSelections.contains(obj)) continue;
var x; var
y;
if (obj.objectType === 'Node') {
x = obj.absX();
y = obj.absY();
} else if (obj.objectType !== 'Wire') {
x = obj.x;
y = obj.y;
} else {
continue;
}
if (x > x1 && x < x2 && y > y1 && y < y2) {
simulationArea.multipleObjectSelections.push(obj);
}
}
}
}
}
}
/**
* Main fn that resolves circuit using event driven simulation
* All inputs are added to a scope using scope.addinput() and
* the simulation starts to play.
* @param {Scope=} scope - the circuit we want to simulate
* @param {boolean} resetNodes - boolean to reset all nodes
* @category engine
*/
export function play(scope = globalScope, resetNodes = false) {
if (errorDetected) return; // Don't simulate until error is fixed
if (loading === true) return; // Don't simulate until loaded
simulationArea.simulationQueue.reset();
plotArea.setExecutionTime(); // Waveform thing
// Reset Nodes if required
if (resetNodes || forceResetNodes) {
scope.reset();
simulationArea.simulationQueue.reset();
forceResetNodesSet(false);
}
// To store list of circuitselements that have shown contention but kept temporarily
// Mainly to resolve tristate bus issues
simulationArea.contentionPending = [];
// add inputs to the simulation queue
scope.addInputs();
// to check if we have infinite loop in circuit
let stepCount = 0;
let elem;
while (!simulationArea.simulationQueue.isEmpty()) {
if (errorDetected) {
simulationArea.simulationQueue.reset();
return;
}
elem = simulationArea.simulationQueue.pop();
elem.resolve();
stepCount++;
if (stepCount > 1000000) { // Cyclic or infinite Circuit Detection
showError('Simulation Stack limit exceeded: maybe due to cyclic paths or contention');
errorDetectedSet(true);
forceResetNodesSet(true);
}
}
// Check for TriState Contentions
if (simulationArea.contentionPending.length) {
showError('Contention at TriState');
forceResetNodesSet(true);
errorDetectedSet(true);
}
}
/**
* Function to check for any UI update, it is throttled by time
* @param {number=} count - this is used to force update
* @param {number=} time - the time throttling parameter
* @param {function} fn - function to run before updating UI
* @category engine
*/
export function scheduleUpdate(count = 0, time = 100, fn) {
if (lightMode) time *= 5;
var updateFn = layoutModeGet() ? layoutUpdate : update;
if (count) { // Force update
updateFn();
for (let i = 0; i < count; i++) { setTimeout(updateFn, 10 + 50 * i); }
}
if (willBeUpdated) return; // Throttling
willBeUpdatedSet(true);
// Call a function before update ..
if (fn) {
setTimeout(() => {
fn();
updateFn();
}, time);
} else setTimeout(updateFn, time);
}
/**
* fn that calls update on everything else. If any change
* is there, it resolves the circuit and draws it again.
* Also updates simulations, selection, minimap, resolves
* circuit and redraws canvas if required.
* @param {Scope=} scope - the circuit to be updated
* @param {boolean=} updateEverything - if true we update the wires, nodes and modules
* @category engine
*/
export function update(scope = globalScope, updateEverything = false) {
willBeUpdatedSet(false);
if (loading === true || layoutModeGet()) return;
var updated = false;
simulationArea.hover = undefined;
// Update wires
if (wireToBeChecked || updateEverything) {
if (wireToBeChecked === 2) wireToBeChecked = 0; // this required due to timing issues
else wireToBeChecked++;
// WHY IS THIS REQUIRED ???? we are checking inside wire ALSO
// Idea: we can just call length again instead of doing it during loop.
var prevLength = scope.wires.length;
for (let i = 0; i < scope.wires.length; i++) {
scope.wires[i].checkConnections();
if (scope.wires.length !== prevLength) {
prevLength--;
i--;
}
}
scheduleUpdate();
}
// Update subcircuits
if (updateSubcircuit || updateEverything) {
for (let i = 0; i < scope.SubCircuit.length; i++) { scope.SubCircuit[i].reset(); }
updateSubcircuitSet(false);
}
// Update UI position
if (updatePosition || updateEverything) {
for (let i = 0; i < updateOrder.length; i++) {
for (let j = 0; j < scope[updateOrder[i]].length; j++) {
updated |= scope[updateOrder[i]][j].update();
}
}
}
// Updates multiple objectselections and panes window
if (updatePosition || updateEverything) {
updateSelectionsAndPane(scope);
}
// Update MiniMap
if (!embed && simulationArea.mouseDown && simulationArea.lastSelected && simulationArea.lastSelected !== globalScope.root) {
if (!lightMode) { $('#miniMap').fadeOut('fast'); }
}
// Run simulation
if (updateSimulation) {
play();
}
// Show properties of selected element
if (!embed && prevPropertyObjGet() !== simulationArea.lastSelected) {
if (simulationArea.lastSelected && simulationArea.lastSelected.objectType !== 'Wire') {
// ideas: why show properties of project in Nodes but not wires?
showProperties(simulationArea.lastSelected);
} else {
// hideProperties();
}
}
// Draw, render everything
if (updateCanvas) {
renderCanvas(scope);
}
updateSimulationSet(false);
updateCanvas = false;
updatePositionSet(false);
}

103
simulator/src/eventQueue.js Executable file
View file

@ -0,0 +1,103 @@
/**
* Event Queue is simply a priority Queue, basic implementation O(n^2).
* @category eventQueue
*/
export default class EventQueue {
constructor(size) {
this.size = size;
this.queue = new Array(size);
this.frontIndex = 0;
this.time = 0;
}
/**
* @param {CircuitElement} obj - the elemnt to be added
* @param {number} delay - the delay in adding an object to queue
*/
add(obj, delay) {
if (obj.queueProperties.inQueue) {
obj.queueProperties.time = this.time + (delay || obj.propagationDelay);
let i = obj.queueProperties.index;
while (i > 0 && obj.queueProperties.time > this.queue[i - 1].queueProperties.time) {
this.swap(i, i - 1);
i--;
}
i = obj.queueProperties.index;
while (i < this.frontIndex - 1 && obj.queueProperties.time < this.queue[i + 1].queueProperties.time) {
this.swap(i, i + 1);
i++;
}
return;
}
if (this.frontIndex == this.size) throw 'EventQueue size exceeded';
this.queue[this.frontIndex] = obj;
// obj.queueProperties.time=obj.propagationDelay;
obj.queueProperties.time = this.time + (delay || obj.propagationDelay);
obj.queueProperties.index = this.frontIndex;
this.frontIndex++;
obj.queueProperties.inQueue = true;
let i = obj.queueProperties.index;
while (i > 0 && obj.queueProperties.time > this.queue[i - 1].queueProperties.time) {
this.swap(i, i - 1);
i--;
}
}
/**
* To add without any delay.
* @param {CircuitElement} obj - the object to be added
*/
addImmediate(obj) {
this.queue[this.frontIndex] = obj;
obj.queueProperties.time = this.time;
obj.queueProperties.index = this.frontIndex;
obj.queueProperties.inQueue = true;
this.frontIndex++;
}
/**
* Function to swap two objects in queue.
* @param {number} v1
* @param {number} v2
*/
swap(v1, v2) {
const obj1 = this.queue[v1];
obj1.queueProperties.index = v2;
const obj2 = this.queue[v2];
obj2.queueProperties.index = v1;
this.queue[v1] = obj2;
this.queue[v2] = obj1;
}
/**
* function to pop element from queue.
*/
pop() {
if (this.isEmpty()) throw 'Queue Empty';
this.frontIndex--;
const obj = this.queue[this.frontIndex];
this.time = obj.queueProperties.time;
obj.queueProperties.inQueue = false;
return obj;
}
/**
* function to reset queue.
*/
reset() {
for (let i = 0; i < this.frontIndex; i++) this.queue[i].queueProperties.inQueue = false;
this.time = 0;
this.frontIndex = 0;
}
/**
* function to check if empty queue.
*/
isEmpty() {
return this.frontIndex == 0;
}
}

298
simulator/src/events.js Executable file
View file

@ -0,0 +1,298 @@
/* eslint-disable import/no-cycle */
import Scope, { scopeList, switchCircuit, newCircuit } from './circuit';
import { loadScope } from './data/load';
import {
scheduleUpdate, updateSimulationSet, updateSubcircuitSet, forceResetNodesSet,
} from './engine';
import { backUp } from './data/backupCircuit';
import { getNextPosition } from './modules';
import { generateId } from './utils';
import simulationArea from './simulationArea';
/**
* Helper function to paste
* @param {JSON} copyData - the data to be pasted
* @category events
*/
export function paste(copyData) {
if (copyData === undefined) return;
var data = JSON.parse(copyData);
if (!data.logixClipBoardData) return;
var currentScopeId = globalScope.id;
for (let i = 0; i < data.scopes.length; i++) {
if (scopeList[data.scopes[i].id] === undefined) {
var isVerilogCircuit = false;
var isMainCircuit = false;
if(data.scopes[i].verilogMetadata) {
isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit;
isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit;
}
var scope = newCircuit(data.scopes[i].name, data.scopes[i].id, isVerilogCircuit, isMainCircuit);
loadScope(scope, data.scopes[i]);
scopeList[data.scopes[i].id] = scope;
}
}
switchCircuit(currentScopeId);
var tempScope = new Scope(globalScope.name, globalScope.id);
var oldOx = globalScope.ox;
var oldOy = globalScope.oy;
var oldScale = globalScope.scale;
loadScope(tempScope, data);
var prevLength = tempScope.allNodes.length;
for (let i = 0; i < tempScope.allNodes.length; i++) {
tempScope.allNodes[i].checkDeleted();
if (tempScope.allNodes.length != prevLength) {
prevLength--;
i--;
}
}
var approxX = 0;
var approxY = 0;
var count = 0;
for (let i = 0; i < updateOrder.length; i++) {
for (let j = 0; j < tempScope[updateOrder[i]].length; j++) {
const obj = tempScope[updateOrder[i]][j];
obj.updateScope(globalScope);
if (obj.objectType != 'Wire') {
approxX += obj.x;
approxY += obj.y;
count++;
}
}
}
for (let j = 0; j < tempScope.CircuitElement.length; j++) {
const obj = tempScope.CircuitElement[j];
obj.updateScope(globalScope);
}
approxX /= count;
approxY /= count;
approxX = Math.round(approxX / 10) * 10;
approxY = Math.round(approxY / 10) * 10;
for (let i = 0; i < updateOrder.length; i++) {
for (let j = 0; j < tempScope[updateOrder[i]].length; j++) {
const obj = tempScope[updateOrder[i]][j];
if (obj.objectType !== 'Wire') {
obj.x += simulationArea.mouseX - approxX;
obj.y += simulationArea.mouseY - approxY;
}
}
}
Object.keys(tempScope).forEach((l) => {
if (tempScope[l] instanceof Array && l !== 'objects' && l !== 'CircuitElement') {
globalScope[l].extend(tempScope[l]);
}
});
for (let i = 0; i < tempScope.Input.length; i++) {
tempScope.Input[i].layoutProperties.y = getNextPosition(0, globalScope);
tempScope.Input[i].layoutProperties.id = generateId();
}
for (let i = 0; i < tempScope.Output.length; i++) {
tempScope.Output[i].layoutProperties.x = globalScope.layout.width;
tempScope.Output[i].layoutProperties.id = generateId();
tempScope.Output[i].layoutProperties.y = getNextPosition(globalScope.layout.width, globalScope);
}
var canvasUpdate = true;
updateSimulationSet(true);
updateSubcircuitSet(true);
scheduleUpdate();
globalScope.ox = oldOx;
globalScope.oy = oldOy;
globalScope.scale = oldScale;
forceResetNodesSet(true);
}
/**
* Helper function for cut
* @param {JSON} copyList - The selected elements
* @category events
*/
export function cut(copyList) {
if (copyList.length === 0) return;
var tempScope = new Scope(globalScope.name, globalScope.id);
var oldOx = globalScope.ox;
var oldOy = globalScope.oy;
var oldScale = globalScope.scale;
d = backUp(globalScope);
loadScope(tempScope, d);
scopeList[tempScope.id] = tempScope;
for (let i = 0; i < copyList.length; i++) {
const obj = copyList[i];
if (obj.objectType === 'Node') obj.objectType = 'allNodes';
for (let j = 0; j < tempScope[obj.objectType].length; j++) {
if (tempScope[obj.objectType][j].x === obj.x && tempScope[obj.objectType][j].y === obj.y && (obj.objectType != 'Node' || obj.type === 2)) {
tempScope[obj.objectType][j].delete();
break;
}
}
}
tempScope.backups = globalScope.backups;
for (let i = 0; i < updateOrder.length; i++) {
let prevLength = globalScope[updateOrder[i]].length; // LOL length of list will reduce automatically when deletion starts
for (let j = 0; j < globalScope[updateOrder[i]].length; j++) {
const obj = globalScope[updateOrder[i]][j];
if (obj.objectType != 'Wire') { // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){
if (!copyList.contains(globalScope[updateOrder[i]][j])) {
globalScope[updateOrder[i]][j].cleanDelete();
}
}
if (globalScope[updateOrder[i]].length != prevLength) {
prevLength--;
j--;
}
}
}
var prevLength = globalScope.wires.length;
for (let i = 0; i < globalScope.wires.length; i++) {
globalScope.wires[i].checkConnections();
if (globalScope.wires.length != prevLength) {
prevLength--;
i--;
}
}
updateSimulationSet(true);
var data = backUp(globalScope);
data.logixClipBoardData = true;
var dependencyList = globalScope.getDependencies();
data.dependencies = {};
Object.keys(dependencyList).forEach((dependency) => {
data.dependencies[dependency] = backUp(scopeList[dependency]);
});
data.logixClipBoardData = true;
data = JSON.stringify(data);
simulationArea.multipleObjectSelections = []; // copyList.slice();
simulationArea.copyList = []; // copyList.slice();
var canvasUpdate = true;
updateSimulationSet(true);
globalScope = tempScope;
scheduleUpdate();
globalScope.ox = oldOx;
globalScope.oy = oldOy;
globalScope.scale = oldScale;
forceResetNodesSet(true);
// eslint-disable-next-line consistent-return
return data;
}
/**
* Helper function for copy
* @param {JSON} copyList - The data to copied
* @param {boolean} cutflag - flase if we want to copy
* @category events
*/
export function copy(copyList, cutflag = false) {
if (copyList.length === 0) return;
var tempScope = new Scope(globalScope.name, globalScope.id);
var oldOx = globalScope.ox;
var oldOy = globalScope.oy;
var oldScale = globalScope.scale;
var d = backUp(globalScope);
loadScope(tempScope, d);
scopeList[tempScope.id] = tempScope;
if (cutflag) {
for (let i = 0; i < copyList.length; i++) {
const obj = copyList[i];
if (obj.objectType === 'Node') obj.objectType = 'allNodes';
for (let j = 0; j < tempScope[obj.objectType].length; j++) {
if (tempScope[obj.objectType][j].x === obj.x && tempScope[obj.objectType][j].y === obj.y && (obj.objectType != 'Node' || obj.type === 2)) {
tempScope[obj.objectType][j].delete();
break;
}
}
}
}
tempScope.backups = globalScope.backups;
for (let i = 0; i < updateOrder.length; i++) {
let prevLength = globalScope[updateOrder[i]].length; // LOL length of list will reduce automatically when deletion starts
for (let j = 0; j < globalScope[updateOrder[i]].length; j++) {
const obj = globalScope[updateOrder[i]][j];
if (obj.objectType != 'Wire') { // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){
if (!copyList.contains(globalScope[updateOrder[i]][j])) {
globalScope[updateOrder[i]][j].cleanDelete();
}
}
if (globalScope[updateOrder[i]].length != prevLength) {
prevLength--;
j--;
}
}
}
var prevLength = globalScope.wires.length;
for (let i = 0; i < globalScope.wires.length; i++) {
globalScope.wires[i].checkConnections();
if (globalScope.wires.length != prevLength) {
prevLength--;
i--;
}
}
updateSimulationSet(true);
var data = backUp(globalScope);
data.scopes = [];
var dependencyList = {};
var requiredDependencies = globalScope.getDependencies();
var completed = {};
Object.keys(scopeList).forEach((id) => {
dependencyList[id] = scopeList[id].getDependencies();
});
function saveScope(id) {
if (completed[id]) return;
for (let i = 0; i < dependencyList[id].length; i++) { saveScope(dependencyList[id][i]); }
completed[id] = true;
data.scopes.push(backUp(scopeList[id]));
}
for (let i = 0; i < requiredDependencies.length; i++) { saveScope(requiredDependencies[i]); }
data.logixClipBoardData = true;
data = JSON.stringify(data);
simulationArea.multipleObjectSelections = []; // copyList.slice();
simulationArea.copyList = []; // copyList.slice();
var canvasUpdate = true;
updateSimulationSet(true);
globalScope = tempScope;
scheduleUpdate();
globalScope.ox = oldOx;
globalScope.oy = oldOy;
globalScope.scale = oldScale;
forceResetNodesSet(true);
// needs to be fixed
// eslint-disable-next-line consistent-return
return data;
}
/**
* Function selects all the elements from the scope
* @category events
*/
export function selectAll(scope = globalScope) {
moduleList.forEach((val, _, __) => {
if (scope.hasOwnProperty(val)) {
simulationArea.multipleObjectSelections.push(...scope[val]);
}
});
if (scope.nodes) {
simulationArea.multipleObjectSelections.push(...scope.nodes);
}
}

View file

@ -0,0 +1,30 @@
/**Add more elements here, along with a valid value for key
* Elements keys must have the same name as their ID
**/
export const defaultKeys = {
"New Circuit": "Shift + N",
"Save Online": "Ctrl + S",
"Save Offline": "Ctrl + Alt + S",
"Download as Image": "Ctrl + D",
"Open Offline": "Ctrl + O",
"Insert Sub-circuit": "Shift + C",
"Combinational Analysis": "Shift + A",
// "Start Plot": "Ctrl + P",
"Direction Up": "Up",
"Direction Down": "Down",
"Direction Left": "Left",
"Direction Right": "Right",
"Insert Label": "Ctrl + L",
"Label Direction Up": "Alt + Up",
"Label Direction Down": "Alt + Down",
"Label Direction Left": "Alt + Left",
"Label Direction Right": "Alt + Right",
"Move Element Up": "Shift + Up",
"Move Element Down": "Shift + Down",
"Move Element Left": "Shift + Left",
"Move Element Right": "Shift + Right",
"Hotkey Preference": "F8",
"Open Documentation": "F1",
};

View file

@ -0,0 +1,45 @@
The feature includes two libraries:
1. shortcuts.plugin.js: It consists of a few function(s)
a. Add
b. Remove
c. RemoveAll
a. function Add takes 3 arguments:
shortcut_combination: <string> eg "Ctrl + X"
callback: <func> reference to a function, which will be invoked when the above combination is detected
opt: <object> some optional parameters such as propagation, type of keyevent, etc
b. function Remove: removes a particular shortcut provided in the param.
c. function RemoveAll: takes no param removes all shortcuts.
supported format:
* Modifier
* Modifier + key
* Single key
2. normalizer.plugin.js: is used in keyBinder.js (line 73), to detect the shortcut_combination in string format
which is then passed to the shortcuts plugin.
MAIN FILEs:
keyBinder.js is the controller, it performs operation that are defined in other files (model, view)
It consist of the keydown listener which detects while user is customizing the keys from the preference panel,
* checks for restrictions
* warns override
* restricts non-overridable combinations
* Others
USAGE:
TO ADD SHORCUTS:
#1 add the shortcut in the defaultKeys.json file
#2 Assign the callback in addShorcut.js:
* the callback should be a func reference,if function to be invoked is already defined in the codebase just set it as it is. (for some func reference may not work, refer next step)
*for callback not present in the codebase, or is present but doesn't work as intended, defined the function that will invoke the required callback in action.js, note that callback must be func reference hence define it as a higher-order func if necessary in action.js
TO REMOVE SHORCUTS:
#1. Remove the option from defaultKeys.json
#2. Remove the case from addShortcut.js
RESTRICTION:
To add restriction to any combination, mention it in utils.js -> checkRestricted func, which contains an array that consists of restricted combination, push to the array.
*The feature make uses of localstorage for persistence, only defaultkeys else userkeys will be present in localstorage not both.
On load IFFE func on keyBinder.js (line 111) will check for userKeys, if found it it will be set, else defaultkeys will be set.

View file

@ -0,0 +1,145 @@
import {
editPanel,
heading,
markUp,
closeEdit,
submit,
updateHTML,
override
} from './view/panel.ui';
import { setDefault, checkUpdate, addKeys, warnOverride } from './model/actions';
import { KeyCode } from './model/normalize/normalizer.plugin.js'
import {checkRestricted} from './model/utils.js'
//** keyBinder dialog */
export function keyBinder() {
$("#customShortcutDialog").append(editPanel);
$("#customShortcutDialog").append(heading);
$("#customShortcutDialog").append(markUp);
$("#customShortcut").on('click',() => {
closeEdit();
$("#customShortcutDialog").dialog({
resizable: false,
buttons: [
{
text: "Reset to default",
click: () => {
if (
confirm(
"Remove all custom keys & set the default keys?"
)
)
setDefault();
},
id: "resetDefault",
},
{
text: "Save",
click: () => {
submit();
$("#customShortcutDialog").dialog("close");
},
id: "submitBtn",
},
],
});
$("#customShortcutDialog").css("display", "flex");
});
//** targetPref is assigned to the target key option to be edited */
let targetPref = null;
$("#preference").on('click',(e) => {
$("#pressedKeys").text("");
$("#warning").text("");
$("#edit").css("border", "none");
$("#edit").css("display", "block");
$($("#edit")).focus();
[, targetPref] = e.target.closest("div").children;
});
//*** Modifiers restriction enabled here */
//*** below fn works in the edit panel where user enters key combo,
//*** responsible for checking duplicate entries, overriding entries, checking restricted keys, arranging keys in
//*** proper order, validating key combos */
$("#edit").keydown((e) => {
e = e || window.event;
e.stopPropagation();
e.preventDefault();
var k = KeyCode;
let modifiers = ["CTRL", "ALT", "SHIFT", "META"];
$("#edit").css("animation", "none");
$("#warning").text("");
if (e.keyCode === 27) closeEdit();
if (e.keyCode === 13) {
if ($("#pressedKeys").text() === "") {
$("#warning").text("Please enter some key(s)");
$("#edit").css("animation", "shake .3s linear");
return;
}
if (!checkRestricted($("#pressedKeys").text())) {
override($("#pressedKeys").text());
targetPref.innerText = $("#pressedKeys").text();
$("#pressedKeys").text("");
$("#edit").css("display", "none");
} else {
$("#warning").text("Please enter different key(s).");
$("#edit").css("animation", "shake .3s linear");
$("#pressedKeys").text("");
}
}
const currentKey =
k.hot_key(k.translate_event(e)).split("+").join(" + ") !== "Enter"
? k.hot_key(k.translate_event(e)).split("+").join(" + ")
: "";
if (
$("#pressedKeys").text().split(" + ").length === 2 &&
!modifiers.includes(currentKey) &&
modifiers.includes($("#pressedKeys").text().split(" + ")[1])
) {
$("#pressedKeys").append(` + ${currentKey}`);
} else if (modifiers.includes($("#pressedKeys").text())) {
modifiers = modifiers.filter((mod) => mod === $("#pressedKeys").text());
if (!modifiers.includes(currentKey)) {
$("#pressedKeys").append(` + ${currentKey}`);
}
} else {
$("#pressedKeys").text("");
$("#pressedKeys").text(currentKey);
}
if (!$("#pressedKeys").text()) {
$("#pressedKeys").text(currentKey);
}
if (
($("#pressedKeys").text().split(" + ").length === 2 &&
["Ctrl", "Meta"].includes(
$("#pressedKeys").text().split(" + ")[1]
)) ||
($("#pressedKeys").text().split(" + ")[0] === "Alt" &&
$("#pressedKeys").text().split(" + ")[1] === "Shift")
) {
$("#pressedKeys").text(
$("#pressedKeys").text().split(" + ").reverse().join(" + ")
);
}
warnOverride($("#pressedKeys").text(), targetPref);
if (checkRestricted($("#pressedKeys").text())) {
$("#warning").text("The above combination is a system default shortcut & cannot be set.");
}
});
//** if users closes hotkey dialog by making changes & not saving them, fn will fallback to previous state */
$('div#customShortcutDialog').on('dialogclose', function(event) {
if (localStorage.userKeys) {
updateHTML("user");
} else updateHTML("default");
});
// Set up shortcuts
if (localStorage.userKeys) {
checkUpdate();
addKeys("user");
} else setDefault();
}

View file

@ -0,0 +1,194 @@
import { defaultKeys } from '../defaultKeys';
import { addShortcut } from './addShortcut';
import { updateHTML } from '../view/panel.ui';
import simulationArea from '../../simulationArea';
import {
scheduleUpdate, update, updateSelectionsAndPane,
wireToBeCheckedSet, updatePositionSet, updateSimulationSet,
updateCanvasSet, gridUpdateSet, errorDetectedSet,
} from '../../engine';
import {getOS} from './utils.js'
import {shortcut} from './shortcuts.plugin.js'
/**
* Function used to add or change keys user or default
* grabs the keycombo from localstorage &
* calls the addShortcut function in a loop to bind them
* @param {string} mode - user custom keys or default keys
*/
export const addKeys = (mode) => {
shortcut.removeAll();
if (mode === "user") {
localStorage.removeItem("defaultKeys");
let userKeys = localStorage.get("userKeys");
for (let pref in userKeys) {
let key = userKeys[pref];
key = key.split(" ").join("");
addShortcut(key, pref);
}
updateHTML("user");
} else if (mode == "default") {
if (localStorage.userKeys) localStorage.removeItem("userKeys");
let defaultKeys = localStorage.get("defaultKeys");
for (let pref in defaultKeys) {
let key = defaultKeys[pref];
key = key.split(" ").join("");
addShortcut(key, pref);
}
updateHTML("default");
}
};
/**
* Function used to check if new keys are added, adds missing keys if added
*/
export const checkUpdate = () => {
const userK = localStorage.get("userKeys");
if (Object.size(userK) !== Object.size(defaultKeys)) {
for (const [key, value] of Object.entries(defaultKeys)) {
if (!Object.keys(userK).includes(key)) {
userK[key] = value;
}
}
localStorage.set("userKeys", userK);
} else {
return;
}
};
/**
* Function used to set userKeys, grabs the keycombo from the panel UI
* sets it to the localStorage & cals addKeys
* removes the defaultkeys from localStorage
*/
export const setUserKeys = () => {
if (localStorage.defaultKeys) localStorage.removeItem('defaultKeys');
let userKeys = {};
let x = 0;
while ($("#preference").children()[x]) {
userKeys[$("#preference").children()[x].children[1].children[0].innerText] = $(
"#preference"
).children()[x].children[1].children[1].innerText;
x++;
}
localStorage.set("userKeys", userKeys);
addKeys("user");
};
/**
* Function used to set defaultKeys, grabs the keycombo from the defaultkeys metadata
* sets it to the localStorage & cals addKeys
* removes the userkeys from localStorage if present
* also checks for OS type
*/
export const setDefault = () => {
if (localStorage.userKeys) localStorage.removeItem('userKeys');
if (getOS() === "MacOS") {
const macDefaultKeys = {};
for (let [key, value] of Object.entries(defaultKeys)) {
if (value.split(" + ")[0] == "Ctrl");
macDefaultKeys[key] =
value.split(" + ")[0] == "Ctrl"
? value.replace("Ctrl", "Meta")
: value;
localStorage.set("defaultKeys", macDefaultKeys);
}
} else {
localStorage.set("defaultKeys", defaultKeys); //TODO add a confirmation alert
}
addKeys("default");
};
/**
* function to check if user entered keys are already assigned to other key
* gives a warning message if keys already assigned
* @param {string} combo the key combo
* @param {string} target the target option of the panel
*/
export const warnOverride = (combo, target) => {
let x = 0;
while ($("#preference").children()[x]) {
if (
$("#preference").children()[x].children[1].children[1].innerText ===
combo &&
$("#preference").children()[x].children[1].children[0].innerText !==
target.previousElementSibling.innerText
) {
const assignee = $("#preference").children()[x].children[1]
.children[0].innerText;
$("#warning").text(
`This key(s) is already assigned to: ${assignee}, press Enter to override.`
);
$("#edit").css("border", "1.5px solid #dc5656");
return;
} else {
$("#edit").css("border", "none");
}
x++;
}
};
export const elementDirection = (direct) => () => {
if (simulationArea.lastSelected) {
simulationArea.lastSelected.newDirection(direct.toUpperCase());
$("select[name |= 'newDirection']").val(direct.toUpperCase());
updateSystem();
}
};
export const labelDirection = (direct) => () => {
if (
simulationArea.lastSelected &&
!simulationArea.lastSelected.labelDirectionFixed
) {
simulationArea.lastSelected.labelDirection = direct.toUpperCase();
$("select[name |= 'newLabelDirection']").val(direct.toUpperCase());
updateSystem();
}
};
export const insertLabel = () => {
if (simulationArea.lastSelected) {
$("input[name |= 'setLabel']").focus();
$("input[name |= 'setLabel']").val().length
? null
: $("input[name |= 'setLabel']").val("Untitled");
$("input[name |= 'setLabel']").select();
updateSystem();
}
};
export const moveElement = (direct) => () => {
if (simulationArea.lastSelected) {
switch (direct) {
case "up":
simulationArea.lastSelected.y -= 10;
break;
case "down":
simulationArea.lastSelected.y += 10;
break;
case "left":
simulationArea.lastSelected.x -= 10;
break;
case "right":
simulationArea.lastSelected.x += 10;
break;
}
updateSystem();
}
};
export const openHotkey = () => $("#customShortcut").trigger('click');
export const newCircuitCall = () => $("#newCircuit").trigger('click');
export const openDocumentation = () => {
if (simulationArea.lastSelected == undefined || simulationArea.lastSelected.helplink == undefined) {
// didn't select any element or documentation not found
window.open("https://docs.circuitverse.org/", "_blank");
} else {
window.open(simulationArea.lastSelected.helplink, "_blank");
}
}
function updateSystem() {
updateCanvasSet(true);
wireToBeCheckedSet(1);
scheduleUpdate(1);
}

View file

@ -0,0 +1,102 @@
// import { shortcut } from './Shortcuts.plugin';
// import createSaveAsImgPrompt from '../../data/saveImage';
//Assign the callback func for the keymap here
import {
newCircuitCall,
elementDirection,
insertLabel,
labelDirection,
openHotkey,
moveElement,
openDocumentation
} from './actions';
import save from '../../data/save';
import { saveOffline, openOffline } from '../../data/project';
import createSaveAsImgPrompt from '../../data/saveImage';
import { createSubCircuitPrompt } from '../../subcircuit';
import { createCombinationalAnalysisPrompt } from '../../combinationalAnalysis';
import {shortcut} from './shortcuts.plugin.js'
export const addShortcut = (keys, action) => {
let callback;
switch (action) {
case "New Circuit":
callback = newCircuitCall;
break;
case "Save Online":
callback = save;
break;
case "Save Offline":
callback = saveOffline;
break;
case "Download as Image":
callback = createSaveAsImgPrompt;
break;
case "Open Offline":
callback = openOffline;
break;
case "Insert Sub-circuit":
callback = createSubCircuitPrompt;
break;
case "Combinational Analysis":
callback = createCombinationalAnalysisPrompt;
break; //bug
// case "Start Plot":
// callback = startPlot;
// break;
case "Direction Up":
callback = elementDirection('up');
break;
case "Direction Down":
callback = elementDirection('down');
break;
case "Direction Left":
callback = elementDirection('left');
break;
case "Direction Right":
callback = elementDirection('right');
break;
case "Insert Label":
callback = insertLabel;
break;
case "Label Direction Up":
callback = labelDirection('up');
break;
case "Label Direction Down":
callback = labelDirection('down');
break;
case "Label Direction Left":
callback = labelDirection('left');
break;
case "Label Direction Right":
callback = labelDirection('right');
break;
case "Move Element Up":
callback = moveElement('up');
break;
case "Move Element Down":
callback = moveElement('down');
break;
case "Move Element Left":
callback = moveElement('left');
break;
case "Move Element Right":
callback = moveElement('right');
break;
case "Hotkey Preference":
callback = openHotkey;
break;
case "Open Documentation":
callback = openDocumentation;
break;
default:
callback = () => console.log('No shortcut found..');
break;
}
shortcut.add(keys, callback, {
type: "keydown",
propagate: false,
target: document,
disable_in_input: true,
});
};

View file

@ -0,0 +1,399 @@
/**
* This plugin has been modified to support metakeys
*/
/*
* Library to normalize key codes across browsers. This works with keydown
* events; keypress events are not fired for all keys, and the codes are
* different for them. It returns an object with the following fields:
* { int code, bool shift, bool alt, bool ctrl }. The normalized keycodes
* obey the following rules:
*
* For alphabetic characters, the ASCII code of the uppercase version
*
* For codes that are identical across all browsers (this includes all
* modifiers, esc, delete, arrows, etc.), the common keycode
*
* For numeric keypad keys, the value returned by numkey().
* (Usually 96 + the number)
*
* For symbols, the ASCII code of the character that appears when shift
* is not held down, EXCEPT for '" => 222 (conflicts with right-arrow/pagedown),
* .> => 190 (conflicts with Delete) and `~ => 126 (conflicts with Num0).
*
* Basic usage:
* document.onkeydown = function(e) {
* do_something_with(KeyCode.translateEvent(e)
* };
*
* The naming conventions for functions use 'code' to represent an integer
* keycode, 'key' to represent a key description (specified above), and 'e'
* to represent an event object.
*
* There's also functionality to track and detect which keys are currently
* being held down: install 'key_up' and 'key_down' on their respective event
* handlers, and then check with 'is_down'.
*
* @fileoverview
* @author Jonathan Tang
* @version 0.9
* @license BSD
*/
/*
Copyright (c) 2008 Jonathan Tang
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var modifiers = ['ctrl', 'alt', 'shift', 'meta'], KEY_MAP = {},
shifted_symbols = {
58: 59, // : -> ;
43: 61, // = -> +
60: 44, // < -> ,
95: 45, // _ -> -
62: 46, // > -> .
63: 47, // ? -> /
96: 192, // ` -> ~
124: 92, // | -> \
39: 222, // ' -> 222
34: 222, // " -> 222
33: 49, // ! -> 1
64: 50, // @ -> 2
35: 51, // # -> 3
36: 52, // $ -> 4
37: 53, // % -> 5
94: 54, // ^ -> 6
38: 55, // & -> 7
42: 56, // * -> 8
40: 57, // ( -> 9
41: 58, // ) -> 0
123: 91, // { -> [
125: 93, // } -> ]
};
function isLower(ascii) {
return ascii >= 97 && ascii <= 122;
}
function capitalize(str) {
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}
var is_gecko = navigator.userAgent.indexOf('Gecko') != -1,
is_ie = navigator.userAgent.indexOf('MSIE') != -1,
is_windows = navigator.platform.indexOf('Win') != -1,
is_opera = window.opera && window.opera.version() < 9.5,
is_konqueror = navigator.vendor && navigator.vendor.indexOf('KDE') != -1,
is_icab = navigator.vendor && navigator.vendor.indexOf('iCab') != -1;
var GECKO_IE_KEYMAP = {
186: 59, // ;: in IE
187: 61, // =+ in IE
188: 44, // ,<
109: 95, // -_ in Mozilla
107: 61, // =+ in Mozilla
189: 95, // -_ in IE
190: 62, // .>
191: 47, // /?
192: 126, // `~
219: 91, // {[
220: 92, // \|
221: 93, // }]
};
var OPERA_KEYMAP = {};
// Browser detection taken from quirksmode.org
if (is_opera && is_windows) {
KEY_MAP = OPERA_KEYMAP;
} else if (is_opera || is_konqueror || is_icab) {
var unshift = [
33,
64,
35,
36,
37,
94,
38,
42,
40,
41,
58,
43,
60,
95,
62,
63,
124,
34,
];
KEY_MAP = OPERA_KEYMAP;
for (var i = 0; i < unshift.length; ++i) {
KEY_MAP[unshift[i]] = shifted_symbols[unshift[i]];
}
} else {
// IE and Gecko are close enough that we can use the same map for both,
// and the rest of the world (eg. Opera 9.50) seems to be standardizing
// on them
KEY_MAP = GECKO_IE_KEYMAP;
}
if (is_konqueror) {
KEY_MAP[0] = 45;
KEY_MAP[127] = 46;
KEY_MAP[45] = 95;
}
var key_names = {
32: 'SPACE',
13: 'ENTER',
9: 'TAB',
8: 'BACKSPACE',
16: 'SHIFT',
17: 'CTRL',
18: 'ALT',
20: 'CAPS_LOCK',
144: 'NUM_LOCK',
145: 'SCROLL_LOCK',
37: 'LEFT',
38: 'UP',
39: 'RIGHT',
40: 'DOWN',
33: 'PAGE_UP',
34: 'PAGE_DOWN',
36: 'HOME',
35: 'END',
45: 'INSERT',
46: 'DELETE',
27: 'ESCAPE',
19: 'PAUSE',
222: '\'',
91: 'META'
};
function fn_name(code) {
if (code >= 112 && code <= 123) return 'F' + (code - 111);
return false;
}
function num_name(code) {
if (code >= 96 && code < 106) return 'Num' + (code - 96);
switch (code) {
case 106:
return 'Num*';
case 111:
return 'Num/';
case 110:
return 'Num.';
default:
return false;
}
}
var current_keys = {
codes: {},
ctrl: false,
alt: false,
shift: false,
meta: false,
};
function update_current_modifiers(key) {
current_keys.ctrl = key.ctrl;
current_keys.alt = key.alt;
current_keys.shift = key.shift;
current_keys.meta = key.meta;
}
function same_modifiers(key1, key2) {
return (
key1.ctrl === key2.ctrl && key1.alt === key2.alt &&
key1.shift === key2.shift && key1.meta === key2.meta);
}
if (typeof window.KeyCode != 'undefined') {
var _KeyCode = window.KeyCode;
}
export const KeyCode = {
no_conflict: function() {
window.KeyCode = _KeyCode;
return KeyCode;
},
/** Generates a function key code from a number between 1 and 12 */
fkey: function(num) {
return 111 + num;
},
/**
* Generates a numeric keypad code from a number between 0 and 9.
* Also works for (some) arithmetic operators. The mappings are:
*
* *: 106, /: 111, .: 110
*
* + and - are not supported because the keycodes generated by Mozilla
* conflict with the non-keypad codes. The same applies to all the
* arithmetic keypad keys on Konqueror and early Opera.
*/
numkey: function(num) {
switch (num) {
case '*':
return 106;
case '/':
return 111;
case '.':
return 110;
default:
return 96 + num;
}
},
/**
* Generates a key code from the ASCII code of (the first character of) a
* string.
*/
key: function(str) {
var c = str.charCodeAt(0);
if (isLower(c)) return c - 32;
return shifted_symbols[c] || c;
},
/** Checks if two key objects are equal. */
key_equals: function(key1, key2) {
return key1.code == key2.code && same_modifiers(key1, key2);
},
/** Translates a keycode to its normalized value. */
translate_key_code: function(code) {
return KEY_MAP[code] || code;
},
/**
* Translates a keyDown event to a normalized key event object. The
* object has the following fields:
* { int code; boolean shift, boolean alt, boolean ctrl }
*/
translate_event: function(e) {
e = e || window.event;
var code = e.which || e.keyCode;
return {
code: KeyCode.translate_key_code(code),
shift: e.shiftKey,
alt: e.altKey,
ctrl: e.ctrlKey,
meta: e.metaKey
};
},
/**
* Keydown event listener to update internal state of which keys are
* currently pressed.
*/
key_down: function(e) {
var key = KeyCode.translate_event(e);
current_keys.codes[key.code] = key.code;
update_current_modifiers(key);
},
/**
* Keyup event listener to update internal state.
*/
key_up: function(e) {
var key = KeyCode.translate_event(e);
delete current_keys.codes[key.code];
update_current_modifiers(key);
},
/**
* Returns true if the key spec (as returned by translate_event) is
* currently held down.
*/
is_down: function(key) {
var code = key.code;
if (code == KeyCode.CTRL) return current_keys.ctrl;
if (code == KeyCode.ALT) return current_keys.alt;
if (code == KeyCode.SHIFT) return current_keys.shift;
return (
current_keys.codes[code] !== undefined &&
same_modifiers(key, current_keys));
},
/**
* Returns a string representation of a key event suitable for the
* shortcut.js or JQuery HotKeys plugins. Also makes a decent UI display.
*/
hot_key: function(key) {
var pieces = [];
for (var i = 0; i < modifiers.length; ++i) {
var modifier = modifiers[i];
if (key[modifier] && modifier.toUpperCase() != key_names[key.code]) {
pieces.push(capitalize(modifier));
}
}
var c = key.code;
var key_name =
key_names[c] || fn_name(c) || num_name(c) || String.fromCharCode(c);
pieces.push(capitalize(key_name));
return pieces.join('+');
},
};
// Add key constants
for (var code in key_names) {
KeyCode[key_names[code]] = code;
}
// var fields = ['charCode', 'keyCode', 'which', 'type', 'timeStamp',
// 'altKey', 'ctrlKey', 'shiftKey', 'metaKey'];
// var types = ['keydown', 'keypress', 'keyup'];
// function show_event(type) {
// return function(e) {
// var lines = [];
// for(var i = 0; i < fields.length; ++i) {
// lines.push('<b>' + fields[i] + '</b>: ' + e[fields[i]] + '<br />');
// }
// document.getElementByI(type).innerHTML = lines.join('\n');
// return false;
// }
// };
// function show_is_key_down(id, code, ctrl, alt, shift) {
// document.getElementById(id + '_down').innerHTML = KeyCode.is_down({
// code: code,
// ctrl: ctrl,
// alt: alt,
// shift: shift
// });
// };
// function update_key_downs() {
// show_is_key_down('x', KeyCode.key('x'), false, false, false);
// show_is_key_down('shift_x', KeyCode.key('x'), false, false, true);
// show_is_key_down('shift_c', KeyCode.key('c'), false, false, true);
// show_is_key_down('ctrl_a', KeyCode.key('a'), true, false, false);
// };

View file

@ -0,0 +1,249 @@
/**
* http://www.openjs.com/scripts/events/keyboard_shortcuts/
* Version : 2.01.B
* By Binny V A
* License : BSD
*/
/**
* Restrictions:
* The shortcut key combination should be specified in this format ... Modifier[+Modifier..]+Key.
* Can have a single key without Modifier .. Key, not Key + Key
* These restrictions must be be hardcoded to not let users input invalid key combo
* There is no way to override Ctrl+N, Ctrl+T, or Ctrl+W in Google Chrome since version 4 of Chrome (shipped in 2010).
*
**/
//*! This plugin has been modified
export const shortcut = {
all_shortcuts: {}, //All the shortcuts are stored in this array ex. download : keycombo;
add: function (shortcut_combination, callback, opt) {
//Provide a set of default options
var default_options = {
type: "keydown",
propagate: false,
disable_in_input: true,
target: document,
keycode: false,
};
if (!opt) opt = default_options;
else {
for (var dfo in default_options) {
if (typeof opt[dfo] == "undefined") opt[dfo] = default_options[dfo];
}
}
var ele = opt.target;
if (typeof opt.target == "string")
ele = document.getElementById(opt.target);
var ths = this;
shortcut_combination = shortcut_combination.toLowerCase();
//The function to be called at keypress
var func = function (e) {
e = e || window.event;
if (opt["disable_in_input"]) {
//Don't enable shortcut keys in Input, Textarea fields
var element;
if (e.target) element = e.target;
else if (e.srcElement) element = e.srcElement;
if (element.nodeType == 3) element = element.parentNode;
if (element.tagName == "INPUT" || element.tagName == "TEXTAREA") return;
}
let code = '';
//Find Which key is pressed
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
var character = String.fromCharCode(code).toLowerCase();
// e.preventDefault();
if (code == 188) character = ","; //If the user presses , when the type is onkeydown
if (code == 190) character = "."; //If the user presses , when the type is onkeydown
var keys = shortcut_combination.split("+");
//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
var kp = 0;
//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
var shift_nums = {
"`": "~",
"1": "!",
"2": "@",
"3": "#",
"4": "$",
"5": "%",
"6": "^",
"7": "&",
"8": "*",
"9": "(",
"0": ")",
"-": "_",
"=": "+",
";": ":",
"'": '"',
",": "<",
".": ">",
"/": "?",
"\\": "|",
};
//Special Keys - and their codes
var special_keys = {
esc: 27,
escape: 27,
tab: 9,
space: 32,
return: 13,
enter: 13,
backspace: 8,
scrolllock: 145,
scroll_lock: 145,
scroll: 145,
capslock: 20,
caps_lock: 20,
caps: 20,
numlock: 144,
num_lock: 144,
num: 144,
pause: 19,
break: 19,
insert: 45,
home: 36,
delete: 46,
end: 35,
pageup: 33,
page_up: 33,
pu: 33,
pagedown: 34,
page_down: 34,
pd: 34,
left: 37,
up: 38,
right: 39,
down: 40,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
};
var modifiers = {
shift: { wanted: false, pressed: false },
ctrl: { wanted: false, pressed: false },
alt: { wanted: false, pressed: false },
meta: { wanted: false, pressed: false }, //Meta is Mac specific
};
if (e.ctrlKey) modifiers.ctrl.pressed = true;
if (e.shiftKey) modifiers.shift.pressed = true;
if (e.altKey) modifiers.alt.pressed = true;
if (e.metaKey) modifiers.meta.pressed = true;
let k;
for (var i = 0; (k = keys[i]), i < keys.length; i++) {
//Modifiers
if (k == "ctrl" || k == "control") {
kp++;
modifiers.ctrl.wanted = true;
} else if (k == "shift") {
kp++;
modifiers.shift.wanted = true;
} else if (k == "alt") {
kp++;
modifiers.alt.wanted = true;
} else if (k == "meta") {
kp++;
modifiers.meta.wanted = true;
} else if (k.length > 1) {
//If it is a special key
if (special_keys[k] == code) kp++;
} else if (opt["keycode"]) {
if (opt["keycode"] == code) kp++;
} else {
//The special keys did not match
if (character == k) kp++;
else {
if (shift_nums[character] && e.shiftKey) {
//Stupid Shift key bug created by using lowercase
character = shift_nums[character];
if (character == k) kp++;
}
}
}
}
if (
kp == keys.length &&
modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
modifiers.shift.pressed == modifiers.shift.wanted &&
modifiers.alt.pressed == modifiers.alt.wanted &&
modifiers.meta.pressed == modifiers.meta.wanted
) {
callback(e);
if (!opt["propagate"]) {
//Stop the event
//e.cancelBubble is supported by IE - this will kill the bubbling process.
e.cancelBubble = true;
e.returnValue = false;
//e.stopPropagation works in Firefox.
if (e.stopPropagation) {
e.stopPropagation();
e.preventDefault();
}
return false;
}
}
};
this.all_shortcuts[shortcut_combination] = {
callback: func,
target: ele,
event: opt["type"],
};
//Attach the function with the event
if (ele.addEventListener) ele.addEventListener(opt["type"], func, false);
else if (ele.attachEvent) ele.attachEvent("on" + opt["type"], func);
else ele["on" + opt["type"]] = func;
},
//Remove the shortcut - just specify the shortcut and I will remove the binding
remove: function (shortcut_combination) {
shortcut_combination = shortcut_combination.toLowerCase();
var binding = this.all_shortcuts[shortcut_combination];
delete this.all_shortcuts[shortcut_combination];
if (!binding) return;
var type = binding["event"];
var ele = binding["target"];
var callback = binding["callback"];
if (ele.detachEvent) ele.detachEvent("on" + type, callback);
else if (ele.removeEventListener)
ele.removeEventListener(type, callback, false);
else ele["on" + type] = false;
},
removeAll: function () {
for (let x in this.all_shortcuts) {
this.remove(x);
}
},
};

View file

@ -0,0 +1,65 @@
Storage.prototype.set = function (key, obj) {
return this.setItem(key, JSON.stringify(obj));
};
Storage.prototype.get = function (key) {
return JSON.parse(this.getItem(key));
};
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
export const getKey = (obj, val) => Object.keys(obj).find((key) => obj[key] === val);
export const getOS = () => {
let OSName = "";
if (navigator.appVersion.indexOf("Win") != -1) OSName = "Windows";
if (navigator.appVersion.indexOf("Mac") != -1) OSName = "MacOS";
if (navigator.appVersion.indexOf("X11") != -1) OSName = "UNIX";
if (navigator.appVersion.indexOf("Linux") != -1) OSName = "Linux";
return OSName;
};
export const checkRestricted = (key) => {
const restrictedKeys = [
"Ctrl + N",
"Ctrl + W",
"Ctrl + T",
"Ctrl + C",
"Ctrl + V",
"Ctrl + Delete",
"Ctrl + Backspace",
"Ctrl + /",
"Ctrl + \\",
"Ctrl + ]",
"Ctrl + '",
"Ctrl + `",
"Ctrl + [",
"Ctrl + ~",
"Ctrl + Num1",
"Ctrl + Num2",
"Ctrl + Num3",
"Ctrl + Num4",
"Ctrl + Num5",
"Ctrl + Num6",
"Ctrl + Num*",
"Ctrl + Num/",
"Ctrl + Num.",
"Ctrl + Num0",
];
if (getOS == "macOS") {
restrictedKeys.forEach((value, i) => {
if (value.split(" + ")[0] == "Ctrl");
restrictedKeys[i] =
value.split(" + ")[0] == "Ctrl"
? value.replace("Ctrl", "Meta")
: value;
});
}
return restrictedKeys.includes(key);
};

View file

@ -0,0 +1,91 @@
import { defaultKeys } from '../defaultKeys';
import { setUserKeys } from '../model/actions';
/**
* function to generate the specific HTML for the hotkey panel
* @param {object} metadata keycombo object
*/
const createElements = (metadata) => {
let elements = ``;
Object.entries(metadata).forEach((entry) => {
elements += `
<div>
<span id='edit-icon'></span>
<div><span id='command'>${entry[0]}</span>
<span id='keyword'></span>
</div>
</div>
`;
});
return `<div id="preference" class="customScroll">${elements}</div>`;
};
export const markUp = createElements(defaultKeys);
export const editPanel = `<div id="edit" tabindex="0">
<span style="font-size: 14px;">Press Desire Key Combination & press Enter or press ESC to cancel.</span>
<div id="pressedKeys"></div>
<div id="warning"></div>
</div>`;
export const heading = `<div id="heading">
<span>Command</span>
<span>Keymapping</span>
</div>`;
/**
* fn to update the htokey panel UI with the currently set configuration
* @param {string} mode user prefered if present, or default keys configuration
*/
export const updateHTML = (mode) => {
let x = 0;
if (mode == "user") {
const userKeys = localStorage.get("userKeys");
while ($("#preference").children()[x]) {
$("#preference").children()[x].children[1].children[1].innerText =
userKeys[
$("#preference").children()[x]
.children[1].children[0].innerText
];
x++;
}
} else if (mode == "default") {
while ($("#preference").children()[x]) {
const defaultKeys = localStorage.get("defaultKeys");
$("#preference").children()[x].children[1].children[1].innerText =
defaultKeys[
$("#preference").children()[x]
.children[1].children[0].innerText
];
x++;
}
}
};
/**
* fn to override key of duplicate entries
* old entry will be left blank & keys will be assigned to the new target
* @param {*} combo
*/
export const override = (combo) => {
let x = 0;
while ($("#preference").children()[x]) {
if (
$("#preference").children()[x]
.children[1].children[1].innerText === combo
)
$("#preference").children()[x]
.children[1].children[1].innerText = "";
x++;
}
};
export const closeEdit = () => {
$("#pressedKeys").text("");
$("#edit").css("display", "none");
};
export const submit = () => {
$("#edit").css("display", "none");
setUserKeys();
updateHTML("user");
};

17
simulator/src/i18n.js Normal file
View file

@ -0,0 +1,17 @@
import Banana from 'banana-i18n';
const banana = new Banana();
banana.setLocale(window.locale);
const { locale } = banana;
const finalFallback = 'en';
// object with default language preloaded
const messages = {
[finalFallback]: require(`./i18n/${finalFallback}.json`),
};
try {
messages[locale] = require(`./i18n/${locale}.json`);
} catch (err) {
// If Asynchronous loading for current locale failed, load default locale
}
banana.load(messages);
export default banana;

View file

@ -0,0 +1,10 @@
{
"@metadata": {
"authors": [
"Pavan"
],
"last-updated": "2021-07-06",
"locale": "en",
"message-documentation": "qqq"
}
}

View file

@ -0,0 +1,10 @@
{
"@metadata": {
"authors": [
"Pavan"
],
"last-updated": "2021-07-06",
"locale": "hi",
"message-documentation": "qqq"
}
}

BIN
simulator/src/img/ALU.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="106" height="106"><path fill="#fff" stroke="#000" paint-order="fill stroke markers" d="M83.5 63.5v-20l-20-30h-40v20l10 10v20l-10 10v20h40l20-30z" stroke-miterlimit="10" stroke-width="3"/><text font-family="Georgia" font-size="6" text-decoration="normal" x="30" y="85" text-anchor="middle" dominant-baseline="alphabetic">B</text><text font-family="Georgia" font-size="6" text-decoration="normal" x="30" y="25" text-anchor="middle" dominant-baseline="alphabetic">A</text><text font-family="Georgia" font-size="6" text-decoration="normal" x="43" y="25" text-anchor="middle" dominant-baseline="alphabetic">CTR</text><text font-family="Georgia" font-size="6" text-decoration="normal" x="43" y="85" text-anchor="middle" dominant-baseline="alphabetic">Carry</text><text font-family="Georgia" font-size="6" text-decoration="normal" x="73" y="55" text-anchor="middle" dominant-baseline="alphabetic">Ans</text><text fill="#006400" font-family="Georgia" font-size="12" text-decoration="normal" x="53" y="57" text-anchor="middle" dominant-baseline="alphabetic">ALU</text><path fill="green" paint-order="stroke fill markers" d="M26 23a3 3 0 110-.003zM26 83a3 3 0 110-.003zM46 13a3 3 0 110-.003zM46 93a3 3 0 110-.003zM86 53a3 3 0 110-.003z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"><path fill="#fff" stroke="#000" paint-order="fill stroke markers" d="M5 5h40v40H5V5z" stroke-miterlimit="10" stroke-width="3"/><path fill="green" paint-order="stroke fill markers" d="M8 15a3 3 0 110-.003zM8 25a3 3 0 110-.003zM8 35a3 3 0 110-.003zM48 25a3 3 0 110-.003zM48 35a3 3 0 110-.003z"/></svg>

After

Width:  |  Height:  |  Size: 362 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"><path fill="#FFF" stroke="#000" paint-order="fill stroke markers" d="M12 5h10a20 20 0 010 40H12V5z" fill-opacity=".8" stroke-miterlimit="10" stroke-width="3"/><path fill="green" paint-order="stroke fill markers" d="M15 15a3 3 0 110-.003zM15 35a3 3 0 110-.003zM45 25a3 3 0 110-.003z"/></svg>

After

Width:  |  Height:  |  Size: 353 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="70" height="70"><path fill="#fff" stroke="red" paint-order="stroke fill markers" d="M5.5 32.5h40v-12l20 15-20 15v-12h-40z" stroke-miterlimit="10" stroke-width="3"/></svg>

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"><path fill="#FFF" stroke="red" paint-order="fill stroke markers" d="M5 5h40v40H5V5z" fill-opacity=".8" stroke-miterlimit="10" stroke-width="3"/><text fill="green" font-family="Georgia" font-size="20" text-decoration="normal" x="25" y="30" text-anchor="middle" dominant-baseline="alphabetic">x</text><path fill="green" paint-order="stroke fill markers" d="M8 25a3 3 0 110-.003zM48 25a3 3 0 110-.003zM28 45a3 3 0 110-.003z"/></svg>

After

Width:  |  Height:  |  Size: 492 B

Some files were not shown because too many files have changed in this diff Show more