Moved loads of stuff

This commit is contained in:
2017-02-27 11:17:16 +00:00
parent a96d786e23
commit 2b323470b4
44 changed files with 4 additions and 4 deletions

View File

@ -0,0 +1,41 @@
module.exports = {
name: "Affine Decrypt",
inputs: {
text: "Text",
a: "a",
b: "b"
},
output: true,
execute: function({text, a, b}, elem){
a = parseInt(a);
b = parseInt(b);
if(isNaN(a)){
return "";
}
if(isNaN(b)){
return "";
}
if(!require("./util/coPrime.js")(a, 26)){
console.log(a, 26, "not coprime");
return "";
}
var reverseLookupTable = [];
for(var i = 0; i < 26; i++){
reverseLookupTable[((a * i) + b)%26] = i;
}
return text
.split("")
.map(require("./util/toNum.js"))
.map((num)=>(reverseLookupTable[num]))
.map(require("./util/toChar.js"))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,41 @@
module.exports = {
name: "Affine Encrypt",
inputs: {
text: "Text",
a: "a",
b: "b"
},
output: true,
execute: function({text, a, b}, elem){
a = parseInt(a);
b = parseInt(b);
if(isNaN(a)){
return "";
}
if(isNaN(b)){
return "";
}
if(!require("./util/coPrime.js")(a, 26)){
return "";
console.log(a, 26, "not coprime");
}
var lookupTable = [];
for(var i = 0; i < 26; i++){
lookupTable[i] = ((a * i) + b)%26;
}
return text
.split("")
.map(require("./util/toNum.js"))
.map((num)=>(lookupTable[num]))
.map(require("./util/toChar.js"))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

18
src/blocks/asciiToHex.js Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
name: "ASCII to Hex",
inputs: {
text: "Text"
},
output: true,
execute: function({text}, elem){
return text
.split("")
.map((char)=>(char.charCodeAt(0)))
.map((int)=>(int.toString(16)))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

26
src/blocks/atbash.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
name: "Atbash",
inputs: {
text: "Text"
},
output: true,
execute: function({text}, elem){
return text
.split("")
.map(require("./util/toNum.js"))
.map((num)=>{
if(Number.isInteger(num)){
return 25 - num;
}
else{
return num;
}
})
.map(require("./util/toChar.js"))
.join("")
},
pageBlock: {
html: "",
js: function(){}
}
}

34
src/blocks/caesar.js Normal file
View File

@ -0,0 +1,34 @@
module.exports = {
name: "Caesar",
inputs: {
text: "Text",
shift: "Shift"
},
output: true,
execute: function({text, shift}, elem){
if(!isNaN(parseInt(shift))){
shift = parseInt(shift);
}
else{
shift = 0;
}
return text
.split("")
.map(require("./util/toNum.js"))
.map((num)=>{
if(Number.isInteger(num)){
return ((num + shift)%26 + 26)%26;
}
else{
return num;
}
})
.map(require("./util/toChar.js"))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

15
src/blocks/concat.js Normal file
View File

@ -0,0 +1,15 @@
module.exports = {
name: "Concat",
inputs: {
str1: "String 1",
str2: "String 2"
},
output: true,
execute: function({str1, str2}, block){
return str1 + str2;
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,18 @@
module.exports = {
name: "Elements to Numbers",
inputs: {
elements: "Elements"
},
output: true,
execute: function({elements}, elem){
return elements
.split(",")
.map((element)=>(require("./util/elements.js").indexOf(element.toLowerCase()) + 1))
.filter((num)=>(num > 0))
.join(",");
},
pageBlock: {
html: "",
js: function(){}
}
}

164
src/blocks/frequency.js Normal file
View File

@ -0,0 +1,164 @@
var events = require("../events.js");
function getGroups(text, size){
return text
.toLowerCase()
.split(/[^a-z]/)
.map((word)=>{
groups = []
for(var i = 0; i <= (word.length - size); i++){
groups.push(word.substr(i, size));
}
return groups;
})
.reduce((allGroups, groups)=>(
allGroups.concat(groups)
), []);
}
function getFirstLetters(text){
return text
.toLowerCase()
.split(/[^a-z]/)
.filter((word)=>(word.length > 0))
.map((word)=>(word[0]));
}
function getFrequency(groups){
return groups
.reduce((frequencies, group)=>{
if(!frequencies[group]){
frequencies[group] = 0;
}
frequencies[group] += 1;
return frequencies;
}, {})
};
function topGroupsByFrequency(groups, alreadyPercentage){
var output = {};
var total = Object.values(groups).reduce((a, b)=>(a + b));
output.labels = Object.keys(groups).sort((a, b)=>(groups[b] - groups[a])).slice(0, 26);
output.values = output.labels.map((groupName)=>(groups[groupName]));
if(!alreadyPercentage){
output.values = output.values.map((value)=>(value / total * 100)); //calculates as percentage of whole thing
}
return output;
}
module.exports = {
name: "Frequency Analysis",
inputs: {
input: "Input"
},
output: false,
execute: function({input}, block){
var topGroups = {};
if(!isNaN(parseInt(block.properties.type))){
//frequency of group with length type
topGroups = topGroupsByFrequency(getFrequency(getGroups(input, parseInt(block.properties.type))));
}
else if(block.properties.type == "first"){
//first letter of each word
topGroups = topGroupsByFrequency(getFrequency(getFirstLetters(input, parseInt(block.properties.type))));
}
$(block.elem).data("chartTop").data.labels = topGroups.labels;
$(block.elem).data("chartTop").data.datasets[0] = {
data: topGroups.values
};
$(block.elem).data("chartTop").update();
},
size: { //update static widths in HTML as well
height: 400,
width: 400
},
pageBlock: {
html: `
<select>
<option value="1">Single Letters</option>
<option value="2">Digraphs</option>
<option value="3">Trigraphs</option>
<option value="first">1st Letter</option>
</select>
<span class="topHidden">
<div class="canvasContainer">
<canvas class="chart top" width="380" height="150"></canvas>
</div>
<div class="canvasContainer">
<canvas class="chart bottom" width="380" height="150"></canvas>
</div>
</span>
`,
js: function(block){
var standardFrequencies = {
"1": {a: 8.167, b: 1.492, c: 2.782, d: 4.253, e: 12.702, f: 2.228, g: 2.015, h: 6.094, i: 6.966, j: 0.153, k: 0.772, l: 4.025, m: 2.406, n: 6.749, o: 7.507, p: 1.929, q: 0.095, r: 5.987, s: 6.327, t: 9.056, u: 2.758, v: 0.978, w: 2.36, x: 0.15, y: 1.974, z: 0.074},
"2": {th: 1.52, he: 1.28, in: 0.94, er: 0.94, an: 0.82, re: 0.68, nd: 0.63, at: 0.59, on: 0.57, nt: 0.56, ha: 0.56, es: 0.56, st: 0.55, en: 0.55, ed: 0.53, to: 0.52, it: 0.5, ou: 0.5, ea: 0.47, hi: 0.46, is: 0.46, or: 0.43, ti: 0.34, as: 0.33, te: 0.27, et: 0.19, ng: 0.18, of: 0.16, al: 0.09, de: 0.09, se: 0.08, le: 0.08, sa: 0.06, si: 0.05, ar: 0.04, ve: 0.04, ra: 0.04, ld: 0.02, ur: 0.02},
"3": {the: 1.3636489593493786, ing: 0.7262728609096382, and: 0.7216909325651525, ion: 0.6628250374662927, tio: 0.5432009819877033, ent: 0.5302935539742596, for: 0.4364176465207254, ati: 0.42040977561828335, ter: 0.35934740727926423, ate: 0.3318152376738465, ers: 0.3060779039472102, res: 0.280786679167951, her: 0.2793230868699396, est: 0.2682868458761497, com: 0.2678235657432033, pro: 0.2649545115201913, ere: 0.2542082834806153, all: 0.25378437256592273, int: 0.25335782763051096, men: 0.25312595645961633, you: 0.2493700011137761, ons: 0.24523864706698645, our: 0.24466859192459378, con: 0.23825624560930553, are: 0.23536230733045035, tha: 0.23203135274154815},
first: {a: 11.602, b: 4.702, c: 3.511, d: 2.67, e: 2.007, f: 3.779, g: 1.95, h: 7.232, i: 6.286, j: 0.597, k: 0.59, l: 2.705, m: 4.383, n: 2.365, o: 6.264, p: 2.545, q: 0.173, r: 1.653, s: 7.755, t: 16.671, u: 1.487, v: 0.649, w: 6.753, x: 0.017, y: 1.62, z: 0.034}
}
if(block.properties.type){
block.elem.find("select").val(block.properties.type);
}
else{
block.properties.type = block.elem.find("select").val();
}
$(block.elem).find("select").change(function(){
block.properties.type = block.elem.find("select").val();
var standardFrequency = standardFrequencies[block.properties.type];
var standardGroups = topGroupsByFrequency(standardFrequency, true);
$(block.elem).data("chartBottom").data.labels = standardGroups.labels;
$(block.elem).data("chartBottom").data.datasets[0] = {
data: standardGroups.values
};
$(block.elem).data("chartBottom").update();
events.emit("inputChanged");
});
var Chart = require("chart.js");
$(block.elem).data("chartTop", new Chart(
$(block.elem).find(".chart.top"),
{
type: "bar",
options: {
title: {
display: true,
text: "Input Text Frequency"
},
legend: {
display: false
}
}
}
));
$(block.elem).data("chartBottom", new Chart(
$(block.elem).find(".chart.bottom"),
{
type: "bar",
options: {
title: {
display: true,
text: "Standard English Frequency"
},
legend: {
display: false
}
},
data: {
labels: ["1", "2", "3"],
datasets: [{
data: [5,3,10]
}]
}
}
));
$(block.elem).find("select").change();
}
}
}

29
src/blocks/hexToAscii.js Normal file
View File

@ -0,0 +1,29 @@
module.exports = {
name: "Hex to ASCII",
inputs: {
hex: "Hex String"
},
output: true,
execute: function({hex}, elem){
return hex
.split("")
.filter((digit)=>(!isNaN(parseInt(digit, 16))))
.reduce((pairs, digit)=>{
if(pairs[pairs.length - 1].length < 2){
pairs[pairs.length - 1] += digit
}
else{
pairs.push([digit]);
}
return pairs;
}, [""])
.filter((pair)=>(pair.length == 2))
.map((pair)=>(parseInt(pair, 16)))
.map((int)=>(String.fromCharCode(int)))
.join("")
},
pageBlock: {
html: "",
js: function(){}
}
}

26
src/blocks/index.js Normal file
View File

@ -0,0 +1,26 @@
var blocks = [
"input",
"output",
"reverse",
"numsToLetters",
"lettersToNums",
"hexToAscii",
"asciiToHex",
"vigenereEncode",
"vigenereDecode",
"caesar",
"affineEncrypt",
"affineDecrypt",
"atbash",
"numbersToElements",
"elementsToNumbers",
"transposition",
"transpositionReverse",
"substitution",
"frequency",
];
module.exports = blocks.reduce((blocks, block)=>{
blocks[block] = require("./" + block + ".js");
return blocks;
}, {});

23
src/blocks/input.js Normal file
View File

@ -0,0 +1,23 @@
var events = require("../events.js");
module.exports = {
name: "Input",
inputs: {
},
output: true,
execute: function({}, block){
return block.properties.value;
},
pageBlock: {
html: "<input type='text' name='input'></input>",
js: function(block){
if(block.properties.value){
block.elem.find("input[name='input']").val(block.properties.value);
}
$(block.elem).find("input[name='input']").keyup(function(){
block.properties.value = block.elem.find("input[name='input']").val();
events.emit("inputChanged");
});
}
}
}

View File

@ -0,0 +1,35 @@
module.exports = {
name: "Letters to Numbers",
inputs: {
letters: "Letters",
offset: "Offset"
},
output: true,
execute: function({letters, offset}, elem){
if(!offset){
offset = 0;
}
else{
offset = parseInt(offset);
}
return letters
.split("")
.map((char)=>(char.charCodeAt(0)))
.filter((asciiVal)=>(((asciiVal >= 65) && (asciiVal <= 90)) || ((asciiVal >= 97) && (asciiVal <= 122))))
.map((asciiVal)=>{
if(asciiVal <= 90){
return asciiVal - 65;
}
else{
return asciiVal - 97;
}
})
.map((num)=>(num + offset))
.join(",");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,19 @@
module.exports = {
name: "Numbers to Elements",
inputs: {
numbers: "Numbers"
},
output: true,
execute: function({numbers}, elem){
return numbers
.split(",")
.map((num)=>(parseInt(num)))
.filter((num)=>(!isNaN(num)))
.map((num)=>(require("./util/elements.js")[num-1]))
.join(",");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,47 @@
module.exports = {
name: "Numbers to Letters",
inputs: {
nums: "Numbers",
offset: "Offset"
},
output: true,
execute: function({nums, offset}, elem){
if(!offset){
offset = 0;
}
else{
offset = parseInt(offset);
}
return nums
.split(",")
.filter((num)=>(num))
.map((num)=>(parseInt(num)))
.filter((num)=>(!isNaN(num)))
.map((num)=>(num - offset))
.map((num)=>(num%52))
.map((num)=>{
if(num < 0){
return 52 + num;
}
else{
return num;
}
})
.map((num)=>{
if(num < 26){
asciiOffset = 97;
}
else{
asciiOffset = 65 - 26;
}
return num + asciiOffset;
})
.map((asciiVal)=>(String.fromCharCode(asciiVal)))
.join("")
},
pageBlock: {
html: "",
js: function(){}
}
}

14
src/blocks/output.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
name: "Output",
inputs: {
input: "Input"
},
output: false,
execute: function({input}, block){
$(block.elem).find("span.output").html(input);
},
pageBlock: {
html: "<span class='output'></span>",
js: function(){}
}
}

17
src/blocks/reverse.js Normal file
View File

@ -0,0 +1,17 @@
module.exports = {
name: "Reverse",
inputs: {
input: "Input"
},
output: true,
execute: function({input}, elem){
return input
.split("")
.reverse()
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,36 @@
module.exports = {
name: "Substitution",
inputs: {
text: "Text",
mapping: "Mapping"
},
output: true,
execute: function({text, mapping}, elem){
mapping = mapping.toLowerCase();
var letterMapping = {};
for(var i = 0; i < (mapping.length - 1); i++){
letterMapping[mapping[i]] = mapping[i+1];
}
return text
.split("")
.map((char)=>{
if(char.toLowerCase() in letterMapping){
if(char.toLowerCase() == char){
return letterMapping[char];
}
else{
return letterMapping[char.toLowerCase()].toUpperCase();
}
}
else{
return char;
}
})
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

14
src/blocks/template.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
name: "Example Block",
inputs: {
input: "Input"
},
output: true,
execute: function({input}, elem){
return input;
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,27 @@
module.exports = {
name: "Transposition",
inputs: {
text: "Text",
n: "n"
},
output: true,
execute: function({text, n}, elem){
var n = parseInt(n);
text = text.replace(/[ ,.]/g, "");
var output = "";
x = 0;
for(var i = 0; i < n; i++){
for(var y = i; y < text.length; y += n){
output += text[y];
}
}
return output;
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,29 @@
module.exports = {
name: "Transposition Reverse",
inputs: {
text: "Text",
n: "n"
},
output: true,
execute: function({text, n}, elem){
var n = parseInt(n);
text = text.replace(/[ ,.]/g, "");
var output = [];
x = 0;
var z = 0;
for(var i = 0; i < n; i++){
for(var y = i; y < text.length; y += n){
output[y] = text[z];
z++;
}
}
return output.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,19 @@
function getFactors(num){
factors = [];
for(var i = 2; i <= num; i++){
if(num%i == 0){
factors.push(i);
}
}
return factors;
}
function coPrime(a, b){
aFactors = getFactors(a);
bFactors = getFactors(b);
common = aFactors.filter((factor)=>(bFactors.indexOf(factor) > -1));
return (common.length == 0);
}
module.exports = coPrime;

120
src/blocks/util/elements.js Normal file
View File

@ -0,0 +1,120 @@
module.exports =[
"h",
"he",
"li",
"be",
"b",
"c",
"n",
"o",
"f",
"ne",
"na",
"mg",
"al",
"si",
"p",
"s",
"cl",
"ar",
"k",
"ca",
"sc",
"ti",
"v",
"cr",
"mn",
"fe",
"co",
"ni",
"cu",
"zn",
"ga",
"ge",
"as",
"se",
"br",
"kr",
"rb",
"sr",
"y",
"zr",
"nb",
"mo",
"tc",
"ru",
"rh",
"pd",
"ag",
"cd",
"in",
"sn",
"sb",
"te",
"i",
"xe",
"cs",
"ba",
"la",
"ce",
"pr",
"nd",
"pm",
"sm",
"eu",
"gd",
"tb",
"dy",
"ho",
"er",
"tm",
"yb",
"lu",
"hf",
"ta",
"w",
"re",
"os",
"ir",
"pt",
"au",
"hg",
"tl",
"pb",
"bi",
"po",
"at",
"rn",
"fr",
"ra",
"ac",
"th",
"pa",
"u",
"np",
"pu",
"am",
"cm",
"bk",
"cf",
"es",
"fm",
"md",
"no",
"lr",
"rf",
"db",
"sg",
"bh",
"hs",
"mt",
"ds",
"rg",
"cn",
"nh",
"fl",
"mc",
"lv",
"ts",
"og"
];

View File

@ -0,0 +1,8 @@
module.exports = function(num){
if(Number.isInteger(num)){
return String.fromCharCode(num + 97);
}
else{
return num;
}
}

9
src/blocks/util/toNum.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = function(char){
var num = char.toLowerCase().charCodeAt(0) - 97;
if((num >= 0) && (num < 26)){
return num;
}
else{
return char;
}
}

View File

@ -0,0 +1,30 @@
module.exports = {
name: "Vigenere Decode",
inputs: {
cipherText: "Ciphertext",
key: "Key"
},
output: true,
execute: function({cipherText, key}, elem){
var keyNums = key.split("").map(require("./util/toNum.js"));
return cipherText
.split("")
.map(require("./util/toNum.js"))
.map((int, pos, ints)=>{
if(Number.isInteger(int)){
var realCharsPosition = ints.slice(0, pos).filter((num)=>(Number.isInteger(num))).length;
return (int + 26 - keyNums[realCharsPosition%key.length])%26;
}
else{
return int;
}
})
.map(require("./util/toChar.js"))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

View File

@ -0,0 +1,30 @@
module.exports = {
name: "Vigenere Encode",
inputs: {
plaintext: "Plaintext",
key: "Key"
},
output: true,
execute: function({plaintext, key}, elem){
var keyNums = key.split("").map(require("./util/toNum.js"));
return plaintext
.split("")
.map(require("./util/toNum.js"))
.map((int, pos, ints)=>{
if(Number.isInteger(int)){
var realCharsPosition = ints.slice(0, pos).filter((num)=>(Number.isInteger(num))).length;
return (int + keyNums[realCharsPosition % key.length]) % 26;
}
else{
return int;
}
})
.map(require("./util/toChar.js"))
.join("");
},
pageBlock: {
html: "",
js: function(){}
}
}

9
src/diagram/export.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = function(){
var diagram = require("./index.js");
return JSON.stringify(diagram, function(key, value){
if(key == "elem"){
return undefined;
}
return value;
});
}

12
src/diagram/import.js Normal file
View File

@ -0,0 +1,12 @@
var diagram = require("./index.js");
var events = require("../events.js");
var $ = require("jquery");
module.exports = function(newDiagram){
Object.assign(diagram, JSON.parse(newDiagram));
$("#workspace>*").remove();
for(var block of diagram.state){
require("../pageInteraction/addBlockToPage.js")(block);
}
events.emit("diagramImport");
}

5
src/diagram/index.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
name: "New Diagram",
state: [],
snapshots: []
};

17
src/events.js Normal file
View File

@ -0,0 +1,17 @@
var subscriptions = {};
module.exports = {
subscribe: function(event, callback){
if(!subscriptions[event]){
subscriptions[event] = [];
}
subscriptions[event].push(callback);
},
emit: function(event, data){
if(subscriptions[event]){
for(var callback of subscriptions[event]){
callback(data);
}
}
}
};

17
src/index.html Normal file
View File

@ -0,0 +1,17 @@
<html>
<head>
<title>Cryptography Assistant</title>
</head>
<body>
<div id="header">
<a href="#" id="projectName">New Diagram</a>
<a href="#" id="import">Open File</a>
<a href="#" id="export">Save</a>
<input type="file" id="importUpload"></input>
</div>
<div id="blocks">
</div>
<div id="workspace">
</div>
</body>
</html>

10
src/index.js Normal file
View File

@ -0,0 +1,10 @@
require("./styles.scss");
require("./pageInteraction");
require("./outputCalculation.js");
require("./menu.js");
//for lazy debugging, remove when done
window.diagram = require("./diagram");
window.$ = require("jquery");
window.exportDiagram = require("./diagram/export.js");
window.importDiagram = require("./diagram/import.js");

34
src/menu.js Normal file
View File

@ -0,0 +1,34 @@
var $ = require("jquery");
var events = require("./events.js");
var diagram = require("./diagram");
$("#header>a#export").click(function(){
var fileSaver = require("file-saver");
var exported = require("./diagram/export.js")();
var exportedBlob = new Blob([exported], {type: "text/plain;chartset=utf-8"})
fileSaver.saveAs(exportedBlob, diagram.name + ".json");
});
$("#header>a#import").click(function(){
$("#header>#importUpload").click();
});
$("#header>#importUpload").change(function(){
var reader = new FileReader();
reader.onload = function(){
require("./diagram/import.js")(reader.result);
}
reader.readAsText(this.files[0]);
});
$("#header>a#projectName").click(function(){
do{
diagram.name = prompt("Please enter a name for the diagram", diagram.name);
}
while(!diagram.name);
$("#header>a#projectName").html(diagram.name);
});
events.subscribe("diagramImport", function(){
$("#header>a#projectName").html(diagram.name);
});

38
src/outputCalculation.js Normal file
View File

@ -0,0 +1,38 @@
var diagram = require("./diagram");
var events = require("./events.js");
var blocks = require("./blocks");
function resolveOutput(block, cache){
var inputValues = {};
for(var input in block.inputs){
if(block.inputs[input] in cache){
inputValues[input] = cache[block.inputs[input]];
}
else{
var inputBlock = diagram.state.filter((diagramBlock)=>(diagramBlock.id == block.inputs[input]))[0];
inputValues[input] = resolveOutput(inputBlock, cache);
}
}
var output = "";
if(Object.keys(blocks[block.type].inputs).length == Object.keys(inputValues).length){ //only execute if all inputs are present
output = blocks[block.type].execute(inputValues, block);
}
cache[block.id] = output;
return output;
}
function calculateOutputBlocks(){
var cache = {};
var outputBlocks = diagram.state.filter((block)=>((block.type == "output") || (block.type == "frequency")));
for(var block of outputBlocks){
resolveOutput(block, cache);
}
}
events.subscribe("inputChanged", calculateOutputBlocks);
events.subscribe("newJoin", calculateOutputBlocks);
events.subscribe("diagramImport", calculateOutputBlocks);
window.calculate = calculateOutputBlocks;

View File

@ -0,0 +1,24 @@
var blocks = require("../blocks");
module.exports = function(newBlock){
var newBlockElement = $(
require("./block.hbs")({
block: blocks[newBlock.type],
instance: newBlock
})
).appendTo("#workspace");
newBlock.elem = newBlockElement;
if(blocks[newBlock.type].size){
newBlockElement.css({
height: blocks[newBlock.type].size.height,
width: blocks[newBlock.type].size.width
});
}
if(newBlock.position){
newBlockElement.css({
top: newBlock.position.y,
left: newBlock.position.x
});
}
blocks[newBlock.type].pageBlock.js(newBlock);
}

View File

@ -0,0 +1,18 @@
<div class="block" data-type="{{block.type}}" id="{{instance.id}}">
<div class="inputs">
{{#each block.inputs as |desc name|}}
<div id="{{name}}">{{desc}}</div>
{{/each}}
</div>
<div class="main">
<div>
<div class="title">{{block.name}}</div>
{{{block.pageBlock.html}}}
</div>
</div>
{{#if block.output}}
<div class="output">
Output
</div>
{{/if}}
</div>

View File

@ -0,0 +1,86 @@
var blocks = require("../blocks");
var diagram = require("../diagram");
var events = require("../events.js");
var $ = require("jquery");
var uuid = require("node-uuid");
function blockPositionChange(event){
var block = diagram.state.filter((block)=>(block.dragging))[0];
if(block){
block.position.x = event.pageX - $("#workspace").position().left - block.offset.x;
block.position.y = event.pageY - $("#workspace").position().top - block.offset.y;
$("#workspace>.block#" + block.id).css({
left: block.position.x,
top: block.position.y,
});
events.emit("blockMove");
}
}
$("#blocks").on("mousedown", ".block>.main,.block>.inputs", function(event){
var newBlock = {
id: uuid.v4(),
position: {
x: 0,
y: 0
},
dragging: true,
offset: {
x: event.pageX - $(this).parent().offset().left,
y: event.pageY - $(this).parent().offset().top
},
type: $(this).parent().data("type"),
inputs: {},
properties: {}
}
diagram.state.push(newBlock);
require("./addBlockToPage.js")(newBlock);
blockPositionChange(event);
});
$("#workspace").on("mousedown", ".block>.main,.block>.inputs", function(event){
if(event.which == 1){ //check left mouse button
var block = diagram.state.filter((block)=>(block.id == $(this).parent().attr("id")))[0];
block.dragging = true;
block.offset.x = event.pageX - $(this).parent().offset().left;
block.offset.y = event.pageY - $(this).parent().offset().top;
blockPositionChange(event);
}
});
$("#workspace").on("mouseup", ".block>.main,.block>.inputs", function(event){
diagram.state.filter((block)=>(block.id == $(this).parent().attr("id")))[0].dragging = false;
});
$(document).on("mousemove", function(event){
event.preventDefault();
blockPositionChange(event);
});
$("#workspace").on("mousedown", ".block>.main", function(event){
if(event.which == 3){ //right mouse button, delete
event.preventDefault();
for(var i in diagram.state){
if(diagram.state[i].id == $(this).parent().attr("id")){
diagram.state.splice(i, 1);
}
}
for(var block of diagram.state){
for(var input in block.inputs){
if(block.inputs[input] == $(this).parent().attr("id")){
delete block.inputs[input];
}
}
}
$(this).parent().remove();
events.emit("blockDelete");
}
});
$(document).on("contextmenu", function(event){
event.preventDefault();
return false;
});

View File

@ -0,0 +1,3 @@
require("./setupBlocks.js");
require("./dragDrop.js");
require("./joining.js");

View File

@ -0,0 +1,88 @@
var blocks = require("../blocks");
var diagram = require("../diagram");
var events = require("../events.js");
var $ = require("jquery");
function moveLine(elem, a, b, c, d){
[[a, b], [c, d]] = [[a, b], [c, d]].sort((a, b)=>(a[0] - b[0])); //swap coords based on x-value, a will always be smaller than b
var l = Math.sqrt(Math.pow(a - c, 2) + Math.pow(d - b, 2));
var x = (a + c - l) / 2;
var y = (b + d) / 2;
var theta = Math.asin(2 * (y - b) / l);
$(elem).css({
left: x,
top: y,
width: l,
transform: "rotate(" + theta + "rad)"
});
}
var lineStart = [0, 0];
var lineEnd = [0, 0];
var dragging = false;
var startBlock = "";
var endBlock = "";
var endInput = "";
$("#workspace").on("mousedown", ".block>.output", function(event){
dragging = true;
$("#workspace").append("<div class='line' id='joiningLine'></div>");
startBlock = $(this).parent().attr("id");
lineStart = [event.pageX - $("#workspace").offset().left, event.pageY - $("#workspace").offset().top];
});
$(document).on("mousemove", function(event){
if(dragging){
lineEnd = [event.pageX - $("#workspace").offset().left, event.pageY - $("#workspace").offset().top];
moveLine($("#joiningLine"), lineStart[0], lineStart[1], lineEnd[0], lineEnd[1]);
}
});
$(document).on("mouseup", function(event){
if(dragging){
dragging = false;
$("#joiningLine").remove();
}
});
$("#workspace").on("mouseup", ".block>.inputs>div", function(event){
if(dragging){
endBlock = $(this).parent().parent().attr("id");
endInput = $(this).attr("id");
var endBlockInstance = diagram.state.filter((block)=>(block.id == endBlock))[0];
endBlockInstance.inputs[endInput] = startBlock;
drawJoiningLines();
events.emit("newJoin");
}
});
events.subscribe("blockMove", drawJoiningLines);
events.subscribe("blockDelete", drawJoiningLines);
events.subscribe("diagramImport", drawJoiningLines);
function drawJoiningLines(){
$(".line").remove();
for(var endBlock of diagram.state){
for(var input in endBlock.inputs){
startBlockId = endBlock.inputs[input];
startBlock = diagram.state.filter((block)=>(block.id == startBlockId))[0];
lineId = startBlock.id + "-" + endBlock.id + "-" + input;
if($("#" + lineId).length){
var line = $("#" + lineId)
}
else{
var line = $("<div class='line' id='" + lineId + "'></div>").appendTo($("#workspace"));
}
outputElem = $("#" + startBlock.id).find(".output").eq(0);
inputElem = $("#" + endBlock.id).find(".inputs>#" + input).eq(0);
moveLine(
line,
startBlock.position.x + (outputElem.outerWidth()/2),
startBlock.position.y + outputElem.position().top + outputElem.outerHeight(),
endBlock.position.x + inputElem.position().left + (inputElem.outerWidth()/2),
endBlock.position.y
);
}
}
}

View File

@ -0,0 +1,8 @@
var $ = require("jquery");
var blocks = require("../blocks");
for(var block of Object.keys(blocks)){
blocks[block].type = block;
$("#blocks").append(require("./block.hbs")({
block: blocks[block]
}));
}

83
src/styles.scss Normal file
View File

@ -0,0 +1,83 @@
html, body{
margin: 0;
border: 0;
font-family: sans-serif;
}
body{
display: flex;
flex-direction: column;
}
#header{
#importUpload{
display: none;
}
}
#blocks{
display: flex;
flex-direction: row;
overflow-x: scroll;
.block{
margin: 10px;
}
.topHidden{
display: none;
}
}
.block{
width: 200px;
height: 200px;
border: 1px solid black;
display: flex;
flex-direction: column;
flex-shrink: 0;
background-color: white;
.inputs{
display: flex;
flex-direction: row;
>div{
font-weight: bold;
border: 1px solid black;
flex-grow: 1;
text-align: center;
padding-top: 5px;
padding-bottom: 5px;
}
}
.main{
flex-grow: 1;
display: flex;
flex-direction: column;
>div{
margin: auto;
display: flex;
flex-direction: column;
>.title{
text-align: center;
}
}
}
>.output{
font-weight: bold;
border: 1px solid black;
text-align: center;
padding-top: 5px;
padding-bottom: 5px;
}
}
#workspace{
position: relative;
.block{
position: absolute;
}
.line{
border-top: 2px solid black;
position: absolute;
z-index: 1;
pointer-events: none;
}
}