Last active
March 2, 2018 14:38
-
-
Save mmaltsev/65b77a4b865d7d98bcd187c79b66ccb2 to your computer and use it in GitHub Desktop.
Linear Programming Table
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> | |
<style> | |
.tables-wrapper { display: flex; flex-direction: row; margin: 15px; } | |
.table-column { display: flex; flex-direction: column; } | |
.table-input-false { width: 80px; border: 0; height: 20px; padding: 0; background-color: inherit; } | |
.table-input-true { width: 40px; border: 0; height: 20px; padding: 0; background-color: inherit; } | |
.table-input-true:nth-child(even) { border-left: 1px solid lightgray; } | |
.side-tables { margin-top: 4px; } | |
.header { font-size: 16px; font-weight: 800; text-align: center; } | |
.prod-cons { margin-top: 20px; } | |
.prof-ingr { text-align: center; } | |
table { border-collapse: collapse; border: 1px solid gray; } | |
tr:nth-child(even) { background-color: #f8f8f8; } | |
td { border-bottom: 1px solid gray; } | |
#delColId, #delRowId { width: 40px; } | |
.menu { display: flex; flex-direction: row; border: 1px solid gray; padding: 10px; width: 420px; } | |
.menu-column { display: flex; flex-direction: column; margin: 0 10px; height: 60px; justify-content: space-around; } | |
.menu-delete { display: flex; flex-direction: row; } | |
button { cursor: pointer; } | |
#addConst { margin-bottom: 10px; } | |
.const-input { height: 24px; width: 400px; font-size: 20px; font-weight: 800; border: 0; border-bottom: 1px solid lightgray; outline: none; } | |
.cross { font-size: 16px; font-family: cursive; cursor: pointer; } | |
.add-const { margin: 0 0 10px 10px; } | |
.add-const-button { margin-left: 10px; } | |
</style> | |
</head> | |
<body onload="drawTables()"> | |
<h2>Linear Programming</h2> | |
<div class="menu"> | |
<div class="menu-column"> | |
<button onclick="addRow()">add row</button> | |
<button onclick="addColumn()">add column</button> | |
</div> | |
<div class="menu-column"> | |
<div> | |
<button onclick="deleteRow()">delete row #</button><input type="number" id="delRowId" min="0" /> | |
</div> | |
<div class="menu-delete"> | |
<button onclick="deleteColumn()">delete column #</button><input type="number" id="delColId" min="0" /> | |
</div> | |
</div> | |
<div class="menu-column"> | |
<button onclick="saveAsJson()">Save as JSON</button> | |
<button onclick="sendData()">Submit data</button> | |
</div> | |
</div> | |
<div class="tables-wrapper"> | |
<div class="table-column"> | |
<span class="header prod-cons">Product</span> | |
<table id="prod" border="1" class="side-tables"></table> | |
</div> | |
<div class="table-column"> | |
<span class="header prof-ingr">Ingredient</span> | |
<table id="ingr" border="1"></table> | |
<table id="mtx" border="1"></table> | |
<table id="cons" border="1"></table> | |
<span class="header prof-ingr">Constraints</span> | |
</div> | |
<div class="table-column"> | |
<span class="header prod-cons">Profit</span> | |
<table id="prof" border="1" class="side-tables"></table> | |
</div> | |
</div> | |
<h3>Additional Constraints:</h3> | |
<div id="addConst"></div> | |
<button class="add-const-button" onclick="addConstraint()">Add Constraint</button> | |
<script> | |
function sendData() { | |
// POST request for sending data object | |
let obj = tablesToJSON() | |
console.log(obj) | |
$.post('/submit_data', obj) | |
.done((reponse) => { | |
console.log(reponse) | |
}) | |
} | |
function fetchData(dataId) { | |
// POST request for fetching data object based on id | |
$.post('/fetch_data', { id: dataId }) | |
.done((data) => { | |
dataToTables(data) | |
}) | |
.fail(() => { | |
dummyData = { | |
mtx: [[0, 1], [3, 4]], | |
prod: [['prod0'], ['prod1']], | |
prof: [[10], [9]], | |
ingr: [['ingr0', 'ingr1']], | |
cons: [[['2', '3'], ['6', '8']]], | |
addConst: ['0 < 12', 'trtr'], | |
} | |
dataToTables(dummyData) | |
alert('dummy data is drawn') | |
}) | |
} | |
function drawTables(dataId = -1) { | |
if (dataId === -1) { | |
// Initialization: add 3x3 matrix | |
addRow() | |
addRow() | |
addColumn() | |
addConstraint() | |
} else { | |
// Fetch predefined matrices | |
fetchData(dataId) | |
} | |
} | |
function addColumn() { | |
let { mtx, mtxRows, prod, prof, ingr, cons, rowsNum } = getTableValues() | |
let colsNum = mtxRows[0].getElementsByTagName('td').length | |
// Push a cell to every row in the mtx and fill it with text input | |
for (let i = 0; i < rowsNum; i++) { | |
insertCell(mtxRows[i], colsNum, 'mtx') | |
} | |
// Push a cell to ingr and cons and fill it with text input | |
let ingrRow = ingr.getElementsByTagName('tr')[0] | |
insertCell(ingrRow, colsNum, 'ingr') | |
let consRow = cons.getElementsByTagName('tr')[0] | |
insertCell(consRow, colsNum, 'cons', true) | |
document.getElementById('delColId').value = colsNum | |
} | |
function addRow() { | |
let { mtx, mtxRows, prod, prof, ingr, cons, rowsNum } = getTableValues() | |
if (rowsNum > 0) { | |
// Push a row to the mtx, add cells to the row and fill them with text input | |
let colsNum = mtxRows[0].getElementsByTagName('td').length | |
let newMtxRow = mtx.insertRow(rowsNum) | |
for (let i = 0; i < colsNum; i++) { | |
insertCell(newMtxRow, i, 'mtx') | |
} | |
// Push a row to prod and to prof, add one cell to each row and fill it with text input | |
insertCell(prod.insertRow(rowsNum), 0, 'prod') | |
insertCell(prof.insertRow(rowsNum), 0, 'prof') | |
} else { | |
// If mtx is empty, initialize all matrixes by adding a row | |
insertCell(mtx.insertRow(0), 0, 'mtx') | |
insertCell(prod.insertRow(0), 0, 'prod') | |
insertCell(prof.insertRow(0), 0, 'prof') | |
insertCell(ingr.insertRow(0), 0, 'ingr') | |
insertCell(cons.insertRow(0), 0, 'cons') | |
} | |
document.getElementById('delRowId').value = rowsNum | |
} | |
function deleteColumn() { | |
let { mtx, mtxRows, prod, prof, ingr, cons, rowsNum } = getTableValues() | |
let colsNum = mtxRows[0].getElementsByTagName('td').length | |
let delColId = document.getElementById('delColId').value || colsNum - 1 | |
// If there are more than one column left -> delete, if no -> error | |
if (colsNum > 1 && delColId < colsNum) { | |
// Delete a cell from every mtx row | |
for (let row of mtxRows) { | |
row.deleteCell(delColId) | |
} | |
// Delete a cell from ingr and from cons | |
let ingrRows = ingr.getElementsByTagName('tr') | |
ingrRows[0].deleteCell(delColId) | |
let consRows = cons.getElementsByTagName('tr') | |
consRows[0].deleteCell(delColId) | |
document.getElementById('delColId').value = colsNum - 2 | |
} else { | |
alert('cannot delete a column!') | |
} | |
} | |
function deleteRow() { | |
let { mtx, mtxRows, prod, prof, ingr, cons, rowsNum } = getTableValues() | |
let delRowId = document.getElementById('delRowId').value || rowsNum - 1 | |
// If there are more than one row left -> delete, if no -> error | |
if (rowsNum > 1 && delRowId < rowsNum) { | |
mtx.deleteRow(delRowId) | |
prod.deleteRow(delRowId) | |
prof.deleteRow(delRowId) | |
document.getElementById('delRowId').value = rowsNum - 2 | |
} else { | |
alert('cannot delete a row!') | |
} | |
} | |
function insertCell(row, cellId, type) { | |
let { inputType, inputValue, isDouble } = getTableAttr(cellId, type) | |
// Insert a new cell to the specified row | |
let cell = row.insertCell(cellId) | |
let input = document.createElement('input') | |
input.type = inputType | |
input.value = inputValue | |
input.onclick = function() { this.select() } | |
input.className = 'table-input-' + isDouble | |
cell.appendChild(input) | |
// If it is Constraints -> add one more input for max value | |
if (isDouble) { | |
let input2 = document.createElement('input') | |
input2.type = inputType | |
input2.value = inputValue | |
input2.onclick = function() { this.select() } | |
input2.className = 'table-input-' + isDouble | |
cell.appendChild(input2) | |
} | |
} | |
function getTableAttr(cellId, type) { | |
// Helper for getting all widely used table attributes | |
let inputType = 'text' | |
let inputValue = '' | |
let isDouble = false | |
if (type === 'mtx') { | |
inputType = 'number' | |
inputValue = 0 | |
isDouble = false | |
} else if (type === 'prod') { | |
let rowId = document.getElementById('prod').getElementsByTagName('tr').length - 1 | |
inputType = 'text' | |
inputValue = 'prod' + rowId | |
isDouble = false | |
} else if (type === 'prof') { | |
inputType = 'number' | |
inputValue = 0 | |
isDouble = false | |
} else if (type === 'ingr') { | |
inputType = 'text' | |
inputValue = 'ingr' + cellId | |
isDouble = false | |
} else if (type === 'cons') { | |
inputType = 'text' | |
inputValue = 'None' | |
isDouble = true | |
} | |
return { inputType, inputValue, isDouble } | |
} | |
function dataToTables(data) { | |
// Convert JSON object to HTML tables and additional constraints inputs | |
for (let arr in data) { | |
let { inputType, inputValue, isDouble } = getTableAttr(0, arr) | |
if (data.hasOwnProperty(arr) && arr !== 'addConst') { | |
let table = document.getElementById(arr) | |
let tableHTML = '<table>' | |
for (let row of data[arr]) { | |
tableHTML += '<tr>' | |
for (let cell of row) { | |
tableHTML += '<td>' | |
if (Array.isArray(cell)) { | |
for (let val of cell) { | |
tableHTML += '<input value="' + val + | |
'" class="table-input-' + isDouble + | |
'" type="' + inputType + '" onclick="select()" />' | |
} | |
} else { | |
tableHTML += '<input value="' + cell + | |
'" class="table-input-' + isDouble + | |
'" type="' + inputType + '" onclick="select()" />' | |
} | |
tableHTML += '</td>' | |
} | |
tableHTML += '</tr>' | |
} | |
tableHTML += '</table>' | |
table.innerHTML = tableHTML | |
} else if (arr === 'addConst') { | |
for (constraint of data[arr]) { | |
addConstraint(constraint) | |
} | |
} | |
} | |
document.getElementById('delRowId').value = data['mtx'].length - 1 | |
document.getElementById('delColId').value = data['mtx'][0].length - 1 | |
} | |
function tablesToJSON() { | |
// Extract tables' data and put to JSON object | |
let tableNames = ['mtx', 'prod', 'prof', 'ingr', 'cons'] | |
let obj = {} | |
for (tableName of tableNames) { | |
obj[tableName] = Array.prototype.map.call(document.querySelectorAll('#' + tableName + ' tr'), function (tr) { | |
return Array.prototype.map.call(tr.querySelectorAll('td'), function (td) { | |
return Array.prototype.map.call(td.querySelectorAll('input'), input => input.value) | |
}) | |
}) | |
} | |
obj['addConst'] = [] | |
let addConst = document.getElementById('addConst') | |
let inputs = addConst.getElementsByTagName('input') | |
for (let input of inputs) { | |
if (input.value !== '') { | |
obj['addConst'].push(input.value) | |
} | |
} | |
return obj | |
} | |
function saveAsJson() { | |
// Create an HTML link and click on it to download the file | |
let jsonObj = JSON.stringify(tablesToJSON()) | |
let a = document.createElement('a') | |
let file = new Blob([jsonObj], {type: 'text/plain'}) | |
a.href = URL.createObjectURL(file) | |
a.download = 'file.json' | |
a.click() | |
} | |
function getTableValues() { | |
// Extract all useful matrices' values | |
let mtx = document.getElementById('mtx') | |
let mtxRows = mtx.getElementsByTagName('tr') | |
return { | |
mtx, | |
mtxRows, | |
prod: document.getElementById('prod'), | |
prof: document.getElementById('prof'), | |
ingr: document.getElementById('ingr'), | |
cons: document.getElementById('cons'), | |
rowsNum: mtxRows.length, | |
} | |
} | |
function addConstraint(value = '') { | |
// Add a new constraint as an input text | |
let addConst = document.getElementById('addConst') | |
let constNum = addConst.getElementsByTagName('div').length | |
let div = document.createElement('div') | |
div.id = 'const' + constNum | |
div.className = 'add-const' | |
div.innerHTML = '<input type="text" class="const-input" value="' + | |
value + '" /> <span onclick="deleteConst(' + | |
constNum +')" class="cross">x</span>' | |
addConst.appendChild(div) | |
} | |
function deleteConst(constId) { | |
document.getElementById('const' + constId).innerHTML = '' | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment